summaryrefslogtreecommitdiff
path: root/lib/pleroma
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma')
-rw-r--r--lib/pleroma/activity.ex32
-rw-r--r--lib/pleroma/activity/html.ex81
-rw-r--r--lib/pleroma/activity/ir/topics.ex33
-rw-r--r--lib/pleroma/activity/queries.ex8
-rw-r--r--lib/pleroma/activity/search.ex64
-rw-r--r--lib/pleroma/announcement.ex160
-rw-r--r--lib/pleroma/announcement_read_relationship.ex55
-rw-r--r--lib/pleroma/application.ex41
-rw-r--r--lib/pleroma/application_requirements.ex50
-rw-r--r--lib/pleroma/bbs/authenticator.ex20
-rw-r--r--lib/pleroma/bbs/handler.ex151
-rw-r--r--lib/pleroma/bookmark.ex2
-rw-r--r--lib/pleroma/caching.ex2
-rw-r--r--lib/pleroma/captcha.ex2
-rw-r--r--lib/pleroma/captcha/kocaptcha.ex2
-rw-r--r--lib/pleroma/captcha/native.ex2
-rw-r--r--lib/pleroma/captcha/service.ex2
-rw-r--r--lib/pleroma/chat.ex2
-rw-r--r--lib/pleroma/chat/message_reference.ex2
-rw-r--r--lib/pleroma/clippy.ex2
-rw-r--r--lib/pleroma/config.ex2
-rw-r--r--lib/pleroma/config/deprecation_warnings.ex230
-rw-r--r--lib/pleroma/config/getting.ex2
-rw-r--r--lib/pleroma/config/helpers.ex2
-rw-r--r--lib/pleroma/config/holder.ex2
-rw-r--r--lib/pleroma/config/loader.ex23
-rw-r--r--lib/pleroma/config/oban.ex6
-rw-r--r--lib/pleroma/config/release_runtime_provider.ex4
-rw-r--r--lib/pleroma/config/transfer_task.ex53
-rw-r--r--lib/pleroma/config_db.ex5
-rw-r--r--lib/pleroma/constants.ex44
-rw-r--r--lib/pleroma/conversation.ex2
-rw-r--r--lib/pleroma/conversation/participation.ex2
-rw-r--r--lib/pleroma/conversation/participation/recipient_ship.ex2
-rw-r--r--lib/pleroma/counter_cache.ex2
-rw-r--r--lib/pleroma/data_migration.ex3
-rw-r--r--lib/pleroma/delivery.ex2
-rw-r--r--lib/pleroma/docs/generator.ex2
-rw-r--r--lib/pleroma/docs/json.ex2
-rw-r--r--lib/pleroma/docs/markdown.ex2
-rw-r--r--lib/pleroma/docs/translator.ex10
-rw-r--r--lib/pleroma/docs/translator/compiler.ex119
-rw-r--r--lib/pleroma/earmark_renderer.ex256
-rw-r--r--lib/pleroma/ecto_enums.ex6
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex23
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex2
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex2
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/mime.ex25
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex2
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex38
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex2
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex2
-rw-r--r--lib/pleroma/ecto_type/config/atom.ex2
-rw-r--r--lib/pleroma/ecto_type/config/binary_value.ex2
-rw-r--r--lib/pleroma/emails/admin_email.ex6
-rw-r--r--lib/pleroma/emails/mailer.ex2
-rw-r--r--lib/pleroma/emails/new_users_digest_email.ex2
-rw-r--r--lib/pleroma/emails/user_email.ex472
-rw-r--r--lib/pleroma/emoji-test.txt233
-rw-r--r--lib/pleroma/emoji.ex69
-rw-r--r--lib/pleroma/emoji/combinations.ex45
-rw-r--r--lib/pleroma/emoji/formatter.ex6
-rw-r--r--lib/pleroma/emoji/loader.ex12
-rw-r--r--lib/pleroma/emoji/pack.ex2
-rw-r--r--lib/pleroma/filter.ex2
-rw-r--r--lib/pleroma/following_relationship.ex5
-rw-r--r--lib/pleroma/formatter.ex50
-rw-r--r--lib/pleroma/frontend.ex2
-rw-r--r--lib/pleroma/gopher/server.ex2
-rw-r--r--lib/pleroma/gun.ex6
-rw-r--r--lib/pleroma/gun/api.ex2
-rw-r--r--lib/pleroma/gun/conn.ex10
-rw-r--r--lib/pleroma/gun/connection_pool.ex2
-rw-r--r--lib/pleroma/gun/connection_pool/reclaimer.ex8
-rw-r--r--lib/pleroma/gun/connection_pool/worker.ex12
-rw-r--r--lib/pleroma/gun/connection_pool/worker_supervisor.ex2
-rw-r--r--lib/pleroma/hashtag.ex2
-rw-r--r--lib/pleroma/healthcheck.ex2
-rw-r--r--lib/pleroma/helpers/auth_helper.ex2
-rw-r--r--lib/pleroma/helpers/inet_helper.ex2
-rw-r--r--lib/pleroma/helpers/media_helper.ex2
-rw-r--r--lib/pleroma/helpers/qt_fast_start.ex2
-rw-r--r--lib/pleroma/helpers/uri_helper.ex2
-rw-r--r--lib/pleroma/html.ex37
-rw-r--r--lib/pleroma/http.ex11
-rw-r--r--lib/pleroma/http/adapter_helper.ex2
-rw-r--r--lib/pleroma/http/adapter_helper/default.ex2
-rw-r--r--lib/pleroma/http/adapter_helper/gun.ex6
-rw-r--r--lib/pleroma/http/adapter_helper/hackney.ex6
-rw-r--r--lib/pleroma/http/ex_aws.ex2
-rw-r--r--lib/pleroma/http/request.ex2
-rw-r--r--lib/pleroma/http/request_builder.ex2
-rw-r--r--lib/pleroma/http/tzdata.ex2
-rw-r--r--lib/pleroma/http/web_push.ex6
-rw-r--r--lib/pleroma/instances.ex19
-rw-r--r--lib/pleroma/instances/instance.ex24
-rw-r--r--lib/pleroma/job_queue_monitor.ex2
-rw-r--r--lib/pleroma/jwt.ex2
-rw-r--r--lib/pleroma/keys.ex2
-rw-r--r--lib/pleroma/list.ex2
-rw-r--r--lib/pleroma/logging.ex2
-rw-r--r--lib/pleroma/maintenance.ex6
-rw-r--r--lib/pleroma/maps.ex8
-rw-r--r--lib/pleroma/marker.ex2
-rw-r--r--lib/pleroma/mfa.ex2
-rw-r--r--lib/pleroma/mfa/backup_codes.ex2
-rw-r--r--lib/pleroma/mfa/changeset.ex2
-rw-r--r--lib/pleroma/mfa/settings.ex2
-rw-r--r--lib/pleroma/mfa/token.ex2
-rw-r--r--lib/pleroma/mfa/totp.ex2
-rw-r--r--lib/pleroma/migration_helper/notification_backfill.ex2
-rw-r--r--lib/pleroma/migrators/context_objects_deletion_migrator.ex139
-rw-r--r--lib/pleroma/migrators/hashtags_table_migrator.ex4
-rw-r--r--lib/pleroma/migrators/support/base_migrator.ex2
-rw-r--r--lib/pleroma/migrators/support/base_migrator_state.ex2
-rw-r--r--lib/pleroma/moderation_log.ex30
-rw-r--r--lib/pleroma/notification.ex140
-rw-r--r--lib/pleroma/object.ex52
-rw-r--r--lib/pleroma/object/containment.ex2
-rw-r--r--lib/pleroma/object/fetcher.ex69
-rw-r--r--lib/pleroma/object/updater.ex290
-rw-r--r--lib/pleroma/object_tombstone.ex2
-rw-r--r--lib/pleroma/otp_version.ex2
-rw-r--r--lib/pleroma/pagination.ex2
-rw-r--r--lib/pleroma/password/pbkdf2.ex2
-rw-r--r--lib/pleroma/password_reset_token.ex2
-rw-r--r--lib/pleroma/registration.ex2
-rw-r--r--lib/pleroma/release_tasks.ex2
-rw-r--r--lib/pleroma/repo.ex4
-rw-r--r--lib/pleroma/report_note.ex2
-rw-r--r--lib/pleroma/reverse_proxy.ex4
-rw-r--r--lib/pleroma/reverse_proxy/client.ex20
-rw-r--r--lib/pleroma/reverse_proxy/client/hackney.ex3
-rw-r--r--lib/pleroma/reverse_proxy/client/tesla.ex2
-rw-r--r--lib/pleroma/reverse_proxy/client/wrapper.ex30
-rw-r--r--lib/pleroma/scheduled_activity.ex2
-rw-r--r--lib/pleroma/signature.ex30
-rw-r--r--lib/pleroma/stats.ex2
-rw-r--r--lib/pleroma/telemetry/logger.ex10
-rw-r--r--lib/pleroma/tesla/middleware/connection_pool.ex2
-rw-r--r--lib/pleroma/tests/auth_test_controller.ex14
-rw-r--r--lib/pleroma/thread_mute.ex2
-rw-r--r--lib/pleroma/upload.ex47
-rw-r--r--lib/pleroma/upload/filter.ex14
-rw-r--r--lib/pleroma/upload/filter/analyze_metadata.ex83
-rw-r--r--lib/pleroma/upload/filter/anonymize_filename.ex2
-rw-r--r--lib/pleroma/upload/filter/dedupe.ex2
-rw-r--r--lib/pleroma/upload/filter/exiftool/read_description.ex52
-rw-r--r--lib/pleroma/upload/filter/exiftool/strip_location.ex (renamed from lib/pleroma/upload/filter/exiftool.ex)5
-rw-r--r--lib/pleroma/upload/filter/mogrifun.ex2
-rw-r--r--lib/pleroma/upload/filter/mogrify.ex2
-rw-r--r--lib/pleroma/upload/filter/only_media.ex20
-rw-r--r--lib/pleroma/uploaders/local.ex2
-rw-r--r--lib/pleroma/uploaders/s3.ex2
-rw-r--r--lib/pleroma/uploaders/uploader.ex6
-rw-r--r--lib/pleroma/user.ex418
-rw-r--r--lib/pleroma/user/backup.ex20
-rw-r--r--lib/pleroma/user/import.ex2
-rw-r--r--lib/pleroma/user/notification_setting.ex2
-rw-r--r--lib/pleroma/user/query.ex71
-rw-r--r--lib/pleroma/user/search.ex7
-rw-r--r--lib/pleroma/user/welcome_chat_message.ex2
-rw-r--r--lib/pleroma/user/welcome_email.ex2
-rw-r--r--lib/pleroma/user/welcome_message.ex2
-rw-r--r--lib/pleroma/user_invite_token.ex2
-rw-r--r--lib/pleroma/user_note.ex52
-rw-r--r--lib/pleroma/user_relationship.ex49
-rw-r--r--lib/pleroma/utils.ex2
-rw-r--r--lib/pleroma/web.ex32
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex248
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub/persisting.ex4
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub/streaming.ex10
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex206
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex130
-rw-r--r--lib/pleroma/web/activity_pub/internal_fetch_actor.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex75
-rw-r--r--lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex25
-rw-r--r--lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex7
-rw-r--r--lib/pleroma/web/activity_pub/mrf/drop_policy.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex10
-rw-r--r--lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex6
-rw-r--r--lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex6
-rw-r--r--lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex137
-rw-r--r--lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex51
-rw-r--r--lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/keyword_policy.ex63
-rw-r--r--lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex13
-rw-r--r--lib/pleroma/web/activity_pub/mrf/mention_policy.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex23
-rw-r--r--lib/pleroma/web/activity_pub/mrf/no_op_policy.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex11
-rw-r--r--lib/pleroma/web/activity_pub/mrf/normalize_markup.ex10
-rw-r--r--lib/pleroma/web/activity_pub/mrf/object_age_policy.ex12
-rw-r--r--lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/policy.ex17
-rw-r--r--lib/pleroma/web/activity_pub/mrf/reject_non_public.ex6
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex225
-rw-r--r--lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex67
-rw-r--r--lib/pleroma/web/activity_pub/mrf/subchain_policy.ex8
-rw-r--r--lib/pleroma/web/activity_pub/mrf/tag_policy.ex14
-rw-r--r--lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex6
-rw-r--r--lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex6
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex146
-rw-r--r--lib/pleroma/web/activity_pub/object_validator/validating.ex2
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex16
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex20
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/announce_validator.ex45
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/answer_validator.ex22
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex109
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex107
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex37
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/audio_image_video_validator.ex (renamed from lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex)70
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/block_validator.ex23
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex2
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/common_fields.ex67
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/common_fixes.ex68
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/common_validations.ex9
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex11
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex81
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex29
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/delete_validator.ex30
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex97
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/event_validator.ex51
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/follow_validator.ex18
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/like_validator.ex67
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex2
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/question_validator.ex47
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/tag_validator.ex8
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/undo_validator.ex28
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/update_validator.ex17
-rw-r--r--lib/pleroma/web/activity_pub/pipeline.ex28
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex30
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex2
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex127
-rw-r--r--lib/pleroma/web/activity_pub/side_effects/handling.ex2
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex240
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex195
-rw-r--r--lib/pleroma/web/activity_pub/views/object_view.ex6
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex15
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex14
-rw-r--r--lib/pleroma/web/admin_api/controllers/admin_api_controller.ex22
-rw-r--r--lib/pleroma/web/admin_api/controllers/announcement_controller.ex83
-rw-r--r--lib/pleroma/web/admin_api/controllers/chat_controller.ex9
-rw-r--r--lib/pleroma/web/admin_api/controllers/config_controller.ex52
-rw-r--r--lib/pleroma/web/admin_api/controllers/fallback_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/controllers/frontend_controller.ex35
-rw-r--r--lib/pleroma/web/admin_api/controllers/instance_controller.ex63
-rw-r--r--lib/pleroma/web/admin_api/controllers/instance_document_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/controllers/invite_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex3
-rw-r--r--lib/pleroma/web/admin_api/controllers/relay_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/controllers/report_controller.ex2
-rw-r--r--lib/pleroma/web/admin_api/controllers/status_controller.ex8
-rw-r--r--lib/pleroma/web/admin_api/controllers/user_controller.ex34
-rw-r--r--lib/pleroma/web/admin_api/report.ex45
-rw-r--r--lib/pleroma/web/admin_api/search.ex10
-rw-r--r--lib/pleroma/web/admin_api/views/account_view.ex7
-rw-r--r--lib/pleroma/web/admin_api/views/announcement_view.ex15
-rw-r--r--lib/pleroma/web/admin_api/views/chat_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/config_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/frontend_view.ex5
-rw-r--r--lib/pleroma/web/admin_api/views/invite_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/moderation_log_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/o_auth_app_view.ex10
-rw-r--r--lib/pleroma/web/admin_api/views/report_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/status_view.ex2
-rw-r--r--lib/pleroma/web/admin_api/views/user_view.ex10
-rw-r--r--lib/pleroma/web/api_spec.ex26
-rw-r--r--lib/pleroma/web/api_spec/cast_and_validate.ex2
-rw-r--r--lib/pleroma/web/api_spec/helpers.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/account_operation.ex196
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex165
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/chat_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/config_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex7
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/invite_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/relay_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/report_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/status_operation.ex10
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/user_operation.ex68
-rw-r--r--lib/pleroma/web/api_spec/operations/announcement_operation.ex57
-rw-r--r--lib/pleroma/web/api_spec/operations/app_operation.ex31
-rw-r--r--lib/pleroma/web/api_spec/operations/chat_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/conversation_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/directory_operation.ex41
-rw-r--r--lib/pleroma/web/api_spec/operations/domain_block_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/filter_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/follow_request_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/instance_operation.ex6
-rw-r--r--lib/pleroma/web/api_spec/operations/list_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/marker_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/media_operation.ex5
-rw-r--r--lib/pleroma/web/api_spec/operations/notification_operation.ex11
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex51
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex31
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex6
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex8
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex30
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex4
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex72
-rw-r--r--lib/pleroma/web/api_spec/operations/poll_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/report_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/search_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/status_operation.ex194
-rw-r--r--lib/pleroma/web/api_spec/operations/subscription_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/timeline_operation.ex5
-rw-r--r--lib/pleroma/web/api_spec/operations/twitter_util_operation.ex442
-rw-r--r--lib/pleroma/web/api_spec/operations/user_import_operation.ex3
-rw-r--r--lib/pleroma/web/api_spec/render_error.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/account.ex12
-rw-r--r--lib/pleroma/web/api_spec/schemas/account_field.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/account_relationship.ex10
-rw-r--r--lib/pleroma/web/api_spec/schemas/actor_type.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/announcement.ex45
-rw-r--r--lib/pleroma/web/api_spec/schemas/api_error.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/app.ex33
-rw-r--r--lib/pleroma/web/api_spec/schemas/attachment.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/boolean_like.ex4
-rw-r--r--lib/pleroma/web/api_spec/schemas/chat.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/chat_message.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/conversation.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/emoji.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/flake_id.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/list.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/poll.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/push_subscription.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/scheduled_status.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/status.ex33
-rw-r--r--lib/pleroma/web/api_spec/schemas/tag.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/visibility_scope.ex2
-rw-r--r--lib/pleroma/web/api_spec/scopes/compiler.ex82
-rw-r--r--lib/pleroma/web/api_spec/scopes/translator.ex10
-rw-r--r--lib/pleroma/web/auth/authenticator.ex65
-rw-r--r--lib/pleroma/web/auth/helpers.ex33
-rw-r--r--lib/pleroma/web/auth/ldap_authenticator.ex5
-rw-r--r--lib/pleroma/web/auth/pleroma_authenticator.ex5
-rw-r--r--lib/pleroma/web/auth/totp_authenticator.ex2
-rw-r--r--lib/pleroma/web/auth/wrapper_authenticator.ex42
-rw-r--r--lib/pleroma/web/channels/user_socket.ex6
-rw-r--r--lib/pleroma/web/common_api.ex64
-rw-r--r--lib/pleroma/web/common_api/activity_draft.ex21
-rw-r--r--lib/pleroma/web/common_api/utils.ex125
-rw-r--r--lib/pleroma/web/controller_helper.ex16
-rw-r--r--lib/pleroma/web/embed_controller.ex2
-rw-r--r--lib/pleroma/web/endpoint.ex3
-rw-r--r--lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex2
-rw-r--r--lib/pleroma/web/fallback/redirect_controller.ex2
-rw-r--r--lib/pleroma/web/federator.ex35
-rw-r--r--lib/pleroma/web/federator/publisher.ex2
-rw-r--r--lib/pleroma/web/federator/publishing.ex2
-rw-r--r--lib/pleroma/web/feed/feed_view.ex120
-rw-r--r--lib/pleroma/web/feed/tag_controller.ex2
-rw-r--r--lib/pleroma/web/feed/user_controller.ex6
-rw-r--r--lib/pleroma/web/gettext.ex194
-rw-r--r--lib/pleroma/web/instance_document.ex2
-rw-r--r--lib/pleroma/web/mailer/subscription_controller.ex2
-rw-r--r--lib/pleroma/web/manifest_controller.ex14
-rw-r--r--lib/pleroma/web/masto_fe_controller.ex65
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex145
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex60
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/app_controller.ex20
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/auth_controller.ex86
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex8
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/directory_controller.ex82
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/filter_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex3
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/instance_controller.ex8
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/list_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/marker_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex8
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/media_controller.ex3
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/notification_controller.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/poll_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/report_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/search_controller.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/status_controller.ex67
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex92
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex13
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api.ex37
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex97
-rw-r--r--lib/pleroma/web/mastodon_api/views/announcement_view.ex15
-rw-r--r--lib/pleroma/web/mastodon_api/views/app_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/conversation_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/views/filter_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/follow_request_view.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/views/instance_view.ex55
-rw-r--r--lib/pleroma/web/mastodon_api/views/list_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/marker_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/media_view.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/views/notification_view.ex25
-rw-r--r--lib/pleroma/web/mastodon_api/views/poll_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/report_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex229
-rw-r--r--lib/pleroma/web/mastodon_api/views/subscription_view.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/views/suggestion_view.ex28
-rw-r--r--lib/pleroma/web/mastodon_api/views/timeline_view.ex10
-rw-r--r--lib/pleroma/web/mastodon_api/websocket_handler.ex19
-rw-r--r--lib/pleroma/web/media_proxy.ex10
-rw-r--r--lib/pleroma/web/media_proxy/invalidation.ex2
-rw-r--r--lib/pleroma/web/media_proxy/invalidation/http.ex2
-rw-r--r--lib/pleroma/web/media_proxy/invalidation/script.ex2
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy_controller.ex11
-rw-r--r--lib/pleroma/web/metadata.ex2
-rw-r--r--lib/pleroma/web/metadata/player_view.ex2
-rw-r--r--lib/pleroma/web/metadata/providers/feed.ex2
-rw-r--r--lib/pleroma/web/metadata/providers/open_graph.ex83
-rw-r--r--lib/pleroma/web/metadata/providers/provider.ex2
-rw-r--r--lib/pleroma/web/metadata/providers/rel_me.ex16
-rw-r--r--lib/pleroma/web/metadata/providers/restrict_indexing.ex2
-rw-r--r--lib/pleroma/web/metadata/providers/twitter_card.ex88
-rw-r--r--lib/pleroma/web/metadata/utils.ex30
-rw-r--r--lib/pleroma/web/mongoose_im/mongoose_im_controller.ex2
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo.ex10
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex8
-rw-r--r--lib/pleroma/web/o_auth.ex2
-rw-r--r--lib/pleroma/web/o_auth/app.ex13
-rw-r--r--lib/pleroma/web/o_auth/authorization.ex2
-rw-r--r--lib/pleroma/web/o_auth/fallback_controller.ex2
-rw-r--r--lib/pleroma/web/o_auth/mfa_controller.ex2
-rw-r--r--lib/pleroma/web/o_auth/mfa_view.ex3
-rw-r--r--lib/pleroma/web/o_auth/o_auth_controller.ex18
-rw-r--r--lib/pleroma/web/o_auth/o_auth_view.ex4
-rw-r--r--lib/pleroma/web/o_auth/scopes.ex2
-rw-r--r--lib/pleroma/web/o_auth/token.ex2
-rw-r--r--lib/pleroma/web/o_auth/token/query.ex2
-rw-r--r--lib/pleroma/web/o_auth/token/strategy/refresh_token.ex2
-rw-r--r--lib/pleroma/web/o_auth/token/strategy/revoke.ex16
-rw-r--r--lib/pleroma/web/o_auth/token/utils.ex2
-rw-r--r--lib/pleroma/web/o_status/o_status_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/account_controller.ex62
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/app_controller.ex23
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/backup_controller.ex4
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/chat_controller.ex6
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex3
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex8
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex26
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/instances_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/notification_controller.ex4
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/report_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/settings_controller.ex79
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/views/account_view.ex10
-rw-r--r--lib/pleroma/web/pleroma_api/views/app_view.ex11
-rw-r--r--lib/pleroma/web/pleroma_api/views/backup_view.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/views/chat_view.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/views/conversation_view.ex10
-rw-r--r--lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex19
-rw-r--r--lib/pleroma/web/pleroma_api/views/notification_view.ex10
-rw-r--r--lib/pleroma/web/pleroma_api/views/report_view.ex2
-rw-r--r--lib/pleroma/web/pleroma_api/views/scrobble_view.ex2
-rw-r--r--lib/pleroma/web/plug.ex2
-rw-r--r--lib/pleroma/web/plugs/admin_secret_authentication_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/authentication_plug.ex10
-rw-r--r--lib/pleroma/web/plugs/basic_auth_decoder_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/cache.ex14
-rw-r--r--lib/pleroma/web/plugs/digest_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/ensure_authenticated_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/ensure_privileged_plug.ex44
-rw-r--r--lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/expect_authenticated_check_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/federating_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/frontend_static.ex11
-rw-r--r--lib/pleroma/web/plugs/http_security_plug.ex6
-rw-r--r--lib/pleroma/web/plugs/http_signature_plug.ex55
-rw-r--r--lib/pleroma/web/plugs/idempotency_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/instance_static.ex2
-rw-r--r--lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/o_auth_plug.ex14
-rw-r--r--lib/pleroma/web/plugs/o_auth_scopes_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/plug_helper.ex2
-rw-r--r--lib/pleroma/web/plugs/rate_limiter.ex2
-rw-r--r--lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex2
-rw-r--r--lib/pleroma/web/plugs/rate_limiter/supervisor.ex2
-rw-r--r--lib/pleroma/web/plugs/remote_ip.ex2
-rw-r--r--lib/pleroma/web/plugs/set_format_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/set_locale_plug.ex64
-rw-r--r--lib/pleroma/web/plugs/set_user_session_id_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/static_fe_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/trailing_format_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/uploaded_media.ex28
-rw-r--r--lib/pleroma/web/plugs/user_enabled_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/user_fetcher_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/user_is_admin_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/user_is_staff_plug.ex23
-rw-r--r--lib/pleroma/web/plugs/user_tracking_plug.ex2
-rw-r--r--lib/pleroma/web/preload.ex7
-rw-r--r--lib/pleroma/web/preload/providers/instance.ex2
-rw-r--r--lib/pleroma/web/preload/providers/provider.ex2
-rw-r--r--lib/pleroma/web/preload/providers/timelines.ex2
-rw-r--r--lib/pleroma/web/preload/providers/user.ex2
-rw-r--r--lib/pleroma/web/push.ex2
-rw-r--r--lib/pleroma/web/push/impl.ex18
-rw-r--r--lib/pleroma/web/push/subscription.ex4
-rw-r--r--lib/pleroma/web/rel_me.ex20
-rw-r--r--lib/pleroma/web/rich_media/helpers.ex2
-rw-r--r--lib/pleroma/web/rich_media/parser.ex2
-rw-r--r--lib/pleroma/web/rich_media/parser/ttl.ex2
-rw-r--r--lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex2
-rw-r--r--lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex2
-rw-r--r--lib/pleroma/web/rich_media/parsers/o_embed.ex6
-rw-r--r--lib/pleroma/web/rich_media/parsers/ogp.ex2
-rw-r--r--lib/pleroma/web/rich_media/parsers/twitter_card.ex2
-rw-r--r--lib/pleroma/web/router.ex360
-rw-r--r--lib/pleroma/web/shout_channel.ex (renamed from lib/pleroma/web/chat_channel.ex)14
-rw-r--r--lib/pleroma/web/static_fe/static_fe_controller.ex13
-rw-r--r--lib/pleroma/web/static_fe/static_fe_view.ex2
-rw-r--r--lib/pleroma/web/streamer.ex90
-rw-r--r--lib/pleroma/web/templates/email/digest.html.eex10
-rw-r--r--lib/pleroma/web/templates/feed/feed/_activity.atom.eex10
-rw-r--r--lib/pleroma/web/templates/feed/feed/_activity.rss.eex13
-rw-r--r--lib/pleroma/web/templates/feed/feed/_author.atom.eex19
-rw-r--r--lib/pleroma/web/templates/feed/feed/_author.rss.eex27
-rw-r--r--lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex70
-rw-r--r--lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex4
-rw-r--r--lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex28
-rw-r--r--lib/pleroma/web/templates/feed/feed/tag.atom.eex34
-rw-r--r--lib/pleroma/web/templates/feed/feed/tag.rss.eex11
-rw-r--r--lib/pleroma/web/templates/feed/feed/user.atom.eex13
-rw-r--r--lib/pleroma/web/templates/feed/feed/user.rss.eex26
-rw-r--r--lib/pleroma/web/templates/layout/app.html.eex2
-rw-r--r--lib/pleroma/web/templates/layout/email.html.eex6
-rw-r--r--lib/pleroma/web/templates/layout/embed.html.eex2
-rw-r--r--lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex2
-rw-r--r--lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex2
-rw-r--r--lib/pleroma/web/templates/masto_fe/index.html.eex35
-rw-r--r--lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex12
-rw-r--r--lib/pleroma/web/templates/o_auth/mfa/totp.html.eex14
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex4
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex8
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex4
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex4
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/register.html.eex28
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/show.html.eex28
-rw-r--r--lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex2
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex2
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/reset.html.eex8
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex8
-rw-r--r--lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex4
-rw-r--r--lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex8
-rw-r--r--lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex10
-rw-r--r--lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex8
-rw-r--r--lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex5
-rw-r--r--lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex10
-rw-r--r--lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex10
-rw-r--r--lib/pleroma/web/translation_helpers.ex2
-rw-r--r--lib/pleroma/web/twitter_api/controller.ex42
-rw-r--r--lib/pleroma/web/twitter_api/controllers/password_controller.ex16
-rw-r--r--lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex8
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex232
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex9
-rw-r--r--lib/pleroma/web/twitter_api/views/password_view.ex3
-rw-r--r--lib/pleroma/web/twitter_api/views/remote_follow_view.ex9
-rw-r--r--lib/pleroma/web/twitter_api/views/token_view.ex2
-rw-r--r--lib/pleroma/web/twitter_api/views/util_view.ex9
-rw-r--r--lib/pleroma/web/uploader_controller.ex2
-rw-r--r--lib/pleroma/web/utils/guards.ex13
-rw-r--r--lib/pleroma/web/utils/params.ex16
-rw-r--r--lib/pleroma/web/views/email_view.ex3
-rw-r--r--lib/pleroma/web/views/embed_view.ex2
-rw-r--r--lib/pleroma/web/views/error_helpers.ex2
-rw-r--r--lib/pleroma/web/views/error_view.ex2
-rw-r--r--lib/pleroma/web/views/layout_view.ex2
-rw-r--r--lib/pleroma/web/views/mailer/subscription_view.ex3
-rw-r--r--lib/pleroma/web/views/manifest_view.ex28
-rw-r--r--lib/pleroma/web/views/masto_fe_view.ex91
-rw-r--r--lib/pleroma/web/views/streamer_view.ex29
-rw-r--r--lib/pleroma/web/web_finger.ex57
-rw-r--r--lib/pleroma/web/web_finger/web_finger_controller.ex2
-rw-r--r--lib/pleroma/web/xml.ex2
-rw-r--r--lib/pleroma/workers/attachments_cleanup_worker.ex5
-rw-r--r--lib/pleroma/workers/background_worker.ex10
-rw-r--r--lib/pleroma/workers/backup_worker.ex30
-rw-r--r--lib/pleroma/workers/cron/digest_emails_worker.ex2
-rw-r--r--lib/pleroma/workers/cron/new_users_digest_worker.ex2
-rw-r--r--lib/pleroma/workers/mailer_worker.ex5
-rw-r--r--lib/pleroma/workers/mute_expire_worker.ex5
-rw-r--r--lib/pleroma/workers/poll_worker.ex48
-rw-r--r--lib/pleroma/workers/publisher_worker.ex5
-rw-r--r--lib/pleroma/workers/purge_expired_activity.ex5
-rw-r--r--lib/pleroma/workers/purge_expired_filter.ex5
-rw-r--r--lib/pleroma/workers/purge_expired_token.ex5
-rw-r--r--lib/pleroma/workers/receiver_worker.ex16
-rw-r--r--lib/pleroma/workers/remote_fetcher_worker.ex5
-rw-r--r--lib/pleroma/workers/scheduled_activity_worker.ex5
-rw-r--r--lib/pleroma/workers/transmogrifier_worker.ex15
-rw-r--r--lib/pleroma/workers/web_pusher_worker.ex5
-rw-r--r--lib/pleroma/workers/worker_helper.ex2
-rw-r--r--lib/pleroma/xml_builder.ex2
616 files changed, 10639 insertions, 4199 deletions
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 53beca5e6..3556aaf9e 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity do
@@ -53,7 +53,7 @@ defmodule Pleroma.Activity do
#
# ```
# |> join(:inner, [activity], o in Object,
- # on: fragment("(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')",
+ # on: fragment("(?->>'id') = associated_object_id((?))",
# o.data, activity.data, activity.data))
# |> preload([activity, object], [object: object])
# ```
@@ -69,9 +69,8 @@ defmodule Pleroma.Activity do
join(query, join_type, [activity], o in Object,
on:
fragment(
- "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
+ "(?->>'id') = associated_object_id(?)",
o.data,
- activity.data,
activity.data
),
as: :object
@@ -292,7 +291,8 @@ defmodule Pleroma.Activity do
get_in_reply_to_activity_from_object(Object.normalize(activity, fetch: false))
end
- def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
+ def normalize(%Activity{data: %{"id" => ap_id}}), do: get_by_ap_id_with_object(ap_id)
+ def normalize(%{"id" => ap_id}), do: get_by_ap_id_with_object(ap_id)
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
def normalize(_), do: nil
@@ -301,7 +301,7 @@ defmodule Pleroma.Activity do
|> Queries.by_object_id()
|> Queries.exclude_type("Delete")
|> select([u], u)
- |> Repo.delete_all()
+ |> Repo.delete_all(timeout: :infinity)
|> elem(1)
|> Enum.find(fn
%{data: %{"type" => "Create", "object" => ap_id}} when is_binary(ap_id) -> ap_id == id
@@ -313,13 +313,15 @@ defmodule Pleroma.Activity do
def delete_all_by_object_ap_id(_), do: nil
- defp purge_web_resp_cache(%Activity{} = activity) do
- %{path: path} = URI.parse(activity.data["id"])
- @cachex.del(:web_resp_cache, path)
+ defp purge_web_resp_cache(%Activity{data: %{"id" => id}} = activity) when is_binary(id) do
+ with %{path: path} <- URI.parse(id) do
+ @cachex.del(:web_resp_cache, path)
+ end
+
activity
end
- defp purge_web_resp_cache(nil), do: nil
+ defp purge_web_resp_cache(activity), do: activity
def follow_accepted?(
%Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity
@@ -359,11 +361,11 @@ defmodule Pleroma.Activity do
end
def restrict_deactivated_users(query) do
- deactivated_users =
- from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
- |> Repo.all()
-
- Activity.Queries.exclude_authors(query, deactivated_users)
+ query
+ |> join(:inner, [activity], user in User,
+ as: :user,
+ on: activity.actor == user.ap_id and user.is_active == true
+ )
end
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
diff --git a/lib/pleroma/activity/html.ex b/lib/pleroma/activity/html.ex
new file mode 100644
index 000000000..706b2d36c
--- /dev/null
+++ b/lib/pleroma/activity/html.ex
@@ -0,0 +1,81 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Activity.HTML do
+ alias Pleroma.HTML
+ alias Pleroma.Object
+
+ @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+
+ # We store a list of cache keys related to an activity in a
+ # separate cache, scrubber_management_cache. It has the same
+ # size as scrubber_cache (see application.ex). Every time we add
+ # a cache to scrubber_cache, we update scrubber_management_cache.
+ #
+ # The most recent write of a certain key in the management cache
+ # is the same as the most recent write of any record related to that
+ # key in the main cache.
+ # Assuming LRW ( https://hexdocs.pm/cachex/Cachex.Policy.LRW.html ),
+ # this means when the management cache is evicted by cachex, all
+ # related records in the main cache will also have been evicted.
+
+ defp get_cache_keys_for(activity_id) do
+ with {:ok, list} when is_list(list) <- @cachex.get(:scrubber_management_cache, activity_id) do
+ list
+ else
+ _ -> []
+ end
+ end
+
+ defp add_cache_key_for(activity_id, additional_key) do
+ current = get_cache_keys_for(activity_id)
+
+ unless additional_key in current do
+ @cachex.put(:scrubber_management_cache, activity_id, [additional_key | current])
+ end
+ end
+
+ def invalidate_cache_for(activity_id) do
+ keys = get_cache_keys_for(activity_id)
+ Enum.map(keys, &@cachex.del(:scrubber_cache, &1))
+ @cachex.del(:scrubber_management_cache, activity_id)
+ end
+
+ def get_cached_scrubbed_html_for_activity(
+ content,
+ scrubbers,
+ activity,
+ key \\ "",
+ callback \\ fn x -> x end
+ ) do
+ key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
+
+ @cachex.fetch!(:scrubber_cache, key, fn _key ->
+ object = Object.normalize(activity, fetch: false)
+
+ add_cache_key_for(activity.id, key)
+ HTML.ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
+ end)
+ end
+
+ def get_cached_stripped_html_for_activity(content, activity, key) do
+ get_cached_scrubbed_html_for_activity(
+ content,
+ FastSanitize.Sanitizer.StripTags,
+ activity,
+ key,
+ &HtmlEntities.decode/1
+ )
+ end
+
+ defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
+ generate_scrubber_signature([scrubber])
+ end
+
+ defp generate_scrubber_signature(scrubbers) do
+ Enum.reduce(scrubbers, "", fn scrubber, signature ->
+ "#{signature}#{to_string(scrubber)}"
+ end)
+ end
+end
diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex
index 7a603a615..8249cbe27 100644
--- a/lib/pleroma/activity/ir/topics.ex
+++ b/lib/pleroma/activity/ir/topics.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Ir.Topics do
@@ -13,6 +13,14 @@ defmodule Pleroma.Activity.Ir.Topics do
|> List.flatten()
end
+ defp generate_topics(%{data: %{"type" => "ChatMessage"}}, %{data: %{"type" => "Delete"}}) do
+ ["user", "user:pleroma_chat"]
+ end
+
+ defp generate_topics(%{data: %{"type" => "ChatMessage"}}, %{data: %{"type" => "Create"}}) do
+ []
+ end
+
defp generate_topics(%{data: %{"type" => "Answer"}}, _) do
[]
end
@@ -21,7 +29,7 @@ defmodule Pleroma.Activity.Ir.Topics do
["user", "list"] ++ visibility_tags(object, activity)
end
- defp visibility_tags(object, activity) do
+ defp visibility_tags(object, %{data: %{"type" => type}} = activity) when type != "Announce" do
case Visibility.get_visibility(activity) do
"public" ->
if activity.local do
@@ -31,6 +39,10 @@ defmodule Pleroma.Activity.Ir.Topics do
end
|> item_creation_tags(object, activity)
+ "local" ->
+ ["public:local"]
+ |> item_creation_tags(object, activity)
+
"direct" ->
["direct"]
@@ -39,6 +51,10 @@ defmodule Pleroma.Activity.Ir.Topics do
end
end
+ defp visibility_tags(_object, _activity) do
+ []
+ end
+
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
tags ++
remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
@@ -63,7 +79,18 @@ defmodule Pleroma.Activity.Ir.Topics do
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
- defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
+ defp attachment_topics(_object, %{local: true} = activity) do
+ case Visibility.get_visibility(activity) do
+ "public" ->
+ ["public:media", "public:local:media"]
+
+ "local" ->
+ ["public:local:media"]
+
+ _ ->
+ []
+ end
+ end
defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex
index 4632651b0..81c44ac05 100644
--- a/lib/pleroma/activity/queries.ex
+++ b/lib/pleroma/activity/queries.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Queries do
@@ -52,8 +52,7 @@ defmodule Pleroma.Activity.Queries do
activity in query,
where:
fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)",
- activity.data,
+ "associated_object_id((?)) = ANY(?)",
activity.data,
^object_ids
)
@@ -64,8 +63,7 @@ defmodule Pleroma.Activity.Queries do
from(activity in query,
where:
fragment(
- "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
- activity.data,
+ "associated_object_id((?)) = ?",
activity.data,
^object_id
)
diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex
index ed898ba4f..0b9b24aa4 100644
--- a/lib/pleroma/activity/search.ex
+++ b/lib/pleroma/activity/search.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Search do
@@ -26,19 +26,23 @@ defmodule Pleroma.Activity.Search do
:plain
end
- Activity
- |> Activity.with_preloaded_object()
- |> Activity.restrict_deactivated_users()
- |> restrict_public()
- |> query_with(index_type, search_query, search_function)
- |> maybe_restrict_local(user)
- |> maybe_restrict_author(author)
- |> maybe_restrict_blocked(user)
- |> Pagination.fetch_paginated(
- %{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum},
- :offset
- )
- |> maybe_fetch(user, search_query)
+ try do
+ Activity
+ |> Activity.with_preloaded_object()
+ |> Activity.restrict_deactivated_users()
+ |> restrict_public(user)
+ |> query_with(index_type, search_query, search_function)
+ |> maybe_restrict_local(user)
+ |> maybe_restrict_author(author)
+ |> maybe_restrict_blocked(user)
+ |> Pagination.fetch_paginated(
+ %{"offset" => offset, "limit" => limit, "skip_order" => index_type == :rum},
+ :offset
+ )
+ |> maybe_fetch(user, search_query)
+ rescue
+ _ -> maybe_fetch([], user, search_query)
+ end
end
def maybe_restrict_author(query, %User{} = author) do
@@ -53,7 +57,19 @@ defmodule Pleroma.Activity.Search do
def maybe_restrict_blocked(query, _), do: query
- defp restrict_public(q) do
+ defp restrict_public(q, user) when not is_nil(user) do
+ intended_recipients = [
+ Pleroma.Constants.as_public(),
+ Pleroma.Web.ActivityPub.Utils.as_local_public()
+ ]
+
+ from([a, o] in q,
+ where: fragment("?->>'type' = 'Create'", a.data),
+ where: fragment("? && ?", ^intended_recipients, a.recipients)
+ )
+ end
+
+ defp restrict_public(q, _user) do
from([a, o] in q,
where: fragment("?->>'type' = 'Create'", a.data),
where: ^Pleroma.Constants.as_public() in a.recipients
@@ -61,10 +77,17 @@ defmodule Pleroma.Activity.Search do
end
defp query_with(q, :gin, search_query, :plain) do
+ %{rows: [[tsc]]} =
+ Ecto.Adapters.SQL.query!(
+ Pleroma.Repo,
+ "select current_setting('default_text_search_config')::regconfig::oid;"
+ )
+
from([a, o] in q,
where:
fragment(
- "to_tsvector(?->>'content') @@ plainto_tsquery(?)",
+ "to_tsvector(?::oid::regconfig, ?->>'content') @@ plainto_tsquery(?)",
+ ^tsc,
o.data,
^search_query
)
@@ -72,10 +95,17 @@ defmodule Pleroma.Activity.Search do
end
defp query_with(q, :gin, search_query, :websearch) do
+ %{rows: [[tsc]]} =
+ Ecto.Adapters.SQL.query!(
+ Pleroma.Repo,
+ "select current_setting('default_text_search_config')::regconfig::oid;"
+ )
+
from([a, o] in q,
where:
fragment(
- "to_tsvector(?->>'content') @@ websearch_to_tsquery(?)",
+ "to_tsvector(?::oid::regconfig, ?->>'content') @@ websearch_to_tsquery(?)",
+ ^tsc,
o.data,
^search_query
)
diff --git a/lib/pleroma/announcement.ex b/lib/pleroma/announcement.ex
new file mode 100644
index 000000000..d97c5e728
--- /dev/null
+++ b/lib/pleroma/announcement.ex
@@ -0,0 +1,160 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Announcement do
+ use Ecto.Schema
+
+ import Ecto.Changeset, only: [cast: 3, validate_required: 2]
+ import Ecto.Query
+
+ alias Pleroma.AnnouncementReadRelationship
+ alias Pleroma.Repo
+
+ @type t :: %__MODULE__{}
+ @primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
+
+ schema "announcements" do
+ field(:data, :map)
+ field(:starts_at, :utc_datetime)
+ field(:ends_at, :utc_datetime)
+ field(:rendered, :map)
+
+ timestamps(type: :utc_datetime)
+ end
+
+ def change(struct, params \\ %{}) do
+ struct
+ |> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered])
+ |> validate_required([:data])
+ end
+
+ defp validate_params(struct, params) do
+ base_data =
+ %{
+ "content" => "",
+ "all_day" => false
+ }
+ |> Map.merge((struct && struct.data) || %{})
+
+ merged_data =
+ Map.merge(base_data, params.data)
+ |> Map.take(["content", "all_day"])
+
+ params
+ |> Map.merge(%{data: merged_data})
+ |> add_rendered_properties()
+ end
+
+ def add_rendered_properties(params) do
+ {content_html, _, _} =
+ Pleroma.Web.CommonAPI.Utils.format_input(params.data["content"], "text/plain",
+ mentions_format: :full
+ )
+
+ rendered = %{
+ "content" => content_html
+ }
+
+ params
+ |> Map.put(:rendered, rendered)
+ end
+
+ def add(params) do
+ changeset = change(%__MODULE__{}, params)
+
+ Repo.insert(changeset)
+ end
+
+ def update(announcement, params) do
+ changeset = change(announcement, params)
+
+ Repo.update(changeset)
+ end
+
+ def list_all do
+ __MODULE__
+ |> Repo.all()
+ end
+
+ def list_paginated(%{limit: limited_number, offset: offset_number}) do
+ __MODULE__
+ |> limit(^limited_number)
+ |> offset(^offset_number)
+ |> Repo.all()
+ end
+
+ def get_by_id(id) do
+ Repo.get_by(__MODULE__, id: id)
+ end
+
+ def delete_by_id(id) do
+ with announcement when not is_nil(announcement) <- get_by_id(id),
+ {:ok, _} <- Repo.delete(announcement) do
+ :ok
+ else
+ _ ->
+ :error
+ end
+ end
+
+ def read_by?(announcement, user) do
+ AnnouncementReadRelationship.exists?(user, announcement)
+ end
+
+ def mark_read_by(announcement, user) do
+ AnnouncementReadRelationship.mark_read(user, announcement)
+ end
+
+ def render_json(announcement, opts \\ []) do
+ extra_params =
+ case Keyword.fetch(opts, :for) do
+ {:ok, user} when not is_nil(user) ->
+ %{read: read_by?(announcement, user)}
+
+ _ ->
+ %{}
+ end
+
+ admin_extra_params =
+ case Keyword.fetch(opts, :admin) do
+ {:ok, true} ->
+ %{pleroma: %{raw_content: announcement.data["content"]}}
+
+ _ ->
+ %{}
+ end
+
+ base = %{
+ id: announcement.id,
+ content: announcement.rendered["content"],
+ starts_at: announcement.starts_at,
+ ends_at: announcement.ends_at,
+ all_day: announcement.data["all_day"],
+ published_at: announcement.inserted_at,
+ updated_at: announcement.updated_at,
+ mentions: [],
+ statuses: [],
+ tags: [],
+ emojis: [],
+ reactions: []
+ }
+
+ base
+ |> Map.merge(extra_params)
+ |> Map.merge(admin_extra_params)
+ end
+
+ # "visible" means:
+ # starts_at < time < ends_at
+ def list_all_visible_when(time) do
+ __MODULE__
+ |> where([a], is_nil(a.starts_at) or a.starts_at < ^time)
+ |> where([a], is_nil(a.ends_at) or a.ends_at > ^time)
+ |> Repo.all()
+ end
+
+ def list_all_visible do
+ list_all_visible_when(DateTime.now("Etc/UTC") |> elem(1))
+ end
+end
diff --git a/lib/pleroma/announcement_read_relationship.ex b/lib/pleroma/announcement_read_relationship.ex
new file mode 100644
index 000000000..9b64404ce
--- /dev/null
+++ b/lib/pleroma/announcement_read_relationship.ex
@@ -0,0 +1,55 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.AnnouncementReadRelationship do
+ use Ecto.Schema
+
+ import Ecto.Changeset
+
+ alias FlakeId.Ecto.CompatType
+ alias Pleroma.Announcement
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ @type t :: %__MODULE__{}
+
+ schema "announcement_read_relationships" do
+ belongs_to(:user, User, type: CompatType)
+ belongs_to(:announcement, Announcement, type: CompatType)
+
+ timestamps(updated_at: false)
+ end
+
+ def mark_read(user, announcement) do
+ %__MODULE__{}
+ |> cast(%{user_id: user.id, announcement_id: announcement.id}, [:user_id, :announcement_id])
+ |> validate_required([:user_id, :announcement_id])
+ |> foreign_key_constraint(:user_id)
+ |> foreign_key_constraint(:announcement_id)
+ |> unique_constraint([:user_id, :announcement_id])
+ |> Repo.insert()
+ end
+
+ def mark_unread(user, announcement) do
+ with relationship <- get(user, announcement),
+ {:exists, true} <- {:exists, not is_nil(relationship)},
+ {:ok, _} <- Repo.delete(relationship) do
+ :ok
+ else
+ {:exists, false} ->
+ :ok
+
+ _ ->
+ :error
+ end
+ end
+
+ def get(user, announcement) do
+ Repo.get_by(__MODULE__, user_id: user.id, announcement_id: announcement.id)
+ end
+
+ def exists?(user, announcement) do
+ not is_nil(get(user, announcement))
+ end
+end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 06d399b2e..e68a3c57e 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Application do
@@ -25,7 +25,7 @@ defmodule Pleroma.Application do
if Process.whereis(Pleroma.Web.Endpoint) do
case Config.get([:http, :user_agent], :default) do
:default ->
- info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
+ info = "#{Pleroma.Web.Endpoint.url()} <#{Config.get([:instance, :email], "")}>"
named_version() <> "; " <> info
custom ->
@@ -61,6 +61,11 @@ defmodule Pleroma.Application do
adapter = Application.get_env(:tesla, :adapter)
+ if match?({Tesla.Adapter.Finch, _}, adapter) do
+ Logger.info("Starting Finch")
+ Finch.start_link(name: MyFinch)
+ end
+
if adapter == Tesla.Adapter.Gun do
if version = Pleroma.OTPVersion.version() do
[major, minor] =
@@ -89,7 +94,8 @@ defmodule Pleroma.Application do
Pleroma.Repo,
Config.TransferTask,
Pleroma.Emoji,
- Pleroma.Web.Plugs.RateLimiter.Supervisor
+ Pleroma.Web.Plugs.RateLimiter.Supervisor,
+ {Task.Supervisor, name: Pleroma.TaskSupervisor}
] ++
cachex_children() ++
http_children(adapter, @mix_env) ++
@@ -102,12 +108,22 @@ defmodule Pleroma.Application do
] ++
task_children(@mix_env) ++
dont_run_in_test(@mix_env) ++
- chat_child(chat_enabled?()) ++
+ shout_child(shout_enabled?()) ++
[Pleroma.Gopher.Server]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options
- opts = [strategy: :one_for_one, name: Pleroma.Supervisor]
+ # If we have a lot of caches, default max_restarts can cause test
+ # resets to fail.
+ # Go for the default 3 unless we're in test
+ max_restarts =
+ if @mix_env == :test do
+ 100
+ else
+ 3
+ end
+
+ opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts]
result = Supervisor.start_link(children, opts)
set_postgres_server_version()
@@ -184,6 +200,7 @@ defmodule Pleroma.Application do
build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000),
build_cachex("scrubber", limit: 2500),
+ build_cachex("scrubber_management", limit: 2500),
build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500),
build_cachex("web_resp", limit: 2500),
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
@@ -192,7 +209,8 @@ defmodule Pleroma.Application do
build_cachex("chat_message_id_idempotency_key",
expiration: chat_message_id_idempotency_key_expiration(),
limit: 500_000
- )
+ ),
+ build_cachex("rel_me", limit: 2500)
]
end
@@ -216,7 +234,7 @@ defmodule Pleroma.Application do
type: :worker
}
- defp chat_enabled?, do: Config.get([:chat, :enabled])
+ defp shout_enabled?, do: Config.get([:shout, :enabled])
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
@@ -233,18 +251,19 @@ defmodule Pleroma.Application do
defp background_migrators do
[
- Pleroma.Migrators.HashtagsTableMigrator
+ Pleroma.Migrators.HashtagsTableMigrator,
+ Pleroma.Migrators.ContextObjectsDeletionMigrator
]
end
- defp chat_child(true) do
+ defp shout_child(true) do
[
- Pleroma.Web.ChatChannel.ChatChannelState,
+ Pleroma.Web.ShoutChannel.ShoutChannelState,
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
]
end
- defp chat_child(_), do: []
+ defp shout_child(_), do: []
defp task_children(:test) do
[
diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex
index 6ef65b263..44b1c1705 100644
--- a/lib/pleroma/application_requirements.ex
+++ b/lib/pleroma/application_requirements.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ApplicationRequirements do
@@ -34,15 +34,16 @@ defmodule Pleroma.ApplicationRequirements do
defp check_welcome_message_config!(:ok) do
if Pleroma.Config.get([:welcome, :email, :enabled], false) and
not Pleroma.Emails.Mailer.enabled?() do
- Logger.error("""
- To send welcome email do you need to enable mail.
- \nconfig :pleroma, Pleroma.Emails.Mailer, enabled: true
- """)
+ Logger.warn("""
+ To send welcome emails, you need to enable the mailer.
+ Welcome emails will NOT be sent with the current config.
- {:error, "The mail disabled."}
- else
- :ok
+ Enable the mailer:
+ config :pleroma, Pleroma.Emails.Mailer, enabled: true
+ """)
end
+
+ :ok
end
defp check_welcome_message_config!(result), do: result
@@ -51,18 +52,21 @@ defmodule Pleroma.ApplicationRequirements do
#
def check_confirmation_accounts!(:ok) do
if Pleroma.Config.get([:instance, :account_activation_required]) &&
- not Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
- Logger.error(
- "Account activation enabled, but no Mailer settings enabled.\n" <>
- "Please set config :pleroma, :instance, account_activation_required: false\n" <>
- "Otherwise setup and enable Mailer."
- )
+ not Pleroma.Emails.Mailer.enabled?() do
+ Logger.warn("""
+ Account activation is required, but the mailer is disabled.
+ Users will NOT be able to confirm their accounts with this config.
+ Either disable account activation or enable the mailer.
- {:error,
- "Account activation enabled, but Mailer is disabled. Cannot send confirmation emails."}
- else
- :ok
+ Disable account activation:
+ config :pleroma, :instance, account_activation_required: false
+
+ Enable the mailer:
+ config :pleroma, Pleroma.Emails.Mailer, enabled: true
+ """)
end
+
+ :ok
end
def check_confirmation_accounts!(result), do: result
@@ -160,9 +164,13 @@ defmodule Pleroma.ApplicationRequirements do
defp check_system_commands!(:ok) do
filter_commands_statuses = [
- check_filter(Pleroma.Upload.Filters.Exiftool, "exiftool"),
- check_filter(Pleroma.Upload.Filters.Mogrify, "mogrify"),
- check_filter(Pleroma.Upload.Filters.Mogrifun, "mogrify")
+ check_filter(Pleroma.Upload.Filter.Exiftool.StripLocation, "exiftool"),
+ check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"),
+ check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
+ check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
+ check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),
+ check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert"),
+ check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
]
preview_proxy_commands_status =
diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex
deleted file mode 100644
index 241fcb53c..000000000
--- a/lib/pleroma/bbs/authenticator.ex
+++ /dev/null
@@ -1,20 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.BBS.Authenticator do
- use Sshd.PasswordAuthenticator
- alias Pleroma.User
- alias Pleroma.Web.Plugs.AuthenticationPlug
-
- def authenticate(username, password) do
- username = to_string(username)
- password = to_string(password)
-
- with %User{} = user <- User.get_by_nickname(username) do
- AuthenticationPlug.checkpw(password, user.password_hash)
- else
- _e -> false
- end
- end
-end
diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex
deleted file mode 100644
index 4a2e255f7..000000000
--- a/lib/pleroma/bbs/handler.ex
+++ /dev/null
@@ -1,151 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.BBS.Handler do
- use Sshd.ShellHandler
- alias Pleroma.Activity
- alias Pleroma.HTML
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
-
- def on_shell(username, _pubkey, _ip, _port) do
- :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!")
- user = Pleroma.User.get_cached_by_nickname(to_string(username))
- Logger.debug("#{inspect(user)}")
- loop(run_state(user: user))
- end
-
- def on_connect(username, ip, port, method) do
- Logger.debug(fn ->
- """
- Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{
- inspect(port)
- } using #{inspect(method)}
- """
- end)
- end
-
- def on_disconnect(username, ip, port) do
- Logger.debug(fn ->
- "Disconnecting SSH shell for #{username} from #{inspect(ip)}:#{inspect(port)}"
- end)
- end
-
- defp loop(state) do
- self_pid = self()
- counter = state.counter
- prefix = state.prefix
- user = state.user
-
- input = spawn(fn -> io_get(self_pid, prefix, counter, user.nickname) end)
- wait_input(state, input)
- end
-
- def puts_activity(activity) do
- status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity})
- IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})")
- IO.puts(HTML.strip_tags(status.content))
- IO.puts("")
- end
-
- def handle_command(state, "help") do
- IO.puts("Available commands:")
- IO.puts("help - This help")
- IO.puts("home - Show the home timeline")
- IO.puts("p <text> - Post the given text")
- IO.puts("r <id> <text> - Reply to the post with the given id")
- IO.puts("quit - Quit")
-
- state
- end
-
- def handle_command(%{user: user} = state, "r " <> text) do
- text = String.trim(text)
- [activity_id, rest] = String.split(text, " ", parts: 2)
-
- with %Activity{} <- Activity.get_by_id(activity_id),
- {:ok, _activity} <-
- CommonAPI.post(user, %{status: rest, in_reply_to_status_id: activity_id}) do
- IO.puts("Replied!")
- else
- _e -> IO.puts("Could not reply...")
- end
-
- state
- end
-
- def handle_command(%{user: user} = state, "p " <> text) do
- text = String.trim(text)
-
- with {:ok, _activity} <- CommonAPI.post(user, %{status: text}) do
- IO.puts("Posted!")
- else
- _e -> IO.puts("Could not post...")
- end
-
- state
- end
-
- def handle_command(state, "home") do
- user = state.user
-
- params =
- %{}
- |> Map.put(:type, ["Create"])
- |> Map.put(:blocking_user, user)
- |> Map.put(:muting_user, user)
- |> Map.put(:user, user)
-
- activities =
- [user.ap_id | Pleroma.User.following(user)]
- |> ActivityPub.fetch_activities(params)
-
- Enum.each(activities, fn activity ->
- puts_activity(activity)
- end)
-
- state
- end
-
- def handle_command(state, command) do
- IO.puts("Unknown command '#{command}'")
- state
- end
-
- defp wait_input(state, input) do
- receive do
- {:input, ^input, "quit\n"} ->
- IO.puts("Exiting...")
-
- {:input, ^input, code} when is_binary(code) ->
- code = String.trim(code)
-
- state = handle_command(state, code)
-
- loop(%{state | counter: state.counter + 1})
-
- {:error, :interrupted} ->
- IO.puts("Caught Ctrl+C...")
- loop(%{state | counter: state.counter + 1})
-
- {:input, ^input, msg} ->
- :ok = Logger.warn("received unknown message: #{inspect(msg)}")
- loop(%{state | counter: state.counter + 1})
- end
- end
-
- defp run_state(opts) do
- %{prefix: "pleroma", counter: 1, user: opts[:user]}
- end
-
- defp io_get(pid, prefix, counter, username) do
- prompt = prompt(prefix, counter, username)
- send(pid, {:input, self(), IO.gets(:stdio, prompt)})
- end
-
- defp prompt(prefix, counter, username) do
- prompt = "#{username}@#{prefix}:#{counter}>"
- prompt <> " "
- end
-end
diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex
index 83cc8e7e1..187749e86 100644
--- a/lib/pleroma/bookmark.ex
+++ b/lib/pleroma/bookmark.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Bookmark do
diff --git a/lib/pleroma/caching.ex b/lib/pleroma/caching.ex
index 02c18564d..eb0588708 100644
--- a/lib/pleroma/caching.ex
+++ b/lib/pleroma/caching.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Caching do
diff --git a/lib/pleroma/captcha.ex b/lib/pleroma/captcha.ex
index bad7b3a66..03910f189 100644
--- a/lib/pleroma/captcha.ex
+++ b/lib/pleroma/captcha.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha do
diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex
index eac6dfa36..e786e28b9 100644
--- a/lib/pleroma/captcha/kocaptcha.ex
+++ b/lib/pleroma/captcha/kocaptcha.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Kocaptcha do
diff --git a/lib/pleroma/captcha/native.ex b/lib/pleroma/captcha/native.ex
index 2c6f64e66..9ba0f30be 100644
--- a/lib/pleroma/captcha/native.ex
+++ b/lib/pleroma/captcha/native.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Native do
diff --git a/lib/pleroma/captcha/service.ex b/lib/pleroma/captcha/service.ex
index a430fafdc..74797605f 100644
--- a/lib/pleroma/captcha/service.ex
+++ b/lib/pleroma/captcha/service.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Service do
diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex
index bacff24b5..fe32ec08c 100644
--- a/lib/pleroma/chat.ex
+++ b/lib/pleroma/chat.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Chat do
diff --git a/lib/pleroma/chat/message_reference.ex b/lib/pleroma/chat/message_reference.ex
index 89537d155..ea65a4a35 100644
--- a/lib/pleroma/chat/message_reference.ex
+++ b/lib/pleroma/chat/message_reference.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Chat.MessageReference do
diff --git a/lib/pleroma/clippy.ex b/lib/pleroma/clippy.ex
index 9c674e075..bcf23ca07 100644
--- a/lib/pleroma/clippy.ex
+++ b/lib/pleroma/clippy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Clippy do
diff --git a/lib/pleroma/config.ex b/lib/pleroma/config.ex
index 54e332595..cf1453c9b 100644
--- a/lib/pleroma/config.ex
+++ b/lib/pleroma/config.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config do
diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex
index 24aa5993b..b53b15d95 100644
--- a/lib/pleroma/config/deprecation_warnings.ex
+++ b/lib/pleroma/config/deprecation_warnings.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.DeprecationWarnings do
@@ -20,6 +20,177 @@ defmodule Pleroma.Config.DeprecationWarnings do
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
]
+ def check_exiftool_filter do
+ filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
+
+ if Pleroma.Upload.Filter.Exiftool in filters do
+ Logger.warn("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+ ```
+ config :pleroma, Pleroma.Upload,
+ filters: [Pleroma.Upload.Filter.Exiftool]
+ ```
+
+ Is now
+
+
+ ```
+ config :pleroma, Pleroma.Upload,
+ filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
+ ```
+ """)
+
+ new_config =
+ filters
+ |> Enum.map(fn
+ Pleroma.Upload.Filter.Exiftool -> Pleroma.Upload.Filter.Exiftool.StripLocation
+ filter -> filter
+ end)
+
+ Config.put([Pleroma.Upload, :filters], new_config)
+
+ :error
+ else
+ :ok
+ end
+ end
+
+ def check_simple_policy_tuples do
+ has_strings =
+ Config.get([:mrf_simple])
+ |> Enum.any?(fn {_, v} -> Enum.any?(v, &is_binary/1) end)
+
+ if has_strings do
+ Logger.warn("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using strings in the SimplePolicy configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+ ```
+ config :pleroma, :mrf_simple,
+ media_removal: ["instance.tld"],
+ media_nsfw: ["instance.tld"],
+ federated_timeline_removal: ["instance.tld"],
+ report_removal: ["instance.tld"],
+ reject: ["instance.tld"],
+ followers_only: ["instance.tld"],
+ accept: ["instance.tld"],
+ avatar_removal: ["instance.tld"],
+ banner_removal: ["instance.tld"],
+ reject_deletes: ["instance.tld"]
+ ```
+
+ Is now
+
+
+ ```
+ config :pleroma, :mrf_simple,
+ media_removal: [{"instance.tld", "Reason for media removal"}],
+ media_nsfw: [{"instance.tld", "Reason for media nsfw"}],
+ federated_timeline_removal: [{"instance.tld", "Reason for federated timeline removal"}],
+ report_removal: [{"instance.tld", "Reason for report removal"}],
+ reject: [{"instance.tld", "Reason for reject"}],
+ followers_only: [{"instance.tld", "Reason for followers only"}],
+ accept: [{"instance.tld", "Reason for accept"}],
+ avatar_removal: [{"instance.tld", "Reason for avatar removal"}],
+ banner_removal: [{"instance.tld", "Reason for banner removal"}],
+ reject_deletes: [{"instance.tld", "Reason for reject deletes"}]
+ ```
+ """)
+
+ new_config =
+ Config.get([:mrf_simple])
+ |> Enum.map(fn {k, v} ->
+ {k,
+ Enum.map(v, fn
+ {instance, reason} -> {instance, reason}
+ instance -> {instance, ""}
+ end)}
+ end)
+
+ Config.put([:mrf_simple], new_config)
+
+ :error
+ else
+ :ok
+ end
+ end
+
+ def check_quarantined_instances_tuples do
+ has_strings = Config.get([:instance, :quarantined_instances]) |> Enum.any?(&is_binary/1)
+
+ if has_strings do
+ Logger.warn("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using strings in the quarantined_instances configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+ ```
+ config :pleroma, :instance,
+ quarantined_instances: ["instance.tld"]
+ ```
+
+ Is now
+
+
+ ```
+ config :pleroma, :instance,
+ quarantined_instances: [{"instance.tld", "Reason for quarantine"}]
+ ```
+ """)
+
+ new_config =
+ Config.get([:instance, :quarantined_instances])
+ |> Enum.map(fn
+ {instance, reason} -> {instance, reason}
+ instance -> {instance, ""}
+ end)
+
+ Config.put([:instance, :quarantined_instances], new_config)
+
+ :error
+ else
+ :ok
+ end
+ end
+
+ def check_transparency_exclusions_tuples do
+ has_strings = Config.get([:mrf, :transparency_exclusions]) |> Enum.any?(&is_binary/1)
+
+ if has_strings do
+ Logger.warn("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using strings in the transparency_exclusions configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
+
+ ```
+ config :pleroma, :mrf,
+ transparency_exclusions: ["instance.tld"]
+ ```
+
+ Is now
+
+
+ ```
+ config :pleroma, :mrf,
+ transparency_exclusions: [{"instance.tld", "Reason to exlude transparency"}]
+ ```
+ """)
+
+ new_config =
+ Config.get([:mrf, :transparency_exclusions])
+ |> Enum.map(fn
+ {instance, reason} -> {instance, reason}
+ instance -> {instance, ""}
+ end)
+
+ Config.put([:mrf, :transparency_exclusions], new_config)
+
+ :error
+ else
+ :ok
+ end
+ end
+
def check_hellthread_threshold do
if Config.get([:mrf_hellthread, :threshold]) do
Logger.warn("""
@@ -34,19 +205,25 @@ defmodule Pleroma.Config.DeprecationWarnings do
end
def warn do
- with :ok <- check_hellthread_threshold(),
- :ok <- check_old_mrf_config(),
- :ok <- check_media_proxy_whitelist_config(),
- :ok <- check_welcome_message_config(),
- :ok <- check_gun_pool_options(),
- :ok <- check_activity_expiration_config(),
- :ok <- check_remote_ip_plug_name(),
- :ok <- check_uploders_s3_public_endpoint() do
- :ok
- else
- _ ->
- :error
- end
+ [
+ check_hellthread_threshold(),
+ check_old_mrf_config(),
+ check_media_proxy_whitelist_config(),
+ check_welcome_message_config(),
+ check_gun_pool_options(),
+ check_activity_expiration_config(),
+ check_remote_ip_plug_name(),
+ check_uploders_s3_public_endpoint(),
+ check_old_chat_shoutbox(),
+ check_quarantined_instances_tuples(),
+ check_transparency_exclusions_tuples(),
+ check_simple_policy_tuples(),
+ check_exiftool_filter()
+ ]
+ |> Enum.reduce(:ok, fn
+ :ok, :ok -> :ok
+ _, _ -> :error
+ end)
end
def check_welcome_message_config do
@@ -134,7 +311,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
warning_preface = """
!!!DEPRECATION WARNING!!!
- Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
+ Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. The setting will not take effect until updated.
"""
updated_config =
@@ -215,4 +392,27 @@ defmodule Pleroma.Config.DeprecationWarnings do
:ok
end
end
+
+ @spec check_old_chat_shoutbox() :: :ok | nil
+ def check_old_chat_shoutbox do
+ instance_config = Pleroma.Config.get([:instance])
+ chat_config = Pleroma.Config.get([:chat]) || []
+
+ use_old_config =
+ Keyword.has_key?(instance_config, :chat_limit) or
+ Keyword.has_key?(chat_config, :enabled)
+
+ if use_old_config do
+ Logger.error("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using the old namespace for the Shoutbox configuration. You need to convert to the new namespace. e.g.,
+ \n* `config :pleroma, :chat, enabled` and `config :pleroma, :instance, chat_limit` are now equal to:
+ \n* `config :pleroma, :shout, enabled` and `config :pleroma, :shout, limit`
+ """)
+
+ :error
+ else
+ :ok
+ end
+ end
end
diff --git a/lib/pleroma/config/getting.ex b/lib/pleroma/config/getting.ex
index 2cc9fe80b..f9b66bba6 100644
--- a/lib/pleroma/config/getting.ex
+++ b/lib/pleroma/config/getting.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Getting do
diff --git a/lib/pleroma/config/helpers.ex b/lib/pleroma/config/helpers.ex
index 9f26c3546..973a75c99 100644
--- a/lib/pleroma/config/helpers.ex
+++ b/lib/pleroma/config/helpers.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Helpers do
diff --git a/lib/pleroma/config/holder.ex b/lib/pleroma/config/holder.ex
index 4d186a854..7822bde89 100644
--- a/lib/pleroma/config/holder.ex
+++ b/lib/pleroma/config/holder.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Holder do
diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex
index b64d06707..bd85eccab 100644
--- a/lib/pleroma/config/loader.ex
+++ b/lib/pleroma/config/loader.ex
@@ -1,11 +1,13 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Loader do
+ # These modules are only being used as keys here (for equality check),
+ # so it's okay to use `Module.concat/1` to have the compiler ignore them.
@reject_keys [
- Pleroma.Repo,
- Pleroma.Web.Endpoint,
+ Module.concat(["Pleroma.Repo"]),
+ Module.concat(["Pleroma.Web.Endpoint"]),
:env,
:configurable_from_database,
:database,
@@ -17,21 +19,10 @@ defmodule Pleroma.Config.Loader do
:tesla
]
- if Code.ensure_loaded?(Config.Reader) do
- @reader Config.Reader
-
- def read(path), do: @reader.read!(path)
- else
- # support for Elixir less than 1.9
- @reader Mix.Config
- def read(path) do
- path
- |> @reader.eval!()
- |> elem(0)
- end
- end
+ @reader Config.Reader
@spec read(Path.t()) :: keyword()
+ def read(path), do: @reader.read!(path)
@spec merge(keyword(), keyword()) :: keyword()
def merge(c1, c2), do: @reader.merge(c1, c2)
diff --git a/lib/pleroma/config/oban.ex b/lib/pleroma/config/oban.ex
index 3e63bca40..483d2bb79 100644
--- a/lib/pleroma/config/oban.ex
+++ b/lib/pleroma/config/oban.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Oban do
@@ -21,9 +21,7 @@ defmodule Pleroma.Config.Oban do
"""
!!!OBAN CONFIG WARNING!!!
You are using old workers in Oban crontab settings, which were removed.
- Please, remove setting from crontab in your config file (prod.secret.exs): #{
- inspect(setting)
- }
+ Please, remove setting from crontab in your config file (prod.secret.exs): #{inspect(setting)}
"""
|> Logger.warn()
diff --git a/lib/pleroma/config/release_runtime_provider.ex b/lib/pleroma/config/release_runtime_provider.ex
index e5e9d3dcd..91e5f1a54 100644
--- a/lib/pleroma/config/release_runtime_provider.ex
+++ b/lib/pleroma/config/release_runtime_provider.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Config.ReleaseRuntimeProvider do
@moduledoc """
Imports runtime config and `{env}.exported_from_db.secret.exs` for releases.
diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex
index aad45aab8..44a984019 100644
--- a/lib/pleroma/config/transfer_task.ex
+++ b/lib/pleroma/config/transfer_task.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.TransferTask do
@@ -13,23 +13,25 @@ defmodule Pleroma.Config.TransferTask do
@type env() :: :test | :benchmark | :dev | :prod
- @reboot_time_keys [
- {:pleroma, :hackney_pools},
- {:pleroma, :chat},
- {:pleroma, Oban},
- {:pleroma, :rate_limit},
- {:pleroma, :markup},
- {:pleroma, :streamer},
- {:pleroma, :pools},
- {:pleroma, :connections_pool}
- ]
-
- @reboot_time_subkeys [
- {:pleroma, Pleroma.Captcha, [:seconds_valid]},
- {:pleroma, Pleroma.Upload, [:proxy_remote]},
- {:pleroma, :instance, [:upload_limit]},
- {:pleroma, :gopher, [:enabled]}
- ]
+ defp reboot_time_keys,
+ do: [
+ {:pleroma, :hackney_pools},
+ {:pleroma, :shout},
+ {:pleroma, Oban},
+ {:pleroma, :rate_limit},
+ {:pleroma, :markup},
+ {:pleroma, :streamer},
+ {:pleroma, :pools},
+ {:pleroma, :connections_pool}
+ ]
+
+ defp reboot_time_subkeys,
+ do: [
+ {:pleroma, Pleroma.Captcha, [:seconds_valid]},
+ {:pleroma, Pleroma.Upload, [:proxy_remote]},
+ {:pleroma, :instance, [:upload_limit]},
+ {:pleroma, :gopher, [:enabled]}
+ ]
def start_link(restart_pleroma? \\ true) do
load_and_update_env([], restart_pleroma?)
@@ -45,7 +47,7 @@ defmodule Pleroma.Config.TransferTask do
{logger, other} =
(Repo.all(ConfigDB) ++ deleted_settings)
|> Enum.map(&merge_with_default/1)
- |> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end)
+ |> Enum.split_with(fn {group, _, _, _} -> group in [:logger] end)
logger
|> Enum.sort()
@@ -102,11 +104,6 @@ defmodule Pleroma.Config.TransferTask do
end
# change logger configuration in runtime, without restart
- defp configure({:quack, key, _, merged}) do
- Logger.configure_backend(Quack.Logger, [{key, merged}])
- :ok = update_env(:quack, key, merged)
- end
-
defp configure({_, :backends, _, merged}) do
# removing current backends
Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1)
@@ -146,9 +143,7 @@ defmodule Pleroma.Config.TransferTask do
rescue
error ->
error_msg =
- "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{
- inspect(value)
- } error: #{inspect(error)}"
+ "updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{inspect(value)} error: #{inspect(error)}"
Logger.warn(error_msg)
@@ -165,12 +160,12 @@ defmodule Pleroma.Config.TransferTask do
end
defp group_and_key_need_reboot?(group, key) do
- Enum.any?(@reboot_time_keys, fn {g, k} -> g == group and k == key end)
+ Enum.any?(reboot_time_keys(), fn {g, k} -> g == group and k == key end)
end
defp group_and_subkey_need_reboot?(group, key, value) do
Keyword.keyword?(value) and
- Enum.any?(@reboot_time_subkeys, fn {g, k, subkeys} ->
+ Enum.any?(reboot_time_subkeys(), fn {g, k, subkeys} ->
g == group and k == key and
Enum.any?(Keyword.keys(value), &(&1 in subkeys))
end)
diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex
index cb57673e3..846cede04 100644
--- a/lib/pleroma/config_db.ex
+++ b/lib/pleroma/config_db.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ConfigDB do
@@ -163,7 +163,6 @@ defmodule Pleroma.ConfigDB do
defp only_full_update?(%ConfigDB{group: group, key: key}) do
full_key_update = [
{:pleroma, :ecto_repos},
- {:quack, :meta},
{:mime, :types},
{:cors_plug, [:max_age, :methods, :expose, :headers]},
{:swarm, :node_blacklist},
@@ -386,7 +385,7 @@ defmodule Pleroma.ConfigDB do
@spec module_name?(String.t()) :: boolean()
def module_name?(string) do
- Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or
+ Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Ueberauth|Swoosh)\./, string) or
string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"]
end
end
diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex
index b24338cc6..cfb405218 100644
--- a/lib/pleroma/constants.ex
+++ b/lib/pleroma/constants.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Constants do
@@ -28,5 +28,45 @@ defmodule Pleroma.Constants do
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
)
- def as_local_public, do: Pleroma.Web.base_url() <> "/#Public"
+ const(status_updatable_fields,
+ do: [
+ "source",
+ "tag",
+ "updated",
+ "emoji",
+ "content",
+ "summary",
+ "sensitive",
+ "attachment",
+ "generator"
+ ]
+ )
+
+ const(updatable_object_types,
+ do: [
+ "Note",
+ "Question",
+ "Audio",
+ "Video",
+ "Event",
+ "Article",
+ "Page"
+ ]
+ )
+
+ const(actor_types,
+ do: [
+ "Application",
+ "Group",
+ "Organization",
+ "Person",
+ "Service"
+ ]
+ )
+
+ # basic regex, just there to weed out potential mistakes
+ # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
+ const(mime_regex,
+ do: ~r/^[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+\/[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+(; .*)?$/
+ )
end
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index 828e27450..42028aa51 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation do
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index e0a3af28b..4ed93e5bd 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation.Participation do
diff --git a/lib/pleroma/conversation/participation/recipient_ship.ex b/lib/pleroma/conversation/participation/recipient_ship.ex
index 094c1a176..d9a0fdff8 100644
--- a/lib/pleroma/conversation/participation/recipient_ship.ex
+++ b/lib/pleroma/conversation/participation/recipient_ship.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation.Participation.RecipientShip do
diff --git a/lib/pleroma/counter_cache.ex b/lib/pleroma/counter_cache.ex
index 1e75d19ae..d2b14bd54 100644
--- a/lib/pleroma/counter_cache.ex
+++ b/lib/pleroma/counter_cache.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.CounterCache do
diff --git a/lib/pleroma/data_migration.ex b/lib/pleroma/data_migration.ex
index 1377af16e..8451678fc 100644
--- a/lib/pleroma/data_migration.ex
+++ b/lib/pleroma/data_migration.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.DataMigration do
@@ -42,4 +42,5 @@ defmodule Pleroma.DataMigration do
end
def populate_hashtags_table, do: get_by_name("populate_hashtags_table")
+ def delete_context_objects, do: get_by_name("delete_context_objects")
end
diff --git a/lib/pleroma/delivery.ex b/lib/pleroma/delivery.ex
index 511d5cf58..5d1853b76 100644
--- a/lib/pleroma/delivery.ex
+++ b/lib/pleroma/delivery.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Delivery do
diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex
index e8a68fd41..6508f1947 100644
--- a/lib/pleroma/docs/generator.ex
+++ b/lib/pleroma/docs/generator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Generator do
diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex
index f22432ea4..05f46f39b 100644
--- a/lib/pleroma/docs/json.ex
+++ b/lib/pleroma/docs/json.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.JSON do
diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex
index 7e54e9d58..949388eb9 100644
--- a/lib/pleroma/docs/markdown.ex
+++ b/lib/pleroma/docs/markdown.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Markdown do
diff --git a/lib/pleroma/docs/translator.ex b/lib/pleroma/docs/translator.ex
new file mode 100644
index 000000000..13e33c87e
--- /dev/null
+++ b/lib/pleroma/docs/translator.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Docs.Translator do
+ require Pleroma.Docs.Translator.Compiler
+ require Pleroma.Web.Gettext
+
+ @before_compile Pleroma.Docs.Translator.Compiler
+end
diff --git a/lib/pleroma/docs/translator/compiler.ex b/lib/pleroma/docs/translator/compiler.ex
new file mode 100644
index 000000000..5d27d9fa2
--- /dev/null
+++ b/lib/pleroma/docs/translator/compiler.ex
@@ -0,0 +1,119 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Docs.Translator.Compiler do
+ @external_resource "config/description.exs"
+ @raw_config Pleroma.Config.Loader.read("config/description.exs")
+ @raw_descriptions @raw_config[:pleroma][:config_description]
+
+ defmacro __before_compile__(_env) do
+ strings =
+ __MODULE__.descriptions()
+ |> __MODULE__.extract_strings()
+
+ quote do
+ def placeholder do
+ unquote do
+ Enum.map(
+ strings,
+ fn {path, type, string} ->
+ ctxt = msgctxt_for(path, type)
+
+ quote do
+ Pleroma.Web.Gettext.dpgettext_noop(
+ "config_descriptions",
+ unquote(ctxt),
+ unquote(string)
+ )
+ end
+ end
+ )
+ end
+ end
+ end
+ end
+
+ def descriptions do
+ Pleroma.Web.ActivityPub.MRF.config_descriptions()
+ |> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end)
+ |> Pleroma.Docs.Generator.convert_to_strings()
+ end
+
+ def extract_strings(descriptions) do
+ descriptions
+ |> Enum.reduce(%{strings: [], path: []}, &process_item/2)
+ |> Map.get(:strings)
+ end
+
+ defp process_item(entity, acc) do
+ current_level =
+ acc
+ |> process_desc(entity)
+ |> process_label(entity)
+
+ process_children(entity, current_level)
+ end
+
+ defp process_desc(acc, %{description: desc} = item) do
+ %{
+ strings: [{acc.path ++ [key_for(item)], "description", desc} | acc.strings],
+ path: acc.path
+ }
+ end
+
+ defp process_desc(acc, _) do
+ acc
+ end
+
+ defp process_label(acc, %{label: label} = item) do
+ %{
+ strings: [{acc.path ++ [key_for(item)], "label", label} | acc.strings],
+ path: acc.path
+ }
+ end
+
+ defp process_label(acc, _) do
+ acc
+ end
+
+ defp process_children(%{children: children} = item, acc) do
+ current_level = Map.put(acc, :path, acc.path ++ [key_for(item)])
+
+ children
+ |> Enum.reduce(current_level, &process_item/2)
+ |> Map.put(:path, acc.path)
+ end
+
+ defp process_children(_, acc) do
+ acc
+ end
+
+ def msgctxt_for(path, type) do
+ "config #{type} at #{Enum.join(path, " > ")}"
+ end
+
+ defp convert_group({_, group}) do
+ group
+ end
+
+ defp convert_group(group) do
+ group
+ end
+
+ def key_for(%{group: group, key: key}) do
+ "#{convert_group(group)}-#{key}"
+ end
+
+ def key_for(%{group: group}) do
+ convert_group(group)
+ end
+
+ def key_for(%{key: key}) do
+ key
+ end
+
+ def key_for(_) do
+ nil
+ end
+end
diff --git a/lib/pleroma/earmark_renderer.ex b/lib/pleroma/earmark_renderer.ex
deleted file mode 100644
index 31cae3c72..000000000
--- a/lib/pleroma/earmark_renderer.ex
+++ /dev/null
@@ -1,256 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-#
-# This file is derived from Earmark, under the following copyright:
-# Copyright © 2014 Dave Thomas, The Pragmatic Programmers
-# SPDX-License-Identifier: Apache-2.0
-# Upstream: https://github.com/pragdave/earmark/blob/master/lib/earmark/html_renderer.ex
-defmodule Pleroma.EarmarkRenderer do
- @moduledoc false
-
- alias Earmark.Block
- alias Earmark.Context
- alias Earmark.HtmlRenderer
- alias Earmark.Options
-
- import Earmark.Inline, only: [convert: 3]
- import Earmark.Helpers.HtmlHelpers
- import Earmark.Message, only: [add_messages_from: 2, get_messages: 1, set_messages: 2]
- import Earmark.Context, only: [append: 2, set_value: 2]
- import Earmark.Options, only: [get_mapper: 1]
-
- @doc false
- def render(blocks, %Context{options: %Options{}} = context) do
- messages = get_messages(context)
-
- {contexts, html} =
- get_mapper(context.options).(
- blocks,
- &render_block(&1, put_in(context.options.messages, []))
- )
- |> Enum.unzip()
-
- all_messages =
- contexts
- |> Enum.reduce(messages, fn ctx, messages1 -> messages1 ++ get_messages(ctx) end)
-
- {put_in(context.options.messages, all_messages), html |> IO.iodata_to_binary()}
- end
-
- #############
- # Paragraph #
- #############
- defp render_block(%Block.Para{lnb: lnb, lines: lines, attrs: attrs}, context) do
- lines = convert(lines, lnb, context)
- add_attrs(lines, "<p>#{lines.value}</p>", attrs, [], lnb)
- end
-
- ########
- # Html #
- ########
- defp render_block(%Block.Html{html: html}, context) do
- {context, html}
- end
-
- defp render_block(%Block.HtmlComment{lines: lines}, context) do
- {context, lines}
- end
-
- defp render_block(%Block.HtmlOneline{html: html}, context) do
- {context, html}
- end
-
- #########
- # Ruler #
- #########
- defp render_block(%Block.Ruler{lnb: lnb, attrs: attrs}, context) do
- add_attrs(context, "<hr />", attrs, [], lnb)
- end
-
- ###########
- # Heading #
- ###########
- defp render_block(
- %Block.Heading{lnb: lnb, level: level, content: content, attrs: attrs},
- context
- ) do
- converted = convert(content, lnb, context)
- html = "<h#{level}>#{converted.value}</h#{level}>"
- add_attrs(converted, html, attrs, [], lnb)
- end
-
- ##############
- # Blockquote #
- ##############
-
- defp render_block(%Block.BlockQuote{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
- {context1, body} = render(blocks, context)
- html = "<blockquote>#{body}</blockquote>"
- add_attrs(context1, html, attrs, [], lnb)
- end
-
- #########
- # Table #
- #########
-
- defp render_block(
- %Block.Table{lnb: lnb, header: header, rows: rows, alignments: aligns, attrs: attrs},
- context
- ) do
- {context1, html} = add_attrs(context, "<table>", attrs, [], lnb)
- context2 = set_value(context1, html)
-
- context3 =
- if header do
- append(add_trs(append(context2, "<thead>"), [header], "th", aligns, lnb), "</thead>")
- else
- # Maybe an error, needed append(context, html)
- context2
- end
-
- context4 = append(add_trs(append(context3, "<tbody>"), rows, "td", aligns, lnb), "</tbody>")
-
- {context4, [context4.value, "</table>"]}
- end
-
- ########
- # Code #
- ########
-
- defp render_block(
- %Block.Code{lnb: lnb, language: language, attrs: attrs} = block,
- %Context{options: options} = context
- ) do
- class =
- if language, do: ~s{ class="#{code_classes(language, options.code_class_prefix)}"}, else: ""
-
- tag = ~s[<pre><code#{class}>]
- lines = options.render_code.(block)
- html = ~s[#{tag}#{lines}</code></pre>]
- add_attrs(context, html, attrs, [], lnb)
- end
-
- #########
- # Lists #
- #########
-
- defp render_block(
- %Block.List{lnb: lnb, type: type, blocks: items, attrs: attrs, start: start},
- context
- ) do
- {context1, content} = render(items, context)
- html = "<#{type}#{start}>#{content}</#{type}>"
- add_attrs(context1, html, attrs, [], lnb)
- end
-
- # format a single paragraph list item, and remove the para tags
- defp render_block(
- %Block.ListItem{lnb: lnb, blocks: blocks, spaced: false, attrs: attrs},
- context
- )
- when length(blocks) == 1 do
- {context1, content} = render(blocks, context)
- content = Regex.replace(~r{</?p>}, content, "")
- html = "<li>#{content}</li>"
- add_attrs(context1, html, attrs, [], lnb)
- end
-
- # format a spaced list item
- defp render_block(%Block.ListItem{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
- {context1, content} = render(blocks, context)
- html = "<li>#{content}</li>"
- add_attrs(context1, html, attrs, [], lnb)
- end
-
- ##################
- # Footnote Block #
- ##################
-
- defp render_block(%Block.FnList{blocks: footnotes}, context) do
- items =
- Enum.map(footnotes, fn note ->
- blocks = append_footnote_link(note)
- %Block.ListItem{attrs: "#fn:#{note.number}", type: :ol, blocks: blocks}
- end)
-
- {context1, html} = render_block(%Block.List{type: :ol, blocks: items}, context)
- {context1, Enum.join([~s[<div class="footnotes">], "<hr />", html, "</div>"])}
- end
-
- #######################################
- # Isolated IALs are rendered as paras #
- #######################################
-
- defp render_block(%Block.Ial{verbatim: verbatim}, context) do
- {context, "<p>{:#{verbatim}}</p>"}
- end
-
- ####################
- # IDDef is ignored #
- ####################
-
- defp render_block(%Block.IdDef{}, context), do: {context, ""}
-
- #####################################
- # And here are the inline renderers #
- #####################################
-
- defdelegate br, to: HtmlRenderer
- defdelegate codespan(text), to: HtmlRenderer
- defdelegate em(text), to: HtmlRenderer
- defdelegate strong(text), to: HtmlRenderer
- defdelegate strikethrough(text), to: HtmlRenderer
-
- defdelegate link(url, text), to: HtmlRenderer
- defdelegate link(url, text, title), to: HtmlRenderer
-
- defdelegate image(path, alt, title), to: HtmlRenderer
-
- defdelegate footnote_link(ref, backref, number), to: HtmlRenderer
-
- # Table rows
- defp add_trs(context, rows, tag, aligns, lnb) do
- numbered_rows =
- rows
- |> Enum.zip(Stream.iterate(lnb, &(&1 + 1)))
-
- numbered_rows
- |> Enum.reduce(context, fn {row, lnb}, ctx ->
- append(add_tds(append(ctx, "<tr>"), row, tag, aligns, lnb), "</tr>")
- end)
- end
-
- defp add_tds(context, row, tag, aligns, lnb) do
- Enum.reduce(1..length(row), context, add_td_fn(row, tag, aligns, lnb))
- end
-
- defp add_td_fn(row, tag, aligns, lnb) do
- fn n, ctx ->
- style =
- case Enum.at(aligns, n - 1, :default) do
- :default -> ""
- align -> " style=\"text-align: #{align}\""
- end
-
- col = Enum.at(row, n - 1)
- converted = convert(col, lnb, set_messages(ctx, []))
- append(add_messages_from(ctx, converted), "<#{tag}#{style}>#{converted.value}</#{tag}>")
- end
- end
-
- ###############################
- # Append Footnote Return Link #
- ###############################
-
- defdelegate append_footnote_link(note), to: HtmlRenderer
- defdelegate append_footnote_link(note, fnlink), to: HtmlRenderer
-
- defdelegate render_code(lines), to: HtmlRenderer
-
- defp code_classes(language, prefix) do
- ["" | String.split(prefix || "")]
- |> Enum.map(fn pfx -> "#{pfx}#{language}" end)
- |> Enum.join(" ")
- end
-end
diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex
index 2a9addabc..a4890b489 100644
--- a/lib/pleroma/ecto_enums.ex
+++ b/lib/pleroma/ecto_enums.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
import EctoEnum
@@ -9,7 +9,9 @@ defenum(Pleroma.UserRelationship.Type,
mute: 2,
reblog_mute: 3,
notification_mute: 4,
- inverse_subscription: 5
+ inverse_subscription: 5,
+ suggestion_dismiss: 6,
+ endorsement: 7
)
defenum(Pleroma.FollowingRelationship.State,
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex
new file mode 100644
index 000000000..1038296e7
--- /dev/null
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri do
+ use Ecto.Type
+
+ def type, do: :string
+
+ def cast(uri) when is_binary(uri) do
+ case URI.parse(uri) do
+ %URI{scheme: nil} -> :error
+ %URI{} -> {:ok, uri}
+ _ -> :error
+ end
+ end
+
+ def cast(_), do: :error
+
+ def dump(data), do: {:ok, data}
+
+ def load(data), do: {:ok, data}
+end
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex
index 8552ae73d..b0258e86d 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/date_time.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime do
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex
index 96674e21f..e0e4449dc 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/emoji.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/mime.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/mime.ex
new file mode 100644
index 000000000..31d51577d
--- /dev/null
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/mime.ex
@@ -0,0 +1,25 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MIME do
+ use Ecto.Type
+
+ require Pleroma.Constants
+
+ def type, do: :string
+
+ def cast(mime) when is_binary(mime) do
+ if mime =~ Pleroma.Constants.mime_regex() do
+ {:ok, mime}
+ else
+ {:ok, "application/octet-stream"}
+ end
+ end
+
+ def cast(_), do: :error
+
+ def dump(data), do: {:ok, data}
+
+ def load(data), do: {:ok, data}
+end
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex
index 45bd6070a..663dc0dee 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID do
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex
index af4b0e527..447d536ed 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/recipients.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do
@@ -13,21 +13,33 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do
cast([object])
end
+ def cast(object) when is_map(object) do
+ case ObjectID.cast(object) do
+ {:ok, data} -> {:ok, [data]}
+ _ -> :error
+ end
+ end
+
def cast(data) when is_list(data) do
- data
- |> Enum.reduce_while({:ok, []}, fn element, {:ok, list} ->
- case ObjectID.cast(element) do
- {:ok, id} ->
- {:cont, {:ok, [id | list]}}
-
- _ ->
- {:halt, :error}
- end
- end)
+ data =
+ data
+ |> Enum.reduce_while([], fn element, list ->
+ case ObjectID.cast(element) do
+ {:ok, id} ->
+ {:cont, [id | list]}
+
+ _ ->
+ {:cont, list}
+ end
+ end)
+ |> Enum.sort()
+ |> Enum.uniq()
+
+ {:ok, data}
end
- def cast(_) do
- :error
+ def cast(data) do
+ {:error, data}
end
def dump(data) do
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex
index d0f5f381f..95bd3ba23 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/safe_text.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText do
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex
index f5b68648c..b8e5c9dad 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/uri.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Uri do
diff --git a/lib/pleroma/ecto_type/config/atom.ex b/lib/pleroma/ecto_type/config/atom.ex
index 3bf0bca5b..c44d655e0 100644
--- a/lib/pleroma/ecto_type/config/atom.ex
+++ b/lib/pleroma/ecto_type/config/atom.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.Config.Atom do
diff --git a/lib/pleroma/ecto_type/config/binary_value.ex b/lib/pleroma/ecto_type/config/binary_value.ex
index 908220a65..4aad0cfce 100644
--- a/lib/pleroma/ecto_type/config/binary_value.ex
+++ b/lib/pleroma/ecto_type/config/binary_value.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.Config.BinaryValue do
diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex
index 5fe74e2f7..372e5529d 100644
--- a/lib/pleroma/emails/admin_email.ex
+++ b/lib/pleroma/emails/admin_email.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.AdminEmail do
@@ -73,7 +73,7 @@ defmodule Pleroma.Emails.AdminEmail do
#{comment_html}
#{statuses_html}
<p>
- <a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/reports/index">View Reports in AdminFE</a>
+ <a href="#{Pleroma.Web.Endpoint.url()}/pleroma/admin/#/reports/index">View Reports in AdminFE</a>
"""
new()
@@ -87,7 +87,7 @@ defmodule Pleroma.Emails.AdminEmail do
html_body = """
<p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
- <a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
+ <a href="#{Pleroma.Web.Endpoint.url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
"""
new()
diff --git a/lib/pleroma/emails/mailer.ex b/lib/pleroma/emails/mailer.ex
index c68550bee..101442130 100644
--- a/lib/pleroma/emails/mailer.ex
+++ b/lib/pleroma/emails/mailer.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.Mailer do
diff --git a/lib/pleroma/emails/new_users_digest_email.ex b/lib/pleroma/emails/new_users_digest_email.ex
index 3552dedae..a9e57c7d4 100644
--- a/lib/pleroma/emails/new_users_digest_email.ex
+++ b/lib/pleroma/emails/new_users_digest_email.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.NewUsersDigestEmail do
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index 52f3d419d..95b963764 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -1,48 +1,104 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.UserEmail do
@moduledoc "User emails"
- use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
+ require Pleroma.Web.Gettext
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.Gettext
alias Pleroma.Web.Router
+ import Swoosh.Email
+ import Phoenix.Swoosh, except: [render_body: 3]
import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
+ def render_body(email, template, assigns \\ %{}) do
+ email
+ |> put_new_layout({Pleroma.Web.LayoutView, :email})
+ |> put_new_view(Pleroma.Web.EmailView)
+ |> Phoenix.Swoosh.render_body(template, assigns)
+ end
+
defp recipient(email, nil), do: email
defp recipient(email, name), do: {name, email}
defp recipient(%User{} = user), do: recipient(user.email, user.name)
@spec welcome(User.t(), map()) :: Swoosh.Email.t()
def welcome(user, opts \\ %{}) do
- new()
- |> to(recipient(user))
- |> from(Map.get(opts, :sender, sender()))
- |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!"))
- |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!"))
- |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!"))
+ Gettext.with_locale_or_default user.language do
+ new()
+ |> to(recipient(user))
+ |> from(Map.get(opts, :sender, sender()))
+ |> subject(
+ Map.get(
+ opts,
+ :subject,
+ Gettext.dpgettext(
+ "static_pages",
+ "welcome email subject",
+ "Welcome to %{instance_name}!",
+ instance_name: instance_name()
+ )
+ )
+ )
+ |> html_body(
+ Map.get(
+ opts,
+ :html,
+ Gettext.dpgettext(
+ "static_pages",
+ "welcome email html body",
+ "Welcome to %{instance_name}!",
+ instance_name: instance_name()
+ )
+ )
+ )
+ |> text_body(
+ Map.get(
+ opts,
+ :text,
+ Gettext.dpgettext(
+ "static_pages",
+ "welcome email text body",
+ "Welcome to %{instance_name}!",
+ instance_name: instance_name()
+ )
+ )
+ )
+ end
end
def password_reset_email(user, token) when is_binary(token) do
- password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
-
- html_body = """
- <h3>Reset your password at #{instance_name()}</h3>
- <p>Someone has requested password change for your account at #{instance_name()}.</p>
- <p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p>
- <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
- """
-
- new()
- |> to(recipient(user))
- |> from(sender())
- |> subject("Password reset")
- |> html_body(html_body)
+ Gettext.with_locale_or_default user.language do
+ password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
+
+ html_body =
+ Gettext.dpgettext(
+ "static_pages",
+ "password reset email body",
+ """
+ <h3>Reset your password at %{instance_name}</h3>
+ <p>Someone has requested password change for your account at %{instance_name}.</p>
+ <p>If it was you, visit the following link to proceed: <a href="%{password_reset_url}">reset password</a>.</p>
+ <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
+ """,
+ instance_name: instance_name(),
+ password_reset_url: password_reset_url
+ )
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext("static_pages", "password reset email subject", "Password reset")
+ )
+ |> html_body(html_body)
+ end
end
def user_invitation_email(
@@ -51,73 +107,136 @@ defmodule Pleroma.Emails.UserEmail do
to_email,
to_name \\ nil
) do
- registration_url =
- Router.Helpers.redirect_url(
- Endpoint,
- :registration_page,
- user_invite_token.token
- )
+ Gettext.with_locale_or_default user.language do
+ registration_url =
+ Router.Helpers.redirect_url(
+ Endpoint,
+ :registration_page,
+ user_invite_token.token
+ )
+
+ html_body =
+ Gettext.dpgettext(
+ "static_pages",
+ "user invitation email body",
+ """
+ <h3>You are invited to %{instance_name}</h3>
+ <p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>
+ <p>Click the following link to register: <a href="%{registration_url}">accept invitation</a>.</p>
+ """,
+ instance_name: instance_name(),
+ inviter_name: user.name,
+ registration_url: registration_url
+ )
- html_body = """
- <h3>You are invited to #{instance_name()}</h3>
- <p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p>
- <p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p>
- """
-
- new()
- |> to(recipient(to_email, to_name))
- |> from(sender())
- |> subject("Invitation to #{instance_name()}")
- |> html_body(html_body)
+ new()
+ |> to(recipient(to_email, to_name))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext(
+ "static_pages",
+ "user invitation email subject",
+ "Invitation to %{instance_name}",
+ instance_name: instance_name()
+ )
+ )
+ |> html_body(html_body)
+ end
end
def account_confirmation_email(user) do
- confirmation_url =
- Router.Helpers.confirm_email_url(
- Endpoint,
- :confirm_email,
- user.id,
- to_string(user.confirmation_token)
- )
+ Gettext.with_locale_or_default user.language do
+ confirmation_url =
+ Router.Helpers.confirm_email_url(
+ Endpoint,
+ :confirm_email,
+ user.id,
+ to_string(user.confirmation_token)
+ )
+
+ html_body =
+ Gettext.dpgettext(
+ "static_pages",
+ "confirmation email body",
+ """
+ <h3>Thank you for registering on %{instance_name}</h3>
+ <p>Email confirmation is required to activate the account.</p>
+ <p>Please click the following link to <a href="%{confirmation_url}">activate your account</a>.</p>
+ """,
+ instance_name: instance_name(),
+ confirmation_url: confirmation_url
+ )
- html_body = """
- <h3>Thank you for registering on #{instance_name()}</h3>
- <p>Email confirmation is required to activate the account.</p>
- <p>Please click the following link to <a href="#{confirmation_url}">activate your account</a>.</p>
- """
-
- new()
- |> to(recipient(user))
- |> from(sender())
- |> subject("#{instance_name()} account confirmation")
- |> html_body(html_body)
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext(
+ "static_pages",
+ "confirmation email subject",
+ "%{instance_name} account confirmation",
+ instance_name: instance_name()
+ )
+ )
+ |> html_body(html_body)
+ end
end
def approval_pending_email(user) do
- html_body = """
- <h3>Awaiting Approval</h3>
- <p>Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.</p>
- """
-
- new()
- |> to(recipient(user))
- |> from(sender())
- |> subject("Your account is awaiting approval")
- |> html_body(html_body)
+ Gettext.with_locale_or_default user.language do
+ html_body =
+ Gettext.dpgettext(
+ "static_pages",
+ "approval pending email body",
+ """
+ <h3>Awaiting Approval</h3>
+ <p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>
+ """,
+ instance_name: instance_name()
+ )
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext(
+ "static_pages",
+ "approval pending email subject",
+ "Your account is awaiting approval"
+ )
+ )
+ |> html_body(html_body)
+ end
end
def successful_registration_email(user) do
- html_body = """
- <h3>Hello @#{user.nickname},</h3>
- <p>Your account at #{instance_name()} has been registered successfully.</p>
- <p>No further action is required to activate your account.</p>
- """
-
- new()
- |> to(recipient(user))
- |> from(sender())
- |> subject("Account registered on #{instance_name()}")
- |> html_body(html_body)
+ Gettext.with_locale_or_default user.language do
+ html_body =
+ Gettext.dpgettext(
+ "static_pages",
+ "successful registration email body",
+ """
+ <h3>Hello @%{nickname},</h3>
+ <p>Your account at %{instance_name} has been registered successfully.</p>
+ <p>No further action is required to activate your account.</p>
+ """,
+ nickname: user.nickname,
+ instance_name: instance_name()
+ )
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext(
+ "static_pages",
+ "successful registration email subject",
+ "Account registered on %{instance_name}",
+ instance_name: instance_name()
+ )
+ )
+ |> html_body(html_body)
+ end
end
@doc """
@@ -127,69 +246,78 @@ defmodule Pleroma.Emails.UserEmail do
"""
@spec digest_email(User.t()) :: Swoosh.Email.t() | nil
def digest_email(user) do
- notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
-
- mentions =
- notifications
- |> Enum.filter(&(&1.activity.data["type"] == "Create"))
- |> Enum.map(fn notification ->
- object = Pleroma.Object.normalize(notification.activity, fetch: false)
-
- if not is_nil(object) do
- object = update_in(object.data["content"], &format_links/1)
-
- %{
- data: notification,
- object: object,
- from: User.get_by_ap_id(notification.activity.actor)
- }
- end
- end)
- |> Enum.filter(& &1)
-
- followers =
- notifications
- |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
- |> Enum.map(fn notification ->
- from = User.get_by_ap_id(notification.activity.actor)
-
- if not is_nil(from) do
- %{
- data: notification,
- object: Pleroma.Object.normalize(notification.activity, fetch: false),
- from: User.get_by_ap_id(notification.activity.actor)
- }
- end
- end)
- |> Enum.filter(& &1)
-
- unless Enum.empty?(mentions) do
- styling = Config.get([__MODULE__, :styling])
- logo = Config.get([__MODULE__, :logo])
-
- html_data = %{
- instance: instance_name(),
- user: user,
- mentions: mentions,
- followers: followers,
- unsubscribe_link: unsubscribe_url(user, "digest"),
- styling: styling
- }
-
- logo_path =
- if is_nil(logo) do
- Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
- else
- Path.join(Config.get([:instance, :static_dir]), logo)
- end
-
- new()
- |> to(recipient(user))
- |> from(sender())
- |> subject("Your digest from #{instance_name()}")
- |> put_layout(false)
- |> render_body("digest.html", html_data)
- |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
+ Gettext.with_locale_or_default user.language do
+ notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
+
+ mentions =
+ notifications
+ |> Enum.filter(&(&1.activity.data["type"] == "Create"))
+ |> Enum.map(fn notification ->
+ object = Pleroma.Object.normalize(notification.activity, fetch: false)
+
+ if not is_nil(object) do
+ object = update_in(object.data["content"], &format_links/1)
+
+ %{
+ data: notification,
+ object: object,
+ from: User.get_by_ap_id(notification.activity.actor)
+ }
+ end
+ end)
+ |> Enum.filter(& &1)
+
+ followers =
+ notifications
+ |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
+ |> Enum.map(fn notification ->
+ from = User.get_by_ap_id(notification.activity.actor)
+
+ if not is_nil(from) do
+ %{
+ data: notification,
+ object: Pleroma.Object.normalize(notification.activity, fetch: false),
+ from: User.get_by_ap_id(notification.activity.actor)
+ }
+ end
+ end)
+ |> Enum.filter(& &1)
+
+ unless Enum.empty?(mentions) do
+ styling = Config.get([__MODULE__, :styling])
+ logo = Config.get([__MODULE__, :logo])
+
+ html_data = %{
+ instance: instance_name(),
+ user: user,
+ mentions: mentions,
+ followers: followers,
+ unsubscribe_link: unsubscribe_url(user, "digest"),
+ styling: styling
+ }
+
+ logo_path =
+ if is_nil(logo) do
+ Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
+ else
+ Path.join(Config.get([:instance, :static_dir]), logo)
+ end
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext(
+ "static_pages",
+ "digest email subject",
+ "Your digest from %{instance_name}",
+ instance_name: instance_name()
+ )
+ )
+ |> put_layout(false)
+ |> render_body("digest.html", html_data)
+ |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
+ end
end
end
@@ -219,27 +347,47 @@ defmodule Pleroma.Emails.UserEmail do
def backup_is_ready_email(backup, admin_user_id \\ nil) do
%{user: user} = Pleroma.Repo.preload(backup, :user)
- download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
-
- html_body =
- if is_nil(admin_user_id) do
- """
- <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
- <p><a href="#{download_url}">#{download_url}</a></p>
- """
- else
- admin = Pleroma.Repo.get(User, admin_user_id)
-
- """
- <p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
- <p><a href="#{download_url}">#{download_url}</a></p>
- """
- end
- new()
- |> to(recipient(user))
- |> from(sender())
- |> subject("Your account archive is ready")
- |> html_body(html_body)
+ Gettext.with_locale_or_default user.language do
+ download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
+
+ html_body =
+ if is_nil(admin_user_id) do
+ Gettext.dpgettext(
+ "static_pages",
+ "account archive email body - self-requested",
+ """
+ <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
+ <p><a href="%{download_url}">%{download_url}</a></p>
+ """,
+ download_url: download_url
+ )
+ else
+ admin = Pleroma.Repo.get(User, admin_user_id)
+
+ Gettext.dpgettext(
+ "static_pages",
+ "account archive email body - admin requested",
+ """
+ <p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
+ <p><a href="%{download_url}">%{download_url}</a></p>
+ """,
+ admin_nickname: admin.nickname,
+ download_url: download_url
+ )
+ end
+
+ new()
+ |> to(recipient(user))
+ |> from(sender())
+ |> subject(
+ Gettext.dpgettext(
+ "static_pages",
+ "account archive email subject",
+ "Your account archive is ready"
+ )
+ )
+ |> html_body(html_body)
+ end
end
end
diff --git a/lib/pleroma/emoji-test.txt b/lib/pleroma/emoji-test.txt
index d3c6d12bd..87d093d64 100644
--- a/lib/pleroma/emoji-test.txt
+++ b/lib/pleroma/emoji-test.txt
@@ -1,13 +1,13 @@
# emoji-test.txt
-# Date: 2020-09-12, 22:19:50 GMT
-# © 2020 Unicode®, Inc.
+# Date: 2022-08-12, 20:24:39 GMT
+# © 2022 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
#
# Emoji Keyboard/Display Test Data for UTS #51
-# Version: 13.1
+# Version: 15.0
#
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
#
# This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed.
# Format: code points; status # emoji name
@@ -43,6 +43,7 @@
1F602 ; fully-qualified # 😂 E0.6 face with tears of joy
1F642 ; fully-qualified # 🙂 E1.0 slightly smiling face
1F643 ; fully-qualified # 🙃 E1.0 upside-down face
+1FAE0 ; fully-qualified # 🫠 E14.0 melting face
1F609 ; fully-qualified # 😉 E0.6 winking face
1F60A ; fully-qualified # 😊 E0.6 smiling face with smiling eyes
1F607 ; fully-qualified # 😇 E1.0 smiling face with halo
@@ -68,10 +69,13 @@
1F911 ; fully-qualified # 🤑 E1.0 money-mouth face
# subgroup: face-hand
-1F917 ; fully-qualified # 🤗 E1.0 hugging face
+1F917 ; fully-qualified # 🤗 E1.0 smiling face with open hands
1F92D ; fully-qualified # 🤭 E5.0 face with hand over mouth
+1FAE2 ; fully-qualified # 🫢 E14.0 face with open eyes and hand over mouth
+1FAE3 ; fully-qualified # 🫣 E14.0 face with peeking eye
1F92B ; fully-qualified # 🤫 E5.0 shushing face
1F914 ; fully-qualified # 🤔 E1.0 thinking face
+1FAE1 ; fully-qualified # 🫡 E14.0 saluting face
# subgroup: face-neutral-skeptical
1F910 ; fully-qualified # 🤐 E1.0 zipper-mouth face
@@ -79,6 +83,7 @@
1F610 ; fully-qualified # 😐 E0.7 neutral face
1F611 ; fully-qualified # 😑 E1.0 expressionless face
1F636 ; fully-qualified # 😶 E1.0 face without mouth
+1FAE5 ; fully-qualified # 🫥 E14.0 dotted line face
1F636 200D 1F32B FE0F ; fully-qualified # 😶‍🌫️ E13.1 face in clouds
1F636 200D 1F32B ; minimally-qualified # 😶‍🌫 E13.1 face in clouds
1F60F ; fully-qualified # 😏 E0.6 smirking face
@@ -87,6 +92,7 @@
1F62C ; fully-qualified # 😬 E1.0 grimacing face
1F62E 200D 1F4A8 ; fully-qualified # 😮‍💨 E13.1 face exhaling
1F925 ; fully-qualified # 🤥 E3.0 lying face
+1FAE8 ; fully-qualified # 🫨 E15.0 shaking face
# subgroup: face-sleepy
1F60C ; fully-qualified # 😌 E0.6 relieved face
@@ -105,7 +111,7 @@
1F975 ; fully-qualified # 🥵 E11.0 hot face
1F976 ; fully-qualified # 🥶 E11.0 cold face
1F974 ; fully-qualified # 🥴 E11.0 woozy face
-1F635 ; fully-qualified # 😵 E0.6 knocked-out face
+1F635 ; fully-qualified # 😵 E0.6 face with crossed-out eyes
1F635 200D 1F4AB ; fully-qualified # 😵‍💫 E13.1 face with spiral eyes
1F92F ; fully-qualified # 🤯 E5.0 exploding head
@@ -121,6 +127,7 @@
# subgroup: face-concerned
1F615 ; fully-qualified # 😕 E1.0 confused face
+1FAE4 ; fully-qualified # 🫤 E14.0 face with diagonal mouth
1F61F ; fully-qualified # 😟 E1.0 worried face
1F641 ; fully-qualified # 🙁 E1.0 slightly frowning face
2639 FE0F ; fully-qualified # ☹️ E0.7 frowning face
@@ -130,6 +137,7 @@
1F632 ; fully-qualified # 😲 E0.6 astonished face
1F633 ; fully-qualified # 😳 E0.6 flushed face
1F97A ; fully-qualified # 🥺 E11.0 pleading face
+1F979 ; fully-qualified # 🥹 E14.0 face holding back tears
1F626 ; fully-qualified # 😦 E1.0 frowning face with open mouth
1F627 ; fully-qualified # 😧 E1.0 anguished face
1F628 ; fully-qualified # 😨 E0.6 fearful face
@@ -148,7 +156,7 @@
# subgroup: face-negative
1F624 ; fully-qualified # 😤 E0.6 face with steam from nose
-1F621 ; fully-qualified # 😡 E0.6 pouting face
+1F621 ; fully-qualified # 😡 E0.6 enraged face
1F620 ; fully-qualified # 😠 E0.6 angry face
1F92C ; fully-qualified # 🤬 E5.0 face with symbols on mouth
1F608 ; fully-qualified # 😈 E1.0 smiling face with horns
@@ -183,8 +191,7 @@
1F649 ; fully-qualified # 🙉 E0.6 hear-no-evil monkey
1F64A ; fully-qualified # 🙊 E0.6 speak-no-evil monkey
-# subgroup: emotion
-1F48B ; fully-qualified # 💋 E0.6 kiss mark
+# subgroup: heart
1F48C ; fully-qualified # 💌 E0.6 love letter
1F498 ; fully-qualified # 💘 E0.6 heart with arrow
1F49D ; fully-qualified # 💝 E0.6 heart with ribbon
@@ -203,14 +210,20 @@
2764 200D 1FA79 ; unqualified # ❤‍🩹 E13.1 mending heart
2764 FE0F ; fully-qualified # ❤️ E0.6 red heart
2764 ; unqualified # ❤ E0.6 red heart
+1FA77 ; fully-qualified # 🩷 E15.0 pink heart
1F9E1 ; fully-qualified # 🧡 E5.0 orange heart
1F49B ; fully-qualified # 💛 E0.6 yellow heart
1F49A ; fully-qualified # 💚 E0.6 green heart
1F499 ; fully-qualified # 💙 E0.6 blue heart
+1FA75 ; fully-qualified # 🩵 E15.0 light blue heart
1F49C ; fully-qualified # 💜 E0.6 purple heart
1F90E ; fully-qualified # 🤎 E12.0 brown heart
1F5A4 ; fully-qualified # 🖤 E3.0 black heart
+1FA76 ; fully-qualified # 🩶 E15.0 grey heart
1F90D ; fully-qualified # 🤍 E12.0 white heart
+
+# subgroup: emotion
+1F48B ; fully-qualified # 💋 E0.6 kiss mark
1F4AF ; fully-qualified # 💯 E0.6 hundred points
1F4A2 ; fully-qualified # 💢 E0.6 anger symbol
1F4A5 ; fully-qualified # 💥 E0.6 collision
@@ -219,21 +232,20 @@
1F4A8 ; fully-qualified # 💨 E0.6 dashing away
1F573 FE0F ; fully-qualified # 🕳️ E0.7 hole
1F573 ; unqualified # 🕳 E0.7 hole
-1F4A3 ; fully-qualified # 💣 E0.6 bomb
1F4AC ; fully-qualified # 💬 E0.6 speech balloon
1F441 FE0F 200D 1F5E8 FE0F ; fully-qualified # 👁️‍🗨️ E2.0 eye in speech bubble
1F441 200D 1F5E8 FE0F ; unqualified # 👁‍🗨️ E2.0 eye in speech bubble
-1F441 FE0F 200D 1F5E8 ; unqualified # 👁️‍🗨 E2.0 eye in speech bubble
+1F441 FE0F 200D 1F5E8 ; minimally-qualified # 👁️‍🗨 E2.0 eye in speech bubble
1F441 200D 1F5E8 ; unqualified # 👁‍🗨 E2.0 eye in speech bubble
1F5E8 FE0F ; fully-qualified # 🗨️ E2.0 left speech bubble
1F5E8 ; unqualified # 🗨 E2.0 left speech bubble
1F5EF FE0F ; fully-qualified # 🗯️ E0.7 right anger bubble
1F5EF ; unqualified # 🗯 E0.7 right anger bubble
1F4AD ; fully-qualified # 💭 E1.0 thought balloon
-1F4A4 ; fully-qualified # 💤 E0.6 zzz
+1F4A4 ; fully-qualified # 💤 E0.6 ZZZ
-# Smileys & Emotion subtotal: 170
-# Smileys & Emotion subtotal: 170 w/o modifiers
+# Smileys & Emotion subtotal: 180
+# Smileys & Emotion subtotal: 180 w/o modifiers
# group: People & Body
@@ -269,6 +281,42 @@
1F596 1F3FD ; fully-qualified # 🖖🏽 E1.0 vulcan salute: medium skin tone
1F596 1F3FE ; fully-qualified # 🖖🏾 E1.0 vulcan salute: medium-dark skin tone
1F596 1F3FF ; fully-qualified # 🖖🏿 E1.0 vulcan salute: dark skin tone
+1FAF1 ; fully-qualified # 🫱 E14.0 rightwards hand
+1FAF1 1F3FB ; fully-qualified # 🫱🏻 E14.0 rightwards hand: light skin tone
+1FAF1 1F3FC ; fully-qualified # 🫱🏼 E14.0 rightwards hand: medium-light skin tone
+1FAF1 1F3FD ; fully-qualified # 🫱🏽 E14.0 rightwards hand: medium skin tone
+1FAF1 1F3FE ; fully-qualified # 🫱🏾 E14.0 rightwards hand: medium-dark skin tone
+1FAF1 1F3FF ; fully-qualified # 🫱🏿 E14.0 rightwards hand: dark skin tone
+1FAF2 ; fully-qualified # 🫲 E14.0 leftwards hand
+1FAF2 1F3FB ; fully-qualified # 🫲🏻 E14.0 leftwards hand: light skin tone
+1FAF2 1F3FC ; fully-qualified # 🫲🏼 E14.0 leftwards hand: medium-light skin tone
+1FAF2 1F3FD ; fully-qualified # 🫲🏽 E14.0 leftwards hand: medium skin tone
+1FAF2 1F3FE ; fully-qualified # 🫲🏾 E14.0 leftwards hand: medium-dark skin tone
+1FAF2 1F3FF ; fully-qualified # 🫲🏿 E14.0 leftwards hand: dark skin tone
+1FAF3 ; fully-qualified # 🫳 E14.0 palm down hand
+1FAF3 1F3FB ; fully-qualified # 🫳🏻 E14.0 palm down hand: light skin tone
+1FAF3 1F3FC ; fully-qualified # 🫳🏼 E14.0 palm down hand: medium-light skin tone
+1FAF3 1F3FD ; fully-qualified # 🫳🏽 E14.0 palm down hand: medium skin tone
+1FAF3 1F3FE ; fully-qualified # 🫳🏾 E14.0 palm down hand: medium-dark skin tone
+1FAF3 1F3FF ; fully-qualified # 🫳🏿 E14.0 palm down hand: dark skin tone
+1FAF4 ; fully-qualified # 🫴 E14.0 palm up hand
+1FAF4 1F3FB ; fully-qualified # 🫴🏻 E14.0 palm up hand: light skin tone
+1FAF4 1F3FC ; fully-qualified # 🫴🏼 E14.0 palm up hand: medium-light skin tone
+1FAF4 1F3FD ; fully-qualified # 🫴🏽 E14.0 palm up hand: medium skin tone
+1FAF4 1F3FE ; fully-qualified # 🫴🏾 E14.0 palm up hand: medium-dark skin tone
+1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone
+1FAF7 ; fully-qualified # 🫷 E15.0 leftwards pushing hand
+1FAF7 1F3FB ; fully-qualified # 🫷🏻 E15.0 leftwards pushing hand: light skin tone
+1FAF7 1F3FC ; fully-qualified # 🫷🏼 E15.0 leftwards pushing hand: medium-light skin tone
+1FAF7 1F3FD ; fully-qualified # 🫷🏽 E15.0 leftwards pushing hand: medium skin tone
+1FAF7 1F3FE ; fully-qualified # 🫷🏾 E15.0 leftwards pushing hand: medium-dark skin tone
+1FAF7 1F3FF ; fully-qualified # 🫷🏿 E15.0 leftwards pushing hand: dark skin tone
+1FAF8 ; fully-qualified # 🫸 E15.0 rightwards pushing hand
+1FAF8 1F3FB ; fully-qualified # 🫸🏻 E15.0 rightwards pushing hand: light skin tone
+1FAF8 1F3FC ; fully-qualified # 🫸🏼 E15.0 rightwards pushing hand: medium-light skin tone
+1FAF8 1F3FD ; fully-qualified # 🫸🏽 E15.0 rightwards pushing hand: medium skin tone
+1FAF8 1F3FE ; fully-qualified # 🫸🏾 E15.0 rightwards pushing hand: medium-dark skin tone
+1FAF8 1F3FF ; fully-qualified # 🫸🏿 E15.0 rightwards pushing hand: dark skin tone
# subgroup: hand-fingers-partial
1F44C ; fully-qualified # 👌 E0.6 OK hand
@@ -302,6 +350,12 @@
1F91E 1F3FD ; fully-qualified # 🤞🏽 E3.0 crossed fingers: medium skin tone
1F91E 1F3FE ; fully-qualified # 🤞🏾 E3.0 crossed fingers: medium-dark skin tone
1F91E 1F3FF ; fully-qualified # 🤞🏿 E3.0 crossed fingers: dark skin tone
+1FAF0 ; fully-qualified # 🫰 E14.0 hand with index finger and thumb crossed
+1FAF0 1F3FB ; fully-qualified # 🫰🏻 E14.0 hand with index finger and thumb crossed: light skin tone
+1FAF0 1F3FC ; fully-qualified # 🫰🏼 E14.0 hand with index finger and thumb crossed: medium-light skin tone
+1FAF0 1F3FD ; fully-qualified # 🫰🏽 E14.0 hand with index finger and thumb crossed: medium skin tone
+1FAF0 1F3FE ; fully-qualified # 🫰🏾 E14.0 hand with index finger and thumb crossed: medium-dark skin tone
+1FAF0 1F3FF ; fully-qualified # 🫰🏿 E14.0 hand with index finger and thumb crossed: dark skin tone
1F91F ; fully-qualified # 🤟 E5.0 love-you gesture
1F91F 1F3FB ; fully-qualified # 🤟🏻 E5.0 love-you gesture: light skin tone
1F91F 1F3FC ; fully-qualified # 🤟🏼 E5.0 love-you gesture: medium-light skin tone
@@ -359,6 +413,12 @@
261D 1F3FD ; fully-qualified # ☝🏽 E1.0 index pointing up: medium skin tone
261D 1F3FE ; fully-qualified # ☝🏾 E1.0 index pointing up: medium-dark skin tone
261D 1F3FF ; fully-qualified # ☝🏿 E1.0 index pointing up: dark skin tone
+1FAF5 ; fully-qualified # 🫵 E14.0 index pointing at the viewer
+1FAF5 1F3FB ; fully-qualified # 🫵🏻 E14.0 index pointing at the viewer: light skin tone
+1FAF5 1F3FC ; fully-qualified # 🫵🏼 E14.0 index pointing at the viewer: medium-light skin tone
+1FAF5 1F3FD ; fully-qualified # 🫵🏽 E14.0 index pointing at the viewer: medium skin tone
+1FAF5 1F3FE ; fully-qualified # 🫵🏾 E14.0 index pointing at the viewer: medium-dark skin tone
+1FAF5 1F3FF ; fully-qualified # 🫵🏿 E14.0 index pointing at the viewer: dark skin tone
# subgroup: hand-fingers-closed
1F44D ; fully-qualified # 👍 E0.6 thumbs up
@@ -411,6 +471,12 @@
1F64C 1F3FD ; fully-qualified # 🙌🏽 E1.0 raising hands: medium skin tone
1F64C 1F3FE ; fully-qualified # 🙌🏾 E1.0 raising hands: medium-dark skin tone
1F64C 1F3FF ; fully-qualified # 🙌🏿 E1.0 raising hands: dark skin tone
+1FAF6 ; fully-qualified # 🫶 E14.0 heart hands
+1FAF6 1F3FB ; fully-qualified # 🫶🏻 E14.0 heart hands: light skin tone
+1FAF6 1F3FC ; fully-qualified # 🫶🏼 E14.0 heart hands: medium-light skin tone
+1FAF6 1F3FD ; fully-qualified # 🫶🏽 E14.0 heart hands: medium skin tone
+1FAF6 1F3FE ; fully-qualified # 🫶🏾 E14.0 heart hands: medium-dark skin tone
+1FAF6 1F3FF ; fully-qualified # 🫶🏿 E14.0 heart hands: dark skin tone
1F450 ; fully-qualified # 👐 E0.6 open hands
1F450 1F3FB ; fully-qualified # 👐🏻 E1.0 open hands: light skin tone
1F450 1F3FC ; fully-qualified # 👐🏼 E1.0 open hands: medium-light skin tone
@@ -424,6 +490,31 @@
1F932 1F3FE ; fully-qualified # 🤲🏾 E5.0 palms up together: medium-dark skin tone
1F932 1F3FF ; fully-qualified # 🤲🏿 E5.0 palms up together: dark skin tone
1F91D ; fully-qualified # 🤝 E3.0 handshake
+1F91D 1F3FB ; fully-qualified # 🤝🏻 E14.0 handshake: light skin tone
+1F91D 1F3FC ; fully-qualified # 🤝🏼 E14.0 handshake: medium-light skin tone
+1F91D 1F3FD ; fully-qualified # 🤝🏽 E14.0 handshake: medium skin tone
+1F91D 1F3FE ; fully-qualified # 🤝🏾 E14.0 handshake: medium-dark skin tone
+1F91D 1F3FF ; fully-qualified # 🤝🏿 E14.0 handshake: dark skin tone
+1FAF1 1F3FB 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏻‍🫲🏼 E14.0 handshake: light skin tone, medium-light skin tone
+1FAF1 1F3FB 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏻‍🫲🏽 E14.0 handshake: light skin tone, medium skin tone
+1FAF1 1F3FB 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏻‍🫲🏾 E14.0 handshake: light skin tone, medium-dark skin tone
+1FAF1 1F3FB 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏻‍🫲🏿 E14.0 handshake: light skin tone, dark skin tone
+1FAF1 1F3FC 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏼‍🫲🏻 E14.0 handshake: medium-light skin tone, light skin tone
+1FAF1 1F3FC 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏼‍🫲🏽 E14.0 handshake: medium-light skin tone, medium skin tone
+1FAF1 1F3FC 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏼‍🫲🏾 E14.0 handshake: medium-light skin tone, medium-dark skin tone
+1FAF1 1F3FC 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏼‍🫲🏿 E14.0 handshake: medium-light skin tone, dark skin tone
+1FAF1 1F3FD 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏽‍🫲🏻 E14.0 handshake: medium skin tone, light skin tone
+1FAF1 1F3FD 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏽‍🫲🏼 E14.0 handshake: medium skin tone, medium-light skin tone
+1FAF1 1F3FD 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏽‍🫲🏾 E14.0 handshake: medium skin tone, medium-dark skin tone
+1FAF1 1F3FD 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏽‍🫲🏿 E14.0 handshake: medium skin tone, dark skin tone
+1FAF1 1F3FE 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏾‍🫲🏻 E14.0 handshake: medium-dark skin tone, light skin tone
+1FAF1 1F3FE 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏾‍🫲🏼 E14.0 handshake: medium-dark skin tone, medium-light skin tone
+1FAF1 1F3FE 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏾‍🫲🏽 E14.0 handshake: medium-dark skin tone, medium skin tone
+1FAF1 1F3FE 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏾‍🫲🏿 E14.0 handshake: medium-dark skin tone, dark skin tone
+1FAF1 1F3FF 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏿‍🫲🏻 E14.0 handshake: dark skin tone, light skin tone
+1FAF1 1F3FF 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏿‍🫲🏼 E14.0 handshake: dark skin tone, medium-light skin tone
+1FAF1 1F3FF 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏿‍🫲🏽 E14.0 handshake: dark skin tone, medium skin tone
+1FAF1 1F3FF 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏿‍🫲🏾 E14.0 handshake: dark skin tone, medium-dark skin tone
1F64F ; fully-qualified # 🙏 E0.6 folded hands
1F64F 1F3FB ; fully-qualified # 🙏🏻 E1.0 folded hands: light skin tone
1F64F 1F3FC ; fully-qualified # 🙏🏼 E1.0 folded hands: medium-light skin tone
@@ -501,6 +592,7 @@
1F441 ; unqualified # 👁 E0.7 eye
1F445 ; fully-qualified # 👅 E0.6 tongue
1F444 ; fully-qualified # 👄 E0.6 mouth
+1FAE6 ; fully-qualified # 🫦 E14.0 biting lip
# subgroup: person
1F476 ; fully-qualified # 👶 E0.6 baby
@@ -1380,7 +1472,7 @@
1F575 1F3FF ; fully-qualified # 🕵🏿 E2.0 detective: dark skin tone
1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️‍♂️ E4.0 man detective
1F575 200D 2642 FE0F ; unqualified # 🕵‍♂️ E4.0 man detective
-1F575 FE0F 200D 2642 ; unqualified # 🕵️‍♂ E4.0 man detective
+1F575 FE0F 200D 2642 ; minimally-qualified # 🕵️‍♂ E4.0 man detective
1F575 200D 2642 ; unqualified # 🕵‍♂ E4.0 man detective
1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻‍♂️ E4.0 man detective: light skin tone
1F575 1F3FB 200D 2642 ; minimally-qualified # 🕵🏻‍♂ E4.0 man detective: light skin tone
@@ -1394,7 +1486,7 @@
1F575 1F3FF 200D 2642 ; minimally-qualified # 🕵🏿‍♂ E4.0 man detective: dark skin tone
1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️‍♀️ E4.0 woman detective
1F575 200D 2640 FE0F ; unqualified # 🕵‍♀️ E4.0 woman detective
-1F575 FE0F 200D 2640 ; unqualified # 🕵️‍♀ E4.0 woman detective
+1F575 FE0F 200D 2640 ; minimally-qualified # 🕵️‍♀ E4.0 woman detective
1F575 200D 2640 ; unqualified # 🕵‍♀ E4.0 woman detective
1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻‍♀️ E4.0 woman detective: light skin tone
1F575 1F3FB 200D 2640 ; minimally-qualified # 🕵🏻‍♀ E4.0 woman detective: light skin tone
@@ -1472,6 +1564,12 @@
1F477 1F3FE 200D 2640 ; minimally-qualified # 👷🏾‍♀ E4.0 woman construction worker: medium-dark skin tone
1F477 1F3FF 200D 2640 FE0F ; fully-qualified # 👷🏿‍♀️ E4.0 woman construction worker: dark skin tone
1F477 1F3FF 200D 2640 ; minimally-qualified # 👷🏿‍♀ E4.0 woman construction worker: dark skin tone
+1FAC5 ; fully-qualified # 🫅 E14.0 person with crown
+1FAC5 1F3FB ; fully-qualified # 🫅🏻 E14.0 person with crown: light skin tone
+1FAC5 1F3FC ; fully-qualified # 🫅🏼 E14.0 person with crown: medium-light skin tone
+1FAC5 1F3FD ; fully-qualified # 🫅🏽 E14.0 person with crown: medium skin tone
+1FAC5 1F3FE ; fully-qualified # 🫅🏾 E14.0 person with crown: medium-dark skin tone
+1FAC5 1F3FF ; fully-qualified # 🫅🏿 E14.0 person with crown: dark skin tone
1F934 ; fully-qualified # 🤴 E3.0 prince
1F934 1F3FB ; fully-qualified # 🤴🏻 E3.0 prince: light skin tone
1F934 1F3FC ; fully-qualified # 🤴🏼 E3.0 prince: medium-light skin tone
@@ -1592,6 +1690,18 @@
1F930 1F3FD ; fully-qualified # 🤰🏽 E3.0 pregnant woman: medium skin tone
1F930 1F3FE ; fully-qualified # 🤰🏾 E3.0 pregnant woman: medium-dark skin tone
1F930 1F3FF ; fully-qualified # 🤰🏿 E3.0 pregnant woman: dark skin tone
+1FAC3 ; fully-qualified # 🫃 E14.0 pregnant man
+1FAC3 1F3FB ; fully-qualified # 🫃🏻 E14.0 pregnant man: light skin tone
+1FAC3 1F3FC ; fully-qualified # 🫃🏼 E14.0 pregnant man: medium-light skin tone
+1FAC3 1F3FD ; fully-qualified # 🫃🏽 E14.0 pregnant man: medium skin tone
+1FAC3 1F3FE ; fully-qualified # 🫃🏾 E14.0 pregnant man: medium-dark skin tone
+1FAC3 1F3FF ; fully-qualified # 🫃🏿 E14.0 pregnant man: dark skin tone
+1FAC4 ; fully-qualified # 🫄 E14.0 pregnant person
+1FAC4 1F3FB ; fully-qualified # 🫄🏻 E14.0 pregnant person: light skin tone
+1FAC4 1F3FC ; fully-qualified # 🫄🏼 E14.0 pregnant person: medium-light skin tone
+1FAC4 1F3FD ; fully-qualified # 🫄🏽 E14.0 pregnant person: medium skin tone
+1FAC4 1F3FE ; fully-qualified # 🫄🏾 E14.0 pregnant person: medium-dark skin tone
+1FAC4 1F3FF ; fully-qualified # 🫄🏿 E14.0 pregnant person: dark skin tone
1F931 ; fully-qualified # 🤱 E5.0 breast-feeding
1F931 1F3FB ; fully-qualified # 🤱🏻 E5.0 breast-feeding: light skin tone
1F931 1F3FC ; fully-qualified # 🤱🏼 E5.0 breast-feeding: medium-light skin tone
@@ -1862,6 +1972,7 @@
1F9DF 200D 2642 ; minimally-qualified # 🧟‍♂ E5.0 man zombie
1F9DF 200D 2640 FE0F ; fully-qualified # 🧟‍♀️ E5.0 woman zombie
1F9DF 200D 2640 ; minimally-qualified # 🧟‍♀ E5.0 woman zombie
+1F9CC ; fully-qualified # 🧌 E14.0 troll
# subgroup: person-activity
1F486 ; fully-qualified # 💆 E0.6 person getting massage
@@ -2208,7 +2319,7 @@
1F3CC 1F3FF ; fully-qualified # 🏌🏿 E4.0 person golfing: dark skin tone
1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️‍♂️ E4.0 man golfing
1F3CC 200D 2642 FE0F ; unqualified # 🏌‍♂️ E4.0 man golfing
-1F3CC FE0F 200D 2642 ; unqualified # 🏌️‍♂ E4.0 man golfing
+1F3CC FE0F 200D 2642 ; minimally-qualified # 🏌️‍♂ E4.0 man golfing
1F3CC 200D 2642 ; unqualified # 🏌‍♂ E4.0 man golfing
1F3CC 1F3FB 200D 2642 FE0F ; fully-qualified # 🏌🏻‍♂️ E4.0 man golfing: light skin tone
1F3CC 1F3FB 200D 2642 ; minimally-qualified # 🏌🏻‍♂ E4.0 man golfing: light skin tone
@@ -2222,7 +2333,7 @@
1F3CC 1F3FF 200D 2642 ; minimally-qualified # 🏌🏿‍♂ E4.0 man golfing: dark skin tone
1F3CC FE0F 200D 2640 FE0F ; fully-qualified # 🏌️‍♀️ E4.0 woman golfing
1F3CC 200D 2640 FE0F ; unqualified # 🏌‍♀️ E4.0 woman golfing
-1F3CC FE0F 200D 2640 ; unqualified # 🏌️‍♀ E4.0 woman golfing
+1F3CC FE0F 200D 2640 ; minimally-qualified # 🏌️‍♀ E4.0 woman golfing
1F3CC 200D 2640 ; unqualified # 🏌‍♀ E4.0 woman golfing
1F3CC 1F3FB 200D 2640 FE0F ; fully-qualified # 🏌🏻‍♀️ E4.0 woman golfing: light skin tone
1F3CC 1F3FB 200D 2640 ; minimally-qualified # 🏌🏻‍♀ E4.0 woman golfing: light skin tone
@@ -2333,7 +2444,7 @@
26F9 1F3FF ; fully-qualified # ⛹🏿 E2.0 person bouncing ball: dark skin tone
26F9 FE0F 200D 2642 FE0F ; fully-qualified # ⛹️‍♂️ E4.0 man bouncing ball
26F9 200D 2642 FE0F ; unqualified # ⛹‍♂️ E4.0 man bouncing ball
-26F9 FE0F 200D 2642 ; unqualified # ⛹️‍♂ E4.0 man bouncing ball
+26F9 FE0F 200D 2642 ; minimally-qualified # ⛹️‍♂ E4.0 man bouncing ball
26F9 200D 2642 ; unqualified # ⛹‍♂ E4.0 man bouncing ball
26F9 1F3FB 200D 2642 FE0F ; fully-qualified # ⛹🏻‍♂️ E4.0 man bouncing ball: light skin tone
26F9 1F3FB 200D 2642 ; minimally-qualified # ⛹🏻‍♂ E4.0 man bouncing ball: light skin tone
@@ -2347,7 +2458,7 @@
26F9 1F3FF 200D 2642 ; minimally-qualified # ⛹🏿‍♂ E4.0 man bouncing ball: dark skin tone
26F9 FE0F 200D 2640 FE0F ; fully-qualified # ⛹️‍♀️ E4.0 woman bouncing ball
26F9 200D 2640 FE0F ; unqualified # ⛹‍♀️ E4.0 woman bouncing ball
-26F9 FE0F 200D 2640 ; unqualified # ⛹️‍♀ E4.0 woman bouncing ball
+26F9 FE0F 200D 2640 ; minimally-qualified # ⛹️‍♀ E4.0 woman bouncing ball
26F9 200D 2640 ; unqualified # ⛹‍♀ E4.0 woman bouncing ball
26F9 1F3FB 200D 2640 FE0F ; fully-qualified # ⛹🏻‍♀️ E4.0 woman bouncing ball: light skin tone
26F9 1F3FB 200D 2640 ; minimally-qualified # ⛹🏻‍♀ E4.0 woman bouncing ball: light skin tone
@@ -2368,7 +2479,7 @@
1F3CB 1F3FF ; fully-qualified # 🏋🏿 E2.0 person lifting weights: dark skin tone
1F3CB FE0F 200D 2642 FE0F ; fully-qualified # 🏋️‍♂️ E4.0 man lifting weights
1F3CB 200D 2642 FE0F ; unqualified # 🏋‍♂️ E4.0 man lifting weights
-1F3CB FE0F 200D 2642 ; unqualified # 🏋️‍♂ E4.0 man lifting weights
+1F3CB FE0F 200D 2642 ; minimally-qualified # 🏋️‍♂ E4.0 man lifting weights
1F3CB 200D 2642 ; unqualified # 🏋‍♂ E4.0 man lifting weights
1F3CB 1F3FB 200D 2642 FE0F ; fully-qualified # 🏋🏻‍♂️ E4.0 man lifting weights: light skin tone
1F3CB 1F3FB 200D 2642 ; minimally-qualified # 🏋🏻‍♂ E4.0 man lifting weights: light skin tone
@@ -2382,7 +2493,7 @@
1F3CB 1F3FF 200D 2642 ; minimally-qualified # 🏋🏿‍♂ E4.0 man lifting weights: dark skin tone
1F3CB FE0F 200D 2640 FE0F ; fully-qualified # 🏋️‍♀️ E4.0 woman lifting weights
1F3CB 200D 2640 FE0F ; unqualified # 🏋‍♀️ E4.0 woman lifting weights
-1F3CB FE0F 200D 2640 ; unqualified # 🏋️‍♀ E4.0 woman lifting weights
+1F3CB FE0F 200D 2640 ; minimally-qualified # 🏋️‍♀ E4.0 woman lifting weights
1F3CB 200D 2640 ; unqualified # 🏋‍♀ E4.0 woman lifting weights
1F3CB 1F3FB 200D 2640 FE0F ; fully-qualified # 🏋🏻‍♀️ E4.0 woman lifting weights: light skin tone
1F3CB 1F3FB 200D 2640 ; minimally-qualified # 🏋🏻‍♀ E4.0 woman lifting weights: light skin tone
@@ -3168,8 +3279,8 @@
1FAC2 ; fully-qualified # 🫂 E13.0 people hugging
1F463 ; fully-qualified # 👣 E0.6 footprints
-# People & Body subtotal: 2899
-# People & Body subtotal: 494 w/o modifiers
+# People & Body subtotal: 2998
+# People & Body subtotal: 508 w/o modifiers
# group: Component
@@ -3212,6 +3323,8 @@
1F405 ; fully-qualified # 🐅 E1.0 tiger
1F406 ; fully-qualified # 🐆 E1.0 leopard
1F434 ; fully-qualified # 🐴 E0.6 horse face
+1FACE ; fully-qualified # 🫎 E15.0 moose
+1FACF ; fully-qualified # 🫏 E15.0 donkey
1F40E ; fully-qualified # 🐎 E0.6 horse
1F984 ; fully-qualified # 🦄 E1.0 unicorn
1F993 ; fully-qualified # 🦓 E5.0 zebra
@@ -3279,6 +3392,9 @@
1F9A9 ; fully-qualified # 🦩 E12.0 flamingo
1F99A ; fully-qualified # 🦚 E11.0 peacock
1F99C ; fully-qualified # 🦜 E11.0 parrot
+1FABD ; fully-qualified # 🪽 E15.0 wing
+1F426 200D 2B1B ; fully-qualified # 🐦‍⬛ E15.0 black bird
+1FABF ; fully-qualified # 🪿 E15.0 goose
# subgroup: animal-amphibian
1F438 ; fully-qualified # 🐸 E0.6 frog
@@ -3304,6 +3420,8 @@
1F988 ; fully-qualified # 🦈 E3.0 shark
1F419 ; fully-qualified # 🐙 E0.6 octopus
1F41A ; fully-qualified # 🐚 E0.6 spiral shell
+1FAB8 ; fully-qualified # 🪸 E14.0 coral
+1FABC ; fully-qualified # 🪼 E15.0 jellyfish
# subgroup: animal-bug
1F40C ; fully-qualified # 🐌 E0.6 snail
@@ -3329,6 +3447,7 @@
1F490 ; fully-qualified # 💐 E0.6 bouquet
1F338 ; fully-qualified # 🌸 E0.6 cherry blossom
1F4AE ; fully-qualified # 💮 E0.6 white flower
+1FAB7 ; fully-qualified # 🪷 E14.0 lotus
1F3F5 FE0F ; fully-qualified # 🏵️ E0.7 rosette
1F3F5 ; unqualified # 🏵 E0.7 rosette
1F339 ; fully-qualified # 🌹 E0.6 rose
@@ -3337,6 +3456,7 @@
1F33B ; fully-qualified # 🌻 E0.6 sunflower
1F33C ; fully-qualified # 🌼 E0.6 blossom
1F337 ; fully-qualified # 🌷 E0.6 tulip
+1FABB ; fully-qualified # 🪻 E15.0 hyacinth
# subgroup: plant-other
1F331 ; fully-qualified # 🌱 E0.6 seedling
@@ -3353,9 +3473,12 @@
1F341 ; fully-qualified # 🍁 E0.6 maple leaf
1F342 ; fully-qualified # 🍂 E0.6 fallen leaf
1F343 ; fully-qualified # 🍃 E0.6 leaf fluttering in wind
+1FAB9 ; fully-qualified # 🪹 E14.0 empty nest
+1FABA ; fully-qualified # 🪺 E14.0 nest with eggs
+1F344 ; fully-qualified # 🍄 E0.6 mushroom
-# Animals & Nature subtotal: 147
-# Animals & Nature subtotal: 147 w/o modifiers
+# Animals & Nature subtotal: 159
+# Animals & Nature subtotal: 159 w/o modifiers
# group: Food & Drink
@@ -3394,9 +3517,11 @@
1F966 ; fully-qualified # 🥦 E5.0 broccoli
1F9C4 ; fully-qualified # 🧄 E12.0 garlic
1F9C5 ; fully-qualified # 🧅 E12.0 onion
-1F344 ; fully-qualified # 🍄 E0.6 mushroom
1F95C ; fully-qualified # 🥜 E3.0 peanuts
+1FAD8 ; fully-qualified # 🫘 E14.0 beans
1F330 ; fully-qualified # 🌰 E0.6 chestnut
+1FADA ; fully-qualified # 🫚 E15.0 ginger root
+1FADB ; fully-qualified # 🫛 E15.0 pea pod
# subgroup: food-prepared
1F35E ; fully-qualified # 🍞 E0.6 bread
@@ -3491,6 +3616,7 @@
1F37B ; fully-qualified # 🍻 E0.6 clinking beer mugs
1F942 ; fully-qualified # 🥂 E3.0 clinking glasses
1F943 ; fully-qualified # 🥃 E3.0 tumbler glass
+1FAD7 ; fully-qualified # 🫗 E14.0 pouring liquid
1F964 ; fully-qualified # 🥤 E5.0 cup with straw
1F9CB ; fully-qualified # 🧋 E13.0 bubble tea
1F9C3 ; fully-qualified # 🧃 E12.0 beverage box
@@ -3504,10 +3630,11 @@
1F374 ; fully-qualified # 🍴 E0.6 fork and knife
1F944 ; fully-qualified # 🥄 E3.0 spoon
1F52A ; fully-qualified # 🔪 E0.6 kitchen knife
+1FAD9 ; fully-qualified # 🫙 E14.0 jar
1F3FA ; fully-qualified # 🏺 E1.0 amphora
-# Food & Drink subtotal: 131
-# Food & Drink subtotal: 131 w/o modifiers
+# Food & Drink subtotal: 135
+# Food & Drink subtotal: 135 w/o modifiers
# group: Travel & Places
@@ -3597,6 +3724,7 @@
2668 FE0F ; fully-qualified # ♨️ E0.6 hot springs
2668 ; unqualified # ♨ E0.6 hot springs
1F3A0 ; fully-qualified # 🎠 E0.6 carousel horse
+1F6DD ; fully-qualified # 🛝 E14.0 playground slide
1F3A1 ; fully-qualified # 🎡 E0.6 ferris wheel
1F3A2 ; fully-qualified # 🎢 E0.6 roller coaster
1F488 ; fully-qualified # 💈 E0.6 barber pole
@@ -3652,6 +3780,7 @@
1F6E2 FE0F ; fully-qualified # 🛢️ E0.7 oil drum
1F6E2 ; unqualified # 🛢 E0.7 oil drum
26FD ; fully-qualified # ⛽ E0.6 fuel pump
+1F6DE ; fully-qualified # 🛞 E14.0 wheel
1F6A8 ; fully-qualified # 🚨 E0.6 police car light
1F6A5 ; fully-qualified # 🚥 E0.6 horizontal traffic light
1F6A6 ; fully-qualified # 🚦 E1.0 vertical traffic light
@@ -3660,6 +3789,7 @@
# subgroup: transport-water
2693 ; fully-qualified # ⚓ E0.6 anchor
+1F6DF ; fully-qualified # 🛟 E14.0 ring buoy
26F5 ; fully-qualified # ⛵ E0.6 sailboat
1F6F6 ; fully-qualified # 🛶 E3.0 canoe
1F6A4 ; fully-qualified # 🚤 E0.6 speedboat
@@ -3797,8 +3927,8 @@
1F4A7 ; fully-qualified # 💧 E0.6 droplet
1F30A ; fully-qualified # 🌊 E0.6 water wave
-# Travel & Places subtotal: 264
-# Travel & Places subtotal: 264 w/o modifiers
+# Travel & Places subtotal: 267
+# Travel & Places subtotal: 267 w/o modifiers
# group: Activities
@@ -3870,10 +4000,10 @@
1F3AF ; fully-qualified # 🎯 E0.6 bullseye
1FA80 ; fully-qualified # 🪀 E12.0 yo-yo
1FA81 ; fully-qualified # 🪁 E12.0 kite
+1F52B ; fully-qualified # 🔫 E0.6 water pistol
1F3B1 ; fully-qualified # 🎱 E0.6 pool 8 ball
1F52E ; fully-qualified # 🔮 E0.6 crystal ball
1FA84 ; fully-qualified # 🪄 E13.0 magic wand
-1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet
1F3AE ; fully-qualified # 🎮 E0.6 video game
1F579 FE0F ; fully-qualified # 🕹️ E0.7 joystick
1F579 ; unqualified # 🕹 E0.7 joystick
@@ -3882,6 +4012,7 @@
1F9E9 ; fully-qualified # 🧩 E11.0 puzzle piece
1F9F8 ; fully-qualified # 🧸 E11.0 teddy bear
1FA85 ; fully-qualified # 🪅 E13.0 piñata
+1FAA9 ; fully-qualified # 🪩 E14.0 mirror ball
1FA86 ; fully-qualified # 🪆 E13.0 nesting dolls
2660 FE0F ; fully-qualified # ♠️ E0.6 spade suit
2660 ; unqualified # ♠ E0.6 spade suit
@@ -3907,8 +4038,8 @@
1F9F6 ; fully-qualified # 🧶 E11.0 yarn
1FAA2 ; fully-qualified # 🪢 E13.0 knot
-# Activities subtotal: 95
-# Activities subtotal: 95 w/o modifiers
+# Activities subtotal: 96
+# Activities subtotal: 96 w/o modifiers
# group: Objects
@@ -3934,6 +4065,7 @@
1FA73 ; fully-qualified # 🩳 E12.0 shorts
1F459 ; fully-qualified # 👙 E0.6 bikini
1F45A ; fully-qualified # 👚 E0.6 woman’s clothes
+1FAAD ; fully-qualified # 🪭 E15.0 folding hand fan
1F45B ; fully-qualified # 👛 E0.6 purse
1F45C ; fully-qualified # 👜 E0.6 handbag
1F45D ; fully-qualified # 👝 E0.6 clutch bag
@@ -3949,6 +4081,7 @@
1F461 ; fully-qualified # 👡 E0.6 woman’s sandal
1FA70 ; fully-qualified # 🩰 E12.0 ballet shoes
1F462 ; fully-qualified # 👢 E0.6 woman’s boot
+1FAAE ; fully-qualified # 🪮 E15.0 hair pick
1F451 ; fully-qualified # 👑 E0.6 crown
1F452 ; fully-qualified # 👒 E0.6 woman’s hat
1F3A9 ; fully-qualified # 🎩 E0.6 top hat
@@ -3997,6 +4130,8 @@
1FA95 ; fully-qualified # 🪕 E12.0 banjo
1F941 ; fully-qualified # 🥁 E3.0 drum
1FA98 ; fully-qualified # 🪘 E13.0 long drum
+1FA87 ; fully-qualified # 🪇 E15.0 maracas
+1FA88 ; fully-qualified # 🪈 E15.0 flute
# subgroup: phone
1F4F1 ; fully-qualified # 📱 E0.6 mobile phone
@@ -4009,6 +4144,7 @@
# subgroup: computer
1F50B ; fully-qualified # 🔋 E0.6 battery
+1FAAB ; fully-qualified # 🪫 E14.0 low battery
1F50C ; fully-qualified # 🔌 E0.6 electric plug
1F4BB ; fully-qualified # 💻 E0.6 laptop
1F5A5 FE0F ; fully-qualified # 🖥️ E0.7 desktop computer
@@ -4168,7 +4304,7 @@
1F5E1 ; unqualified # 🗡 E0.7 dagger
2694 FE0F ; fully-qualified # ⚔️ E1.0 crossed swords
2694 ; unqualified # ⚔ E1.0 crossed swords
-1F52B ; fully-qualified # 🔫 E0.6 water pistol
+1F4A3 ; fully-qualified # 💣 E0.6 bomb
1FA83 ; fully-qualified # 🪃 E13.0 boomerang
1F3F9 ; fully-qualified # 🏹 E1.0 bow and arrow
1F6E1 FE0F ; fully-qualified # 🛡️ E0.7 shield
@@ -4207,7 +4343,9 @@
1FA78 ; fully-qualified # 🩸 E12.0 drop of blood
1F48A ; fully-qualified # 💊 E0.6 pill
1FA79 ; fully-qualified # 🩹 E12.0 adhesive bandage
+1FA7C ; fully-qualified # 🩼 E14.0 crutch
1FA7A ; fully-qualified # 🩺 E12.0 stethoscope
+1FA7B ; fully-qualified # 🩻 E14.0 x-ray
# subgroup: household
1F6AA ; fully-qualified # 🚪 E0.6 door
@@ -4232,6 +4370,7 @@
1F9FB ; fully-qualified # 🧻 E11.0 roll of paper
1FAA3 ; fully-qualified # 🪣 E13.0 bucket
1F9FC ; fully-qualified # 🧼 E11.0 soap
+1FAE7 ; fully-qualified # 🫧 E14.0 bubbles
1FAA5 ; fully-qualified # 🪥 E13.0 toothbrush
1F9FD ; fully-qualified # 🧽 E11.0 sponge
1F9EF ; fully-qualified # 🧯 E11.0 fire extinguisher
@@ -4244,11 +4383,14 @@
1FAA6 ; fully-qualified # 🪦 E13.0 headstone
26B1 FE0F ; fully-qualified # ⚱️ E1.0 funeral urn
26B1 ; unqualified # ⚱ E1.0 funeral urn
+1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet
+1FAAC ; fully-qualified # 🪬 E14.0 hamsa
1F5FF ; fully-qualified # 🗿 E0.6 moai
1FAA7 ; fully-qualified # 🪧 E13.0 placard
+1FAAA ; fully-qualified # 🪪 E14.0 identification card
-# Objects subtotal: 299
-# Objects subtotal: 299 w/o modifiers
+# Objects subtotal: 310
+# Objects subtotal: 310 w/o modifiers
# group: Symbols
@@ -4344,6 +4486,7 @@
262E ; unqualified # ☮ E1.0 peace symbol
1F54E ; fully-qualified # 🕎 E1.0 menorah
1F52F ; fully-qualified # 🔯 E0.6 dotted six-pointed star
+1FAAF ; fully-qualified # 🪯 E15.0 khanda
# subgroup: zodiac
2648 ; fully-qualified # ♈ E0.6 Aries
@@ -4392,6 +4535,7 @@
1F505 ; fully-qualified # 🔅 E1.0 dim button
1F506 ; fully-qualified # 🔆 E1.0 bright button
1F4F6 ; fully-qualified # 📶 E0.6 antenna bars
+1F6DC ; fully-qualified # 🛜 E15.0 wireless
1F4F3 ; fully-qualified # 📳 E0.6 vibration mode
1F4F4 ; fully-qualified # 📴 E0.6 mobile phone off
@@ -4409,6 +4553,7 @@
2795 ; fully-qualified # ➕ E0.6 plus
2796 ; fully-qualified # ➖ E0.6 minus
2797 ; fully-qualified # ➗ E0.6 divide
+1F7F0 ; fully-qualified # 🟰 E14.0 heavy equals sign
267E FE0F ; fully-qualified # ♾️ E11.0 infinity
267E ; unqualified # ♾ E11.0 infinity
@@ -4581,8 +4726,8 @@
1F533 ; fully-qualified # 🔳 E0.6 white square button
1F532 ; fully-qualified # 🔲 E0.6 black square button
-# Symbols subtotal: 301
-# Symbols subtotal: 301 w/o modifiers
+# Symbols subtotal: 304
+# Symbols subtotal: 304 w/o modifiers
# group: Flags
@@ -4597,7 +4742,7 @@
1F3F3 200D 1F308 ; unqualified # 🏳‍🌈 E4.0 rainbow flag
1F3F3 FE0F 200D 26A7 FE0F ; fully-qualified # 🏳️‍⚧️ E13.0 transgender flag
1F3F3 200D 26A7 FE0F ; unqualified # 🏳‍⚧️ E13.0 transgender flag
-1F3F3 FE0F 200D 26A7 ; unqualified # 🏳️‍⚧ E13.0 transgender flag
+1F3F3 FE0F 200D 26A7 ; minimally-qualified # 🏳️‍⚧ E13.0 transgender flag
1F3F3 200D 26A7 ; unqualified # 🏳‍⚧ E13.0 transgender flag
1F3F4 200D 2620 FE0F ; fully-qualified # 🏴‍☠️ E11.0 pirate flag
1F3F4 200D 2620 ; minimally-qualified # 🏴‍☠ E11.0 pirate flag
@@ -4871,9 +5016,9 @@
# Flags subtotal: 275 w/o modifiers
# Status Counts
-# fully-qualified : 3512
-# minimally-qualified : 817
-# unqualified : 252
+# fully-qualified : 3655
+# minimally-qualified : 827
+# unqualified : 242
# component : 9
#EOF
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index f077fe5b4..43a3447c3 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji do
@@ -9,6 +9,7 @@ defmodule Pleroma.Emoji do
"""
use GenServer
+ alias Pleroma.Emoji.Combinations
alias Pleroma.Emoji.Loader
require Logger
@@ -50,6 +51,8 @@ defmodule Pleroma.Emoji do
@doc "Returns the path of the emoji `name`."
@spec get(String.t()) :: String.t() | nil
def get(name) do
+ name = maybe_strip_name(name)
+
case :ets.lookup(@ets, name) do
[{_, path}] -> path
_ -> nil
@@ -137,4 +140,68 @@ defmodule Pleroma.Emoji do
end
def is_unicode_emoji?(_), do: false
+
+ @emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
+
+ def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
+
+ def is_custom_emoji?(_), do: false
+
+ def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":")
+
+ def maybe_strip_name(name), do: name
+
+ def maybe_quote(name) when is_binary(name) do
+ if is_unicode_emoji?(name) do
+ name
+ else
+ if String.starts_with?(name, ":") do
+ name
+ else
+ ":#{name}:"
+ end
+ end
+ end
+
+ def maybe_quote(name), do: name
+
+ def emoji_url(%{"type" => "EmojiReact", "content" => _, "tag" => []}), do: nil
+
+ def emoji_url(%{"type" => "EmojiReact", "content" => emoji, "tag" => tags}) do
+ emoji = maybe_strip_name(emoji)
+
+ tag =
+ tags
+ |> Enum.find(fn tag ->
+ tag["type"] == "Emoji" && !is_nil(tag["name"]) && tag["name"] == emoji
+ end)
+
+ if is_nil(tag) do
+ nil
+ else
+ tag
+ |> Map.get("icon")
+ |> Map.get("url")
+ end
+ end
+
+ def emoji_url(_), do: nil
+
+ def emoji_name_with_instance(name, url) do
+ url = url |> URI.parse() |> Map.get(:host)
+ "#{name}@#{url}"
+ end
+
+ emoji_qualification_map =
+ emojis
+ |> Enum.filter(&String.contains?(&1, "\uFE0F"))
+ |> Combinations.variate_emoji_qualification()
+
+ for {qualified, unqualified_list} <- emoji_qualification_map do
+ for unqualified <- unqualified_list do
+ def fully_qualify_emoji(unquote(unqualified)), do: unquote(qualified)
+ end
+ end
+
+ def fully_qualify_emoji(emoji), do: emoji
end
diff --git a/lib/pleroma/emoji/combinations.ex b/lib/pleroma/emoji/combinations.ex
new file mode 100644
index 000000000..981c73596
--- /dev/null
+++ b/lib/pleroma/emoji/combinations.ex
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Emoji.Combinations do
+ # FE0F is the emoji variation sequence. It is used for fully-qualifying
+ # emoji, and that includes emoji combinations.
+ # This code generates combinations per emoji: for each FE0F, all possible
+ # combinations of the character being removed or staying will be generated.
+ # This is made as an attempt to find all partially-qualified and unqualified
+ # versions of a fully-qualified emoji.
+ # I have found *no cases* for which this would be a problem, after browsing
+ # the entire emoji list in emoji-test.txt. This is safe, and, sadly, most
+ # likely sane too.
+
+ defp qualification_combinations(codepoints) do
+ qualification_combinations([[]], codepoints)
+ end
+
+ defp qualification_combinations(acc, []), do: acc
+
+ defp qualification_combinations(acc, ["\uFE0F" | tail]) do
+ acc
+ |> Enum.flat_map(fn x -> [x, x ++ ["\uFE0F"]] end)
+ |> qualification_combinations(tail)
+ end
+
+ defp qualification_combinations(acc, [codepoint | tail]) do
+ acc
+ |> Enum.map(&Kernel.++(&1, [codepoint]))
+ |> qualification_combinations(tail)
+ end
+
+ def variate_emoji_qualification(emoji) when is_binary(emoji) do
+ emoji
+ |> String.codepoints()
+ |> qualification_combinations()
+ |> Enum.map(&List.to_string/1)
+ end
+
+ def variate_emoji_qualification(emoji) when is_list(emoji) do
+ emoji
+ |> Enum.map(fn emoji -> {emoji, variate_emoji_qualification(emoji)} end)
+ end
+end
diff --git a/lib/pleroma/emoji/formatter.ex b/lib/pleroma/emoji/formatter.ex
index 50150e951..87fd35f13 100644
--- a/lib/pleroma/emoji/formatter.ex
+++ b/lib/pleroma/emoji/formatter.ex
@@ -1,11 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji.Formatter do
alias Pleroma.Emoji
alias Pleroma.HTML
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.MediaProxy
def emojify(text) do
@@ -44,7 +44,7 @@ defmodule Pleroma.Emoji.Formatter do
Emoji.get_all()
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
- Map.put(acc, name, to_string(URI.merge(Web.base_url(), file)))
+ Map.put(acc, name, to_string(URI.merge(Endpoint.url(), file)))
end)
end
diff --git a/lib/pleroma/emoji/loader.ex b/lib/pleroma/emoji/loader.ex
index 67acd7069..97d4b8f70 100644
--- a/lib/pleroma/emoji/loader.ex
+++ b/lib/pleroma/emoji/loader.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji.Loader do
@@ -60,9 +60,7 @@ defmodule Pleroma.Emoji.Loader do
if not Enum.empty?(files) do
Logger.warn(
- "Found files in the emoji folder. These will be ignored, please move them to a subdirectory\nFound files: #{
- Enum.join(files, ", ")
- }"
+ "Found files in the emoji folder. These will be ignored, please move them to a subdirectory\nFound files: #{Enum.join(files, ", ")}"
)
end
@@ -105,6 +103,7 @@ defmodule Pleroma.Emoji.Loader do
pack_file = Path.join(pack_dir, "pack.json")
if File.exists?(pack_file) do
+ Logger.info("Loading emoji pack from JSON: #{pack_file}")
contents = Jason.decode!(File.read!(pack_file))
contents["files"]
@@ -117,14 +116,13 @@ defmodule Pleroma.Emoji.Loader do
emoji_txt = Path.join(pack_dir, "emoji.txt")
if File.exists?(emoji_txt) do
+ Logger.info("Loading emoji pack from emoji.txt: #{emoji_txt}")
load_from_file(emoji_txt, emoji_groups)
else
extensions = Config.get([:emoji, :pack_extensions])
Logger.info(
- "No emoji.txt found for pack \"#{pack_name}\", assuming all #{
- Enum.join(extensions, ", ")
- } files are emoji"
+ "No emoji.txt found for pack \"#{pack_name}\", assuming all #{Enum.join(extensions, ", ")} files are emoji"
)
make_shortcode_to_file_map(pack_dir, extensions)
diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex
index 09bfcc868..a361ea200 100644
--- a/lib/pleroma/emoji/pack.ex
+++ b/lib/pleroma/emoji/pack.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji.Pack do
diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex
index 82b9caf9b..db88bc021 100644
--- a/lib/pleroma/filter.ex
+++ b/lib/pleroma/filter.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Filter do
diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex
index a0c7e6e39..15664c876 100644
--- a/lib/pleroma/following_relationship.ex
+++ b/lib/pleroma/following_relationship.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.FollowingRelationship do
@@ -194,12 +194,13 @@ defmodule Pleroma.FollowingRelationship do
|> join(:inner, [r], f in assoc(r, :follower))
|> where(following_id: ^origin.id)
|> where([r, f], f.allow_following_move == true)
+ |> where([r, f], f.local == true)
|> limit(50)
|> preload([:follower])
|> Repo.all()
|> Enum.map(fn following_relationship ->
- Repo.delete(following_relationship)
Pleroma.Web.CommonAPI.follow(following_relationship.follower, target)
+ Pleroma.Web.CommonAPI.unfollow(following_relationship.follower, origin)
end)
|> case do
[] ->
diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex
index 7a08e48a9..a46c3e381 100644
--- a/lib/pleroma/formatter.ex
+++ b/lib/pleroma/formatter.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Formatter do
@@ -34,35 +34,37 @@ defmodule Pleroma.Formatter do
def mention_handler("@" <> nickname, buffer, opts, acc) do
case User.get_cached_by_nickname(nickname) do
- %User{id: id} = user ->
- user_url = user.uri || user.ap_id
- nickname_text = get_nickname_text(nickname, opts)
-
- link =
- Phoenix.HTML.Tag.content_tag(
- :span,
- Phoenix.HTML.Tag.content_tag(
- :a,
- ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
- "data-user": id,
- class: "u-url mention",
- href: user_url,
- rel: "ugc"
- ),
- class: "h-card"
- )
- |> Phoenix.HTML.safe_to_string()
-
- {link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
+ %User{} = user ->
+ {mention_from_user(user, opts),
+ %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
_ ->
{buffer, acc}
end
end
+ def mention_from_user(%User{id: id} = user, opts \\ %{mentions_format: :full}) do
+ user_url = user.uri || user.ap_id
+ nickname_text = get_nickname_text(user.nickname, opts)
+
+ Phoenix.HTML.Tag.content_tag(
+ :span,
+ Phoenix.HTML.Tag.content_tag(
+ :a,
+ ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
+ "data-user": id,
+ class: "u-url mention",
+ href: user_url,
+ rel: "ugc"
+ ),
+ class: "h-card"
+ )
+ |> Phoenix.HTML.safe_to_string()
+ end
+
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
tag = String.downcase(tag)
- url = "#{Pleroma.Web.base_url()}/tag/#{tag}"
+ url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}"
link =
Phoenix.HTML.Tag.content_tag(:a, tag_text,
@@ -121,6 +123,10 @@ defmodule Pleroma.Formatter do
end
end
+ def markdown_to_html(text) do
+ Earmark.as_html!(text, %Earmark.Options{compact_output: true})
+ end
+
def html_escape({text, mentions, hashtags}, type) do
{html_escape(text, type), mentions, hashtags}
end
diff --git a/lib/pleroma/frontend.ex b/lib/pleroma/frontend.ex
index 34b7befb8..ec72fb6a4 100644
--- a/lib/pleroma/frontend.ex
+++ b/lib/pleroma/frontend.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Frontend do
diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex
index 1b85c49f5..0fde0adcf 100644
--- a/lib/pleroma/gopher/server.ex
+++ b/lib/pleroma/gopher/server.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gopher.Server do
diff --git a/lib/pleroma/gun.ex b/lib/pleroma/gun.ex
index f9c828fac..c2d6b4bf2 100644
--- a/lib/pleroma/gun.ex
+++ b/lib/pleroma/gun.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun do
@@ -11,9 +11,7 @@ defmodule Pleroma.Gun do
@callback await(pid(), reference()) :: {:response, :fin, 200, []}
@callback set_owner(pid(), pid()) :: :ok
- @api Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
-
- defp api, do: @api
+ defp api, do: Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
def open(host, port, opts), do: api().open(host, port, opts)
diff --git a/lib/pleroma/gun/api.ex b/lib/pleroma/gun/api.ex
index 24d542781..ff2100623 100644
--- a/lib/pleroma/gun/api.ex
+++ b/lib/pleroma/gun/api.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.API do
diff --git a/lib/pleroma/gun/conn.ex b/lib/pleroma/gun/conn.ex
index a56625699..7c5785def 100644
--- a/lib/pleroma/gun/conn.ex
+++ b/lib/pleroma/gun/conn.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.Conn do
@@ -57,9 +57,7 @@ defmodule Pleroma.Gun.Conn do
else
error ->
Logger.warn(
- "Opening proxied connection to #{compose_uri_log(uri)} failed with error #{
- inspect(error)
- }"
+ "Opening proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
)
error
@@ -93,9 +91,7 @@ defmodule Pleroma.Gun.Conn do
else
error ->
Logger.warn(
- "Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{
- inspect(error)
- }"
+ "Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
)
error
diff --git a/lib/pleroma/gun/connection_pool.ex b/lib/pleroma/gun/connection_pool.ex
index f9fd77ade..2e851de19 100644
--- a/lib/pleroma/gun/connection_pool.ex
+++ b/lib/pleroma/gun/connection_pool.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool do
diff --git a/lib/pleroma/gun/connection_pool/reclaimer.ex b/lib/pleroma/gun/connection_pool/reclaimer.ex
index c37b62bf2..efd5c9fb8 100644
--- a/lib/pleroma/gun/connection_pool/reclaimer.ex
+++ b/lib/pleroma/gun/connection_pool/reclaimer.ex
@@ -1,15 +1,15 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
use GenServer, restart: :temporary
- @registry Pleroma.Gun.ConnectionPool
+ defp registry, do: Pleroma.Gun.ConnectionPool
def start_monitor do
pid =
- case :gen_server.start(__MODULE__, [], name: {:via, Registry, {@registry, "reclaimer"}}) do
+ case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
{:ok, pid} ->
pid
@@ -46,7 +46,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
# {worker_pid, crf, last_reference} end)
unused_conns =
Registry.select(
- @registry,
+ registry(),
[
{{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
]
diff --git a/lib/pleroma/gun/connection_pool/worker.ex b/lib/pleroma/gun/connection_pool/worker.ex
index 02bfff274..38527ec1d 100644
--- a/lib/pleroma/gun/connection_pool/worker.ex
+++ b/lib/pleroma/gun/connection_pool/worker.ex
@@ -1,15 +1,15 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool.Worker do
alias Pleroma.Gun
use GenServer, restart: :temporary
- @registry Pleroma.Gun.ConnectionPool
+ defp registry, do: Pleroma.Gun.ConnectionPool
def start_link([key | _] = opts) do
- GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {@registry, key}})
+ GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {registry(), key}})
end
@impl true
@@ -24,7 +24,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
time = :erlang.monotonic_time(:millisecond)
{_, _} =
- Registry.update_value(@registry, key, fn _ ->
+ Registry.update_value(registry(), key, fn _ ->
{conn_pid, [client_pid], 1, time}
end)
@@ -65,7 +65,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
time = :erlang.monotonic_time(:millisecond)
{{conn_pid, used_by, _, _}, _} =
- Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
+ Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
end)
@@ -92,7 +92,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
@impl true
def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do
{{_conn_pid, used_by, _crf, _last_reference}, _} =
- Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
+ Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
{conn_pid, List.delete(used_by, client_pid), crf, last_reference}
end)
diff --git a/lib/pleroma/gun/connection_pool/worker_supervisor.ex b/lib/pleroma/gun/connection_pool/worker_supervisor.ex
index 016b675f4..d26a70be3 100644
--- a/lib/pleroma/gun/connection_pool/worker_supervisor.ex
+++ b/lib/pleroma/gun/connection_pool/worker_supervisor.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
diff --git a/lib/pleroma/hashtag.ex b/lib/pleroma/hashtag.ex
index 53e2e9c89..a43d88220 100644
--- a/lib/pleroma/hashtag.ex
+++ b/lib/pleroma/hashtag.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Hashtag do
diff --git a/lib/pleroma/healthcheck.ex b/lib/pleroma/healthcheck.ex
index c905bba3f..8e9ab8225 100644
--- a/lib/pleroma/healthcheck.ex
+++ b/lib/pleroma/healthcheck.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Healthcheck do
diff --git a/lib/pleroma/helpers/auth_helper.ex b/lib/pleroma/helpers/auth_helper.ex
index 13e4c8158..61599e71c 100644
--- a/lib/pleroma/helpers/auth_helper.ex
+++ b/lib/pleroma/helpers/auth_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Helpers.AuthHelper do
diff --git a/lib/pleroma/helpers/inet_helper.ex b/lib/pleroma/helpers/inet_helper.ex
index 5acdfaed0..704d37f8a 100644
--- a/lib/pleroma/helpers/inet_helper.ex
+++ b/lib/pleroma/helpers/inet_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Helpers.InetHelper do
diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex
index 738adfcaa..24c845fcd 100644
--- a/lib/pleroma/helpers/media_helper.ex
+++ b/lib/pleroma/helpers/media_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Helpers.MediaHelper do
diff --git a/lib/pleroma/helpers/qt_fast_start.ex b/lib/pleroma/helpers/qt_fast_start.ex
index c4d11b9dd..5711c7162 100644
--- a/lib/pleroma/helpers/qt_fast_start.ex
+++ b/lib/pleroma/helpers/qt_fast_start.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Helpers.QtFastStart do
diff --git a/lib/pleroma/helpers/uri_helper.ex b/lib/pleroma/helpers/uri_helper.ex
index 8f6a664ad..c8d10d307 100644
--- a/lib/pleroma/helpers/uri_helper.ex
+++ b/lib/pleroma/helpers/uri_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Helpers.UriHelper do
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index 2dfdca693..5bf735c4f 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTML do
@@ -49,31 +49,6 @@ defmodule Pleroma.HTML do
def filter_tags(html), do: filter_tags(html, nil)
def strip_tags(html), do: filter_tags(html, FastSanitize.Sanitizer.StripTags)
- def get_cached_scrubbed_html_for_activity(
- content,
- scrubbers,
- activity,
- key \\ "",
- callback \\ fn x -> x end
- ) do
- key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
-
- @cachex.fetch!(:scrubber_cache, key, fn _key ->
- object = Pleroma.Object.normalize(activity, fetch: false)
- ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
- end)
- end
-
- def get_cached_stripped_html_for_activity(content, activity, key) do
- get_cached_scrubbed_html_for_activity(
- content,
- FastSanitize.Sanitizer.StripTags,
- activity,
- key,
- &HtmlEntities.decode/1
- )
- end
-
def ensure_scrubbed_html(
content,
scrubbers,
@@ -92,16 +67,6 @@ defmodule Pleroma.HTML do
end
end
- defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
- generate_scrubber_signature([scrubber])
- end
-
- defp generate_scrubber_signature(scrubbers) do
- Enum.reduce(scrubbers, "", fn scrubber, signature ->
- "#{signature}#{to_string(scrubber)}"
- end)
- end
-
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
when is_binary(content) do
unless object.data["fake"] do
diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex
index 07b3ab0ae..d41061538 100644
--- a/lib/pleroma/http.ex
+++ b/lib/pleroma/http.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP do
@@ -106,5 +106,12 @@ defmodule Pleroma.HTTP do
[Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool]
end
- defp adapter_middlewares(_), do: []
+ defp adapter_middlewares(_) do
+ if Pleroma.Config.get(:env) == :test do
+ # Emulate redirects in test env, which are handled by adapters in other environments
+ [Tesla.Middleware.FollowRedirects]
+ else
+ []
+ end
+ end
end
diff --git a/lib/pleroma/http/adapter_helper.ex b/lib/pleroma/http/adapter_helper.ex
index c667afd25..252a6aba5 100644
--- a/lib/pleroma/http/adapter_helper.ex
+++ b/lib/pleroma/http/adapter_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper do
diff --git a/lib/pleroma/http/adapter_helper/default.ex b/lib/pleroma/http/adapter_helper/default.ex
index a1614b9c5..9c9414738 100644
--- a/lib/pleroma/http/adapter_helper/default.ex
+++ b/lib/pleroma/http/adapter_helper/default.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.Default do
diff --git a/lib/pleroma/http/adapter_helper/gun.ex b/lib/pleroma/http/adapter_helper/gun.ex
index 82c7fd654..74ab9851e 100644
--- a/lib/pleroma/http/adapter_helper/gun.ex
+++ b/lib/pleroma/http/adapter_helper/gun.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.Gun do
@@ -54,8 +54,8 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
Config.get([:pools, pool, :recv_timeout], default)
end
- @prefix Pleroma.Gun.ConnectionPool
def limiter_setup do
+ prefix = Pleroma.Gun.ConnectionPool
wait = Config.get([:connections_pool, :connection_acquisition_wait])
retries = Config.get([:connections_pool, :connection_acquisition_retries])
@@ -66,7 +66,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
max_waiting = Keyword.get(opts, :max_waiting, 10)
result =
- ConcurrentLimiter.new(:"#{@prefix}.#{name}", max_running, max_waiting,
+ ConcurrentLimiter.new(:"#{prefix}.#{name}", max_running, max_waiting,
wait: wait,
max_retries: retries
)
diff --git a/lib/pleroma/http/adapter_helper/hackney.ex b/lib/pleroma/http/adapter_helper/hackney.ex
index fe3f91a72..f3be1f3d0 100644
--- a/lib/pleroma/http/adapter_helper/hackney.ex
+++ b/lib/pleroma/http/adapter_helper/hackney.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.AdapterHelper.Hackney do
@@ -24,10 +24,6 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
end
- defp add_scheme_opts(opts, %URI{scheme: "https"}) do
- Keyword.put(opts, :ssl_options, versions: [:"tlsv1.2", :"tlsv1.1", :tlsv1])
- end
-
defp add_scheme_opts(opts, _), do: opts
defp maybe_add_with_body(opts) do
diff --git a/lib/pleroma/http/ex_aws.ex b/lib/pleroma/http/ex_aws.ex
index 283590b18..469c13819 100644
--- a/lib/pleroma/http/ex_aws.ex
+++ b/lib/pleroma/http/ex_aws.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.ExAws do
diff --git a/lib/pleroma/http/request.ex b/lib/pleroma/http/request.ex
index d906024de..01045f8c9 100644
--- a/lib/pleroma/http/request.ex
+++ b/lib/pleroma/http/request.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.Request do
diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex
index 631c927af..f16fb3b35 100644
--- a/lib/pleroma/http/request_builder.ex
+++ b/lib/pleroma/http/request_builder.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.RequestBuilder do
diff --git a/lib/pleroma/http/tzdata.ex b/lib/pleroma/http/tzdata.ex
index 77e1b537e..5d2529c08 100644
--- a/lib/pleroma/http/tzdata.ex
+++ b/lib/pleroma/http/tzdata.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.Tzdata do
diff --git a/lib/pleroma/http/web_push.ex b/lib/pleroma/http/web_push.ex
index 51f72fbf8..ca399b6c8 100644
--- a/lib/pleroma/http/web_push.ex
+++ b/lib/pleroma/http/web_push.ex
@@ -1,12 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.WebPush do
@moduledoc false
- def post(url, payload, headers) do
+ def post(url, payload, headers, options \\ []) do
list_headers = Map.to_list(headers)
- Pleroma.HTTP.post(url, payload, list_headers)
+ Pleroma.HTTP.post(url, payload, list_headers, options)
end
end
diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex
index 80addcc52..782948f83 100644
--- a/lib/pleroma/instances.ex
+++ b/lib/pleroma/instances.ex
@@ -1,17 +1,22 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Instances do
@moduledoc "Instances context."
- @adapter Pleroma.Instances.Instance
+ alias Pleroma.Instances.Instance
- defdelegate filter_reachable(urls_or_hosts), to: @adapter
- defdelegate reachable?(url_or_host), to: @adapter
- defdelegate set_reachable(url_or_host), to: @adapter
- defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
- defdelegate get_consistently_unreachable(), to: @adapter
+ def filter_reachable(urls_or_hosts), do: Instance.filter_reachable(urls_or_hosts)
+
+ def reachable?(url_or_host), do: Instance.reachable?(url_or_host)
+
+ def set_reachable(url_or_host), do: Instance.set_reachable(url_or_host)
+
+ def set_unreachable(url_or_host, unreachable_since \\ nil),
+ do: Instance.set_unreachable(url_or_host, unreachable_since)
+
+ def get_consistently_unreachable, do: Instance.get_consistently_unreachable()
def set_consistently_unreachable(url_or_host),
do: set_unreachable(url_or_host, reachability_datetime_threshold())
diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex
index 4d0e8034d..a5529ad44 100644
--- a/lib/pleroma/instances/instance.ex
+++ b/lib/pleroma/instances/instance.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Instances.Instance do
@@ -8,6 +8,8 @@ defmodule Pleroma.Instances.Instance do
alias Pleroma.Instances
alias Pleroma.Instances.Instance
alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.Workers.BackgroundWorker
use Ecto.Schema
@@ -195,4 +197,24 @@ defmodule Pleroma.Instances.Instance do
nil
end
end
+
+ @doc """
+ Deletes all users from an instance in a background task, thus also deleting
+ all of those users' activities and notifications.
+ """
+ def delete_users_and_activities(host) when is_binary(host) do
+ BackgroundWorker.enqueue("delete_instance", %{"host" => host})
+ end
+
+ def perform(:delete_instance, host) when is_binary(host) do
+ User.Query.build(%{nickname: "@#{host}"})
+ |> Repo.chunk_stream(100, :batches)
+ |> Stream.each(fn users ->
+ users
+ |> Enum.each(fn user ->
+ User.perform(:delete, user)
+ end)
+ end)
+ |> Stream.run()
+ end
end
diff --git a/lib/pleroma/job_queue_monitor.ex b/lib/pleroma/job_queue_monitor.ex
index b5f124923..6233cdcf5 100644
--- a/lib/pleroma/job_queue_monitor.ex
+++ b/lib/pleroma/job_queue_monitor.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.JobQueueMonitor do
diff --git a/lib/pleroma/jwt.ex b/lib/pleroma/jwt.ex
index c75c44bd1..7ec6245d3 100644
--- a/lib/pleroma/jwt.ex
+++ b/lib/pleroma/jwt.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.JWT do
diff --git a/lib/pleroma/keys.ex b/lib/pleroma/keys.ex
index 413861b15..496d20a71 100644
--- a/lib/pleroma/keys.ex
+++ b/lib/pleroma/keys.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Keys do
diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex
index fe5721c34..b446b91a0 100644
--- a/lib/pleroma/list.ex
+++ b/lib/pleroma/list.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.List do
diff --git a/lib/pleroma/logging.ex b/lib/pleroma/logging.ex
index 11e1c3bed..ac09c0415 100644
--- a/lib/pleroma/logging.ex
+++ b/lib/pleroma/logging.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Logging do
diff --git a/lib/pleroma/maintenance.ex b/lib/pleroma/maintenance.ex
index f1058b68a..eb5a6ef42 100644
--- a/lib/pleroma/maintenance.ex
+++ b/lib/pleroma/maintenance.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Maintenance do
@@ -9,7 +9,7 @@ defmodule Pleroma.Maintenance do
def vacuum(args) do
case args do
"analyze" ->
- Logger.info("Runnning VACUUM ANALYZE.")
+ Logger.info("Running VACUUM ANALYZE.")
Repo.query!(
"vacuum analyze;",
@@ -18,7 +18,7 @@ defmodule Pleroma.Maintenance do
)
"full" ->
- Logger.info("Runnning VACUUM FULL.")
+ Logger.info("Running VACUUM FULL.")
Logger.warn(
"Re-packing your entire database may take a while and will consume extra disk space during the process."
diff --git a/lib/pleroma/maps.ex b/lib/pleroma/maps.ex
index 0d2e94248..6d586e53e 100644
--- a/lib/pleroma/maps.ex
+++ b/lib/pleroma/maps.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Maps do
@@ -12,4 +12,10 @@ defmodule Pleroma.Maps do
_ -> map
end
end
+
+ def safe_put_in(data, keys, value) when is_map(data) and is_list(keys) do
+ Kernel.put_in(data, keys, value)
+ rescue
+ _ -> data
+ end
end
diff --git a/lib/pleroma/marker.ex b/lib/pleroma/marker.ex
index 9909de161..68b054e4d 100644
--- a/lib/pleroma/marker.ex
+++ b/lib/pleroma/marker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Marker do
diff --git a/lib/pleroma/mfa.ex b/lib/pleroma/mfa.ex
index 02dce7d49..01b730c76 100644
--- a/lib/pleroma/mfa.ex
+++ b/lib/pleroma/mfa.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MFA do
diff --git a/lib/pleroma/mfa/backup_codes.ex b/lib/pleroma/mfa/backup_codes.ex
index a7a1fba2e..2f6962c1f 100644
--- a/lib/pleroma/mfa/backup_codes.ex
+++ b/lib/pleroma/mfa/backup_codes.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MFA.BackupCodes do
diff --git a/lib/pleroma/mfa/changeset.ex b/lib/pleroma/mfa/changeset.ex
index 2d46cdf73..3ec3cfe91 100644
--- a/lib/pleroma/mfa/changeset.ex
+++ b/lib/pleroma/mfa/changeset.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MFA.Changeset do
diff --git a/lib/pleroma/mfa/settings.ex b/lib/pleroma/mfa/settings.ex
index 94fbff635..2c7f13e3f 100644
--- a/lib/pleroma/mfa/settings.ex
+++ b/lib/pleroma/mfa/settings.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MFA.Settings do
diff --git a/lib/pleroma/mfa/token.ex b/lib/pleroma/mfa/token.ex
index 76573182a..57bc11ed5 100644
--- a/lib/pleroma/mfa/token.ex
+++ b/lib/pleroma/mfa/token.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MFA.Token do
diff --git a/lib/pleroma/mfa/totp.ex b/lib/pleroma/mfa/totp.ex
index f33e3a379..429c4b700 100644
--- a/lib/pleroma/mfa/totp.ex
+++ b/lib/pleroma/mfa/totp.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MFA.TOTP do
diff --git a/lib/pleroma/migration_helper/notification_backfill.ex b/lib/pleroma/migration_helper/notification_backfill.ex
index 62b710f82..9f4976d9c 100644
--- a/lib/pleroma/migration_helper/notification_backfill.ex
+++ b/lib/pleroma/migration_helper/notification_backfill.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MigrationHelper.NotificationBackfill do
diff --git a/lib/pleroma/migrators/context_objects_deletion_migrator.ex b/lib/pleroma/migrators/context_objects_deletion_migrator.ex
new file mode 100644
index 000000000..fb224795a
--- /dev/null
+++ b/lib/pleroma/migrators/context_objects_deletion_migrator.ex
@@ -0,0 +1,139 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Migrators.ContextObjectsDeletionMigrator do
+ defmodule State do
+ use Pleroma.Migrators.Support.BaseMigratorState
+
+ @impl Pleroma.Migrators.Support.BaseMigratorState
+ defdelegate data_migration(), to: Pleroma.DataMigration, as: :delete_context_objects
+ end
+
+ use Pleroma.Migrators.Support.BaseMigrator
+
+ alias Pleroma.Migrators.Support.BaseMigrator
+ alias Pleroma.Object
+
+ @doc "This migration removes objects created exclusively for contexts, containing only an `id` field."
+
+ @impl BaseMigrator
+ def feature_config_path, do: [:features, :delete_context_objects]
+
+ @impl BaseMigrator
+ def fault_rate_allowance, do: Config.get([:delete_context_objects, :fault_rate_allowance], 0)
+
+ @impl BaseMigrator
+ def perform do
+ data_migration_id = data_migration_id()
+ max_processed_id = get_stat(:max_processed_id, 0)
+
+ Logger.info("Deleting context objects from `objects` (from oid: #{max_processed_id})...")
+
+ query()
+ |> where([object], object.id > ^max_processed_id)
+ |> Repo.chunk_stream(100, :batches, timeout: :infinity)
+ |> Stream.each(fn objects ->
+ object_ids = Enum.map(objects, & &1.id)
+
+ results = Enum.map(object_ids, &delete_context_object(&1))
+
+ failed_ids =
+ results
+ |> Enum.filter(&(elem(&1, 0) == :error))
+ |> Enum.map(&elem(&1, 1))
+
+ chunk_affected_count =
+ results
+ |> Enum.filter(&(elem(&1, 0) == :ok))
+ |> length()
+
+ for failed_id <- failed_ids do
+ _ =
+ Repo.query(
+ "INSERT INTO data_migration_failed_ids(data_migration_id, record_id) " <>
+ "VALUES ($1, $2) ON CONFLICT DO NOTHING;",
+ [data_migration_id, failed_id]
+ )
+ end
+
+ _ =
+ Repo.query(
+ "DELETE FROM data_migration_failed_ids " <>
+ "WHERE data_migration_id = $1 AND record_id = ANY($2)",
+ [data_migration_id, object_ids -- failed_ids]
+ )
+
+ max_object_id = Enum.at(object_ids, -1)
+
+ put_stat(:max_processed_id, max_object_id)
+ increment_stat(:iteration_processed_count, length(object_ids))
+ increment_stat(:processed_count, length(object_ids))
+ increment_stat(:failed_count, length(failed_ids))
+ increment_stat(:affected_count, chunk_affected_count)
+ put_stat(:records_per_second, records_per_second())
+ persist_state()
+
+ # A quick and dirty approach to controlling the load this background migration imposes
+ sleep_interval = Config.get([:delete_context_objects, :sleep_interval_ms], 0)
+ Process.sleep(sleep_interval)
+ end)
+ |> Stream.run()
+ end
+
+ @impl BaseMigrator
+ def query do
+ # Context objects have no activity type, and only one field, `id`.
+ # Only those context objects are without types.
+ from(
+ object in Object,
+ where: fragment("(?)->'type' IS NULL", object.data),
+ select: %{
+ id: object.id
+ }
+ )
+ end
+
+ @spec delete_context_object(integer()) :: {:ok | :error, integer()}
+ defp delete_context_object(id) do
+ result =
+ %Object{id: id}
+ |> Repo.delete()
+ |> elem(0)
+
+ {result, id}
+ end
+
+ @impl BaseMigrator
+ def retry_failed do
+ data_migration_id = data_migration_id()
+
+ failed_objects_query()
+ |> Repo.chunk_stream(100, :one)
+ |> Stream.each(fn object ->
+ with {res, _} when res != :error <- delete_context_object(object.id) do
+ _ =
+ Repo.query(
+ "DELETE FROM data_migration_failed_ids " <>
+ "WHERE data_migration_id = $1 AND record_id = $2",
+ [data_migration_id, object.id]
+ )
+ end
+ end)
+ |> Stream.run()
+
+ put_stat(:failed_count, failures_count())
+ persist_state()
+
+ force_continue()
+ end
+
+ defp failed_objects_query do
+ from(o in Object)
+ |> join(:inner, [o], dmf in fragment("SELECT * FROM data_migration_failed_ids"),
+ on: dmf.record_id == o.id
+ )
+ |> where([_o, dmf], dmf.data_migration_id == ^data_migration_id())
+ |> order_by([o], asc: o.id)
+ end
+end
diff --git a/lib/pleroma/migrators/hashtags_table_migrator.ex b/lib/pleroma/migrators/hashtags_table_migrator.ex
index b84058e11..dca4bfa6f 100644
--- a/lib/pleroma/migrators/hashtags_table_migrator.ex
+++ b/lib/pleroma/migrators/hashtags_table_migrator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Migrators.HashtagsTableMigrator do
@@ -183,7 +183,7 @@ defmodule Pleroma.Migrators.HashtagsTableMigrator do
DELETE FROM hashtags_objects WHERE object_id IN
(SELECT DISTINCT objects.id FROM objects
JOIN hashtags_objects ON hashtags_objects.object_id = objects.id LEFT JOIN activities
- ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') =
+ ON associated_object_id(activities) =
(objects.data->>'id')
AND activities.data->>'type' = 'Create'
WHERE activities.id IS NULL);
diff --git a/lib/pleroma/migrators/support/base_migrator.ex b/lib/pleroma/migrators/support/base_migrator.ex
index 1f8a5402b..3bcd59fd0 100644
--- a/lib/pleroma/migrators/support/base_migrator.ex
+++ b/lib/pleroma/migrators/support/base_migrator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Migrators.Support.BaseMigrator do
diff --git a/lib/pleroma/migrators/support/base_migrator_state.ex b/lib/pleroma/migrators/support/base_migrator_state.ex
index b698587f2..3d7769fc6 100644
--- a/lib/pleroma/migrators/support/base_migrator_state.ex
+++ b/lib/pleroma/migrators/support/base_migrator_state.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Migrators.Support.BaseMigratorState do
diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex
index 1849cacc8..7203423e2 100644
--- a/lib/pleroma/moderation_log.ex
+++ b/lib/pleroma/moderation_log.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ModerationLog do
@@ -341,6 +341,26 @@ defmodule Pleroma.ModerationLog do
def get_log_entry_message(%ModerationLog{
data: %{
"actor" => %{"nickname" => actor_nickname},
+ "action" => "add_suggestion",
+ "subject" => users
+ }
+ }) do
+ "@#{actor_nickname} added suggested users: #{users_to_nicknames_string(users)}"
+ end
+
+ def get_log_entry_message(%ModerationLog{
+ data: %{
+ "actor" => %{"nickname" => actor_nickname},
+ "action" => "remove_suggestion",
+ "subject" => users
+ }
+ }) do
+ "@#{actor_nickname} removed suggested users: #{users_to_nicknames_string(users)}"
+ end
+
+ def get_log_entry_message(%ModerationLog{
+ data: %{
+ "actor" => %{"nickname" => actor_nickname},
"nicknames" => nicknames,
"tags" => tags,
"action" => "tag"
@@ -481,9 +501,7 @@ defmodule Pleroma.ModerationLog do
"visibility" => visibility
}
}) do
- "@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{
- visibility
- }'"
+ "@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}', visibility: '#{visibility}'"
end
def get_log_entry_message(%ModerationLog{
@@ -523,9 +541,7 @@ defmodule Pleroma.ModerationLog do
"subject" => subjects
}
}) do
- "@#{actor_nickname} re-sent confirmation email for users: #{
- users_to_nicknames_string(subjects)
- }"
+ "@#{actor_nickname} re-sent confirmation email for users: #{users_to_nicknames_string(subjects)}"
end
def get_log_entry_message(%ModerationLog{
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 7efbdc49a..48d467c59 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Notification do
@@ -72,6 +72,7 @@ defmodule Pleroma.Notification do
pleroma:emoji_reaction
pleroma:report
reblog
+ poll
}
def changeset(%Notification{} = notification, attrs) do
@@ -116,9 +117,8 @@ defmodule Pleroma.Notification do
|> join(:left, [n, a], object in Object,
on:
fragment(
- "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
+ "(?->>'id') = associated_object_id(?)",
object.data,
- a.data,
a.data
)
)
@@ -127,6 +127,7 @@ defmodule Pleroma.Notification do
|> where([user_actor: user_actor], user_actor.is_active)
|> exclude_notification_muted(user, exclude_notification_muted_opts)
|> exclude_blocked(user, exclude_blocked_opts)
+ |> exclude_blockers(user)
|> exclude_filtered(user)
|> exclude_visibility(opts)
end
@@ -140,6 +141,17 @@ defmodule Pleroma.Notification do
|> FollowingRelationship.keep_following_or_not_domain_blocked(user)
end
+ defp exclude_blockers(query, user) do
+ if Pleroma.Config.get([:activitypub, :blockers_visible]) == true do
+ query
+ else
+ blocker_ap_ids = User.incoming_relationships_ungrouped_ap_ids(user, [:block])
+
+ query
+ |> where([n, a], a.actor not in ^blocker_ap_ids)
+ end
+ end
+
defp exclude_notification_muted(query, _, %{@include_muted_option => true}) do
query
end
@@ -166,6 +178,7 @@ defmodule Pleroma.Notification do
from([_n, a, o] in query,
where:
fragment("not(?->>'content' ~* ?)", o.data, ^regex) or
+ fragment("?->>'content' is null", o.data) or
fragment("?->>'actor' = ?", o.data, ^user.ap_id)
)
end
@@ -180,13 +193,11 @@ defmodule Pleroma.Notification do
|> join(:left, [n, a], mutated_activity in Pleroma.Activity,
on:
fragment(
- "COALESCE((?->'object')->>'id', ?->>'object')",
- a.data,
+ "associated_object_id(?)",
a.data
) ==
fragment(
- "COALESCE((?->'object')->>'id', ?->>'object')",
- mutated_activity.data,
+ "associated_object_id(?)",
mutated_activity.data
) and
fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and
@@ -364,7 +375,7 @@ defmodule Pleroma.Notification do
end
def create_notifications(%Activity{data: %{"type" => type}} = activity, options)
- when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag"] do
+ when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag", "Update"] do
do_create_notifications(activity, options)
end
@@ -379,7 +390,7 @@ defmodule Pleroma.Notification do
notifications =
Enum.map(potential_receivers, fn user ->
do_send = do_send && user in enabled_receivers
- create_notification(activity, user, do_send)
+ create_notification(activity, user, do_send: do_send)
end)
|> Enum.reject(&is_nil/1)
@@ -418,6 +429,9 @@ defmodule Pleroma.Notification do
activity
|> type_from_activity_object()
+ "Update" ->
+ "update"
+
t ->
raise "No notification type for activity type #{t}"
end
@@ -435,15 +449,18 @@ defmodule Pleroma.Notification do
end
# TODO move to sql, too.
- def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true) do
- unless skip?(activity, user) do
+ def create_notification(%Activity{} = activity, %User{} = user, opts \\ []) do
+ do_send = Keyword.get(opts, :do_send, true)
+ type = Keyword.get(opts, :type, type_from_activity(activity))
+
+ unless skip?(activity, user, opts) do
{:ok, %{notification: notification}} =
Multi.new()
|> Multi.insert(:notification, %Notification{
user_id: user.id,
activity: activity,
seen: mark_as_read?(activity, user),
- type: type_from_activity(activity)
+ type: type
})
|> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction()
@@ -457,6 +474,28 @@ defmodule Pleroma.Notification do
end
end
+ def create_poll_notifications(%Activity{} = activity) do
+ with %Object{data: %{"type" => "Question", "actor" => actor} = data} <-
+ Object.normalize(activity) do
+ voters =
+ case data do
+ %{"voters" => voters} when is_list(voters) -> voters
+ _ -> []
+ end
+
+ notifications =
+ Enum.reduce([actor | voters], [], fn ap_id, acc ->
+ with %User{local: true} = user <- User.get_by_ap_id(ap_id) do
+ [create_notification(activity, user, type: "poll") | acc]
+ else
+ _ -> acc
+ end
+ end)
+
+ {:ok, notifications}
+ end
+ end
+
@doc """
Returns a tuple with 2 elements:
{notification-enabled receivers, currently disabled receivers (blocking / [thread] muting)}
@@ -467,7 +506,16 @@ defmodule Pleroma.Notification do
def get_notified_from_activity(activity, local_only \\ true)
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
- when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag"] do
+ when type in [
+ "Create",
+ "Like",
+ "Announce",
+ "Follow",
+ "Move",
+ "EmojiReact",
+ "Flag",
+ "Update"
+ ] do
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
potential_receivers =
@@ -504,7 +552,24 @@ defmodule Pleroma.Notification do
end
def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag", "actor" => actor}}) do
- (User.all_superusers() |> Enum.map(fn user -> user.ap_id end)) -- [actor]
+ (User.all_users_with_privilege(:reports_manage_reports)
+ |> Enum.map(fn user -> user.ap_id end)) --
+ [actor]
+ end
+
+ # Update activity: notify all who repeated this
+ def get_potential_receiver_ap_ids(%{data: %{"type" => "Update", "actor" => actor}} = activity) do
+ with %Object{data: %{"id" => object_id}} <- Object.normalize(activity, fetch: false) do
+ repeaters =
+ Activity.Queries.by_type("Announce")
+ |> Activity.Queries.by_object_id(object_id)
+ |> Activity.with_joined_user_actor()
+ |> where([a, u], u.local)
+ |> select([a, u], u.ap_id)
+ |> Repo.all()
+
+ repeaters -- [actor]
+ end
end
def get_potential_receiver_ap_ids(activity) do
@@ -572,8 +637,10 @@ defmodule Pleroma.Notification do
Enum.uniq(ap_ids) -- thread_muter_ap_ids
end
- @spec skip?(Activity.t(), User.t()) :: boolean()
- def skip?(%Activity{} = activity, %User{} = user) do
+ def skip?(activity, user, opts \\ [])
+
+ @spec skip?(Activity.t(), User.t(), Keyword.t()) :: boolean()
+ def skip?(%Activity{} = activity, %User{} = user, opts) do
[
:self,
:invisible,
@@ -581,17 +648,21 @@ defmodule Pleroma.Notification do
:recently_followed,
:filtered
]
- |> Enum.find(&skip?(&1, activity, user))
+ |> Enum.find(&skip?(&1, activity, user, opts))
end
- def skip?(_, _), do: false
+ def skip?(_activity, _user, _opts), do: false
- @spec skip?(atom(), Activity.t(), User.t()) :: boolean()
- def skip?(:self, %Activity{} = activity, %User{} = user) do
- activity.data["actor"] == user.ap_id
+ @spec skip?(atom(), Activity.t(), User.t(), Keyword.t()) :: boolean()
+ def skip?(:self, %Activity{} = activity, %User{} = user, opts) do
+ cond do
+ opts[:type] == "poll" -> false
+ activity.data["actor"] == user.ap_id -> true
+ true -> false
+ end
end
- def skip?(:invisible, %Activity{} = activity, _) do
+ def skip?(:invisible, %Activity{} = activity, _user, _opts) do
actor = activity.data["actor"]
user = User.get_cached_by_ap_id(actor)
User.invisible?(user)
@@ -600,15 +671,27 @@ defmodule Pleroma.Notification do
def skip?(
:block_from_strangers,
%Activity{} = activity,
- %User{notification_settings: %{block_from_strangers: true}} = user
+ %User{notification_settings: %{block_from_strangers: true}} = user,
+ opts
) do
actor = activity.data["actor"]
follower = User.get_cached_by_ap_id(actor)
- !User.following?(follower, user)
+
+ cond do
+ opts[:type] == "poll" -> false
+ user.ap_id == actor -> false
+ !User.following?(user, follower) -> true
+ true -> false
+ end
end
# To do: consider defining recency in hours and checking FollowingRelationship with a single SQL
- def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity, %User{} = user) do
+ def skip?(
+ :recently_followed,
+ %Activity{data: %{"type" => "Follow"}} = activity,
+ %User{} = user,
+ _opts
+ ) do
actor = activity.data["actor"]
Notification.for_user(user)
@@ -618,9 +701,10 @@ defmodule Pleroma.Notification do
end)
end
- def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false
+ def skip?(:filtered, %{data: %{"type" => type}}, _user, _opts) when type in ["Follow", "Move"],
+ do: false
- def skip?(:filtered, activity, user) do
+ def skip?(:filtered, activity, user, _opts) do
object = Object.normalize(activity, fetch: false)
cond do
@@ -638,7 +722,7 @@ defmodule Pleroma.Notification do
end
end
- def skip?(_, _, _), do: false
+ def skip?(_type, _activity, _user, _opts), do: false
def mark_as_read?(activity, target_user) do
user = Activity.user_actor(activity)
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index 3ba749d1a..aa137d250 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object do
@@ -40,8 +40,7 @@ defmodule Pleroma.Object do
join(query, join_type, [{object, object_position}], a in Activity,
on:
fragment(
- "COALESCE(?->'object'->>'id', ?->>'object') = (? ->> 'id') AND (?->>'type' = ?) ",
- a.data,
+ "associated_object_id(?) = (? ->> 'id') AND (?->>'type' = ?) ",
a.data,
object.data,
a.data,
@@ -145,7 +144,7 @@ defmodule Pleroma.Object do
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
end
- def normalize(_, options \\ [fetch: false])
+ def normalize(_, options \\ [fetch: false, id_only: false])
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
# Use this whenever possible, especially when walking graphs in an O(N) loop!
@@ -173,10 +172,15 @@ defmodule Pleroma.Object do
def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
def normalize(ap_id, options) when is_binary(ap_id) do
- if Keyword.get(options, :fetch) do
- Fetcher.fetch_object_from_id!(ap_id, options)
- else
- get_cached_by_ap_id(ap_id)
+ cond do
+ Keyword.get(options, :id_only) ->
+ ap_id
+
+ Keyword.get(options, :fetch) ->
+ Fetcher.fetch_object_from_id!(ap_id, options)
+
+ true ->
+ get_cached_by_ap_id(ap_id)
end
end
@@ -208,10 +212,6 @@ defmodule Pleroma.Object do
end
end
- def context_mapping(context) do
- Object.change(%Object{}, %{data: %{"id" => context}})
- end
-
def make_tombstone(%Object{data: %{"id" => id, "type" => type}}, deleted \\ DateTime.utc_now()) do
%ObjectTombstone{
id: id,
@@ -366,7 +366,7 @@ defmodule Pleroma.Object do
end
def local?(%Object{data: %{"id" => id}}) do
- String.starts_with?(id, Pleroma.Web.base_url() <> "/")
+ String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
end
def replies(object, opts \\ []) do
@@ -425,4 +425,30 @@ defmodule Pleroma.Object do
end
def object_data_hashtags(_), do: []
+
+ def get_emoji_reactions(object) do
+ reactions = object.data["reactions"]
+
+ if is_list(reactions) or is_map(reactions) do
+ reactions
+ |> Enum.map(fn
+ [_emoji, users, _maybe_url] = item when is_list(users) ->
+ item
+
+ [emoji, users] when is_list(users) ->
+ [emoji, users, nil]
+
+ # This case is here to process the Map situation, which will happen
+ # only with the legacy two-value format.
+ {emoji, users} when is_list(users) ->
+ [emoji, users, nil]
+
+ _ ->
+ nil
+ end)
+ |> Enum.reject(&is_nil/1)
+ else
+ []
+ end
+ end
end
diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex
index 040537acf..f6106cb3f 100644
--- a/lib/pleroma/object/containment.ex
+++ b/lib/pleroma/object/containment.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.Containment do
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index bcccf1c4c..cc3772563 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -1,48 +1,37 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.Fetcher do
alias Pleroma.HTTP
+ alias Pleroma.Instances
+ alias Pleroma.Maps
alias Pleroma.Object
alias Pleroma.Object.Containment
- alias Pleroma.Repo
alias Pleroma.Signature
alias Pleroma.Web.ActivityPub.InternalFetchActor
+ alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.ObjectValidator
+ alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.Federator
require Logger
require Pleroma.Constants
- defp touch_changeset(changeset) do
- updated_at =
- NaiveDateTime.utc_now()
- |> NaiveDateTime.truncate(:second)
-
- Ecto.Changeset.put_change(changeset, :updated_at, updated_at)
- end
-
- defp maybe_reinject_internal_fields(%{data: %{} = old_data}, new_data) do
- internal_fields = Map.take(old_data, Pleroma.Constants.object_internal_fields())
-
- Map.merge(new_data, internal_fields)
- end
-
- defp maybe_reinject_internal_fields(_, new_data), do: new_data
-
@spec reinject_object(struct(), map()) :: {:ok, Object.t()} | {:error, any()}
- defp reinject_object(%Object{data: %{"type" => "Question"}} = object, new_data) do
+ defp reinject_object(%Object{data: %{}} = object, new_data) do
Logger.debug("Reinjecting object #{new_data["id"]}")
- with data <- maybe_reinject_internal_fields(object, new_data),
- {:ok, data, _} <- ObjectValidator.validate(data, %{}),
- changeset <- Object.change(object, %{data: data}),
- changeset <- touch_changeset(changeset),
- {:ok, object} <- Repo.insert_or_update(changeset),
- {:ok, object} <- Object.set_cache(object) do
- {:ok, object}
+ with {:ok, new_data, _} <- ObjectValidator.validate(new_data, %{}),
+ {:ok, new_data} <- MRF.filter(new_data),
+ {:ok, new_object, _} <-
+ Object.Updater.do_update_and_invalidate_cache(
+ object,
+ new_data,
+ _touch_changeset? = true
+ ) do
+ {:ok, new_object}
else
e ->
Logger.error("Error while processing object: #{inspect(e)}")
@@ -50,20 +39,11 @@ defmodule Pleroma.Object.Fetcher do
end
end
- defp reinject_object(%Object{} = object, new_data) do
- Logger.debug("Reinjecting object #{new_data["id"]}")
-
- with new_data <- Transmogrifier.fix_object(new_data),
- data <- maybe_reinject_internal_fields(object, new_data),
- changeset <- Object.change(object, %{data: data}),
- changeset <- touch_changeset(changeset),
- {:ok, object} <- Repo.insert_or_update(changeset),
- {:ok, object} <- Object.set_cache(object) do
+ defp reinject_object(_, new_data) do
+ with {:ok, object, _} <- Pipeline.common_pipeline(new_data, local: false) do
{:ok, object}
else
- e ->
- Logger.error("Error while processing object: #{inspect(e)}")
- {:error, e}
+ e -> e
end
end
@@ -101,6 +81,9 @@ defmodule Pleroma.Object.Fetcher do
{:transmogrifier, {:error, {:reject, e}}} ->
{:reject, e}
+ {:transmogrifier, {:reject, e}} ->
+ {:reject, e}
+
{:transmogrifier, _} = e ->
{:error, e}
@@ -124,12 +107,14 @@ defmodule Pleroma.Object.Fetcher do
defp prepare_activity_params(data) do
%{
"type" => "Create",
- "to" => data["to"] || [],
- "cc" => data["cc"] || [],
# Should we seriously keep this attributedTo thing?
"actor" => data["actor"] || data["attributedTo"],
"object" => data
}
+ |> Maps.put_if_present("to", data["to"])
+ |> Maps.put_if_present("cc", data["cc"])
+ |> Maps.put_if_present("bto", data["bto"])
+ |> Maps.put_if_present("bcc", data["bcc"])
end
def fetch_object_from_id!(id, options \\ []) do
@@ -194,6 +179,10 @@ defmodule Pleroma.Object.Fetcher do
{:ok, body} <- get_object(id),
{:ok, data} <- safe_json_decode(body),
:ok <- Containment.contain_origin_from_id(id, data) do
+ if not Instances.reachable?(id) do
+ Instances.set_reachable(id)
+ end
+
{:ok, data}
else
{:scheme, _} ->
diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex
new file mode 100644
index 000000000..b1e4870ba
--- /dev/null
+++ b/lib/pleroma/object/updater.ex
@@ -0,0 +1,290 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Object.Updater do
+ require Pleroma.Constants
+
+ alias Pleroma.Object
+ alias Pleroma.Repo
+
+ def update_content_fields(orig_object_data, updated_object) do
+ Pleroma.Constants.status_updatable_fields()
+ |> Enum.reduce(
+ %{data: orig_object_data, updated: false},
+ fn field, %{data: data, updated: updated} ->
+ updated =
+ updated or
+ (field != "updated" and
+ Map.get(updated_object, field) != Map.get(orig_object_data, field))
+
+ data =
+ if Map.has_key?(updated_object, field) do
+ Map.put(data, field, updated_object[field])
+ else
+ Map.drop(data, [field])
+ end
+
+ %{data: data, updated: updated}
+ end
+ )
+ end
+
+ def maybe_history(object) do
+ with history <- Map.get(object, "formerRepresentations"),
+ true <- is_map(history),
+ "OrderedCollection" <- Map.get(history, "type"),
+ true <- is_list(Map.get(history, "orderedItems")),
+ true <- is_integer(Map.get(history, "totalItems")) do
+ history
+ else
+ _ -> nil
+ end
+ end
+
+ def history_for(object) do
+ with history when not is_nil(history) <- maybe_history(object) do
+ history
+ else
+ _ -> history_skeleton()
+ end
+ end
+
+ defp history_skeleton do
+ %{
+ "type" => "OrderedCollection",
+ "totalItems" => 0,
+ "orderedItems" => []
+ }
+ end
+
+ def maybe_update_history(
+ updated_object,
+ orig_object_data,
+ opts
+ ) do
+ updated = opts[:updated]
+ use_history_in_new_object? = opts[:use_history_in_new_object?]
+
+ if not updated do
+ %{updated_object: updated_object, used_history_in_new_object?: false}
+ else
+ # Put edit history
+ # Note that we may have got the edit history by first fetching the object
+ {new_history, used_history_in_new_object?} =
+ with true <- use_history_in_new_object?,
+ updated_history when not is_nil(updated_history) <- maybe_history(opts[:new_data]) do
+ {updated_history, true}
+ else
+ _ ->
+ history = history_for(orig_object_data)
+
+ latest_history_item =
+ orig_object_data
+ |> Map.drop(["id", "formerRepresentations"])
+
+ updated_history =
+ history
+ |> Map.put("orderedItems", [latest_history_item | history["orderedItems"]])
+ |> Map.put("totalItems", history["totalItems"] + 1)
+
+ {updated_history, false}
+ end
+
+ updated_object =
+ updated_object
+ |> Map.put("formerRepresentations", new_history)
+
+ %{updated_object: updated_object, used_history_in_new_object?: used_history_in_new_object?}
+ end
+ end
+
+ defp maybe_update_poll(to_be_updated, updated_object) do
+ choice_key = fn
+ %{"anyOf" => [_ | _]} -> "anyOf"
+ %{"oneOf" => [_ | _]} -> "oneOf"
+ _ -> nil
+ end
+
+ with true <- to_be_updated["type"] == "Question",
+ key when not is_nil(key) <- choice_key.(updated_object),
+ true <- key == choice_key.(to_be_updated),
+ orig_choices <- to_be_updated[key] |> Enum.map(&Map.drop(&1, ["replies"])),
+ new_choices <- updated_object[key] |> Enum.map(&Map.drop(&1, ["replies"])),
+ true <- orig_choices == new_choices do
+ # Choices are the same, but counts are different
+ to_be_updated
+ |> Map.put(key, updated_object[key])
+ else
+ # Choices (or vote type) have changed, do not allow this
+ _ -> to_be_updated
+ end
+ end
+
+ # This calculates the data to be sent as the object of an Update.
+ # new_data's formerRepresentations is not considered.
+ # formerRepresentations is added to the returned data.
+ def make_update_object_data(original_data, new_data, date) do
+ %{data: updated_data, updated: updated} =
+ original_data
+ |> update_content_fields(new_data)
+
+ if not updated do
+ updated_data
+ else
+ %{updated_object: updated_data} =
+ updated_data
+ |> maybe_update_history(original_data, updated: updated, use_history_in_new_object?: false)
+
+ updated_data
+ |> Map.put("updated", date)
+ end
+ end
+
+ # This calculates the data of the new Object from an Update.
+ # new_data's formerRepresentations is considered.
+ def make_new_object_data_from_update_object(original_data, new_data) do
+ update_is_reasonable =
+ with {_, updated} when not is_nil(updated) <- {:cur_updated, new_data["updated"]},
+ {_, {:ok, updated_time, _}} <- {:cur_updated, DateTime.from_iso8601(updated)},
+ {_, last_updated} when not is_nil(last_updated) <-
+ {:last_updated, original_data["updated"] || original_data["published"]},
+ {_, {:ok, last_updated_time, _}} <-
+ {:last_updated, DateTime.from_iso8601(last_updated)},
+ :gt <- DateTime.compare(updated_time, last_updated_time) do
+ :update_everything
+ else
+ # only allow poll updates
+ {:cur_updated, _} -> :no_content_update
+ :eq -> :no_content_update
+ # allow all updates
+ {:last_updated, _} -> :update_everything
+ # allow no updates
+ _ -> false
+ end
+
+ %{
+ updated_object: updated_data,
+ used_history_in_new_object?: used_history_in_new_object?,
+ updated: updated
+ } =
+ if update_is_reasonable == :update_everything do
+ %{data: updated_data, updated: updated} =
+ original_data
+ |> update_content_fields(new_data)
+
+ updated_data
+ |> maybe_update_history(original_data,
+ updated: updated,
+ use_history_in_new_object?: true,
+ new_data: new_data
+ )
+ |> Map.put(:updated, updated)
+ else
+ %{
+ updated_object: original_data,
+ used_history_in_new_object?: false,
+ updated: false
+ }
+ end
+
+ updated_data =
+ if update_is_reasonable != false do
+ updated_data
+ |> maybe_update_poll(new_data)
+ else
+ updated_data
+ end
+
+ %{
+ updated_data: updated_data,
+ updated: updated,
+ used_history_in_new_object?: used_history_in_new_object?
+ }
+ end
+
+ def for_each_history_item(%{"orderedItems" => items} = history, _object, fun) do
+ new_items =
+ Enum.map(items, fun)
+ |> Enum.reduce_while(
+ {:ok, []},
+ fn
+ {:ok, item}, {:ok, acc} -> {:cont, {:ok, acc ++ [item]}}
+ e, _acc -> {:halt, e}
+ end
+ )
+
+ case new_items do
+ {:ok, items} -> {:ok, Map.put(history, "orderedItems", items)}
+ e -> e
+ end
+ end
+
+ def for_each_history_item(history, _, _) do
+ {:ok, history}
+ end
+
+ def do_with_history(object, fun) do
+ with history <- object["formerRepresentations"],
+ object <- Map.drop(object, ["formerRepresentations"]),
+ {_, {:ok, object}} <- {:main_body, fun.(object)},
+ {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do
+ object =
+ if history do
+ Map.put(object, "formerRepresentations", history)
+ else
+ object
+ end
+
+ {:ok, object}
+ else
+ {:main_body, e} -> e
+ {:history_items, e} -> e
+ end
+ end
+
+ defp maybe_touch_changeset(changeset, true) do
+ updated_at =
+ NaiveDateTime.utc_now()
+ |> NaiveDateTime.truncate(:second)
+
+ Ecto.Changeset.put_change(changeset, :updated_at, updated_at)
+ end
+
+ defp maybe_touch_changeset(changeset, _), do: changeset
+
+ def do_update_and_invalidate_cache(orig_object, updated_object, touch_changeset? \\ false) do
+ orig_object_ap_id = updated_object["id"]
+ orig_object_data = orig_object.data
+
+ %{
+ updated_data: updated_object_data,
+ updated: updated,
+ used_history_in_new_object?: used_history_in_new_object?
+ } = make_new_object_data_from_update_object(orig_object_data, updated_object)
+
+ changeset =
+ orig_object
+ |> Repo.preload(:hashtags)
+ |> Object.change(%{data: updated_object_data})
+ |> maybe_touch_changeset(touch_changeset?)
+
+ with {:ok, new_object} <- Repo.update(changeset),
+ {:ok, _} <- Object.invalid_object_cache(new_object),
+ {:ok, _} <- Object.set_cache(new_object),
+ # The metadata/utils.ex uses the object id for the cache.
+ {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(new_object.id) do
+ if used_history_in_new_object? do
+ with create_activity when not is_nil(create_activity) <-
+ Pleroma.Activity.get_create_by_object_ap_id(orig_object_ap_id),
+ {:ok, _} <- Pleroma.Activity.HTML.invalidate_cache_for(create_activity.id) do
+ nil
+ else
+ _ -> nil
+ end
+ end
+
+ {:ok, new_object, updated}
+ end
+ end
+end
diff --git a/lib/pleroma/object_tombstone.ex b/lib/pleroma/object_tombstone.ex
index a42d2d9a0..8bdc8f661 100644
--- a/lib/pleroma/object_tombstone.ex
+++ b/lib/pleroma/object_tombstone.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ObjectTombstone do
diff --git a/lib/pleroma/otp_version.ex b/lib/pleroma/otp_version.ex
index a5ac1b072..80b15275a 100644
--- a/lib/pleroma/otp_version.ex
+++ b/lib/pleroma/otp_version.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.OTPVersion do
diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex
index 33e45a0eb..f12ca2819 100644
--- a/lib/pleroma/pagination.ex
+++ b/lib/pleroma/pagination.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Pagination do
diff --git a/lib/pleroma/password/pbkdf2.ex b/lib/pleroma/password/pbkdf2.ex
index 2fd5f4491..92e9e1952 100644
--- a/lib/pleroma/password/pbkdf2.ex
+++ b/lib/pleroma/password/pbkdf2.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Password.Pbkdf2 do
diff --git a/lib/pleroma/password_reset_token.ex b/lib/pleroma/password_reset_token.ex
index edc8ed6a0..42a789ea2 100644
--- a/lib/pleroma/password_reset_token.ex
+++ b/lib/pleroma/password_reset_token.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.PasswordResetToken do
diff --git a/lib/pleroma/registration.ex b/lib/pleroma/registration.ex
index 7b49618e1..b043c37ac 100644
--- a/lib/pleroma/registration.ex
+++ b/lib/pleroma/registration.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Registration do
diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex
index 1e06aafe4..f9e8d1948 100644
--- a/lib/pleroma/release_tasks.ex
+++ b/lib/pleroma/release_tasks.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReleaseTasks do
diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex
index b8ea06e33..515b0c1ff 100644
--- a/lib/pleroma/repo.ex
+++ b/lib/pleroma/repo.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Repo do
@@ -8,8 +8,6 @@ defmodule Pleroma.Repo do
adapter: Ecto.Adapters.Postgres,
migration_timestamps: [type: :naive_datetime_usec]
- use Ecto.Explain
-
import Ecto.Query
require Logger
diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex
index f8bab1548..f2ad76fa8 100644
--- a/lib/pleroma/report_note.ex
+++ b/lib/pleroma/report_note.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReportNote do
diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex
index 406f7e2b8..2248c2713 100644
--- a/lib/pleroma/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy do
@@ -411,7 +411,7 @@ defmodule Pleroma.ReverseProxy do
{:ok, :no_duration_limit, :no_duration_limit}
end
- defp client, do: Pleroma.ReverseProxy.Client
+ defp client, do: Pleroma.ReverseProxy.Client.Wrapper
defp track_failed_url(url, error, opts) do
ttl =
diff --git a/lib/pleroma/reverse_proxy/client.ex b/lib/pleroma/reverse_proxy/client.ex
index 8698fa2e1..91f6e5a95 100644
--- a/lib/pleroma/reverse_proxy/client.ex
+++ b/lib/pleroma/reverse_proxy/client.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy.Client do
@@ -17,22 +17,4 @@ defmodule Pleroma.ReverseProxy.Client do
@callback stream_body(map()) :: {:ok, binary(), map()} | :done | {:error, atom() | String.t()}
@callback close(reference() | pid() | map()) :: :ok
-
- def request(method, url, headers, body \\ "", opts \\ []) do
- client().request(method, url, headers, body, opts)
- end
-
- def stream_body(ref), do: client().stream_body(ref)
-
- def close(ref), do: client().close(ref)
-
- defp client do
- :tesla
- |> Application.get_env(:adapter)
- |> client()
- end
-
- defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
- defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
- defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
end
diff --git a/lib/pleroma/reverse_proxy/client/hackney.ex b/lib/pleroma/reverse_proxy/client/hackney.ex
index dba946308..d3e986912 100644
--- a/lib/pleroma/reverse_proxy/client/hackney.ex
+++ b/lib/pleroma/reverse_proxy/client/hackney.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy.Client.Hackney do
@@ -7,7 +7,6 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
@impl true
def request(method, url, headers, body, opts \\ []) do
- opts = Keyword.put(opts, :ssl_options, versions: [:"tlsv1.2", :"tlsv1.1", :tlsv1])
:hackney.request(method, url, headers, body, opts)
end
diff --git a/lib/pleroma/reverse_proxy/client/tesla.ex b/lib/pleroma/reverse_proxy/client/tesla.ex
index 36a0a2060..4596d7a7f 100644
--- a/lib/pleroma/reverse_proxy/client/tesla.ex
+++ b/lib/pleroma/reverse_proxy/client/tesla.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy.Client.Tesla do
diff --git a/lib/pleroma/reverse_proxy/client/wrapper.ex b/lib/pleroma/reverse_proxy/client/wrapper.ex
new file mode 100644
index 000000000..1ce476927
--- /dev/null
+++ b/lib/pleroma/reverse_proxy/client/wrapper.ex
@@ -0,0 +1,30 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.ReverseProxy.Client.Wrapper do
+ @moduledoc "Meta-client that calls the appropriate client from the config."
+ @behaviour Pleroma.ReverseProxy.Client
+
+ @impl true
+ def request(method, url, headers, body \\ "", opts \\ []) do
+ client().request(method, url, headers, body, opts)
+ end
+
+ @impl true
+ def stream_body(ref), do: client().stream_body(ref)
+
+ @impl true
+ def close(ref), do: client().close(ref)
+
+ defp client do
+ :tesla
+ |> Application.get_env(:adapter)
+ |> client()
+ end
+
+ defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
+ defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
+ defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney
+ defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
+end
diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex
index 2b156341f..a7be58512 100644
--- a/lib/pleroma/scheduled_activity.ex
+++ b/lib/pleroma/scheduled_activity.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ScheduledActivity do
diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex
index 43ab569a4..5cfdae051 100644
--- a/lib/pleroma/signature.ex
+++ b/lib/pleroma/signature.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Signature do
@@ -10,17 +10,14 @@ defmodule Pleroma.Signature do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ @known_suffixes ["/publickey", "/main-key"]
+
def key_id_to_actor_id(key_id) do
uri =
- URI.parse(key_id)
+ key_id
+ |> URI.parse()
|> Map.put(:fragment, nil)
-
- uri =
- if not is_nil(uri.path) and String.ends_with?(uri.path, "/publickey") do
- Map.put(uri, :path, String.replace(uri.path, "/publickey", ""))
- else
- uri
- end
+ |> remove_suffix(@known_suffixes)
maybe_ap_id = URI.to_string(uri)
@@ -36,6 +33,16 @@ defmodule Pleroma.Signature do
end
end
+ defp remove_suffix(uri, [test | rest]) do
+ if not is_nil(uri.path) and String.ends_with?(uri.path, test) do
+ Map.put(uri, :path, String.replace(uri.path, test, ""))
+ else
+ remove_suffix(uri, rest)
+ end
+ end
+
+ defp remove_suffix(uri, []), do: uri
+
def fetch_public_key(conn) do
with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn),
{:ok, actor_id} <- key_id_to_actor_id(kid),
@@ -59,9 +66,8 @@ defmodule Pleroma.Signature do
end
end
- def sign(%User{} = user, headers) do
- with {:ok, %{keys: keys}} <- User.ensure_keys_present(user),
- {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
+ def sign(%User{keys: keys} = user, headers) do
+ with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
end
end
diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex
index 3e3f24c2c..47b30b951 100644
--- a/lib/pleroma/stats.ex
+++ b/lib/pleroma/stats.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Stats do
diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex
index 44d2f48dc..384c70fbc 100644
--- a/lib/pleroma/telemetry/logger.ex
+++ b/lib/pleroma/telemetry/logger.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Telemetry.Logger do
@@ -29,9 +29,7 @@ defmodule Pleroma.Telemetry.Logger do
_
) do
Logger.debug(fn ->
- "Connection pool is exhausted (reached #{max_connections} connections). Starting idle connection cleanup to reclaim as much as #{
- reclaim_max
- } connections"
+ "Connection pool is exhausted (reached #{max_connections} connections). Starting idle connection cleanup to reclaim as much as #{reclaim_max} connections"
end)
end
@@ -73,9 +71,7 @@ defmodule Pleroma.Telemetry.Logger do
_
) do
Logger.warn(fn ->
- "Pool worker for #{key}: Client #{inspect(client_pid)} died before releasing the connection with #{
- inspect(reason)
- }"
+ "Pool worker for #{key}: Client #{inspect(client_pid)} died before releasing the connection with #{inspect(reason)}"
end)
end
diff --git a/lib/pleroma/tesla/middleware/connection_pool.ex b/lib/pleroma/tesla/middleware/connection_pool.ex
index 906706d39..de74270f8 100644
--- a/lib/pleroma/tesla/middleware/connection_pool.ex
+++ b/lib/pleroma/tesla/middleware/connection_pool.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Tesla.Middleware.ConnectionPool do
diff --git a/lib/pleroma/tests/auth_test_controller.ex b/lib/pleroma/tests/auth_test_controller.ex
index ddf3fea4f..d244badf4 100644
--- a/lib/pleroma/tests/auth_test_controller.ex
+++ b/lib/pleroma/tests/auth_test_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# A test controller reachable only in :test env.
@@ -9,7 +9,6 @@ defmodule Pleroma.Tests.AuthTestController do
use Pleroma.Web, :controller
alias Pleroma.User
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Web.Plugs.OAuthScopesPlug
# Serves only with proper OAuth token (:api and :authenticated_api)
@@ -47,10 +46,7 @@ defmodule Pleroma.Tests.AuthTestController do
# Via :authenticated_api, serves if token is present and has requested scopes
#
# Suggested use: as :fallback_oauth_check but open with nil :user for :api on private instances
- plug(
- :skip_plug,
- EnsurePublicOrAuthenticatedPlug when action == :fallback_oauth_skip_publicity_check
- )
+ plug(:skip_public_check when action == :fallback_oauth_skip_publicity_check)
plug(
OAuthScopesPlug,
@@ -62,11 +58,7 @@ defmodule Pleroma.Tests.AuthTestController do
# Via :authenticated_api, serves if :user is set (regardless of token presence and its scopes)
#
# Suggested use: making an :api endpoint always accessible (e.g. email confirmation endpoint)
- plug(
- :skip_plug,
- [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
- when action == :skip_oauth_skip_publicity_check
- )
+ plug(:skip_auth when action == :skip_oauth_skip_publicity_check)
# Via :authenticated_api, always fails with 403 (endpoint is insecure)
# Via :api, drops :user if present and serves if public (private instance rejects on no user)
diff --git a/lib/pleroma/thread_mute.ex b/lib/pleroma/thread_mute.ex
index 5d06cf030..8ea4181bd 100644
--- a/lib/pleroma/thread_mute.ex
+++ b/lib/pleroma/thread_mute.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ThreadMute do
diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex
index 654711351..4aee9326f 100644
--- a/lib/pleroma/upload.ex
+++ b/lib/pleroma/upload.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload do
@@ -23,6 +23,9 @@ defmodule Pleroma.Upload do
is once created permanent and changing it (especially in uploaders) is probably a bad idea!
* `:tempfile` - path to the temporary file. Prefer in-place changes on the file rather than changing the
path as the temporary file is also tracked by `Plug.Upload{}` and automatically deleted once the request is over.
+ * `:width` - width of the media in pixels
+ * `:height` - height of the media in pixels
+ * `:blurhash` - string hash of the image encoded with the blurhash algorithm (https://blurha.sh/)
Related behaviors:
@@ -32,6 +35,8 @@ defmodule Pleroma.Upload do
"""
alias Ecto.UUID
alias Pleroma.Config
+ alias Pleroma.Maps
+ alias Pleroma.Web.ActivityPub.Utils
require Logger
@type source ::
@@ -53,12 +58,26 @@ defmodule Pleroma.Upload do
name: String.t(),
tempfile: String.t(),
content_type: String.t(),
+ width: integer(),
+ height: integer(),
+ blurhash: String.t(),
+ description: String.t(),
path: String.t()
}
- defstruct [:id, :name, :tempfile, :content_type, :path]
-
- defp get_description(opts, upload) do
- case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do
+ defstruct [
+ :id,
+ :name,
+ :tempfile,
+ :content_type,
+ :width,
+ :height,
+ :blurhash,
+ :description,
+ :path
+ ]
+
+ defp get_description(upload) do
+ case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do
{description, _} when is_binary(description) -> description
{_, :filename} -> upload.name
{_, str} when is_binary(str) -> str
@@ -74,13 +93,14 @@ defmodule Pleroma.Upload do
with {:ok, upload} <- prepare_upload(upload, opts),
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
- description = get_description(opts, upload),
+ description = get_description(upload),
{_, true} <-
{:description_limit,
String.length(description) <= Pleroma.Config.get([:instance, :description_limit])},
{:ok, url_spec} <- Pleroma.Uploaders.Uploader.put_file(opts.uploader, upload) do
{:ok,
%{
+ "id" => Utils.generate_object_id(),
"type" => opts.activity_type,
"mediaType" => upload.content_type,
"url" => [
@@ -89,9 +109,12 @@ defmodule Pleroma.Upload do
"mediaType" => upload.content_type,
"href" => url_from_spec(upload, opts.base_url, url_spec)
}
+ |> Maps.put_if_present("width", upload.width)
+ |> Maps.put_if_present("height", upload.height)
],
"name" => description
- }}
+ }
+ |> Maps.put_if_present("blurhash", upload.blurhash)}
else
{:description_limit, _} ->
{:error, :description_too_long}
@@ -142,7 +165,8 @@ defmodule Pleroma.Upload do
id: UUID.generate(),
name: file.filename,
tempfile: file.path,
- content_type: file.content_type
+ content_type: file.content_type,
+ description: opts.description
}}
end
end
@@ -162,7 +186,8 @@ defmodule Pleroma.Upload do
id: UUID.generate(),
name: hash <> "." <> ext,
tempfile: tmp_path,
- content_type: content_type
+ content_type: content_type,
+ description: opts.description
}}
end
end
@@ -225,7 +250,7 @@ defmodule Pleroma.Upload do
case uploader do
Pleroma.Uploaders.Local ->
- upload_base_url || Pleroma.Web.base_url() <> "/media/"
+ upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
Pleroma.Uploaders.S3 ->
bucket = Config.get([Pleroma.Uploaders.S3, :bucket])
@@ -251,7 +276,7 @@ defmodule Pleroma.Upload do
end
_ ->
- public_endpoint || upload_base_url || Pleroma.Web.base_url() <> "/media/"
+ public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
end
end
end
diff --git a/lib/pleroma/upload/filter.ex b/lib/pleroma/upload/filter.ex
index c677d4b9f..809bc6e70 100644
--- a/lib/pleroma/upload/filter.ex
+++ b/lib/pleroma/upload/filter.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter do
@@ -15,13 +15,13 @@ defmodule Pleroma.Upload.Filter do
require Logger
- @callback filter(Pleroma.Upload.t()) ::
+ @callback filter(upload :: struct()) ::
{:ok, :filtered}
| {:ok, :noop}
- | {:ok, :filtered, Pleroma.Upload.t()}
+ | {:ok, :filtered, upload :: struct()}
| {:error, any()}
- @spec filter([module()], Pleroma.Upload.t()) :: {:ok, Pleroma.Upload.t()} | {:error, any()}
+ @spec filter([module()], upload :: struct()) :: {:ok, upload :: struct()} | {:error, any()}
def filter([], upload) do
{:ok, upload}
@@ -38,9 +38,9 @@ defmodule Pleroma.Upload.Filter do
{:ok, :noop} ->
filter(rest, upload)
- error ->
- Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(error)}")
- error
+ {:error, e} ->
+ Logger.error("#{__MODULE__}: Filter #{filter} failed: #{inspect(e)}")
+ {:error, e}
end
end
end
diff --git a/lib/pleroma/upload/filter/analyze_metadata.ex b/lib/pleroma/upload/filter/analyze_metadata.ex
new file mode 100644
index 000000000..9a76a998b
--- /dev/null
+++ b/lib/pleroma/upload/filter/analyze_metadata.ex
@@ -0,0 +1,83 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
+ @moduledoc """
+ Extracts metadata about the upload, such as width/height
+ """
+ require Logger
+
+ @behaviour Pleroma.Upload.Filter
+
+ @spec filter(Pleroma.Upload.t()) ::
+ {:ok, :filtered, Pleroma.Upload.t()} | {:ok, :noop} | {:error, String.t()}
+ def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do
+ try do
+ image =
+ file
+ |> Mogrify.open()
+ |> Mogrify.verbose()
+
+ upload =
+ upload
+ |> Map.put(:width, image.width)
+ |> Map.put(:height, image.height)
+ |> Map.put(:blurhash, get_blurhash(file))
+
+ {:ok, :filtered, upload}
+ rescue
+ e in ErlangError ->
+ Logger.warn("#{__MODULE__}: #{inspect(e)}")
+ {:ok, :noop}
+ end
+ end
+
+ def filter(%Pleroma.Upload{tempfile: file, content_type: "video" <> _} = upload) do
+ try do
+ result = media_dimensions(file)
+
+ upload =
+ upload
+ |> Map.put(:width, result.width)
+ |> Map.put(:height, result.height)
+
+ {:ok, :filtered, upload}
+ rescue
+ e in ErlangError ->
+ Logger.warn("#{__MODULE__}: #{inspect(e)}")
+ {:ok, :noop}
+ end
+ end
+
+ def filter(_), do: {:ok, :noop}
+
+ defp get_blurhash(file) do
+ with {:ok, blurhash} <- :eblurhash.magick(file) do
+ blurhash
+ else
+ _ -> nil
+ end
+ end
+
+ defp media_dimensions(file) do
+ with executable when is_binary(executable) <- System.find_executable("ffprobe"),
+ args = [
+ "-v",
+ "error",
+ "-show_entries",
+ "stream=width,height",
+ "-of",
+ "csv=p=0:s=x",
+ file
+ ],
+ {result, 0} <- System.cmd(executable, args),
+ [width, height] <-
+ String.split(String.trim(result), "x") |> Enum.map(&String.to_integer(&1)) do
+ %{width: width, height: height}
+ else
+ nil -> {:error, {:ffprobe, :command_not_found}}
+ {:error, _} = error -> error
+ end
+ end
+end
diff --git a/lib/pleroma/upload/filter/anonymize_filename.ex b/lib/pleroma/upload/filter/anonymize_filename.ex
index 7e62b3d13..234ccb6bb 100644
--- a/lib/pleroma/upload/filter/anonymize_filename.ex
+++ b/lib/pleroma/upload/filter/anonymize_filename.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.AnonymizeFilename do
diff --git a/lib/pleroma/upload/filter/dedupe.ex b/lib/pleroma/upload/filter/dedupe.ex
index 2bf581b05..ef793d390 100644
--- a/lib/pleroma/upload/filter/dedupe.ex
+++ b/lib/pleroma/upload/filter/dedupe.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.Dedupe do
diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex
new file mode 100644
index 000000000..543b22031
--- /dev/null
+++ b/lib/pleroma/upload/filter/exiftool/read_description.ex
@@ -0,0 +1,52 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do
+ @moduledoc """
+ Gets a valid description from the related EXIF tags and provides them in the response if no description is provided yet.
+ It will first check ImageDescription, when that doesn't probide a valid description, it will check iptc:Caption-Abstract.
+ A valid description means the fields are filled in and not too long (see `:instance, :description_limit`).
+ """
+ @behaviour Pleroma.Upload.Filter
+
+ @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()}
+
+ def filter(%Pleroma.Upload{description: description})
+ when is_binary(description),
+ do: {:ok, :noop}
+
+ def filter(%Pleroma.Upload{tempfile: file} = upload),
+ do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))}
+
+ def filter(_, _), do: {:ok, :noop}
+
+ defp read_description_from_exif_data(file) do
+ nil
+ |> read_when_empty(file, "-ImageDescription")
+ |> read_when_empty(file, "-iptc:Caption-Abstract")
+ end
+
+ defp read_when_empty(current_description, _, _) when is_binary(current_description),
+ do: current_description
+
+ defp read_when_empty(_, file, tag) do
+ try do
+ {tag_content, 0} =
+ System.cmd("exiftool", ["-b", "-s3", tag, file],
+ stderr_to_stdout: false,
+ parallelism: true
+ )
+
+ tag_content = String.trim(tag_content)
+
+ if tag_content != "" and
+ String.length(tag_content) <=
+ Pleroma.Config.get([:instance, :description_limit]),
+ do: tag_content,
+ else: nil
+ rescue
+ _ in ErlangError -> nil
+ end
+ end
+end
diff --git a/lib/pleroma/upload/filter/exiftool.ex b/lib/pleroma/upload/filter/exiftool/strip_location.ex
index a2bfbbf61..f2bcc4622 100644
--- a/lib/pleroma/upload/filter/exiftool.ex
+++ b/lib/pleroma/upload/filter/exiftool/strip_location.ex
@@ -1,8 +1,8 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Upload.Filter.Exiftool do
+defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do
@moduledoc """
Strips GPS related EXIF tags and overwrites the file in place.
Also strips or replaces filesystem metadata e.g., timestamps.
@@ -14,6 +14,7 @@ defmodule Pleroma.Upload.Filter.Exiftool do
# Formats not compatible with exiftool at this time
def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop}
def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop}
+ def filter(%Pleroma.Upload{content_type: "image/svg" <> _}), do: {:ok, :noop}
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
try do
diff --git a/lib/pleroma/upload/filter/mogrifun.ex b/lib/pleroma/upload/filter/mogrifun.ex
index 01126aaeb..a0f247b70 100644
--- a/lib/pleroma/upload/filter/mogrifun.ex
+++ b/lib/pleroma/upload/filter/mogrifun.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.Mogrifun do
diff --git a/lib/pleroma/upload/filter/mogrify.ex b/lib/pleroma/upload/filter/mogrify.ex
index f27aefc22..06efbf321 100644
--- a/lib/pleroma/upload/filter/mogrify.ex
+++ b/lib/pleroma/upload/filter/mogrify.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.Mogrify do
diff --git a/lib/pleroma/upload/filter/only_media.ex b/lib/pleroma/upload/filter/only_media.ex
new file mode 100644
index 000000000..a9caeba67
--- /dev/null
+++ b/lib/pleroma/upload/filter/only_media.ex
@@ -0,0 +1,20 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.OnlyMedia do
+ @behaviour Pleroma.Upload.Filter
+ alias Pleroma.Upload
+
+ def filter(%Upload{content_type: content_type}) do
+ [type, _subtype] = String.split(content_type, "/")
+
+ if type in ["image", "video", "audio"] do
+ {:ok, :noop}
+ else
+ {:error, "Disallowed content-type: #{content_type}"}
+ end
+ end
+
+ def filter(_), do: {:ok, :noop}
+end
diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex
index 0e1ba4b90..e4a309cea 100644
--- a/lib/pleroma/uploaders/local.ex
+++ b/lib/pleroma/uploaders/local.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.Local do
diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex
index d85c8cb2f..19287c532 100644
--- a/lib/pleroma/uploaders/s3.ex
+++ b/lib/pleroma/uploaders/s3.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.S3 do
diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex
index 0be878ca2..77f6f02dd 100644
--- a/lib/pleroma/uploaders/uploader.ex
+++ b/lib/pleroma/uploaders/uploader.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.Uploader do
@@ -35,7 +35,7 @@ defmodule Pleroma.Uploaders.Uploader do
"""
@type file_spec :: {:file | :url, String.t()}
- @callback put_file(Pleroma.Upload.t()) ::
+ @callback put_file(upload :: struct()) ::
:ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
@@ -46,7 +46,7 @@ defmodule Pleroma.Uploaders.Uploader do
| {:error, Plug.Conn.t(), String.t()}
@optional_callbacks http_callback: 2
- @spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()}
+ @spec put_file(module(), upload :: struct()) :: {:ok, file_spec()} | {:error, String.t()}
def put_file(uploader, upload) do
case uploader.put_file(upload) do
:ok -> {:ok, {:file, upload.path}}
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index b78777141..ce125d608 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User do
@@ -27,13 +27,13 @@ defmodule Pleroma.User do
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.UserRelationship
- alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.OAuth
alias Pleroma.Web.RelMe
alias Pleroma.Workers.BackgroundWorker
@@ -78,6 +78,10 @@ defmodule Pleroma.User do
inverse_subscription: [
subscribee_subscriptions: :subscriber_users,
subscriber_subscriptions: :subscribee_users
+ ],
+ endorsement: [
+ endorser_endorsements: :endorsed_users,
+ endorsee_endorsements: :endorser_users
]
]
@@ -120,11 +124,9 @@ defmodule Pleroma.User do
field(:domain_blocks, {:array, :string}, default: [])
field(:is_active, :boolean, default: true)
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(:mastofe_settings, :map, default: nil)
field(:uri, ObjectValidators.Uri, default: nil)
field(:hide_followers_count, :boolean, default: false)
field(:hide_follows_count, :boolean, default: false)
@@ -149,6 +151,11 @@ defmodule Pleroma.User do
field(:last_active_at, :naive_datetime)
field(:disclose_client, :boolean, default: true)
field(:pinned_objects, :map, default: %{})
+ field(:is_suggested, :boolean, default: false)
+ field(:last_status_at, :naive_datetime)
+ field(:birthday, :date)
+ field(:show_birthday, :boolean, default: false)
+ field(:language, :string)
embeds_one(
:notification_settings,
@@ -169,25 +176,25 @@ defmodule Pleroma.User do
{incoming_relation, incoming_relation_source}
]} <- @user_relationships_config do
# Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
- # :notification_muter_mutes, :subscribee_subscriptions
+ # :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
has_many(outgoing_relation, UserRelationship,
foreign_key: :source_id,
where: [relationship_type: relationship_type]
)
# Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
- # :notification_mutee_mutes, :subscriber_subscriptions
+ # :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
has_many(incoming_relation, UserRelationship,
foreign_key: :target_id,
where: [relationship_type: relationship_type]
)
# Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
- # :notification_muted_users, :subscriber_users
+ # :notification_muted_users, :subscriber_users, :endorsed_users
has_many(outgoing_relation_target, through: [outgoing_relation, :target])
# Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
- # :notification_muter_users, :subscribee_users
+ # :notification_muter_users, :subscribee_users, :endorser_users
has_many(incoming_relation_source, through: [incoming_relation, :source])
end
@@ -215,7 +222,7 @@ defmodule Pleroma.User do
@user_relationships_config do
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
- # `def subscriber_users/2`
+ # `def subscriber_users/2`, `def endorsed_users_relation/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
target_users_query = assoc(user, unquote(outgoing_relation_target))
@@ -228,7 +235,7 @@ defmodule Pleroma.User do
end
# `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
- # `def notification_muted_users/2`, `def subscriber_users/2`
+ # `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
__MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@@ -239,7 +246,8 @@ defmodule Pleroma.User do
end
# `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
- # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`
+ # `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
+ # `def endorsed_users_ap_ids/2`
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
__MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@@ -317,7 +325,7 @@ defmodule Pleroma.User do
end
def visible_for(%User{} = user, for_user) do
- if superuser?(for_user) do
+ if privileged?(for_user, :users_manage_activation_state) do
:visible
else
visible_account_status(user)
@@ -344,10 +352,45 @@ defmodule Pleroma.User do
end
end
- @spec superuser?(User.t()) :: boolean()
- def superuser?(%User{local: true, is_admin: true}), do: true
- def superuser?(%User{local: true, is_moderator: true}), do: true
- def superuser?(_), do: false
+ @spec privileged?(User.t(), atom()) :: boolean()
+ def privileged?(%User{is_admin: false, is_moderator: false}, _), do: false
+
+ def privileged?(
+ %User{local: true, is_admin: is_admin, is_moderator: is_moderator},
+ privilege_tag
+ ),
+ do:
+ privileged_for?(privilege_tag, is_admin, :admin_privileges) or
+ privileged_for?(privilege_tag, is_moderator, :moderator_privileges)
+
+ def privileged?(_, _), do: false
+
+ defp privileged_for?(privilege_tag, true, config_role_key),
+ do: privilege_tag in Config.get([:instance, config_role_key])
+
+ defp privileged_for?(_, _, _), do: false
+
+ @spec privileges(User.t()) :: [atom()]
+ def privileges(%User{local: false}) do
+ []
+ end
+
+ def privileges(%User{is_moderator: false, is_admin: false}) do
+ []
+ end
+
+ def privileges(%User{local: true, is_moderator: true, is_admin: true}) do
+ (Config.get([:instance, :moderator_privileges]) ++ Config.get([:instance, :admin_privileges]))
+ |> Enum.uniq()
+ end
+
+ def privileges(%User{local: true, is_moderator: true, is_admin: false}) do
+ Config.get([:instance, :moderator_privileges])
+ end
+
+ def privileges(%User{local: true, is_moderator: false, is_admin: true}) do
+ Config.get([:instance, :admin_privileges])
+ end
@spec invisible?(User.t()) :: boolean()
def invisible?(%User{invisible: true}), do: true
@@ -360,7 +403,7 @@ defmodule Pleroma.User do
_ ->
unless options[:no_default] do
- Config.get([:assets, :default_user_avatar], "#{Web.base_url()}/images/avi.png")
+ Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
end
end
end
@@ -368,13 +411,13 @@ defmodule Pleroma.User do
def banner_url(user, options \\ []) do
case user.banner do
%{"url" => [%{"href" => href} | _]} -> href
- _ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png"
+ _ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
end
end
# Should probably be renamed or removed
@spec ap_id(User.t()) :: String.t()
- def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
+ def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
@spec ap_followers(User.t()) :: String.t()
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
@@ -444,7 +487,6 @@ defmodule Pleroma.User do
:nickname,
:public_key,
:avatar,
- :ap_enabled,
:banner,
:is_locked,
:last_refreshed_at,
@@ -464,7 +506,9 @@ defmodule Pleroma.User do
:actor_type,
:also_known_as,
:accepts_chat_messages,
- :pinned_objects
+ :pinned_objects,
+ :birthday,
+ :show_birthday
]
)
|> cast(params, [:name], empty_values: [])
@@ -525,9 +569,12 @@ defmodule Pleroma.User do
:is_discoverable,
:actor_type,
:accepts_chat_messages,
- :disclose_client
+ :disclose_client,
+ :birthday,
+ :show_birthday
]
)
+ |> validate_min_age()
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: bio_limit)
@@ -597,7 +644,13 @@ defmodule Pleroma.User do
{:ok, new_value} <- value_function.(value) do
put_change(changeset, map_field, new_value)
else
- _ -> changeset
+ {:error, :file_too_large} ->
+ Ecto.Changeset.validate_change(changeset, map_field, fn map_field, _value ->
+ [{map_field, "file is too large"}]
+ end)
+
+ _ ->
+ changeset
end
end
@@ -692,11 +745,12 @@ defmodule Pleroma.User do
])
|> validate_required([:name, :nickname])
|> unique_constraint(:nickname)
- |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
+ |> validate_not_restricted_nickname(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> put_ap_id()
|> unique_constraint(:ap_id)
|> put_following_and_follower_and_featured_address()
+ |> put_private_key()
end
def register_changeset(struct, params \\ %{}, opts \\ []) do
@@ -732,32 +786,58 @@ defmodule Pleroma.User do
:password_confirmation,
:emoji,
:accepts_chat_messages,
- :registration_reason
+ :registration_reason,
+ :birthday,
+ :language
])
|> validate_required([:name, :nickname, :password, :password_confirmation])
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> validate_format(:email, @email_regex)
- |> validate_change(:email, fn :email, email ->
- valid? =
- Config.get([User, :email_blacklist])
- |> Enum.all?(fn blacklisted_domain ->
- !String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain])
- end)
-
- if valid?, do: [], else: [email: "Invalid email"]
- end)
+ |> validate_email_not_in_blacklisted_domain(:email)
|> unique_constraint(:nickname)
- |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
+ |> validate_not_restricted_nickname(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
|> validate_length(:registration_reason, max: reason_limit)
|> maybe_validate_required_email(opts[:external])
+ |> maybe_validate_required_birthday
+ |> validate_min_age()
|> put_password_hash
|> put_ap_id()
|> unique_constraint(:ap_id)
|> put_following_and_follower_and_featured_address()
+ |> put_private_key()
+ end
+
+ def validate_not_restricted_nickname(changeset, field) do
+ validate_change(changeset, field, fn _, value ->
+ valid? =
+ Config.get([User, :restricted_nicknames])
+ |> Enum.all?(fn restricted_nickname ->
+ String.downcase(value) != String.downcase(restricted_nickname)
+ end)
+
+ if valid?, do: [], else: [nickname: "Invalid nickname"]
+ end)
+ end
+
+ def validate_email_not_in_blacklisted_domain(changeset, field) do
+ validate_change(changeset, field, fn _, value ->
+ valid? =
+ Config.get([User, :email_blacklist])
+ |> Enum.all?(fn blacklisted_domain ->
+ blacklisted_domain_downcase = String.downcase(blacklisted_domain)
+
+ !String.ends_with?(String.downcase(value), [
+ "@" <> blacklisted_domain_downcase,
+ "." <> blacklisted_domain_downcase
+ ])
+ end)
+
+ if valid?, do: [], else: [email: "Invalid email"]
+ end)
end
def maybe_validate_required_email(changeset, true), do: changeset
@@ -770,6 +850,26 @@ defmodule Pleroma.User do
end
end
+ defp maybe_validate_required_birthday(changeset) do
+ if Config.get([:instance, :birthday_required]) do
+ validate_required(changeset, [:birthday])
+ else
+ changeset
+ end
+ end
+
+ defp validate_min_age(changeset) do
+ changeset
+ |> validate_change(:birthday, fn :birthday, birthday ->
+ valid? =
+ Date.utc_today()
+ |> Date.diff(birthday) >=
+ Config.get([:instance, :birthday_min_age])
+
+ if valid?, do: [], else: [birthday: "Invalid age"]
+ end)
+ end
+
defp put_ap_id(changeset) do
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
put_change(changeset, :ap_id, ap_id)
@@ -787,6 +887,11 @@ defmodule Pleroma.User do
|> put_change(:featured_address, featured)
end
+ defp put_private_key(changeset) do
+ {:ok, pem} = Keys.generate_rsa_pem()
+ put_change(changeset, :keys, pem)
+ end
+
defp autofollow_users(user) do
candidates = Config.get([:instance, :autofollowed_nicknames])
@@ -839,7 +944,7 @@ defmodule Pleroma.User do
end
end
- defp send_user_approval_email(user) do
+ defp send_user_approval_email(%User{email: email} = user) when is_binary(email) do
user
|> Pleroma.Emails.UserEmail.approval_pending_email()
|> Pleroma.Emails.Mailer.deliver_async()
@@ -847,6 +952,10 @@ defmodule Pleroma.User do
{:ok, :enqueued}
end
+ defp send_user_approval_email(_user) do
+ {:ok, :skipped}
+ end
+
defp send_admin_approval_emails(user) do
all_superusers()
|> Enum.filter(fn user -> not is_nil(user.email) end)
@@ -950,11 +1059,7 @@ defmodule Pleroma.User do
end
def maybe_direct_follow(%User{} = follower, %User{} = followed) do
- if not ap_enabled?(followed) do
- follow(follower, followed)
- else
- {:ok, follower, followed}
- end
+ {:ok, follower, followed}
end
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
@@ -1049,6 +1154,10 @@ defmodule Pleroma.User do
Repo.get_by(User, ap_id: ap_id)
end
+ def get_by_uri(uri) do
+ Repo.get_by(User, uri: uri)
+ end
+
def get_all_by_ap_id(ap_ids) do
from(u in __MODULE__,
where: u.ap_id in ^ap_ids
@@ -1403,17 +1512,30 @@ defmodule Pleroma.User do
{:ok, list(UserRelationship.t())} | {:error, String.t()}
def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
notifications? = Map.get(params, :notifications, true)
- expires_in = Map.get(params, :expires_in, 0)
+ duration = Map.get(params, :duration, 0)
+
+ expires_at =
+ if duration > 0 do
+ DateTime.utc_now()
+ |> DateTime.add(duration)
+ else
+ nil
+ end
- with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee),
+ with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee, expires_at),
{:ok, user_notification_mute} <-
- (notifications? && UserRelationship.create_notification_mute(muter, mutee)) ||
+ (notifications? &&
+ UserRelationship.create_notification_mute(
+ muter,
+ mutee,
+ expires_at
+ )) ||
{:ok, nil} do
- if expires_in > 0 do
+ if duration > 0 do
Pleroma.Workers.MuteExpireWorker.enqueue(
"unmute_user",
%{"muter_id" => muter.id, "mutee_id" => mutee.id},
- schedule_in: expires_in
+ scheduled_at: expires_at
)
end
@@ -1484,13 +1606,19 @@ defmodule Pleroma.User do
blocker
end
- # clear any requested follows as well
+ # clear any requested follows from both sides as well
blocked =
case CommonAPI.reject_follow_request(blocked, blocker) do
{:ok, %User{} = updated_blocked} -> updated_blocked
nil -> blocked
end
+ blocker =
+ case CommonAPI.reject_follow_request(blocker, blocked) do
+ {:ok, %User{} = updated_blocker} -> updated_blocker
+ nil -> blocker
+ end
+
unsubscribe(blocked, blocker)
unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true)
@@ -1515,6 +1643,40 @@ defmodule Pleroma.User do
unblock(blocker, get_cached_by_ap_id(ap_id))
end
+ def endorse(%User{} = endorser, %User{} = target) do
+ with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
+ endorsed_users <-
+ User.endorsed_users_relation(endorser)
+ |> Repo.aggregate(:count, :id) do
+ cond do
+ endorsed_users >= max_endorsed_users ->
+ {:error, "You have already pinned the maximum number of users"}
+
+ not following?(endorser, target) ->
+ {:error, "Could not endorse: You are not following #{target.nickname}"}
+
+ true ->
+ UserRelationship.create_endorsement(endorser, target)
+ end
+ end
+ end
+
+ def endorse(%User{} = endorser, %{ap_id: ap_id}) do
+ with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
+ endorse(endorser, endorsed)
+ end
+ end
+
+ def unendorse(%User{} = unendorser, %User{} = target) do
+ UserRelationship.delete_endorsement(unendorser, target)
+ end
+
+ def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
+ with %User{} = user <- get_cached_by_ap_id(ap_id) do
+ unendorse(unendorser, user)
+ end
+ end
+
def mutes?(nil, _), do: false
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
@@ -1560,6 +1722,10 @@ defmodule Pleroma.User do
end
end
+ def endorses?(%User{} = user, %User{} = target) do
+ UserRelationship.endorsement_exists?(user, target)
+ end
+
@doc """
Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
@@ -1677,6 +1843,22 @@ defmodule Pleroma.User do
def confirm(%User{} = user), do: {:ok, user}
+ def set_suggestion(users, is_suggested) when is_list(users) do
+ Repo.transaction(fn ->
+ Enum.map(users, fn user ->
+ with {:ok, user} <- set_suggestion(user, is_suggested), do: user
+ end)
+ end)
+ end
+
+ def set_suggestion(%User{is_suggested: is_suggested} = user, is_suggested), do: {:ok, user}
+
+ def set_suggestion(%User{} = user, is_suggested) when is_boolean(is_suggested) do
+ user
+ |> change(is_suggested: is_suggested)
+ |> update_and_set_cache()
+ end
+
def update_notification_settings(%User{} = user, settings) do
user
|> cast(%{notification_settings: settings}, [])
@@ -1695,8 +1877,6 @@ defmodule Pleroma.User do
email: nil,
name: nil,
password_hash: nil,
- keys: nil,
- public_key: nil,
avatar: %{},
tags: [],
last_refreshed_at: nil,
@@ -1707,17 +1887,13 @@ defmodule Pleroma.User do
follower_count: 0,
following_count: 0,
is_locked: false,
- is_confirmed: true,
password_reset_pending: false,
- is_approved: true,
registration_reason: nil,
confirmation_token: nil,
domain_blocks: [],
is_active: false,
- ap_enabled: false,
is_moderator: false,
is_admin: false,
- mastofe_settings: nil,
mascot: nil,
emoji: %{},
pleroma_settings_store: %{},
@@ -1725,45 +1901,53 @@ defmodule Pleroma.User do
raw_fields: [],
is_discoverable: false,
also_known_as: []
+ # id: preserved
+ # ap_id: preserved
+ # nickname: preserved
})
end
+ # Purge doesn't delete the user from the database.
+ # It just nulls all its fields and deactivates it.
+ # See `User.purge_user_changeset/1` above.
+ defp purge(%User{} = user) do
+ user
+ |> purge_user_changeset()
+ |> 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
+ # Purge the user immediately
+ purge(user)
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
end
- defp delete_and_invalidate_cache(%User{} = user) do
+ # *Actually* delete the user from the DB
+ defp delete_from_db(%User{} = user) do
invalidate_cache(user)
Repo.delete(user)
end
- defp delete_or_deactivate(%User{local: false} = user), do: delete_and_invalidate_cache(user)
-
- defp delete_or_deactivate(%User{local: true} = user) do
- status = account_status(user)
+ # If the user never finalized their account, it's safe to delete them.
+ defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
+ do: delete_from_db(user)
- case status do
- :confirmation_pending ->
- delete_and_invalidate_cache(user)
+ defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
+ do: delete_from_db(user)
- :approval_pending ->
- delete_and_invalidate_cache(user)
-
- _ ->
- user
- |> purge_user_changeset()
- |> update_and_set_cache()
- end
- end
+ defp maybe_delete_from_db(user), do: {:ok, user}
def perform(:force_password_reset, user), do: force_password_reset(user)
@spec perform(atom(), User.t()) :: {:ok, User.t()}
def perform(:delete, %User{} = user) do
+ # Purge the user again, in case perform/2 is called directly
+ purge(user)
+
# Remove all relationships
user
|> get_followers()
@@ -1781,10 +1965,9 @@ defmodule Pleroma.User do
delete_user_activities(user)
delete_notifications_from_user_activities(user)
-
delete_outgoing_pending_follow_requests(user)
- delete_or_deactivate(user)
+ maybe_delete_from_db(user)
end
def perform(:set_activation_async, user, status), do: set_activation(user, status)
@@ -1934,6 +2117,7 @@ defmodule Pleroma.User do
follower_address: uri <> "/followers"
}
|> change
+ |> put_private_key()
|> unique_constraint(:nickname)
|> Repo.insert()
|> set_cache()
@@ -1960,13 +2144,10 @@ defmodule Pleroma.User do
end
end
- def ap_enabled?(%User{local: true}), do: true
- 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."
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
- def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
+ def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
+ def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
# wait a period of time and return newest version of the User structs
@@ -2094,6 +2275,11 @@ defmodule Pleroma.User do
|> Repo.all()
end
+ @spec all_users_with_privilege(atom()) :: [User.t()]
+ def all_users_with_privilege(privilege) do
+ User.Query.build(%{is_privileged: privilege}) |> Repo.all()
+ end
+
def muting_reblogs?(%User{} = user, %User{} = target) do
UserRelationship.reblog_mute_exists?(user, target)
end
@@ -2199,20 +2385,10 @@ defmodule Pleroma.User do
}
end
- def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user}
-
- def ensure_keys_present(%User{} = user) do
- with {:ok, pem} <- Keys.generate_rsa_pem() do
- user
- |> cast(%{keys: pem}, [:keys])
- |> validate_required([:keys])
- |> update_and_set_cache()
- end
- end
-
def get_ap_ids_by_nicknames(nicknames) do
from(u in User,
where: u.nickname in ^nicknames,
+ order_by: fragment("array_position(?, ?)", ^nicknames, u.nickname),
select: u.ap_id
)
|> Repo.all()
@@ -2245,12 +2421,44 @@ defmodule Pleroma.User do
def change_email(user, email) do
user
|> cast(%{email: email}, [:email])
- |> validate_required([:email])
+ |> maybe_validate_required_email(false)
|> unique_constraint(:email)
|> validate_format(:email, @email_regex)
|> update_and_set_cache()
end
+ def alias_users(user) do
+ user.also_known_as
+ |> Enum.map(&User.get_cached_by_ap_id/1)
+ |> Enum.filter(fn user -> user != nil end)
+ end
+
+ def add_alias(user, new_alias_user) do
+ current_aliases = user.also_known_as || []
+ new_alias_ap_id = new_alias_user.ap_id
+
+ if new_alias_ap_id in current_aliases do
+ {:ok, user}
+ else
+ user
+ |> cast(%{also_known_as: current_aliases ++ [new_alias_ap_id]}, [:also_known_as])
+ |> update_and_set_cache()
+ end
+ end
+
+ def delete_alias(user, alias_user) do
+ current_aliases = user.also_known_as || []
+ alias_ap_id = alias_user.ap_id
+
+ if alias_ap_id in current_aliases do
+ user
+ |> cast(%{also_known_as: current_aliases -- [alias_ap_id]}, [:also_known_as])
+ |> update_and_set_cache()
+ else
+ {:error, :no_such_alias}
+ end
+ end
+
# Internal function; public one is `deactivate/2`
defp set_activation_status(user, status) do
user
@@ -2328,13 +2536,6 @@ defmodule Pleroma.User do
|> update_and_set_cache()
end
- def mastodon_settings_update(user, settings) do
- user
- |> cast(%{mastofe_settings: settings}, [:mastofe_settings])
- |> validate_required([:mastofe_settings])
- |> update_and_set_cache()
- end
-
@spec confirmation_changeset(User.t(), keyword()) :: Changeset.t()
def confirmation_changeset(user, set_confirmation: confirmed?) do
params =
@@ -2480,12 +2681,33 @@ defmodule Pleroma.User do
|> update_and_set_cache()
end
- def active_user_count(weeks \\ 4) do
- active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks)
+ def active_user_count(days \\ 30) do
+ active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
__MODULE__
|> where([u], u.last_active_at >= ^active_after)
|> where([u], u.local == true)
|> Repo.aggregate(:count)
end
+
+ def update_last_status_at(user) do
+ User
+ |> where(id: ^user.id)
+ |> update([u], set: [last_status_at: fragment("NOW()")])
+ |> select([u], u)
+ |> Repo.update_all([])
+ |> case do
+ {1, [user]} -> set_cache(user)
+ _ -> {:error, user}
+ end
+ end
+
+ def get_friends_birthdays_query(%User{} = user, day, month) do
+ User.Query.build(%{
+ friends: user,
+ deactivated: false,
+ birthday_day: day,
+ birthday_month: month
+ })
+ end
end
diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex
index cba94248f..9df010605 100644
--- a/lib/pleroma/user/backup.ex
+++ b/lib/pleroma/user/backup.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Backup do
@@ -32,9 +32,7 @@ defmodule Pleroma.User.Backup do
end
def create(user, admin_id \\ nil) do
- with :ok <- validate_email_enabled(),
- :ok <- validate_user_email(user),
- :ok <- validate_limit(user, admin_id),
+ with :ok <- validate_limit(user, admin_id),
{:ok, backup} <- user |> new() |> Repo.insert() do
BackupWorker.process(backup, admin_id)
end
@@ -86,20 +84,6 @@ defmodule Pleroma.User.Backup do
end
end
- defp validate_email_enabled do
- if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
- :ok
- else
- {:error, dgettext("errors", "Backups require enabled email")}
- end
- end
-
- defp validate_user_email(%User{email: nil}) do
- {:error, dgettext("errors", "Email is required")}
- end
-
- defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok
-
def get_last(user_id) do
__MODULE__
|> where(user_id: ^user_id)
diff --git a/lib/pleroma/user/import.ex b/lib/pleroma/user/import.ex
index 60cd18041..4baa7e3a4 100644
--- a/lib/pleroma/user/import.ex
+++ b/lib/pleroma/user/import.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Import do
diff --git a/lib/pleroma/user/notification_setting.ex b/lib/pleroma/user/notification_setting.ex
index a7cd61499..3fb845d71 100644
--- a/lib/pleroma/user/notification_setting.ex
+++ b/lib/pleroma/user/notification_setting.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.NotificationSetting do
diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex
index fa46545da..3e090cac0 100644
--- a/lib/pleroma/user/query.ex
+++ b/lib/pleroma/user/query.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Query do
@@ -27,8 +27,9 @@ defmodule Pleroma.User.Query do
- e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]})
"""
import Ecto.Query
- import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1]
+ import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
+ alias Pleroma.Config
alias Pleroma.FollowingRelationship
alias Pleroma.User
@@ -46,7 +47,10 @@ defmodule Pleroma.User.Query do
unconfirmed: boolean(),
is_admin: boolean(),
is_moderator: boolean(),
+ is_suggested: boolean(),
+ is_discoverable: boolean(),
super_users: boolean(),
+ is_privileged: atom(),
invisible: boolean(),
internal: boolean(),
followers: User.t(),
@@ -57,7 +61,9 @@ defmodule Pleroma.User.Query do
order_by: term(),
select: term(),
limit: pos_integer(),
- actor_types: [String.t()]
+ actor_types: [String.t()],
+ birthday_day: pos_integer(),
+ birthday_month: pos_integer()
}
| map()
@@ -132,6 +138,43 @@ defmodule Pleroma.User.Query do
)
end
+ defp compose_query({:is_privileged, privilege}, query) do
+ moderator_privileged = privilege in Config.get([:instance, :moderator_privileges])
+ admin_privileged = privilege in Config.get([:instance, :admin_privileges])
+
+ query = compose_query({:active, true}, query)
+ query = compose_query({:local, true}, query)
+
+ case {admin_privileged, moderator_privileged} do
+ {false, false} ->
+ where(
+ query,
+ false
+ )
+
+ {true, true} ->
+ where(
+ query,
+ [u],
+ u.is_admin or u.is_moderator
+ )
+
+ {true, false} ->
+ where(
+ query,
+ [u],
+ u.is_admin
+ )
+
+ {false, true} ->
+ where(
+ query,
+ [u],
+ u.is_moderator
+ )
+ end
+ end
+
defp compose_query({:local, _}, query), do: location_query(query, true)
defp compose_query({:external, _}, query), do: location_query(query, false)
@@ -167,6 +210,14 @@ defmodule Pleroma.User.Query do
where(query, [u], u.is_confirmed == false)
end
+ defp compose_query({:is_suggested, bool}, query) do
+ where(query, [u], u.is_suggested == ^bool)
+ end
+
+ defp compose_query({:is_discoverable, bool}, query) do
+ where(query, [u], u.is_discoverable == ^bool)
+ end
+
defp compose_query({:followers, %User{id: id}}, query) do
query
|> where([u], u.id != ^id)
@@ -220,6 +271,20 @@ defmodule Pleroma.User.Query do
|> where([u], not like(u.nickname, "internal.%"))
end
+ defp compose_query({:birthday_day, day}, query) do
+ query
+ |> where([u], u.show_birthday == true)
+ |> where([u], not is_nil(u.birthday))
+ |> where([u], fragment("date_part('day', ?)", u.birthday) == ^day)
+ end
+
+ defp compose_query({:birthday_month, month}, query) do
+ query
+ |> where([u], u.show_birthday == true)
+ |> where([u], not is_nil(u.birthday))
+ |> where([u], fragment("date_part('month', ?)", u.birthday) == ^month)
+ end
+
defp compose_query(_unsupported_param, query), do: query
defp location_query(query, local) do
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index a4f6abca2..a7fb8fb83 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do
@@ -94,6 +94,7 @@ defmodule Pleroma.User.Search do
|> subquery()
|> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user)
+ |> filter_deactivated_users()
end
defp select_top_users(query, top_user_ids) do
@@ -166,6 +167,10 @@ defmodule Pleroma.User.Search do
from(q in query, where: q.actor_type != "Application")
end
+ defp filter_deactivated_users(query) do
+ from(q in query, where: q.is_active == true)
+ end
+
defp filter_blocked_user(query, %User{} = blocker) do
query
|> join(:left, [u], b in Pleroma.UserRelationship,
diff --git a/lib/pleroma/user/welcome_chat_message.ex b/lib/pleroma/user/welcome_chat_message.ex
index 0d6690e34..31e0bfaf6 100644
--- a/lib/pleroma/user/welcome_chat_message.ex
+++ b/lib/pleroma/user/welcome_chat_message.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.WelcomeChatMessage do
diff --git a/lib/pleroma/user/welcome_email.ex b/lib/pleroma/user/welcome_email.ex
index 295c1acc7..970975a49 100644
--- a/lib/pleroma/user/welcome_email.ex
+++ b/lib/pleroma/user/welcome_email.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.WelcomeEmail do
diff --git a/lib/pleroma/user/welcome_message.ex b/lib/pleroma/user/welcome_message.ex
index 2cff05549..6010e808c 100644
--- a/lib/pleroma/user/welcome_message.ex
+++ b/lib/pleroma/user/welcome_message.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.WelcomeMessage do
diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex
index 4cff1c515..b242a8848 100644
--- a/lib/pleroma/user_invite_token.ex
+++ b/lib/pleroma/user_invite_token.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserInviteToken do
diff --git a/lib/pleroma/user_note.ex b/lib/pleroma/user_note.ex
new file mode 100644
index 000000000..d4b8256b0
--- /dev/null
+++ b/lib/pleroma/user_note.ex
@@ -0,0 +1,52 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.UserNote do
+ use Ecto.Schema
+
+ import Ecto.Changeset
+ import Ecto.Query
+
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.UserNote
+
+ schema "user_notes" do
+ belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
+ belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
+ field(:comment, :string)
+
+ timestamps()
+ end
+
+ def changeset(%UserNote{} = user_note, params \\ %{}) do
+ user_note
+ |> cast(params, [:source_id, :target_id, :comment])
+ |> validate_required([:source_id, :target_id])
+ end
+
+ def show(%User{} = source, %User{} = target) do
+ with %UserNote{} = note <-
+ UserNote
+ |> where(source_id: ^source.id, target_id: ^target.id)
+ |> Repo.one() do
+ note.comment
+ else
+ _ -> ""
+ end
+ end
+
+ def create(%User{} = source, %User{} = target, comment) do
+ %UserNote{}
+ |> changeset(%{
+ source_id: source.id,
+ target_id: target.id,
+ comment: comment
+ })
+ |> Repo.insert(
+ on_conflict: {:replace, [:comment]},
+ conflict_target: [:source_id, :target_id]
+ )
+ end
+end
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index a467e9b65..fbecf3129 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserRelationship do
@@ -18,25 +18,35 @@ defmodule Pleroma.UserRelationship do
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
field(:relationship_type, Pleroma.UserRelationship.Type)
+ field(:expires_at, :utc_datetime)
timestamps(updated_at: false)
end
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
- # `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
- # `def create_notification_mute/2`, `def create_inverse_subscription/2`
- def unquote(:"create_#{relationship_type}")(source, target),
- do: create(unquote(relationship_type), source, target)
+ # `def create_block/3`, `def create_mute/3`, `def create_reblog_mute/3`,
+ # `def create_notification_mute/3`, `def create_inverse_subscription/3`,
+ # `def endorsement/3`
+ def unquote(:"create_#{relationship_type}")(source, target, expires_at \\ nil),
+ do: create(unquote(relationship_type), source, target, expires_at)
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
- # `def delete_notification_mute/2`, `def delete_inverse_subscription/2`
+ # `def delete_notification_mute/2`, `def delete_inverse_subscription/2`,
+ # `def delete_endorsement/2`
def unquote(:"delete_#{relationship_type}")(source, target),
do: delete(unquote(relationship_type), source, target)
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
- # `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`
+ # `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`,
+ # `def inverse_endorsement_exists?/2`
def unquote(:"#{relationship_type}_exists?")(source, target),
do: exists?(unquote(relationship_type), source, target)
+
+ # `def get_block_expire_date/2`, `def get_mute_expire_date/2`,
+ # `def get_reblog_mute_expire_date/2`, `def get_notification_mute_exists?/2`,
+ # `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2`
+ def unquote(:"get_#{relationship_type}_expire_date")(source, target),
+ do: get_expire_date(unquote(relationship_type), source, target)
end
def user_relationship_types, do: Keyword.keys(user_relationship_mappings())
@@ -45,7 +55,7 @@ defmodule Pleroma.UserRelationship do
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
user_relationship
- |> cast(params, [:relationship_type, :source_id, :target_id])
+ |> cast(params, [:relationship_type, :source_id, :target_id, :expires_at])
|> validate_required([:relationship_type, :source_id, :target_id])
|> unique_constraint(:relationship_type,
name: :user_relationships_source_id_relationship_type_target_id_index
@@ -59,16 +69,31 @@ defmodule Pleroma.UserRelationship do
|> Repo.exists?()
end
- def create(relationship_type, %User{} = source, %User{} = target) do
+ def get_expire_date(relationship_type, %User{} = source, %User{} = target) do
+ %UserRelationship{expires_at: expires_at} =
+ UserRelationship
+ |> where(
+ relationship_type: ^relationship_type,
+ source_id: ^source.id,
+ target_id: ^target.id
+ )
+ |> Repo.one!()
+
+ expires_at
+ end
+
+ def create(relationship_type, %User{} = source, %User{} = target, expires_at \\ nil) do
%UserRelationship{}
|> changeset(%{
relationship_type: relationship_type,
source_id: source.id,
- target_id: target.id
+ target_id: target.id,
+ expires_at: expires_at
})
|> Repo.insert(
- on_conflict: {:replace_all_except, [:id]},
- conflict_target: [:source_id, :relationship_type, :target_id]
+ on_conflict: {:replace_all_except, [:id, :inserted_at]},
+ conflict_target: [:source_id, :relationship_type, :target_id],
+ returning: true
)
end
diff --git a/lib/pleroma/utils.ex b/lib/pleroma/utils.ex
index a446d3ae6..73001c987 100644
--- a/lib/pleroma/utils.ex
+++ b/lib/pleroma/utils.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Utils do
diff --git a/lib/pleroma/web.ex b/lib/pleroma/web.ex
index 8630f244b..aee41b0fe 100644
--- a/lib/pleroma/web.ex
+++ b/lib/pleroma/web.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web do
@@ -35,9 +35,10 @@ defmodule Pleroma.Web do
import Plug.Conn
import Pleroma.Web.Gettext
- import Pleroma.Web.Router.Helpers
import Pleroma.Web.TranslationHelpers
+ alias Pleroma.Web.Router.Helpers, as: Routes
+
plug(:set_put_layout)
defp set_put_layout(conn, _) do
@@ -61,6 +62,14 @@ defmodule Pleroma.Web do
)
end
+ defp skip_auth(conn, _) do
+ skip_plug(conn, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug])
+ end
+
+ defp skip_public_check(conn, _) do
+ skip_plug(conn, EnsurePublicOrAuthenticatedPlug)
+ end
+
# Executed just before actual controller action, invokes before-action hooks (callbacks)
defp action(conn, params) do
with %{halted: false} = conn <-
@@ -131,7 +140,8 @@ defmodule Pleroma.Web do
import Pleroma.Web.ErrorHelpers
import Pleroma.Web.Gettext
- import Pleroma.Web.Router.Helpers
+
+ alias Pleroma.Web.Router.Helpers, as: Routes
require Logger
@@ -229,20 +239,4 @@ defmodule Pleroma.Web do
defmacro __using__(which) when is_atom(which) do
apply(__MODULE__, which, [])
end
-
- def base_url do
- Pleroma.Web.Endpoint.url()
- end
-
- # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
- def get_api_routes do
- Pleroma.Web.Router.__routes__()
- |> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
- |> Enum.map(fn r ->
- r.path
- |> String.split("/", trim: true)
- |> List.first()
- end)
- |> Enum.uniq()
- end
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index d0051d1cb..1a10aef1d 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ActivityPub do
@@ -25,6 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Web.Streamer
alias Pleroma.Web.WebFinger
alias Pleroma.Workers.BackgroundWorker
+ alias Pleroma.Workers.PollWorker
import Ecto.Query
import Pleroma.Web.ActivityPub.Utils
@@ -53,15 +54,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{recipients, to, cc}
end
- defp check_actor_is_active(nil), do: true
+ defp check_actor_can_insert(%{"type" => "Delete"}), do: true
+ defp check_actor_can_insert(%{"type" => "Undo"}), do: true
- defp check_actor_is_active(actor) when is_binary(actor) do
+ defp check_actor_can_insert(%{"actor" => actor}) when is_binary(actor) do
case User.get_cached_by_ap_id(actor) do
%User{is_active: true} -> true
_ -> false
end
end
+ defp check_actor_can_insert(_), do: true
+
defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
limit = Config.get([:instance, :remote_limit])
String.length(content) <= limit
@@ -77,6 +81,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
end
+ def update_last_status_at_if_public(actor, object) do
+ if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
+ end
+
defp increase_replies_count_if_reply(%{
"object" => %{"inReplyTo" => reply_ap_id} = object,
"type" => "Create"
@@ -88,7 +96,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp increase_replies_count_if_reply(_create_data), do: :noop
- @object_types ~w[ChatMessage Question Answer Audio Video Event Article]
+ @object_types ~w[ChatMessage Question Answer Audio Video Image Event Article Note Page]
@impl true
def persist(%{"type" => type} = object, meta) when type in @object_types do
with {:ok, object} <- Object.create(object) do
@@ -117,7 +125,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
with nil <- Activity.normalize(map),
map <- lazy_put_activity_defaults(map, fake),
- {_, true} <- {:actor_check, bypass_actor_check || check_actor_is_active(map["actor"])},
+ {_, true} <- {:actor_check, bypass_actor_check || check_actor_can_insert(map)},
{_, true} <- {:remote_limit_pass, check_remote_limit(map)},
{:ok, map} <- MRF.filter(map),
{recipients, _, _} = get_recipients(map),
@@ -182,7 +190,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def notify_and_stream(activity) do
Notification.create_notifications(activity)
- conversation = create_or_bump_conversation(activity, activity.actor)
+ original_activity =
+ case activity do
+ %{data: %{"type" => "Update"}, object: %{data: %{"id" => id}}} ->
+ Activity.get_create_by_object_ap_id_with_object(id)
+
+ _ ->
+ activity
+ end
+
+ conversation = create_or_bump_conversation(original_activity, original_activity.actor)
participations = get_participations(conversation)
stream_out(activity)
stream_out_participations(participations)
@@ -248,7 +265,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@impl true
def stream_out(%Activity{data: %{"type" => data_type}} = activity)
- when data_type in ["Create", "Announce", "Delete"] do
+ when data_type in ["Create", "Announce", "Delete", "Update"] do
activity
|> Topics.get_activity_topics()
|> Streamer.stream(activity)
@@ -284,7 +301,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
_ <- increase_replies_count_if_reply(create_data),
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
+ {:ok, _actor} <- update_last_status_at_if_public(actor, activity),
_ <- notify_and_stream(activity),
+ :ok <- maybe_schedule_poll_notifications(activity),
:ok <- maybe_federate(activity) do
{:ok, activity}
else
@@ -299,6 +318,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp maybe_schedule_poll_notifications(activity) do
+ PollWorker.schedule_poll_end(activity)
+ :ok
+ end
+
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
additional = params[:additional] || %{}
@@ -377,11 +401,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
_ <- notify_and_stream(activity),
:ok <-
maybe_federate(stripped_activity) do
- User.all_superusers()
+ User.all_users_with_privilege(:reports_manage_reports)
|> Enum.filter(fn user -> user.ap_id != actor end)
|> Enum.filter(fn user -> not is_nil(user.email) end)
- |> Enum.each(fn superuser ->
- superuser
+ |> Enum.each(fn privileged_user ->
+ privileged_user
|> Pleroma.Emails.AdminEmail.report(actor, account, statuses, content)
|> Pleroma.Emails.Mailer.deliver_async()
end)
@@ -398,7 +422,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"type" => "Move",
"actor" => origin.ap_id,
"object" => origin.ap_id,
- "target" => target.ap_id
+ "target" => target.ap_id,
+ "to" => [origin.follower_address]
}
with true <- origin.ap_id in target.also_known_as,
@@ -431,6 +456,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_preload_bookmarks(opts)
|> maybe_set_thread_muted_field(opts)
|> restrict_blocked(opts)
+ |> restrict_blockers_visibility(opts)
|> restrict_recipients(recipients, opts[:user])
|> restrict_filtered(opts)
|> where(
@@ -485,9 +511,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
+ includes_local_public = Map.get(opts, :includes_local_public, false)
+
opts = Map.delete(opts, :user)
- [Constants.as_public()]
+ intended_recipients =
+ if includes_local_public do
+ [Constants.as_public(), as_local_public()]
+ else
+ [Constants.as_public()]
+ end
+
+ intended_recipients
|> fetch_activities_query(opts)
|> restrict_unlisted(opts)
|> fetch_paginated_optimized(opts, pagination)
@@ -587,9 +622,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
do: query
defp restrict_thread_visibility(query, %{user: %User{ap_id: ap_id}}, _) do
+ local_public = as_local_public()
+
from(
a in query,
- where: fragment("thread_visibility(?, (?)->>'id') = true", ^ap_id, a.data)
+ where: fragment("thread_visibility(?, (?)->>'id', ?) = true", ^ap_id, a.data, ^local_public)
)
end
@@ -676,8 +713,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp user_activities_recipients(%{godmode: true}), do: []
defp user_activities_recipients(%{reading_user: reading_user}) do
- if reading_user do
- [Constants.as_public(), reading_user.ap_id | User.following(reading_user)]
+ if not is_nil(reading_user) and reading_user.local do
+ [
+ Constants.as_public(),
+ as_local_public(),
+ reading_user.ap_id | User.following(reading_user)
+ ]
else
[Constants.as_public()]
end
@@ -1018,7 +1059,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
from(
[activity, object: o] in query,
+ # You don't block the author
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
+
+ # You don't block any recipients, and didn't author the post
where:
fragment(
"((not (? && ?)) or ? = ?)",
@@ -1027,12 +1071,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
activity.actor,
^user.ap_id
),
+
+ # You don't block the domain of any recipients, and didn't author the post
where:
fragment(
- "recipients_contain_blocked_domains(?, ?) = false",
+ "(recipients_contain_blocked_domains(?, ?) = false) or ? = ?",
activity.recipients,
- ^domain_blocks
+ ^domain_blocks,
+ activity.actor,
+ ^user.ap_id
),
+
+ # It's not a boost of a user you block
where:
fragment(
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
@@ -1040,6 +1090,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
activity.data,
^blocked_ap_ids
),
+
+ # You don't block the author's domain, and also don't follow the author
where:
fragment(
"(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
@@ -1048,6 +1100,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
activity.actor,
^following_ap_ids
),
+
+ # Same as above, but checks the Object
where:
fragment(
"(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
@@ -1061,6 +1115,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_blocked(query, _), do: query
+ defp restrict_blockers_visibility(query, %{blocking_user: %User{} = user}) do
+ if Config.get([:activitypub, :blockers_visible]) == true do
+ query
+ else
+ blocker_ap_ids = User.incoming_relationships_ungrouped_ap_ids(user, [:block])
+
+ from(
+ activity in query,
+ # The author doesn't block you
+ where: fragment("not (? = ANY(?))", activity.actor, ^blocker_ap_ids),
+
+ # It's not a boost of a user that blocks you
+ where:
+ fragment(
+ "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
+ activity.data,
+ activity.data,
+ ^blocker_ap_ids
+ )
+ )
+ end
+ end
+
+ defp restrict_blockers_visibility(query, _), do: query
+
defp restrict_unlisted(query, %{restrict_unlisted: true}) do
from(
activity in query,
@@ -1080,8 +1159,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
[activity, object: o] in query,
where:
fragment(
- "(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)",
- activity.data,
+ "(?)->>'type' = 'Create' and associated_object_id((?)) = any (?)",
activity.data,
activity.data,
^ids
@@ -1161,15 +1239,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp exclude_invisible_actors(query, %{type: "Flag"}), do: query
defp exclude_invisible_actors(query, %{invisible_actors: true}), do: query
defp exclude_invisible_actors(query, _opts) do
- invisible_ap_ids =
- User.Query.build(%{invisible: true, select: [:ap_id]})
- |> Repo.all()
- |> Enum.map(fn %{ap_id: ap_id} -> ap_id end)
-
- from([activity] in query, where: activity.actor not in ^invisible_ap_ids)
+ query
+ |> join(:inner, [activity], u in User,
+ as: :u,
+ on: activity.actor == u.ap_id and u.invisible == false
+ )
end
defp exclude_id(query, %{exclude_id: id}) when is_binary(id) do
@@ -1287,6 +1365,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_state(opts)
|> restrict_favorited_by(opts)
|> restrict_blocked(restrict_blocked_opts)
+ |> restrict_blockers_visibility(opts)
|> restrict_muted(restrict_muted_opts)
|> restrict_filtered(opts)
|> restrict_media(opts)
@@ -1298,7 +1377,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_instance(opts)
|> restrict_announce_object_actor(opts)
|> restrict_filtered(opts)
- |> Activity.restrict_deactivated_users()
+ |> maybe_restrict_deactivated_users(opts)
|> exclude_poll_votes(opts)
|> exclude_chat_messages(opts)
|> exclude_invisible_actors(opts)
@@ -1374,13 +1453,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
def upload(file, opts \\ []) do
- with {:ok, data} <- Upload.store(file, opts) do
+ with {:ok, data} <- Upload.store(sanitize_upload_file(file), opts) do
obj_data = Maps.put_if_present(data, "actor", opts[:actor])
Repo.insert(%Object{data: obj_data})
end
end
+ defp sanitize_upload_file(%Plug.Upload{filename: filename} = upload) when is_binary(filename) do
+ %Plug.Upload{
+ upload
+ | filename: Path.basename(filename)
+ }
+ end
+
+ defp sanitize_upload_file(upload), do: upload
+
@spec get_actor_url(any()) :: binary() | nil
defp get_actor_url(url) when is_binary(url), do: url
defp get_actor_url(%{"href" => href}) when is_binary(href), do: href
@@ -1403,7 +1491,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil
- defp object_to_user_data(data) do
+ defp object_to_user_data(data, additional) do
fields =
data
|> Map.get("attachment", [])
@@ -1435,21 +1523,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
public_key =
if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
data["publicKey"]["publicKeyPem"]
- else
- nil
end
shared_inbox =
if is_map(data["endpoints"]) && is_binary(data["endpoints"]["sharedInbox"]) do
data["endpoints"]["sharedInbox"]
- else
- nil
end
- user_data = %{
+ birthday =
+ if is_binary(data["vcard:bday"]) do
+ case Date.from_iso8601(data["vcard:bday"]) do
+ {:ok, date} -> date
+ {:error, _} -> nil
+ end
+ end
+
+ show_birthday = !!birthday
+
+ # if WebFinger request was already done, we probably have acct, otherwise
+ # we request WebFinger here
+ nickname = additional[:nickname_from_acct] || generate_nickname(data)
+
+ %{
ap_id: data["id"],
uri: get_actor_url(data["url"]),
- ap_enabled: true,
banner: normalize_image(data["image"]),
fields: fields,
emoji: emojis,
@@ -1468,21 +1565,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
inbox: data["inbox"],
shared_inbox: shared_inbox,
accepts_chat_messages: accepts_chat_messages,
- pinned_objects: pinned_objects
+ birthday: birthday,
+ show_birthday: show_birthday,
+ pinned_objects: pinned_objects,
+ nickname: nickname
}
+ end
- # nickname can be nil because of virtual actors
- if data["preferredUsername"] do
- Map.put(
- user_data,
- :nickname,
- "#{data["preferredUsername"]}@#{URI.parse(data["id"]).host}"
- )
+ defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do
+ generated = "#{username}@#{URI.parse(data["id"]).host}"
+
+ if Config.get([WebFinger, :update_nickname_on_user_fetch]) do
+ case WebFinger.finger(generated) do
+ {:ok, %{"subject" => "acct:" <> acct}} -> acct
+ _ -> generated
+ end
else
- Map.put(user_data, :nickname, nil)
+ generated
end
end
+ # nickname can be nil because of virtual actors
+ defp generate_nickname(_), do: nil
+
def fetch_follow_information_for_user(user) do
with {:ok, following_data} <-
Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
@@ -1554,17 +1659,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp collection_private(_data), do: {:ok, true}
- def user_data_from_user_object(data) do
+ def user_data_from_user_object(data, additional \\ []) do
with {:ok, data} <- MRF.filter(data) do
- {:ok, object_to_user_data(data)}
+ {:ok, object_to_user_data(data, additional)}
else
e -> {:error, e}
end
end
- def fetch_and_prepare_user_from_ap_id(ap_id) do
+ defp fetch_and_prepare_user_from_ap_id(ap_id, additional) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
- {:ok, data} <- user_data_from_user_object(data) do
+ {:ok, data} <- user_data_from_user_object(data, additional) do
{:ok, maybe_update_follow_information(data)}
else
# If this has been deleted, only log a debug and not an error
@@ -1587,9 +1692,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
%User{} = old_user <- User.get_by_nickname(nickname),
{_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do
Logger.info(
- "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{
- data[:ap_id]
- }, renaming."
+ "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{data[:ap_id]}, renaming."
)
old_user
@@ -1611,7 +1714,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"orderedItems" => objects
})
when type in ["OrderedCollection", "Collection"] do
- Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end)
+ Map.new(objects, fn
+ %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()}
+ object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()}
+ end)
end
def fetch_and_prepare_featured_from_ap_id(nil) do
@@ -1641,34 +1747,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def make_user_from_ap_id(ap_id) do
+ def make_user_from_ap_id(ap_id, additional \\ []) do
user = User.get_cached_by_ap_id(ap_id)
- if user && !User.ap_enabled?(user) do
- Transmogrifier.upgrade_user_from_ap_id(ap_id)
- else
- with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
- {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
+ with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do
+ {:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
- if user do
- user
- |> User.remote_user_changeset(data)
- |> User.update_and_set_cache()
- else
- maybe_handle_clashing_nickname(data)
+ if user do
+ user
+ |> User.remote_user_changeset(data)
+ |> User.update_and_set_cache()
+ else
+ maybe_handle_clashing_nickname(data)
- data
- |> User.remote_user_changeset()
- |> Repo.insert()
- |> User.set_cache()
- end
+ data
+ |> User.remote_user_changeset()
+ |> Repo.insert()
+ |> User.set_cache()
end
end
end
def make_user_from_nickname(nickname) do
- with {:ok, %{"ap_id" => ap_id}} when not is_nil(ap_id) <- WebFinger.finger(nickname) do
- make_user_from_ap_id(ap_id)
+ with {:ok, %{"ap_id" => ap_id, "subject" => "acct:" <> acct}} when not is_nil(ap_id) <-
+ WebFinger.finger(nickname) do
+ make_user_from_ap_id(ap_id, nickname_from_acct: acct)
else
_e -> {:error, "No AP id in WebFinger"}
end
@@ -1690,4 +1793,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_visibility(%{visibility: "direct"})
|> order_by([activity], asc: activity.id)
end
+
+ defp maybe_restrict_deactivated_users(activity, %{type: "Flag"}), do: activity
+
+ defp maybe_restrict_deactivated_users(activity, _opts),
+ do: Activity.restrict_deactivated_users(activity)
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub/persisting.ex b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex
index 5ec8b7bab..3dbfdee28 100644
--- a/lib/pleroma/web/activity_pub/activity_pub/persisting.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex
@@ -1,7 +1,7 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do
- @callback persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
+ @callback persist(map(), keyword()) :: {:ok, struct()}
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
index 983168bff..d7358171d 100644
--- a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
@@ -1,12 +1,8 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do
- alias Pleroma.Activity
- alias Pleroma.Object
- alias Pleroma.User
-
- @callback stream_out(Activity.t()) :: any()
- @callback stream_out_participations(Object.t(), User.t()) :: any()
+ @callback stream_out(struct()) :: any()
+ @callback stream_out_participations(struct(), struct()) :: any()
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 5aa3b281a..1357c379c 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
@@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
alias Pleroma.Object.Fetcher
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.InternalFetchActor
alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.Pipeline
@@ -67,8 +66,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end
def user(conn, %{"nickname" => nickname}) do
- with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- User.ensure_keys_present(user) do
+ with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@@ -85,6 +83,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
user <- Map.get(assigns, :user, nil),
{_, true} <- {:visible?, Visibility.visible_for_user?(object, user)} do
conn
+ |> maybe_skip_cache(user)
|> assign(:tracking_fun_data, object.id)
|> set_cache_ttl_for(object)
|> put_resp_content_type("application/activity+json")
@@ -113,6 +112,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
user <- Map.get(assigns, :user, nil),
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, user)} do
conn
+ |> maybe_skip_cache(user)
|> maybe_set_tracking_data(activity)
|> set_cache_ttl_for(activity)
|> put_resp_content_type("application/activity+json")
@@ -152,6 +152,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
assign(conn, :cache_ttl, ttl)
end
+ def maybe_skip_cache(conn, user) do
+ if user do
+ conn
+ |> assign(:skip_cache, true)
+ else
+ conn
+ end
+ end
+
# GET /relay/following
def relay_following(conn, _params) do
with %{halted: false} = conn <- FederatingPlug.call(conn, []) do
@@ -164,7 +173,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) 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.hide_follows} do
{page, _} = Integer.parse(page)
@@ -182,8 +190,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) 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) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@@ -203,7 +210,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) 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.hide_followers} do
{page, _} = Integer.parse(page)
@@ -221,8 +227,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) 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) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@@ -235,8 +240,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
%{"nickname" => nickname, "page" => page?} = params
)
when page? in [true, "true"] do
- with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- User.ensure_keys_present(user) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
# "include_poll_votes" is a hack because postgres generates inefficient
# queries when filtering by 'Answer', poll votes will be hidden by the
# visibility filter in this case anyway
@@ -260,8 +264,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end
def outbox(conn, %{"nickname" => nickname}) do
- with %User{} = user <- User.get_cached_by_nickname(nickname),
- {:ok, user} <- User.ensure_keys_present(user) do
+ with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@@ -284,15 +287,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
json(conn, "ok")
end
+ def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do
+ conn
+ |> put_status(:bad_request)
+ |> json("Invalid HTTP Signature")
+ end
+
# POST /relay/inbox -or- POST /internal/fetch/inbox
- def inbox(conn, params) do
- if params["type"] == "Create" && FederatingPlug.federating?() do
+ def inbox(conn, %{"type" => "Create"} = params) do
+ if FederatingPlug.federating?() do
post_inbox_relayed_create(conn, params)
else
- post_inbox_fallback(conn, params)
+ conn
+ |> put_status(:bad_request)
+ |> json("Not federating")
end
end
+ def inbox(conn, _params) do
+ conn
+ |> put_status(:bad_request)
+ |> json("error, missing HTTP Signature")
+ end
+
defp post_inbox_relayed_create(conn, params) do
Logger.debug(
"Signature missing or not from author, relayed Create message, fetching object from source"
@@ -303,32 +320,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
json(conn, "ok")
end
- defp post_inbox_fallback(conn, params) do
- headers = Enum.into(conn.req_headers, %{})
-
- if headers["signature"] && params["actor"] &&
- String.contains?(headers["signature"], params["actor"]) do
- Logger.debug(
- "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
- )
-
- Logger.debug(inspect(conn.req_headers))
- end
-
- conn
- |> put_status(:bad_request)
- |> json(dgettext("errors", "error"))
- end
-
defp represent_service_actor(%User{} = user, conn) do
- with {:ok, user} <- User.ensure_keys_present(user) do
- conn
- |> put_resp_content_type("application/activity+json")
- |> put_view(UserView)
- |> render("user.json", %{user: user})
- else
- nil -> {:error, :not_found}
- end
+ conn
+ |> put_resp_content_type("application/activity+json")
+ |> put_view(UserView)
+ |> render("user.json", %{user: user})
end
defp represent_service_actor(nil, _), do: {:error, :not_found}
@@ -381,12 +377,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
"nickname" => nickname
}) do
- with {:ok, user} <- User.ensure_keys_present(user) do
- conn
- |> put_resp_content_type("application/activity+json")
- |> put_view(UserView)
- |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
- end
+ conn
+ |> put_resp_content_type("application/activity+json")
+ |> put_view(UserView)
+ |> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
end
def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
@@ -403,83 +397,90 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> json(err)
end
- defp handle_user_activity(
- %User{} = user,
- %{"type" => "Create", "object" => %{"type" => "Note"} = object} = params
- ) do
- content = if is_binary(object["content"]), do: object["content"], else: ""
- name = if is_binary(object["name"]), do: object["name"], else: ""
- summary = if is_binary(object["summary"]), do: object["summary"], else: ""
- length = String.length(content <> name <> summary)
+ defp fix_user_message(%User{ap_id: actor}, %{"type" => "Create", "object" => object} = activity)
+ when is_map(object) do
+ length =
+ [object["content"], object["summary"], object["name"]]
+ |> Enum.filter(&is_binary(&1))
+ |> Enum.join("")
+ |> String.length()
- if length > Pleroma.Config.get([:instance, :limit]) do
- {:error, dgettext("errors", "Note is over the character limit")}
- else
+ limit = Pleroma.Config.get([:instance, :limit])
+
+ if length < limit do
object =
object
- |> Map.merge(Map.take(params, ["to", "cc"]))
- |> Map.put("attributedTo", user.ap_id)
- |> Transmogrifier.fix_object()
-
- ActivityPub.create(%{
- to: params["to"],
- actor: user,
- context: object["context"],
- object: object,
- additional: Map.take(params, ["cc"])
- })
- end
- end
+ |> Transmogrifier.strip_internal_fields()
+ |> Map.put("attributedTo", actor)
+ |> Map.put("actor", actor)
+ |> Map.put("id", Utils.generate_object_id())
- defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do
- with %Object{} = object <- Object.normalize(params["object"], fetch: false),
- true <- user.is_moderator || user.ap_id == object.data["actor"],
- {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
- {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
- {:ok, delete}
+ {:ok, Map.put(activity, "object", object)}
else
- _ -> {:error, dgettext("errors", "Can't delete object")}
+ {:error,
+ dgettext(
+ "errors",
+ "Character limit (%{limit} characters) exceeded, contains %{length} characters",
+ limit: limit,
+ length: length
+ )}
end
end
- defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
- with %Object{} = object <- Object.normalize(params["object"], fetch: false),
- {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
- {_, {:ok, %Activity{} = activity, _meta}} <-
- {:common_pipeline,
- Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
+ defp fix_user_message(
+ %User{ap_id: actor} = user,
+ %{"type" => "Delete", "object" => object} = activity
+ ) do
+ with {_, %Object{data: object_data}} <- {:normalize, Object.normalize(object, fetch: false)},
+ {_, true} <- {:permission, user.is_moderator || actor == object_data["actor"]} do
{:ok, activity}
else
- _ -> {:error, dgettext("errors", "Can't like object")}
+ {:normalize, _} ->
+ {:error, "No such object found"}
+
+ {:permission, _} ->
+ {:forbidden, "You can't delete this object"}
end
end
- defp handle_user_activity(_, _) do
- {:error, dgettext("errors", "Unhandled activity type")}
+ defp fix_user_message(%User{}, activity) do
+ {:ok, activity}
end
def update_outbox(
- %{assigns: %{user: %User{nickname: nickname} = user}} = conn,
+ %{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn,
%{"nickname" => nickname} = params
) do
- actor = user.ap_id
-
params =
params
- |> Map.drop(["id"])
+ |> Map.drop(["nickname"])
+ |> Map.put("id", Utils.generate_activity_id())
|> Map.put("actor", actor)
- |> Transmogrifier.fix_addressing()
- with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
+ with {:ok, params} <- fix_user_message(user, params),
+ {:ok, activity, _} <- Pipeline.common_pipeline(params, local: true),
+ %Activity{data: activity_data} <- Activity.normalize(activity) do
conn
|> put_status(:created)
- |> put_resp_header("location", activity.data["id"])
- |> json(activity.data)
+ |> put_resp_header("location", activity_data["id"])
+ |> json(activity_data)
else
+ {:forbidden, message} ->
+ conn
+ |> put_status(:forbidden)
+ |> json(message)
+
{:error, message} ->
conn
|> put_status(:bad_request)
|> json(message)
+
+ e ->
+ Logger.warn(fn -> "AP C2S: #{inspect(e)}" end)
+
+ conn
+ |> put_status(:bad_request)
+ |> json("Bad Request")
end
end
@@ -516,19 +517,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
conn
end
- defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
- {:ok, new_user} = User.ensure_keys_present(user)
-
- for_user =
- if new_user != user and match?(%User{}, for_user) do
- User.get_cached_by_nickname(for_user.nickname)
- else
- for_user
- end
-
- {new_user, for_user}
- end
-
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
with {:ok, object} <-
ActivityPub.upload(
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 91a45836f..8eab3a241 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Builder do
@@ -15,6 +15,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.CommonAPI.ActivityDraft
+ alias Pleroma.Web.Endpoint
require Pleroma.Constants
@@ -53,13 +55,87 @@ defmodule Pleroma.Web.ActivityPub.Builder do
{:ok, data, []}
end
+ defp unicode_emoji_react(_object, data, emoji) do
+ data
+ |> Map.put("content", emoji)
+ |> Map.put("type", "EmojiReact")
+ end
+
+ defp add_emoji_content(data, emoji, url) do
+ tag = [
+ %{
+ "id" => url,
+ "type" => "Emoji",
+ "name" => Emoji.maybe_quote(emoji),
+ "icon" => %{
+ "type" => "Image",
+ "url" => url
+ }
+ }
+ ]
+
+ data
+ |> Map.put("content", Emoji.maybe_quote(emoji))
+ |> Map.put("type", "EmojiReact")
+ |> Map.put("tag", tag)
+ end
+
+ defp remote_custom_emoji_react(
+ %{data: %{"reactions" => existing_reactions}},
+ data,
+ emoji
+ ) do
+ [emoji_code, instance] = String.split(Emoji.maybe_strip_name(emoji), "@")
+
+ matching_reaction =
+ Enum.find(
+ existing_reactions,
+ fn [name, _, url] ->
+ if url != nil do
+ url = URI.parse(url)
+ url.host == instance && name == emoji_code
+ end
+ end
+ )
+
+ if matching_reaction do
+ [name, _, url] = matching_reaction
+ add_emoji_content(data, name, url)
+ else
+ {:error, "Could not react"}
+ end
+ end
+
+ defp remote_custom_emoji_react(_object, _data, _emoji) do
+ {:error, "Could not react"}
+ end
+
+ defp local_custom_emoji_react(data, emoji) do
+ with %{file: path} = emojo <- Emoji.get(emoji) do
+ url = "#{Endpoint.url()}#{path}"
+ add_emoji_content(data, emojo.code, url)
+ else
+ _ -> {:error, "Emoji does not exist"}
+ end
+ end
+
+ defp custom_emoji_react(object, data, emoji) do
+ if String.contains?(emoji, "@") do
+ remote_custom_emoji_react(object, data, emoji)
+ else
+ local_custom_emoji_react(data, emoji)
+ end
+ end
+
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do
data =
- data
- |> Map.put("content", emoji)
- |> Map.put("type", "EmojiReact")
+ if Emoji.is_unicode_emoji?(emoji) do
+ unicode_emoji_react(object, data, emoji)
+ else
+ custom_emoji_react(object, data, emoji)
+ end
{:ok, data, meta}
end
@@ -125,6 +201,37 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|> Pleroma.Maps.put_if_present("context", context), []}
end
+ @spec note(ActivityDraft.t()) :: {:ok, map(), keyword()}
+ def note(%ActivityDraft{} = draft) do
+ data =
+ %{
+ "type" => "Note",
+ "to" => draft.to,
+ "cc" => draft.cc,
+ "content" => draft.content_html,
+ "summary" => draft.summary,
+ "sensitive" => draft.sensitive,
+ "context" => draft.context,
+ "attachment" => draft.attachments,
+ "actor" => draft.user.ap_id,
+ "tag" => Keyword.values(draft.tags) |> Enum.uniq()
+ }
+ |> add_in_reply_to(draft.in_reply_to)
+ |> Map.merge(draft.extra)
+
+ {:ok, data, []}
+ end
+
+ defp add_in_reply_to(object, nil), do: object
+
+ defp add_in_reply_to(object, in_reply_to) do
+ with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do
+ Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
+ else
+ _ -> object
+ end
+ end
+
def chat_message(actor, recipient, content, opts \\ []) do
basic = %{
"id" => Utils.generate_object_id(),
@@ -186,10 +293,16 @@ defmodule Pleroma.Web.ActivityPub.Builder do
end
end
- # Retricted to user updates for now, always public
@spec update(User.t(), Object.t()) :: {:ok, map(), keyword()}
def update(actor, object) do
- to = [Pleroma.Constants.as_public(), actor.follower_address]
+ {to, cc} =
+ if object["type"] in Pleroma.Constants.actor_types() do
+ # User updates, always public
+ {[Pleroma.Constants.as_public(), actor.follower_address], []}
+ else
+ # Status updates, follow the recipients in the object
+ {object["to"] || [], object["cc"] || []}
+ end
{:ok,
%{
@@ -197,7 +310,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
"type" => "Update",
"actor" => actor.ap_id,
"object" => object,
- "to" => to
+ "to" => to,
+ "cc" => cc
}, []}
end
@@ -223,7 +337,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
[actor.follower_address]
public? and Visibility.is_local_public?(object) ->
- [actor.follower_address, object.data["actor"], Pleroma.Constants.as_local_public()]
+ [actor.follower_address, object.data["actor"], Utils.as_local_public()]
public? ->
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
diff --git a/lib/pleroma/web/activity_pub/internal_fetch_actor.ex b/lib/pleroma/web/activity_pub/internal_fetch_actor.ex
index ca76071e5..083723894 100644
--- a/lib/pleroma/web/activity_pub/internal_fetch_actor.ex
+++ b/lib/pleroma/web/activity_pub/internal_fetch_actor.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.InternalFetchActor do
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index f2fec3ff6..ff9f84497 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF do
@@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
type: [:module, {:list, :module}],
description:
"A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
- suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
+ suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF.Policy}
},
%{
key: :transparency,
@@ -33,9 +33,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do
%{
key: :transparency_exclusions,
label: "MRF transparency exclusions",
- type: {:list, :string},
+ type: {:list, :tuple},
+ key_placeholder: "instance",
+ value_placeholder: "reason",
description:
- "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
+ "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. You can also provide a reason for excluding these instance names. The instances and reasons won't be publicly disclosed.",
suggestions: [
"exclusion.com"
]
@@ -51,21 +53,53 @@ defmodule Pleroma.Web.ActivityPub.MRF do
@required_description_keys [:key, :related_policy]
- @callback filter(Map.t()) :: {:ok | :reject, Map.t()}
- @callback describe() :: {:ok | :error, Map.t()}
- @callback config_description() :: %{
- optional(:children) => [map()],
- key: atom(),
- related_policy: String.t(),
- label: String.t(),
- description: String.t()
- }
- @optional_callbacks config_description: 0
+ def filter_one(policy, message) do
+ should_plug_history? =
+ if function_exported?(policy, :history_awareness, 0) do
+ policy.history_awareness()
+ else
+ :manual
+ end
+ |> Kernel.==(:auto)
+
+ if not should_plug_history? do
+ policy.filter(message)
+ else
+ main_result = policy.filter(message)
+
+ with {_, {:ok, main_message}} <- {:main, main_result},
+ {_,
+ %{
+ "formerRepresentations" => %{
+ "orderedItems" => [_ | _]
+ }
+ }} = {_, object} <- {:object, message["object"]},
+ {_, {:ok, new_history}} <-
+ {:history,
+ Pleroma.Object.Updater.for_each_history_item(
+ object["formerRepresentations"],
+ object,
+ fn item ->
+ with {:ok, filtered} <- policy.filter(Map.put(message, "object", item)) do
+ {:ok, filtered["object"]}
+ else
+ e -> e
+ end
+ end
+ )} do
+ {:ok, put_in(main_message, ["object", "formerRepresentations"], new_history)}
+ else
+ {:main, _} -> main_result
+ {:object, _} -> main_result
+ {:history, e} -> e
+ end
+ end
+ end
def filter(policies, %{} = message) do
policies
|> Enum.reduce({:ok, message}, fn
- policy, {:ok, message} -> policy.filter(message)
+ policy, {:ok, message} -> filter_one(policy, message)
_, error -> error
end)
end
@@ -111,6 +145,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
end
+ @spec instance_list_from_tuples([{String.t(), String.t()}]) :: [String.t()]
+ def instance_list_from_tuples(list) do
+ Enum.map(list, fn {instance, _} -> instance end)
+ end
+
def describe(policies) do
{:ok, policy_configs} =
policies
@@ -142,7 +181,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
def describe, do: get_policies() |> describe()
def config_descriptions do
- Pleroma.Web.ActivityPub.MRF
+ Pleroma.Web.ActivityPub.MRF.Policy
|> Pleroma.Docs.Generator.list_behaviour_implementations()
|> config_descriptions()
end
@@ -161,9 +200,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
[description | acc]
else
Logger.warn(
- "#{policy} config description doesn't have one or all required keys #{
- inspect(@required_description_keys)
- }"
+ "#{policy} config description doesn't have one or all required keys #{inspect(@required_description_keys)}"
)
acc
diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
index fc347236e..88f6ca028 100644
--- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
@@ -1,10 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
@moduledoc "Adds expiration to all local Create activities"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
def filter(activity) do
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
index b8bfdc3ce..97d75ecf2 100644
--- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
@@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
@moduledoc "Prevent followbots from following with a bit of heuristic"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
# XXX: this should become User.normalize_by_ap_id() or similar, really.
defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id)
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
defp score_displayname("fedibot"), do: 1.0
defp score_displayname(_), do: 0.0
- defp determine_if_followbot(%User{nickname: nickname, name: displayname}) do
+ defp determine_if_followbot(%User{nickname: nickname, name: displayname, actor_type: actor_type}) do
# nickname will be a binary string except when following a relay
nick_score =
if is_binary(nickname) do
@@ -45,19 +45,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
0.0
end
- nick_score + name_score
+ # actor_type "Service" is a Bot account
+ actor_type_score =
+ if actor_type == "Service" do
+ 1.0
+ else
+ 0.0
+ end
+
+ nick_score + name_score + actor_type_score
end
defp determine_if_followbot(_), do: 0.0
+ defp bot_allowed?(%{"object" => target}, bot_actor) do
+ %User{} = user = normalize_by_ap_id(target)
+
+ User.following?(user, bot_actor)
+ end
+
@impl true
def filter(%{"type" => "Follow", "actor" => actor_id} = message) do
%User{} = actor = normalize_by_ap_id(actor_id)
score = determine_if_followbot(actor)
- # TODO: scan biography data for keywords and score it somehow.
- if score < 0.8 do
+ if score < 0.8 || bot_allowed?(message, actor) do
{:ok, message}
else
{:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"}
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
index 40b19c3ab..3ec9c52ee 100644
--- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
@@ -1,14 +1,17 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
alias Pleroma.User
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Logger
+ @impl true
+ def history_awareness, do: :auto
+
# has the user successfully posted before?
defp old_user?(%User{} = u) do
u.note_count > 0 || u.follower_count > 0
diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
index 378175205..ad0936839 100644
--- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex
@@ -1,11 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
require Logger
@moduledoc "Drop and log everything received"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
def filter(object) do
diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
index 2d3a10889..a148cc1e7 100644
--- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
+++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
@@ -1,15 +1,17 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
alias Pleroma.Object
@moduledoc "Ensure a re: is prepended on replies to a post with a Subject"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
+ def history_awareness, do: :auto
+
def filter_by_summary(
%{data: %{"summary" => parent_summary}} = _in_reply_to,
%{"summary" => child_summary} = child
@@ -27,8 +29,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
def filter_by_summary(_in_reply_to, child), do: child
- def filter(%{"type" => "Create", "object" => child_object} = object)
- when is_map(child_object) do
+ def filter(%{"type" => type, "object" => child_object} = object)
+ when type in ["Create", "Update"] and is_map(child_object) do
child =
child_object["inReplyTo"]
|> Object.normalize(fetch: false)
diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex
index 7307c9c14..5b6adbb4b 100644
--- a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex
@@ -1,5 +1,9 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.CommonAPI
diff --git a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
index 51dbb1ad4..8cec8eabe 100644
--- a/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/force_bot_unlisted_policy.ex
@@ -1,16 +1,16 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
alias Pleroma.User
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@moduledoc "Remove bot posts from federated timeline"
require Pleroma.Constants
defp check_by_actor_type(user), do: user.actor_type in ["Application", "Service"]
- defp check_by_nickname(user), do: Regex.match?(~r/bot@|ebooks@/i, user.nickname)
+ defp check_by_nickname(user), do: Regex.match?(~r/.bot@|ebooks@/i, user.nickname)
defp check_if_bot(user), do: check_by_actor_type(user) or check_by_nickname(user)
diff --git a/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex
new file mode 100644
index 000000000..5532093cb
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/force_mentions_in_content.ex
@@ -0,0 +1,137 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do
+ require Pleroma.Constants
+
+ alias Pleroma.Formatter
+ alias Pleroma.Object
+ alias Pleroma.User
+
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
+
+ @impl true
+ def history_awareness, do: :auto
+
+ defp do_extract({:a, attrs, _}, acc) do
+ if Enum.find(attrs, fn {name, value} ->
+ name == "class" && value in ["mention", "u-url mention", "mention u-url"]
+ end) do
+ href = Enum.find(attrs, fn {name, _} -> name == "href" end) |> elem(1)
+ acc ++ [href]
+ else
+ acc
+ end
+ end
+
+ defp do_extract({_, _, children}, acc) do
+ do_extract(children, acc)
+ end
+
+ defp do_extract(nodes, acc) when is_list(nodes) do
+ Enum.reduce(nodes, acc, fn node, acc -> do_extract(node, acc) end)
+ end
+
+ defp do_extract(_, acc), do: acc
+
+ defp extract_mention_uris_from_content(content) do
+ {:ok, tree} = :fast_html.decode(content, format: [:html_atoms])
+ do_extract(tree, [])
+ end
+
+ defp get_replied_to_user(%{"inReplyTo" => in_reply_to}) do
+ case Object.normalize(in_reply_to, fetch: false) do
+ %Object{data: %{"actor" => actor}} -> User.get_cached_by_ap_id(actor)
+ _ -> nil
+ end
+ end
+
+ defp get_replied_to_user(_object), do: nil
+
+ # Ensure the replied-to user is sorted to the left
+ defp sort_replied_user([%User{id: user_id} | _] = users, %User{id: user_id}), do: users
+
+ defp sort_replied_user(users, %User{id: user_id} = user) do
+ if Enum.find(users, fn u -> u.id == user_id end) do
+ users = Enum.reject(users, fn u -> u.id == user_id end)
+ [user | users]
+ else
+ users
+ end
+ end
+
+ defp sort_replied_user(users, _), do: users
+
+ # Drop constants and the actor's own AP ID
+ defp clean_recipients(recipients, object) do
+ Enum.reject(recipients, fn ap_id ->
+ ap_id in [
+ object["object"]["actor"],
+ Pleroma.Constants.as_public(),
+ Pleroma.Web.ActivityPub.Utils.as_local_public()
+ ]
+ end)
+ end
+
+ @impl true
+ def filter(
+ %{
+ "type" => type,
+ "object" => %{"type" => "Note", "to" => to, "inReplyTo" => in_reply_to}
+ } = object
+ )
+ when type in ["Create", "Update"] and is_list(to) and is_binary(in_reply_to) do
+ # image-only posts from pleroma apparently reach this MRF without the content field
+ content = object["object"]["content"] || ""
+
+ # Get the replied-to user for sorting
+ replied_to_user = get_replied_to_user(object["object"])
+
+ mention_users =
+ to
+ |> clean_recipients(object)
+ |> Enum.map(&User.get_cached_by_ap_id/1)
+ |> Enum.reject(&is_nil/1)
+ |> sort_replied_user(replied_to_user)
+
+ explicitly_mentioned_uris =
+ extract_mention_uris_from_content(content)
+ |> MapSet.new()
+
+ added_mentions =
+ Enum.reduce(mention_users, "", fn %User{ap_id: ap_id, uri: uri} = user, acc ->
+ if MapSet.disjoint?(MapSet.new([ap_id, uri]), explicitly_mentioned_uris) do
+ acc <> Formatter.mention_from_user(user, %{mentions_format: :compact}) <> " "
+ else
+ acc
+ end
+ end)
+
+ recipients_inline =
+ if added_mentions != "",
+ do: "<span class=\"recipients-inline\">#{added_mentions}</span>",
+ else: ""
+
+ content =
+ cond do
+ # For Markdown posts, insert the mentions inside the first <p> tag
+ recipients_inline != "" && String.starts_with?(content, "<p>") ->
+ "<p>" <> recipients_inline <> String.trim_leading(content, "<p>")
+
+ recipients_inline != "" ->
+ recipients_inline <> content
+
+ true ->
+ content
+ end
+
+ {:ok, put_in(object["object"]["content"], content)}
+ end
+
+ @impl true
+ def filter(object), do: {:ok, object}
+
+ @impl true
+ def describe, do: {:ok, %{}}
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex
index def0c437c..b73fd974c 100644
--- a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
@@ -14,7 +14,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
"""
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
+
+ @impl true
+ def history_awareness, do: :manual
defp check_reject(message, hashtags) do
if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
@@ -47,22 +50,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
defp check_ftl_removal(message, _hashtags), do: {:ok, message}
- defp check_sensitive(message, hashtags) do
- if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do
- {:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
- else
- {:ok, message}
- end
+ defp check_sensitive(message) do
+ {:ok, new_object} =
+ Object.Updater.do_with_history(message["object"], fn object ->
+ hashtags = Object.hashtags(%Object{data: object})
+
+ if Enum.any?(Config.get([:mrf_hashtag, :sensitive]), fn match -> match in hashtags end) do
+ {:ok, Map.put(object, "sensitive", true)}
+ else
+ {:ok, object}
+ end
+ end)
+
+ {:ok, Map.put(message, "object", new_object)}
end
@impl true
- def filter(%{"type" => "Create", "object" => object} = message) do
- hashtags = Object.hashtags(%Object{data: object})
+ def filter(%{"type" => type, "object" => object} = message) when type in ["Create", "Update"] do
+ history_items =
+ with %{"formerRepresentations" => %{"orderedItems" => items}} <- object do
+ items
+ else
+ _ -> []
+ end
+
+ historical_hashtags =
+ Enum.reduce(history_items, [], fn item, acc ->
+ acc ++ Object.hashtags(%Object{data: item})
+ end)
+
+ hashtags = Object.hashtags(%Object{data: object}) ++ historical_hashtags
if hashtags != [] do
with {:ok, message} <- check_reject(message, hashtags),
- {:ok, message} <- check_ftl_removal(message, hashtags),
- {:ok, message} <- check_sensitive(message, hashtags) do
+ {:ok, message} <-
+ (if "type" == "Create" do
+ check_ftl_removal(message, hashtags)
+ else
+ {:ok, message}
+ end),
+ {:ok, message} <- check_sensitive(message) do
{:ok, message}
end
else
diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
index 768a669f3..80e235d6e 100644
--- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
@moduledoc "Block messages with too much mentions (configurable)"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp delist_message(message, threshold) when threshold > 0 do
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
index f91b51bcf..687ec6c2f 100644
--- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
@@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp string_matches?(string, _) when not is_binary(string) do
false
end
@@ -27,24 +27,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end
defp check_reject(%{"object" => %{} = object} = message) do
- payload = object_payload(object)
-
- if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
- string_matches?(payload, pattern)
- end) do
- {:reject, "[KeywordPolicy] Matches with rejected keyword"}
- else
+ with {:ok, _new_object} <-
+ Pleroma.Object.Updater.do_with_history(object, fn object ->
+ payload = object_payload(object)
+
+ if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
+ string_matches?(payload, pattern)
+ end) do
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"}
+ else
+ {:ok, message}
+ end
+ end) do
{:ok, message}
+ else
+ e -> e
end
end
- defp check_ftl_removal(%{"to" => to, "object" => %{} = object} = message) do
- payload = object_payload(object)
+ defp check_ftl_removal(%{"type" => "Create", "to" => to, "object" => %{} = object} = message) do
+ check_keyword = fn object ->
+ payload = object_payload(object)
- if Pleroma.Constants.as_public() in to and
- Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
+ if Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->
string_matches?(payload, pattern)
end) do
+ {:should_delist, nil}
+ else
+ {:ok, %{}}
+ end
+ end
+
+ should_delist? = fn object ->
+ with {:ok, _} <- Pleroma.Object.Updater.do_with_history(object, check_keyword) do
+ false
+ else
+ _ -> true
+ end
+ end
+
+ if Pleroma.Constants.as_public() in to and should_delist?.(object) do
to = List.delete(to, Pleroma.Constants.as_public())
cc = [Pleroma.Constants.as_public() | message["cc"] || []]
@@ -59,8 +81,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end
end
+ defp check_ftl_removal(message) do
+ {:ok, message}
+ end
+
defp check_replace(%{"object" => %{} = object} = message) do
- object =
+ replace_kw = fn object ->
["content", "name", "summary"]
|> Enum.filter(fn field -> Map.has_key?(object, field) && object[field] end)
|> Enum.reduce(object, fn field, object ->
@@ -73,6 +99,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
Map.put(object, field, data)
end)
+ |> (fn object -> {:ok, object} end).()
+ end
+
+ {:ok, object} = Pleroma.Object.Updater.do_with_history(object, replace_kw)
message = Map.put(message, "object", object)
@@ -80,7 +110,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
end
@impl true
- def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do
+ def filter(%{"type" => type, "object" => %{"content" => _content}} = message)
+ when type in ["Create", "Update"] do
with {:ok, message} <- check_reject(message),
{:ok, message} <- check_ftl_removal(message),
{:ok, message} <- check_replace(message) do
@@ -159,6 +190,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
%{
key: :replace,
type: {:list, :tuple},
+ key_placeholder: "instance",
+ value_placeholder: "reason",
description: """
**Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
index 8dbf44071..c95d35bb9 100644
--- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
@@ -1,10 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
@moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.HTTP
alias Pleroma.Web.MediaProxy
@@ -16,6 +16,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
recv_timeout: 10_000
]
+ @impl true
+ def history_awareness, do: :auto
+
defp prefetch(url) do
# Fetching only proxiable resources
if MediaProxy.enabled?() and MediaProxy.url_proxiable?(url) do
@@ -54,10 +57,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
end
@impl true
- def filter(
- %{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message
- )
- when is_list(attachments) and length(attachments) > 0 do
+ def filter(%{"type" => type, "object" => %{"attachment" => attachments} = _object} = message)
+ when type in ["Create", "Update"] and is_list(attachments) and length(attachments) > 0 do
preload(message)
{:ok, message}
diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
index 877277d4f..8aa4f347f 100644
--- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
@@ -1,11 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
@moduledoc "Block messages which mention a user"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
def filter(%{"type" => "Create"} = message) do
diff --git a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex
index 32bb1b645..855cda3b9 100644
--- a/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/no_empty_policy.ex
@@ -1,16 +1,17 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
@moduledoc "Filter local activities which have no content"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
@impl true
def filter(%{"actor" => actor} = object) do
with true <- is_local?(actor),
+ true <- is_eligible_type?(object),
true <- is_note?(object),
false <- has_attachment?(object),
true <- only_mentions?(object) do
@@ -24,7 +25,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
def filter(object), do: {:ok, object}
defp is_local?(actor) do
- if actor |> String.starts_with?("#{Web.base_url()}") do
+ if actor |> String.starts_with?("#{Endpoint.url()}") do
true
else
false
@@ -32,7 +33,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
end
defp has_attachment?(%{
- "type" => "Create",
"object" => %{"type" => "Note", "attachment" => attachments}
})
when length(attachments) > 0,
@@ -40,7 +40,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
defp has_attachment?(_), do: false
- defp only_mentions?(%{"type" => "Create", "object" => %{"type" => "Note", "source" => source}}) do
+ defp only_mentions?(%{"object" => %{"type" => "Note", "source" => source}}) do
+ source =
+ case source do
+ %{"content" => text} -> text
+ _ -> source
+ end
+
non_mentions =
source |> String.split() |> Enum.filter(&(not String.starts_with?(&1, "@"))) |> length
@@ -53,9 +59,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
defp only_mentions?(_), do: false
- defp is_note?(%{"type" => "Create", "object" => %{"type" => "Note"}}), do: true
+ defp is_note?(%{"object" => %{"type" => "Note"}}), do: true
defp is_note?(_), do: false
+ defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
+ defp is_eligible_type?(_), do: false
+
@impl true
def describe, do: {:ok, %{}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex
index 2ebc0674d..8840c4fac 100644
--- a/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/no_op_policy.ex
@@ -1,10 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
@moduledoc "Does nothing (lets the messages go through unmodified)"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
def filter(object) do
diff --git a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
index b658d7d41..f81e9e52a 100644
--- a/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/no_placeholder_text_policy.ex
@@ -1,19 +1,22 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
@moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
+
+ @impl true
+ def history_awareness, do: :auto
@impl true
def filter(
%{
- "type" => "Create",
+ "type" => type,
"object" => %{"content" => content, "attachment" => _} = _child_object
} = object
)
- when content in [".", "<p>.</p>"] do
+ when type in ["Create", "Update"] and content in [".", "<p>.</p>"] do
{:ok, put_in(object, ["object", "content"], "")}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
index 2ad3fde0b..2dfc9a901 100644
--- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
+++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
@@ -1,15 +1,19 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@moduledoc "Scrub configured hypertext markup"
alias Pleroma.HTML
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
- def filter(%{"type" => "Create", "object" => child_object} = object) do
+ def history_awareness, do: :auto
+
+ @impl true
+ def filter(%{"type" => type, "object" => child_object} = object)
+ when type in ["Create", "Update"] do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
content =
diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
index aac24c0ec..df1a6dcbb 100644
--- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
require Pleroma.Constants
@moduledoc "Filter activities depending on their age"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp check_date(%{"object" => %{"published" => published}} = message) do
with %DateTime{} = now <- DateTime.utc_now(),
@@ -49,6 +49,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
message
|> Map.put("to", to)
|> Map.put("cc", cc)
+ |> Kernel.put_in(["object", "to"], to)
+ |> Kernel.put_in(["object", "cc"], cc)
{:ok, message}
else
@@ -70,6 +72,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
message
|> Map.put("to", to)
|> Map.put("cc", cc)
+ |> Kernel.put_in(["object", "to"], to)
+ |> Kernel.put_in(["object", "cc"], cc)
{:ok, message}
else
@@ -82,7 +86,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
end
@impl true
- def filter(%{"type" => "Create", "published" => _} = message) do
+ def filter(%{"type" => "Create", "object" => %{"published" => _}} = message) do
with actions <- Config.get([:mrf_object_age, :actions]),
{:reject, _} <- check_date(message),
{:ok, message} <- check_reject(message, actions),
@@ -127,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
type: {:list, :atom},
description:
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
- "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
+ "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines, additionally for followers-only it degrades to a direct message; " <>
"`:reject` rejects the message entirely",
suggestions: [:delist, :strip_followers, :reject]
}
diff --git a/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
index be95e38ec..b2477fed4 100644
--- a/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
+++ b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.PipelineFiltering do
diff --git a/lib/pleroma/web/activity_pub/mrf/policy.ex b/lib/pleroma/web/activity_pub/mrf/policy.ex
new file mode 100644
index 000000000..0234de4d5
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/policy.ex
@@ -0,0 +1,17 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.MRF.Policy do
+ @callback filter(Map.t()) :: {:ok | :reject, Map.t()}
+ @callback describe() :: {:ok | :error, Map.t()}
+ @callback config_description() :: %{
+ optional(:children) => [map()],
+ key: atom(),
+ related_policy: String.t(),
+ label: String.t(),
+ description: String.t()
+ }
+ @callback history_awareness() :: :auto | :manual
+ @optional_callbacks config_description: 0, history_awareness: 0
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index 47a43c6a2..9d4a7a405 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
alias Pleroma.Config
alias Pleroma.User
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
require Pleroma.Constants
@@ -47,7 +47,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
@impl true
def describe,
- do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
+ do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Map.new()}}
@impl true
def config_description do
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 62024c58c..829ddeaea 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -1,10 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
@moduledoc "Filter activities depending on their origin instance"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config
alias Pleroma.FollowingRelationship
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_accept(%{host: actor_host} = _actor_info, object) do
accepts =
- Config.get([:mrf_simple, :accept])
+ instance_list(:accept)
|> MRF.subdomains_regex()
cond do
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_reject(%{host: actor_host} = _actor_info, object) do
rejects =
- Config.get([:mrf_simple, :reject])
+ instance_list(:reject)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(rejects, actor_host) do
@@ -40,11 +40,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_media_removal(
%{host: actor_host} = _actor_info,
- %{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
+ %{"type" => type, "object" => %{"attachment" => child_attachment}} = object
)
- when length(child_attachment) > 0 do
+ when length(child_attachment) > 0 and type in ["Create", "Update"] do
media_removal =
- Config.get([:mrf_simple, :media_removal])
+ instance_list(:media_removal)
|> MRF.subdomains_regex()
object =
@@ -63,12 +63,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_media_nsfw(
%{host: actor_host} = _actor_info,
%{
- "type" => "Create",
+ "type" => type,
"object" => %{} = _child_object
} = object
- ) do
+ )
+ when type in ["Create", "Update"] do
media_nsfw =
- Config.get([:mrf_simple, :media_nsfw])
+ instance_list(:media_nsfw)
|> MRF.subdomains_regex()
object =
@@ -85,7 +86,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
timeline_removal =
- Config.get([:mrf_simple, :federated_timeline_removal])
+ instance_list(:federated_timeline_removal)
|> MRF.subdomains_regex()
object =
@@ -112,7 +113,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_followers_only(%{host: actor_host} = _actor_info, object) do
followers_only =
- Config.get([:mrf_simple, :followers_only])
+ instance_list(:followers_only)
|> MRF.subdomains_regex()
object =
@@ -137,7 +138,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
report_removal =
- Config.get([:mrf_simple, :report_removal])
+ instance_list(:report_removal)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(report_removal, actor_host) do
@@ -151,7 +152,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
avatar_removal =
- Config.get([:mrf_simple, :avatar_removal])
+ instance_list(:avatar_removal)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(avatar_removal, actor_host) do
@@ -165,7 +166,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
banner_removal =
- Config.get([:mrf_simple, :banner_removal])
+ instance_list(:banner_removal)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(banner_removal, actor_host) do
@@ -177,12 +178,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_banner_removal(_actor_info, object), do: {:ok, object}
+ defp check_object(%{"object" => object} = activity) do
+ with {:ok, _object} <- filter(object) do
+ {:ok, activity}
+ end
+ end
+
+ defp check_object(object), do: {:ok, object}
+
+ defp instance_list(config_key) do
+ Config.get([:mrf_simple, config_key])
+ |> MRF.instance_list_from_tuples()
+ end
+
@impl true
def filter(%{"type" => "Delete", "actor" => actor} = object) do
%{host: actor_host} = URI.parse(actor)
reject_deletes =
- Config.get([:mrf_simple, :reject_deletes])
+ instance_list(:reject_deletes)
|> MRF.subdomains_regex()
if MRF.subdomain_match?(reject_deletes, actor_host) do
@@ -202,7 +216,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, object} <- check_media_nsfw(actor_info, object),
{:ok, object} <- check_ftl_removal(actor_info, object),
{:ok, object} <- check_followers_only(actor_info, object),
- {:ok, object} <- check_report_removal(actor_info, object) do
+ {:ok, object} <- check_report_removal(actor_info, object),
+ {:ok, object} <- check_object(object) do
{:ok, object}
else
{:reject, nil} -> {:reject, "[SimplePolicy]"}
@@ -227,18 +242,59 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
end
end
+ def filter(object) when is_binary(object) do
+ uri = URI.parse(object)
+
+ with {:ok, object} <- check_accept(uri, object),
+ {:ok, object} <- check_reject(uri, object) do
+ {:ok, object}
+ else
+ {:reject, nil} -> {:reject, "[SimplePolicy]"}
+ {:reject, _} = e -> e
+ _ -> {:reject, "[SimplePolicy]"}
+ end
+ end
+
def filter(object), do: {:ok, object}
@impl true
def describe do
- exclusions = Config.get([:mrf, :transparency_exclusions])
+ exclusions = Config.get([:mrf, :transparency_exclusions]) |> MRF.instance_list_from_tuples()
- mrf_simple =
+ mrf_simple_excluded =
Config.get(:mrf_simple)
- |> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
- |> Enum.into(%{})
+ |> Enum.map(fn {rule, instances} ->
+ {rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)}
+ end)
- {:ok, %{mrf_simple: mrf_simple}}
+ mrf_simple =
+ mrf_simple_excluded
+ |> Enum.map(fn {rule, instances} ->
+ {rule, Enum.map(instances, fn {host, _} -> host end)}
+ end)
+ |> Map.new()
+
+ # This is for backwards compatibility. We originally didn't sent
+ # extra info like a reason why an instance was rejected/quarantined/etc.
+ # Because we didn't want to break backwards compatibility it was decided
+ # to add an extra "info" key.
+ mrf_simple_info =
+ mrf_simple_excluded
+ |> Enum.map(fn {rule, instances} ->
+ {rule, Enum.reject(instances, fn {_, reason} -> reason == "" end)}
+ end)
+ |> Enum.reject(fn {_, instances} -> instances == [] end)
+ |> Enum.map(fn {rule, instances} ->
+ instances =
+ instances
+ |> Enum.map(fn {host, reason} -> {host, %{"reason" => reason}} end)
+ |> Map.new()
+
+ {rule, instances}
+ end)
+ |> Map.new()
+
+ {:ok, %{mrf_simple: mrf_simple, mrf_simple_info: mrf_simple_info}}
end
@impl true
@@ -248,70 +304,67 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
label: "MRF Simple",
description: "Simple ingress policies",
- children: [
- %{
- key: :media_removal,
- type: {:list, :string},
- description: "List of instances to strip media attachments from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :media_nsfw,
- label: "Media NSFW",
- type: {:list, :string},
- description: "List of instances to tag all media as NSFW (sensitive) from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :federated_timeline_removal,
- type: {:list, :string},
- description:
- "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :reject,
- type: {:list, :string},
- description: "List of instances to reject activities from (except deletes)",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :accept,
- type: {:list, :string},
- description: "List of instances to only accept activities from (except deletes)",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :followers_only,
- type: {:list, :string},
- description: "Force posts from the given instances to be visible by followers only",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :report_removal,
- type: {:list, :string},
- description: "List of instances to reject reports from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :avatar_removal,
- type: {:list, :string},
- description: "List of instances to strip avatars from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :banner_removal,
- type: {:list, :string},
- description: "List of instances to strip banners from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :reject_deletes,
- type: {:list, :string},
- description: "List of instances to reject deletions from",
- suggestions: ["example.com", "*.example.com"]
- }
- ]
+ children:
+ [
+ %{
+ key: :media_removal,
+ description:
+ "List of instances to strip media attachments from and the reason for doing so"
+ },
+ %{
+ key: :media_nsfw,
+ label: "Media NSFW",
+ description:
+ "List of instances to tag all media as NSFW (sensitive) from and the reason for doing so"
+ },
+ %{
+ key: :federated_timeline_removal,
+ description:
+ "List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so"
+ },
+ %{
+ key: :reject,
+ description:
+ "List of instances to reject activities from (except deletes) and the reason for doing so"
+ },
+ %{
+ key: :accept,
+ description:
+ "List of instances to only accept activities from (except deletes) and the reason for doing so"
+ },
+ %{
+ key: :followers_only,
+ description:
+ "Force posts from the given instances to be visible by followers only and the reason for doing so"
+ },
+ %{
+ key: :report_removal,
+ description: "List of instances to reject reports from and the reason for doing so"
+ },
+ %{
+ key: :avatar_removal,
+ description: "List of instances to strip avatars from and the reason for doing so"
+ },
+ %{
+ key: :banner_removal,
+ description: "List of instances to strip banners from and the reason for doing so"
+ },
+ %{
+ key: :reject_deletes,
+ description: "List of instances to reject deletions from and the reason for doing so"
+ }
+ ]
+ |> Enum.map(fn setting ->
+ Map.merge(
+ setting,
+ %{
+ type: {:list, :tuple},
+ key_placeholder: "instance",
+ value_placeholder: "reason",
+ suggestions: [{"example.com", "Some reason"}, {"*.example.com", "Another reason"}]
+ }
+ )
+ end)
}
end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex
index 4c5e33619..f66c379b5 100644
--- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
@@ -8,10 +8,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
alias Pleroma.Config
@moduledoc "Detect new emojis by their shortcode and steals them"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], [])
+ defp shortcode_matches?(shortcode, pattern) when is_binary(pattern) do
+ shortcode == pattern
+ end
+
+ defp shortcode_matches?(shortcode, pattern) do
+ String.match?(shortcode, pattern)
+ end
+
defp steal_emoji({shortcode, url}, emoji_dir_path) do
url = Pleroma.Web.MediaProxy.url(url)
@@ -38,9 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
end
else
Logger.debug(
- "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{
- size_limit
- } B)"
+ "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)"
)
nil
@@ -74,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
reject_emoji? =
[:mrf_steal_emoji, :rejected_shortcodes]
|> Config.get([])
- |> Enum.find(false, fn regex -> String.match?(shortcode, regex) end)
+ |> Enum.find(false, fn pattern -> shortcode_matches?(shortcode, pattern) end)
!reject_emoji?
end)
@@ -93,6 +99,55 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
def filter(message), do: {:ok, message}
@impl true
+ @spec config_description :: %{
+ children: [
+ %{
+ description: <<_::272, _::_*256>>,
+ key: :hosts | :rejected_shortcodes | :size_limit,
+ suggestions: [any(), ...],
+ type: {:list, :string} | {:list, :string} | :integer
+ },
+ ...
+ ],
+ description: <<_::448>>,
+ key: :mrf_steal_emoji,
+ label: <<_::80>>,
+ related_policy: <<_::352>>
+ }
+ def config_description do
+ %{
+ key: :mrf_steal_emoji,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy",
+ label: "MRF Emojis",
+ description: "Steals emojis from selected instances when it sees them.",
+ children: [
+ %{
+ key: :hosts,
+ type: {:list, :string},
+ description: "List of hosts to steal emojis from",
+ suggestions: [""]
+ },
+ %{
+ key: :rejected_shortcodes,
+ type: {:list, :string},
+ description: """
+ A list of patterns or matches to reject shortcodes with.
+
+ Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+ """,
+ suggestions: ["foo", ~r/foo/]
+ },
+ %{
+ key: :size_limit,
+ type: :integer,
+ description: "File size limit (in bytes), checked before an emoji is saved to the disk",
+ suggestions: ["100000"]
+ }
+ ]
+ }
+ end
+
+ @impl true
def describe do
{:ok, %{}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
index 86965d47b..fdb9e5176 100644
--- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
require Logger
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp lookup_subchain(actor) do
with matches <- Config.get([:mrf_subchain, :match_actor]),
@@ -23,9 +23,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
def filter(%{"actor" => actor} = message) do
with {:ok, match, subchain} <- lookup_subchain(actor) do
Logger.debug(
- "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{
- inspect(subchain)
- }"
+ "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{inspect(subchain)}"
)
MRF.filter(subchain, message)
diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
index 528093ac0..73760ca8f 100644
--- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex
@@ -1,10 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
alias Pleroma.User
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@moduledoc """
Apply policies based on user tags
@@ -27,22 +27,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
defp process_tag(
"mrf_tag:media-force-nsfw",
%{
- "type" => "Create",
+ "type" => type,
"object" => %{"attachment" => child_attachment}
} = message
)
- when length(child_attachment) > 0 do
+ when length(child_attachment) > 0 and type in ["Create", "Update"] do
{:ok, Kernel.put_in(message, ["object", "sensitive"], true)}
end
defp process_tag(
"mrf_tag:media-strip",
%{
- "type" => "Create",
+ "type" => type,
"object" => %{"attachment" => child_attachment} = object
} = message
)
- when length(child_attachment) > 0 do
+ when length(child_attachment) > 0 and type in ["Create", "Update"] do
object = Map.delete(object, "attachment")
message = Map.put(message, "object", object)
@@ -152,7 +152,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
do: filter_message(target_actor, message)
@impl true
- def filter(%{"actor" => actor, "type" => "Create"} = message),
+ def filter(%{"actor" => actor, "type" => type} = message) when type in ["Create", "Update"],
do: filter_message(actor, message)
@impl true
diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
index 65b371bf3..e14047d4e 100644
--- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
@@ -1,12 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
alias Pleroma.Config
@moduledoc "Accept-list of users from specified instances"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp filter_by_list(object, []), do: {:ok, object}
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
def describe do
mrf_user_allowlist =
Config.get([:mrf_user_allowlist], [])
- |> Enum.into(%{}, fn {k, v} -> {k, length(v)} end)
+ |> Map.new(fn {k, v} -> {k, length(v)} end)
{:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
index ce559a239..d9deff35f 100644
--- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
@@ -1,11 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
@moduledoc "Filter messages which belong to certain activity vocabularies"
- @behaviour Pleroma.Web.ActivityPub.MRF
+ @behaviour Pleroma.Web.ActivityPub.MRF.Policy
@impl true
def filter(%{"type" => "Undo", "object" => child_message} = message) do
@@ -39,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
@impl true
def describe,
- do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
+ do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Map.new()}}
@impl true
def config_description do
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index 1dce33f1a..5e0d1aa8e 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@@ -20,8 +20,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator
- alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator
- alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
@@ -102,9 +102,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
meta
)
- when objtype in ~w[Question Answer Audio Video Event Article] do
- with {:ok, object_data} <- cast_and_apply(object),
- meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
+ when objtype in ~w[Question Answer Audio Video Image Event Article Note Page] do
+ with {:ok, object_data} <- cast_and_apply_and_stringify_with_history(object),
+ meta = Keyword.put(meta, :object_data, object_data),
{:ok, create_activity} <-
create_activity
|> CreateGenericValidator.cast_and_validate(meta)
@@ -115,27 +115,64 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
end
def validate(%{"type" => type} = object, meta)
- when type in ~w[Event Question Audio Video Article] do
+ when type in ~w[Event Question Audio Video Image Article Note Page] do
validator =
case type do
"Event" -> EventValidator
"Question" -> QuestionValidator
- "Audio" -> AudioVideoValidator
- "Video" -> AudioVideoValidator
- "Article" -> ArticleNoteValidator
+ "Audio" -> AudioImageVideoValidator
+ "Video" -> AudioImageVideoValidator
+ "Image" -> AudioImageVideoValidator
+ "Article" -> ArticleNotePageValidator
+ "Note" -> ArticleNotePageValidator
+ "Page" -> ArticleNotePageValidator
end
with {:ok, object} <-
- object
- |> validator.cast_and_validate()
- |> Ecto.Changeset.apply_action(:insert) do
- object = stringify_keys(object)
+ do_separate_with_history(object, fn object ->
+ with {:ok, object} <-
+ object
+ |> validator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ object = stringify_keys(object)
+
+ # Insert copy of hashtags as strings for the non-hashtag table indexing
+ tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object})
+ object = Map.put(object, "tag", tag)
+
+ {:ok, object}
+ end
+ end) do
+ {:ok, object, meta}
+ end
+ end
- # Insert copy of hashtags as strings for the non-hashtag table indexing
- tag = (object["tag"] || []) ++ Object.hashtags(%Object{data: object})
- object = Map.put(object, "tag", tag)
+ def validate(
+ %{"type" => "Update", "object" => %{"type" => objtype} = object} = update_activity,
+ meta
+ )
+ when objtype in ~w[Question Answer Audio Video Event Article Note Page] do
+ with {_, false} <- {:local, Access.get(meta, :local, false)},
+ {_, {:ok, object_data, _}} <- {:object_validation, validate(object, meta)},
+ meta = Keyword.put(meta, :object_data, object_data),
+ {:ok, update_activity} <-
+ update_activity
+ |> UpdateValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ update_activity = stringify_keys(update_activity)
+ {:ok, update_activity, meta}
+ else
+ {:local, _} ->
+ with {:ok, object} <-
+ update_activity
+ |> UpdateValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ object = stringify_keys(object)
+ {:ok, object, meta}
+ end
- {:ok, object, meta}
+ {:object_validation, e} ->
+ e
end
end
@@ -174,6 +211,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
end
end
+ def validate(o, m), do: {:error, {:validator_not_set, {o, m}}}
+
+ def cast_and_apply_and_stringify_with_history(object) do
+ do_separate_with_history(object, fn object ->
+ with {:ok, object_data} <- cast_and_apply(object),
+ object_data <- object_data |> stringify_keys() do
+ {:ok, object_data}
+ end
+ end)
+ end
+
def cast_and_apply(%{"type" => "ChatMessage"} = object) do
ChatMessageValidator.cast_and_apply(object)
end
@@ -186,22 +234,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
AnswerValidator.cast_and_apply(object)
end
- def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Video] do
- AudioVideoValidator.cast_and_apply(object)
+ def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Image Video] do
+ AudioImageVideoValidator.cast_and_apply(object)
end
def cast_and_apply(%{"type" => "Event"} = object) do
EventValidator.cast_and_apply(object)
end
- def cast_and_apply(%{"type" => "Article"} = object) do
- ArticleNoteValidator.cast_and_apply(object)
+ def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do
+ ArticleNotePageValidator.cast_and_apply(object)
end
def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
- # is_struct/1 appears in Elixir 1.11
- def stringify_keys(%{__struct__: _} = object) do
+ def stringify_keys(object) when is_struct(object) do
object
|> Map.from_struct()
|> stringify_keys
@@ -209,6 +256,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
def stringify_keys(object) when is_map(object) do
object
+ |> Enum.filter(fn {_, v} -> v != nil end)
|> Map.new(fn {key, val} -> {to_string(key), stringify_keys(val)} end)
end
@@ -231,4 +279,54 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
Object.normalize(object["object"], fetch: true)
:ok
end
+
+ defp for_each_history_item(
+ %{"type" => "OrderedCollection", "orderedItems" => items} = history,
+ object,
+ fun
+ ) do
+ processed_items =
+ Enum.map(items, fn item ->
+ with item <- Map.put(item, "id", object["id"]),
+ {:ok, item} <- fun.(item) do
+ item
+ else
+ _ -> nil
+ end
+ end)
+
+ if Enum.all?(processed_items, &(not is_nil(&1))) do
+ {:ok, Map.put(history, "orderedItems", processed_items)}
+ else
+ {:error, :invalid_history}
+ end
+ end
+
+ defp for_each_history_item(nil, _object, _fun) do
+ {:ok, nil}
+ end
+
+ defp for_each_history_item(_, _object, _fun) do
+ {:error, :invalid_history}
+ end
+
+ # fun is (object -> {:ok, validated_object_with_string_keys})
+ defp do_separate_with_history(object, fun) do
+ with history <- object["formerRepresentations"],
+ object <- Map.drop(object, ["formerRepresentations"]),
+ {_, {:ok, object}} <- {:main_body, fun.(object)},
+ {_, {:ok, history}} <- {:history_items, for_each_history_item(history, object, fun)} do
+ object =
+ if history do
+ Map.put(object, "formerRepresentations", history)
+ else
+ object
+ end
+
+ {:ok, object}
+ else
+ {:main_body, e} -> e
+ {:history_items, e} -> e
+ end
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validator/validating.ex b/lib/pleroma/web/activity_pub/object_validator/validating.ex
index 28e8d2498..b695946b4 100644
--- a/lib/pleroma/web/activity_pub/object_validator/validating.ex
+++ b/lib/pleroma/web/activity_pub/object_validator/validating.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidator.Validating do
diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex
index b577a1044..d611da051 100644
--- a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex
@@ -1,12 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
use Ecto.Schema
alias Pleroma.Activity
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@@ -14,12 +13,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:object, ObjectValidators.ObjectID)
- field(:actor, ObjectValidators.ObjectID)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
end
def cast_data(data) do
diff --git a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex
index f885aabe4..db3259550 100644
--- a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do
@@ -10,19 +10,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do
require Pleroma.Constants
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.User
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
field(:target)
- field(:object, ObjectValidators.ObjectID)
- field(:actor, ObjectValidators.ObjectID)
- field(:type)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
+
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
end
def cast_and_validate(data) do
@@ -72,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do
end
defp maybe_refetch_user(%User{ap_id: ap_id}) do
- Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(ap_id)
+ # Maybe it could use User.get_or_fetch_by_ap_id to avoid refreshing too often
+ User.fetch_by_ap_id(ap_id)
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
index 576341790..c2c7ba1a8 100644
--- a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
@@ -19,13 +20,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:object, ObjectValidators.ObjectID)
- field(:actor, ObjectValidators.ObjectID)
- field(:context, :string, autogenerate: {Utils, :generate_context_id, []})
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
+
+ field(:context, :string)
field(:published, ObjectValidators.DateTime)
end
@@ -36,6 +39,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
end
def cast_data(data) do
+ data =
+ data
+ |> fix()
+
%__MODULE__{}
|> changeset(data)
end
@@ -43,11 +50,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
def changeset(struct, data) do
struct
|> cast(data, __schema__(:fields))
- |> fix_after_cast()
end
- def fix_after_cast(cng) do
- cng
+ defp fix(data) do
+ data =
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
+
+ with %Object{} = object <- Object.normalize(data["object"]) do
+ data
+ |> CommonFixes.fix_activity_context(object)
+ |> CommonFixes.fix_object_action_recipients(object)
+ else
+ _ -> data
+ end
end
defp validate_data(data_cng) do
@@ -60,7 +77,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|> validate_announcable()
end
- def validate_announcable(cng) do
+ defp validate_announcable(cng) do
with actor when is_binary(actor) <- get_field(cng, :actor),
object when is_binary(object) <- get_field(cng, :object),
%User{} = actor <- User.get_cached_by_ap_id(actor),
@@ -68,7 +85,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
false <- Visibility.is_public?(object) do
same_actor = object.data["actor"] == actor.ap_id
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
- local_public = Pleroma.Constants.as_local_public()
+ local_public = Utils.as_local_public()
is_public =
Enum.member?(recipients, Pleroma.Constants.as_public()) or
@@ -91,7 +108,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
end
end
- def validate_existing_announce(cng) do
+ defp validate_existing_announce(cng) do
actor = get_field(cng, :actor)
object = get_field(cng, :object)
diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex
index c9bd9e42d..2d9b8ba02 100644
--- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex
@@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
import Ecto.Changeset
@@ -14,15 +15,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
@derive Jason.Encoder
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:bto, ObjectValidators.Recipients, default: [])
- field(:bcc, ObjectValidators.Recipients, default: [])
- field(:type, :string)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ end
+ end
+
field(:name, :string)
field(:inReplyTo, ObjectValidators.ObjectID)
field(:attributedTo, ObjectValidators.ObjectID)
+ field(:context, :string)
# TODO: Remove actor on objects
field(:actor, ObjectValidators.ObjectID)
@@ -46,6 +49,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
end
def changeset(struct, data) do
+ data =
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_object_defaults()
+
struct
|> cast(data, __schema__(:fields))
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex
new file mode 100644
index 000000000..2670e3f17
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex
@@ -0,0 +1,109 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
+ use Ecto.Schema
+
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+ alias Pleroma.Web.ActivityPub.Transmogrifier
+
+ import Ecto.Changeset
+
+ @primary_key false
+ @derive Jason.Encoder
+
+ embedded_schema do
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ object_fields()
+ status_object_fields()
+ end
+ end
+
+ field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
+ end
+
+ def cast_and_apply(data) do
+ data
+ |> cast_data
+ |> apply_action(:insert)
+ end
+
+ def cast_and_validate(data) do
+ data
+ |> cast_data()
+ |> validate_data()
+ end
+
+ def cast_data(data) do
+ %__MODULE__{}
+ |> changeset(data)
+ end
+
+ defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data
+ defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"])
+ defp fix_url(data), do: data
+
+ defp fix_tag(%{"tag" => tag} = data) when is_list(tag) do
+ Map.put(data, "tag", Enum.filter(tag, &is_map/1))
+ end
+
+ defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag])
+ defp fix_tag(data), do: Map.drop(data, ["tag"])
+
+ defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data)
+ when is_list(replies),
+ do: Map.put(data, "replies", replies)
+
+ defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies),
+ do: Map.put(data, "replies", replies)
+
+ # TODO: Pleroma does not have any support for Collections at the moment.
+ # If the `replies` field is not something the ObjectID validator can handle,
+ # the activity/object would be rejected, which is bad behavior.
+ defp fix_replies(%{"replies" => replies} = data) when not is_list(replies),
+ do: Map.drop(data, ["replies"])
+
+ defp fix_replies(data), do: data
+
+ def fix_attachments(%{"attachment" => attachment} = data) when is_map(attachment),
+ do: Map.put(data, "attachment", [attachment])
+
+ def fix_attachments(data), do: data
+
+ defp fix(data) do
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_object_defaults()
+ |> fix_url()
+ |> fix_tag()
+ |> fix_replies()
+ |> fix_attachments()
+ |> Transmogrifier.fix_emoji()
+ |> Transmogrifier.fix_content_map()
+ end
+
+ def changeset(struct, data) do
+ data = fix(data)
+
+ struct
+ |> cast(data, __schema__(:fields) -- [:attachment, :tag])
+ |> cast_embed(:attachment)
+ |> cast_embed(:tag)
+ end
+
+ defp validate_data(data_cng) do
+ data_cng
+ |> validate_inclusion(:type, ["Article", "Note", "Page"])
+ |> validate_required([:id, :actor, :attributedTo, :type, :context])
+ |> CommonValidations.validate_any_presence([:cc, :to])
+ |> CommonValidations.validate_fields_match([:actor, :attributedTo])
+ |> CommonValidations.validate_actor_presence()
+ |> CommonValidations.validate_host_match()
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex
deleted file mode 100644
index 39ef6dc29..000000000
--- a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex
+++ /dev/null
@@ -1,107 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do
- use Ecto.Schema
-
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
- alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
- alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
- alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
- alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
- alias Pleroma.Web.ActivityPub.Transmogrifier
-
- import Ecto.Changeset
-
- @primary_key false
- @derive Jason.Encoder
-
- embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:bto, ObjectValidators.Recipients, default: [])
- field(:bcc, ObjectValidators.Recipients, default: [])
- embeds_many(:tag, TagValidator)
- field(:type, :string)
-
- field(:name, :string)
- field(:summary, :string)
- field(:content, :string)
-
- field(:context, :string)
- # short identifier for PleromaFE to group statuses by context
- field(:context_id, :integer)
-
- # TODO: Remove actor on objects
- field(:actor, ObjectValidators.ObjectID)
-
- field(:attributedTo, ObjectValidators.ObjectID)
- field(:published, ObjectValidators.DateTime)
- field(:emoji, ObjectValidators.Emoji, default: %{})
- field(:sensitive, :boolean, default: false)
- embeds_many(:attachment, AttachmentValidator)
- field(:replies_count, :integer, default: 0)
- field(:like_count, :integer, default: 0)
- field(:announcement_count, :integer, default: 0)
- field(:inReplyTo, ObjectValidators.ObjectID)
- field(:url, ObjectValidators.Uri)
-
- field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
- field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
- end
-
- def cast_and_apply(data) do
- data
- |> cast_data
- |> apply_action(:insert)
- end
-
- def cast_and_validate(data) do
- data
- |> cast_data()
- |> validate_data()
- end
-
- def cast_data(data) do
- data = fix(data)
-
- %__MODULE__{}
- |> changeset(data)
- end
-
- defp fix_url(%{"url" => url} = data) when is_map(url) do
- Map.put(data, "url", url["href"])
- end
-
- defp fix_url(data), do: data
-
- defp fix(data) do
- data
- |> CommonFixes.fix_defaults()
- |> CommonFixes.fix_attribution()
- |> CommonFixes.fix_actor()
- |> fix_url()
- |> Transmogrifier.fix_emoji()
- end
-
- def changeset(struct, data) do
- data = fix(data)
-
- struct
- |> cast(data, __schema__(:fields) -- [:attachment, :tag])
- |> cast_embed(:attachment)
- |> cast_embed(:tag)
- end
-
- defp validate_data(data_cng) do
- data_cng
- |> validate_inclusion(:type, ["Article", "Note"])
- |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
- |> CommonValidations.validate_any_presence([:cc, :to])
- |> CommonValidations.validate_fields_match([:actor, :attributedTo])
- |> CommonValidations.validate_actor_presence()
- |> CommonValidations.validate_host_match()
- end
-end
diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
index 4a0d1473d..398020bff 100644
--- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
@@ -11,15 +11,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
@primary_key false
embedded_schema do
+ field(:id, :string)
field(:type, :string)
- field(:mediaType, :string, default: "application/octet-stream")
+ field(:mediaType, ObjectValidators.MIME, default: "application/octet-stream")
field(:name, :string)
field(:blurhash, :string)
embeds_many :url, UrlObjectValidator, primary_key: false do
field(:type, :string)
field(:href, ObjectValidators.Uri)
- field(:mediaType, :string, default: "application/octet-stream")
+ field(:mediaType, ObjectValidators.MIME, default: "application/octet-stream")
+ field(:width, :integer)
+ field(:height, :integer)
end
end
@@ -41,37 +44,33 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|> fix_url()
struct
- |> cast(data, [:type, :mediaType, :name, :blurhash])
- |> cast_embed(:url, with: &url_changeset/2)
+ |> cast(data, [:id, :type, :mediaType, :name, :blurhash])
+ |> cast_embed(:url, with: &url_changeset/2, required: true)
|> validate_inclusion(:type, ~w[Link Document Audio Image Video])
- |> validate_required([:type, :mediaType, :url])
+ |> validate_required([:type, :mediaType])
end
def url_changeset(struct, data) do
data = fix_media_type(data)
struct
- |> cast(data, [:type, :href, :mediaType])
+ |> cast(data, [:type, :href, :mediaType, :width, :height])
|> validate_inclusion(:type, ["Link"])
|> validate_required([:type, :href, :mediaType])
end
def fix_media_type(data) do
- data = Map.put_new(data, "mediaType", data["mimeType"])
-
- if MIME.valid?(data["mediaType"]) do
- data
- else
- Map.put(data, "mediaType", "application/octet-stream")
- end
+ Map.put_new(data, "mediaType", data["mimeType"] || "application/octet-stream")
end
- defp handle_href(href, mediaType) do
+ defp handle_href(href, mediaType, data) do
[
%{
"href" => href,
"type" => "Link",
- "mediaType" => mediaType
+ "mediaType" => mediaType,
+ "width" => data["width"],
+ "height" => data["height"]
}
]
end
@@ -79,10 +78,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
defp fix_url(data) do
cond do
is_binary(data["url"]) ->
- Map.put(data, "url", handle_href(data["url"], data["mediaType"]))
+ Map.put(data, "url", handle_href(data["url"], data["mediaType"], data))
is_binary(data["href"]) and data["url"] == nil ->
- Map.put(data, "url", handle_href(data["href"], data["mediaType"]))
+ Map.put(data, "url", handle_href(data["href"], data["mediaType"], data))
true ->
data
@@ -92,6 +91,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
defp validate_data(cng) do
cng
|> validate_inclusion(:type, ~w[Document Audio Image Video])
- |> validate_required([:mediaType, :url, :type])
+ |> validate_required([:mediaType, :type])
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_image_video_validator.ex
index 8a5a60526..79ff76104 100644
--- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/audio_image_video_validator.ex
@@ -1,16 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator do
use Ecto.Schema
- alias Pleroma.EarmarkRenderer
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
- alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
- alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
alias Pleroma.Web.ActivityPub.Transmogrifier
import Ecto.Changeset
@@ -19,38 +15,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do
@derive Jason.Encoder
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:bto, ObjectValidators.Recipients, default: [])
- field(:bcc, ObjectValidators.Recipients, default: [])
- embeds_many(:tag, TagValidator)
- field(:type, :string)
-
- field(:name, :string)
- field(:summary, :string)
- field(:content, :string)
-
- field(:context, :string)
- # short identifier for PleromaFE to group statuses by context
- field(:context_id, :integer)
-
- # TODO: Remove actor on objects
- field(:actor, ObjectValidators.ObjectID)
-
- field(:attributedTo, ObjectValidators.ObjectID)
- field(:published, ObjectValidators.DateTime)
- field(:emoji, ObjectValidators.Emoji, default: %{})
- field(:sensitive, :boolean, default: false)
- embeds_many(:attachment, AttachmentValidator)
- field(:replies_count, :integer, default: 0)
- field(:like_count, :integer, default: 0)
- field(:announcement_count, :integer, default: 0)
- field(:inReplyTo, ObjectValidators.ObjectID)
- field(:url, ObjectValidators.Uri)
-
- field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
- field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ object_fields()
+ status_object_fields()
+ end
+ end
end
def cast_and_apply(data) do
@@ -83,9 +55,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do
url
|> Enum.concat(mpeg_url["tag"] || [])
|> Enum.find(fn
- %{"mediaType" => mime_type} -> String.starts_with?(mime_type, ["video/", "audio/"])
- %{"mimeType" => mime_type} -> String.starts_with?(mime_type, ["video/", "audio/"])
- _ -> false
+ %{"mediaType" => mime_type} ->
+ String.starts_with?(mime_type, ["video/", "audio/", "image/"])
+
+ %{"mimeType" => mime_type} ->
+ String.starts_with?(mime_type, ["video/", "audio/", "image/"])
+
+ _ ->
+ false
end)
end
@@ -110,7 +87,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do
when is_binary(content) do
content =
content
- |> Earmark.as_html!(%Earmark.Options{renderer: EarmarkRenderer})
+ |> Pleroma.Formatter.markdown_to_html()
|> Pleroma.HTML.filter_tags()
Map.put(data, "content", content)
@@ -120,9 +97,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do
defp fix(data) do
data
- |> CommonFixes.fix_defaults()
- |> CommonFixes.fix_attribution()
|> CommonFixes.fix_actor()
+ |> CommonFixes.fix_object_defaults()
|> Transmogrifier.fix_emoji()
|> fix_url()
|> fix_content()
@@ -133,14 +109,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do
struct
|> cast(data, __schema__(:fields) -- [:attachment, :tag])
- |> cast_embed(:attachment)
+ |> cast_embed(:attachment, required: true)
|> cast_embed(:tag)
end
defp validate_data(data_cng) do
data_cng
- |> validate_inclusion(:type, ["Audio", "Video"])
- |> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment])
+ |> validate_inclusion(:type, ~w[Audio Image Video])
+ |> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()
diff --git a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex
index 88948135f..0de87a27e 100644
--- a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex
@@ -1,24 +1,25 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do
use Ecto.Schema
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
import Ecto.Changeset
- import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@primary_key false
+ @derive Jason.Encoder
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:actor, ObjectValidators.ObjectID)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:object, ObjectValidators.ObjectID)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
end
def cast_data(data) do
@@ -30,8 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do
cng
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|> validate_inclusion(:type, ["Block"])
- |> validate_actor_presence()
- |> validate_actor_presence(field_name: :object)
+ |> CommonValidations.validate_actor_presence()
+ |> CommonValidations.validate_actor_presence(field_name: :object)
end
def cast_and_validate(data) do
diff --git a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
index b153156b0..efae48cae 100644
--- a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex
new file mode 100644
index 000000000..d580208df
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex
@@ -0,0 +1,67 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
+
+ # Activities and Objects, except (Create)ChatMessage
+ defmacro message_fields do
+ quote bind_quoted: binding() do
+ field(:type, :string)
+ field(:id, ObjectValidators.ObjectID, primary_key: true)
+
+ field(:to, ObjectValidators.Recipients, default: [])
+ field(:cc, ObjectValidators.Recipients, default: [])
+ field(:bto, ObjectValidators.Recipients, default: [])
+ field(:bcc, ObjectValidators.Recipients, default: [])
+ end
+ end
+
+ defmacro activity_fields do
+ quote bind_quoted: binding() do
+ field(:object, ObjectValidators.ObjectID)
+ field(:actor, ObjectValidators.ObjectID)
+ end
+ end
+
+ # All objects except Answer and CHatMessage
+ defmacro object_fields do
+ quote bind_quoted: binding() do
+ field(:content, :string)
+
+ field(:published, ObjectValidators.DateTime)
+ field(:updated, ObjectValidators.DateTime)
+ field(:emoji, ObjectValidators.Emoji, default: %{})
+ embeds_many(:attachment, AttachmentValidator)
+ end
+ end
+
+ # Basically objects that aren't ChatMessage and Answer
+ defmacro status_object_fields do
+ quote bind_quoted: binding() do
+ # TODO: Remove actor on objects
+ field(:actor, ObjectValidators.ObjectID)
+ field(:attributedTo, ObjectValidators.ObjectID)
+
+ embeds_many(:tag, TagValidator)
+
+ field(:name, :string)
+ field(:summary, :string)
+
+ field(:context, :string)
+
+ field(:sensitive, :boolean, default: false)
+ field(:replies_count, :integer, default: 0)
+ field(:like_count, :integer, default: 0)
+ field(:announcement_count, :integer, default: 0)
+ field(:inReplyTo, ObjectValidators.ObjectID)
+ field(:url, ObjectValidators.BareUri)
+
+ field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
+ field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
+ end
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
index 5f2c633bc..add46d561 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex
@@ -1,31 +1,79 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Object
alias Pleroma.Object.Containment
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
- # based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults
- def fix_defaults(data) do
- %{data: %{"id" => context}, id: context_id} =
- Utils.create_context(data["context"] || data["conversation"])
+ def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do
+ {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback)
+
+ data =
+ Enum.reject(data, fn x ->
+ String.ends_with?(x, "/followers") and x != follower_collection
+ end)
+
+ Map.put(message, field, data)
+ end
+
+ def fix_object_defaults(data) do
+ context =
+ Utils.maybe_create_context(
+ data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
+ )
+
+ %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"])
data
|> Map.put("context", context)
- |> Map.put("context_id", context_id)
+ |> cast_and_filter_recipients("to", follower_collection)
+ |> cast_and_filter_recipients("cc", follower_collection)
+ |> cast_and_filter_recipients("bto", follower_collection)
+ |> cast_and_filter_recipients("bcc", follower_collection)
+ |> Transmogrifier.fix_implicit_addressing(follower_collection)
end
- def fix_attribution(data) do
- data
- |> Map.put_new("actor", data["attributedTo"])
+ def fix_activity_addressing(activity) do
+ %User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"])
+
+ activity
+ |> cast_and_filter_recipients("to", follower_collection)
+ |> cast_and_filter_recipients("cc", follower_collection)
+ |> cast_and_filter_recipients("bto", follower_collection)
+ |> cast_and_filter_recipients("bcc", follower_collection)
+ |> Transmogrifier.fix_implicit_addressing(follower_collection)
end
def fix_actor(data) do
- actor = Containment.get_actor(data)
+ actor =
+ data
+ |> Map.put_new("actor", data["attributedTo"])
+ |> Containment.get_actor()
data
|> Map.put("actor", actor)
|> Map.put("attributedTo", actor)
end
+
+ def fix_activity_context(data, %Object{data: %{"context" => object_context}}) do
+ data
+ |> Map.put("context", object_context)
+ end
+
+ def fix_object_action_recipients(%{"actor" => actor} = data, %Object{data: %{"actor" => actor}}) do
+ to = ((data["to"] || []) -- [actor]) |> Enum.uniq()
+
+ Map.put(data, "to", to)
+ end
+
+ def fix_object_action_recipients(data, %Object{data: %{"actor" => actor}}) do
+ to = ((data["to"] || []) ++ [actor]) |> Enum.uniq()
+
+ Map.put(data, "to", to)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
index 940430588..1c5b1a059 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
@@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
fields
|> Enum.map(fn field -> get_field(cng, field) end)
|> Enum.any?(fn
+ nil -> false
[] -> false
_ -> true
end)
@@ -135,11 +136,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
# This figures out if a user is able to create, delete or modify something
# based on the domain and superuser status
- @spec validate_modification_rights(Ecto.Changeset.t()) :: Ecto.Changeset.t()
- def validate_modification_rights(cng) do
+ @spec validate_modification_rights(Ecto.Changeset.t(), atom()) :: Ecto.Changeset.t()
+ def validate_modification_rights(cng, privilege) do
actor = User.get_cached_by_ap_id(get_field(cng, :actor))
- if User.superuser?(actor) || same_domain?(cng) do
+ if User.privileged?(actor, privilege) || same_domain?(cng) do
cng
else
cng
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex
index 7a31a99bf..b299647a1 100644
--- a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# NOTES
@@ -17,11 +17,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator do
@primary_key false
embedded_schema do
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ activity_fields()
+ end
+ end
+
field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:actor, ObjectValidators.ObjectID)
field(:type, :string)
field(:to, ObjectValidators.Recipients, default: [])
- field(:object, ObjectValidators.ObjectID)
end
def cast_and_apply(data) do
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex
index e06e442f4..2395abfd4 100644
--- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# Code based on CreateChatMessageValidator
@@ -10,20 +10,24 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+ alias Pleroma.Web.ActivityPub.Transmogrifier
import Ecto.Changeset
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:actor, ObjectValidators.ObjectID)
- field(:type, :string)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:object, ObjectValidators.ObjectID)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
+
field(:expires_at, ObjectValidators.DateTime)
# Should be moved to object, done for CommonAPI.Utils.make_context
@@ -54,39 +58,37 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
|> cast(data, __schema__(:fields))
end
- defp fix_context(data, meta) do
- if object = meta[:object_data] do
- Map.put_new(data, "context", object["context"])
- else
- data
- end
- end
+ # CommonFixes.fix_activity_addressing adapted for Create specific behavior
+ defp fix_addressing(data, object) do
+ %User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"])
- defp fix_addressing(data, meta) do
- if object = meta[:object_data] do
- data
- |> Map.put_new("to", object["to"] || [])
- |> Map.put_new("cc", object["cc"] || [])
- else
- data
- end
+ data
+ |> CommonFixes.cast_and_filter_recipients("to", follower_collection, object["to"])
+ |> CommonFixes.cast_and_filter_recipients("cc", follower_collection, object["cc"])
+ |> CommonFixes.cast_and_filter_recipients("bto", follower_collection, object["bto"])
+ |> CommonFixes.cast_and_filter_recipients("bcc", follower_collection, object["bcc"])
+ |> Transmogrifier.fix_implicit_addressing(follower_collection)
end
- defp fix(data, meta) do
+ def fix(data, meta) do
+ object = meta[:object_data]
+
data
- |> fix_context(meta)
- |> fix_addressing(meta)
|> CommonFixes.fix_actor()
+ |> Map.put("context", object["context"])
+ |> fix_addressing(object)
end
defp validate_data(cng, meta) do
+ object = meta[:object_data]
+
cng
- |> validate_required([:actor, :type, :object])
+ |> validate_required([:actor, :type, :object, :to, :cc])
|> validate_inclusion(:type, ["Create"])
|> CommonValidations.validate_actor_presence()
- |> CommonValidations.validate_any_presence([:to, :cc])
- |> validate_actors_match(meta)
- |> validate_context_match(meta)
+ |> validate_actors_match(object)
+ |> validate_context_match(object)
+ |> validate_addressing_match(object)
|> validate_object_nonexistence()
|> validate_object_containment()
end
@@ -118,8 +120,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
end)
end
- def validate_actors_match(cng, meta) do
- attributed_to = meta[:object_data]["attributedTo"] || meta[:object_data]["actor"]
+ def validate_actors_match(cng, object) do
+ attributed_to = object["attributedTo"] || object["actor"]
cng
|> validate_change(:actor, fn :actor, actor ->
@@ -131,7 +133,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
end)
end
- def validate_context_match(cng, %{object_data: %{"context" => object_context}}) do
+ def validate_context_match(cng, %{"context" => object_context}) do
cng
|> validate_change(:context, fn :context, context ->
if context == object_context do
@@ -142,5 +144,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
end)
end
- def validate_context_match(cng, _), do: cng
+ def validate_addressing_match(cng, object) do
+ [:to, :cc, :bcc, :bto]
+ |> Enum.reduce(cng, fn field, cng ->
+ object_data = object[to_string(field)]
+
+ validate_change(cng, field, fn field, data ->
+ if data == object_data do
+ []
+ else
+ [{field, "field doesn't match with object (#{inspect(object_data)})"}]
+ end
+ end)
+ end)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex
deleted file mode 100644
index a85a0298c..000000000
--- a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex
+++ /dev/null
@@ -1,29 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
- use Ecto.Schema
-
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
- alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
-
- import Ecto.Changeset
-
- @primary_key false
-
- embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:actor, ObjectValidators.ObjectID)
- field(:type, :string)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:bto, ObjectValidators.Recipients, default: [])
- field(:bcc, ObjectValidators.Recipients, default: [])
- embeds_one(:object, NoteValidator)
- end
-
- def cast_data(data) do
- cast(%__MODULE__{}, data, __schema__(:fields))
- end
-end
diff --git a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex
index 7da67bf16..4d8502ada 100644
--- a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.User
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@@ -14,13 +15,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:actor, ObjectValidators.ObjectID)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
+
field(:deleted_activity_id, ObjectValidators.ObjectID)
- field(:object, ObjectValidators.ObjectID)
end
def cast_data(data) do
@@ -57,8 +60,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
cng
|> validate_required([:id, :type, :actor, :to, :cc, :object])
|> validate_inclusion(:type, ["Delete"])
- |> validate_actor_presence()
- |> validate_modification_rights()
+ |> validate_delete_actor(:actor)
+ |> validate_modification_rights(:messages_delete)
|> validate_object_or_user_presence(allowed_types: @deletable_types)
|> add_deleted_activity_id()
end
@@ -72,4 +75,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do
|> cast_data
|> validate_data
end
+
+ defp validate_delete_actor(cng, field_name) do
+ validate_change(cng, field_name, fn field_name, actor ->
+ case User.get_cached_by_ap_id(actor) do
+ %User{} -> []
+ _ -> [{field_name, "can't find user"}]
+ end
+ end)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex
index ec7566515..a0b82b325 100644
--- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex
@@ -1,12 +1,14 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
use Ecto.Schema
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.Emoji
alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
+ alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@@ -14,14 +16,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:object, ObjectValidators.ObjectID)
- field(:actor, ObjectValidators.ObjectID)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ embeds_many(:tag, TagValidator)
+ end
+ end
+
field(:context, :string)
field(:content, :string)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
end
def cast_and_validate(data) do
@@ -31,42 +36,85 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
end
def cast_data(data) do
+ data =
+ data
+ |> fix()
+
%__MODULE__{}
|> changeset(data)
end
def changeset(struct, data) do
struct
- |> cast(data, __schema__(:fields))
- |> fix_after_cast()
+ |> cast(data, __schema__(:fields) -- [:tag])
+ |> cast_embed(:tag)
end
- def fix_after_cast(cng) do
- cng
- |> fix_context()
+ defp fix(data) do
+ data =
+ data
+ |> fix_emoji_qualification()
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
+
+ data = Map.put_new(data, "tag", [])
+
+ case Object.normalize(data["object"]) do
+ %Object{} = object ->
+ data
+ |> CommonFixes.fix_activity_context(object)
+ |> CommonFixes.fix_object_action_recipients(object)
+
+ _ ->
+ data
+ end
end
- def fix_context(cng) do
- object = get_field(cng, :object)
+ defp fix_emoji_qualification(%{"content" => emoji} = data) do
+ new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
+
+ cond do
+ Pleroma.Emoji.is_unicode_emoji?(emoji) ->
+ data
- with nil <- get_field(cng, :context),
- %Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
+ Pleroma.Emoji.is_unicode_emoji?(new_emoji) ->
+ data |> Map.put("content", new_emoji)
+
+ true ->
+ data
+ end
+ end
+
+ defp fix_emoji_qualification(data), do: data
+
+ defp validate_emoji(cng) do
+ content = get_field(cng, :content)
+
+ if Emoji.is_unicode_emoji?(content) || Emoji.is_custom_emoji?(content) do
cng
- |> put_change(:context, context)
else
- _ ->
- cng
+ cng
+ |> add_error(:content, "is not a valid emoji")
end
end
- def validate_emoji(cng) do
+ defp maybe_validate_tag_presence(cng) do
content = get_field(cng, :content)
- if Pleroma.Emoji.is_unicode_emoji?(content) do
+ if Emoji.is_unicode_emoji?(content) do
cng
else
- cng
- |> add_error(:content, "must be a single character emoji")
+ tag = get_field(cng, :tag)
+ emoji_name = Emoji.maybe_strip_name(content)
+
+ case tag do
+ [%{name: ^emoji_name, type: "Emoji", icon: %{url: _}}] ->
+ cng
+
+ _ ->
+ cng
+ |> add_error(:tag, "does not contain an Emoji tag")
+ end
end
end
@@ -77,5 +125,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
|> validate_actor_presence()
|> validate_object_presence()
|> validate_emoji()
+ |> maybe_validate_tag_presence()
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex
index d42458ef5..ab204f69a 100644
--- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex
@@ -1,15 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
use Ecto.Schema
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
- alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
- alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
alias Pleroma.Web.ActivityPub.Transmogrifier
import Ecto.Changeset
@@ -19,38 +16,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
# Extends from NoteValidator
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:bto, ObjectValidators.Recipients, default: [])
- field(:bcc, ObjectValidators.Recipients, default: [])
- embeds_many(:tag, TagValidator)
- field(:type, :string)
-
- field(:name, :string)
- field(:summary, :string)
- field(:content, :string)
-
- field(:context, :string)
- # short identifier for PleromaFE to group statuses by context
- field(:context_id, :integer)
-
- # TODO: Remove actor on objects
- field(:actor, ObjectValidators.ObjectID)
-
- field(:attributedTo, ObjectValidators.ObjectID)
- field(:published, ObjectValidators.DateTime)
- field(:emoji, ObjectValidators.Emoji, default: %{})
- field(:sensitive, :boolean, default: false)
- embeds_many(:attachment, AttachmentValidator)
- field(:replies_count, :integer, default: 0)
- field(:like_count, :integer, default: 0)
- field(:announcement_count, :integer, default: 0)
- field(:inReplyTo, ObjectValidators.ObjectID)
- field(:url, ObjectValidators.Uri)
-
- field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
- field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ object_fields()
+ status_object_fields()
+ end
+ end
end
def cast_and_apply(data) do
@@ -72,8 +45,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
defp fix(data) do
data
- |> CommonFixes.fix_defaults()
- |> CommonFixes.fix_attribution()
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_object_defaults()
|> Transmogrifier.fix_emoji()
end
@@ -89,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
defp validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Event"])
- |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
+ |> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()
diff --git a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex
index 239cee5e7..b3ca5b691 100644
--- a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex
@@ -1,24 +1,24 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator do
use Ecto.Schema
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
-
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:actor, ObjectValidators.ObjectID)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:object, ObjectValidators.ObjectID)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
+
field(:state, :string, default: "pending")
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex
index 509da507b..bdc4d7181 100644
--- a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex
@@ -1,12 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
use Ecto.Schema
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.Utils
import Ecto.Changeset
@@ -15,13 +15,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:object, ObjectValidators.ObjectID)
- field(:actor, ObjectValidators.ObjectID)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
+
field(:context, :string)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
end
def cast_and_validate(data) do
@@ -31,6 +33,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
end
def cast_data(data) do
+ data =
+ data
+ |> fix()
+
%__MODULE__{}
|> changeset(data)
end
@@ -38,41 +44,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
def changeset(struct, data) do
struct
|> cast(data, __schema__(:fields))
- |> fix_after_cast()
- end
-
- def fix_after_cast(cng) do
- cng
- |> fix_recipients()
- |> fix_context()
- end
-
- def fix_context(cng) do
- object = get_field(cng, :object)
-
- with nil <- get_field(cng, :context),
- %Object{data: %{"context" => context}} <- Object.get_cached_by_ap_id(object) do
- cng
- |> put_change(:context, context)
- else
- _ ->
- cng
- end
end
- def fix_recipients(cng) do
- to = get_field(cng, :to)
- cc = get_field(cng, :cc)
- object = get_field(cng, :object)
+ defp fix(data) do
+ data =
+ data
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_activity_addressing()
- with {[], []} <- {to, cc},
- %Object{data: %{"actor" => actor}} <- Object.get_cached_by_ap_id(object),
- {:ok, actor} <- ObjectValidators.ObjectID.cast(actor) do
- cng
- |> put_change(:to, [actor])
+ with %Object{} = object <- Object.normalize(data["object"]) do
+ data
+ |> CommonFixes.fix_activity_context(object)
+ |> CommonFixes.fix_object_action_recipients(object)
else
- _ ->
- cng
+ _ -> data
end
end
@@ -85,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
|> validate_existing_like()
end
- def validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
+ defp validate_existing_like(%{changes: %{actor: actor, object: object}} = cng) do
if Utils.get_existing_like(actor, %{data: %{"id" => object}}) do
cng
|> add_error(:actor, "already liked this object")
@@ -95,5 +80,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do
end
end
- def validate_existing_like(cng), do: cng
+ defp validate_existing_like(cng), do: cng
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex
index ddcd1be7c..541945fa4 100644
--- a/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/question_options_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator do
diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex
index 7012e2e1d..ce3305142 100644
--- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex
@@ -1,16 +1,14 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators
- alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator
- alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator
alias Pleroma.Web.ActivityPub.Transmogrifier
import Ecto.Changeset
@@ -20,35 +18,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
# Extends from NoteValidator
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
- field(:bto, ObjectValidators.Recipients, default: [])
- field(:bcc, ObjectValidators.Recipients, default: [])
- embeds_many(:tag, TagValidator)
- field(:type, :string)
- field(:content, :string)
- field(:context, :string)
-
- # TODO: Remove actor on objects
- field(:actor, ObjectValidators.ObjectID)
-
- field(:attributedTo, ObjectValidators.ObjectID)
- field(:summary, :string)
- field(:published, ObjectValidators.DateTime)
- field(:emoji, ObjectValidators.Emoji, default: %{})
- field(:sensitive, :boolean, default: false)
- embeds_many(:attachment, AttachmentValidator)
- field(:replies_count, :integer, default: 0)
- field(:like_count, :integer, default: 0)
- field(:announcement_count, :integer, default: 0)
- field(:inReplyTo, ObjectValidators.ObjectID)
- field(:url, ObjectValidators.Uri)
- # short identifier for PleromaFE to group statuses by context
- field(:context_id, :integer)
-
- field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
- field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ object_fields()
+ status_object_fields()
+ end
+ end
field(:closed, ObjectValidators.DateTime)
field(:voters, {:array, ObjectValidators.ObjectID}, default: [])
@@ -83,8 +60,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
defp fix(data) do
data
- |> CommonFixes.fix_defaults()
- |> CommonFixes.fix_attribution()
+ |> CommonFixes.fix_actor()
+ |> CommonFixes.fix_object_defaults()
|> Transmogrifier.fix_emoji()
|> fix_closed()
end
@@ -103,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
defp validate_data(data_cng) do
data_cng
|> validate_inclusion(:type, ["Question"])
- |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
+ |> validate_required([:id, :actor, :attributedTo, :type, :context])
|> CommonValidations.validate_any_presence([:cc, :to])
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|> CommonValidations.validate_actor_presence()
diff --git a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex
index 751021585..cfd510c19 100644
--- a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do
@@ -68,6 +68,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do
|> validate_required([:type, :name, :icon])
end
+ def changeset(struct, %{"type" => _} = data) do
+ struct
+ |> cast(data, [])
+ |> Map.put(:action, :ignore)
+ end
+
def icon_changeset(struct, data) do
struct
|> cast(data, [:type, :url])
diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex
index e8af60ffa..f03051491 100644
--- a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex
@@ -1,12 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
use Ecto.Schema
alias Pleroma.Activity
- alias Pleroma.EctoType.ActivityPub.ObjectValidators
+ alias Pleroma.User
import Ecto.Changeset
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
@@ -14,12 +14,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
- field(:object, ObjectValidators.ObjectID)
- field(:actor, ObjectValidators.ObjectID)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ activity_fields()
+ end
+ end
end
def cast_and_validate(data) do
@@ -42,7 +43,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
data_cng
|> validate_inclusion(:type, ["Undo"])
|> validate_required([:id, :type, :object, :actor, :to, :cc])
- |> validate_actor_presence()
+ |> validate_undo_actor(:actor)
|> validate_object_presence()
|> validate_undo_rights()
end
@@ -59,4 +60,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
_ -> cng
end
end
+
+ defp validate_undo_actor(cng, field_name) do
+ validate_change(cng, field_name, fn field_name, actor ->
+ case User.get_cached_by_ap_id(actor) do
+ %User{} -> []
+ _ -> [{field_name, "can't find user"}]
+ end
+ end)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
index 6bb1dc7fa..1e940a400 100644
--- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
@@ -13,11 +13,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
@primary_key false
embedded_schema do
- field(:id, ObjectValidators.ObjectID, primary_key: true)
- field(:type, :string)
+ quote do
+ unquote do
+ import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields
+ message_fields()
+ end
+ end
+
field(:actor, ObjectValidators.ObjectID)
- field(:to, ObjectValidators.Recipients, default: [])
- field(:cc, ObjectValidators.Recipients, default: [])
# In this case, we save the full object in this activity instead of just a
# reference, so we can always see what was actually changed by this.
field(:object, :map)
@@ -48,7 +51,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
with actor = get_field(cng, :actor),
object = get_field(cng, :object),
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
- true <- actor == object_id do
+ actor_uri <- URI.parse(actor),
+ object_uri <- URI.parse(object_id),
+ true <- actor_uri.host == object_uri.host do
cng
else
_e ->
diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex
index a0f2e0312..ca8653ab1 100644
--- a/lib/pleroma/web/activity_pub/pipeline.ex
+++ b/lib/pleroma/web/activity_pub/pipeline.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Pipeline do
@@ -15,19 +15,19 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
- @side_effects Config.get([:pipeline, :side_effects], SideEffects)
- @federator Config.get([:pipeline, :federator], Federator)
- @object_validator Config.get([:pipeline, :object_validator], ObjectValidator)
- @mrf Config.get([:pipeline, :mrf], MRF)
- @activity_pub Config.get([:pipeline, :activity_pub], ActivityPub)
- @config Config.get([:pipeline, :config], Config)
+ defp side_effects, do: Config.get([:pipeline, :side_effects], SideEffects)
+ defp federator, do: Config.get([:pipeline, :federator], Federator)
+ defp object_validator, do: Config.get([:pipeline, :object_validator], ObjectValidator)
+ defp mrf, do: Config.get([:pipeline, :mrf], MRF)
+ defp activity_pub, do: Config.get([:pipeline, :activity_pub], ActivityPub)
+ defp config, do: Config.get([:pipeline, :config], Config)
@spec common_pipeline(map(), keyword()) ::
{:ok, Activity.t() | Object.t(), keyword()} | {:error, any()}
def common_pipeline(object, meta) do
case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do
{:ok, {:ok, activity, meta}} ->
- @side_effects.handle_after_transaction(meta)
+ side_effects().handle_after_transaction(meta)
{:ok, activity, meta}
{:ok, value} ->
@@ -44,10 +44,10 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
def do_common_pipeline(%{__struct__: _}, _meta), do: {:error, :is_struct}
def do_common_pipeline(message, meta) do
- with {_, {:ok, message, meta}} <- {:validate, @object_validator.validate(message, meta)},
- {_, {:ok, message, meta}} <- {:mrf, @mrf.pipeline_filter(message, meta)},
- {_, {:ok, message, meta}} <- {:persist, @activity_pub.persist(message, meta)},
- {_, {:ok, message, meta}} <- {:side_effects, @side_effects.handle(message, meta)},
+ with {_, {:ok, message, meta}} <- {:validate, object_validator().validate(message, meta)},
+ {_, {:ok, message, meta}} <- {:mrf, mrf().pipeline_filter(message, meta)},
+ {_, {:ok, message, meta}} <- {:persist, activity_pub().persist(message, meta)},
+ {_, {:ok, message, meta}} <- {:side_effects, side_effects().handle(message, meta)},
{_, {:ok, _}} <- {:federation, maybe_federate(message, meta)} do
{:ok, message, meta}
else
@@ -60,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
defp maybe_federate(%Activity{} = activity, meta) do
with {:ok, local} <- Keyword.fetch(meta, :local) do
- do_not_federate = meta[:do_not_federate] || !@config.get([:instance, :federating])
+ do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating])
if !do_not_federate and local and not Visibility.is_local_public?(activity) do
activity =
@@ -70,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
activity
end
- @federator.publish(activity)
+ federator().publish(activity)
{:ok, :federated}
else
{:ok, :not_federated}
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index b12b2fc24..af6aa0781 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Publisher do
@@ -63,18 +63,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
date: date
})
- with {:ok, %{status: code}} when code in 200..299 <-
- result =
- HTTP.post(
- inbox,
- json,
- [
- {"Content-Type", "application/activity+json"},
- {"Date", date},
- {"signature", signature},
- {"digest", digest}
- ]
- ) do
+ with {:ok, %{status: code}} = result when code in 200..299 <-
+ HTTP.post(
+ inbox,
+ json,
+ [
+ {"Content-Type", "application/activity+json"},
+ {"Date", date},
+ {"signature", signature},
+ {"digest", digest}
+ ]
+ ) do
if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
Instances.set_reachable(inbox)
end
@@ -112,6 +111,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
quarantined_instances =
Config.get([:instance, :quarantined_instances], [])
+ |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
!Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host)
@@ -199,7 +199,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
inboxes =
recipients
- |> Enum.filter(&User.ap_enabled?/1)
|> Enum.map(fn actor -> actor.inbox end)
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|> Instances.filter_reachable()
@@ -241,7 +240,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
json = Jason.encode!(data)
recipients(actor, activity)
- |> Enum.filter(fn user -> User.ap_enabled?(user) end)
|> Enum.map(fn %User{} = user ->
determine_inbox(activity, user)
end)
@@ -272,7 +270,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
},
%{
"rel" => "http://ostatus.org/schema/1.0/subscribe",
- "template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}"
+ "template" => "#{Pleroma.Web.Endpoint.url()}/ostatus_subscribe?acct={uri}"
}
]
end
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index 6d60a074f..2010351d1 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Relay do
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index 5fe143c2b..098c177c7 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.SideEffects do
@@ -10,7 +10,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
collection, and so on.
"""
alias Pleroma.Activity
- alias Pleroma.Activity.Ir.Topics
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
alias Pleroma.FollowingRelationship
@@ -24,15 +23,18 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer
+ alias Pleroma.Workers.PollWorker
+ require Pleroma.Constants
require Logger
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
- @ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
+ defp ap_streamer, do: Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
+
@impl true
def handle(object, meta \\ [])
@@ -152,23 +154,26 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# Tasks this handles:
# - Update the user
+ # - Update a non-user object (Note, Question, etc.)
#
# For a local user, we also get a changeset with the full information, so we
# can update non-federating, non-activitypub settings as well.
@impl true
def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do
- if changeset = Keyword.get(meta, :user_update_changeset) do
- changeset
- |> User.update_and_set_cache()
+ updated_object_id = updated_object["id"]
+
+ with {_, true} <- {:has_id, is_binary(updated_object_id)},
+ %{"type" => type} <- updated_object,
+ {_, is_user} <- {:is_user, type in Pleroma.Constants.actor_types()} do
+ if is_user do
+ handle_update_user(object, meta)
+ else
+ handle_update_object(object, meta)
+ end
else
- {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object)
-
- User.get_by_ap_id(updated_object["id"])
- |> User.remote_user_changeset(new_user_data)
- |> User.update_and_set_cache()
+ _ ->
+ {:ok, object, meta}
end
-
- {:ok, object, meta}
end
# Tasks this handles:
@@ -194,15 +199,29 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Set up notifications
@impl true
def handle(%{data: %{"type" => "Create"}} = activity, meta) do
- with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta),
+ with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta),
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
+ {:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object)
- if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
+ if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do
Object.increase_replies_count(in_reply_to)
end
+ reply_depth = (meta[:depth] || 0) + 1
+
+ # FIXME: Force inReplyTo to replies
+ if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and
+ object.data["replies"] != nil do
+ for reply_id <- object.data["replies"] do
+ Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
+ "id" => reply_id,
+ "depth" => reply_depth
+ })
+ end
+ end
+
ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
end)
@@ -211,6 +230,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
meta
|> add_notifications(notifications)
+ ap_streamer().stream_out(activity)
+
{:ok, activity, meta}
else
e -> Repo.rollback(e)
@@ -231,9 +252,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
if !User.is_internal_user?(user) do
Notification.create_notifications(object)
- object
- |> Topics.get_activity_topics()
- |> Streamer.stream(object)
+ ap_streamer().stream_out(object)
end
{:ok, object, meta}
@@ -263,7 +282,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# Tasks this handles:
# - Delete and unpins the create activity
# - Replace object with Tombstone
- # - Set up notification
# - Reduce the user note count
# - Reduce the reply count
# - Stream out the activity
@@ -289,8 +307,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
MessageReference.delete_for_object(deleted_object)
- @ap_streamer.stream_out(object)
- @ap_streamer.stream_out_participations(deleted_object, user)
+ ap_streamer().stream_out(object)
+ ap_streamer().stream_out_participations(deleted_object, user)
:ok
else
{:actor, _} ->
@@ -305,7 +323,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
if result == :ok do
- Notification.create_notifications(object)
{:ok, object, meta}
else
{:error, result}
@@ -375,7 +392,56 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, object, meta}
end
- def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do
+ defp handle_update_user(
+ %{data: %{"type" => "Update", "object" => updated_object}} = object,
+ meta
+ ) do
+ if changeset = Keyword.get(meta, :user_update_changeset) do
+ changeset
+ |> User.update_and_set_cache()
+ else
+ {:ok, new_user_data} = ActivityPub.user_data_from_user_object(updated_object)
+
+ User.get_by_ap_id(updated_object["id"])
+ |> User.remote_user_changeset(new_user_data)
+ |> User.update_and_set_cache()
+ end
+
+ {:ok, object, meta}
+ end
+
+ defp handle_update_object(
+ %{data: %{"type" => "Update", "object" => updated_object}} = object,
+ meta
+ ) do
+ orig_object_ap_id = updated_object["id"]
+ orig_object = Object.get_by_ap_id(orig_object_ap_id)
+ orig_object_data = orig_object.data
+
+ updated_object =
+ if meta[:local] do
+ # If this is a local Update, we don't process it by transmogrifier,
+ # so we use the embedded object as-is.
+ updated_object
+ else
+ meta[:object_data]
+ end
+
+ if orig_object_data["type"] in Pleroma.Constants.updatable_object_types() do
+ {:ok, _, updated} =
+ Object.Updater.do_update_and_invalidate_cache(orig_object, updated_object)
+
+ if updated do
+ object
+ |> Activity.normalize()
+ |> ActivityPub.notify_and_stream()
+ end
+ end
+
+ {:ok, object, meta}
+ end
+
+ def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
actor = User.get_cached_by_ap_id(object.data["actor"])
recipient = User.get_cached_by_ap_id(hd(object.data["to"]))
@@ -410,7 +476,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
end
- def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
+ def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do
+ with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
+ PollWorker.schedule_poll_end(activity)
+ {:ok, object, meta}
+ end
+ end
+
+ def handle_object_creation(%{"type" => "Answer"} = object_map, _activity, meta) do
with {:ok, object, meta} <- Pipeline.common_pipeline(object_map, meta) do
Object.increase_vote_count(
object.data["inReplyTo"],
@@ -422,15 +495,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
end
- def handle_object_creation(%{"type" => objtype} = object, meta)
- when objtype in ~w[Audio Video Question Event Article] do
+ def handle_object_creation(%{"type" => objtype} = object, _activity, meta)
+ when objtype in ~w[Audio Video Image Event Article Note Page] do
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
{:ok, object, meta}
end
end
# Nothing to do
- def handle_object_creation(object, meta) do
+ def handle_object_creation(object, _activity, meta) do
{:ok, object, meta}
end
diff --git a/lib/pleroma/web/activity_pub/side_effects/handling.ex b/lib/pleroma/web/activity_pub/side_effects/handling.ex
index a82305155..eb012f576 100644
--- a/lib/pleroma/web/activity_pub/side_effects/handling.ex
+++ b/lib/pleroma/web/activity_pub/side_effects/handling.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index d27d0bed4..0e6c429f9 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Transmogrifier do
@@ -20,7 +20,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
- alias Pleroma.Workers.TransmogrifierWorker
import Ecto.Query
@@ -43,7 +42,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_content_map()
|> fix_addressing()
|> fix_summary()
- |> fix_type(options)
end
def fix_summary(%{"summary" => nil} = object) do
@@ -72,17 +70,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
- def fix_explicit_addressing(
- %{"to" => to, "cc" => cc} = object,
- explicit_mentions,
- follower_collection
- ) do
- explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end)
+ # if directMessage flag is set to true, leave the addressing alone
+ def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection),
+ do: object
+ def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, follower_collection) do
+ explicit_mentions =
+ Utils.determine_explicit_mentions(object) ++
+ [Pleroma.Constants.as_public(), follower_collection]
+
+ explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end)
explicit_cc = Enum.filter(to, fn x -> x not in explicit_mentions end)
final_cc =
(cc ++ explicit_cc)
+ |> Enum.filter(& &1)
|> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end)
|> Enum.uniq()
@@ -91,29 +93,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("cc", final_cc)
end
- def fix_explicit_addressing(object, _explicit_mentions, _followers_collection), do: object
-
- # if directMessage flag is set to true, leave the addressing alone
- def fix_explicit_addressing(%{"directMessage" => true} = object), do: object
-
- def fix_explicit_addressing(object) do
- explicit_mentions = Utils.determine_explicit_mentions(object)
-
- %User{follower_address: follower_collection} =
- object
- |> Containment.get_actor()
- |> User.get_cached_by_ap_id()
-
- explicit_mentions =
- explicit_mentions ++
- [
- Pleroma.Constants.as_public(),
- follower_collection
- ]
-
- fix_explicit_addressing(object, explicit_mentions, follower_collection)
- end
-
# if as:Public is addressed, then make sure the followers collection is also addressed
# so that the activities will be delivered to local users.
def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do
@@ -137,19 +116,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
- def fix_implicit_addressing(object, _), do: object
-
def fix_addressing(object) do
- {:ok, %User{} = user} = User.get_or_fetch_by_ap_id(object["actor"])
- followers_collection = User.ap_followers(user)
+ {:ok, %User{follower_address: follower_collection}} =
+ object
+ |> Containment.get_actor()
+ |> User.get_or_fetch_by_ap_id()
object
|> fix_addressing_list("to")
|> fix_addressing_list("cc")
|> fix_addressing_list("bto")
|> fix_addressing_list("bcc")
- |> fix_explicit_addressing()
- |> fix_implicit_addressing(followers_collection)
+ |> fix_explicit_addressing(follower_collection)
+ |> fix_implicit_addressing(follower_collection)
end
def fix_actor(%{"attributedTo" => actor} = object) do
@@ -223,10 +202,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
media_type =
cond do
- is_map(url) && MIME.valid?(url["mediaType"]) -> url["mediaType"]
- MIME.valid?(data["mediaType"]) -> data["mediaType"]
- MIME.valid?(data["mimeType"]) -> data["mimeType"]
- true -> nil
+ is_map(url) && url =~ Pleroma.Constants.mime_regex() ->
+ url["mediaType"]
+
+ is_bitstring(data["mediaType"]) && data["mediaType"] =~ Pleroma.Constants.mime_regex() ->
+ data["mediaType"]
+
+ is_bitstring(data["mimeType"]) && data["mimeType"] =~ Pleroma.Constants.mime_regex() ->
+ data["mimeType"]
+
+ true ->
+ nil
end
href =
@@ -244,6 +230,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"type" => Map.get(url || %{}, "type", "Link")
}
|> Maps.put_if_present("mediaType", media_type)
+ |> Maps.put_if_present("width", (url || %{})["width"] || data["width"])
+ |> Maps.put_if_present("height", (url || %{})["height"] || data["height"])
%{
"url" => [attachment_url],
@@ -340,19 +328,18 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_content_map(object), do: object
- def fix_type(object, options \\ [])
+ defp fix_type(%{"type" => "Note", "inReplyTo" => reply_id, "name" => _} = object, options)
+ when is_binary(reply_id) do
+ options = Keyword.put(options, :fetch, true)
- def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
- when is_binary(reply_id) do
- with true <- Federator.allowed_thread_distance?(options[:depth]),
- {:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
+ with %Object{data: %{"type" => "Question"}} <- Object.normalize(reply_id, options) do
Map.put(object, "type", "Answer")
else
_ -> object
end
end
- def fix_type(object, _), do: object
+ defp fix_type(object, _options), do: object
# Reduce the object list to find the reported user.
defp get_reported(objects) do
@@ -365,29 +352,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end)
end
- # Compatibility wrapper for Mastodon votes
- defp handle_create(%{"object" => %{"type" => "Answer"}} = data, _user) do
- handle_incoming(data)
- end
-
- defp handle_create(%{"object" => object} = data, user) do
- %{
- to: data["to"],
- object: object,
- actor: user,
- context: object["context"],
- local: false,
- published: data["published"],
- additional:
- Map.take(data, [
- "cc",
- "directMessage",
- "id"
- ])
- }
- |> ActivityPub.create()
- end
-
def handle_incoming(data, options \\ [])
# Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them
@@ -419,44 +383,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8,
do: :error
- # TODO: validate those with a Ecto scheme
- # - tags
- # - emoji
- def handle_incoming(
- %{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
- options
- )
- when objtype in ~w{Note Page} do
- actor = Containment.get_actor(data)
-
- with nil <- Activity.get_create_by_object_ap_id(object["id"]),
- {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor) do
- data =
- data
- |> Map.put("object", fix_object(object, options))
- |> Map.put("actor", actor)
- |> fix_addressing()
-
- with {:ok, created_activity} <- handle_create(data, user) do
- reply_depth = (options[:depth] || 0) + 1
-
- if Federator.allowed_thread_distance?(reply_depth) do
- for reply_id <- replies(object) do
- Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
- "id" => reply_id,
- "depth" => reply_depth
- })
- end
- end
-
- {:ok, created_activity}
- end
- else
- %Activity{} = activity -> {:ok, activity}
- _e -> :error
- end
- end
-
def handle_incoming(
%{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = data,
options
@@ -518,14 +444,23 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
- _options
+ options
)
- when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do
- data = Map.put(data, "object", strip_internal_fields(data["object"]))
+ when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page Image} do
+ fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
+
+ object =
+ data["object"]
+ |> strip_internal_fields()
+ |> fix_type(fetch_options)
+ |> fix_in_reply_to(fetch_options)
+
+ data = Map.put(data, "object", object)
+ options = Keyword.put(options, :local, false)
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
nil <- Activity.get_create_by_object_ap_id(obj_id),
- {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
+ {:ok, activity, _} <- Pipeline.common_pipeline(data, options) do
{:ok, activity}
else
%Activity{} = activity -> {:ok, activity}
@@ -751,6 +686,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> strip_internal_fields
|> strip_internal_tags
|> set_type
+ |> maybe_process_history
+ end
+
+ defp maybe_process_history(%{"formerRepresentations" => %{"orderedItems" => history}} = object) do
+ processed_history =
+ Enum.map(
+ history,
+ fn
+ item when is_map(item) -> prepare_object(item)
+ item -> item
+ end
+ )
+
+ put_in(object, ["formerRepresentations", "orderedItems"], processed_history)
+ end
+
+ defp maybe_process_history(object) do
+ object
end
# @doc
@@ -775,6 +728,21 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
end
+ def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
+ when objtype in Pleroma.Constants.updatable_object_types() do
+ object =
+ object
+ |> prepare_object
+
+ data =
+ data
+ |> Map.put("object", object)
+ |> Map.merge(Utils.make_json_ld_header())
+ |> Map.delete("bcc")
+
+ {:ok, data}
+ end
+
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
object =
object_id
@@ -949,7 +917,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
object
|> Map.get("attachment", [])
|> Enum.map(fn data ->
- [%{"mediaType" => media_type, "href" => href} | _] = data["url"]
+ [%{"mediaType" => media_type, "href" => href} = url | _] = data["url"]
%{
"url" => href,
@@ -957,6 +925,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
"name" => data["name"],
"type" => "Document"
}
+ |> Maps.put_if_present("width", url["width"])
+ |> Maps.put_if_present("height", url["height"])
+ |> Maps.put_if_present("blurhash", data["blurhash"])
end)
Map.put(object, "attachment", attachments)
@@ -974,47 +945,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
defp strip_internal_tags(object), do: object
- def perform(:user_upgrade, user) do
- # we pass a fake user so that the followers collection is stripped away
- old_follower_address = User.ap_followers(%User{nickname: user.nickname})
-
- from(
- a in Activity,
- where: ^old_follower_address in a.recipients,
- update: [
- set: [
- recipients:
- fragment(
- "array_replace(?,?,?)",
- a.recipients,
- ^old_follower_address,
- ^user.follower_address
- )
- ]
- ]
- )
- |> Repo.update_all([])
- end
-
- def upgrade_user_from_ap_id(ap_id) do
- with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
- {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
- {:ok, user} <- update_user(user, data) do
- {:ok, _pid} = Task.start(fn -> ActivityPub.pinned_fetch_task(user) end)
- TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
- {:ok, user}
- else
- %User{} = user -> {:ok, user}
- e -> e
- end
- end
-
- defp update_user(user, data) do
- user
- |> User.remote_user_changeset(data)
- |> User.update_and_set_cache()
- end
-
def maybe_fix_user_url(%{"url" => url} = data) when is_map(url) do
Map.put(data, "url", url["href"])
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index a4dc469dc..437220077 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Utils do
@@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
- alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.AdminAPI.AccountView
@@ -32,12 +31,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"Page",
"Question",
"Answer",
- "Audio"
+ "Audio",
+ "Image"
]
@strip_status_report_states ~w(closed resolved)
@supported_report_states ~w(open closed resolved)
@valid_visibilities ~w(public unlisted private direct)
+ def as_local_public, do: Endpoint.url() <> "/#Public"
+
# Some implementations send the actor URI as the actor field, others send the entire actor object,
# so figure out what the actor's URI is based on what we have.
def get_ap_id(%{"id" => id} = _), do: id
@@ -96,8 +98,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do
!label_in_collection?(ap_id, params["cc"])
if need_splice? do
- cc_list = extract_list(params["cc"])
- Map.put(params, "cc", [ap_id | cc_list])
+ cc = [ap_id | extract_list(params["cc"])]
+
+ params
+ |> Map.put("cc", cc)
+ |> Maps.safe_put_in(["object", "cc"], cc)
else
params
end
@@ -107,7 +112,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
%{
"@context" => [
"https://www.w3.org/ns/activitystreams",
- "#{Web.base_url()}/schemas/litepub-0.1.jsonld",
+ "#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
%{
"@language" => "und"
}
@@ -132,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
def generate_id(type) do
- "#{Web.base_url()}/#{type}/#{UUID.generate()}"
+ "#{Endpoint.url()}/#{type}/#{UUID.generate()}"
end
def get_notified_from_object(%{"type" => type} = object) when type in @supported_object_types do
@@ -150,22 +155,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Notification.get_notified_from_activity(%Activity{data: object}, false)
end
- def create_context(context) do
- context = context || generate_id("contexts")
-
- # Ecto has problems accessing the constraint inside the jsonb,
- # so we explicitly check for the existed object before insert
- object = Object.get_cached_by_ap_id(context)
-
- with true <- is_nil(object),
- changeset <- Object.context_mapping(context),
- {:ok, inserted_object} <- Repo.insert(changeset) do
- inserted_object
- else
- _ ->
- object
- end
- end
+ def maybe_create_context(context), do: context || generate_id("contexts")
@doc """
Enqueues an activity for federation if it's local
@@ -197,18 +187,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Map.put_new("id", "pleroma:fakeid")
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", "pleroma:fakecontext")
- |> Map.put_new("context_id", -1)
|> lazy_put_object_defaults(true)
end
def lazy_put_activity_defaults(map, _fake?) do
- %{data: %{"id" => context}, id: context_id} = create_context(map["context"])
+ context = maybe_create_context(map["context"])
map
|> Map.put_new_lazy("id", &generate_activity_id/0)
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", context)
- |> Map.put_new("context_id", context_id)
|> lazy_put_object_defaults(false)
end
@@ -222,7 +210,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Map.put_new("id", "pleroma:fake_object_id")
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", activity["context"])
- |> Map.put_new("context_id", activity["context_id"])
|> Map.put_new("fake", true)
%{activity | "object" => object}
@@ -235,7 +222,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Map.put_new_lazy("id", &generate_object_id/0)
|> Map.put_new_lazy("published", &make_date/0)
|> Map.put_new("context", activity["context"])
- |> Map.put_new("context_id", activity["context_id"])
%{activity | "object" => object}
end
@@ -340,21 +326,29 @@ defmodule Pleroma.Web.ActivityPub.Utils do
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def add_emoji_reaction_to_object(
- %Activity{data: %{"content" => emoji, "actor" => actor}},
+ %Activity{data: %{"content" => emoji, "actor" => actor}} = activity,
object
) do
reactions = get_cached_emoji_reactions(object)
+ emoji = Pleroma.Emoji.maybe_strip_name(emoji)
+ url = maybe_emoji_url(emoji, activity)
new_reactions =
- case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do
+ case Enum.find_index(reactions, fn [candidate, _, candidate_url] ->
+ if is_nil(candidate_url) do
+ emoji == candidate
+ else
+ url == candidate_url
+ end
+ end) do
nil ->
- reactions ++ [[emoji, [actor]]]
+ reactions ++ [[emoji, [actor], url]]
index ->
List.update_at(
reactions,
index,
- fn [emoji, users] -> [emoji, Enum.uniq([actor | users])] end
+ fn [emoji, users, url] -> [emoji, Enum.uniq([actor | users]), url] end
)
end
@@ -363,18 +357,40 @@ defmodule Pleroma.Web.ActivityPub.Utils do
update_element_in_object("reaction", new_reactions, object, count)
end
+ defp maybe_emoji_url(
+ name,
+ %Activity{
+ data: %{
+ "tag" => [
+ %{"type" => "Emoji", "name" => name, "icon" => %{"url" => url}}
+ ]
+ }
+ }
+ ),
+ do: url
+
+ defp maybe_emoji_url(_, _), do: nil
+
def emoji_count(reactions_list) do
- Enum.reduce(reactions_list, 0, fn [_, users], acc -> acc + length(users) end)
+ Enum.reduce(reactions_list, 0, fn [_, users, _], acc -> acc + length(users) end)
end
def remove_emoji_reaction_from_object(
- %Activity{data: %{"content" => emoji, "actor" => actor}},
+ %Activity{data: %{"content" => emoji, "actor" => actor}} = activity,
object
) do
+ emoji = Pleroma.Emoji.maybe_strip_name(emoji)
reactions = get_cached_emoji_reactions(object)
+ url = maybe_emoji_url(emoji, activity)
new_reactions =
- case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do
+ case Enum.find_index(reactions, fn [candidate, _, candidate_url] ->
+ if is_nil(candidate_url) do
+ emoji == candidate
+ else
+ url == candidate_url
+ end
+ end) do
nil ->
reactions
@@ -382,9 +398,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do
List.update_at(
reactions,
index,
- fn [emoji, users] -> [emoji, List.delete(users, actor)] end
+ fn [emoji, users, url] -> [emoji, List.delete(users, actor), url] end
)
- |> Enum.reject(fn [_, users] -> Enum.empty?(users) end)
+ |> Enum.reject(fn [_, users, _] -> Enum.empty?(users) end)
end
count = emoji_count(new_reactions)
@@ -392,11 +408,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
def get_cached_emoji_reactions(object) do
- if is_list(object.data["reactions"]) do
- object.data["reactions"]
- else
- []
- end
+ Object.get_emoji_reactions(object)
end
@spec add_like_to_object(Activity.t(), Object.t()) ::
@@ -442,7 +454,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Activity.Queries.by_type()
|> Activity.Queries.by_actor(actor)
|> Activity.Queries.by_object_id(object)
- |> where(fragment("data->>'state' = 'pending'"))
+ |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'"))
|> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
|> Repo.update_all([])
@@ -504,17 +516,37 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do
%{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id)
+ emoji = Pleroma.Emoji.maybe_quote(emoji)
"EmojiReact"
|> Activity.Queries.by_type()
|> where(actor: ^ap_id)
- |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji))
+ |> custom_emoji_discriminator(emoji)
|> Activity.Queries.by_object_id(object_ap_id)
|> order_by([activity], fragment("? desc nulls last", activity.id))
|> limit(1)
|> Repo.one()
end
+ defp custom_emoji_discriminator(query, emoji) do
+ if String.contains?(emoji, "@") do
+ stripped = Pleroma.Emoji.maybe_strip_name(emoji)
+ [name, domain] = String.split(stripped, "@")
+ domain_pattern = "%/" <> domain <> "/%"
+ emoji_pattern = Pleroma.Emoji.maybe_quote(name)
+
+ query
+ |> where([activity], fragment("?->>'content' = ?
+ AND EXISTS (
+ SELECT FROM jsonb_array_elements(?->'tag') elem
+ WHERE elem->>'id' ILIKE ?
+ )", activity.data, ^emoji_pattern, activity.data, ^domain_pattern))
+ else
+ query
+ |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji))
+ end
+ end
+
#### Announce-related helpers
@doc """
@@ -710,20 +742,24 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Enum.map(statuses || [], &build_flag_object/1)
end
- defp build_flag_object(%Activity{data: %{"id" => id}, object: %{data: data}}) do
- activity_actor = User.get_by_ap_id(data["actor"])
+ defp build_flag_object(%Activity{} = activity) do
+ object = Object.normalize(activity, fetch: false)
- %{
- "type" => "Note",
- "id" => id,
- "content" => data["content"],
- "published" => data["published"],
- "actor" =>
- AccountView.render(
- "show.json",
- %{user: activity_actor, skip_visibility_check: true}
- )
- }
+ # Do not allow people to report Creates. Instead, report the Object that is Created.
+ if activity.data["type"] != "Create" do
+ build_flag_object_with_actor_and_id(
+ object,
+ User.get_by_ap_id(activity.data["actor"]),
+ activity.data["id"]
+ )
+ else
+ build_flag_object(object)
+ end
+ end
+
+ defp build_flag_object(%Object{} = object) do
+ actor = User.get_by_ap_id(object.data["actor"])
+ build_flag_object_with_actor_and_id(object, actor, object.data["id"])
end
defp build_flag_object(act) when is_map(act) or is_binary(act) do
@@ -735,12 +771,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
case Activity.get_by_ap_id_with_object(id) do
- %Activity{} = activity ->
- build_flag_object(activity)
+ %Activity{object: object} = _ ->
+ build_flag_object(object)
nil ->
- if activity = Activity.get_by_object_ap_id_with_object(id) do
- build_flag_object(activity)
+ if %Object{} = object = Object.get_by_ap_id(id) do
+ build_flag_object(object)
else
%{"id" => id, "deleted" => true}
end
@@ -749,6 +785,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do
defp build_flag_object(_), do: []
+ defp build_flag_object_with_actor_and_id(%Object{data: data}, actor, id) do
+ %{
+ "type" => "Note",
+ "id" => id,
+ "content" => data["content"],
+ "published" => data["published"],
+ "actor" =>
+ AccountView.render(
+ "show.json",
+ %{user: actor, skip_visibility_check: true}
+ )
+ }
+ end
+
#### Report-related helpers
def get_reports(params, page, page_size) do
params =
@@ -763,22 +813,21 @@ defmodule Pleroma.Web.ActivityPub.Utils do
ActivityPub.fetch_activities([], params, :offset)
end
- def update_report_state(%Activity{} = activity, state)
- when state in @strip_status_report_states do
- {:ok, stripped_activity} = strip_report_status_data(activity)
+ defp maybe_strip_report_status(data, state) do
+ with true <- Config.get([:instance, :report_strip_status]),
+ true <- state in @strip_status_report_states,
+ {:ok, stripped_activity} = strip_report_status_data(%Activity{data: data}) do
+ data |> Map.put("object", stripped_activity.data["object"])
+ else
+ _ -> data
+ end
+ end
+ def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
new_data =
activity.data
|> Map.put("state", state)
- |> Map.put("object", stripped_activity.data["object"])
-
- activity
- |> Changeset.change(data: new_data)
- |> Repo.update()
- end
-
- def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
- new_data = Map.put(activity.data, "state", state)
+ |> maybe_strip_report_status(state)
activity
|> Changeset.change(data: new_data)
diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex
index 8a3e4d77b..63caa915c 100644
--- a/lib/pleroma/web/activity_pub/views/object_view.ex
+++ b/lib/pleroma/web/activity_pub/views/object_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ObjectView do
@@ -29,11 +29,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
def render("object.json", %{object: %Activity{} = activity}) do
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
- object = Object.normalize(activity, fetch: false)
+ object_id = Object.normalize(activity, id_only: true)
additional =
Transmogrifier.prepare_object(activity.data)
- |> Map.put("object", object.data["id"])
+ |> Map.put("object", object_id)
Map.merge(base, additional)
end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 462f3b4a7..f69fca075 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.UserView do
@@ -34,7 +34,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def render("endpoints.json", _), do: %{}
def render("service.json", %{user: user}) do
- {:ok, user} = User.ensure_keys_present(user)
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
@@ -71,7 +70,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
def render("user.json", %{user: user}) do
- {:ok, user} = User.ensure_keys_present(user)
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
@@ -92,6 +90,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do
%{}
end
+ birthday =
+ if user.show_birthday && user.birthday,
+ do: Date.to_iso8601(user.birthday),
+ else: nil
+
%{
"id" => user.ap_id,
"type" => user.actor_type,
@@ -116,7 +119,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
# Note: key name is indeed "discoverable" (not an error)
"discoverable" => user.is_discoverable,
"capabilities" => capabilities,
- "alsoKnownAs" => user.also_known_as
+ "alsoKnownAs" => user.also_known_as,
+ "vcard:bday" => birthday
}
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
@@ -261,7 +265,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
%{
"id" => featured_address,
"type" => "OrderedCollection",
- "orderedItems" => objects
+ "orderedItems" => objects,
+ "totalItems" => length(objects)
}
|> Map.merge(Utils.make_json_ld_header())
end
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 00234c0b0..7c57f88f9 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Visibility do
@@ -20,14 +20,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
def is_public?(data) do
Utils.label_in_message?(Pleroma.Constants.as_public(), data) or
- Utils.label_in_message?(Pleroma.Constants.as_local_public(), data)
+ Utils.label_in_message?(Utils.as_local_public(), data)
end
def is_local_public?(%Object{data: data}), do: is_local_public?(data)
def is_local_public?(%Activity{data: data}), do: is_local_public?(data)
def is_local_public?(data) do
- Utils.label_in_message?(Pleroma.Constants.as_local_public(), data) and
+ Utils.label_in_message?(Utils.as_local_public(), data) and
not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
end
@@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
def is_list?(_), do: false
@spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean()
+ def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false
def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true
def visible_for_user?(%Object{data: %{"actor" => ap_id}}, %User{ap_id: ap_id}), do: true
def visible_for_user?(nil, _), do: false
@@ -83,7 +84,10 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
when module in [Activity, Object] do
x = [user.ap_id | User.following(user)]
y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || [])
- is_public?(message) || Enum.any?(x, &(&1 in y))
+
+ user_is_local = user.local
+ federatable = not is_local_public?(message)
+ (is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
end
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
@@ -127,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
Pleroma.Constants.as_public() in cc ->
"unlisted"
- Pleroma.Constants.as_local_public() in to ->
+ Utils.as_local_public() in to ->
"local"
# this should use the sql for the object's activity
diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
index 839ac1a8d..1894000ff 100644
--- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
@@ -49,7 +49,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug(
OAuthScopesPlug,
%{scopes: ["admin:read:statuses"]}
- when action in [:list_user_statuses, :list_instance_statuses]
+ when action in [:list_user_statuses]
)
plug(
@@ -81,24 +81,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
action_fallback(AdminAPI.FallbackController)
- def list_instance_statuses(conn, %{"instance" => instance} = params) do
- with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
- {page, page_size} = page_params(params)
-
- result =
- ActivityPub.fetch_statuses(nil, %{
- instance: instance,
- limit: page_size,
- offset: (page - 1) * page_size,
- exclude_reblogs: not with_reblogs,
- total: true
- })
-
- conn
- |> put_view(AdminAPI.StatusView)
- |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
- end
-
def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
godmode = params["godmode"] == "true" || params["godmode"] == true
diff --git a/lib/pleroma/web/admin_api/controllers/announcement_controller.ex b/lib/pleroma/web/admin_api/controllers/announcement_controller.ex
new file mode 100644
index 000000000..6ad5fc12c
--- /dev/null
+++ b/lib/pleroma/web/admin_api/controllers/announcement_controller.ex
@@ -0,0 +1,83 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.AnnouncementController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Announcement
+ alias Pleroma.Web.ControllerHelper
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+ plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:create, :delete, :change])
+ plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action in [:index, :show])
+ action_fallback(Pleroma.Web.AdminAPI.FallbackController)
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.AnnouncementOperation
+
+ defp default_limit, do: 20
+
+ def index(conn, params) do
+ limit = Map.get(params, :limit, default_limit())
+ offset = Map.get(params, :offset, 0)
+
+ announcements = Announcement.list_paginated(%{limit: limit, offset: offset})
+
+ render(conn, "index.json", announcements: announcements)
+ end
+
+ def show(conn, %{id: id} = _params) do
+ announcement = Announcement.get_by_id(id)
+
+ if is_nil(announcement) do
+ {:error, :not_found}
+ else
+ render(conn, "show.json", announcement: announcement)
+ end
+ end
+
+ def create(%{body_params: params} = conn, _params) do
+ with {:ok, announcement} <- Announcement.add(change_params(params)) do
+ render(conn, "show.json", announcement: announcement)
+ else
+ _ ->
+ {:error, 400}
+ end
+ end
+
+ def change_params(orig_params) do
+ data =
+ %{}
+ |> Pleroma.Maps.put_if_present("content", orig_params, &Map.fetch(&1, :content))
+ |> Pleroma.Maps.put_if_present("all_day", orig_params, &Map.fetch(&1, :all_day))
+
+ orig_params
+ |> Map.merge(%{data: data})
+ end
+
+ def change(%{body_params: params} = conn, %{id: id} = _params) do
+ with announcement <- Announcement.get_by_id(id),
+ {:exists, true} <- {:exists, not is_nil(announcement)},
+ {:ok, announcement} <- Announcement.update(announcement, change_params(params)) do
+ render(conn, "show.json", announcement: announcement)
+ else
+ {:exists, false} ->
+ {:error, :not_found}
+
+ _ ->
+ {:error, 400}
+ end
+ end
+
+ def delete(conn, %{id: id} = _params) do
+ case Announcement.delete_by_id(id) do
+ :ok ->
+ conn
+ |> ControllerHelper.json_response(:ok, %{})
+
+ _ ->
+ {:error, :not_found}
+ end
+ end
+end
diff --git a/lib/pleroma/web/admin_api/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex
index ff20c8604..298543fcf 100644
--- a/lib/pleroma/web/admin_api/controllers/chat_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ChatController do
@@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.ChatController do
alias Pleroma.Activity
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
- alias Pleroma.ModerationLog
alias Pleroma.Pagination
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.CommonAPI
@@ -42,12 +41,6 @@ defmodule Pleroma.Web.AdminAPI.ChatController do
^chat_id <- to_string(cm_ref.chat_id),
%Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object_ap_id),
{:ok, _} <- CommonAPI.delete(activity_id, user) do
- ModerationLog.insert_log(%{
- action: "chat_message_delete",
- actor: user,
- subject_id: message_id
- })
-
conn
|> put_view(MessageReferenceView)
|> render("show.json", chat_message_reference: cm_ref)
diff --git a/lib/pleroma/web/admin_api/controllers/config_controller.ex b/lib/pleroma/web/admin_api/controllers/config_controller.ex
index a718d7b8d..a03318c0e 100644
--- a/lib/pleroma/web/admin_api/controllers/config_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/config_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ConfigController do
@@ -22,10 +22,58 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
+ defp translate_descriptions(descriptions, path \\ []) do
+ Enum.map(descriptions, fn desc -> translate_item(desc, path) end)
+ end
+
+ defp translate_string(str, path, type) do
+ Gettext.dpgettext(
+ Pleroma.Web.Gettext,
+ "config_descriptions",
+ Pleroma.Docs.Translator.Compiler.msgctxt_for(path, type),
+ str
+ )
+ end
+
+ defp maybe_put_translated(item, key, path) do
+ if item[key] do
+ Map.put(
+ item,
+ key,
+ translate_string(
+ item[key],
+ path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)],
+ to_string(key)
+ )
+ )
+ else
+ item
+ end
+ end
+
+ defp translate_item(item, path) do
+ item
+ |> maybe_put_translated(:label, path)
+ |> maybe_put_translated(:description, path)
+ |> translate_children(path)
+ end
+
+ defp translate_children(%{children: children} = item, path) when is_list(children) do
+ item
+ |> Map.put(
+ :children,
+ translate_descriptions(children, path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)])
+ )
+ end
+
+ defp translate_children(item, _path) do
+ item
+ end
+
def descriptions(conn, _params) do
descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1)
- json(conn, descriptions)
+ json(conn, translate_descriptions(descriptions))
end
def show(conn, %{only_db: true}) do
diff --git a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex
index 45d8815b5..e72f45c21 100644
--- a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.FallbackController do
diff --git a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex
index 722f51bd2..9e2ed4aac 100644
--- a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.FrontendController do
@@ -18,13 +18,24 @@ defmodule Pleroma.Web.AdminAPI.FrontendController do
def index(conn, _params) do
installed = installed()
+ # FIrst get frontends from config,
+ # then add frontends that are installed but not in the config
frontends =
- [:frontends, :available]
- |> Config.get([])
+ Config.get([:frontends, :available], [])
|> Enum.map(fn {name, desc} ->
- Map.put(desc, "installed", name in installed)
+ desc
+ |> Map.put("installed", name in installed)
+ |> Map.put("installed_refs", installed_refs(name))
end)
+ frontends =
+ frontends ++
+ (installed
+ |> Enum.filter(fn n -> not Enum.any?(frontends, fn f -> f["name"] == n end) end)
+ |> Enum.map(fn name ->
+ %{"name" => name, "installed" => true, "installed_refs" => installed_refs(name)}
+ end))
+
render(conn, "index.json", frontends: frontends)
end
@@ -35,6 +46,20 @@ defmodule Pleroma.Web.AdminAPI.FrontendController do
end
defp installed do
- File.ls!(Pleroma.Frontend.dir())
+ frontend_directory = Pleroma.Frontend.dir()
+
+ if File.exists?(frontend_directory) do
+ File.ls!(frontend_directory)
+ else
+ []
+ end
+ end
+
+ def installed_refs(name) do
+ if name in installed() do
+ File.ls!(Path.join(Pleroma.Frontend.dir(), name))
+ else
+ []
+ end
end
end
diff --git a/lib/pleroma/web/admin_api/controllers/instance_controller.ex b/lib/pleroma/web/admin_api/controllers/instance_controller.ex
new file mode 100644
index 000000000..117a72280
--- /dev/null
+++ b/lib/pleroma/web/admin_api/controllers/instance_controller.ex
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.InstanceController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper, only: [fetch_integer_param: 3]
+
+ alias Pleroma.Instances.Instance
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.AdminAPI
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ require Logger
+
+ @default_page_size 50
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["admin:read:statuses"]}
+ when action in [:list_statuses]
+ )
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["admin:write:accounts", "admin:write:statuses"]}
+ when action in [:delete]
+ )
+
+ action_fallback(AdminAPI.FallbackController)
+
+ def list_statuses(conn, %{"instance" => instance} = params) do
+ with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
+ {page, page_size} = page_params(params)
+
+ result =
+ ActivityPub.fetch_statuses(nil, %{
+ instance: instance,
+ limit: page_size,
+ offset: (page - 1) * page_size,
+ exclude_reblogs: not with_reblogs,
+ total: true
+ })
+
+ conn
+ |> put_view(AdminAPI.StatusView)
+ |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
+ end
+
+ def delete(conn, %{"instance" => instance}) do
+ with {:ok, _job} <- Instance.delete_users_and_activities(instance) do
+ json(conn, instance)
+ end
+ end
+
+ defp page_params(params) do
+ {
+ fetch_integer_param(params, "page", 1),
+ fetch_integer_param(params, "page_size", @default_page_size)
+ }
+ end
+end
diff --git a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex
index a55857a0e..990a94313 100644
--- a/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/instance_document_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
diff --git a/lib/pleroma/web/admin_api/controllers/invite_controller.ex b/lib/pleroma/web/admin_api/controllers/invite_controller.ex
index 727ebd846..c5d759bb5 100644
--- a/lib/pleroma/web/admin_api/controllers/invite_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/invite_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InviteController do
diff --git a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex
index a6d7aaf54..4d53f5451 100644
--- a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
diff --git a/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex
index 005fe67e2..879e8b2b4 100644
--- a/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/o_auth_app_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.OAuthAppController do
@@ -13,7 +13,6 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppController do
require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:put_view, Pleroma.Web.MastodonAPI.AppView)
plug(
OAuthScopesPlug,
diff --git a/lib/pleroma/web/admin_api/controllers/relay_controller.ex b/lib/pleroma/web/admin_api/controllers/relay_controller.ex
index c6bd43fea..2e83fe139 100644
--- a/lib/pleroma/web/admin_api/controllers/relay_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/relay_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.RelayController do
diff --git a/lib/pleroma/web/admin_api/controllers/report_controller.ex b/lib/pleroma/web/admin_api/controllers/report_controller.ex
index d4a4935ee..15cbbcc3e 100644
--- a/lib/pleroma/web/admin_api/controllers/report_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/report_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ReportController do
diff --git a/lib/pleroma/web/admin_api/controllers/status_controller.ex b/lib/pleroma/web/admin_api/controllers/status_controller.ex
index 7058def82..9a3d49b57 100644
--- a/lib/pleroma/web/admin_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/status_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.StatusController do
@@ -65,12 +65,6 @@ defmodule Pleroma.Web.AdminAPI.StatusController do
def delete(%{assigns: %{user: user}} = conn, %{id: id}) do
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
- ModerationLog.insert_log(%{
- action: "status_delete",
- actor: user,
- subject_id: id
- })
-
json(conn, %{})
end
end
diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex
index d3e4c18a3..7b4ee46a4 100644
--- a/lib/pleroma/web/admin_api/controllers/user_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/user_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.UserController do
@@ -35,7 +35,9 @@ defmodule Pleroma.Web.AdminAPI.UserController do
:toggle_activation,
:activate,
:deactivate,
- :approve
+ :approve,
+ :suggest,
+ :unsuggest
]
)
@@ -45,8 +47,6 @@ defmodule Pleroma.Web.AdminAPI.UserController do
when action in [:follow, :unfollow]
)
- plug(:put_view, Pleroma.Web.AdminAPI.AccountView)
-
action_fallback(AdminAPI.FallbackController)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation
@@ -241,6 +241,32 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users)
end
+ def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
+ users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+ {:ok, updated_users} = User.set_suggestion(users, true)
+
+ ModerationLog.insert_log(%{
+ actor: admin,
+ subject: users,
+ action: "add_suggestion"
+ })
+
+ render(conn, "index.json", users: updated_users)
+ end
+
+ def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
+ users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+ {:ok, updated_users} = User.set_suggestion(users, false)
+
+ ModerationLog.insert_log(%{
+ actor: admin,
+ subject: users,
+ action: "remove_suggestion"
+ })
+
+ render(conn, "index.json", users: updated_users)
+ end
+
def index(conn, params) do
{page, page_size} = page_params(params)
filters = maybe_parse_filters(params[:filters])
diff --git a/lib/pleroma/web/admin_api/report.ex b/lib/pleroma/web/admin_api/report.ex
index 259068f04..fa89e3405 100644
--- a/lib/pleroma/web/admin_api/report.ex
+++ b/lib/pleroma/web/admin_api/report.ex
@@ -1,9 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.Report do
alias Pleroma.Activity
+ alias Pleroma.Object
alias Pleroma.User
def extract_report_info(
@@ -13,11 +14,47 @@ defmodule Pleroma.Web.AdminAPI.Report do
account = User.get_cached_by_ap_id(account_ap_id)
statuses =
- Enum.map(status_ap_ids, fn
- act when is_map(act) -> Activity.get_by_ap_id_with_object(act["id"])
- act when is_binary(act) -> Activity.get_by_ap_id_with_object(act)
+ status_ap_ids
+ |> Enum.reject(&is_nil(&1))
+ |> Enum.map(fn
+ act when is_map(act) ->
+ Activity.get_create_by_object_ap_id_with_object(act["id"]) ||
+ Activity.get_by_ap_id_with_object(act["id"]) || make_fake_activity(act, user)
+
+ act when is_binary(act) ->
+ Activity.get_create_by_object_ap_id_with_object(act) ||
+ Activity.get_by_ap_id_with_object(act)
end)
%{report: report, user: user, account: account, statuses: statuses}
end
+
+ defp make_fake_activity(act, user) do
+ %Activity{
+ id: "pleroma:fake:#{act["id"]}",
+ data: %{
+ "actor" => user.ap_id,
+ "type" => "Create",
+ "to" => [],
+ "cc" => [],
+ "object" => act["id"],
+ "published" => act["published"],
+ "id" => act["id"],
+ "context" => "pleroma:fake"
+ },
+ recipients: [user.ap_id],
+ object: %Object{
+ data: %{
+ "actor" => user.ap_id,
+ "type" => "Note",
+ "content" => act["content"],
+ "published" => act["published"],
+ "to" => [],
+ "cc" => [],
+ "id" => act["id"],
+ "context" => "pleroma:fake"
+ }
+ }
+ }
+ end
end
diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex
index eeeebdf4e..f5195acde 100644
--- a/lib/pleroma/web/admin_api/search.ex
+++ b/lib/pleroma/web/admin_api/search.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.Search do
@@ -10,12 +10,6 @@ defmodule Pleroma.Web.AdminAPI.Search do
@page_size 50
- defmacro not_empty_string(string) do
- quote do
- is_binary(unquote(string)) and unquote(string) != ""
- end
- end
-
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
def user(params \\ %{}) do
query =
@@ -23,7 +17,7 @@ defmodule Pleroma.Web.AdminAPI.Search do
|> Map.drop([:page, :page_size])
|> Map.put(:invisible, false)
|> User.Query.build()
- |> order_by([u], u.nickname)
+ |> order_by(desc: :id)
paginated_query =
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)
diff --git a/lib/pleroma/web/admin_api/views/account_view.ex b/lib/pleroma/web/admin_api/views/account_view.ex
index e053a9b67..280152241 100644
--- a/lib/pleroma/web/admin_api/views/account_view.ex
+++ b/lib/pleroma/web/admin_api/views/account_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AccountView do
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
alias Pleroma.User
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.AccountView
+ alias Pleroma.Web.CommonAPI
alias Pleroma.Web.MastodonAPI
alias Pleroma.Web.MediaProxy
@@ -79,9 +80,11 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
"tags" => user.tags || [],
"is_confirmed" => user.is_confirmed,
"is_approved" => user.is_approved,
+ "is_suggested" => user.is_suggested,
"url" => user.uri || user.ap_id,
"registration_reason" => user.registration_reason,
- "actor_type" => user.actor_type
+ "actor_type" => user.actor_type,
+ "created_at" => CommonAPI.Utils.to_masto_date(user.inserted_at)
}
end
diff --git a/lib/pleroma/web/admin_api/views/announcement_view.ex b/lib/pleroma/web/admin_api/views/announcement_view.ex
new file mode 100644
index 000000000..a35bd60cf
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/announcement_view.ex
@@ -0,0 +1,15 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.AnnouncementView do
+ use Pleroma.Web, :view
+
+ def render("index.json", %{announcements: announcements}) do
+ render_many(announcements, __MODULE__, "show.json")
+ end
+
+ def render("show.json", %{announcement: announcement}) do
+ Pleroma.Announcement.render_json(announcement, admin: true)
+ end
+end
diff --git a/lib/pleroma/web/admin_api/views/chat_view.ex b/lib/pleroma/web/admin_api/views/chat_view.ex
index 2a2015ad1..d58bf8eda 100644
--- a/lib/pleroma/web/admin_api/views/chat_view.ex
+++ b/lib/pleroma/web/admin_api/views/chat_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ChatView do
diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex
index d29b4963d..f582ad42e 100644
--- a/lib/pleroma/web/admin_api/views/config_view.ex
+++ b/lib/pleroma/web/admin_api/views/config_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ConfigView do
diff --git a/lib/pleroma/web/admin_api/views/frontend_view.ex b/lib/pleroma/web/admin_api/views/frontend_view.ex
index a3933a57d..ae4016581 100644
--- a/lib/pleroma/web/admin_api/views/frontend_view.ex
+++ b/lib/pleroma/web/admin_api/views/frontend_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.FrontendView do
@@ -15,7 +15,8 @@ defmodule Pleroma.Web.AdminAPI.FrontendView do
git: frontend["git"],
build_url: frontend["build_url"],
ref: frontend["ref"],
- installed: frontend["installed"]
+ installed: frontend["installed"],
+ installed_refs: frontend["installed_refs"]
}
end
end
diff --git a/lib/pleroma/web/admin_api/views/invite_view.ex b/lib/pleroma/web/admin_api/views/invite_view.ex
index c7e307bda..76cee3bc5 100644
--- a/lib/pleroma/web/admin_api/views/invite_view.ex
+++ b/lib/pleroma/web/admin_api/views/invite_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.InviteView do
diff --git a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex
index 1ec123048..b46f54efe 100644
--- a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex
+++ b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.MediaProxyCacheView do
diff --git a/lib/pleroma/web/admin_api/views/moderation_log_view.ex b/lib/pleroma/web/admin_api/views/moderation_log_view.ex
index b3a9efff3..1f25f194f 100644
--- a/lib/pleroma/web/admin_api/views/moderation_log_view.ex
+++ b/lib/pleroma/web/admin_api/views/moderation_log_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ModerationLogView do
diff --git a/lib/pleroma/web/admin_api/views/o_auth_app_view.ex b/lib/pleroma/web/admin_api/views/o_auth_app_view.ex
new file mode 100644
index 000000000..d1aef0e10
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/o_auth_app_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.OAuthAppView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.AppView.render(view, opts)
+end
diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex
index 1c67b2458..b761dbb22 100644
--- a/lib/pleroma/web/admin_api/views/report_view.ex
+++ b/lib/pleroma/web/admin_api/views/report_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ReportView do
diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex
index 48d639b41..03b5c440a 100644
--- a/lib/pleroma/web/admin_api/views/status_view.ex
+++ b/lib/pleroma/web/admin_api/views/status_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.StatusView do
diff --git a/lib/pleroma/web/admin_api/views/user_view.ex b/lib/pleroma/web/admin_api/views/user_view.ex
new file mode 100644
index 000000000..f198921ae
--- /dev/null
+++ b/lib/pleroma/web/admin_api/views/user_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.UserView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.AdminAPI
+
+ def render(view, opts), do: AdminAPI.AccountView.render(view, opts)
+end
diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex
index 528cd9cf4..2d56dc643 100644
--- a/lib/pleroma/web/api_spec.ex
+++ b/lib/pleroma/web/api_spec.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec do
@@ -95,7 +95,8 @@ defmodule Pleroma.Web.ApiSpec do
"Relays",
"Report managment",
"Status administration",
- "User administration"
+ "User administration",
+ "Announcement management"
]
},
%{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]},
@@ -110,10 +111,12 @@ defmodule Pleroma.Web.ApiSpec do
"Follow requests",
"Mascot",
"Markers",
- "Notifications"
+ "Notifications",
+ "Filters",
+ "Settings"
]
},
- %{"name" => "Instance", "tags" => ["Custom emojis"]},
+ %{"name" => "Instance", "tags" => ["Custom emojis", "Instance misc"]},
%{"name" => "Messaging", "tags" => ["Chats", "Conversations"]},
%{
"name" => "Statuses",
@@ -125,10 +128,21 @@ defmodule Pleroma.Web.ApiSpec do
"Retrieve status information",
"Scheduled statuses",
"Search",
- "Status actions"
+ "Status actions",
+ "Media attachments"
]
},
- %{"name" => "Miscellaneous", "tags" => ["Emoji packs", "Reports", "Suggestions"]}
+ %{
+ "name" => "Miscellaneous",
+ "tags" => [
+ "Emoji packs",
+ "Reports",
+ "Suggestions",
+ "Announcements",
+ "Remote interaction",
+ "Others"
+ ]
+ }
]
}
}
diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex
index d23a7dcb6..add59eb88 100644
--- a/lib/pleroma/web/api_spec/cast_and_validate.ex
+++ b/lib/pleroma/web/api_spec/cast_and_validate.ex
@@ -1,6 +1,6 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019-2020 Moxley Stratton, Mike Buhot <https://github.com/open-api-spex/open_api_spex>, MPL-2.0
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.CastAndValidate do
diff --git a/lib/pleroma/web/api_spec/helpers.ex b/lib/pleroma/web/api_spec/helpers.ex
index 6f67339e6..f20a9163d 100644
--- a/lib/pleroma/web/api_spec/helpers.ex
+++ b/lib/pleroma/web/api_spec/helpers.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Helpers do
diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex
index 54e5ebc76..f2897a3a3 100644
--- a/lib/pleroma/web/api_spec/operations/account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/account_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.AccountOperation do
@@ -64,7 +64,8 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
requestBody: request_body("Parameters", update_credentials_request(), required: true),
responses: %{
200 => Operation.response("Account", "application/json", Account),
- 403 => Operation.response("Error", "application/json", ApiError)
+ 403 => Operation.response("Error", "application/json", ApiError),
+ 413 => Operation.response("Error", "application/json", ApiError)
}
}
end
@@ -223,9 +224,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
type: :object,
properties: %{
reblogs: %Schema{
- type: :boolean,
+ allOf: [BooleanLike],
description: "Receive this account's reblogs in home timeline? Defaults to true.",
default: true
+ },
+ notify: %Schema{
+ allOf: [BooleanLike],
+ description:
+ "Receive notifications for all statuses posted by the account? Defaults to false.",
+ default: false
}
}
},
@@ -273,10 +280,16 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"Mute notifications in addition to statuses? Defaults to `true`."
),
Operation.parameter(
+ :duration,
+ :query,
+ %Schema{type: :integer},
+ "Expire the mute in `duration` seconds. Default 0 for infinity"
+ ),
+ Operation.parameter(
:expires_in,
:query,
%Schema{type: :integer, default: 0},
- "Expire the mute in `expires_in` seconds. Default 0 for infinity"
+ "Deprecated, use `duration` instead"
)
],
responses: %{
@@ -328,6 +341,81 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
+ def endorse_operation do
+ %Operation{
+ tags: ["Account actions"],
+ summary: "Endorse",
+ operationId: "AccountController.endorse",
+ security: [%{"oAuth" => ["follow", "write:accounts"]}],
+ description: "Addds the given account to endorsed accounts list.",
+ parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
+ responses: %{
+ 200 => Operation.response("Relationship", "application/json", AccountRelationship),
+ 400 =>
+ Operation.response("Bad Request", "application/json", %Schema{
+ allOf: [ApiError],
+ title: "Unprocessable Entity",
+ example: %{
+ "error" => "You have already pinned the maximum number of users"
+ }
+ })
+ }
+ }
+ end
+
+ def unendorse_operation do
+ %Operation{
+ tags: ["Account actions"],
+ summary: "Unendorse",
+ operationId: "AccountController.unendorse",
+ security: [%{"oAuth" => ["follow", "write:accounts"]}],
+ description: "Removes the given account from endorsed accounts list.",
+ parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
+ responses: %{
+ 200 => Operation.response("Relationship", "application/json", AccountRelationship)
+ }
+ }
+ end
+
+ def remove_from_followers_operation do
+ %Operation{
+ tags: ["Account actions"],
+ summary: "Remove from followers",
+ operationId: "AccountController.remove_from_followers",
+ security: [%{"oAuth" => ["follow", "write:follows"]}],
+ description: "Remove the given account from followers",
+ parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
+ responses: %{
+ 200 => Operation.response("Relationship", "application/json", AccountRelationship),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def note_operation do
+ %Operation{
+ tags: ["Account actions"],
+ summary: "Set a private note about a user.",
+ operationId: "AccountController.note",
+ security: [%{"oAuth" => ["follow", "write:accounts"]}],
+ requestBody: request_body("Parameters", note_request()),
+ description: "Create a note for the given account.",
+ parameters: [
+ %Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
+ Operation.parameter(
+ :comment,
+ :query,
+ %Schema{type: :string},
+ "Account note body"
+ )
+ ],
+ responses: %{
+ 200 => Operation.response("Relationship", "application/json", AccountRelationship)
+ }
+ }
+ end
+
def follow_by_uri_operation do
%Operation{
tags: ["Account actions"],
@@ -364,22 +452,42 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
operationId: "AccountController.blocks",
description: "View your blocks. See also accounts/:id/{block,unblock}",
security: [%{"oAuth" => ["read:blocks"]}],
- parameters: pagination_params(),
+ parameters: [with_relationships_param() | pagination_params()],
responses: %{
200 => Operation.response("Accounts", "application/json", array_of_accounts())
}
}
end
+ def lookup_operation do
+ %Operation{
+ tags: ["Retrieve account information"],
+ summary: "Find a user by nickname",
+ operationId: "AccountController.lookup",
+ parameters: [
+ Operation.parameter(
+ :acct,
+ :query,
+ :string,
+ "User nickname"
+ )
+ ],
+ responses: %{
+ 200 => Operation.response("Account", "application/json", Account),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
def endorsements_operation do
%Operation{
tags: ["Retrieve account information"],
summary: "Endorsements",
operationId: "AccountController.endorsements",
- description: "Not implemented",
+ description: "Returns endorsed accounts",
security: [%{"oAuth" => ["read:accounts"]}],
responses: %{
- 200 => empty_array_response()
+ 200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
}
}
end
@@ -458,6 +566,25 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
type: :string,
nullable: true,
description: "Invite token required when the registrations aren't public"
+ },
+ birthday: %Schema{
+ nullable: true,
+ description: "User's birthday",
+ anyOf: [
+ %Schema{
+ type: :string,
+ format: :date
+ },
+ %Schema{
+ type: :string,
+ maxLength: 0
+ }
+ ]
+ },
+ language: %Schema{
+ type: :string,
+ nullable: true,
+ description: "User's preferred language for emails"
}
},
example: %{
@@ -635,7 +762,26 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
description:
"Discovery (listing, indexing) of this account by external services (search bots etc.) is allowed."
},
- actor_type: ActorType
+ actor_type: ActorType,
+ birthday: %Schema{
+ nullable: true,
+ description: "User's birthday",
+ anyOf: [
+ %Schema{
+ type: :string,
+ format: :date
+ },
+ %Schema{
+ type: :string,
+ maxLength: 0
+ }
+ ]
+ },
+ show_birthday: %Schema{
+ allOf: [BooleanLike],
+ nullable: true,
+ description: "User's birthday will be visible"
+ }
},
example: %{
bot: false,
@@ -655,7 +801,9 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
allow_following_move: false,
also_known_as: ["https://foo.bar/users/foo"],
discoverable: false,
- actor_type: "Person"
+ actor_type: "Person",
+ show_birthday: false,
+ birthday: "2001-02-12"
}
}
end
@@ -685,9 +833,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"blocked_by" => true,
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"domain_blocking" => false,
"subscribing" => false,
+ "notifying" => false,
"endorsed" => true
},
%{
@@ -699,9 +849,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"blocked_by" => true,
"muting" => true,
"muting_notifications" => false,
+ "note" => "",
"requested" => true,
"domain_blocking" => false,
"subscribing" => false,
+ "notifying" => false,
"endorsed" => false
},
%{
@@ -713,9 +865,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"blocked_by" => false,
"muting" => true,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"domain_blocking" => true,
"subscribing" => true,
+ "notifying" => true,
"endorsed" => false
}
]
@@ -746,10 +900,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
description: "Mute notifications in addition to statuses? Defaults to true.",
default: true
},
+ duration: %Schema{
+ type: :integer,
+ nullable: true,
+ description: "Expire the mute in `expires_in` seconds. Default 0 for infinity"
+ },
expires_in: %Schema{
type: :integer,
nullable: true,
- description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
+ description: "Deprecated, use `duration` instead",
default: 0
}
},
@@ -760,6 +919,23 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
+ defp note_request do
+ %Schema{
+ title: "AccountNoteRequest",
+ description: "POST body for adding a note for an account",
+ type: :object,
+ properties: %{
+ comment: %Schema{
+ type: :string,
+ description: "Account note body"
+ }
+ },
+ example: %{
+ "comment" => "Example note"
+ }
+ }
+ end
+
defp array_of_lists do
%Schema{
title: "ArrayOfLists",
diff --git a/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex
new file mode 100644
index 000000000..49850e5d2
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex
@@ -0,0 +1,165 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.Announcement
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def index_operation do
+ %Operation{
+ tags: ["Announcement management"],
+ summary: "Retrieve a list of announcements",
+ operationId: "AdminAPI.AnnouncementController.index",
+ security: [%{"oAuth" => ["admin:read"]}],
+ parameters: [
+ Operation.parameter(
+ :limit,
+ :query,
+ %Schema{type: :integer, minimum: 1},
+ "the maximum number of announcements to return"
+ ),
+ Operation.parameter(
+ :offset,
+ :query,
+ %Schema{type: :integer, minimum: 0},
+ "the offset of the first announcement to return"
+ )
+ | admin_api_params()
+ ],
+ responses: %{
+ 200 => Operation.response("Response", "application/json", list_of_announcements()),
+ 400 => Operation.response("Forbidden", "application/json", ApiError),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def show_operation do
+ %Operation{
+ tags: ["Announcement management"],
+ summary: "Display one announcement",
+ operationId: "AdminAPI.AnnouncementController.show",
+ security: [%{"oAuth" => ["admin:read"]}],
+ parameters: [
+ Operation.parameter(
+ :id,
+ :path,
+ :string,
+ "announcement id"
+ )
+ | admin_api_params()
+ ],
+ responses: %{
+ 200 => Operation.response("Response", "application/json", Announcement),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def delete_operation do
+ %Operation{
+ tags: ["Announcement management"],
+ summary: "Delete one announcement",
+ operationId: "AdminAPI.AnnouncementController.delete",
+ security: [%{"oAuth" => ["admin:write"]}],
+ parameters: [
+ Operation.parameter(
+ :id,
+ :path,
+ :string,
+ "announcement id"
+ )
+ | admin_api_params()
+ ],
+ responses: %{
+ 200 => Operation.response("Response", "application/json", %Schema{type: :object}),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def create_operation do
+ %Operation{
+ tags: ["Announcement management"],
+ summary: "Create one announcement",
+ operationId: "AdminAPI.AnnouncementController.create",
+ security: [%{"oAuth" => ["admin:write"]}],
+ requestBody: request_body("Parameters", create_request(), required: true),
+ responses: %{
+ 200 => Operation.response("Response", "application/json", Announcement),
+ 400 => Operation.response("Bad Request", "application/json", ApiError),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def change_operation do
+ %Operation{
+ tags: ["Announcement management"],
+ summary: "Change one announcement",
+ operationId: "AdminAPI.AnnouncementController.change",
+ security: [%{"oAuth" => ["admin:write"]}],
+ parameters: [
+ Operation.parameter(
+ :id,
+ :path,
+ :string,
+ "announcement id"
+ )
+ | admin_api_params()
+ ],
+ requestBody: request_body("Parameters", change_request(), required: true),
+ responses: %{
+ 200 => Operation.response("Response", "application/json", Announcement),
+ 400 => Operation.response("Bad Request", "application/json", ApiError),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp create_or_change_props do
+ %{
+ content: %Schema{type: :string},
+ starts_at: %Schema{type: :string, format: "date-time", nullable: true},
+ ends_at: %Schema{type: :string, format: "date-time", nullable: true},
+ all_day: %Schema{type: :boolean}
+ }
+ end
+
+ def create_request do
+ %Schema{
+ title: "AnnouncementCreateRequest",
+ type: :object,
+ required: [:content],
+ properties: create_or_change_props()
+ }
+ end
+
+ def change_request do
+ %Schema{
+ title: "AnnouncementChangeRequest",
+ type: :object,
+ properties: create_or_change_props()
+ }
+ end
+
+ def list_of_announcements do
+ %Schema{
+ type: :array,
+ items: Announcement
+ }
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex
index 57906445e..2a274e080 100644
--- a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/config_operation.ex b/lib/pleroma/web/api_spec/operations/admin/config_operation.ex
index 30c3433b7..487dd5cda 100644
--- a/lib/pleroma/web/api_spec/operations/admin/config_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/config_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex
index 566f1eeb1..3e85c44d2 100644
--- a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do
@@ -51,8 +51,9 @@ defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do
name: %Schema{type: :string},
git: %Schema{type: :string, format: :uri, nullable: true},
build_url: %Schema{type: :string, format: :uri, nullable: true},
- ref: %Schema{type: :string},
- installed: %Schema{type: :boolean}
+ ref: %Schema{type: :string, nullable: true},
+ installed: %Schema{type: :boolean},
+ installed_refs: %Schema{type: :array, items: %Schema{type: :string}}
}
}
}
diff --git a/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex b/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex
index 79ceae970..fc0de499b 100644
--- a/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/instance_document_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.InstanceDocumentOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex b/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
index 704f082ba..e4a9ffaeb 100644
--- a/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex
index 8f85ebf2d..0b1eb3946 100644
--- a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.MediaProxyCacheOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex
index 35b029b19..1a05aff6a 100644
--- a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex
index c55c84fee..8b241bd49 100644
--- a/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/relay_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex
index 8d7577505..312e091a5 100644
--- a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
diff --git a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex
index d25ab5247..17383f1d0 100644
--- a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
@@ -70,7 +70,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
def show_operation do
%Operation{
- tags: ["Status adminitration)"],
+ tags: ["Status administration"],
summary: "Get status",
operationId: "AdminAPI.StatusController.show",
parameters: [id_param() | admin_api_params()],
@@ -84,7 +84,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
def update_operation do
%Operation{
- tags: ["Status adminitration)"],
+ tags: ["Status administration"],
summary: "Change the scope of a status",
operationId: "AdminAPI.StatusController.update",
parameters: [id_param() | admin_api_params()],
@@ -99,7 +99,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
def delete_operation do
%Operation{
- tags: ["Status adminitration)"],
+ tags: ["Status administration"],
summary: "Delete status",
operationId: "AdminAPI.StatusController.delete",
parameters: [id_param() | admin_api_params()],
@@ -143,7 +143,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
}
},
tags: %Schema{type: :string},
- is_confirmed: %Schema{type: :string}
+ is_confirmed: %Schema{type: :boolean}
}
}
end
diff --git a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex
index c9d0bfd7c..a5179ac39 100644
--- a/lib/pleroma/web/api_spec/operations/admin/user_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/user_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
@@ -216,7 +216,71 @@ defmodule Pleroma.Web.ApiSpec.Admin.UserOperation do
request_body(
"Parameters",
%Schema{
- description: "POST body for deleting multiple users",
+ description: "POST body for approving multiple users",
+ type: :object,
+ properties: %{
+ nicknames: %Schema{
+ type: :array,
+ items: %Schema{type: :string}
+ }
+ }
+ }
+ ),
+ responses: %{
+ 200 =>
+ Operation.response("Response", "application/json", %Schema{
+ type: :object,
+ properties: %{user: %Schema{type: :array, items: user()}}
+ }),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def suggest_operation do
+ %Operation{
+ tags: ["User administration"],
+ summary: "Suggest multiple users",
+ operationId: "AdminAPI.UserController.suggest",
+ security: [%{"oAuth" => ["admin:write:accounts"]}],
+ parameters: admin_api_params(),
+ requestBody:
+ request_body(
+ "Parameters",
+ %Schema{
+ description: "POST body for adding multiple suggested users",
+ type: :object,
+ properties: %{
+ nicknames: %Schema{
+ type: :array,
+ items: %Schema{type: :string}
+ }
+ }
+ }
+ ),
+ responses: %{
+ 200 =>
+ Operation.response("Response", "application/json", %Schema{
+ type: :object,
+ properties: %{user: %Schema{type: :array, items: user()}}
+ }),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def unsuggest_operation do
+ %Operation{
+ tags: ["User administration"],
+ summary: "Unsuggest multiple users",
+ operationId: "AdminAPI.UserController.unsuggest",
+ security: [%{"oAuth" => ["admin:write:accounts"]}],
+ parameters: admin_api_params(),
+ requestBody:
+ request_body(
+ "Parameters",
+ %Schema{
+ description: "POST body for removing multiple suggested users",
type: :object,
properties: %{
nicknames: %Schema{
diff --git a/lib/pleroma/web/api_spec/operations/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/announcement_operation.ex
new file mode 100644
index 000000000..6f7031962
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/announcement_operation.ex
@@ -0,0 +1,57 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.AnnouncementOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.Announcement
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def index_operation do
+ %Operation{
+ tags: ["Announcements"],
+ summary: "Retrieve a list of announcements",
+ operationId: "MastodonAPI.AnnouncementController.index",
+ security: [%{"oAuth" => []}],
+ responses: %{
+ 200 => Operation.response("Response", "application/json", list_of_announcements()),
+ 403 => Operation.response("Forbidden", "application/json", ApiError)
+ }
+ }
+ end
+
+ def mark_read_operation do
+ %Operation{
+ tags: ["Announcements"],
+ summary: "Mark one announcement as read",
+ operationId: "MastodonAPI.AnnouncementController.mark_read",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ parameters: [
+ Operation.parameter(
+ :id,
+ :path,
+ :string,
+ "announcement id"
+ )
+ ],
+ responses: %{
+ 200 => Operation.response("Response", "application/json", %Schema{type: :object}),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def list_of_announcements do
+ %Schema{
+ type: :array,
+ items: Announcement
+ }
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/app_operation.ex b/lib/pleroma/web/api_spec/operations/app_operation.ex
index dfb1c7170..dfa2237c0 100644
--- a/lib/pleroma/web/api_spec/operations/app_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/app_operation.ex
@@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.AppOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Helpers
+ alias Pleroma.Web.ApiSpec.Schemas.App
@spec open_api_operation(atom) :: Operation.t()
def open_api_operation(action) do
@@ -22,7 +23,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
operationId: "AppController.create",
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
responses: %{
- 200 => Operation.response("App", "application/json", create_response()),
+ 200 => Operation.response("App", "application/json", App),
422 =>
Operation.response(
"Unprocessable Entity",
@@ -119,30 +120,4 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
}
}
end
-
- defp create_response do
- %Schema{
- title: "AppCreateResponse",
- description: "Response schema for an app",
- type: :object,
- properties: %{
- id: %Schema{type: :string},
- name: %Schema{type: :string},
- client_id: %Schema{type: :string},
- client_secret: %Schema{type: :string},
- redirect_uri: %Schema{type: :string},
- vapid_key: %Schema{type: :string},
- website: %Schema{type: :string, nullable: true}
- },
- example: %{
- "id" => "123",
- "name" => "My App",
- "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
- "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
- "vapid_key" =>
- "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
- "website" => "https://myapp.com/"
- }
- }
- end
end
diff --git a/lib/pleroma/web/api_spec/operations/chat_operation.ex b/lib/pleroma/web/api_spec/operations/chat_operation.ex
index 23cb66392..cf6a055fc 100644
--- a/lib/pleroma/web/api_spec/operations/chat_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/chat_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.ChatOperation do
diff --git a/lib/pleroma/web/api_spec/operations/conversation_operation.ex b/lib/pleroma/web/api_spec/operations/conversation_operation.ex
index 17ed1af5e..82ccf41f9 100644
--- a/lib/pleroma/web/api_spec/operations/conversation_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/conversation_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.ConversationOperation do
diff --git a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex
index 98da1a6de..77823f13e 100644
--- a/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/custom_emoji_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do
diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex
new file mode 100644
index 000000000..23fa84dff
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.DirectoryOperation do
+ alias OpenApiSpex.Operation
+ alias Pleroma.Web.ApiSpec.AccountOperation
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
+
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def index_operation do
+ %Operation{
+ tags: ["Others"],
+ summary: "Profile directory",
+ operationId: "DirectoryController.index",
+ parameters:
+ [
+ Operation.parameter(
+ :order,
+ :query,
+ :string,
+ "Order by recent activity or account creation",
+ required: nil
+ ),
+ Operation.parameter(:local, :query, BooleanLike, "Include local users only")
+ ] ++ pagination_params(),
+ responses: %{
+ 200 =>
+ Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex
index f124e7fe5..2340fd914 100644
--- a/lib/pleroma/web/api_spec/operations/domain_block_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/domain_block_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.DomainBlockOperation do
diff --git a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
index a7b306a30..74341d64f 100644
--- a/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/emoji_reaction_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
diff --git a/lib/pleroma/web/api_spec/operations/filter_operation.ex b/lib/pleroma/web/api_spec/operations/filter_operation.ex
index 5102921bc..a1700b7c9 100644
--- a/lib/pleroma/web/api_spec/operations/filter_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/filter_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.FilterOperation do
diff --git a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex
index 784019699..72dc8b5fa 100644
--- a/lib/pleroma/web/api_spec/operations/follow_request_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/follow_request_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do
diff --git a/lib/pleroma/web/api_spec/operations/instance_operation.ex b/lib/pleroma/web/api_spec/operations/instance_operation.ex
index 9384acc32..a07be7e40 100644
--- a/lib/pleroma/web/api_spec/operations/instance_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/instance_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.InstanceOperation do
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
def show_operation do
%Operation{
- tags: ["Instance"],
+ tags: ["Instance misc"],
summary: "Retrieve instance information",
description: "Information about the server",
operationId: "InstanceController.show",
@@ -25,7 +25,7 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
def peers_operation do
%Operation{
- tags: ["Instance"],
+ tags: ["Instance misc"],
summary: "Retrieve list of known instances",
operationId: "InstanceController.peers",
responses: %{
diff --git a/lib/pleroma/web/api_spec/operations/list_operation.ex b/lib/pleroma/web/api_spec/operations/list_operation.ex
index 8a6e92b99..7d876ae2d 100644
--- a/lib/pleroma/web/api_spec/operations/list_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/list_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.ListOperation do
diff --git a/lib/pleroma/web/api_spec/operations/marker_operation.ex b/lib/pleroma/web/api_spec/operations/marker_operation.ex
index c5ff5984b..4dfdeb448 100644
--- a/lib/pleroma/web/api_spec/operations/marker_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/marker_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.MarkerOperation do
diff --git a/lib/pleroma/web/api_spec/operations/media_operation.ex b/lib/pleroma/web/api_spec/operations/media_operation.ex
index 85aa14869..e6df21246 100644
--- a/lib/pleroma/web/api_spec/operations/media_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/media_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.MediaOperation do
@@ -24,6 +24,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
requestBody: Helpers.request_body("Parameters", create_request()),
responses: %{
200 => Operation.response("Media", "application/json", Attachment),
+ 400 => Operation.response("Media", "application/json", ApiError),
401 => Operation.response("Media", "application/json", ApiError),
422 => Operation.response("Media", "application/json", ApiError)
}
@@ -105,6 +106,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
responses: %{
200 => Operation.response("Media", "application/json", Attachment),
401 => Operation.response("Media", "application/json", ApiError),
+ 403 => Operation.response("Media", "application/json", ApiError),
422 => Operation.response("Media", "application/json", ApiError)
}
}
@@ -120,6 +122,7 @@ defmodule Pleroma.Web.ApiSpec.MediaOperation do
requestBody: Helpers.request_body("Parameters", create_request()),
responses: %{
202 => Operation.response("Media", "application/json", Attachment),
+ 400 => Operation.response("Media", "application/json", ApiError),
422 => Operation.response("Media", "application/json", ApiError),
500 => Operation.response("Media", "application/json", ApiError)
}
diff --git a/lib/pleroma/web/api_spec/operations/notification_operation.ex b/lib/pleroma/web/api_spec/operations/notification_operation.ex
index ec88eabe1..56aa129d2 100644
--- a/lib/pleroma/web/api_spec/operations/notification_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/notification_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.NotificationOperation do
@@ -51,6 +51,12 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
:include_types,
:query,
%Schema{type: :array, items: notification_type()},
+ "Deprecated, use `types` instead"
+ ),
+ Operation.parameter(
+ :types,
+ :query,
+ %Schema{type: :array, items: notification_type()},
"Include the notifications for activities with the given types"
),
Operation.parameter(
@@ -195,7 +201,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
"pleroma:chat_mention",
"pleroma:report",
"move",
- "follow_request"
+ "follow_request",
+ "poll"
],
description: """
The type of event that resulted in the notification.
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
index ad49f6426..5375c5b15 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
@@ -1,9 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
@@ -62,6 +64,25 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
}
end
+ def endorsements_operation do
+ %Operation{
+ tags: ["Retrieve account information"],
+ summary: "Endorsements",
+ description: "Returns endorsed accounts",
+ operationId: "PleromaAPI.AccountController.endorsements",
+ parameters: [with_relationships_param(), id_param()],
+ responses: %{
+ 200 =>
+ Operation.response(
+ "Array of Accounts",
+ "application/json",
+ AccountOperation.array_of_accounts()
+ ),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
def subscribe_operation do
%Operation{
tags: ["Account actions"],
@@ -92,6 +113,34 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
}
end
+ def birthdays_operation do
+ %Operation{
+ tags: ["Retrieve account information"],
+ summary: "Birthday reminders",
+ description: "Birthday reminders about users you follow.",
+ operationId: "PleromaAPI.AccountController.birthdays",
+ parameters: [
+ Operation.parameter(
+ :day,
+ :query,
+ %Schema{type: :integer},
+ "Day of users' birthdays"
+ ),
+ Operation.parameter(
+ :month,
+ :query,
+ %Schema{type: :integer},
+ "Month of users' birthdays"
+ )
+ ],
+ security: [%{"oAuth" => ["read:accounts"]}],
+ responses: %{
+ 200 =>
+ Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts())
+ }
+ }
+ end
+
defp id_param do
Operation.parameter(:id, :path, FlakeID, "Account ID",
example: "9umDrYheeY451cQnEe",
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
new file mode 100644
index 000000000..9f8df6c5c
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.App
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ @spec index_operation() :: Operation.t()
+ def index_operation do
+ %Operation{
+ tags: ["Applications"],
+ summary: "List applications",
+ description: "List the OAuth applications for the current user",
+ operationId: "AppController.index",
+ responses: %{
+ 200 => Operation.response("Array of App", "application/json", array_of_apps())
+ }
+ }
+ end
+
+ defp array_of_apps do
+ %Schema{type: :array, items: App, example: [App.schema().example]}
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex
index c78e9780f..45fa2b058 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_backup_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do
%Operation{
tags: ["Backups"],
summary: "List backups",
- security: [%{"oAuth" => ["read:account"]}],
+ security: [%{"oAuth" => ["read:backups"]}],
operationId: "PleromaAPI.BackupController.index",
responses: %{
200 =>
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do
%Operation{
tags: ["Backups"],
summary: "Create a backup",
- security: [%{"oAuth" => ["read:account"]}],
+ security: [%{"oAuth" => ["read:backups"]}],
operationId: "PleromaAPI.BackupController.create",
responses: %{
200 =>
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex
index 12fb8ed36..89f0e13e1 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_conversation_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaConversationOperation do
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex
index 8c76096b5..b05bad197 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do
@@ -133,7 +133,11 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do
defp files_object do
%Schema{
type: :object,
- additionalProperties: %Schema{type: :string},
+ additionalProperties: %Schema{
+ type: :string,
+ description: "Filename of the emoji",
+ extensions: %{"x-additionalPropertiesName": "Emoji name"}
+ },
description: "Object with emoji names as keys and filenames as values"
}
end
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex
index 49247d9b6..efa36ffdc 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
@@ -227,13 +227,29 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
defp emoji_packs_response do
Operation.response(
- "Object with pack names as keys and pack contents as values",
+ "Emoji packs and the count",
"application/json",
%Schema{
type: :object,
- additionalProperties: emoji_pack(),
+ properties: %{
+ packs: %Schema{
+ type: :object,
+ description: "Object with pack names as keys and pack contents as values",
+ additionalProperties: %Schema{
+ emoji_pack()
+ | extensions: %{"x-additionalPropertiesName": "Pack name"}
+ }
+ },
+ count: %Schema{
+ type: :integer,
+ description: "Number of emoji packs"
+ }
+ },
example: %{
- "emojos" => emoji_pack().example
+ "packs" => %{
+ "emojos" => emoji_pack().example
+ },
+ "count" => 1
}
}
)
@@ -274,7 +290,11 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
defp files_object do
%Schema{
type: :object,
- additionalProperties: %Schema{type: :string},
+ additionalProperties: %Schema{
+ type: :string,
+ description: "Filename",
+ extensions: %{"x-additionalPropertiesName": "Emoji name"}
+ },
description: "Object with emoji names as keys and filenames as values"
}
end
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex
index 612113147..e9319f3fb 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do
def show_operation do
%Operation{
- tags: ["Instance"],
+ tags: ["Instance misc"],
summary: "Retrieve federation status",
description: "Information about instances deemed unreachable by the server",
operationId: "PleromaInstances.show",
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex
index 6191cb97d..775e27219 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_mascot_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaMascotOperation do
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
index 1dda39240..a994345db 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_notification_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaNotificationOperation do
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex
index ee8870dc2..9bc1877b1 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_report_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaReportOperation do
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex
index 6a909fc85..b6273bfcf 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex
new file mode 100644
index 000000000..e2cef4f67
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/pleroma_settings_operation.ex
@@ -0,0 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.PleromaSettingsOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def show_operation do
+ %Operation{
+ tags: ["Settings"],
+ summary: "Get settings for an application",
+ description: "Get synchronized settings for an application",
+ operationId: "SettingsController.show",
+ parameters: [app_name_param()],
+ security: [%{"oAuth" => ["read:accounts"]}],
+ responses: %{
+ 200 => Operation.response("object", "application/json", object())
+ }
+ }
+ end
+
+ def update_operation do
+ %Operation{
+ tags: ["Settings"],
+ summary: "Update settings for an application",
+ description: "Update synchronized settings for an application",
+ operationId: "SettingsController.update",
+ parameters: [app_name_param()],
+ security: [%{"oAuth" => ["write:accounts"]}],
+ requestBody: request_body("Parameters", update_request(), required: true),
+ responses: %{
+ 200 => Operation.response("object", "application/json", object())
+ }
+ }
+ end
+
+ def app_name_param do
+ Operation.parameter(:app, :path, %Schema{type: :string}, "Application name",
+ example: "pleroma-fe",
+ required: true
+ )
+ end
+
+ def object do
+ %Schema{
+ title: "Settings object",
+ description: "The object that contains settings for the application.",
+ type: :object
+ }
+ end
+
+ def update_request do
+ %Schema{
+ title: "SettingsUpdateRequest",
+ type: :object,
+ description:
+ "The settings object to be merged with the current settings. To remove a field, set it to null.",
+ example: %{
+ "config1" => true,
+ "config2_to_unset" => nil
+ }
+ }
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/poll_operation.ex b/lib/pleroma/web/api_spec/operations/poll_operation.ex
index 0d1c8d099..efd784f03 100644
--- a/lib/pleroma/web/api_spec/operations/poll_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/poll_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.PollOperation do
diff --git a/lib/pleroma/web/api_spec/operations/report_operation.ex b/lib/pleroma/web/api_spec/operations/report_operation.ex
index b744efa60..c74ac7d5f 100644
--- a/lib/pleroma/web/api_spec/operations/report_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/report_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.ReportOperation do
diff --git a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex
index b9c5b35c1..802d3b6dd 100644
--- a/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/scheduled_activity_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.ScheduledActivityOperation do
diff --git a/lib/pleroma/web/api_spec/operations/search_operation.ex b/lib/pleroma/web/api_spec/operations/search_operation.ex
index ff4fd0027..1a7e49be4 100644
--- a/lib/pleroma/web/api_spec/operations/search_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/search_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.SearchOperation do
diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex
index 802fbef3e..5d6e82f3c 100644
--- a/lib/pleroma/web/api_spec/operations/status_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/status_operation.ex
@@ -1,14 +1,18 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.StatusOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.AccountOperation
+ alias Pleroma.Web.ApiSpec.Schemas.Account
alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ alias Pleroma.Web.ApiSpec.Schemas.Attachment
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
+ alias Pleroma.Web.ApiSpec.Schemas.Emoji
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
+ alias Pleroma.Web.ApiSpec.Schemas.Poll
alias Pleroma.Web.ApiSpec.Schemas.ScheduledStatus
alias Pleroma.Web.ApiSpec.Schemas.Status
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
@@ -434,6 +438,59 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
}
end
+ def show_history_operation do
+ %Operation{
+ tags: ["Retrieve status information"],
+ summary: "Status history",
+ description: "View history of a status",
+ operationId: "StatusController.show_history",
+ security: [%{"oAuth" => ["read:statuses"]}],
+ parameters: [
+ id_param()
+ ],
+ responses: %{
+ 200 => status_history_response(),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def show_source_operation do
+ %Operation{
+ tags: ["Retrieve status information"],
+ summary: "Status source",
+ description: "View source of a status",
+ operationId: "StatusController.show_source",
+ security: [%{"oAuth" => ["read:statuses"]}],
+ parameters: [
+ id_param()
+ ],
+ responses: %{
+ 200 => status_source_response(),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def update_operation do
+ %Operation{
+ tags: ["Status actions"],
+ summary: "Update status",
+ description: "Change the content of a status",
+ operationId: "StatusController.update",
+ security: [%{"oAuth" => ["write:statuses"]}],
+ parameters: [
+ id_param()
+ ],
+ requestBody: request_body("Parameters", update_request(), required: true),
+ responses: %{
+ 200 => status_response(),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
def array_of_statuses do
%Schema{type: :array, items: Status, example: [Status.schema().example]}
end
@@ -537,6 +594,60 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
}
end
+ defp update_request do
+ %Schema{
+ title: "StatusUpdateRequest",
+ type: :object,
+ properties: %{
+ status: %Schema{
+ type: :string,
+ nullable: true,
+ description:
+ "Text content of the status. If `media_ids` is provided, this becomes optional. Attaching a `poll` is optional while `status` is provided."
+ },
+ media_ids: %Schema{
+ nullable: true,
+ type: :array,
+ items: %Schema{type: :string},
+ description: "Array of Attachment ids to be attached as media."
+ },
+ poll: poll_params(),
+ sensitive: %Schema{
+ allOf: [BooleanLike],
+ nullable: true,
+ description: "Mark status and attached media as sensitive?"
+ },
+ spoiler_text: %Schema{
+ type: :string,
+ nullable: true,
+ description:
+ "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field."
+ },
+ content_type: %Schema{
+ type: :string,
+ nullable: true,
+ description:
+ "The MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint."
+ },
+ to: %Schema{
+ type: :array,
+ nullable: true,
+ items: %Schema{type: :string},
+ description:
+ "A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply"
+ }
+ },
+ example: %{
+ "status" => "What time is it?",
+ "sensitive" => "false",
+ "poll" => %{
+ "options" => ["Cofe", "Adventure"],
+ "expires_in" => 420
+ }
+ }
+ }
+ end
+
def poll_params do
%Schema{
nullable: true,
@@ -579,6 +690,87 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
Operation.response("Status", "application/json", Status)
end
+ defp status_history_response do
+ Operation.response(
+ "Status History",
+ "application/json",
+ %Schema{
+ title: "Status history",
+ description: "Response schema for history of a status",
+ type: :array,
+ items: %Schema{
+ type: :object,
+ properties: %{
+ account: %Schema{
+ allOf: [Account],
+ description: "The account that authored this status"
+ },
+ content: %Schema{
+ type: :string,
+ format: :html,
+ description: "HTML-encoded status content"
+ },
+ sensitive: %Schema{
+ type: :boolean,
+ description: "Is this status marked as sensitive content?"
+ },
+ spoiler_text: %Schema{
+ type: :string,
+ description:
+ "Subject or summary line, below which status content is collapsed until expanded"
+ },
+ created_at: %Schema{
+ type: :string,
+ format: "date-time",
+ description: "The date when this status was created"
+ },
+ media_attachments: %Schema{
+ type: :array,
+ items: Attachment,
+ description: "Media that is attached to this status"
+ },
+ emojis: %Schema{
+ type: :array,
+ items: Emoji,
+ description: "Custom emoji to be used when rendering status content"
+ },
+ poll: %Schema{
+ allOf: [Poll],
+ nullable: true,
+ description: "The poll attached to the status"
+ }
+ }
+ }
+ }
+ )
+ end
+
+ defp status_source_response do
+ Operation.response(
+ "Status Source",
+ "application/json",
+ %Schema{
+ type: :object,
+ properties: %{
+ id: FlakeID,
+ text: %Schema{
+ type: :string,
+ description: "Raw source of status content"
+ },
+ spoiler_text: %Schema{
+ type: :string,
+ description:
+ "Subject or summary line, below which status content is collapsed until expanded"
+ },
+ content_type: %Schema{
+ type: :string,
+ description: "The content type of the source"
+ }
+ }
+ }
+ )
+ end
+
defp context do
%Schema{
title: "StatusContext",
diff --git a/lib/pleroma/web/api_spec/operations/subscription_operation.ex b/lib/pleroma/web/api_spec/operations/subscription_operation.ex
index 60a7fb3b0..c53ec2956 100644
--- a/lib/pleroma/web/api_spec/operations/subscription_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/subscription_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.SubscriptionOperation do
diff --git a/lib/pleroma/web/api_spec/operations/timeline_operation.ex b/lib/pleroma/web/api_spec/operations/timeline_operation.ex
index cae18c758..fbe3f763a 100644
--- a/lib/pleroma/web/api_spec/operations/timeline_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/timeline_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.TimelineOperation do
@@ -115,7 +115,8 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
],
operationId: "TimelineController.hashtag",
responses: %{
- 200 => Operation.response("Array of Status", "application/json", array_of_statuses())
+ 200 => Operation.response("Array of Status", "application/json", array_of_statuses()),
+ 401 => Operation.response("Error", "application/json", ApiError)
}
}
end
diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
new file mode 100644
index 000000000..084329ad7
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
@@ -0,0 +1,442 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
+ alias OpenApiSpex.Operation
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
+
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def emoji_operation do
+ %Operation{
+ tags: ["Custom emojis"],
+ summary: "List all custom emojis",
+ operationId: "UtilController.emoji",
+ parameters: [],
+ responses: %{
+ 200 =>
+ Operation.response("List", "application/json", %Schema{
+ type: :object,
+ additionalProperties: %Schema{
+ type: :object,
+ properties: %{
+ image_url: %Schema{type: :string},
+ tags: %Schema{type: :array, items: %Schema{type: :string}}
+ },
+ extensions: %{"x-additionalPropertiesName": "Emoji name"}
+ },
+ example: %{
+ "firefox" => %{
+ "image_url" => "/emoji/firefox.png",
+ "tag" => ["Fun"]
+ }
+ }
+ })
+ }
+ }
+ end
+
+ def frontend_configurations_operation do
+ %Operation{
+ tags: ["Others"],
+ summary: "Dump frontend configurations",
+ operationId: "UtilController.frontend_configurations",
+ parameters: [],
+ responses: %{
+ 200 =>
+ Operation.response("List", "application/json", %Schema{
+ type: :object,
+ additionalProperties: %Schema{
+ type: :object,
+ description:
+ "Opaque object representing the instance-wide configuration for the frontend",
+ extensions: %{"x-additionalPropertiesName": "Frontend name"}
+ }
+ })
+ }
+ }
+ end
+
+ def change_password_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Change account password",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.change_password",
+ requestBody: request_body("Parameters", change_password_request(), required: true),
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{status: %Schema{type: :string, example: "success"}}
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 403 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp change_password_request do
+ %Schema{
+ title: "ChangePasswordRequest",
+ description: "POST body for changing the account's passowrd",
+ type: :object,
+ required: [:password, :new_password, :new_password_confirmation],
+ properties: %{
+ password: %Schema{type: :string, description: "Current password"},
+ new_password: %Schema{type: :string, description: "New password"},
+ new_password_confirmation: %Schema{
+ type: :string,
+ description: "New password, confirmation"
+ }
+ }
+ }
+ end
+
+ def change_email_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Change account email",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.change_email",
+ requestBody: request_body("Parameters", change_email_request(), required: true),
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{status: %Schema{type: :string, example: "success"}}
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 403 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp change_email_request do
+ %Schema{
+ title: "ChangeEmailRequest",
+ description: "POST body for changing the account's email",
+ type: :object,
+ required: [:email, :password],
+ properties: %{
+ email: %Schema{
+ type: :string,
+ description: "New email. Set to blank to remove the user's email."
+ },
+ password: %Schema{type: :string, description: "Current password"}
+ }
+ }
+ end
+
+ def update_notificaton_settings_operation do
+ %Operation{
+ tags: ["Settings"],
+ summary: "Update Notification Settings",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.update_notificaton_settings",
+ parameters: [
+ Operation.parameter(
+ :block_from_strangers,
+ :query,
+ BooleanLike,
+ "blocks notifications from accounts you do not follow"
+ ),
+ Operation.parameter(
+ :hide_notification_contents,
+ :query,
+ BooleanLike,
+ "removes the contents of a message from the push notification"
+ )
+ ],
+ requestBody: nil,
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{status: %Schema{type: :string, example: "success"}}
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def disable_account_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Disable Account",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.disable_account",
+ parameters: [
+ Operation.parameter(:password, :query, :string, "Password")
+ ],
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{status: %Schema{type: :string, example: "success"}}
+ }),
+ 403 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def delete_account_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Delete Account",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.delete_account",
+ parameters: [
+ Operation.parameter(:password, :query, :string, "Password")
+ ],
+ requestBody: request_body("Parameters", delete_account_request(), required: false),
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{status: %Schema{type: :string, example: "success"}}
+ }),
+ 403 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def captcha_operation do
+ %Operation{
+ summary: "Get a captcha",
+ operationId: "UtilController.captcha",
+ tags: ["Others"],
+ parameters: [],
+ responses: %{
+ 200 => Operation.response("Success", "application/json", %Schema{type: :object})
+ }
+ }
+ end
+
+ def move_account_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Move account",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.move_account",
+ requestBody: request_body("Parameters", move_account_request(), required: true),
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{status: %Schema{type: :string, example: "success"}}
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 403 => Operation.response("Error", "application/json", ApiError),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp move_account_request do
+ %Schema{
+ title: "MoveAccountRequest",
+ description: "POST body for moving the account",
+ type: :object,
+ required: [:password, :target_account],
+ properties: %{
+ password: %Schema{type: :string, description: "Current password"},
+ target_account: %Schema{
+ type: :string,
+ description: "The nickname of the target account to move to"
+ }
+ }
+ }
+ end
+
+ def list_aliases_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "List account aliases",
+ security: [%{"oAuth" => ["read:accounts"]}],
+ operationId: "UtilController.list_aliases",
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{
+ aliases: %Schema{
+ type: :array,
+ items: %Schema{type: :string},
+ example: ["foo@example.org"]
+ }
+ }
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 403 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ def add_alias_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Add an alias to this account",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.add_alias",
+ requestBody: request_body("Parameters", add_alias_request(), required: true),
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{
+ status: %Schema{
+ type: :string,
+ example: "success"
+ }
+ }
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 403 => Operation.response("Error", "application/json", ApiError),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp add_alias_request do
+ %Schema{
+ title: "AddAliasRequest",
+ description: "PUT body for adding aliases",
+ type: :object,
+ required: [:alias],
+ properties: %{
+ alias: %Schema{
+ type: :string,
+ description: "The nickname of the account to add to aliases"
+ }
+ }
+ }
+ end
+
+ def delete_alias_operation do
+ %Operation{
+ tags: ["Account credentials"],
+ summary: "Delete an alias from this account",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.delete_alias",
+ requestBody: request_body("Parameters", delete_alias_request(), required: true),
+ responses: %{
+ 200 =>
+ Operation.response("Success", "application/json", %Schema{
+ type: :object,
+ properties: %{
+ status: %Schema{
+ type: :string,
+ example: "success"
+ }
+ }
+ }),
+ 400 => Operation.response("Error", "application/json", ApiError),
+ 403 => Operation.response("Error", "application/json", ApiError),
+ 404 => Operation.response("Error", "application/json", ApiError)
+ }
+ }
+ end
+
+ defp delete_alias_request do
+ %Schema{
+ title: "DeleteAliasRequest",
+ description: "PUT body for deleting aliases",
+ type: :object,
+ required: [:alias],
+ properties: %{
+ alias: %Schema{
+ type: :string,
+ description: "The nickname of the account to delete from aliases"
+ }
+ }
+ }
+ end
+
+ def healthcheck_operation do
+ %Operation{
+ tags: ["Others"],
+ summary: "Quick status check on the instance",
+ security: [%{"oAuth" => ["write:accounts"]}],
+ operationId: "UtilController.healthcheck",
+ parameters: [],
+ responses: %{
+ 200 => Operation.response("Healthy", "application/json", %Schema{type: :object}),
+ 503 =>
+ Operation.response("Disabled or Unhealthy", "application/json", %Schema{type: :object})
+ }
+ }
+ end
+
+ def remote_subscribe_operation do
+ %Operation{
+ tags: ["Remote interaction"],
+ summary: "Remote Subscribe",
+ operationId: "UtilController.remote_subscribe",
+ parameters: [],
+ responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
+ }
+ end
+
+ def remote_interaction_operation do
+ %Operation{
+ tags: ["Remote interaction"],
+ summary: "Remote interaction",
+ operationId: "UtilController.remote_interaction",
+ requestBody: request_body("Parameters", remote_interaction_request(), required: true),
+ responses: %{
+ 200 =>
+ Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
+ }
+ }
+ end
+
+ defp remote_interaction_request do
+ %Schema{
+ title: "RemoteInteractionRequest",
+ description: "POST body for remote interaction",
+ type: :object,
+ required: [:ap_id, :profile],
+ properties: %{
+ ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
+ profile: %Schema{type: :string, description: "Remote profile webfinger"}
+ }
+ }
+ end
+
+ def show_subscribe_form_operation do
+ %Operation{
+ tags: ["Remote interaction"],
+ summary: "Show remote subscribe form",
+ operationId: "UtilController.show_subscribe_form",
+ parameters: [],
+ responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
+ }
+ end
+
+ defp delete_account_request do
+ %Schema{
+ title: "AccountDeleteRequest",
+ description: "POST body for deleting one's own account",
+ type: :object,
+ properties: %{
+ password: %Schema{
+ type: :string,
+ description: "The user's own password for confirmation.",
+ format: :password
+ }
+ },
+ example: %{
+ "password" => "prettyp0ony1313"
+ }
+ }
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/user_import_operation.ex b/lib/pleroma/web/api_spec/operations/user_import_operation.ex
index 6292e2004..e99e6e648 100644
--- a/lib/pleroma/web/api_spec/operations/user_import_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/user_import_operation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.UserImportOperation do
@@ -23,6 +23,7 @@ defmodule Pleroma.Web.ApiSpec.UserImportOperation do
requestBody: request_body("Parameters", import_request(), required: true),
responses: %{
200 => ok_response(),
+ 403 => Operation.response("Error", "application/json", ApiError),
500 => Operation.response("Error", "application/json", ApiError)
},
security: [%{"oAuth" => ["write:follow"]}]
diff --git a/lib/pleroma/web/api_spec/render_error.ex b/lib/pleroma/web/api_spec/render_error.ex
index e501a6be4..3539af6e4 100644
--- a/lib/pleroma/web/api_spec/render_error.ex
+++ b/lib/pleroma/web/api_spec/render_error.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.RenderError do
diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex
index bd7143ab9..8aeb821a8 100644
--- a/lib/pleroma/web/api_spec/schemas/account.ex
+++ b/lib/pleroma/web/api_spec/schemas/account.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Account do
@@ -33,6 +33,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
header: %Schema{type: :string, format: :uri},
id: FlakeID,
locked: %Schema{type: :boolean},
+ mute_expires_at: %Schema{type: :string, format: "date-time", nullable: true},
note: %Schema{type: :string, format: :html},
statuses_count: %Schema{type: :integer},
url: %Schema{type: :string, format: :uri},
@@ -47,12 +48,14 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
description: "whether the user allows automatically follow moved following accounts"
},
background_image: %Schema{type: :string, nullable: true, format: :uri},
+ birthday: %Schema{type: :string, nullable: true, format: :date},
chat_token: %Schema{type: :string},
is_confirmed: %Schema{
type: :boolean,
description:
"whether the user account is waiting on email confirmation to be activated"
},
+ show_birthday: %Schema{type: :boolean, nullable: true},
hide_favorites: %Schema{type: :boolean},
hide_followers_count: %Schema{
type: :boolean,
@@ -194,13 +197,16 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
"id" => "9tKi3esbG7OQgZ2920",
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"showing_reblogs" => true,
- "subscribing" => false
+ "subscribing" => false,
+ "notifying" => false
},
"settings_store" => %{
"pleroma-fe" => %{}
- }
+ },
+ "birthday" => "2001-02-12"
},
"source" => %{
"fields" => [],
diff --git a/lib/pleroma/web/api_spec/schemas/account_field.ex b/lib/pleroma/web/api_spec/schemas/account_field.ex
index 7c4f94001..93ba1b5a5 100644
--- a/lib/pleroma/web/api_spec/schemas/account_field.ex
+++ b/lib/pleroma/web/api_spec/schemas/account_field.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.AccountField do
diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex
index 16b73ebb4..68219a099 100644
--- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex
+++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
@@ -22,9 +22,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
id: FlakeID,
muting: %Schema{type: :boolean},
muting_notifications: %Schema{type: :boolean},
+ note: %Schema{type: :string},
requested: %Schema{type: :boolean},
showing_reblogs: %Schema{type: :boolean},
- subscribing: %Schema{type: :boolean}
+ subscribing: %Schema{type: :boolean},
+ notifying: %Schema{type: :boolean}
},
example: %{
"blocked_by" => false,
@@ -36,9 +38,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
"id" => "9tKi3esbG7OQgZ2920",
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"showing_reblogs" => true,
- "subscribing" => false
+ "subscribing" => false,
+ "notifying" => false
}
})
end
diff --git a/lib/pleroma/web/api_spec/schemas/actor_type.ex b/lib/pleroma/web/api_spec/schemas/actor_type.ex
index 1336640a1..13b6b476b 100644
--- a/lib/pleroma/web/api_spec/schemas/actor_type.ex
+++ b/lib/pleroma/web/api_spec/schemas/actor_type.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.ActorType do
diff --git a/lib/pleroma/web/api_spec/schemas/announcement.ex b/lib/pleroma/web/api_spec/schemas/announcement.ex
new file mode 100644
index 000000000..67d129ef6
--- /dev/null
+++ b/lib/pleroma/web/api_spec/schemas/announcement.ex
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Schemas.Announcement do
+ alias OpenApiSpex.Schema
+ alias Pleroma.Web.ApiSpec.Schemas.FlakeID
+
+ require OpenApiSpex
+
+ OpenApiSpex.schema(%{
+ title: "Announcement",
+ description: "Response schema for an announcement",
+ type: :object,
+ properties: %{
+ id: FlakeID,
+ content: %Schema{type: :string},
+ starts_at: %Schema{
+ type: :string,
+ format: "date-time",
+ nullable: true
+ },
+ ends_at: %Schema{
+ type: :string,
+ format: "date-time",
+ nullable: true
+ },
+ all_day: %Schema{type: :boolean},
+ published_at: %Schema{type: :string, format: "date-time"},
+ updated_at: %Schema{type: :string, format: "date-time"},
+ read: %Schema{type: :boolean},
+ mentions: %Schema{type: :array},
+ statuses: %Schema{type: :array},
+ tags: %Schema{type: :array},
+ emojis: %Schema{type: :array},
+ reactions: %Schema{type: :array},
+ pleroma: %Schema{
+ type: :object,
+ properties: %{
+ raw_content: %Schema{type: :string}
+ }
+ }
+ }
+ })
+end
diff --git a/lib/pleroma/web/api_spec/schemas/api_error.ex b/lib/pleroma/web/api_spec/schemas/api_error.ex
index 0d6d0b75c..58a710761 100644
--- a/lib/pleroma/web/api_spec/schemas/api_error.ex
+++ b/lib/pleroma/web/api_spec/schemas/api_error.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.ApiError do
diff --git a/lib/pleroma/web/api_spec/schemas/app.ex b/lib/pleroma/web/api_spec/schemas/app.ex
new file mode 100644
index 000000000..742413b33
--- /dev/null
+++ b/lib/pleroma/web/api_spec/schemas/app.ex
@@ -0,0 +1,33 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Schemas.App do
+ alias OpenApiSpex.Schema
+
+ require OpenApiSpex
+
+ OpenApiSpex.schema(%{
+ title: "App",
+ description: "Response schema for an app",
+ type: :object,
+ properties: %{
+ id: %Schema{type: :string},
+ name: %Schema{type: :string},
+ client_id: %Schema{type: :string},
+ client_secret: %Schema{type: :string},
+ redirect_uri: %Schema{type: :string},
+ vapid_key: %Schema{type: :string},
+ website: %Schema{type: :string, nullable: true}
+ },
+ example: %{
+ "id" => "123",
+ "name" => "My App",
+ "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
+ "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
+ "vapid_key" =>
+ "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
+ "website" => "https://myapp.com/"
+ }
+ })
+end
diff --git a/lib/pleroma/web/api_spec/schemas/attachment.ex b/lib/pleroma/web/api_spec/schemas/attachment.ex
index ca3659c93..48634a14f 100644
--- a/lib/pleroma/web/api_spec/schemas/attachment.ex
+++ b/lib/pleroma/web/api_spec/schemas/attachment.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do
diff --git a/lib/pleroma/web/api_spec/schemas/boolean_like.ex b/lib/pleroma/web/api_spec/schemas/boolean_like.ex
index 778158f66..14f728eef 100644
--- a/lib/pleroma/web/api_spec/schemas/boolean_like.ex
+++ b/lib/pleroma/web/api_spec/schemas/boolean_like.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do
@@ -34,7 +34,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do
def cast(%Cast{value: value} = context) do
context
- |> Map.put(:value, Pleroma.Web.ControllerHelper.truthy_param?(value))
+ |> Map.put(:value, Pleroma.Web.Utils.Params.truthy_param?(value))
|> Cast.ok()
end
end
diff --git a/lib/pleroma/web/api_spec/schemas/chat.ex b/lib/pleroma/web/api_spec/schemas/chat.ex
index 4afed910d..a07d12865 100644
--- a/lib/pleroma/web/api_spec/schemas/chat.ex
+++ b/lib/pleroma/web/api_spec/schemas/chat.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Chat do
diff --git a/lib/pleroma/web/api_spec/schemas/chat_message.ex b/lib/pleroma/web/api_spec/schemas/chat_message.ex
index 348fe95f8..57f7890e5 100644
--- a/lib/pleroma/web/api_spec/schemas/chat_message.ex
+++ b/lib/pleroma/web/api_spec/schemas/chat_message.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.ChatMessage do
diff --git a/lib/pleroma/web/api_spec/schemas/conversation.ex b/lib/pleroma/web/api_spec/schemas/conversation.ex
index 7c609965f..f00a9733f 100644
--- a/lib/pleroma/web/api_spec/schemas/conversation.ex
+++ b/lib/pleroma/web/api_spec/schemas/conversation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Conversation do
diff --git a/lib/pleroma/web/api_spec/schemas/emoji.ex b/lib/pleroma/web/api_spec/schemas/emoji.ex
index ceb3c7186..936bbe1da 100644
--- a/lib/pleroma/web/api_spec/schemas/emoji.ex
+++ b/lib/pleroma/web/api_spec/schemas/emoji.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Emoji do
diff --git a/lib/pleroma/web/api_spec/schemas/flake_id.ex b/lib/pleroma/web/api_spec/schemas/flake_id.ex
index 45314d53a..4c3ec01e7 100644
--- a/lib/pleroma/web/api_spec/schemas/flake_id.ex
+++ b/lib/pleroma/web/api_spec/schemas/flake_id.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.FlakeID do
diff --git a/lib/pleroma/web/api_spec/schemas/list.ex b/lib/pleroma/web/api_spec/schemas/list.ex
index 90f5ec987..e57de7917 100644
--- a/lib/pleroma/web/api_spec/schemas/list.ex
+++ b/lib/pleroma/web/api_spec/schemas/list.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.List do
diff --git a/lib/pleroma/web/api_spec/schemas/poll.ex b/lib/pleroma/web/api_spec/schemas/poll.ex
index 943ad8bd4..91570582b 100644
--- a/lib/pleroma/web/api_spec/schemas/poll.ex
+++ b/lib/pleroma/web/api_spec/schemas/poll.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
diff --git a/lib/pleroma/web/api_spec/schemas/push_subscription.ex b/lib/pleroma/web/api_spec/schemas/push_subscription.ex
index 20fe9f304..a5466630b 100644
--- a/lib/pleroma/web/api_spec/schemas/push_subscription.ex
+++ b/lib/pleroma/web/api_spec/schemas/push_subscription.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.PushSubscription do
diff --git a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex
index 607586e32..a1acda1e8 100644
--- a/lib/pleroma/web/api_spec/schemas/scheduled_status.ex
+++ b/lib/pleroma/web/api_spec/schemas/scheduled_status.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.ScheduledStatus do
diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex
index 3d042dc19..bc29cf4a6 100644
--- a/lib/pleroma/web/api_spec/schemas/status.ex
+++ b/lib/pleroma/web/api_spec/schemas/status.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Status do
@@ -73,6 +73,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
format: "date-time",
description: "The date when this status was created"
},
+ edited_at: %Schema{
+ type: :string,
+ format: "date-time",
+ nullable: true,
+ description: "The date when this status was last edited"
+ },
emojis: %Schema{
type: :array,
items: Emoji,
@@ -138,13 +144,23 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
properties: %{
content: %Schema{
type: :object,
- additionalProperties: %Schema{type: :string},
+ additionalProperties: %Schema{
+ type: :string,
+ description: "Alternate representation in the MIME type specified",
+ extensions: %{"x-additionalPropertiesName": "MIME type"}
+ },
description:
"A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`"
},
+ context: %Schema{
+ type: :string,
+ description: "The thread identifier the status is associated with"
+ },
conversation_id: %Schema{
type: :integer,
- description: "The ID of the AP context the status is associated with (if any)"
+ deprecated: true,
+ description:
+ "The ID of the AP context the status is associated with (if any); deprecated, please use `context` instead"
},
direct_conversation_id: %Schema{
type: :integer,
@@ -183,7 +199,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
},
spoiler_text: %Schema{
type: :object,
- additionalProperties: %Schema{type: :string},
+ additionalProperties: %Schema{
+ type: :string,
+ description: "Alternate representation in the MIME type specified",
+ extensions: %{"x-additionalPropertiesName": "MIME type"}
+ },
description:
"A map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`."
},
@@ -282,9 +302,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
"id" => "9toJCsKN7SmSf3aj5c",
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"showing_reblogs" => true,
- "subscribing" => false
+ "subscribing" => false,
+ "notifying" => false
},
"skip_thread_containment" => false,
"tags" => []
@@ -317,6 +339,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
"pinned" => false,
"pleroma" => %{
"content" => %{"text/plain" => "foobar"},
+ "context" => "http://localhost:4001/objects/8b4c0c80-6a37-4d2a-b1b9-05a19e3875aa",
"conversation_id" => 345_972,
"direct_conversation_id" => nil,
"emoji_reactions" => [],
diff --git a/lib/pleroma/web/api_spec/schemas/tag.ex b/lib/pleroma/web/api_spec/schemas/tag.ex
index 657b675e5..66bf0ca71 100644
--- a/lib/pleroma/web/api_spec/schemas/tag.ex
+++ b/lib/pleroma/web/api_spec/schemas/tag.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.Tag do
diff --git a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex
index 25a08a0b2..ecd247ba4 100644
--- a/lib/pleroma/web/api_spec/schemas/visibility_scope.ex
+++ b/lib/pleroma/web/api_spec/schemas/visibility_scope.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do
diff --git a/lib/pleroma/web/api_spec/scopes/compiler.ex b/lib/pleroma/web/api_spec/scopes/compiler.ex
new file mode 100644
index 000000000..162edc9a3
--- /dev/null
+++ b/lib/pleroma/web/api_spec/scopes/compiler.ex
@@ -0,0 +1,82 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Scopes.Compiler do
+ defmacro __before_compile__(_env) do
+ strings = __MODULE__.extract_all_scopes()
+
+ quote do
+ def placeholder do
+ unquote do
+ Enum.map(
+ strings,
+ fn string ->
+ quote do
+ Pleroma.Web.Gettext.dgettext_noop(
+ "oauth_scopes",
+ unquote(string)
+ )
+ end
+ end
+ )
+ end
+ end
+ end
+ end
+
+ def extract_all_scopes do
+ extract_all_scopes_from(Pleroma.Web.ApiSpec.spec())
+ end
+
+ def extract_all_scopes_from(specs) do
+ specs.paths
+ |> Enum.reduce([], fn
+ {_path, %{} = path_item}, acc ->
+ extract_routes(path_item)
+ |> Enum.flat_map(fn operation -> process_operation(operation) end)
+ |> Kernel.++(acc)
+
+ {_, _}, acc ->
+ acc
+ end)
+ |> Enum.uniq()
+ end
+
+ defp extract_routes(path_item) do
+ path_item
+ |> Map.from_struct()
+ |> Enum.map(fn {_method, path_item} -> path_item end)
+ |> Enum.filter(fn
+ %OpenApiSpex.Operation{} = _operation -> true
+ _ -> false
+ end)
+ end
+
+ defp process_operation(operation) do
+ operation.security
+ |> Kernel.||([])
+ |> Enum.flat_map(fn
+ %{"oAuth" => scopes} -> process_scopes(scopes)
+ _ -> []
+ end)
+ end
+
+ defp process_scopes(scopes) do
+ scopes
+ |> Enum.flat_map(fn scope ->
+ process_scope(scope)
+ end)
+ end
+
+ def process_scope(scope) do
+ hierarchy = String.split(scope, ":")
+
+ {_, list} =
+ Enum.reduce(hierarchy, {"", []}, fn comp, {cur, list} ->
+ {cur <> comp <> ":", [cur <> comp | list]}
+ end)
+
+ list
+ end
+end
diff --git a/lib/pleroma/web/api_spec/scopes/translator.ex b/lib/pleroma/web/api_spec/scopes/translator.ex
new file mode 100644
index 000000000..54eea3593
--- /dev/null
+++ b/lib/pleroma/web/api_spec/scopes/translator.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Scopes.Translator do
+ require Pleroma.Web.ApiSpec.Scopes.Compiler
+ require Pleroma.Web.Gettext
+
+ @before_compile Pleroma.Web.ApiSpec.Scopes.Compiler
+end
diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex
index 84741ee11..a0bd154db 100644
--- a/lib/pleroma/web/auth/authenticator.ex
+++ b/lib/pleroma/web/auth/authenticator.ex
@@ -1,70 +1,13 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Auth.Authenticator do
- alias Pleroma.Registration
- alias Pleroma.User
-
- def implementation do
- Pleroma.Config.get(
- Pleroma.Web.Auth.Authenticator,
- Pleroma.Web.Auth.PleromaAuthenticator
- )
- end
-
- @callback get_user(Plug.Conn.t()) :: {:ok, User.t()} | {:error, any()}
- def get_user(plug), do: implementation().get_user(plug)
-
- @callback create_from_registration(Plug.Conn.t(), Registration.t()) ::
+ @callback get_user(Plug.Conn.t()) :: {:ok, user :: struct()} | {:error, any()}
+ @callback create_from_registration(Plug.Conn.t(), registration :: struct()) ::
{:ok, User.t()} | {:error, any()}
- def create_from_registration(plug, registration),
- do: implementation().create_from_registration(plug, registration)
-
- @callback get_registration(Plug.Conn.t()) :: {:ok, Registration.t()} | {:error, any()}
- def get_registration(plug), do: implementation().get_registration(plug)
-
+ @callback get_registration(Plug.Conn.t()) :: {:ok, registration :: struct()} | {:error, any()}
@callback handle_error(Plug.Conn.t(), any()) :: any()
- def handle_error(plug, error),
- do: implementation().handle_error(plug, error)
-
@callback auth_template() :: String.t() | nil
- def auth_template do
- # Note: `config :pleroma, :auth_template, "..."` support is deprecated
- implementation().auth_template() ||
- Pleroma.Config.get([:auth, :auth_template], Pleroma.Config.get(:auth_template)) ||
- "show.html"
- end
-
@callback oauth_consumer_template() :: String.t() | nil
- def oauth_consumer_template do
- implementation().oauth_consumer_template() ||
- Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
- end
-
- @doc "Gets user by nickname or email for auth."
- @spec fetch_user(String.t()) :: User.t() | nil
- def fetch_user(name) do
- User.get_by_nickname_or_email(name)
- end
-
- # Gets name and password from conn
- #
- @spec fetch_credentials(Plug.Conn.t() | map()) ::
- {:ok, {name :: any, password :: any}} | {:error, :invalid_credentials}
- def fetch_credentials(%Plug.Conn{params: params} = _),
- do: fetch_credentials(params)
-
- def fetch_credentials(params) do
- case params do
- %{"authorization" => %{"name" => name, "password" => password}} ->
- {:ok, {name, password}}
-
- %{"grant_type" => "password", "username" => name, "password" => password} ->
- {:ok, {name, password}}
-
- _ ->
- {:error, :invalid_credentials}
- end
- end
end
diff --git a/lib/pleroma/web/auth/helpers.ex b/lib/pleroma/web/auth/helpers.ex
new file mode 100644
index 000000000..02e1f39ab
--- /dev/null
+++ b/lib/pleroma/web/auth/helpers.ex
@@ -0,0 +1,33 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.Helpers do
+ alias Pleroma.User
+
+ @doc "Gets user by nickname or email for auth."
+ @spec fetch_user(String.t()) :: User.t() | nil
+ def fetch_user(name) do
+ User.get_by_nickname_or_email(name)
+ end
+
+ # Gets name and password from conn
+ #
+ @spec fetch_credentials(Plug.Conn.t() | map()) ::
+ {:ok, {name :: any, password :: any}} | {:error, :invalid_credentials}
+ def fetch_credentials(%Plug.Conn{params: params} = _),
+ do: fetch_credentials(params)
+
+ def fetch_credentials(params) do
+ case params do
+ %{"authorization" => %{"name" => name, "password" => password}} ->
+ {:ok, {name, password}}
+
+ %{"grant_type" => "password", "username" => name, "password" => password} ->
+ {:ok, {name, password}}
+
+ _ ->
+ {:error, :invalid_credentials}
+ end
+ end
+end
diff --git a/lib/pleroma/web/auth/ldap_authenticator.ex b/lib/pleroma/web/auth/ldap_authenticator.ex
index 17e08a2a6..e8cd4491c 100644
--- a/lib/pleroma/web/auth/ldap_authenticator.ex
+++ b/lib/pleroma/web/auth/ldap_authenticator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Auth.LDAPAuthenticator do
@@ -7,8 +7,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
require Logger
- import Pleroma.Web.Auth.Authenticator,
- only: [fetch_credentials: 1, fetch_user: 1]
+ import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
@behaviour Pleroma.Web.Auth.Authenticator
@base Pleroma.Web.Auth.PleromaAuthenticator
diff --git a/lib/pleroma/web/auth/pleroma_authenticator.ex b/lib/pleroma/web/auth/pleroma_authenticator.ex
index 401f23c9f..09a58eb66 100644
--- a/lib/pleroma/web/auth/pleroma_authenticator.ex
+++ b/lib/pleroma/web/auth/pleroma_authenticator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Auth.PleromaAuthenticator do
@@ -8,8 +8,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
alias Pleroma.User
alias Pleroma.Web.Plugs.AuthenticationPlug
- import Pleroma.Web.Auth.Authenticator,
- only: [fetch_credentials: 1, fetch_user: 1]
+ import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
@behaviour Pleroma.Web.Auth.Authenticator
diff --git a/lib/pleroma/web/auth/totp_authenticator.ex b/lib/pleroma/web/auth/totp_authenticator.ex
index 5947cd8c9..4be3641fd 100644
--- a/lib/pleroma/web/auth/totp_authenticator.ex
+++ b/lib/pleroma/web/auth/totp_authenticator.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Auth.TOTPAuthenticator do
diff --git a/lib/pleroma/web/auth/wrapper_authenticator.ex b/lib/pleroma/web/auth/wrapper_authenticator.ex
new file mode 100644
index 000000000..a077cfa41
--- /dev/null
+++ b/lib/pleroma/web/auth/wrapper_authenticator.ex
@@ -0,0 +1,42 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.WrapperAuthenticator do
+ @behaviour Pleroma.Web.Auth.Authenticator
+
+ defp implementation do
+ Pleroma.Config.get(
+ Pleroma.Web.Auth.Authenticator,
+ Pleroma.Web.Auth.PleromaAuthenticator
+ )
+ end
+
+ @impl true
+ def get_user(plug), do: implementation().get_user(plug)
+
+ @impl true
+ def create_from_registration(plug, registration),
+ do: implementation().create_from_registration(plug, registration)
+
+ @impl true
+ def get_registration(plug), do: implementation().get_registration(plug)
+
+ @impl true
+ def handle_error(plug, error),
+ do: implementation().handle_error(plug, error)
+
+ @impl true
+ def auth_template do
+ # Note: `config :pleroma, :auth_template, "..."` support is deprecated
+ implementation().auth_template() ||
+ Pleroma.Config.get([:auth, :auth_template], Pleroma.Config.get(:auth_template)) ||
+ "show.html"
+ end
+
+ @impl true
+ def oauth_consumer_template do
+ implementation().oauth_consumer_template() ||
+ Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
+ end
+end
diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex
index 1c09b6768..0f61b80c3 100644
--- a/lib/pleroma/web/channels/user_socket.ex
+++ b/lib/pleroma/web/channels/user_socket.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.UserSocket do
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.UserSocket do
## Channels
# channel "room:*", Pleroma.Web.RoomChannel
- channel("chat:*", Pleroma.Web.ChatChannel)
+ channel("chat:*", Pleroma.Web.ShoutChannel)
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.UserSocket do
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
def connect(%{"token" => token}, socket) do
- with true <- Pleroma.Config.get([:chat, :enabled]),
+ with true <- Pleroma.Config.get([:shout, :enabled]),
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
%User{} = user <- Pleroma.User.get_cached_by_id(user_id) do
{:ok, assign(socket, :user_name, user.nickname)}
diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex
index 1b5f8491e..65d08de49 100644
--- a/lib/pleroma/web/common_api.ex
+++ b/lib/pleroma/web/common_api.ex
@@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Activity
alias Pleroma.Conversation.Participation
alias Pleroma.Formatter
+ alias Pleroma.ModerationLog
alias Pleroma.Object
alias Pleroma.ThreadMute
alias Pleroma.User
@@ -117,7 +118,8 @@ defmodule Pleroma.Web.CommonAPI do
def unfollow(follower, unfollowed) do
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
- {:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do
+ {:ok, _subscription} <- User.unsubscribe(follower, unfollowed),
+ {:ok, _endorsement} <- User.unendorse(follower, unfollowed) do
{:ok, follower}
end
end
@@ -140,12 +142,27 @@ defmodule Pleroma.Web.CommonAPI do
def delete(activity_id, user) do
with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <-
- {:find_activity, Activity.get_by_id(activity_id)},
+ {:find_activity, Activity.get_by_id(activity_id, filter: [])},
{_, %Object{} = object, _} <-
{:find_object, Object.normalize(activity, fetch: false), activity},
- true <- User.superuser?(user) || user.ap_id == object.data["actor"],
+ true <- User.privileged?(user, :messages_delete) || user.ap_id == object.data["actor"],
{:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
{:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
+ if User.privileged?(user, :messages_delete) and user.ap_id != object.data["actor"] do
+ action =
+ if object.data["type"] == "ChatMessage" do
+ "chat_message_delete"
+ else
+ "status_delete"
+ end
+
+ ModerationLog.insert_log(%{
+ action: action,
+ actor: user,
+ subject_id: activity_id
+ })
+ end
+
{:ok, delete}
else
{:find_activity, _} ->
@@ -401,6 +418,41 @@ defmodule Pleroma.Web.CommonAPI do
end
end
+ def update(user, orig_activity, changes) do
+ with orig_object <- Object.normalize(orig_activity),
+ {:ok, new_object} <- make_update_data(user, orig_object, changes),
+ {:ok, update_data, _} <- Builder.update(user, new_object),
+ {:ok, update, _} <- Pipeline.common_pipeline(update_data, local: true) do
+ {:ok, update}
+ else
+ _ -> {:error, nil}
+ end
+ end
+
+ defp make_update_data(user, orig_object, changes) do
+ kept_params = %{
+ visibility: Visibility.get_visibility(orig_object),
+ in_reply_to_id:
+ with replied_id when is_binary(replied_id) <- orig_object.data["inReplyTo"],
+ %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(replied_id) do
+ activity_id
+ else
+ _ -> nil
+ end
+ }
+
+ params = Map.merge(changes, kept_params)
+
+ with {:ok, draft} <- ActivityDraft.create(user, params) do
+ change =
+ Object.Updater.make_update_object_data(orig_object.data, draft.object, Utils.make_date())
+
+ {:ok, change}
+ else
+ _ -> {:error, nil}
+ end
+ end
+
@spec pin(String.t(), User.t()) :: {:ok, Activity.t()} | {:error, term()}
def pin(id, %User{} = user) do
with %Activity{} = activity <- create_activity_by_id(id),
@@ -487,9 +539,7 @@ defmodule Pleroma.Web.CommonAPI do
else
{what, result} = error ->
Logger.warn(
- "CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{
- activity_id
- }"
+ "CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{activity_id}"
)
{:error, error}
diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex
index 80a9fa7bb..9af635da8 100644
--- a/lib/pleroma/web/common_api/activity_draft.ex
+++ b/lib/pleroma/web/common_api/activity_draft.ex
@@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.ActivityDraft do
alias Pleroma.Activity
alias Pleroma.Conversation.Participation
alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils
@@ -111,7 +112,12 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
defp attachments(%{params: params} = draft) do
attachments = Utils.attachments_from_ids(params)
- %__MODULE__{draft | attachments: attachments}
+ draft = %__MODULE__{draft | attachments: attachments}
+
+ case Utils.validate_attachments_count(attachments) do
+ :ok -> draft
+ {:error, message} -> add_error(draft, message)
+ end
end
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft
@@ -213,17 +219,22 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
emoji = Map.merge(emoji, summary_emoji)
+ {:ok, note_data, _meta} = Builder.note(draft)
+
object =
- Utils.make_note_data(draft)
+ note_data
|> Map.put("emoji", emoji)
- |> Map.put("source", draft.status)
+ |> Map.put("source", %{
+ "content" => draft.status,
+ "mediaType" => Utils.get_content_type(draft.params[:content_type])
+ })
|> Map.put("generator", draft.params[:generator])
%__MODULE__{draft | object: object}
end
defp preview?(draft) do
- preview? = Pleroma.Web.ControllerHelper.truthy_param?(draft.params[:preview])
+ preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
%__MODULE__{draft | preview?: preview?}
end
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 4e6a3feb0..a93c97e1e 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -1,10 +1,9 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.Utils do
import Pleroma.Web.Gettext
- import Pleroma.Web.ControllerHelper, only: [truthy_param?: 1]
alias Calendar.Strftime
alias Pleroma.Activity
@@ -19,6 +18,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.Web.CommonAPI.ActivityDraft
alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Plugs.AuthenticationPlug
+ alias Pleroma.Web.Utils.Params
require Logger
require Pleroma.Constants
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def attachments_from_ids_no_descs(ids) do
Enum.map(ids, fn media_id ->
- case Repo.get(Object, media_id) do
+ case get_attachment(media_id) do
%Object{data: data} -> data
_ -> nil
end
@@ -51,13 +51,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do
{_, descs} = Jason.decode(descs_str)
Enum.map(ids, fn media_id ->
- with %Object{data: data} <- Repo.get(Object, media_id) do
+ with %Object{data: data} <- get_attachment(media_id) do
Map.put(data, "name", descs[media_id])
end
end)
|> Enum.reject(&is_nil/1)
end
+ defp get_attachment(media_id) do
+ Repo.get(Object, media_id)
+ end
+
@spec get_to_and_cc(ActivityDraft.t()) :: {list(String.t()), list(String.t())}
def get_to_and_cc(%{in_reply_to_conversation: %Participation{} = participation}) do
@@ -69,7 +73,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
to =
case visibility do
"public" -> [Pleroma.Constants.as_public() | draft.mentions]
- "local" -> [Pleroma.Constants.as_local_public() | draft.mentions]
+ "local" -> [Utils.as_local_public() | draft.mentions]
end
cc = [draft.user.follower_address]
@@ -141,6 +145,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
when is_list(options) do
limits = Config.get([:instance, :poll_limits])
+ options = options |> Enum.uniq()
+
with :ok <- validate_poll_expiration(expires_in, limits),
:ok <- validate_poll_options_amount(options, limits),
:ok <- validate_poll_options_length(options, limits) do
@@ -160,7 +166,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> DateTime.add(expires_in)
|> DateTime.to_iso8601()
- key = if truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf"
+ key = if Params.truthy_param?(data.poll[:multiple]), do: "anyOf", else: "oneOf"
poll = %{"type" => "Question", key => option_notes, "closed" => end_time}
{:ok, {poll, emoji}}
@@ -176,10 +182,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end
defp validate_poll_options_amount(options, %{max_options: max_options}) do
- if Enum.count(options) > max_options do
- {:error, "Poll can't contain more than #{max_options} options"}
- else
- :ok
+ cond do
+ Enum.count(options) < 2 ->
+ {:error, "Poll must contain at least 2 options"}
+
+ Enum.count(options) > max_options ->
+ {:error, "Poll can't contain more than #{max_options} options"}
+
+ true ->
+ :ok
end
end
@@ -203,7 +214,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
attachment_links =
draft.params
|> Map.get("attachment_links", Config.get([:instance, :attachment_links]))
- |> truthy_param?()
+ |> Params.truthy_param?()
content_type = get_content_type(draft.params[:content_type])
@@ -219,7 +230,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> maybe_add_attachments(draft.attachments, attachment_links)
end
- defp get_content_type(content_type) do
+ def get_content_type(content_type) do
if Enum.member?(Config.get([:instance, :allowed_post_formats]), content_type) do
content_type
else
@@ -286,38 +297,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def format_input(text, "text/markdown", options) do
text
|> Formatter.mentions_escape(options)
- |> Earmark.as_html!(%Earmark.Options{renderer: Pleroma.EarmarkRenderer})
+ |> Formatter.markdown_to_html()
|> Formatter.linkify(options)
|> Formatter.html_escape("text/html")
end
- def make_note_data(%ActivityDraft{} = draft) do
- %{
- "type" => "Note",
- "to" => draft.to,
- "cc" => draft.cc,
- "content" => draft.content_html,
- "summary" => draft.summary,
- "sensitive" => draft.sensitive,
- "context" => draft.context,
- "attachment" => draft.attachments,
- "actor" => draft.user.ap_id,
- "tag" => Keyword.values(draft.tags) |> Enum.uniq()
- }
- |> add_in_reply_to(draft.in_reply_to)
- |> Map.merge(draft.extra)
- end
-
- defp add_in_reply_to(object, nil), do: object
-
- defp add_in_reply_to(object, in_reply_to) do
- with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do
- Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
- else
- _ -> object
- end
- end
-
def format_naive_asctime(date) do
date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
end
@@ -412,19 +396,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
- # Do not notify subscribers if author is making a reply
- def maybe_notify_subscribers(recipients, %Activity{
- object: %Object{data: %{"inReplyTo" => _ap_id}}
- }) do
- recipients
- end
-
def maybe_notify_subscribers(
recipients,
- %Activity{data: %{"actor" => actor, "type" => type}} = activity
- )
- when type == "Create" do
- with %User{} = user <- User.get_cached_by_ap_id(actor) do
+ %Activity{data: %{"actor" => actor, "type" => "Create"}} = activity
+ ) do
+ # Do not notify subscribers if author is making a reply
+ with %Object{data: object} <- Object.normalize(activity, fetch: false),
+ nil <- object["inReplyTo"],
+ %User{} = user <- User.get_cached_by_ap_id(actor) do
subscriber_ids =
user
|> User.subscriber_users()
@@ -481,35 +460,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def get_report_statuses(_, _), do: {:ok, nil}
- # DEPRECATED mostly, context objects are now created at insertion time.
- def context_to_conversation_id(context) do
- with %Object{id: id} <- Object.get_cached_by_ap_id(context) do
- id
- else
- _e ->
- changeset = Object.context_mapping(context)
-
- case Repo.insert(changeset) do
- {:ok, %{id: id}} ->
- id
-
- # This should be solved by an upsert, but it seems ecto
- # has problems accessing the constraint inside the jsonb.
- {:error, _} ->
- Object.get_cached_by_ap_id(context).id
- end
- end
- end
-
- def conversation_id_to_context(id) do
- with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do
- context
- else
- _e ->
- {:error, dgettext("errors", "No such conversation")}
- end
- end
-
def validate_character_limit("" = _full_payload, [] = _attachments) do
{:error, dgettext("errors", "Cannot post an empty status without attachments")}
end
@@ -524,4 +474,19 @@ defmodule Pleroma.Web.CommonAPI.Utils do
{:error, dgettext("errors", "The status is over the character limit")}
end
end
+
+ def validate_attachments_count([] = _attachments) do
+ :ok
+ end
+
+ def validate_attachments_count(attachments) do
+ limit = Config.get([:instance, :max_media_attachments])
+ count = length(attachments)
+
+ if count <= limit do
+ :ok
+ else
+ {:error, dgettext("errors", "Too many attachments")}
+ end
+ end
end
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex
index 61d65e7a3..0c7fc17f4 100644
--- a/lib/pleroma/web/controller_helper.ex
+++ b/lib/pleroma/web/controller_helper.ex
@@ -1,22 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ControllerHelper do
use Pleroma.Web, :controller
alias Pleroma.Pagination
-
- # As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
- @falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
-
- def explicitly_falsy_param?(value), do: value in @falsy_param_values
-
- # Note: `nil` and `""` are considered falsy values in Pleroma
- def falsy_param?(value),
- do: explicitly_falsy_param?(value) or value in [nil, ""]
-
- def truthy_param?(value), do: not falsy_param?(value)
+ alias Pleroma.Web.Utils.Params
def json_response(conn, status, _) when status in [204, :no_content] do
conn
@@ -123,6 +113,6 @@ defmodule Pleroma.Web.ControllerHelper do
# To do once OpenAPI transition mess is over: just `truthy_param?(params[:with_relationships])`
params
|> Map.get(:with_relationships, params["with_relationships"])
- |> truthy_param?()
+ |> Params.truthy_param?()
end
end
diff --git a/lib/pleroma/web/embed_controller.ex b/lib/pleroma/web/embed_controller.ex
index c7912bb1f..8b9f0a051 100644
--- a/lib/pleroma/web/embed_controller.ex
+++ b/lib/pleroma/web/embed_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.EmbedController do
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 8e274de88..d8d40cceb 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Endpoint do
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.Endpoint do
alias Pleroma.Config
socket("/socket", Pleroma.Web.UserSocket)
+ socket("/live", Phoenix.LiveView.Socket)
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
diff --git a/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex b/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex
index f86d6b52b..6176f3d90 100644
--- a/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex
+++ b/lib/pleroma/web/fallback/legacy_pleroma_api_rerouter_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Fallback.LegacyPleromaApiRerouterPlug do
diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex
index 5fca290e5..1a86f7a53 100644
--- a/lib/pleroma/web/fallback/redirect_controller.ex
+++ b/lib/pleroma/web/fallback/redirect_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Fallback.RedirectController do
diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex
index f5ef76d32..84b77cda1 100644
--- a/lib/pleroma/web/federator.ex
+++ b/lib/pleroma/web/federator.ex
@@ -1,12 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Federator do
alias Pleroma.Activity
alias Pleroma.Object.Containment
alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Federator.Publisher
@@ -47,10 +46,15 @@ defmodule Pleroma.Web.Federator do
end
@impl true
- def publish(activity) do
- PublisherWorker.enqueue("publish", %{"activity_id" => activity.id})
+ def publish(%Pleroma.Activity{data: %{"type" => type}} = activity) do
+ PublisherWorker.enqueue("publish", %{"activity_id" => activity.id},
+ priority: publish_priority(type)
+ )
end
+ defp publish_priority("Delete"), do: 3
+ defp publish_priority(_), do: 0
+
# Job Worker Callbacks
@spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}
@@ -61,10 +65,8 @@ defmodule Pleroma.Web.Federator do
def perform(:publish, activity) do
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
- with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
- {:ok, actor} <- User.ensure_keys_present(actor) do
- Publisher.publish(actor, activity)
- end
+ %User{} = actor = User.get_cached_by_ap_id(activity.data["actor"])
+ Publisher.publish(actor, activity)
end
def perform(:incoming_ap_doc, params) do
@@ -77,7 +79,7 @@ defmodule Pleroma.Web.Federator do
# NOTE: we use the actor ID to do the containment, this is fine because an
# actor shouldn't be acting on objects outside their own AP server.
- with {_, {:ok, _user}} <- {:actor, ap_enabled_actor(actor)},
+ with {_, {:ok, _user}} <- {:actor, User.get_or_fetch_by_ap_id(actor)},
nil <- Activity.normalize(params["id"]),
{_, :ok} <-
{:correct_origin?, Containment.contain_origin_from_id(actor, params)},
@@ -96,20 +98,15 @@ defmodule Pleroma.Web.Federator do
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
{:error, e}
+ {:error, {:validate_object, _}} = e ->
+ Logger.error("Incoming AP doc validation error: #{inspect(e)}")
+ Logger.debug(Jason.encode!(params, pretty: true))
+ e
+
e ->
# Just drop those for now
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
{:error, e}
end
end
-
- def ap_enabled_actor(id) do
- user = User.get_cached_by_ap_id(id)
-
- if User.ap_enabled?(user) do
- {:ok, user}
- else
- ActivityPub.make_user_from_ap_id(id)
- end
- end
end
diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex
index b7ee56803..a45796e9d 100644
--- a/lib/pleroma/web/federator/publisher.ex
+++ b/lib/pleroma/web/federator/publisher.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Federator.Publisher do
diff --git a/lib/pleroma/web/federator/publishing.ex b/lib/pleroma/web/federator/publishing.ex
index fe7805be9..3a242b8dc 100644
--- a/lib/pleroma/web/federator/publishing.ex
+++ b/lib/pleroma/web/federator/publishing.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Federator.Publishing do
diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex
index 66940f311..034722eb2 100644
--- a/lib/pleroma/web/feed/feed_view.ex
+++ b/lib/pleroma/web/feed/feed_view.ex
@@ -1,26 +1,20 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.FeedView do
use Phoenix.HTML
use Pleroma.Web, :view
- alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.User
+ alias Pleroma.Web.Gettext
alias Pleroma.Web.MediaProxy
require Pleroma.Constants
- @spec pub_date(String.t() | DateTime.t()) :: String.t()
- def pub_date(date) when is_binary(date) do
- date
- |> Timex.parse!("{ISO:Extended}")
- |> pub_date
- end
-
- def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}")
+ @days ~w(Mon Tue Wed Thu Fri Sat Sun)
+ @months ~w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
def prepare_activity(activity, opts \\ []) do
object = Object.normalize(activity, fetch: false)
@@ -40,26 +34,35 @@ defmodule Pleroma.Web.Feed.FeedView do
def most_recent_update(activities) do
with %{updated_at: updated_at} <- List.first(activities) do
- NaiveDateTime.to_iso8601(updated_at)
+ to_rfc3339(updated_at)
end
end
- def most_recent_update(activities, user) do
+ def most_recent_update(activities, user, :atom) do
+ (List.first(activities) || user).updated_at
+ |> to_rfc3339()
+ end
+
+ def most_recent_update(activities, user, :rss) do
(List.first(activities) || user).updated_at
- |> NaiveDateTime.to_iso8601()
+ |> to_rfc2822()
end
def feed_logo do
case Pleroma.Config.get([:feed, :logo]) do
nil ->
- "#{Pleroma.Web.base_url()}/static/logo.svg"
+ "#{Pleroma.Web.Endpoint.url()}/static/logo.svg"
logo ->
- "#{Pleroma.Web.base_url()}#{logo}"
+ "#{Pleroma.Web.Endpoint.url()}#{logo}"
end
|> MediaProxy.url()
end
+ def email(user) do
+ user.nickname <> "@" <> Pleroma.Web.Endpoint.host()
+ end
+
def logo(user) do
user
|> User.avatar_url()
@@ -68,18 +71,35 @@ defmodule Pleroma.Web.Feed.FeedView do
def last_activity(activities), do: List.last(activities)
- def activity_title(%{"content" => content}, opts \\ %{}) do
- content
- |> Pleroma.Web.Metadata.Utils.scrub_html()
- |> Pleroma.Emoji.Formatter.demojify()
- |> Formatter.truncate(opts[:max_length], opts[:omission])
- |> escape()
+ def activity_title(%{"content" => content} = data, opts \\ %{}) do
+ summary = Map.get(data, "summary", "")
+
+ title =
+ cond do
+ summary != "" -> summary
+ content != "" -> activity_content(data)
+ true -> "a post"
+ end
+
+ title
+ |> Pleroma.Web.Metadata.Utils.scrub_html_and_truncate(opts[:max_length], opts[:omission])
+ |> HtmlEntities.encode()
+ end
+
+ def activity_description(data) do
+ content = activity_content(data)
+ summary = data["summary"]
+
+ cond do
+ content != "" -> escape(content)
+ summary != "" -> escape(summary)
+ true -> escape(data["type"])
+ end
end
def activity_content(%{"content" => content}) do
content
|> String.replace(~r/[\n\r]/, "")
- |> escape()
end
def activity_content(_), do: ""
@@ -111,4 +131,60 @@ defmodule Pleroma.Web.Feed.FeedView do
|> html_escape()
|> safe_to_string()
end
+
+ @spec to_rfc3339(String.t() | NativeDateTime.t()) :: String.t()
+ def to_rfc3339(date) when is_binary(date) do
+ date
+ |> Timex.parse!("{ISO:Extended}")
+ |> to_rfc3339()
+ end
+
+ def to_rfc3339(nd) do
+ nd
+ |> Timex.to_datetime()
+ |> Timex.format!("{RFC3339}")
+ end
+
+ @spec to_rfc2822(String.t() | DateTime.t() | NativeDateTime.t()) :: String.t()
+ def to_rfc2822(datestr) when is_binary(datestr) do
+ datestr
+ |> Timex.parse!("{ISO:Extended}")
+ |> to_rfc2822()
+ end
+
+ def to_rfc2822(%DateTime{} = date) do
+ date
+ |> DateTime.to_naive()
+ |> NaiveDateTime.to_erl()
+ |> rfc2822_from_erl()
+ end
+
+ def to_rfc2822(nd) do
+ nd
+ |> Timex.to_datetime()
+ |> DateTime.to_naive()
+ |> NaiveDateTime.to_erl()
+ |> rfc2822_from_erl()
+ end
+
+ @doc """
+ Builds a RFC2822 timestamp from an Erlang timestamp
+ [RFC2822 3.3 - Date and Time Specification](https://tools.ietf.org/html/rfc2822#section-3.3)
+ This function always assumes the Erlang timestamp is in Universal time, not Local time
+ """
+ def rfc2822_from_erl({{year, month, day} = date, {hour, minute, second}}) do
+ day_name = Enum.at(@days, :calendar.day_of_the_week(date) - 1)
+ month_name = Enum.at(@months, month - 1)
+
+ date_part = "#{day_name}, #{day} #{month_name} #{year}"
+ time_part = "#{pad(hour)}:#{pad(minute)}:#{pad(second)}"
+
+ date_part <> " " <> time_part <> " +0000"
+ end
+
+ defp pad(num) do
+ num
+ |> Integer.to_string()
+ |> String.pad_leading(2, "0")
+ end
end
diff --git a/lib/pleroma/web/feed/tag_controller.ex b/lib/pleroma/web/feed/tag_controller.ex
index ef9293a55..e60767327 100644
--- a/lib/pleroma/web/feed/tag_controller.ex
+++ b/lib/pleroma/web/feed/tag_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.TagController do
diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex
index 58d35da1e..6657c2b3e 100644
--- a/lib/pleroma/web/feed/user_controller.ex
+++ b/lib/pleroma/web/feed/user_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.UserController do
@@ -18,6 +18,8 @@ defmodule Pleroma.Web.Feed.UserController do
def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do
Pleroma.Web.Fallback.RedirectController.redirector_with_meta(conn, %{user: user})
+ else
+ _ -> Pleroma.Web.Fallback.RedirectController.redirector(conn, nil)
end
end
@@ -28,7 +30,7 @@ defmodule Pleroma.Web.Feed.UserController do
def feed_redirect(conn, %{"nickname" => nickname}) do
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
- redirect(conn, external: "#{user_feed_url(conn, :feed, user.nickname)}.atom")
+ redirect(conn, external: "#{Routes.user_feed_url(conn, :feed, user.nickname)}.atom")
end
end
diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex
index c0ca4d0e9..5ef49d841 100644
--- a/lib/pleroma/web/gettext.ex
+++ b/lib/pleroma/web/gettext.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Gettext do
@@ -25,4 +25,196 @@ defmodule Pleroma.Web.Gettext do
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
"""
use Gettext, otp_app: :pleroma
+
+ def language_tag do
+ # Naive implementation: HTML lang attribute uses BCP 47, which
+ # uses - as a separator.
+ # https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang
+
+ Gettext.get_locale()
+ |> String.replace("_", "-", global: true)
+ end
+
+ def normalize_locale(locale) do
+ if is_binary(locale) do
+ String.replace(locale, "-", "_", global: true)
+ else
+ nil
+ end
+ end
+
+ def supports_locale?(locale) do
+ Pleroma.Web.Gettext
+ |> Gettext.known_locales()
+ |> Enum.member?(locale)
+ end
+
+ def variant?(locale), do: String.contains?(locale, "_")
+
+ def language_for_variant(locale) do
+ Enum.at(String.split(locale, "_"), 0)
+ end
+
+ def ensure_fallbacks(locales) do
+ locales
+ |> Enum.flat_map(fn locale ->
+ others =
+ other_supported_variants_of_locale(locale)
+ |> Enum.filter(fn l -> not Enum.member?(locales, l) end)
+
+ [locale] ++ others
+ end)
+ end
+
+ def other_supported_variants_of_locale(locale) do
+ cond do
+ supports_locale?(locale) ->
+ []
+
+ variant?(locale) ->
+ lang = language_for_variant(locale)
+ if supports_locale?(lang), do: [lang], else: []
+
+ true ->
+ Gettext.known_locales(Pleroma.Web.Gettext)
+ |> Enum.filter(fn l -> String.starts_with?(l, locale <> "_") end)
+ end
+ end
+
+ def get_locales do
+ Process.get({Pleroma.Web.Gettext, :locales}, [])
+ end
+
+ def is_locale_list(locales) do
+ Enum.all?(locales, &is_binary/1)
+ end
+
+ def put_locales(locales) do
+ if is_locale_list(locales) do
+ Process.put({Pleroma.Web.Gettext, :locales}, Enum.uniq(locales))
+ Gettext.put_locale(Enum.at(locales, 0, Gettext.get_locale()))
+ :ok
+ else
+ {:error, :not_locale_list}
+ end
+ end
+
+ def locale_or_default(locale) do
+ if supports_locale?(locale) do
+ locale
+ else
+ Gettext.get_locale()
+ end
+ end
+
+ def with_locales_func(locales, fun) do
+ prev_locales = Process.get({Pleroma.Web.Gettext, :locales})
+ put_locales(locales)
+
+ try do
+ fun.()
+ after
+ if prev_locales do
+ put_locales(prev_locales)
+ else
+ Process.delete({Pleroma.Web.Gettext, :locales})
+ Process.delete(Gettext)
+ end
+ end
+ end
+
+ defmacro with_locales(locales, do: fun) do
+ quote do
+ Pleroma.Web.Gettext.with_locales_func(unquote(locales), fn ->
+ unquote(fun)
+ end)
+ end
+ end
+
+ def to_locale_list(locale) when is_binary(locale) do
+ locale
+ |> String.split(",")
+ |> Enum.filter(&supports_locale?/1)
+ end
+
+ def to_locale_list(_), do: []
+
+ defmacro with_locale_or_default(locale, do: fun) do
+ quote do
+ Pleroma.Web.Gettext.with_locales_func(
+ Pleroma.Web.Gettext.to_locale_list(unquote(locale))
+ |> Enum.concat(Pleroma.Web.Gettext.get_locales()),
+ fn ->
+ unquote(fun)
+ end
+ )
+ end
+ end
+
+ defp next_locale(locale, list) do
+ index = Enum.find_index(list, fn item -> item == locale end)
+
+ if not is_nil(index) do
+ Enum.at(list, index + 1)
+ else
+ nil
+ end
+ end
+
+ # We do not yet have a proper English translation. The "English"
+ # version is currently but the fallback msgid. However, this
+ # will not work if the user puts English as the first language,
+ # and at the same time specifies other languages, as gettext will
+ # think the English translation is missing, and call
+ # handle_missing_translation functions. This may result in
+ # text in other languages being shown even if English is preferred
+ # by the user.
+ #
+ # To prevent this, we do not allow fallbacking when the current
+ # locale missing a translation is English.
+ defp should_fallback?(locale) do
+ locale != "en"
+ end
+
+ def handle_missing_translation(locale, domain, msgctxt, msgid, bindings) do
+ next = next_locale(locale, get_locales())
+
+ if is_nil(next) or not should_fallback?(locale) do
+ super(locale, domain, msgctxt, msgid, bindings)
+ else
+ {:ok,
+ Gettext.with_locale(next, fn ->
+ Gettext.dpgettext(Pleroma.Web.Gettext, domain, msgctxt, msgid, bindings)
+ end)}
+ end
+ end
+
+ def handle_missing_plural_translation(
+ locale,
+ domain,
+ msgctxt,
+ msgid,
+ msgid_plural,
+ n,
+ bindings
+ ) do
+ next = next_locale(locale, get_locales())
+
+ if is_nil(next) or not should_fallback?(locale) do
+ super(locale, domain, msgctxt, msgid, msgid_plural, n, bindings)
+ else
+ {:ok,
+ Gettext.with_locale(next, fn ->
+ Gettext.dpngettext(
+ Pleroma.Web.Gettext,
+ domain,
+ msgctxt,
+ msgid,
+ msgid_plural,
+ n,
+ bindings
+ )
+ end)}
+ end
+ end
end
diff --git a/lib/pleroma/web/instance_document.ex b/lib/pleroma/web/instance_document.ex
index a33bf605b..9da3c5008 100644
--- a/lib/pleroma/web/instance_document.ex
+++ b/lib/pleroma/web/instance_document.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.InstanceDocument do
diff --git a/lib/pleroma/web/mailer/subscription_controller.ex b/lib/pleroma/web/mailer/subscription_controller.ex
index f89abe46a..f2fc8fbc8 100644
--- a/lib/pleroma/web/mailer/subscription_controller.ex
+++ b/lib/pleroma/web/mailer/subscription_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Mailer.SubscriptionController do
diff --git a/lib/pleroma/web/manifest_controller.ex b/lib/pleroma/web/manifest_controller.ex
new file mode 100644
index 000000000..3b02e4bc0
--- /dev/null
+++ b/lib/pleroma/web/manifest_controller.ex
@@ -0,0 +1,14 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ManifestController do
+ use Pleroma.Web, :controller
+
+ plug(:skip_auth when action == :show)
+
+ @doc "GET /manifest.json"
+ def show(conn, _params) do
+ render(conn, "manifest.json")
+ end
+end
diff --git a/lib/pleroma/web/masto_fe_controller.ex b/lib/pleroma/web/masto_fe_controller.ex
deleted file mode 100644
index e788ab37a..000000000
--- a/lib/pleroma/web/masto_fe_controller.ex
+++ /dev/null
@@ -1,65 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MastoFEController do
- use Pleroma.Web, :controller
-
- alias Pleroma.User
- alias Pleroma.Web.MastodonAPI.AuthController
- alias Pleroma.Web.OAuth.Token
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
- alias Pleroma.Web.Plugs.OAuthScopesPlug
-
- plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings)
-
- # Note: :index action handles attempt of unauthenticated access to private instance with redirect
- plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action == :index)
-
- plug(
- OAuthScopesPlug,
- %{scopes: ["read"], fallback: :proceed_unauthenticated}
- when action == :index
- )
-
- plug(
- :skip_plug,
- [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :manifest
- )
-
- @doc "GET /web/*path"
- def index(conn, _params) do
- with %{assigns: %{user: %User{} = user, token: %Token{app_id: token_app_id} = token}} <- conn,
- {:ok, %{id: ^token_app_id}} <- AuthController.local_mastofe_app() do
- conn
- |> put_layout(false)
- |> render("index.html",
- token: token.token,
- user: user,
- custom_emojis: Pleroma.Emoji.get_all()
- )
- else
- _ ->
- conn
- |> put_session(:return_to, conn.request_path)
- |> redirect(to: "/web/login")
- end
- end
-
- @doc "GET /web/manifest.json"
- def manifest(conn, _params) do
- render(conn, "manifest.json")
- end
-
- @doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere"
- def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
- with {:ok, _} <- User.mastodon_settings_update(user, settings) do
- json(conn, %{})
- else
- e ->
- conn
- |> put_status(:internal_server_error)
- |> json(%{error: inspect(e)})
- end
- end
-end
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 7a1e99044..c313a0e97 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountController do
@@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
import Pleroma.Web.ControllerHelper,
only: [
add_link_headers: 2,
- truthy_param?: 1,
assign_account_by_id: 2,
embed_relationships?: 1,
json_response: 3
@@ -16,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
alias Pleroma.Maps
alias Pleroma.User
+ alias Pleroma.UserNote
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.Pipeline
@@ -25,16 +25,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
alias Pleroma.Web.MastodonAPI.MastodonAPIController
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.OAuth.OAuthController
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.Plugs.RateLimiter
alias Pleroma.Web.TwitterAPI.TwitterAPI
+ alias Pleroma.Web.Utils.Params
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
+ plug(:skip_auth when action in [:create, :lookup])
- plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:show, :statuses])
+ plug(:skip_public_check when action in [:show, :statuses])
plug(
OAuthScopesPlug,
@@ -54,7 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
when action in [:verify_credentials, :endorsements, :identity_proofs]
)
- plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials)
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["write:accounts"]}
+ when action in [:update_credentials, :note, :endorse, :unendorse]
+ )
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
@@ -72,15 +76,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
- %{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow]
+ %{scopes: ["follow", "write:follows"]}
+ when action in [:follow_by_uri, :follow, :unfollow, :remove_from_followers]
)
plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes)
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
- @relationship_actions [:follow, :unfollow]
- @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a
+ @relationship_actions [:follow, :unfollow, :remove_from_followers]
+ @needs_account ~W(
+ followers following lists follow unfollow mute unmute block unblock
+ note endorse unendorse remove_from_followers
+ )a
plug(
RateLimiter,
@@ -185,10 +193,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
:skip_thread_containment,
:allow_following_move,
:also_known_as,
- :accepts_chat_messages
+ :accepts_chat_messages,
+ :show_birthday
]
|> Enum.reduce(%{}, fn key, acc ->
- Maps.put_if_present(acc, key, params[key], &{:ok, truthy_param?(&1)})
+ Maps.put_if_present(acc, key, params[key], &{:ok, Params.truthy_param?(&1)})
end)
|> Maps.put_if_present(:name, params[:display_name])
|> Maps.put_if_present(:bio, params[:note])
@@ -213,6 +222,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|> Maps.put_if_present(:is_locked, params[:locked])
# Note: param name is indeed :discoverable (not an error)
|> Maps.put_if_present(:is_discoverable, params[:discoverable])
+ |> Maps.put_if_present(:birthday, params[:birthday])
+ |> Maps.put_if_present(:language, Pleroma.Web.Gettext.normalize_locale(params[:language]))
# What happens here:
#
@@ -243,19 +254,26 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
with_pleroma_settings: true
)
else
- _e -> render_error(conn, :forbidden, "Invalid request")
+ {:error, %Ecto.Changeset{errors: [avatar: {"file is too large", _}]}} ->
+ render_error(conn, :request_entity_too_large, "File is too large")
+
+ {:error, %Ecto.Changeset{errors: [banner: {"file is too large", _}]}} ->
+ render_error(conn, :request_entity_too_large, "File is too large")
+
+ {:error, %Ecto.Changeset{errors: [background: {"file is too large", _}]}} ->
+ render_error(conn, :request_entity_too_large, "File is too large")
+
+ _e ->
+ render_error(conn, :forbidden, "Invalid request")
end
end
defp normalize_fields_attributes(fields) do
- if Enum.all?(fields, &is_tuple/1) do
- Enum.map(fields, fn {_, v} -> v end)
- else
- Enum.map(fields, fn
- %{} = field -> %{"name" => field.name, "value" => field.value}
- field -> field
- end)
- end
+ if(Enum.all?(fields, &is_tuple/1), do: Enum.map(fields, fn {_, v} -> v end), else: fields)
+ |> Enum.map(fn
+ %{} = field -> %{"name" => field.name, "value" => field.value}
+ field -> field
+ end)
end
@doc "GET /api/v1/accounts/relationships"
@@ -402,6 +420,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@doc "POST /api/v1/accounts/:id/mute"
def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do
+ params =
+ params
+ |> Map.put_new(:duration, Map.get(params, :expires_in, 0))
+
with {:ok, _user_relationships} <- User.mute(muter, muted, params) do
render(conn, "relationship.json", user: muter, target: muted)
else
@@ -436,6 +458,48 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
end
+ @doc "POST /api/v1/accounts/:id/note"
+ def note(
+ %{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn,
+ _params
+ ) do
+ with {:ok, _user_note} <- UserNote.create(noter, target, comment) do
+ render(conn, "relationship.json", user: noter, target: target)
+ end
+ end
+
+ @doc "POST /api/v1/accounts/:id/pin"
+ def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
+ with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
+ render(conn, "relationship.json", user: endorser, target: endorsed)
+ else
+ {:error, message} -> json_response(conn, :bad_request, %{error: message})
+ end
+ end
+
+ @doc "POST /api/v1/accounts/:id/unpin"
+ def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
+ with {:ok, _user_relationships} <- User.unendorse(endorser, endorsed) do
+ render(conn, "relationship.json", user: endorser, target: endorsed)
+ else
+ {:error, message} -> json_response(conn, :forbidden, %{error: message})
+ end
+ end
+
+ @doc "POST /api/v1/accounts/:id/remove_from_followers"
+ def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
+ {:error, "Can not unfollow yourself"}
+ end
+
+ def remove_from_followers(%{assigns: %{user: followed, account: follower}} = conn, _params) do
+ with {:ok, follower} <- CommonAPI.reject_follow_request(follower, followed) do
+ render(conn, "relationship.json", user: followed, target: follower)
+ else
+ nil ->
+ render_error(conn, :not_found, "Record not found")
+ end
+ end
+
@doc "POST /api/v1/follows"
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
case User.get_cached_by_nickname(uri) do
@@ -454,7 +518,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
users =
user
|> User.muted_users_relation(_restrict_deactivated = true)
- |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
+ |> Pleroma.Pagination.fetch_paginated(params)
conn
|> add_link_headers(users)
@@ -462,7 +526,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
users: users,
for: user,
as: :user,
- embed_relationships: embed_relationships?(params)
+ embed_relationships: embed_relationships?(params),
+ mutes: true
)
end
@@ -471,15 +536,45 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
users =
user
|> User.blocked_users_relation(_restrict_deactivated = true)
- |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
+ |> Pleroma.Pagination.fetch_paginated(params)
conn
|> add_link_headers(users)
- |> render("index.json", users: users, for: user, as: :user)
+ |> render("index.json",
+ users: users,
+ for: user,
+ as: :user,
+ embed_relationships: embed_relationships?(params)
+ )
+ end
+
+ @doc "GET /api/v1/accounts/lookup"
+ def lookup(conn, %{acct: nickname} = _params) do
+ with %User{} = user <- User.get_by_nickname(nickname) do
+ render(conn, "show.json",
+ user: user,
+ skip_visibility_check: true
+ )
+ else
+ error -> user_visibility_error(conn, error)
+ end
end
@doc "GET /api/v1/endorsements"
- def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
+ def endorsements(%{assigns: %{user: user}} = conn, params) do
+ users =
+ user
+ |> User.endorsed_users_relation(_restrict_deactivated = true)
+ |> Pleroma.Repo.all()
+
+ conn
+ |> render("index.json",
+ users: users,
+ for: user,
+ as: :user,
+ embed_relationships: embed_relationships?(params)
+ )
+ end
@doc "GET /api/v1/identity_proofs"
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
diff --git a/lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex b/lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex
new file mode 100644
index 000000000..080af96d5
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/announcement_controller.ex
@@ -0,0 +1,60 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.AnnouncementController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper,
+ only: [
+ json_response: 3
+ ]
+
+ alias Pleroma.Announcement
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+ # Mastodon docs say this only requires a user token, no scopes needed
+ # As the op `|` requires at least one scope to be present, we use `&` here.
+ plug(
+ OAuthScopesPlug,
+ %{scopes: [], op: :&}
+ when action in [:index]
+ )
+
+ # Same as in MastodonAPI specs
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["write:accounts"]}
+ when action in [:mark_read]
+ )
+
+ action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AnnouncementOperation
+
+ @doc "GET /api/v1/announcements"
+ def index(%{assigns: %{user: user}} = conn, _params) do
+ render(conn, "index.json", announcements: all_visible(), user: user)
+ end
+
+ def index(conn, _params) do
+ render(conn, "index.json", announcements: all_visible(), user: nil)
+ end
+
+ defp all_visible do
+ Announcement.list_all_visible()
+ end
+
+ @doc "POST /api/v1/announcements/:id/dismiss"
+ def mark_read(%{assigns: %{user: user}} = conn, %{id: id} = _params) do
+ with announcement when not is_nil(announcement) <- Announcement.get_by_id(id),
+ {:ok, _} <- Announcement.mark_read_by(announcement, user) do
+ json_response(conn, :ok, %{})
+ else
+ _ ->
+ {:error, :not_found}
+ end
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
index dd3b39c77..844673ae0 100644
--- a/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/app_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AppController do
@@ -10,43 +10,41 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
use Pleroma.Web, :controller
+ alias Pleroma.Maps
alias Pleroma.Repo
+ alias Pleroma.User
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Scopes
alias Pleroma.Web.OAuth.Token
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
- alias Pleroma.Web.Plugs.OAuthScopesPlug
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
- plug(
- :skip_plug,
- [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
- when action in [:create, :verify_credentials]
- )
+ plug(:skip_auth when action in [:create, :verify_credentials])
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- @local_mastodon_name "Mastodon-Local"
-
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation
@doc "POST /api/v1/apps"
def create(%{body_params: params} = conn, _params) do
scopes = Scopes.fetch_scopes(params, ["read"])
+ user_id = get_user_id(conn)
app_attrs =
params
|> Map.take([:client_name, :redirect_uris, :website])
|> Map.put(:scopes, scopes)
+ |> Maps.put_if_present(:user_id, user_id)
with cs <- App.register_changeset(%App{}, app_attrs),
- false <- cs.changes[:client_name] == @local_mastodon_name,
{:ok, app} <- Repo.insert(cs) do
render(conn, "show.json", app: app)
end
end
+ defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id
+ defp get_user_id(_conn), do: nil
+
@doc """
GET /api/v1/apps/verify_credentials
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
diff --git a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
index eb6639fc5..fbb54a171 100644
--- a/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/auth_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AuthController do
@@ -7,77 +7,12 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
- alias Pleroma.Helpers.AuthHelper
- alias Pleroma.Helpers.UriHelper
- alias Pleroma.User
- alias Pleroma.Web.OAuth.App
- alias Pleroma.Web.OAuth.Authorization
- alias Pleroma.Web.OAuth.Token
- alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
alias Pleroma.Web.TwitterAPI.TwitterAPI
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
- @local_mastodon_name "Mastodon-Local"
-
- @doc "GET /web/login"
- # Local Mastodon FE login callback action
- def login(conn, %{"code" => auth_token} = params) do
- with {:ok, app} <- local_mastofe_app(),
- {:ok, auth} <- Authorization.get_by_token(app, auth_token),
- {:ok, oauth_token} <- Token.exchange_token(app, auth) do
- redirect_to =
- conn
- |> local_mastodon_post_login_path()
- |> UriHelper.modify_uri_params(%{"access_token" => oauth_token.token})
-
- conn
- |> AuthHelper.put_session_token(oauth_token.token)
- |> redirect(to: redirect_to)
- else
- _ -> redirect_to_oauth_form(conn, params)
- end
- end
-
- def login(conn, params) do
- with %{assigns: %{user: %User{}, token: %Token{app_id: app_id}}} <- conn,
- {:ok, %{id: ^app_id}} <- local_mastofe_app() do
- redirect(conn, to: local_mastodon_post_login_path(conn))
- else
- _ -> redirect_to_oauth_form(conn, params)
- end
- end
-
- defp redirect_to_oauth_form(conn, _params) do
- with {:ok, app} <- local_mastofe_app() do
- path =
- o_auth_path(conn, :authorize,
- response_type: "code",
- client_id: app.client_id,
- redirect_uri: ".",
- scope: Enum.join(app.scopes, " ")
- )
-
- redirect(conn, to: path)
- end
- end
-
- @doc "DELETE /auth/sign_out"
- def logout(conn, _) do
- conn =
- with %{assigns: %{token: %Token{} = oauth_token}} <- conn,
- session_token = AuthHelper.get_session_token(conn),
- {:ok, %Token{token: ^session_token}} <- RevokeToken.revoke(oauth_token) do
- AuthHelper.delete_session_token(conn)
- else
- _ -> conn
- end
-
- redirect(conn, to: "/")
- end
-
@doc "POST /auth/password"
def password_reset(conn, params) do
nickname_or_email = params["email"] || params["nickname"]
@@ -86,23 +21,4 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
json_response(conn, :no_content, "")
end
-
- defp local_mastodon_post_login_path(conn) do
- case get_session(conn, :return_to) do
- nil ->
- masto_fe_path(conn, :index, ["getting-started"])
-
- return_to ->
- delete_session(conn, :return_to)
- return_to
- end
- end
-
- @spec local_mastofe_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
- def local_mastofe_app do
- App.get_or_make(
- %{client_name: @local_mastodon_name, redirect_uris: "."},
- ["read", "write", "follow", "push", "admin"]
- )
- end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex
index f2a0949e8..9cc6225c6 100644
--- a/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ConversationController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex
index d7e18dc92..8b27b0b02 100644
--- a/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
@@ -7,11 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(
- :skip_plug,
- [Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug]
- when action == :index
- )
+ plug(:skip_auth when action == :index)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.CustomEmojiOperation
diff --git a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex
new file mode 100644
index 000000000..253f06cfb
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex
@@ -0,0 +1,82 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.DirectoryController do
+ use Pleroma.Web, :controller
+
+ import Ecto.Query
+ alias Pleroma.Pagination
+ alias Pleroma.User
+ alias Pleroma.UserRelationship
+ alias Pleroma.Web.MastodonAPI.AccountView
+
+ require Logger
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+ plug(:skip_auth when action == "index")
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation
+
+ @doc "GET /api/v1/directory"
+ def index(%{assigns: %{user: user}} = conn, params) do
+ with true <- Pleroma.Config.get([:instance, :profile_directory]) do
+ limit = Map.get(params, :limit, 20) |> min(80)
+
+ users =
+ User.Query.build(%{is_discoverable: true, invisible: false, limit: limit})
+ |> order_by_creation_date(params)
+ |> exclude_remote(params)
+ |> exclude_user(user)
+ |> exclude_relationships(user, [:block, :mute])
+ |> Pagination.fetch_paginated(params, :offset)
+
+ conn
+ |> put_view(AccountView)
+ |> render("index.json", for: user, users: users, as: :user)
+ else
+ _ -> json(conn, [])
+ end
+ end
+
+ defp order_by_creation_date(query, %{order: "new"}) do
+ query
+ end
+
+ defp order_by_creation_date(query, _params) do
+ query
+ |> order_by([u], desc_nulls_last: u.last_status_at)
+ end
+
+ defp exclude_remote(query, %{local: true}) do
+ where(query, [u], u.local == true)
+ end
+
+ defp exclude_remote(query, _params) do
+ query
+ end
+
+ defp exclude_user(query, %User{id: user_id}) do
+ where(query, [u], u.id != ^user_id)
+ end
+
+ defp exclude_user(query, _user) do
+ query
+ end
+
+ defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
+ query
+ |> join(:left, [u], r in UserRelationship,
+ as: :user_relationships,
+ on:
+ r.target_id == u.id and r.source_id == ^user_id and
+ r.relationship_type in ^relationship_types
+ )
+ |> where([user_relationships: r], is_nil(r.target_id))
+ end
+
+ defp exclude_relationships(query, _user, _relationship_types) do
+ query
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
index 30300307d..b2e347ed9 100644
--- a/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex
index 84621500e..1c650eb21 100644
--- a/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.FallbackController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
index 9b1ae809d..0959b4bcc 100644
--- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.FilterController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
index 63d0e2c35..ba6d074cc 100644
--- a/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
@@ -9,7 +9,6 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
- plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:assign_follower when action != :index)
diff --git a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex
index c7a5267d4..6410e872c 100644
--- a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.InstanceController do
@@ -7,11 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(
- :skip_plug,
- [Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug]
- when action in [:show, :peers]
- )
+ plug(:skip_auth when action in [:show, :peers])
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
diff --git a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex
index b7b41f449..2117aae3a 100644
--- a/lib/pleroma/web/mastodon_api/controllers/list_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/list_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ListController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
index c745f3493..4ad30f330 100644
--- a/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/marker_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MarkerController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
index a1bcc91d9..0aa7b379f 100644
--- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
@@ -15,11 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
require Logger
- plug(
- :skip_plug,
- [Pleroma.Web.Plugs.OAuthScopesPlug, Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug]
- when action in [:empty_array, :empty_object]
- )
+ plug(:skip_auth when action in [:empty_array, :empty_object])
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
diff --git a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex
index d6949ed80..7d9a63cf4 100644
--- a/lib/pleroma/web/mastodon_api/controllers/media_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/media_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MediaController do
@@ -13,7 +13,6 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:create, :create2])
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
plug(OAuthScopesPlug, %{scopes: ["read:media"]} when action == :show)
plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action != :show)
diff --git a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
index 647ba661e..a490e8319 100644
--- a/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/notification_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.NotificationController do
@@ -50,11 +50,13 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
favourite
move
pleroma:emoji_reaction
+ poll
+ update
}
def index(%{assigns: %{user: user}} = conn, params) do
params =
Map.new(params, fn {k, v} -> {to_string(k), v} end)
- |> Map.put_new("include_types", @default_notification_types)
+ |> Map.put_new("types", Map.get(params, :include_types, @default_notification_types))
notifications = MastodonAPI.get_notifications(user, params)
diff --git a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
index f44ff997d..002c210d2 100644
--- a/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/poll_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.PollController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex
index 03d9a4f4f..3db80d728 100644
--- a/lib/pleroma/web/mastodon_api/controllers/report_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/report_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ReportController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
index 3b7a0c788..0392fcef1 100644
--- a/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
index af93e453d..5e6e04734 100644
--- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SearchController do
@@ -8,8 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
alias Pleroma.Activity
alias Pleroma.Repo
alias Pleroma.User
- alias Pleroma.Web
alias Pleroma.Web.ControllerHelper
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.Plugs.OAuthScopesPlug
@@ -17,6 +17,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
require Logger
+ @search_limit 40
+
plug(Pleroma.Web.ApiSpec.CastAndValidate)
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
@@ -77,7 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
[
resolve: params[:resolve],
following: params[:following],
- limit: params[:limit],
+ limit: min(params[:limit], @search_limit),
offset: params[:offset],
type: params[:type],
author: get_author(params),
@@ -108,7 +110,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
end
defp resource_search(:v2, "hashtags", query, options) do
- tags_path = Web.base_url() <> "/tag/"
+ tags_path = Endpoint.url() <> "/tag/"
query
|> prepare_tags(options)
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
index 724dc5c5d..e594ea491 100644
--- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.StatusController do
@@ -27,10 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(
- :skip_plug,
- Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show]
- )
+ plug(:skip_public_check when action in [:index, :show])
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
@@ -41,7 +38,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
:index,
:show,
:card,
- :context
+ :context,
+ :show_history,
+ :show_source
]
)
@@ -52,7 +51,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
:create,
:delete,
:reblog,
- :unreblog
+ :unreblog,
+ :update
]
)
@@ -194,6 +194,59 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
create(%Plug.Conn{conn | body_params: params}, %{})
end
+ @doc "GET /api/v1/statuses/:id/history"
+ def show_history(%{assigns: assigns} = conn, %{id: id} = params) do
+ with user = assigns[:user],
+ %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ true <- Visibility.visible_for_user?(activity, user) do
+ try_render(conn, "history.json",
+ activity: activity,
+ for: user,
+ with_direct_conversation_id: true,
+ with_muted: Map.get(params, :with_muted, false)
+ )
+ else
+ _ -> {:error, :not_found}
+ end
+ end
+
+ @doc "GET /api/v1/statuses/:id/source"
+ def show_source(%{assigns: assigns} = conn, %{id: id} = _params) do
+ with user = assigns[:user],
+ %Activity{} = activity <- Activity.get_by_id_with_object(id),
+ true <- Visibility.visible_for_user?(activity, user) do
+ try_render(conn, "source.json",
+ activity: activity,
+ for: user
+ )
+ else
+ _ -> {:error, :not_found}
+ end
+ end
+
+ @doc "PUT /api/v1/statuses/:id"
+ def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{id: id} = params) do
+ with {_, %Activity{}} = {_, activity} <- {:activity, Activity.get_by_id_with_object(id)},
+ {_, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
+ {_, true} <- {:is_create, activity.data["type"] == "Create"},
+ actor <- Activity.user_actor(activity),
+ {_, true} <- {:own_status, actor.id == user.id},
+ changes <- body_params |> put_application(conn),
+ {_, {:ok, _update_activity}} <- {:pipeline, CommonAPI.update(user, activity, changes)},
+ {_, %Activity{}} = {_, activity} <- {:refetched, Activity.get_by_id_with_object(id)} do
+ try_render(conn, "show.json",
+ activity: activity,
+ for: user,
+ with_direct_conversation_id: true,
+ with_muted: Map.get(params, :with_muted, false)
+ )
+ else
+ {:own_status, _} -> {:error, :forbidden}
+ {:pipeline, _} -> {:error, :internal_server_error}
+ _ -> {:error, :not_found}
+ end
+ end
+
@doc "GET /api/v1/statuses/:id"
def show(%{assigns: %{user: user}} = conn, %{id: id} = params) do
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
diff --git a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex
index fcb3d4829..9cc0071f6 100644
--- a/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
diff --git a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
index 01e122dd9..69ae70ad4 100644
--- a/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex
@@ -1,14 +1,19 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
use Pleroma.Web, :controller
+ import Ecto.Query
+ alias Pleroma.FollowingRelationship
+ alias Pleroma.User
+ alias Pleroma.UserRelationship
require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action == :index)
+ plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:index, :index2])
+ plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["write"]} when action in [:dismiss])
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
@@ -26,7 +31,90 @@ defmodule Pleroma.Web.MastodonAPI.SuggestionController do
}
end
+ def index2_operation do
+ %OpenApiSpex.Operation{
+ tags: ["Suggestions"],
+ summary: "Follow suggestions",
+ operationId: "SuggestionController.index2",
+ responses: %{
+ 200 => Pleroma.Web.ApiSpec.Helpers.empty_array_response()
+ }
+ }
+ end
+
+ def dismiss_operation do
+ %OpenApiSpex.Operation{
+ tags: ["Suggestions"],
+ summary: "Remove a suggestion",
+ operationId: "SuggestionController.dismiss",
+ parameters: [
+ OpenApiSpex.Operation.parameter(
+ :account_id,
+ :path,
+ %OpenApiSpex.Schema{type: :string},
+ "Account to dismiss",
+ required: true
+ )
+ ],
+ responses: %{
+ 200 => Pleroma.Web.ApiSpec.Helpers.empty_object_response()
+ }
+ }
+ end
+
@doc "GET /api/v1/suggestions"
def index(conn, params),
do: Pleroma.Web.MastodonAPI.MastodonAPIController.empty_array(conn, params)
+
+ @doc "GET /api/v2/suggestions"
+ def index2(%{assigns: %{user: user}} = conn, params) do
+ limit = Map.get(params, :limit, 40) |> min(80)
+
+ users =
+ %{is_suggested: true, invisible: false, limit: limit}
+ |> User.Query.build()
+ |> exclude_user(user)
+ |> exclude_relationships(user, [:block, :mute, :suggestion_dismiss])
+ |> exclude_following(user)
+ |> Pleroma.Repo.all()
+
+ render(conn, "index.json", %{
+ users: users,
+ source: :staff,
+ for: user,
+ skip_visibility_check: true
+ })
+ end
+
+ defp exclude_user(query, %User{id: user_id}) do
+ where(query, [u], u.id != ^user_id)
+ end
+
+ defp exclude_relationships(query, %User{id: user_id}, relationship_types) do
+ query
+ |> join(:left, [u], r in UserRelationship,
+ as: :user_relationships,
+ on:
+ r.target_id == u.id and r.source_id == ^user_id and
+ r.relationship_type in ^relationship_types
+ )
+ |> where([user_relationships: r], is_nil(r.target_id))
+ end
+
+ defp exclude_following(query, %User{id: user_id}) do
+ query
+ |> join(:left, [u], r in FollowingRelationship,
+ as: :following_relationships,
+ on: r.following_id == u.id and r.follower_id == ^user_id and r.state == :follow_accept
+ )
+ |> where([following_relationships: r], is_nil(r.following_id))
+ end
+
+ @doc "DELETE /api/v1/suggestions/:account_id"
+ def dismiss(%{assigns: %{user: source}} = conn, %{account_id: user_id}) do
+ with %User{} = target <- User.get_cached_by_id(user_id),
+ {:ok, _} <- UserRelationship.create(:suggestion_dismiss, source, target) do
+ json(conn, %{})
+ end
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
index c611958be..293c61b41 100644
--- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.TimelineController do
@@ -12,12 +12,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.Plugs.RateLimiter
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag])
+ plug(:skip_public_check when action in [:public, :hashtag])
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
@@ -37,8 +36,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
when action in [:public, :hashtag]
)
- plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
-
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation
# GET /api/v1/timelines/home
@@ -115,6 +112,8 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> Map.put(:instance, params[:instance])
+ # Restricts unfederated content to authenticated users
+ |> Map.put(:includes_local_public, not is_nil(user))
|> ActivityPub.fetch_public_activities()
conn
@@ -196,7 +195,9 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|> ActivityPub.fetch_activities_bounded(following, params)
|> Enum.reverse()
- render(conn, "index.json",
+ conn
+ |> add_link_headers(activities)
+ |> render("index.json",
activities: activities,
for: user,
as: :activity,
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex
index 71479550e..467dc2fac 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
@@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
with {:ok, follower, _followed, _} <- result do
options = cast_params(params)
set_reblogs_visibility(options[:reblogs], result)
+ set_subscription(options[:notify], result)
{:ok, follower}
end
end
@@ -36,6 +37,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
CommonAPI.show_reblogs(follower, followed)
end
+ defp set_subscription(true, {:ok, follower, followed, _}) do
+ User.subscribe(follower, followed)
+ end
+
+ defp set_subscription(false, {:ok, follower, followed, _}) do
+ User.unsubscribe(follower, followed)
+ end
+
+ defp set_subscription(_, _), do: {:ok, nil}
+
@spec get_followers(User.t(), map()) :: list(User.t())
def get_followers(user, params \\ %{}) do
user
@@ -50,11 +61,24 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
end
def get_notifications(user, params \\ %{}) do
- options = cast_params(params)
+ options =
+ cast_params(params) |> Map.update(:include_types, [], fn include_types -> include_types end)
+
+ options =
+ if ("pleroma:report" not in options.include_types and
+ User.privileged?(user, :reports_manage_reports)) or
+ User.privileged?(user, :reports_manage_reports) do
+ options
+ else
+ options
+ |> Map.update(:exclude_types, ["pleroma:report"], fn current_exclude_types ->
+ current_exclude_types ++ ["pleroma:report"]
+ end)
+ end
user
|> Notification.for_user_query(options)
- |> restrict(:include_types, options)
+ |> restrict(:types, options)
|> restrict(:exclude_types, options)
|> restrict(:account_ap_id, options)
|> Pagination.fetch_paginated(params)
@@ -69,18 +93,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
defp cast_params(params) do
param_types = %{
exclude_types: {:array, :string},
- include_types: {:array, :string},
+ types: {:array, :string},
exclude_visibilities: {:array, :string},
reblogs: :boolean,
with_muted: :boolean,
- account_ap_id: :string
+ account_ap_id: :string,
+ notify: :boolean
}
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
changeset.changes
end
- defp restrict(query, :include_types, %{include_types: mastodon_types = [_ | _]}) do
+ defp restrict(query, :types, %{types: mastodon_types = [_ | _]}) do
where(query, [n], n.type in ^mastodon_types)
end
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index ac25aefdd..cc3e3582f 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountView do
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
alias Pleroma.FollowingRelationship
alias Pleroma.User
+ alias Pleroma.UserNote
alias Pleroma.UserRelationship
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
@@ -101,6 +102,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
User.following?(target, reading_user)
end
+ subscribing =
+ UserRelationship.exists?(
+ user_relationships,
+ :inverse_subscription,
+ target,
+ reading_user,
+ &User.subscribed_to?(&2, &1)
+ )
+
# NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags
%{
id: to_string(target.id),
@@ -138,14 +148,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
target,
&User.muted_notifications?(&1, &2)
),
- subscribing:
- UserRelationship.exists?(
- user_relationships,
- :inverse_subscription,
- target,
- reading_user,
- &User.subscribed_to?(&2, &1)
- ),
+ subscribing: subscribing,
+ notifying: subscribing,
requested: follow_state == :follow_pending,
domain_blocking: User.blocks_domain?(reading_user, target),
showing_reblogs:
@@ -156,7 +160,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
target,
&User.muting_reblogs?(&1, &2)
),
- endorsed: false
+ note:
+ UserNote.show(
+ reading_user,
+ target
+ ),
+ endorsed:
+ UserRelationship.exists?(
+ user_relationships,
+ :endorsement,
+ target,
+ reading_user,
+ &User.endorses?(&2, &1)
+ )
}
end
@@ -261,6 +277,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
actor_type: user.actor_type
}
},
+ last_status_at: user.last_status_at,
# Pleroma extensions
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
@@ -269,6 +286,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
ap_id: user.ap_id,
also_known_as: user.also_known_as,
is_confirmed: user.is_confirmed,
+ is_suggested: user.is_suggested,
tags: user.tags,
hide_followers_count: user.hide_followers_count,
hide_follows_count: user.hide_follows_count,
@@ -292,6 +310,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|> maybe_put_allow_following_move(user, opts[:for])
|> maybe_put_unread_conversation_count(user, opts[:for])
|> maybe_put_unread_notification_count(user, opts[:for])
+ |> maybe_put_email_address(user, opts[:for])
+ |> maybe_put_mute_expires_at(user, opts[:for], opts)
+ |> maybe_show_birthday(user, opts[:for])
end
defp username_from_nickname(string) when is_binary(string) do
@@ -325,6 +346,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|> 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)
+ |> Kernel.put_in([:source, :pleroma, :show_birthday], user.show_birthday)
end
defp maybe_put_settings(data, _, _, _), do: data
@@ -348,19 +370,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_chat_token(data, _, _, _), do: data
defp maybe_put_role(data, %User{show_role: true} = user, _) do
- data
- |> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
- |> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
+ put_role(data, user)
end
defp maybe_put_role(data, %User{id: user_id} = user, %User{id: user_id}) do
+ put_role(data, user)
+ end
+
+ defp maybe_put_role(data, _, _), do: data
+
+ defp put_role(data, user) do
data
|> Kernel.put_in([:pleroma, :is_admin], user.is_admin)
|> Kernel.put_in([:pleroma, :is_moderator], user.is_moderator)
+ |> Kernel.put_in([:pleroma, :privileges], User.privileges(user))
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,
@@ -377,12 +402,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_allow_following_move(data, _, _), do: data
- defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
- Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active)
+ defp maybe_put_activation_status(data, user, user_for) do
+ if User.privileged?(user_for, :users_manage_activation_state),
+ do: Kernel.put_in(data, [:pleroma, :deactivated], !user.is_active),
+ else: data
end
- defp maybe_put_activation_status(data, _, _), do: data
-
defp maybe_put_unread_conversation_count(data, %User{id: user_id} = user, %User{id: user_id}) do
data
|> Kernel.put_in(
@@ -403,6 +428,40 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_unread_notification_count(data, _, _), do: data
+ defp maybe_put_email_address(data, %User{id: user_id}, %User{id: user_id} = user) do
+ Kernel.put_in(
+ data,
+ [:pleroma, :email],
+ user.email
+ )
+ end
+
+ defp maybe_put_email_address(data, _, _), do: data
+
+ defp maybe_put_mute_expires_at(data, %User{} = user, target, %{mutes: true}) do
+ Map.put(
+ data,
+ :mute_expires_at,
+ UserRelationship.get_mute_expire_date(target, user)
+ )
+ end
+
+ defp maybe_put_mute_expires_at(data, _, _, _), do: data
+
+ defp maybe_show_birthday(data, %User{id: user_id} = user, %User{id: user_id}) do
+ data
+ |> Kernel.put_in([:pleroma, :birthday], user.birthday)
+ end
+
+ defp maybe_show_birthday(data, %User{show_birthday: true} = user, _) do
+ data
+ |> Kernel.put_in([:pleroma, :birthday], user.birthday)
+ end
+
+ defp maybe_show_birthday(data, _, _) do
+ data
+ end
+
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
defp image_url(_), do: nil
end
diff --git a/lib/pleroma/web/mastodon_api/views/announcement_view.ex b/lib/pleroma/web/mastodon_api/views/announcement_view.ex
new file mode 100644
index 000000000..93fdfb1f1
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/announcement_view.ex
@@ -0,0 +1,15 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.AnnouncementView do
+ use Pleroma.Web, :view
+
+ def render("index.json", %{announcements: announcements, user: user}) do
+ render_many(announcements, __MODULE__, "show.json", user: user)
+ end
+
+ def render("show.json", %{announcement: announcement, user: user}) do
+ Pleroma.Announcement.render_json(announcement, for: user)
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex
index c406b5a27..92cccd452 100644
--- a/lib/pleroma/web/mastodon_api/views/app_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/app_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AppView do
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index 46b63b54b..f6577cd2f 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ConversationView do
diff --git a/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex b/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex
index 40e314164..cd59ab946 100644
--- a/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/custom_emoji_view.ex
@@ -1,19 +1,19 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.CustomEmojiView do
use Pleroma.Web, :view
alias Pleroma.Emoji
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
def render("index.json", %{custom_emojis: custom_emojis}) do
render_many(custom_emojis, __MODULE__, "show.json")
end
def render("show.json", %{custom_emoji: {shortcode, %Emoji{file: relative_url, tags: tags}}}) do
- url = Web.base_url() |> URI.merge(relative_url) |> to_string()
+ url = Endpoint.url() |> URI.merge(relative_url) |> to_string()
%{
"shortcode" => shortcode,
diff --git a/lib/pleroma/web/mastodon_api/views/filter_view.ex b/lib/pleroma/web/mastodon_api/views/filter_view.ex
index 8e8798c1e..0c9706166 100644
--- a/lib/pleroma/web/mastodon_api/views/filter_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/filter_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.FilterView do
diff --git a/lib/pleroma/web/mastodon_api/views/follow_request_view.ex b/lib/pleroma/web/mastodon_api/views/follow_request_view.ex
new file mode 100644
index 000000000..5e50bc2b6
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/follow_request_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.FollowRequestView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.AccountView.render(view, opts)
+end
diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex
index dac68d8e6..efd2a0af6 100644
--- a/lib/pleroma/web/mastodon_api/views/instance_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.InstanceView do
@@ -14,9 +14,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
instance = Config.get(:instance)
%{
- uri: Pleroma.Web.base_url(),
+ uri: Pleroma.Web.Endpoint.url(),
title: Keyword.get(instance, :name),
description: Keyword.get(instance, :description),
+ short_description: Keyword.get(instance, :short_description),
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
email: Keyword.get(instance, :email),
urls: %{
@@ -24,19 +25,21 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
},
stats: Pleroma.Stats.get_stats(),
thumbnail:
- URI.merge(Pleroma.Web.base_url(), Keyword.get(instance, :instance_thumbnail)) |> to_string,
- languages: ["en"],
+ URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
+ |> to_string,
+ languages: Keyword.get(instance, :languages, ["en"]),
registrations: Keyword.get(instance, :registrations_open),
approval_required: Keyword.get(instance, :account_approval_required),
# Extra (not present in Mastodon):
max_toot_chars: Keyword.get(instance, :limit),
+ max_media_attachments: Keyword.get(instance, :max_media_attachments),
poll_limits: Keyword.get(instance, :poll_limits),
upload_limit: Keyword.get(instance, :upload_limit),
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
background_upload_limit: Keyword.get(instance, :background_upload_limit),
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
- background_image: Pleroma.Web.base_url() <> Keyword.get(instance, :background_image),
- chat_limit: Keyword.get(instance, :chat_limit),
+ background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
+ shout_limit: Config.get([:shout, :limit]),
description_limit: Keyword.get(instance, :description_limit),
pleroma: %{
metadata: %{
@@ -44,7 +47,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
features: features(),
federation: federation(),
fields_limits: fields_limits(),
- post_formats: Config.get([:instance, :allowed_post_formats])
+ post_formats: Config.get([:instance, :allowed_post_formats]),
+ birthday_required: Config.get([:instance, :birthday_required]),
+ birthday_min_age: Config.get([:instance, :birthday_min_age])
},
stats: %{mau: Pleroma.User.active_user_count()},
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
@@ -58,19 +63,28 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
"mastodon_api",
"mastodon_api_streaming",
"polls",
+ "v2_suggestions",
"pleroma_explicit_addressing",
"shareable_emoji_packs",
"multifetch",
"pleroma:api/v1/notifications:include_types_filter",
+ "editing",
+ if Config.get([:activitypub, :blockers_visible]) do
+ "blockers_visible"
+ end,
if Config.get([:media_proxy, :enabled]) do
"media_proxy"
end,
if Config.get([:gopher, :enabled]) do
"gopher"
end,
- if Config.get([:chat, :enabled]) do
+ # backwards compat
+ if Config.get([:shout, :enabled]) do
"chat"
end,
+ if Config.get([:shout, :enabled]) do
+ "shout"
+ end,
if Config.get([:instance, :allow_relay]) do
"relay"
end,
@@ -78,7 +92,15 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
"safe_dm_mentions"
end,
"pleroma_emoji_reactions",
- "pleroma_chat_messages"
+ "pleroma_custom_emoji_reactions",
+ "pleroma_chat_messages",
+ if Config.get([:instance, :show_reactions]) do
+ "exposable_reactions"
+ end,
+ if Config.get([:instance, :profile_directory]) do
+ "profile_directory"
+ end,
+ "pleroma:get:main/ostatus"
]
|> Enum.filter(& &1)
end
@@ -90,7 +112,20 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
{:ok, data} = MRF.describe()
data
- |> Map.merge(%{quarantined_instances: quarantined})
+ |> Map.put(
+ :quarantined_instances,
+ Enum.map(quarantined, fn {instance, _reason} -> instance end)
+ )
+ # This is for backwards compatibility. We originally didn't sent
+ # extra info like a reason why an instance was rejected/quarantined/etc.
+ # Because we didn't want to break backwards compatibility it was decided
+ # to add an extra "info" key.
+ |> Map.put(:quarantined_instances_info, %{
+ "quarantined_instances" =>
+ quarantined
+ |> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end)
+ |> Map.new()
+ })
else
%{}
end
diff --git a/lib/pleroma/web/mastodon_api/views/list_view.ex b/lib/pleroma/web/mastodon_api/views/list_view.ex
index 931e77769..a7ae7c5f7 100644
--- a/lib/pleroma/web/mastodon_api/views/list_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/list_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ListView do
diff --git a/lib/pleroma/web/mastodon_api/views/marker_view.ex b/lib/pleroma/web/mastodon_api/views/marker_view.ex
index 0c1880935..944769b1a 100644
--- a/lib/pleroma/web/mastodon_api/views/marker_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/marker_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MarkerView do
diff --git a/lib/pleroma/web/mastodon_api/views/media_view.ex b/lib/pleroma/web/mastodon_api/views/media_view.ex
new file mode 100644
index 000000000..4db72de8f
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/media_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MediaView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.StatusView.render(view, opts)
+end
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index df9bedfed..2a51f3755 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.NotificationView do
@@ -17,9 +17,14 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.NotificationView
alias Pleroma.Web.MastodonAPI.StatusView
+ alias Pleroma.Web.MediaProxy
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
- @parent_types ~w{Like Announce EmojiReact}
+ defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id
+
+ defp object_id_for(%{data: %{"object" => id}}) when is_binary(id), do: id
+
+ @parent_types ~w{Like Announce EmojiReact Update}
def render("index.json", %{notifications: notifications, for: reading_user} = opts) do
activities = Enum.map(notifications, & &1.activity)
@@ -30,7 +35,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
%{data: %{"type" => type}} ->
type in @parent_types
end)
- |> Enum.map(& &1.data["object"])
+ |> Enum.map(&object_id_for/1)
|> Activity.create_by_object_ap_id()
|> Activity.with_preloaded_object(:left)
|> Pleroma.Repo.all()
@@ -78,9 +83,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
parent_activity_fn = fn ->
if opts[:parent_activities] do
- Activity.Queries.find_by_object_ap_id(opts[:parent_activities], activity.data["object"])
+ Activity.Queries.find_by_object_ap_id(opts[:parent_activities], object_id_for(activity))
else
- Activity.get_create_by_object_ap_id(activity.data["object"])
+ Activity.get_create_by_object_ap_id(object_id_for(activity))
end
end
@@ -109,9 +114,15 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
"reblog" ->
put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
+ "update" ->
+ put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
+
"move" ->
put_target(response, activity, reading_user, %{})
+ "poll" ->
+ put_status(response, activity, reading_user, status_render_opts)
+
"pleroma:emoji_reaction" ->
response
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
@@ -135,7 +146,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
end
defp put_emoji(response, activity) do
- Map.put(response, :emoji, activity.data["content"])
+ response
+ |> Map.put(:emoji, activity.data["content"])
+ |> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))
end
defp put_chat_message(response, activity, reading_user, opts) do
diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex
index 71bc8b949..34e23873e 100644
--- a/lib/pleroma/web/mastodon_api/views/poll_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.PollView do
diff --git a/lib/pleroma/web/mastodon_api/views/report_view.ex b/lib/pleroma/web/mastodon_api/views/report_view.ex
index 0ff347ade..983f7bd24 100644
--- a/lib/pleroma/web/mastodon_api/views/report_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/report_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ReportView do
diff --git a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex
index 453221f41..772d22f0c 100644
--- a/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/scheduled_activity_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityView do
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 814b3d142..dea22f9c2 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.StatusView do
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Activity
alias Pleroma.HTML
+ alias Pleroma.Maps
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
@@ -56,19 +57,35 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end)
end
- defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id),
- do: context_id
-
- defp get_context_id(%{data: %{"context" => context}}) when is_binary(context),
- do: Utils.context_to_conversation_id(context)
+ # DEPRECATED This field seems to be a left-over from the StatusNet era.
+ # If your application uses `pleroma.conversation_id`: this field is deprecated.
+ # It is currently stubbed instead by doing a CRC32 of the context, and
+ # clearing the MSB to avoid overflow exceptions with signed integers on the
+ # different clients using this field (Java/Kotlin code, mostly; see Husky.)
+ # This should be removed in a future version of Pleroma. Pleroma-FE currently
+ # depends on this field, as well.
+ defp get_context_id(%{data: %{"context" => context}}) when is_binary(context) do
+ import Bitwise
+
+ :erlang.crc32(context)
+ |> band(bnot(0x8000_0000))
+ end
defp get_context_id(_), do: nil
- defp reblogged?(activity, user) do
- object = Object.normalize(activity, fetch: false) || %{}
- present?(user && user.ap_id in (object.data["announcements"] || []))
+ # Check if the user reblogged this status
+ defp reblogged?(activity, %User{ap_id: ap_id}) do
+ with %Object{data: %{"announcements" => announcements}} when is_list(announcements) <-
+ Object.normalize(activity, fetch: false) do
+ ap_id in announcements
+ else
+ _ -> false
+ end
end
+ # False if the user is logged out
+ defp reblogged?(_activity, _user), do: false
+
def render("index.json", opts) do
reading_user = opts[:for]
@@ -249,27 +266,47 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
created_at = Utils.to_masto_date(object.data["published"])
+ edited_at =
+ with %{"updated" => updated} <- object.data,
+ date <- Utils.to_masto_date(updated),
+ true <- date != "" do
+ date
+ else
+ _ ->
+ nil
+ end
+
reply_to = get_reply_to(activity, opts)
reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"])
+ history_len =
+ 1 +
+ (Object.Updater.history_for(object.data)
+ |> Map.get("orderedItems")
+ |> length())
+
+ # See render("history.json", ...) for more details
+ # Here the implicit index of the current content is 0
+ chrono_order = history_len - 1
+
content =
object
|> render_content()
content_html =
content
- |> HTML.get_cached_scrubbed_html_for_activity(
+ |> Activity.HTML.get_cached_scrubbed_html_for_activity(
User.html_filter_policy(opts[:for]),
activity,
- "mastoapi:content"
+ "mastoapi:content:#{chrono_order}"
)
content_plaintext =
content
- |> HTML.get_cached_stripped_html_for_activity(
+ |> Activity.HTML.get_cached_stripped_html_for_activity(
activity,
- "mastoapi:content"
+ "mastoapi:content:#{chrono_order}"
)
summary = object.data["summary"] || ""
@@ -297,14 +334,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
emoji_reactions =
- object.data
- |> Map.get("reactions", [])
+ object
+ |> Object.get_emoji_reactions()
|> EmojiReactionController.filter_allowed_users(
opts[:for],
Map.get(opts, :with_muted, false)
)
- |> Stream.map(fn {emoji, users} ->
- build_emoji_map(emoji, users, opts[:for])
+ |> Stream.map(fn {emoji, users, url} ->
+ build_emoji_map(emoji, users, url, opts[:for])
end)
|> Enum.to_list()
@@ -335,8 +372,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
reblog: nil,
card: card,
content: content_html,
- text: opts[:with_source] && object.data["source"],
+ text: opts[:with_source] && get_source_text(object.data["source"]),
created_at: created_at,
+ edited_at: edited_at,
reblogs_count: announcement_count,
replies_count: object.data["repliesCount"] || 0,
favourites_count: like_count,
@@ -358,6 +396,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
pleroma: %{
local: activity.local,
conversation_id: get_context_id(activity),
+ context: object.data["context"],
in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,
content: %{"text/plain" => content_plaintext},
spoiler_text: %{"text/plain" => summary},
@@ -375,6 +414,100 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
nil
end
+ def render("history.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
+ object = Object.normalize(activity, fetch: false)
+
+ hashtags = Object.hashtags(object)
+
+ user = CommonAPI.get_user(activity.data["actor"])
+
+ past_history =
+ Object.Updater.history_for(object.data)
+ |> Map.get("orderedItems")
+ |> Enum.map(&Map.put(&1, "id", object.data["id"]))
+ |> Enum.map(&%Object{data: &1, id: object.id})
+
+ history =
+ [object | past_history]
+ # Mastodon expects the original to be at the first
+ |> Enum.reverse()
+ |> Enum.with_index()
+ |> Enum.map(fn {object, chrono_order} ->
+ %{
+ # The history is prepended every time there is a new edit.
+ # In chrono_order, the oldest item is always at 0, and so on.
+ # The chrono_order is an invariant kept between edits.
+ chrono_order: chrono_order,
+ object: object
+ }
+ end)
+
+ individual_opts =
+ opts
+ |> Map.put(:as, :item)
+ |> Map.put(:user, user)
+ |> Map.put(:hashtags, hashtags)
+
+ render_many(history, StatusView, "history_item.json", individual_opts)
+ end
+
+ def render(
+ "history_item.json",
+ %{
+ activity: activity,
+ user: user,
+ item: %{object: object, chrono_order: chrono_order},
+ hashtags: hashtags
+ } = opts
+ ) do
+ sensitive = object.data["sensitive"] || Enum.member?(hashtags, "nsfw")
+
+ attachment_data = object.data["attachment"] || []
+ attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
+
+ created_at = Utils.to_masto_date(object.data["updated"] || object.data["published"])
+
+ content =
+ object
+ |> render_content()
+
+ content_html =
+ content
+ |> Activity.HTML.get_cached_scrubbed_html_for_activity(
+ User.html_filter_policy(opts[:for]),
+ activity,
+ "mastoapi:content:#{chrono_order}"
+ )
+
+ summary = object.data["summary"] || ""
+
+ %{
+ account:
+ AccountView.render("show.json", %{
+ user: user,
+ for: opts[:for]
+ }),
+ content: content_html,
+ sensitive: sensitive,
+ spoiler_text: summary,
+ created_at: created_at,
+ media_attachments: attachments,
+ emojis: build_emojis(object.data["emoji"]),
+ poll: render(PollView, "show.json", object: object, for: opts[:for])
+ }
+ end
+
+ def render("source.json", %{activity: %{data: %{"object" => _object}} = activity} = _opts) do
+ object = Object.normalize(activity, fetch: false)
+
+ %{
+ id: activity.id,
+ text: get_source_text(Map.get(object.data, "source", "")),
+ spoiler_text: Map.get(object.data, "summary", ""),
+ content_type: get_source_content_type(object.data["source"])
+ }
+ end
+
def render("card.json", %{rich_media: rich_media, page_url: page_url}) do
page_url_data = URI.parse(page_url)
@@ -417,6 +550,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image"
href = attachment_url["href"] |> MediaProxy.url()
href_preview = attachment_url["href"] |> MediaProxy.preview_url()
+ meta = render("attachment_meta.json", %{attachment: attachment})
type =
cond do
@@ -426,10 +560,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
true -> "unknown"
end
- <<hash_id::signed-32, _rest::binary>> = :crypto.hash(:md5, href)
+ attachment_id =
+ with {_, ap_id} when is_binary(ap_id) <- {:ap_id, attachment["id"]},
+ {_, %Object{data: _object_data, id: object_id}} <-
+ {:object, Object.get_by_ap_id(ap_id)} do
+ to_string(object_id)
+ else
+ _ ->
+ <<hash_id::signed-32, _rest::binary>> = :crypto.hash(:md5, href)
+ to_string(attachment["id"] || hash_id)
+ end
%{
- id: to_string(attachment["id"] || hash_id),
+ id: attachment_id,
url: href,
remote_url: href,
preview_url: href_preview,
@@ -439,8 +582,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
pleroma: %{mime_type: media_type},
blurhash: attachment["blurhash"]
}
+ |> Maps.put_if_present(:meta, meta)
end
+ def render("attachment_meta.json", %{
+ attachment: %{"url" => [%{"width" => width, "height" => height} | _]}
+ })
+ when is_integer(width) and is_integer(height) do
+ %{
+ original: %{
+ width: width,
+ height: height,
+ aspect: width / height
+ }
+ }
+ end
+
+ def render("attachment_meta.json", _), do: nil
+
def render("context.json", %{activity: activity, activities: activities, user: user}) do
%{ancestors: ancestors, descendants: descendants} =
activities
@@ -496,7 +655,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def build_tags(object_tags) when is_list(object_tags) do
object_tags
|> Enum.filter(&is_binary/1)
- |> Enum.map(&%{name: &1, url: "#{Pleroma.Web.base_url()}/tag/#{URI.encode(&1)}"})
+ |> Enum.map(&%{name: &1, url: "#{Pleroma.Web.Endpoint.url()}/tag/#{URI.encode(&1)}"})
end
def build_tags(_), do: []
@@ -543,11 +702,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
end
- defp build_emoji_map(emoji, users, current_user) do
+ defp build_emoji_map(emoji, users, url, current_user) do
%{
- name: emoji,
+ name: Pleroma.Web.PleromaAPI.EmojiReactionView.emoji_name(emoji, url),
count: length(users),
- me: !!(current_user && current_user.ap_id in users)
+ url: MediaProxy.url(url),
+ me: !!(current_user && current_user.ap_id in users),
+ account_ids: Enum.map(users, fn user -> User.get_cached_by_ap_id(user).id end)
}
end
@@ -575,4 +736,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end
defp build_image_url(_, _), do: nil
+
+ defp get_source_text(%{"content" => content} = _source) do
+ content
+ end
+
+ defp get_source_text(source) when is_binary(source) do
+ source
+ end
+
+ defp get_source_text(_) do
+ ""
+ end
+
+ defp get_source_content_type(%{"mediaType" => type} = _source) do
+ type
+ end
+
+ defp get_source_content_type(_source) do
+ Utils.get_content_type(nil)
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/views/subscription_view.ex b/lib/pleroma/web/mastodon_api/views/subscription_view.ex
index a07d23512..baa1e03bd 100644
--- a/lib/pleroma/web/mastodon_api/views/subscription_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/subscription_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SubscriptionView do
diff --git a/lib/pleroma/web/mastodon_api/views/suggestion_view.ex b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex
new file mode 100644
index 000000000..d3df4ef3f
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/suggestion_view.ex
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.SuggestionView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI.AccountView
+
+ @source_types [:staff, :global, :past_interactions]
+
+ def render("index.json", %{users: users} = opts) do
+ Enum.map(users, fn user ->
+ opts =
+ opts
+ |> Map.put(:user, user)
+ |> Map.delete(:users)
+
+ render("show.json", opts)
+ end)
+ end
+
+ def render("show.json", %{source: source, user: _user} = opts) when source in @source_types do
+ %{
+ source: source,
+ account: AccountView.render("show.json", opts)
+ }
+ end
+end
diff --git a/lib/pleroma/web/mastodon_api/views/timeline_view.ex b/lib/pleroma/web/mastodon_api/views/timeline_view.ex
new file mode 100644
index 000000000..702eb7eeb
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/timeline_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.TimelineView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.StatusView.render(view, opts)
+end
diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex
index 0d1faffbd..88444106d 100644
--- a/lib/pleroma/web/mastodon_api/websocket_handler.ex
+++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
@@ -32,7 +32,8 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
req
end
- {:cowboy_websocket, req, %{user: user, topic: topic, count: 0, timer: nil},
+ {:cowboy_websocket, req,
+ %{user: user, topic: topic, oauth_token: oauth_token, count: 0, timer: nil},
%{idle_timeout: @timeout}}
else
{:error, :bad_topic} ->
@@ -49,12 +50,10 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
def websocket_init(state) do
Logger.debug(
- "#{__MODULE__} accepted websocket connection for user #{
- (state.user || %{id: "anonymous"}).id
- }, topic #{state.topic}"
+ "#{__MODULE__} accepted websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topic #{state.topic}"
)
- Streamer.add_socket(state.topic, state.user)
+ Streamer.add_socket(state.topic, state.oauth_token)
{:ok, %{state | timer: timer()}}
end
@@ -100,15 +99,17 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
{:reply, :ping, %{state | timer: nil, count: 0}, :hibernate}
end
+ def websocket_info(:close, state) do
+ {:stop, state}
+ end
+
# State can be `[]` only in case we terminate before switching to websocket,
# we already log errors for these cases in `init/1`, so just do nothing here
def terminate(_reason, _req, []), do: :ok
def terminate(reason, _req, state) do
Logger.debug(
- "#{__MODULE__} terminating websocket connection for user #{
- (state.user || %{id: "anonymous"}).id
- }, topic #{state.topic || "?"}: #{inspect(reason)}"
+ "#{__MODULE__} terminating websocket connection for user #{(state.user || %{id: "anonymous"}).id}, topic #{state.topic || "?"}: #{inspect(reason)}"
)
Streamer.remove_socket(state.topic)
diff --git a/lib/pleroma/web/media_proxy.ex b/lib/pleroma/web/media_proxy.ex
index d0d4bb4b3..d64760fc2 100644
--- a/lib/pleroma/web/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy.ex
@@ -1,12 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MediaProxy do
alias Pleroma.Config
alias Pleroma.Helpers.UriHelper
alias Pleroma.Upload
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.MediaProxy.Invalidation
@base64_opts [padding: false]
@@ -69,7 +69,7 @@ defmodule Pleroma.Web.MediaProxy do
# non-local non-whitelisted URLs through it and be sure that body size constraint is preserved.
def preview_enabled?, do: enabled?() and !!Config.get([:media_preview_proxy, :enabled])
- def local?(url), do: String.starts_with?(url, Web.base_url())
+ def local?(url), do: String.starts_with?(url, Endpoint.url())
def whitelisted?(url) do
%{host: domain} = URI.parse(url)
@@ -127,7 +127,7 @@ defmodule Pleroma.Web.MediaProxy do
end
defp signed_url(url) do
- :crypto.hmac(:sha, Config.get([Web.Endpoint, :secret_key_base]), url)
+ :crypto.mac(:hmac, :sha, Config.get([Endpoint, :secret_key_base]), url)
end
def filename(url_or_path) do
@@ -135,7 +135,7 @@ defmodule Pleroma.Web.MediaProxy do
end
def base_url do
- Config.get([:media_proxy, :base_url], Web.base_url())
+ Config.get([:media_proxy, :base_url], Endpoint.url())
end
defp proxy_url(path, sig_base64, url_base64, filename) do
diff --git a/lib/pleroma/web/media_proxy/invalidation.ex b/lib/pleroma/web/media_proxy/invalidation.ex
index cb2db5ce9..ea927fe41 100644
--- a/lib/pleroma/web/media_proxy/invalidation.ex
+++ b/lib/pleroma/web/media_proxy/invalidation.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MediaProxy.Invalidation do
diff --git a/lib/pleroma/web/media_proxy/invalidation/http.ex b/lib/pleroma/web/media_proxy/invalidation/http.ex
index 0b2a45518..28ea74991 100644
--- a/lib/pleroma/web/media_proxy/invalidation/http.ex
+++ b/lib/pleroma/web/media_proxy/invalidation/http.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MediaProxy.Invalidation.Http do
diff --git a/lib/pleroma/web/media_proxy/invalidation/script.ex b/lib/pleroma/web/media_proxy/invalidation/script.ex
index 87a21166c..784178f1c 100644
--- a/lib/pleroma/web/media_proxy/invalidation/script.ex
+++ b/lib/pleroma/web/media_proxy/invalidation/script.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MediaProxy.Invalidation.Script do
diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
index c74eaaf93..bda5b36ed 100644
--- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
@@ -12,6 +12,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
alias Pleroma.Web.MediaProxy
alias Plug.Conn
+ plug(:sandbox)
+
def remote(conn, %{"sig" => sig64, "url" => url64}) do
with {_, true} <- {:enabled, MediaProxy.enabled?()},
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
@@ -54,7 +56,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
media_proxy_url = MediaProxy.url(url)
with {:ok, %{status: status} = head_response} when status in 200..299 <-
- Pleroma.HTTP.request("head", media_proxy_url, [], [], pool: :media) do
+ Pleroma.HTTP.request("HEAD", media_proxy_url, [], [], pool: :media) do
content_type = Tesla.get_header(head_response, "content-type")
content_length = Tesla.get_header(head_response, "content-length")
content_length = content_length && String.to_integer(content_length)
@@ -202,4 +204,9 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
defp media_proxy_opts do
Config.get([:media_proxy, :proxy_opts], [])
end
+
+ defp sandbox(conn, _params) do
+ conn
+ |> merge_resp_headers([{"content-security-policy", "sandbox;"}])
+ end
end
diff --git a/lib/pleroma/web/metadata.ex b/lib/pleroma/web/metadata.ex
index 46ef00c08..59d018730 100644
--- a/lib/pleroma/web/metadata.ex
+++ b/lib/pleroma/web/metadata.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata do
diff --git a/lib/pleroma/web/metadata/player_view.ex b/lib/pleroma/web/metadata/player_view.ex
index 9be5e433d..59c56a236 100644
--- a/lib/pleroma/web/metadata/player_view.ex
+++ b/lib/pleroma/web/metadata/player_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.PlayerView do
diff --git a/lib/pleroma/web/metadata/providers/feed.ex b/lib/pleroma/web/metadata/providers/feed.ex
index d0ab5c19e..e97d6a54f 100644
--- a/lib/pleroma/web/metadata/providers/feed.ex
+++ b/lib/pleroma/web/metadata/providers/feed.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.Feed do
diff --git a/lib/pleroma/web/metadata/providers/open_graph.ex b/lib/pleroma/web/metadata/providers/open_graph.ex
index 1687b2634..97d3865ed 100644
--- a/lib/pleroma/web/metadata/providers/open_graph.ex
+++ b/lib/pleroma/web/metadata/providers/open_graph.ex
@@ -1,9 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
alias Pleroma.User
+ alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Metadata
alias Pleroma.Web.Metadata.Providers.Provider
alias Pleroma.Web.Metadata.Utils
@@ -19,37 +20,24 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
}) do
attachments = build_attachments(object)
scrubbed_content = Utils.scrub_html_and_truncate(object)
- # Zero width space
- content =
- if scrubbed_content != "" and scrubbed_content != "\u200B" do
- ": “" <> scrubbed_content <> "”"
- else
- ""
- end
- # Most previews only show og:title which is inconvenient. Instagram
- # hacks this by putting the description in the title and making the
- # description longer prefixed by how many likes and shares the post
- # has. Here we use the descriptive nickname in the title, and expand
- # the full account & nickname in the description. We also use the cute^Wevil
- # smart quotes around the status text like Instagram, too.
[
{:meta,
[
property: "og:title",
- content: "#{user.name}" <> content
+ content: Utils.user_name_string(user)
], []},
{:meta, [property: "og:url", content: url], []},
{:meta,
[
property: "og:description",
- content: "#{Utils.user_name_string(user)}" <> content
+ content: scrubbed_content
], []},
- {:meta, [property: "og:type", content: "website"], []}
+ {:meta, [property: "og:type", content: "article"], []}
] ++
if attachments == [] or Metadata.activity_nsfw?(object) do
[
- {:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))],
+ {:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
[]},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
@@ -70,8 +58,9 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
], []},
{:meta, [property: "og:url", content: user.uri || user.ap_id], []},
{:meta, [property: "og:description", content: truncated_bio], []},
- {:meta, [property: "og:type", content: "website"], []},
- {:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))], []},
+ {:meta, [property: "og:type", content: "article"], []},
+ {:meta, [property: "og:image", content: MediaProxy.preview_url(User.avatar_url(user))],
+ []},
{:meta, [property: "og:image:width", content: 150], []},
{:meta, [property: "og:image:height", content: 150], []}
]
@@ -82,29 +71,35 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
Enum.reduce(attachments, [], fn attachment, acc ->
rendered_tags =
Enum.reduce(attachment["url"], [], fn url, acc ->
- # TODO: Add additional properties to objects when we have the data available.
- # Also, Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
+ # TODO: Whatsapp only wants JPEG or PNGs. It seems that if we add a second og:image
# object when a Video or GIF is attached it will display that in Whatsapp Rich Preview.
case Utils.fetch_media_type(@media_types, url["mediaType"]) do
"audio" ->
[
- {:meta, [property: "og:audio", content: Utils.attachment_url(url["href"])], []}
+ {:meta, [property: "og:audio", content: MediaProxy.url(url["href"])], []}
| acc
]
+ # Not using preview_url for this. It saves bandwidth, but the image dimensions will
+ # be wrong. We generate it on the fly and have no way to capture or analyze the
+ # image to get the dimensions. This can be an issue for apps/FEs rendering images
+ # in timelines too, but you can get clever with the aspect ratio metadata as a
+ # workaround.
"image" ->
[
- {:meta, [property: "og:image", content: Utils.attachment_url(url["href"])], []},
- {:meta, [property: "og:image:width", content: 150], []},
- {:meta, [property: "og:image:height", content: 150], []}
+ {:meta, [property: "og:image", content: MediaProxy.url(url["href"])], []},
+ {:meta, [property: "og:image:alt", content: attachment["name"]], []}
| acc
]
+ |> maybe_add_dimensions(url)
"video" ->
[
- {:meta, [property: "og:video", content: Utils.attachment_url(url["href"])], []}
+ {:meta, [property: "og:video", content: MediaProxy.url(url["href"])], []}
| acc
]
+ |> maybe_add_dimensions(url)
+ |> maybe_add_video_thumbnail(url)
_ ->
acc
@@ -116,4 +111,38 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
end
defp build_attachments(_), do: []
+
+ # We can use url["mediaType"] to dynamically fill the metadata
+ defp maybe_add_dimensions(metadata, url) do
+ type = url["mediaType"] |> String.split("/") |> List.first()
+
+ cond do
+ !is_nil(url["height"]) && !is_nil(url["width"]) ->
+ metadata ++
+ [
+ {:meta, [property: "og:#{type}:width", content: "#{url["width"]}"], []},
+ {:meta, [property: "og:#{type}:height", content: "#{url["height"]}"], []}
+ ]
+
+ true ->
+ metadata
+ end
+ end
+
+ # Media Preview Proxy makes thumbnails of videos without resizing, so we can trust the
+ # width and height of the source video.
+ defp maybe_add_video_thumbnail(metadata, url) do
+ cond do
+ Pleroma.Config.get([:media_preview_proxy, :enabled], false) ->
+ metadata ++
+ [
+ {:meta, [property: "og:image:width", content: "#{url["width"]}"], []},
+ {:meta, [property: "og:image:height", content: "#{url["height"]}"], []},
+ {:meta, [property: "og:image", content: MediaProxy.preview_url(url["href"])], []}
+ ]
+
+ true ->
+ metadata
+ end
+ end
end
diff --git a/lib/pleroma/web/metadata/providers/provider.ex b/lib/pleroma/web/metadata/providers/provider.ex
index c91d87c6d..bb31c4d83 100644
--- a/lib/pleroma/web/metadata/providers/provider.ex
+++ b/lib/pleroma/web/metadata/providers/provider.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.Provider do
diff --git a/lib/pleroma/web/metadata/providers/rel_me.ex b/lib/pleroma/web/metadata/providers/rel_me.ex
index f013def51..eabd8cb00 100644
--- a/lib/pleroma/web/metadata/providers/rel_me.ex
+++ b/lib/pleroma/web/metadata/providers/rel_me.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.RelMe do
@@ -8,12 +8,20 @@ defmodule Pleroma.Web.Metadata.Providers.RelMe do
@impl Provider
def build_tags(%{user: user}) do
- bio_tree = Floki.parse_fragment!(user.bio)
+ profile_tree =
+ user.bio
+ |> append_fields_tag(user.fields)
+ |> Floki.parse_fragment!()
- (Floki.attribute(bio_tree, "link[rel~=me]", "href") ++
- Floki.attribute(bio_tree, "a[rel~=me]", "href"))
+ (Floki.attribute(profile_tree, "link[rel~=me]", "href") ++
+ Floki.attribute(profile_tree, "a[rel~=me]", "href"))
|> Enum.map(fn link ->
{:link, [rel: "me", href: link], []}
end)
end
+
+ defp append_fields_tag(bio, fields) do
+ fields
+ |> Enum.reduce(bio, fn %{"value" => v}, res -> res <> v end)
+ end
end
diff --git a/lib/pleroma/web/metadata/providers/restrict_indexing.ex b/lib/pleroma/web/metadata/providers/restrict_indexing.ex
index aa6511610..a43a7c92e 100644
--- a/lib/pleroma/web/metadata/providers/restrict_indexing.ex
+++ b/lib/pleroma/web/metadata/providers/restrict_indexing.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.RestrictIndexing do
diff --git a/lib/pleroma/web/metadata/providers/twitter_card.ex b/lib/pleroma/web/metadata/providers/twitter_card.ex
index 58fc05cf9..426022c65 100644
--- a/lib/pleroma/web/metadata/providers/twitter_card.ex
+++ b/lib/pleroma/web/metadata/providers/twitter_card.ex
@@ -1,10 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
alias Pleroma.User
+ alias Pleroma.Web.MediaProxy
alias Pleroma.Web.Metadata
alias Pleroma.Web.Metadata.Providers.Provider
alias Pleroma.Web.Metadata.Utils
@@ -16,22 +17,15 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
def build_tags(%{activity_id: id, object: object, user: user}) do
attachments = build_attachments(id, object)
scrubbed_content = Utils.scrub_html_and_truncate(object)
- # Zero width space
- content =
- if scrubbed_content != "" and scrubbed_content != "\u200B" do
- "“" <> scrubbed_content <> "”"
- else
- ""
- end
[
title_tag(user),
- {:meta, [property: "twitter:description", content: content], []}
+ {:meta, [name: "twitter:description", content: scrubbed_content], []}
] ++
if attachments == [] or Metadata.activity_nsfw?(object) do
[
image_tag(user),
- {:meta, [property: "twitter:card", content: "summary"], []}
+ {:meta, [name: "twitter:card", content: "summary"], []}
]
else
attachments
@@ -43,55 +37,67 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
[
title_tag(user),
- {:meta, [property: "twitter:description", content: truncated_bio], []},
+ {:meta, [name: "twitter:description", content: truncated_bio], []},
image_tag(user),
- {:meta, [property: "twitter:card", content: "summary"], []}
+ {:meta, [name: "twitter:card", content: "summary"], []}
]
end
end
defp title_tag(user) do
- {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []}
+ {:meta, [name: "twitter:title", content: Utils.user_name_string(user)], []}
end
def image_tag(user) do
- {:meta, [property: "twitter:image", content: Utils.attachment_url(User.avatar_url(user))], []}
+ {:meta, [name: "twitter:image", content: MediaProxy.preview_url(User.avatar_url(user))], []}
end
defp build_attachments(id, %{data: %{"attachment" => attachments}}) do
Enum.reduce(attachments, [], fn attachment, acc ->
rendered_tags =
Enum.reduce(attachment["url"], [], fn url, acc ->
- # TODO: Add additional properties to objects when we have the data available.
case Utils.fetch_media_type(@media_types, url["mediaType"]) do
"audio" ->
[
- {:meta, [property: "twitter:card", content: "player"], []},
- {:meta, [property: "twitter:player:width", content: "480"], []},
- {:meta, [property: "twitter:player:height", content: "80"], []},
- {:meta, [property: "twitter:player", content: player_url(id)], []}
+ {:meta, [name: "twitter:card", content: "player"], []},
+ {:meta, [name: "twitter:player:width", content: "480"], []},
+ {:meta, [name: "twitter:player:height", content: "80"], []},
+ {:meta, [name: "twitter:player", content: player_url(id)], []}
| acc
]
+ # Not using preview_url for this. It saves bandwidth, but the image dimensions will
+ # be wrong. We generate it on the fly and have no way to capture or analyze the
+ # image to get the dimensions. This can be an issue for apps/FEs rendering images
+ # in timelines too, but you can get clever with the aspect ratio metadata as a
+ # workaround.
"image" ->
[
- {:meta, [property: "twitter:card", content: "summary_large_image"], []},
+ {:meta, [name: "twitter:card", content: "summary_large_image"], []},
{:meta,
[
- property: "twitter:player",
- content: Utils.attachment_url(url["href"])
- ], []}
+ name: "twitter:image",
+ content: MediaProxy.url(url["href"])
+ ], []},
+ {:meta, [name: "twitter:image:alt", content: truncate(attachment["name"])], []}
| acc
]
+ |> maybe_add_dimensions(url)
- # TODO: Need the true width and height values here or Twitter renders an iFrame with
- # a bad aspect ratio
"video" ->
+ # fallback to old placeholder values
+ height = url["height"] || 480
+ width = url["width"] || 480
+
[
- {:meta, [property: "twitter:card", content: "player"], []},
- {:meta, [property: "twitter:player", content: player_url(id)], []},
- {:meta, [property: "twitter:player:width", content: "480"], []},
- {:meta, [property: "twitter:player:height", content: "480"], []}
+ {:meta, [name: "twitter:card", content: "player"], []},
+ {:meta, [name: "twitter:player", content: player_url(id)], []},
+ {:meta, [name: "twitter:player:width", content: "#{width}"], []},
+ {:meta, [name: "twitter:player:height", content: "#{height}"], []},
+ {:meta, [name: "twitter:player:stream", content: MediaProxy.url(url["href"])],
+ []},
+ {:meta, [name: "twitter:player:stream:content_type", content: url["mediaType"]],
+ []}
| acc
]
@@ -109,4 +115,28 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
defp player_url(id) do
Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id)
end
+
+ # Videos have problems without dimensions, but we used to not provide WxH for images.
+ # A default (read: incorrect) fallback for images is likely to cause rendering bugs.
+ defp maybe_add_dimensions(metadata, url) do
+ cond do
+ !is_nil(url["height"]) && !is_nil(url["width"]) ->
+ metadata ++
+ [
+ {:meta, [name: "twitter:player:width", content: "#{url["width"]}"], []},
+ {:meta, [name: "twitter:player:height", content: "#{url["height"]}"], []}
+ ]
+
+ true ->
+ metadata
+ end
+ end
+
+ defp truncate(nil), do: ""
+
+ defp truncate(text) do
+ # truncate to 420 characters
+ # see https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
+ Pleroma.Formatter.truncate(text, 420)
+ end
end
diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex
index de7195435..80a8be9a2 100644
--- a/lib/pleroma/web/metadata/utils.ex
+++ b/lib/pleroma/web/metadata/utils.ex
@@ -1,30 +1,42 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Utils do
+ alias Pleroma.Activity
alias Pleroma.Emoji
alias Pleroma.Formatter
alias Pleroma.HTML
- alias Pleroma.Web.MediaProxy
- def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
- content
+ defp scrub_html_and_truncate_object_field(field, object) do
+ field
# html content comes from DB already encoded, decode first and scrub after
|> HtmlEntities.decode()
|> String.replace(~r/<br\s?\/?>/, " ")
- |> HTML.get_cached_stripped_html_for_activity(object, "metadata")
+ |> Activity.HTML.get_cached_stripped_html_for_activity(object, "metadata")
|> Emoji.Formatter.demojify()
|> HtmlEntities.decode()
|> Formatter.truncate()
end
- def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
+ def scrub_html_and_truncate(%{data: %{"summary" => summary}} = object)
+ when is_binary(summary) and summary != "" do
+ summary
+ |> scrub_html_and_truncate_object_field(object)
+ end
+
+ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
+ content
+ |> scrub_html_and_truncate_object_field(object)
+ end
+
+ def scrub_html_and_truncate(content, max_length \\ 200, omission \\ "...")
+ when is_binary(content) do
content
|> scrub_html
|> Emoji.Formatter.demojify()
|> HtmlEntities.decode()
- |> Formatter.truncate(max_length)
+ |> Formatter.truncate(max_length, omission)
end
def scrub_html(content) when is_binary(content) do
@@ -37,10 +49,6 @@ defmodule Pleroma.Web.Metadata.Utils do
def scrub_html(content), do: content
- def attachment_url(url) do
- MediaProxy.preview_url(url)
- end
-
def user_name_string(user) do
"#{user.name} " <>
if user.local do
diff --git a/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex
index 6ace3e0b5..0945ebb12 100644
--- a/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex
+++ b/lib/pleroma/web/mongoose_im/mongoose_im_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MongooseIM.MongooseIMController do
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex
index 6a0112d2a..9e27ac26c 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
@@ -35,7 +35,9 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
openRegistrations: Config.get([:instance, :registrations_open]),
usage: %{
users: %{
- total: Map.get(stats, :user_count, 0)
+ total: Map.get(stats, :user_count, 0),
+ activeMonth: Pleroma.User.active_user_count(30),
+ activeHalfyear: Pleroma.User.active_user_count(180)
},
localPosts: Map.get(stats, :status_count, 0)
},
@@ -47,6 +49,10 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
enabled: false
},
staffAccounts: staff_accounts,
+ roles: %{
+ admin: Config.get([:instance, :admin_privileges]),
+ moderator: Config.get([:instance, :moderator_privileges])
+ },
federation: federation,
pollLimits: Config.get([:instance, :poll_limits]),
postFormats: Config.get([:instance, :allowed_post_formats]),
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
index bca94d236..85c2393ff 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
@@ -1,11 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
use Pleroma.Web, :controller
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.Nodeinfo.Nodeinfo
def schemas(conn, _params) do
@@ -13,11 +13,11 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
links: [
%{
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
- href: Web.base_url() <> "/nodeinfo/2.0.json"
+ href: Endpoint.url() <> "/nodeinfo/2.0.json"
},
%{
rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
- href: Web.base_url() <> "/nodeinfo/2.1.json"
+ href: Endpoint.url() <> "/nodeinfo/2.1.json"
}
]
}
diff --git a/lib/pleroma/web/o_auth.ex b/lib/pleroma/web/o_auth.ex
index 3bc1a6ad4..d8c68df66 100644
--- a/lib/pleroma/web/o_auth.ex
+++ b/lib/pleroma/web/o_auth.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth do
diff --git a/lib/pleroma/web/o_auth/app.ex b/lib/pleroma/web/o_auth/app.ex
index 382750010..0aa655381 100644
--- a/lib/pleroma/web/o_auth/app.ex
+++ b/lib/pleroma/web/o_auth/app.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.App do
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
import Ecto.Changeset
import Ecto.Query
alias Pleroma.Repo
+ alias Pleroma.User
@type t :: %__MODULE__{}
@@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do
field(:client_secret, :string)
field(:trusted, :boolean, default: false)
+ belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
+
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
@@ -27,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do
@spec changeset(t(), map()) :: Ecto.Changeset.t()
def changeset(struct, params) do
- cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
+ cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id])
end
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
@@ -129,6 +132,12 @@ defmodule Pleroma.Web.OAuth.App do
{:ok, Repo.all(query), count}
end
+ @spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()}
+ def get_user_apps(%User{id: user_id}) do
+ from(a in __MODULE__, where: a.user_id == ^user_id)
+ |> Repo.all()
+ end
+
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
def destroy(id) do
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
diff --git a/lib/pleroma/web/o_auth/authorization.ex b/lib/pleroma/web/o_auth/authorization.ex
index e0ecb0f4f..593d2d66f 100644
--- a/lib/pleroma/web/o_auth/authorization.ex
+++ b/lib/pleroma/web/o_auth/authorization.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Authorization do
diff --git a/lib/pleroma/web/o_auth/fallback_controller.ex b/lib/pleroma/web/o_auth/fallback_controller.ex
index df68cbfc1..684a52c81 100644
--- a/lib/pleroma/web/o_auth/fallback_controller.ex
+++ b/lib/pleroma/web/o_auth/fallback_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.FallbackController do
diff --git a/lib/pleroma/web/o_auth/mfa_controller.ex b/lib/pleroma/web/o_auth/mfa_controller.ex
index b38b00213..c4bc4a879 100644
--- a/lib/pleroma/web/o_auth/mfa_controller.ex
+++ b/lib/pleroma/web/o_auth/mfa_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.MFAController do
diff --git a/lib/pleroma/web/o_auth/mfa_view.ex b/lib/pleroma/web/o_auth/mfa_view.ex
index 3d473f29c..bcadafe57 100644
--- a/lib/pleroma/web/o_auth/mfa_view.ex
+++ b/lib/pleroma/web/o_auth/mfa_view.ex
@@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.MFAView do
use Pleroma.Web, :view
import Phoenix.HTML.Form
alias Pleroma.MFA
+ alias Pleroma.Web.Gettext
def render("mfa_response.json", %{token: token, user: user}) do
%{
diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex
index 215d97b3a..c1fb4f378 100644
--- a/lib/pleroma/web/o_auth/o_auth_controller.ex
+++ b/lib/pleroma/web/o_auth/o_auth_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.OAuthController do
@@ -12,8 +12,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
alias Pleroma.Registration
alias Pleroma.Repo
alias Pleroma.User
- alias Pleroma.Web.Auth.Authenticator
- alias Pleroma.Web.ControllerHelper
+ alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.MFAController
@@ -24,6 +23,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
alias Pleroma.Web.Plugs.RateLimiter
+ alias Pleroma.Web.Utils.Params
require Logger
@@ -32,10 +32,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
plug(:fetch_session)
plug(:fetch_flash)
- plug(:skip_plug, [
- Pleroma.Web.Plugs.OAuthScopesPlug,
- Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
- ])
+ plug(:skip_auth)
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
@@ -50,7 +47,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
def authorize(%Plug.Conn{assigns: %{token: %Token{}}} = conn, %{"force_login" => _} = params) do
- if ControllerHelper.truthy_param?(params["force_login"]) do
+ if Params.truthy_param?(params["force_login"]) do
do_authorize(conn, params)
else
handle_existing_authorization(conn, params)
@@ -427,7 +424,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|> Map.put("state", state)
# Handing the request to Ueberauth
- redirect(conn, to: o_auth_path(conn, :request, provider, params))
+ redirect(conn, to: Routes.o_auth_path(conn, :request, provider, params))
end
def request(%Plug.Conn{} = conn, params) do
@@ -600,9 +597,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do
end
end
- # Special case: Local MastodonFE
- defp redirect_uri(%Plug.Conn{} = conn, "."), do: auth_url(conn, :login)
-
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :registration_id)
diff --git a/lib/pleroma/web/o_auth/o_auth_view.ex b/lib/pleroma/web/o_auth/o_auth_view.ex
index 1419c96a2..108102ce2 100644
--- a/lib/pleroma/web/o_auth/o_auth_view.ex
+++ b/lib/pleroma/web/o_auth/o_auth_view.ex
@@ -1,10 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.OAuthView do
use Pleroma.Web, :view
import Phoenix.HTML.Form
+ import Phoenix.HTML
+ alias Pleroma.Web.Gettext
alias Pleroma.Web.OAuth.Token.Utils
diff --git a/lib/pleroma/web/o_auth/scopes.ex b/lib/pleroma/web/o_auth/scopes.ex
index ada43eae9..f33fc21c4 100644
--- a/lib/pleroma/web/o_auth/scopes.ex
+++ b/lib/pleroma/web/o_auth/scopes.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Scopes do
diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex
index 9d69e9db4..26de7bb10 100644
--- a/lib/pleroma/web/o_auth/token.ex
+++ b/lib/pleroma/web/o_auth/token.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token do
diff --git a/lib/pleroma/web/o_auth/token/query.ex b/lib/pleroma/web/o_auth/token/query.ex
index d16a759d8..4a4d2d3ef 100644
--- a/lib/pleroma/web/o_auth/token/query.ex
+++ b/lib/pleroma/web/o_auth/token/query.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Query do
diff --git a/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex
index f5a0ed272..6b0d9503c 100644
--- a/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex
+++ b/lib/pleroma/web/o_auth/token/strategy/refresh_token.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
diff --git a/lib/pleroma/web/o_auth/token/strategy/revoke.ex b/lib/pleroma/web/o_auth/token/strategy/revoke.ex
index 8d6572704..3b265b339 100644
--- a/lib/pleroma/web/o_auth/token/strategy/revoke.ex
+++ b/lib/pleroma/web/o_auth/token/strategy/revoke.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do
@@ -21,6 +21,18 @@ defmodule Pleroma.Web.OAuth.Token.Strategy.Revoke do
@doc "Revokes access token"
@spec revoke(Token.t()) :: {:ok, Token.t()} | {:error, Ecto.Changeset.t()}
def revoke(%Token{} = token) do
- Repo.delete(token)
+ with {:ok, token} <- Repo.delete(token) do
+ Task.Supervisor.start_child(
+ Pleroma.TaskSupervisor,
+ Pleroma.Web.Streamer,
+ :close_streams_by_oauth_token,
+ [token],
+ restart: :transient
+ )
+
+ {:ok, token}
+ else
+ result -> result
+ end
end
end
diff --git a/lib/pleroma/web/o_auth/token/utils.ex b/lib/pleroma/web/o_auth/token/utils.ex
index b572dc9cf..773cd9792 100644
--- a/lib/pleroma/web/o_auth/token/utils.ex
+++ b/lib/pleroma/web/o_auth/token/utils.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OAuth.Token.Utils do
diff --git a/lib/pleroma/web/o_status/o_status_controller.ex b/lib/pleroma/web/o_status/o_status_controller.ex
index da3264149..ea4994bd0 100644
--- a/lib/pleroma/web/o_status/o_status_controller.ex
+++ b/lib/pleroma/web/o_status/o_status_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.OStatus.OStatusController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
index 165afd3b4..591391b60 100644
--- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
@@ -1,17 +1,21 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.AccountController do
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper,
- only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
+ only: [
+ json_response: 3,
+ add_link_headers: 2,
+ embed_relationships?: 1,
+ assign_account_by_id: 2
+ ]
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.MastodonAPI.StatusView
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.Plugs.RateLimiter
@@ -29,10 +33,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(
- :skip_plug,
- [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirmation_resend
- )
+ plug(:skip_auth when action == :confirmation_resend)
plug(
OAuthScopesPlug,
@@ -44,10 +45,23 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
)
+ plug(
+ OAuthScopesPlug,
+ %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
+ when action == :endorsements
+ )
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["read:accounts"]} when action == :birthdays
+ )
+
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
- plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
- plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
+ plug(
+ :assign_account_by_id
+ when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
+ )
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
@@ -95,6 +109,22 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
)
end
+ @doc "GET /api/v1/pleroma/accounts/:id/endorsements"
+ def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
+ users =
+ user
+ |> User.endorsed_users_relation(_restrict_deactivated = true)
+ |> Pleroma.Repo.all()
+
+ conn
+ |> render("index.json",
+ for: for_user,
+ users: users,
+ as: :user,
+ embed_relationships: embed_relationships?(params)
+ )
+ end
+
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
@@ -112,4 +142,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
{:error, message} -> json_response(conn, :forbidden, %{error: message})
end
end
+
+ @doc "GET /api/v1/pleroma/birthdays"
+ def birthdays(%{assigns: %{user: %User{} = user}} = conn, %{day: day, month: month} = _params) do
+ birthdays =
+ User.get_friends_birthdays_query(user, day, month)
+ |> Pleroma.Repo.all()
+
+ conn
+ |> render("index.json",
+ for: user,
+ users: birthdays,
+ as: :user
+ )
+ end
end
diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex
new file mode 100644
index 000000000..3e84f75a4
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AppController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Web.OAuth.App
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index])
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation
+
+ @doc "GET /api/v1/pleroma/apps"
+ def index(%{assigns: %{user: user}} = conn, _params) do
+ with apps <- App.get_user_apps(user) do
+ render(conn, "index.json", %{apps: apps})
+ end
+ end
+end
diff --git a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
index fc5d16771..b9daed22b 100644
--- a/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.BackupController do
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupController do
alias Pleroma.Web.Plugs.OAuthScopesPlug
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
- plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create])
+ plug(OAuthScopesPlug, %{scopes: ["read:backups"]} when action in [:index, :create])
plug(Pleroma.Web.ApiSpec.CastAndValidate)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation
diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
index dcd54b1af..3d7b6a4a7 100644
--- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ChatController do
use Pleroma.Web, :controller
@@ -151,7 +151,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
index_query(user, params)
|> Pagination.fetch_paginated(params)
- render(conn, "index.json", chats: chats)
+ conn
+ |> add_link_headers(chats)
+ |> render("index.json", chats: chats)
end
defp index_query(%{id: user_id} = user, params) do
diff --git a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex
index d285e4907..37990db54 100644
--- a/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ConversationController do
@@ -13,7 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.ConversationController do
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:put_view, Pleroma.Web.MastodonAPI.ConversationView)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:show, :statuses])
plug(
diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex
index 204e81311..f854cf9c1 100644
--- a/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/emoji_file_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EmojiFileController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex
index d0f677d3c..420fea12c 100644
--- a/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
@@ -22,11 +22,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
]
)
- @skip_plugs [
- Pleroma.Web.Plugs.OAuthScopesPlug,
- Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
- ]
- plug(:skip_plug, @skip_plugs when action in [:index, :archive, :show])
+ plug(:skip_auth when action in [:index, :archive, :show])
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiPackOperation
diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
index da5f2474f..662cc15d6 100644
--- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
@@ -28,8 +28,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do
with true <- Pleroma.Config.get([:instance, :show_reactions]),
%Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
- %Object{data: %{"reactions" => reactions}} when is_list(reactions) <-
- Object.normalize(activity, fetch: false) do
+ %Object{} = object <- Object.normalize(activity, fetch: false),
+ reactions <- Object.get_emoji_reactions(object) do
reactions =
reactions
|> filter(params)
@@ -50,29 +50,32 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
if not with_muted, do: User.cached_muted_users_ap_ids(user), else: []
end
- filter_emoji = fn emoji, users ->
+ filter_emoji = fn emoji, users, url ->
case Enum.reject(users, &(&1 in exclude_ap_ids)) do
[] -> nil
- users -> {emoji, users}
+ users -> {emoji, users, url}
end
end
reactions
|> Stream.map(fn
- [emoji, users] when is_list(users) -> filter_emoji.(emoji, users)
- {emoji, users} when is_list(users) -> filter_emoji.(emoji, users)
- _ -> nil
+ [emoji, users, url] when is_list(users) -> filter_emoji.(emoji, users, url)
end)
|> Stream.reject(&is_nil/1)
end
defp filter(reactions, %{emoji: emoji}) when is_binary(emoji) do
- Enum.filter(reactions, fn [e, _] -> e == emoji end)
+ Enum.filter(reactions, fn [e, _, _] -> e == emoji end)
end
defp filter(reactions, _), do: reactions
def create(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
+ emoji =
+ emoji
+ |> Pleroma.Emoji.fully_qualify_emoji()
+ |> Pleroma.Emoji.maybe_quote()
+
with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji) do
activity = Activity.get_by_id(activity_id)
@@ -83,6 +86,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
end
def delete(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do
+ emoji =
+ emoji
+ |> Pleroma.Emoji.fully_qualify_emoji()
+ |> Pleroma.Emoji.maybe_quote()
+
with {:ok, _activity} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji) do
activity = Activity.get_by_id(activity_id)
diff --git a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex
index 01424c6ba..6257e3153 100644
--- a/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/instances_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.InstancesController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex
index 429ef5112..66e9d8481 100644
--- a/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.MascotController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
index 257bcd550..87ea81cef 100644
--- a/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/notification_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.NotificationController do
@@ -14,8 +14,6 @@ defmodule Pleroma.Web.PleromaAPI.NotificationController do
%{scopes: ["write:notifications"]} when action == :mark_as_read
)
- plug(:put_view, Pleroma.Web.MastodonAPI.NotificationView)
-
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaNotificationOperation
def mark_as_read(%{assigns: %{user: user}, body_params: %{id: notification_id}} = conn, _) do
diff --git a/lib/pleroma/web/pleroma_api/controllers/report_controller.ex b/lib/pleroma/web/pleroma_api/controllers/report_controller.ex
index d93d7570a..1f0a82cee 100644
--- a/lib/pleroma/web/pleroma_api/controllers/report_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/report_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ReportController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex
index ca26d80ef..bf6dc500c 100644
--- a/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex b/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex
new file mode 100644
index 000000000..1136575b6
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/controllers/settings_controller.ex
@@ -0,0 +1,79 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.SettingsController do
+ use Pleroma.Web, :controller
+
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["write:accounts"]} when action in [:update]
+ )
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["read:accounts"]} when action in [:show]
+ )
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaSettingsOperation
+
+ @doc "GET /api/v1/pleroma/settings/:app"
+ def show(%{assigns: %{user: user}} = conn, %{app: app} = _params) do
+ conn
+ |> json(get_settings(user, app))
+ end
+
+ @doc "PATCH /api/v1/pleroma/settings/:app"
+ def update(%{assigns: %{user: user}, body_params: body_params} = conn, %{app: app} = _params) do
+ settings =
+ get_settings(user, app)
+ |> merge_recursively(body_params)
+
+ with changeset <-
+ Pleroma.User.update_changeset(
+ user,
+ %{pleroma_settings_store: %{app => settings}}
+ ),
+ {:ok, _} <- Pleroma.Repo.update(changeset) do
+ conn
+ |> json(settings)
+ end
+ end
+
+ defp merge_recursively(old, %{} = new) do
+ old = ensure_object(old)
+
+ Enum.reduce(
+ new,
+ old,
+ fn
+ {k, nil}, acc ->
+ Map.drop(acc, [k])
+
+ {k, %{} = new_child}, acc ->
+ Map.put(acc, k, merge_recursively(acc[k], new_child))
+
+ {k, v}, acc ->
+ Map.put(acc, k, v)
+ end
+ )
+ end
+
+ defp get_settings(user, app) do
+ user.pleroma_settings_store
+ |> Map.get(app, %{})
+ |> ensure_object()
+ end
+
+ defp ensure_object(%{} = object) do
+ object
+ end
+
+ defp ensure_object(_) do
+ %{}
+ end
+end
diff --git a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex
index 3940ad581..e69847b00 100644
--- a/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do
diff --git a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex
index 078d470d9..90428a532 100644
--- a/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/user_import_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.UserImportController do
diff --git a/lib/pleroma/web/pleroma_api/views/account_view.ex b/lib/pleroma/web/pleroma_api/views/account_view.ex
new file mode 100644
index 000000000..910bcee16
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/views/account_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AccountView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.AccountView.render(view, opts)
+end
diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex
new file mode 100644
index 000000000..1fdc3c224
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/views/app_view.ex
@@ -0,0 +1,11 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AppView do
+ use Pleroma.Web, :view
+
+ def render("index.json", %{apps: apps}) do
+ render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json")
+ end
+end
diff --git a/lib/pleroma/web/pleroma_api/views/backup_view.ex b/lib/pleroma/web/pleroma_api/views/backup_view.ex
index 944600c86..d778590f0 100644
--- a/lib/pleroma/web/pleroma_api/views/backup_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/backup_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.BackupView do
diff --git a/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex b/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex
index 2e4355992..241bf0010 100644
--- a/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/chat/message_reference_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
diff --git a/lib/pleroma/web/pleroma_api/views/chat_view.ex b/lib/pleroma/web/pleroma_api/views/chat_view.ex
index 3794818a7..db6c13c05 100644
--- a/lib/pleroma/web/pleroma_api/views/chat_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/chat_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ChatView do
diff --git a/lib/pleroma/web/pleroma_api/views/conversation_view.ex b/lib/pleroma/web/pleroma_api/views/conversation_view.ex
new file mode 100644
index 000000000..2c9c8d1c6
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/views/conversation_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.ConversationView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.ConversationView.render(view, opts)
+end
diff --git a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex
index c94527e6d..6df4ab9d0 100644
--- a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do
@@ -7,17 +7,30 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do
alias Pleroma.Web.MastodonAPI.AccountView
+ def emoji_name(emoji, nil), do: emoji
+
+ def emoji_name(emoji, url) do
+ url = URI.parse(url)
+
+ if url.host == Pleroma.Web.Endpoint.host() do
+ emoji
+ else
+ "#{emoji}@#{url.host}"
+ end
+ end
+
def render("index.json", %{emoji_reactions: emoji_reactions} = opts) do
render_many(emoji_reactions, __MODULE__, "show.json", opts)
end
- def render("show.json", %{emoji_reaction: {emoji, user_ap_ids}, user: user}) do
+ def render("show.json", %{emoji_reaction: {emoji, user_ap_ids, url}, user: user}) do
users = fetch_users(user_ap_ids)
%{
- name: emoji,
+ name: emoji_name(emoji, url),
count: length(users),
accounts: render(AccountView, "index.json", users: users, for: user),
+ url: Pleroma.Web.MediaProxy.url(url),
me: !!(user && user.ap_id in user_ap_ids)
}
end
diff --git a/lib/pleroma/web/pleroma_api/views/notification_view.ex b/lib/pleroma/web/pleroma_api/views/notification_view.ex
new file mode 100644
index 000000000..db7f94803
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/views/notification_view.ex
@@ -0,0 +1,10 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.NotificationView do
+ use Pleroma.Web, :view
+ alias Pleroma.Web.MastodonAPI
+
+ def render(view, opts), do: MastodonAPI.NotificationView.render(view, opts)
+end
diff --git a/lib/pleroma/web/pleroma_api/views/report_view.ex b/lib/pleroma/web/pleroma_api/views/report_view.ex
index a0b3f085c..127e8f0cb 100644
--- a/lib/pleroma/web/pleroma_api/views/report_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/report_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ReportView do
diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
index 2bc069529..a5985fb2a 100644
--- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
diff --git a/lib/pleroma/web/plug.ex b/lib/pleroma/web/plug.ex
index dffad3a06..29ab52e55 100644
--- a/lib/pleroma/web/plug.ex
+++ b/lib/pleroma/web/plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plug do
diff --git a/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex
index 976e5cd92..2e4702fa1 100644
--- a/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex
+++ b/lib/pleroma/web/plugs/admin_secret_authentication_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
diff --git a/lib/pleroma/web/plugs/authentication_plug.ex b/lib/pleroma/web/plugs/authentication_plug.ex
index 8d58169cf..f912a1542 100644
--- a/lib/pleroma/web/plugs/authentication_plug.ex
+++ b/lib/pleroma/web/plugs/authentication_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.AuthenticationPlug do
@@ -38,10 +38,6 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
def call(conn, _), do: conn
- def checkpw(password, "$6" <> _ = password_hash) do
- :crypt.crypt(password, password_hash) == password_hash
- end
-
def checkpw(password, "$2" <> _ = password_hash) do
# Handle bcrypt passwords for Mastodon migration
Bcrypt.verify_pass(password, password_hash)
@@ -60,10 +56,6 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
do_update_password(user, password)
end
- def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
- do_update_password(user, password)
- end
-
def maybe_update_password(user, _), do: {:ok, user}
defp do_update_password(user, password) do
diff --git a/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex
index 397f26de5..3eb13f955 100644
--- a/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex
+++ b/lib/pleroma/web/plugs/basic_auth_decoder_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlug do
diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex
index 111854859..667477857 100644
--- a/lib/pleroma/web/plugs/cache.ex
+++ b/lib/pleroma/web/plugs/cache.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.Cache do
@@ -97,13 +97,21 @@ defmodule Pleroma.Web.Plugs.Cache do
key = cache_key(conn, opts)
content_type = content_type(conn)
+ should_cache = not Map.get(conn.assigns, :skip_cache, false)
+
conn =
unless opts[:tracking_fun] do
- @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl)
+ if should_cache do
+ @cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl)
+ end
+
conn
else
tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil)
- @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl)
+
+ if should_cache do
+ @cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl)
+ end
opts.tracking_fun.(conn, tracking_fun_data)
end
diff --git a/lib/pleroma/web/plugs/digest_plug.ex b/lib/pleroma/web/plugs/digest_plug.ex
index d72f8073c..20e265f8f 100644
--- a/lib/pleroma/web/plugs/digest_plug.ex
+++ b/lib/pleroma/web/plugs/digest_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.DigestPlug do
diff --git a/lib/pleroma/web/plugs/ensure_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex
index 31e7410d6..8044a6bb2 100644
--- a/lib/pleroma/web/plugs/ensure_authenticated_plug.ex
+++ b/lib/pleroma/web/plugs/ensure_authenticated_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlug do
diff --git a/lib/pleroma/web/plugs/ensure_privileged_plug.ex b/lib/pleroma/web/plugs/ensure_privileged_plug.ex
new file mode 100644
index 000000000..f886c87ea
--- /dev/null
+++ b/lib/pleroma/web/plugs/ensure_privileged_plug.ex
@@ -0,0 +1,44 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.EnsurePrivilegedPlug do
+ @moduledoc """
+ Ensures staff are privileged enough to do certain tasks.
+ """
+ import Pleroma.Web.TranslationHelpers
+ import Plug.Conn
+
+ alias Pleroma.Config
+ alias Pleroma.User
+
+ def init(options) do
+ options
+ end
+
+ def call(%{assigns: %{user: %User{is_admin: false, is_moderator: false}}} = conn, _) do
+ conn
+ |> render_error(:forbidden, "User isn't privileged.")
+ |> halt()
+ end
+
+ def call(
+ %{assigns: %{user: %User{is_admin: is_admin, is_moderator: is_moderator}}} = conn,
+ privilege
+ ) do
+ if (is_admin and privilege in Config.get([:instance, :admin_privileges])) or
+ (is_moderator and privilege in Config.get([:instance, :moderator_privileges])) do
+ conn
+ else
+ conn
+ |> render_error(:forbidden, "User isn't privileged.")
+ |> halt()
+ end
+ end
+
+ def call(conn, _) do
+ conn
+ |> render_error(:forbidden, "User isn't privileged.")
+ |> halt()
+ end
+end
diff --git a/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex
index 8a8532f41..e98a3b661 100644
--- a/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex
+++ b/lib/pleroma/web/plugs/ensure_public_or_authenticated_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug do
diff --git a/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex b/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex
index 534b0cff1..5c57d2707 100644
--- a/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex
+++ b/lib/pleroma/web/plugs/ensure_user_token_assigns_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug do
diff --git a/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex
index f09cffe95..d1403e2bd 100644
--- a/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex
+++ b/lib/pleroma/web/plugs/expect_authenticated_check_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug do
diff --git a/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex
index e227d5150..a74c76395 100644
--- a/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex
+++ b/lib/pleroma/web/plugs/expect_public_or_authenticated_check_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug do
diff --git a/lib/pleroma/web/plugs/federating_plug.ex b/lib/pleroma/web/plugs/federating_plug.ex
index eeef7e45b..d5b8ef3ed 100644
--- a/lib/pleroma/web/plugs/federating_plug.ex
+++ b/lib/pleroma/web/plugs/federating_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.FederatingPlug do
diff --git a/lib/pleroma/web/plugs/frontend_static.ex b/lib/pleroma/web/plugs/frontend_static.ex
index eb385e94d..6ab8e4667 100644
--- a/lib/pleroma/web/plugs/frontend_static.ex
+++ b/lib/pleroma/web/plugs/frontend_static.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.FrontendStatic do
@@ -10,8 +10,6 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
"""
@behaviour Plug
- @api_routes Pleroma.Web.get_api_routes()
-
def file_path(path, frontend_type \\ :primary) do
if configuration = Pleroma.Config.get([:frontends, frontend_type]) do
instance_static_path = Pleroma.Config.get([:instance, :static_dir], "instance/static")
@@ -55,10 +53,13 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
defp invalid_path?([h | t], match), do: String.contains?(h, match) or invalid_path?(t)
defp invalid_path?([], _match), do: false
- defp api_route?([h | _]) when h in @api_routes, do: true
- defp api_route?([_ | t]), do: api_route?(t)
defp api_route?([]), do: false
+ defp api_route?([h | t]) do
+ api_routes = Pleroma.Web.Router.get_api_routes()
+ if h in api_routes, do: true, else: api_route?(t)
+ end
+
defp call_static(conn, opts, from) do
opts = Map.put(opts, :from, from)
Plug.Static.call(conn, opts)
diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex
index d1e6cc9d3..34895c8d5 100644
--- a/lib/pleroma/web/plugs/http_security_plug.ex
+++ b/lib/pleroma/web/plugs/http_security_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
@@ -68,7 +68,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
]
}
- [{"reply-to", Jason.encode!(report_group)} | headers]
+ [{"report-to", Jason.encode!(report_group)} | headers]
else
headers
end
@@ -117,7 +117,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
if Config.get(:env) == :dev do
"script-src 'self' 'unsafe-eval'"
else
- "script-src 'self'"
+ "script-src 'self' 'wasm-unsafe-eval'"
end
report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
diff --git a/lib/pleroma/web/plugs/http_signature_plug.ex b/lib/pleroma/web/plugs/http_signature_plug.ex
index 0f7550516..4bf325218 100644
--- a/lib/pleroma/web/plugs/http_signature_plug.ex
+++ b/lib/pleroma/web/plugs/http_signature_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
@@ -25,21 +25,58 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
end
end
+ defp validate_signature(conn, request_target) do
+ # Newer drafts for HTTP signatures now use @request-target instead of the
+ # old (request-target). We'll now support both for incoming signatures.
+ conn =
+ conn
+ |> put_req_header("(request-target)", request_target)
+ |> put_req_header("@request-target", request_target)
+
+ HTTPSignatures.validate_conn(conn)
+ end
+
+ defp validate_signature(conn) do
+ # This (request-target) is non-standard, but many implementations do it
+ # this way due to a misinterpretation of
+ # https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-06
+ # "path" was interpreted as not having the query, though later examples
+ # show that it must be the absolute path + query. This behavior is kept to
+ # make sure most software (Pleroma itself, Mastodon, and probably others)
+ # do not break.
+ request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
+
+ # This is the proper way to build the @request-target, as expected by
+ # many HTTP signature libraries, clarified in the following draft:
+ # https://www.ietf.org/archive/id/draft-ietf-httpbis-message-signatures-11.html#section-2.2.6
+ # It is the same as before, but containing the query part as well.
+ proper_target = request_target <> "?#{conn.query_string}"
+
+ cond do
+ # Normal, non-standard behavior but expected by Pleroma and more.
+ validate_signature(conn, request_target) ->
+ true
+
+ # Has query string and the previous one failed: let's try the standard.
+ conn.query_string != "" ->
+ validate_signature(conn, proper_target)
+
+ # If there's no query string and signature fails, it's rotten.
+ true ->
+ false
+ end
+ end
+
defp maybe_assign_valid_signature(conn) do
if has_signature_header?(conn) do
- # set (request-target) header to the appropriate value
- # we also replace the digest header with the one we computed
- request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
-
+ # we replace the digest header with the one we computed in DigestPlug
conn =
- conn
- |> put_req_header("(request-target)", request_target)
- |> case do
+ case conn do
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
conn -> conn
end
- assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
+ assign(conn, :valid_signature, validate_signature(conn))
else
Logger.debug("No signature header!")
conn
diff --git a/lib/pleroma/web/plugs/idempotency_plug.ex b/lib/pleroma/web/plugs/idempotency_plug.ex
index 9ac8f3647..a3b7af869 100644
--- a/lib/pleroma/web/plugs/idempotency_plug.ex
+++ b/lib/pleroma/web/plugs/idempotency_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.IdempotencyPlug do
diff --git a/lib/pleroma/web/plugs/instance_static.ex b/lib/pleroma/web/plugs/instance_static.ex
index 723b25679..75bfdd65b 100644
--- a/lib/pleroma/web/plugs/instance_static.ex
+++ b/lib/pleroma/web/plugs/instance_static.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.InstanceStatic do
diff --git a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex
index 58cb0316a..c6d531086 100644
--- a/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex
+++ b/lib/pleroma/web/plugs/mapped_signature_to_identity_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
diff --git a/lib/pleroma/web/plugs/o_auth_plug.ex b/lib/pleroma/web/plugs/o_auth_plug.ex
index 5e06ac3f6..ba04ddb72 100644
--- a/lib/pleroma/web/plugs/o_auth_plug.ex
+++ b/lib/pleroma/web/plugs/o_auth_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.OAuthPlug do
@@ -47,15 +47,17 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
#
@spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil
defp fetch_user_and_token(token) do
- query =
+ token_query =
from(t in Token,
- where: t.token == ^token,
- join: user in assoc(t, :user),
- preload: [user: user]
+ where: t.token == ^token
)
- with %Token{user: user} = token_record <- Repo.one(query) do
+ with %Token{user_id: user_id} = token_record <- Repo.one(token_query),
+ false <- is_nil(user_id),
+ %User{} = user <- User.get_cached_by_id(user_id) do
{:ok, user, token_record}
+ else
+ _ -> nil
end
end
diff --git a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex
index f017c8bc7..faf0fd8c6 100644
--- a/lib/pleroma/web/plugs/o_auth_scopes_plug.ex
+++ b/lib/pleroma/web/plugs/o_auth_scopes_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.OAuthScopesPlug do
diff --git a/lib/pleroma/web/plugs/plug_helper.ex b/lib/pleroma/web/plugs/plug_helper.ex
index d73021bf7..21bf03523 100644
--- a/lib/pleroma/web/plugs/plug_helper.ex
+++ b/lib/pleroma/web/plugs/plug_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.PlugHelper do
diff --git a/lib/pleroma/web/plugs/rate_limiter.ex b/lib/pleroma/web/plugs/rate_limiter.ex
index 5bebe0ad5..2080b06bd 100644
--- a/lib/pleroma/web/plugs/rate_limiter.ex
+++ b/lib/pleroma/web/plugs/rate_limiter.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.RateLimiter do
diff --git a/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex
index 3db59bf17..a96be670a 100644
--- a/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex
+++ b/lib/pleroma/web/plugs/rate_limiter/limiter_supervisor.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.RateLimiter.LimiterSupervisor do
diff --git a/lib/pleroma/web/plugs/rate_limiter/supervisor.ex b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex
index 0dc2aa71b..f00f3d95e 100644
--- a/lib/pleroma/web/plugs/rate_limiter/supervisor.ex
+++ b/lib/pleroma/web/plugs/rate_limiter/supervisor.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.RateLimiter.Supervisor do
diff --git a/lib/pleroma/web/plugs/remote_ip.ex b/lib/pleroma/web/plugs/remote_ip.ex
index 4d7daca56..f207d9fef 100644
--- a/lib/pleroma/web/plugs/remote_ip.ex
+++ b/lib/pleroma/web/plugs/remote_ip.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.RemoteIp do
diff --git a/lib/pleroma/web/plugs/set_format_plug.ex b/lib/pleroma/web/plugs/set_format_plug.ex
index 7ef88f305..84c67e7f0 100644
--- a/lib/pleroma/web/plugs/set_format_plug.ex
+++ b/lib/pleroma/web/plugs/set_format_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.SetFormatPlug do
diff --git a/lib/pleroma/web/plugs/set_locale_plug.ex b/lib/pleroma/web/plugs/set_locale_plug.ex
index d77191cff..271912ace 100644
--- a/lib/pleroma/web/plugs/set_locale_plug.ex
+++ b/lib/pleroma/web/plugs/set_locale_plug.ex
@@ -1,23 +1,61 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
# NOTE: this module is based on https://github.com/smeevil/set_locale
defmodule Pleroma.Web.Plugs.SetLocalePlug do
import Plug.Conn, only: [get_req_header: 2, assign: 3]
+ def frontend_language_cookie_name, do: "userLanguage"
+
def init(_), do: nil
def call(conn, _) do
- locale = get_locale_from_header(conn) || Gettext.get_locale()
- Gettext.put_locale(locale)
- assign(conn, :locale, locale)
+ locales = get_locales_from_header(conn)
+ first_locale = Enum.at(locales, 0, Gettext.get_locale())
+
+ Pleroma.Web.Gettext.put_locales(locales)
+
+ conn
+ |> assign(:locale, first_locale)
+ |> assign(:locales, locales)
end
- defp get_locale_from_header(conn) do
+ defp get_locales_from_header(conn) do
conn
- |> extract_accept_language()
- |> Enum.find(&supported_locale?/1)
+ |> extract_preferred_language()
+ |> normalize_language_codes()
+ |> all_supported()
+ |> Enum.uniq()
+ end
+
+ defp all_supported(locales) do
+ locales
+ |> Pleroma.Web.Gettext.ensure_fallbacks()
+ |> Enum.filter(&supported_locale?/1)
+ end
+
+ defp normalize_language_codes(codes) do
+ codes
+ |> Enum.map(fn code -> Pleroma.Web.Gettext.normalize_locale(code) end)
+ end
+
+ defp extract_preferred_language(conn) do
+ extract_frontend_language(conn) ++ extract_accept_language(conn)
+ end
+
+ defp extract_frontend_language(conn) do
+ %{req_cookies: cookies} =
+ conn
+ |> Plug.Conn.fetch_cookies()
+
+ case cookies[frontend_language_cookie_name()] do
+ nil ->
+ []
+
+ fe_lang ->
+ String.split(fe_lang, ",")
+ end
end
defp extract_accept_language(conn) do
@@ -29,7 +67,6 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do
|> Enum.sort(&(&1.quality > &2.quality))
|> Enum.map(& &1.tag)
|> Enum.reject(&is_nil/1)
- |> ensure_language_fallbacks()
_ ->
[]
@@ -37,9 +74,7 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do
end
defp supported_locale?(locale) do
- Pleroma.Web.Gettext
- |> Gettext.known_locales()
- |> Enum.member?(locale)
+ Pleroma.Web.Gettext.supports_locale?(locale)
end
defp parse_language_option(string) do
@@ -53,11 +88,4 @@ defmodule Pleroma.Web.Plugs.SetLocalePlug do
%{tag: captures["tag"], quality: quality}
end
-
- defp ensure_language_fallbacks(tags) do
- Enum.flat_map(tags, fn tag ->
- [language | _] = String.split(tag, "-")
- if Enum.member?(tags, language), do: [tag], else: [tag, language]
- end)
- end
end
diff --git a/lib/pleroma/web/plugs/set_user_session_id_plug.ex b/lib/pleroma/web/plugs/set_user_session_id_plug.ex
index a1cfa0915..c37214573 100644
--- a/lib/pleroma/web/plugs/set_user_session_id_plug.ex
+++ b/lib/pleroma/web/plugs/set_user_session_id_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.SetUserSessionIdPlug do
diff --git a/lib/pleroma/web/plugs/static_fe_plug.ex b/lib/pleroma/web/plugs/static_fe_plug.ex
index 9ba9dc5ff..9a364fdbc 100644
--- a/lib/pleroma/web/plugs/static_fe_plug.ex
+++ b/lib/pleroma/web/plugs/static_fe_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.StaticFEPlug do
diff --git a/lib/pleroma/web/plugs/trailing_format_plug.ex b/lib/pleroma/web/plugs/trailing_format_plug.ex
index c5069ae0e..a883ba541 100644
--- a/lib/pleroma/web/plugs/trailing_format_plug.ex
+++ b/lib/pleroma/web/plugs/trailing_format_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.TrailingFormatPlug do
diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex
index 2378e98d2..9dd5eb239 100644
--- a/lib/pleroma/web/plugs/uploaded_media.ex
+++ b/lib/pleroma/web/plugs/uploaded_media.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.UploadedMedia do
@@ -35,9 +35,9 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
conn =
case fetch_query_params(conn) do
%{query_params: %{"name" => name}} = conn ->
- name = String.replace(name, "\"", "\\\"")
+ name = String.replace(name, ~s["], ~s[\\"])
- put_resp_header(conn, "content-disposition", "filename=\"#{name}\"")
+ put_resp_header(conn, "content-disposition", ~s[inline; filename="#{name}"])
conn ->
conn
@@ -46,12 +46,32 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
config = Pleroma.Config.get(Pleroma.Upload)
- with uploader <- Keyword.fetch!(config, :uploader),
+ %{scheme: media_scheme, host: media_host, port: media_port} =
+ Pleroma.Upload.base_url() |> URI.parse()
+
+ with {:valid_host, true} <- {:valid_host, match?(^media_host, conn.host)},
+ uploader <- Keyword.fetch!(config, :uploader),
proxy_remote = Keyword.get(config, :proxy_remote, false),
{:ok, get_method} <- uploader.get_file(file),
false <- media_is_banned(conn, get_method) do
get_media(conn, get_method, proxy_remote, opts)
else
+ {:valid_host, false} ->
+ redirect_url =
+ %URI{
+ scheme: media_scheme,
+ host: media_host,
+ port: media_port,
+ path: conn.request_path,
+ query: conn.query_string
+ }
+ |> URI.to_string()
+ |> String.trim_trailing("?")
+
+ conn
+ |> Phoenix.Controller.redirect(external: redirect_url)
+ |> halt()
+
_ ->
conn
|> send_resp(:internal_server_error, dgettext("errors", "Failed"))
diff --git a/lib/pleroma/web/plugs/user_enabled_plug.ex b/lib/pleroma/web/plugs/user_enabled_plug.ex
index 1142a8dbc..ca104166a 100644
--- a/lib/pleroma/web/plugs/user_enabled_plug.ex
+++ b/lib/pleroma/web/plugs/user_enabled_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.UserEnabledPlug do
diff --git a/lib/pleroma/web/plugs/user_fetcher_plug.ex b/lib/pleroma/web/plugs/user_fetcher_plug.ex
index 707df9bfd..87bc27393 100644
--- a/lib/pleroma/web/plugs/user_fetcher_plug.ex
+++ b/lib/pleroma/web/plugs/user_fetcher_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.UserFetcherPlug do
diff --git a/lib/pleroma/web/plugs/user_is_admin_plug.ex b/lib/pleroma/web/plugs/user_is_admin_plug.ex
index 7649912ba..548eb9b9a 100644
--- a/lib/pleroma/web/plugs/user_is_admin_plug.ex
+++ b/lib/pleroma/web/plugs/user_is_admin_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.UserIsAdminPlug do
diff --git a/lib/pleroma/web/plugs/user_is_staff_plug.ex b/lib/pleroma/web/plugs/user_is_staff_plug.ex
new file mode 100644
index 000000000..951e14674
--- /dev/null
+++ b/lib/pleroma/web/plugs/user_is_staff_plug.ex
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UserIsStaffPlug do
+ import Pleroma.Web.TranslationHelpers
+ import Plug.Conn
+
+ alias Pleroma.User
+
+ def init(options) do
+ options
+ end
+
+ def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn
+ def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _), do: conn
+
+ def call(conn, _) do
+ conn
+ |> render_error(:forbidden, "User is not a staff member.")
+ |> halt()
+ end
+end
diff --git a/lib/pleroma/web/plugs/user_tracking_plug.ex b/lib/pleroma/web/plugs/user_tracking_plug.ex
index c9a988f00..9b52fd505 100644
--- a/lib/pleroma/web/plugs/user_tracking_plug.ex
+++ b/lib/pleroma/web/plugs/user_tracking_plug.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.UserTrackingPlug do
diff --git a/lib/pleroma/web/preload.ex b/lib/pleroma/web/preload.ex
index e8588bcc9..6a4a8885e 100644
--- a/lib/pleroma/web/preload.ex
+++ b/lib/pleroma/web/preload.ex
@@ -1,10 +1,9 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload do
alias Phoenix.HTML
- require Logger
def build_tags(_conn, params) do
preload_data =
@@ -12,7 +11,7 @@ defmodule Pleroma.Web.Preload do
terms =
params
|> parser.generate_terms()
- |> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v))} end)
+ |> Enum.map(fn {k, v} -> {k, Base.encode64(Jason.encode!(v, escape: :html_safe))} end)
|> Enum.into(%{})
Map.merge(acc, terms)
@@ -20,7 +19,7 @@ defmodule Pleroma.Web.Preload do
rendered_html =
preload_data
- |> Jason.encode!()
+ |> Jason.encode!(escape: :html_safe)
|> build_script_tag()
|> HTML.safe_to_string()
diff --git a/lib/pleroma/web/preload/providers/instance.ex b/lib/pleroma/web/preload/providers/instance.ex
index eb0254c74..6183f7b70 100644
--- a/lib/pleroma/web/preload/providers/instance.ex
+++ b/lib/pleroma/web/preload/providers/instance.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.Instance do
diff --git a/lib/pleroma/web/preload/providers/provider.ex b/lib/pleroma/web/preload/providers/provider.ex
index 60f304f2c..85e2badaa 100644
--- a/lib/pleroma/web/preload/providers/provider.ex
+++ b/lib/pleroma/web/preload/providers/provider.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.Provider do
diff --git a/lib/pleroma/web/preload/providers/timelines.ex b/lib/pleroma/web/preload/providers/timelines.ex
index c1704ccdc..1031f48d2 100644
--- a/lib/pleroma/web/preload/providers/timelines.ex
+++ b/lib/pleroma/web/preload/providers/timelines.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.Timelines do
diff --git a/lib/pleroma/web/preload/providers/user.ex b/lib/pleroma/web/preload/providers/user.ex
index 504f79ba0..74550cd56 100644
--- a/lib/pleroma/web/preload/providers/user.ex
+++ b/lib/pleroma/web/preload/providers/user.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.User do
diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex
index 154dae614..9665b0b4a 100644
--- a/lib/pleroma/web/push.ex
+++ b/lib/pleroma/web/push.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Push do
diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex
index 83cbdc870..3c5f00764 100644
--- a/lib/pleroma/web/push/impl.ex
+++ b/lib/pleroma/web/push/impl.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Push.Impl do
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do
require Logger
import Ecto.Query
- @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact"]
+ @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"]
@doc "Performs sending notifications for user subscriptions"
@spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type}
@@ -124,8 +124,8 @@ defmodule Pleroma.Web.Push.Impl do
def format_body(activity, actor, object, mastodon_type \\ nil)
- def format_body(_activity, actor, %{data: %{"type" => "ChatMessage", "content" => content}}, _) do
- case content do
+ def format_body(_activity, actor, %{data: %{"type" => "ChatMessage"} = data}, _) do
+ case data["content"] do
nil -> "@#{actor.nickname}: (Attachment)"
content -> "@#{actor.nickname}: #{Utils.scrub_html_and_truncate(content, 80)}"
end
@@ -174,6 +174,15 @@ defmodule Pleroma.Web.Push.Impl do
end
end
+ def format_body(
+ %{activity: %{data: %{"type" => "Update"}}},
+ actor,
+ _object,
+ _mastodon_type
+ ) do
+ "@#{actor.nickname} edited a status"
+ end
+
def format_title(activity, mastodon_type \\ nil)
def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do
@@ -187,6 +196,7 @@ defmodule Pleroma.Web.Push.Impl do
"follow_request" -> "New Follow Request"
"reblog" -> "New Repeat"
"favourite" -> "New Favorite"
+ "update" -> "New Update"
"pleroma:chat_mention" -> "New Chat Message"
"pleroma:emoji_reaction" -> "New Reaction"
type -> "New #{String.capitalize(type || "event")}"
diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex
index 4f6c9bc9f..6fc45bd61 100644
--- a/lib/pleroma/web/push/subscription.ex
+++ b/lib/pleroma/web/push/subscription.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Push.Subscription do
@@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do
end
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
- @supported_alert_types ~w[follow favourite mention reblog pleroma:chat_mention pleroma:emoji_reaction]a
+ @supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a
defp alerts(%{data: %{alerts: alerts}}) do
alerts = Map.take(alerts, @supported_alert_types)
diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex
index 7e745d07e..ceb6a05f0 100644
--- a/lib/pleroma/web/rel_me.ex
+++ b/lib/pleroma/web/rel_me.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RelMe do
@@ -9,17 +9,13 @@ defmodule Pleroma.Web.RelMe do
recv_timeout: 2_000
]
- if Pleroma.Config.get(:env) == :test do
- def parse(url) when is_binary(url), do: parse_url(url)
- else
- @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
- def parse(url) when is_binary(url) do
- @cachex.fetch!(:rel_me_cache, url, fn _ ->
- {:commit, parse_url(url)}
- end)
- rescue
- e -> {:error, "Cachex error: #{inspect(e)}"}
- end
+ @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+ def parse(url) when is_binary(url) do
+ @cachex.fetch!(:rel_me_cache, url, fn _ ->
+ {:commit, parse_url(url)}
+ end)
+ rescue
+ e -> {:error, "Cachex error: #{inspect(e)}"}
end
def parse(_), do: {:error, "No URL provided"}
diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
index 566fc8c8a..0488df30e 100644
--- a/lib/pleroma/web/rich_media/helpers.ex
+++ b/lib/pleroma/web/rich_media/helpers.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright _ 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Helpers do
diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex
index d6b54943b..dbe81eabb 100644
--- a/lib/pleroma/web/rich_media/parser.ex
+++ b/lib/pleroma/web/rich_media/parser.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser do
diff --git a/lib/pleroma/web/rich_media/parser/ttl.ex b/lib/pleroma/web/rich_media/parser/ttl.ex
index 0b7f14fb2..59d7f87ab 100644
--- a/lib/pleroma/web/rich_media/parser/ttl.ex
+++ b/lib/pleroma/web/rich_media/parser/ttl.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.TTL do
diff --git a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex
index c7eb267f3..fa41c160d 100644
--- a/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex
+++ b/lib/pleroma/web/rich_media/parser/ttl/aws_signed_url.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl do
diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
index 31c3d1e33..320a5f515 100644
--- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
+++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
diff --git a/lib/pleroma/web/rich_media/parsers/o_embed.ex b/lib/pleroma/web/rich_media/parsers/o_embed.ex
index 09eabec56..0f303176c 100644
--- a/lib/pleroma/web/rich_media/parsers/o_embed.ex
+++ b/lib/pleroma/web/rich_media/parsers/o_embed.ex
@@ -1,13 +1,13 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html),
oembed_url when is_binary(oembed_url) <- get_oembed_url(elements),
- {:ok, oembed_data} <- get_oembed_data(oembed_url) do
- oembed_data
+ {:ok, oembed_data = %{"html" => html}} <- get_oembed_data(oembed_url) do
+ %{oembed_data | "html" => Pleroma.HTML.filter_tags(html)}
else
_e -> %{}
end
diff --git a/lib/pleroma/web/rich_media/parsers/ogp.ex b/lib/pleroma/web/rich_media/parsers/ogp.ex
index d0edf1c88..b7f2b4216 100644
--- a/lib/pleroma/web/rich_media/parsers/ogp.ex
+++ b/lib/pleroma/web/rich_media/parsers/ogp.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.OGP do
diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
index 0adf84159..cc653729d 100644
--- a/lib/pleroma/web/rich_media/parsers/twitter_card.ex
+++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index ccf2ef796..c1a690e28 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -1,9 +1,10 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Router do
use Pleroma.Web, :router
+ import Phoenix.LiveDashboard.Router
pipeline :accepts_html do
plug(:accepts, ["html"])
@@ -96,14 +97,82 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug)
plug(:after_auth)
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
- plug(Pleroma.Web.Plugs.UserIsAdminPlug)
+ plug(Pleroma.Web.Plugs.UserIsStaffPlug)
plug(Pleroma.Web.Plugs.IdempotencyPlug)
end
- pipeline :mastodon_html do
- plug(:browser)
- plug(:authenticate)
- plug(:after_auth)
+ pipeline :require_admin do
+ plug(Pleroma.Web.Plugs.UserIsAdminPlug)
+ end
+
+ pipeline :require_privileged_role_users_delete do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_delete)
+ end
+
+ pipeline :require_privileged_role_users_manage_credentials do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_credentials)
+ end
+
+ pipeline :require_privileged_role_messages_read do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :messages_read)
+ end
+
+ pipeline :require_privileged_role_users_manage_tags do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_tags)
+ end
+
+ pipeline :require_privileged_role_users_manage_activation_state do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_activation_state)
+ end
+
+ pipeline :require_privileged_role_users_manage_invites do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_manage_invites)
+ end
+
+ pipeline :require_privileged_role_reports_manage_reports do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :reports_manage_reports)
+ end
+
+ pipeline :require_privileged_role_users_read do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :users_read)
+ end
+
+ pipeline :require_privileged_role_messages_delete do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :messages_delete)
+ end
+
+ pipeline :require_privileged_role_emoji_manage_emoji do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :emoji_manage_emoji)
+ end
+
+ pipeline :require_privileged_role_instances_delete do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :instances_delete)
+ end
+
+ pipeline :require_privileged_role_moderation_log_read do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :moderation_log_read)
+ end
+
+ pipeline :require_privileged_role_statistics_read do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :statistics_read)
+ end
+
+ pipeline :require_privileged_role_announcements_manage_announcements do
+ plug(:admin_api)
+ plug(Pleroma.Web.Plugs.EnsurePrivilegedPlug, :announcements_manage_announcements)
end
pipeline :pleroma_html do
@@ -140,6 +209,10 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Web.Plugs.MappedSignatureToIdentityPlug)
end
+ pipeline :static_fe do
+ plug(Pleroma.Web.Plugs.StaticFEPlug)
+ end
+
scope "/api/v1/pleroma", Pleroma.Web.TwitterAPI do
pipe_through(:pleroma_api)
@@ -148,6 +221,7 @@ defmodule Pleroma.Web.Router do
get("/emoji", UtilController, :emoji)
get("/captcha", UtilController, :captcha)
get("/healthcheck", UtilController, :healthcheck)
+ post("/remote_interaction", UtilController, :remote_interaction)
end
scope "/api/v1/pleroma", Pleroma.Web do
@@ -155,12 +229,9 @@ defmodule Pleroma.Web.Router do
post("/uploader_callback/:upload_path", UploaderController, :callback)
end
+ # AdminAPI: only admins can perform these actions
scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
- pipe_through(:admin_api)
-
- put("/users/disable_mfa", AdminAPIController, :disable_mfa)
- put("/users/tag", AdminAPIController, :tag_users)
- delete("/users/tag", AdminAPIController, :untag_users)
+ pipe_through([:admin_api, :require_admin])
get("/users/:nickname/permission_group", AdminAPIController, :right_get)
get("/users/:nickname/permission_group/:permission_group", AdminAPIController, :right_get)
@@ -183,85 +254,176 @@ defmodule Pleroma.Web.Router do
post("/users/follow", UserController, :follow)
post("/users/unfollow", UserController, :unfollow)
- delete("/users", UserController, :delete)
post("/users", UserController, :create)
- patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
- patch("/users/activate", UserController, :activate)
- patch("/users/deactivate", UserController, :deactivate)
- patch("/users/approve", UserController, :approve)
+
+ patch("/users/suggest", UserController, :suggest)
+ patch("/users/unsuggest", UserController, :unsuggest)
get("/relay", RelayController, :index)
post("/relay", RelayController, :follow)
delete("/relay", RelayController, :unfollow)
- post("/users/invite_token", InviteController, :create)
- get("/users/invites", InviteController, :index)
- post("/users/revoke_invite", InviteController, :revoke)
- post("/users/email_invite", InviteController, :email)
+ get("/instance_document/:name", InstanceDocumentController, :show)
+ patch("/instance_document/:name", InstanceDocumentController, :update)
+ delete("/instance_document/:name", InstanceDocumentController, :delete)
+
+ get("/config", ConfigController, :show)
+ post("/config", ConfigController, :update)
+ get("/config/descriptions", ConfigController, :descriptions)
+ get("/need_reboot", AdminAPIController, :need_reboot)
+ get("/restart", AdminAPIController, :restart)
+
+ get("/oauth_app", OAuthAppController, :index)
+ post("/oauth_app", OAuthAppController, :create)
+ patch("/oauth_app/:id", OAuthAppController, :update)
+ delete("/oauth_app/:id", OAuthAppController, :delete)
+
+ get("/media_proxy_caches", MediaProxyCacheController, :index)
+ post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
+ post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
+
+ get("/frontends", FrontendController, :index)
+ post("/frontends/install", FrontendController, :install)
+
+ post("/backups", AdminAPIController, :create_backup)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_announcements_manage_announcements)
+
+ get("/announcements", AnnouncementController, :index)
+ post("/announcements", AnnouncementController, :create)
+ get("/announcements/:id", AnnouncementController, :show)
+ patch("/announcements/:id", AnnouncementController, :change)
+ delete("/announcements/:id", AnnouncementController, :delete)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_users_delete)
+
+ delete("/users", UserController, :delete)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_users_manage_credentials)
get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
- patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
+ put("/users/disable_mfa", AdminAPIController, :disable_mfa)
+ patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
+ patch("/users/confirm_email", AdminAPIController, :confirm_email)
+ patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_messages_read)
- get("/users", UserController, :index)
- get("/users/:nickname", UserController, :show)
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
- get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses)
+ get("/statuses", StatusController, :index)
- get("/instance_document/:name", InstanceDocumentController, :show)
- patch("/instance_document/:name", InstanceDocumentController, :update)
- delete("/instance_document/:name", InstanceDocumentController, :delete)
+ get("/chats/:id", ChatController, :show)
+ get("/chats/:id/messages", ChatController, :messages)
- patch("/users/confirm_email", AdminAPIController, :confirm_email)
- patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
+ get("/instances/:instance/statuses", InstanceController, :list_statuses)
+
+ get("/statuses/:id", StatusController, :show)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_users_manage_tags)
+
+ put("/users/tag", AdminAPIController, :tag_users)
+ delete("/users/tag", AdminAPIController, :untag_users)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_users_manage_activation_state)
+
+ patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
+ patch("/users/activate", UserController, :activate)
+ patch("/users/deactivate", UserController, :deactivate)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_users_manage_invites)
+
+ patch("/users/approve", UserController, :approve)
+ post("/users/invite_token", InviteController, :create)
+ get("/users/invites", InviteController, :index)
+ post("/users/revoke_invite", InviteController, :revoke)
+ post("/users/email_invite", InviteController, :email)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_reports_manage_reports)
get("/reports", ReportController, :index)
get("/reports/:id", ReportController, :show)
patch("/reports", ReportController, :update)
post("/reports/:id/notes", ReportController, :notes_create)
delete("/reports/:report_id/notes/:id", ReportController, :notes_delete)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_users_read)
+
+ get("/users", UserController, :index)
+ get("/users/:nickname", UserController, :show)
+ end
+
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_messages_delete)
- get("/statuses/:id", StatusController, :show)
put("/statuses/:id", StatusController, :update)
delete("/statuses/:id", StatusController, :delete)
- get("/statuses", StatusController, :index)
- get("/config", ConfigController, :show)
- post("/config", ConfigController, :update)
- get("/config/descriptions", ConfigController, :descriptions)
- get("/need_reboot", AdminAPIController, :need_reboot)
- get("/restart", AdminAPIController, :restart)
+ delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
+ end
- get("/moderation_log", AdminAPIController, :list_log)
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_emoji_manage_emoji)
post("/reload_emoji", AdminAPIController, :reload_emoji)
- get("/stats", AdminAPIController, :stats)
+ end
- get("/oauth_app", OAuthAppController, :index)
- post("/oauth_app", OAuthAppController, :create)
- patch("/oauth_app/:id", OAuthAppController, :update)
- delete("/oauth_app/:id", OAuthAppController, :delete)
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_instances_delete)
- get("/media_proxy_caches", MediaProxyCacheController, :index)
- post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
- post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
+ delete("/instances/:instance", InstanceController, :delete)
+ end
- get("/chats/:id", ChatController, :show)
- get("/chats/:id/messages", ChatController, :messages)
- delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_moderation_log_read)
- get("/frontends", FrontendController, :index)
- post("/frontends/install", FrontendController, :install)
+ get("/moderation_log", AdminAPIController, :list_log)
+ end
- post("/backups", AdminAPIController, :create_backup)
+ # AdminAPI: admins and mods (staff) can perform these actions (if privileged by role)
+ scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+ pipe_through(:require_privileged_role_statistics_read)
+
+ get("/stats", AdminAPIController, :stats)
end
scope "/api/v1/pleroma/emoji", Pleroma.Web.PleromaAPI do
scope "/pack" do
- pipe_through(:admin_api)
+ pipe_through(:require_privileged_role_emoji_manage_emoji)
post("/", EmojiPackController, :create)
patch("/", EmojiPackController, :update)
@@ -276,7 +438,7 @@ defmodule Pleroma.Web.Router do
# Modifying packs
scope "/packs" do
- pipe_through(:admin_api)
+ pipe_through(:require_privileged_role_emoji_manage_emoji)
get("/import", EmojiPackController, :import_from_filesystem)
get("/remote", EmojiPackController, :remote)
@@ -300,6 +462,7 @@ defmodule Pleroma.Web.Router do
pipe_through(:pleroma_html)
post("/main/ostatus", UtilController, :remote_subscribe)
+ get("/main/ostatus", UtilController, :show_subscribe_form)
get("/ostatus_subscribe", RemoteFollowController, :follow)
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
end
@@ -312,6 +475,11 @@ defmodule Pleroma.Web.Router do
post("/delete_account", UtilController, :delete_account)
put("/notification_settings", UtilController, :update_notificaton_settings)
post("/disable_account", UtilController, :disable_account)
+ post("/move_account", UtilController, :move_account)
+
+ put("/aliases", UtilController, :add_alias)
+ get("/aliases", UtilController, :list_aliases)
+ delete("/aliases", UtilController, :delete_alias)
end
scope "/api/pleroma", Pleroma.Web.PleromaAPI do
@@ -364,6 +532,7 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
pipe_through(:api)
+ get("/apps", AppController, :index)
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
get("/statuses/:id/reactions", EmojiReactionController, :index)
end
@@ -408,6 +577,7 @@ defmodule Pleroma.Web.Router do
scope [] do
pipe_through(:api)
get("/accounts/:id/favourites", AccountController, :favourites)
+ get("/accounts/:id/endorsements", AccountController, :endorsements)
end
scope [] do
@@ -415,6 +585,15 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/subscribe", AccountController, :subscribe)
post("/accounts/:id/unsubscribe", AccountController, :unsubscribe)
+
+ get("/birthdays", AccountController, :birthdays)
+ end
+
+ scope [] do
+ pipe_through(:authenticated_api)
+
+ get("/settings/:app", SettingsController, :show)
+ patch("/settings/:app", SettingsController, :update)
end
post("/accounts/confirmation_resend", AccountController, :confirmation_resend)
@@ -453,6 +632,10 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/unblock", AccountController, :unblock)
post("/accounts/:id/mute", AccountController, :mute)
post("/accounts/:id/unmute", AccountController, :unmute)
+ post("/accounts/:id/note", AccountController, :note)
+ post("/accounts/:id/pin", AccountController, :endorse)
+ post("/accounts/:id/unpin", AccountController, :unendorse)
+ post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers)
get("/conversations", ConversationController, :index)
post("/conversations/:id/read", ConversationController, :mark_as_read)
@@ -514,6 +697,7 @@ defmodule Pleroma.Web.Router do
get("/bookmarks", StatusController, :bookmarks)
post("/statuses", StatusController, :create)
+ put("/statuses/:id", StatusController, :update)
delete("/statuses/:id", StatusController, :delete)
post("/statuses/:id/reblog", StatusController, :reblog)
post("/statuses/:id/unreblog", StatusController, :unreblog)
@@ -532,17 +716,14 @@ defmodule Pleroma.Web.Router do
delete("/push/subscription", SubscriptionController, :delete)
get("/suggestions", SuggestionController, :index)
+ delete("/suggestions/:account_id", SuggestionController, :dismiss)
get("/timelines/home", TimelineController, :home)
get("/timelines/direct", TimelineController, :direct)
get("/timelines/list/:list_id", TimelineController, :list)
- end
-
- scope "/api/web", Pleroma.Web do
- pipe_through(:authenticated_api)
- # Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
- put("/settings", MastoFEController, :put_settings)
+ get("/announcements", AnnouncementController, :index)
+ post("/announcements/:id/dismiss", AnnouncementController, :mark_read)
end
scope "/api/v1", Pleroma.Web.MastodonAPI do
@@ -558,6 +739,8 @@ defmodule Pleroma.Web.Router do
get("/accounts/search", SearchController, :account_search)
get("/search", SearchController, :search)
+ get("/accounts/lookup", AccountController, :lookup)
+
get("/accounts/:id/statuses", AccountController, :statuses)
get("/accounts/:id/followers", AccountController, :followers)
get("/accounts/:id/following", AccountController, :following)
@@ -574,6 +757,8 @@ defmodule Pleroma.Web.Router do
get("/statuses/:id/card", StatusController, :card)
get("/statuses/:id/favourited_by", StatusController, :favourited_by)
get("/statuses/:id/reblogged_by", StatusController, :reblogged_by)
+ get("/statuses/:id/history", StatusController, :show_history)
+ get("/statuses/:id/source", StatusController, :show_source)
get("/custom_emojis", CustomEmojiController, :index)
@@ -583,6 +768,8 @@ defmodule Pleroma.Web.Router do
get("/timelines/tag/:tag", TimelineController, :hashtag)
get("/polls/:id", PollController, :show)
+
+ get("/directory", DirectoryController, :index)
end
scope "/api/v2", Pleroma.Web.MastodonAPI do
@@ -590,6 +777,8 @@ defmodule Pleroma.Web.Router do
get("/search", SearchController, :search2)
post("/media", MediaController, :create2)
+
+ get("/suggestions", SuggestionController, :index2)
end
scope "/api", Pleroma.Web do
@@ -620,18 +809,12 @@ defmodule Pleroma.Web.Router do
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
-
- post(
- "/qvitter/statuses/notifications/read",
- TwitterAPI.Controller,
- :mark_notifications_as_read
- )
end
scope "/", Pleroma.Web do
# Note: html format is supported only if static FE is enabled
# Note: http signature is only considered for json requests (no auth for non-json requests)
- pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
+ pipe_through([:accepts_html_json, :http_signature, :static_fe])
get("/objects/:uuid", OStatus.OStatusController, :object)
get("/activities/:uuid", OStatus.OStatusController, :activity)
@@ -645,15 +828,14 @@ defmodule Pleroma.Web.Router do
scope "/", Pleroma.Web do
# Note: html format is supported only if static FE is enabled
# Note: http signature is only considered for json requests (no auth for non-json requests)
- pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
+ pipe_through([:accepts_html_xml_json, :http_signature, :static_fe])
# Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
end
scope "/", Pleroma.Web do
- # Note: html format is supported only if static FE is enabled
- pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
+ pipe_through([:accepts_html_xml])
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
end
@@ -749,27 +931,20 @@ defmodule Pleroma.Web.Router do
scope "/", Pleroma.Web do
pipe_through(:api)
- get("/web/manifest.json", MastoFEController, :manifest)
+ get("/manifest.json", ManifestController, :show)
end
scope "/", Pleroma.Web do
- pipe_through(:mastodon_html)
-
- get("/web/login", MastodonAPI.AuthController, :login)
- delete("/auth/sign_out", MastodonAPI.AuthController, :logout)
-
- post("/auth/password", MastodonAPI.AuthController, :password_reset)
-
- get("/web/*path", MastoFEController, :index)
+ pipe_through(:pleroma_html)
- get("/embed/:id", EmbedController, :show)
+ post("/auth/password", TwitterAPI.PasswordController, :request)
end
- scope "/proxy/", Pleroma.Web.MediaProxy do
- get("/preview/:sig/:url", MediaProxyController, :preview)
- get("/preview/:sig/:url/:filename", MediaProxyController, :preview)
- get("/:sig/:url", MediaProxyController, :remote)
- get("/:sig/:url/:filename", MediaProxyController, :remote)
+ scope "/proxy/", Pleroma.Web do
+ get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
+ get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
+ get("/:sig/:url", MediaProxy.MediaProxyController, :remote)
+ get("/:sig/:url/:filename", MediaProxy.MediaProxyController, :remote)
end
if Pleroma.Config.get(:env) == :dev do
@@ -780,6 +955,11 @@ defmodule Pleroma.Web.Router do
end
end
+ scope "/" do
+ pipe_through([:pleroma_html, :authenticate, :require_admin])
+ live_dashboard("/phoenix/live_dashboard")
+ end
+
# Test-only routes needed to test action dispatching and plug chain execution
if Pleroma.Config.get(:env) == :test do
@test_actions [
@@ -822,4 +1002,16 @@ defmodule Pleroma.Web.Router do
options("/*path", RedirectController, :empty)
end
+
+ # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
+ def get_api_routes do
+ __MODULE__.__routes__()
+ |> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
+ |> Enum.map(fn r ->
+ r.path
+ |> String.split("/", trim: true)
+ |> List.first()
+ end)
+ |> Enum.uniq()
+ end
end
diff --git a/lib/pleroma/web/chat_channel.ex b/lib/pleroma/web/shout_channel.ex
index 4008129e9..928f0a1dd 100644
--- a/lib/pleroma/web/chat_channel.ex
+++ b/lib/pleroma/web/shout_channel.ex
@@ -1,13 +1,13 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Web.ChatChannel do
+defmodule Pleroma.Web.ShoutChannel do
use Phoenix.Channel
alias Pleroma.User
- alias Pleroma.Web.ChatChannel.ChatChannelState
alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.ShoutChannel.ShoutChannelState
def join("chat:public", _message, socket) do
send(self(), :after_join)
@@ -15,18 +15,18 @@ defmodule Pleroma.Web.ChatChannel do
end
def handle_info(:after_join, socket) do
- push(socket, "messages", %{messages: ChatChannelState.messages()})
+ push(socket, "messages", %{messages: ShoutChannelState.messages()})
{:noreply, socket}
end
def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do
text = String.trim(text)
- if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do
+ if String.length(text) in 1..Pleroma.Config.get([:shout, :limit]) do
author = User.get_cached_by_nickname(user_name)
author_json = AccountView.render("show.json", user: author, skip_visibility_check: true)
- message = ChatChannelState.add_message(%{text: text, author: author_json})
+ message = ShoutChannelState.add_message(%{text: text, author: author_json})
broadcast!(socket, "new_msg", message)
end
@@ -35,7 +35,7 @@ defmodule Pleroma.Web.ChatChannel do
end
end
-defmodule Pleroma.Web.ChatChannel.ChatChannelState do
+defmodule Pleroma.Web.ShoutChannel.ShoutChannelState do
use Agent
@max_messages 20
diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex
index fe485d10d..8019a218a 100644
--- a/lib/pleroma/web/static_fe/static_fe_controller.ex
+++ b/lib/pleroma/web/static_fe/static_fe_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.StaticFE.StaticFEController do
@@ -14,7 +14,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
alias Pleroma.Web.Router.Helpers
plug(:put_layout, :static_fe)
- plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
plug(:assign_id)
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
@@ -26,7 +25,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
true <- Visibility.is_public?(activity.object),
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
- meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
+ url = Helpers.url(conn) <> conn.request_path
+
+ meta =
+ Metadata.build_tags(%{
+ activity_id: notice_id,
+ object: activity.object,
+ user: user,
+ url: url
+ })
timeline =
activity.object.data["context"]
diff --git a/lib/pleroma/web/static_fe/static_fe_view.ex b/lib/pleroma/web/static_fe/static_fe_view.ex
index c04715337..8e23a79a3 100644
--- a/lib/pleroma/web/static_fe/static_fe_view.ex
+++ b/lib/pleroma/web/static_fe/static_fe_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.StaticFE.StaticFEView do
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index fc3bbb130..b9a04cc76 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Streamer do
@@ -25,6 +25,7 @@ defmodule Pleroma.Web.Streamer do
def registry, do: @registry
@public_streams ["public", "public:local", "public:media", "public:local:media"]
+ @local_streams ["public:local", "public:local:media"]
@user_streams ["user", "user:notification", "direct", "user:pleroma_chat"]
@doc "Expands and authorizes a stream, and registers the process for streaming."
@@ -37,7 +38,23 @@ defmodule Pleroma.Web.Streamer do
{:ok, topic :: String.t()} | {:error, :bad_topic} | {:error, :unauthorized}
def get_topic_and_add_socket(stream, user, oauth_token, params \\ %{}) do
with {:ok, topic} <- get_topic(stream, user, oauth_token, params) do
- add_socket(topic, user)
+ add_socket(topic, oauth_token)
+ end
+ end
+
+ defp can_access_stream(user, oauth_token, kind) do
+ with {_, true} <- {:restrict?, Config.restrict_unauthenticated_access?(:timelines, kind)},
+ {_, %User{id: user_id}, %Token{user_id: user_id}} <- {:user, user, oauth_token},
+ {_, true} <-
+ {:scopes,
+ OAuthScopesPlug.filter_descendants(["read:statuses"], oauth_token.scopes) != []} do
+ true
+ else
+ {:restrict?, _} ->
+ true
+
+ _ ->
+ false
end
end
@@ -46,9 +63,16 @@ defmodule Pleroma.Web.Streamer do
{:ok, topic :: String.t()} | {:error, :bad_topic}
def get_topic(stream, user, oauth_token, params \\ %{})
- # Allow all public steams.
- def get_topic(stream, _user, _oauth_token, _params) when stream in @public_streams do
- {:ok, stream}
+ # Allow all public steams if the instance allows unauthenticated access.
+ # Otherwise, only allow users with valid oauth tokens.
+ def get_topic(stream, user, oauth_token, _params) when stream in @public_streams do
+ kind = if stream in @local_streams, do: :local, else: :federated
+
+ if can_access_stream(user, oauth_token, kind) do
+ {:ok, stream}
+ else
+ {:error, :unauthorized}
+ end
end
# Allow all hashtags streams.
@@ -57,12 +81,20 @@ defmodule Pleroma.Web.Streamer do
end
# Allow remote instance streams.
- def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
- {:ok, "public:remote:" <> instance}
+ def get_topic("public:remote", user, oauth_token, %{"instance" => instance} = _params) do
+ if can_access_stream(user, oauth_token, :federated) do
+ {:ok, "public:remote:" <> instance}
+ else
+ {:error, :unauthorized}
+ end
end
- def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
- {:ok, "public:remote:media:" <> instance}
+ def get_topic("public:remote:media", user, oauth_token, %{"instance" => instance} = _params) do
+ if can_access_stream(user, oauth_token, :federated) do
+ {:ok, "public:remote:media:" <> instance}
+ else
+ {:error, :unauthorized}
+ end
end
# Expand user streams.
@@ -120,10 +152,10 @@ defmodule Pleroma.Web.Streamer do
end
@doc "Registers the process for streaming. Use `get_topic/3` to get the full authorized topic."
- def add_socket(topic, user) do
+ def add_socket(topic, oauth_token) do
if should_env_send?() do
- auth? = if user, do: true
- Registry.register(@registry, topic, auth?)
+ oauth_token_id = if oauth_token, do: oauth_token.id, else: false
+ Registry.register(@registry, topic, oauth_token_id)
end
{:ok, topic}
@@ -296,6 +328,24 @@ defmodule Pleroma.Web.Streamer do
defp push_to_socket(_topic, %Activity{data: %{"type" => "Delete"}}), do: :noop
+ defp push_to_socket(topic, %Activity{data: %{"type" => "Update"}} = item) do
+ create_activity =
+ Pleroma.Activity.get_create_by_object_ap_id(item.object.data["id"])
+ |> Map.put(:object, item.object)
+
+ anon_render = StreamerView.render("status_update.json", create_activity)
+
+ Registry.dispatch(@registry, topic, fn list ->
+ Enum.each(list, fn {pid, auth?} ->
+ if auth? do
+ send(pid, {:render_with_user, StreamerView, "status_update.json", create_activity})
+ else
+ send(pid, {:text, anon_render})
+ end
+ end)
+ end)
+ end
+
defp push_to_socket(topic, item) do
anon_render = StreamerView.render("update.json", item)
@@ -320,6 +370,22 @@ defmodule Pleroma.Web.Streamer do
end
end
+ def close_streams_by_oauth_token(oauth_token) do
+ if should_env_send?() do
+ Registry.select(
+ @registry,
+ [
+ {
+ {:"$1", :"$2", :"$3"},
+ [{:==, :"$3", oauth_token.id}],
+ [:"$2"]
+ }
+ ]
+ )
+ |> Enum.each(fn pid -> send(pid, :close) end)
+ end
+ end
+
# In test environement, only return true if the registry is started.
# In benchmark environment, returns false.
# In any other environment, always returns true.
diff --git a/lib/pleroma/web/templates/email/digest.html.eex b/lib/pleroma/web/templates/email/digest.html.eex
index 60eceff22..1efc76e1a 100644
--- a/lib/pleroma/web/templates/email/digest.html.eex
+++ b/lib/pleroma/web/templates/email/digest.html.eex
@@ -160,7 +160,7 @@
<div
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;">
<p style="line-height: 36px; text-align: center; margin: 0;"><span
- style="font-size: 30px; color: <%= @styling.header_color %>;">Hey <%= @user.nickname %>, here is what you've missed!</span></p>
+ style="font-size: 30px; color: <%= @styling.header_color %>;"><%= Gettext.dpgettext("static_pages", "digest email header line", "Hey %{nickname}, here is what you've missed!", nickname: @user.nickname) %></span></p>
</div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
@@ -382,7 +382,7 @@
<div
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
<p style="font-size: 12px; line-height: 24px; text-align: center; margin: 0;"><span
- style="font-size: 20px;"><%= length(@followers) %> New Followers</span><span
+ style="font-size: 20px;"><%= Gettext.dpngettext("static_pages", "new followers count header", "%{count} New Follower", "%{count} New Followers", length(@followers), count: length(@followers)) %></span><span
style="font-size: 20px; line-height: 24px;"></span></p>
</div>
</div>
@@ -535,16 +535,16 @@
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
<p
style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
- <span style="font-size: 14px;">You have received this email because you have signed up to receive digest emails from <b><%= @instance %></b> Pleroma instance.</span></p>
+ <span style="font-size: 14px;"><%= raw Gettext.dpgettext("static_pages", "digest email sending reason", "You have received this email because you have signed up to receive digest emails from <b>%{instance}</b> Pleroma instance.", instance: safe_to_string(html_escape(@instance))) %></span></p>
<p
style="font-size: 12px; line-height: 14px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
 </p>
<p
style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
- <span style="font-size: 14px;">The email address you are subscribed as is <a href="mailto:<%= @user.email %>" style="color: <%= @styling.link_color %>;text-decoration: none;"><%= @user.email %></a>. </span></p>
+ <span style="font-size: 14px;"><%= raw Gettext.dpgettext("static_pages", "digest email receiver address", "The email address you are subscribed as is <a href='mailto:%{@user.email}' style='color: %{color};text-decoration: none;'>%{email}</a>. ", color: safe_to_string(html_escape(@styling.link_color)), email: safe_to_string(html_escape(@user.email))) %></span></p>
<p
style="font-size: 12px; line-height: 16px; text-align: center; color: <%= @styling.text_color %>; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0;">
- <span style="font-size: 14px;">To unsubscribe, please go <%= link "here", style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link %>.</span></p>
+ <span style="font-size: 14px;"><%= raw Gettext.dpgettext("static_pages", "digest email unsubscribe action", "To unsubscribe, please go %{here}.", here: safe_to_string link(Gettext.dpgettext("static_pages", "digest email unsubscribe action link text", "here"), style: "color: #{@styling.link_color};text-decoration: none;", to: @unsubscribe_link)) %></span></p>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!--[if (!mso)&(!IE)]><!-->
diff --git a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
index 6688830ba..b774f7984 100644
--- a/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_activity.atom.eex
@@ -3,15 +3,15 @@
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<id><%= @data["id"] %></id>
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
- <content type="html"><%= activity_content(@data) %></content>
- <published><%= @activity.data["published"] %></published>
- <updated><%= @activity.data["published"] %></updated>
+ <content type="html"><%= activity_description(@data) %></content>
+ <published><%= to_rfc3339(@data["published"]) %></published>
+ <updated><%= to_rfc3339(@data["published"]) %></updated>
<ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %>
</ostatus:conversation>
<link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
- <%= if @data["summary"] do %>
+ <%= if @data["summary"] != "" do %>
<summary><%= escape(@data["summary"]) %></summary>
<% end %>
@@ -38,7 +38,7 @@
<%= if id == Pleroma.Constants.as_public() do %>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
<% else %>
- <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %>
+ <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="<%= id %>"/>
<% end %>
<% end %>
diff --git a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex
index 592b9dcdc..7de98f736 100644
--- a/lib/pleroma/web/templates/feed/feed/_activity.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/_activity.rss.eex
@@ -3,17 +3,12 @@
<activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
<guid><%= @data["id"] %></guid>
<title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
- <description><%= activity_content(@data) %></description>
- <pubDate><%= @activity.data["published"] %></pubDate>
- <updated><%= @activity.data["published"] %></updated>
+ <description><%= activity_description(@data) %></description>
+ <pubDate><%= to_rfc2822(@data["published"]) %></pubDate>
<ostatus:conversation ref="<%= activity_context(@activity) %>">
<%= activity_context(@activity) %>
</ostatus:conversation>
- <%= if @data["summary"] do %>
- <description><%= escape(@data["summary"]) %></description>
- <% end %>
-
<%= if @activity.local do %>
<link><%= @data["id"] %></link>
<% else %>
@@ -27,7 +22,7 @@
<% end %>
<%= for attachment <- @data["attachment"] || [] do %>
- <link type="<%= attachment_type(attachment) %>"><%= attachment_href(attachment) %></link>
+ <enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>" />
<% end %>
<%= if @data["inReplyTo"] do %>
@@ -38,7 +33,7 @@
<%= if id == Pleroma.Constants.as_public() do %>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection">http://activityschema.org/collection/public</link>
<% else %>
- <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %>
+ <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person"><%= id %></link>
<% end %>
<% end %>
diff --git a/lib/pleroma/web/templates/feed/feed/_author.atom.eex b/lib/pleroma/web/templates/feed/feed/_author.atom.eex
index 25cbffada..90be8a559 100644
--- a/lib/pleroma/web/templates/feed/feed/_author.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_author.atom.eex
@@ -1,17 +1,14 @@
<author>
- <id><%= @user.ap_id %></id>
- <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
<uri><%= @user.ap_id %></uri>
+ <name><%= @user.nickname %></name>
+ <activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
+ <activity:displayName><%= @user.name %></activity:displayName>
+ <activity:image><%= User.avatar_url(@user) %></activity:image>
+ <activity:id><%= @user.ap_id %></activity:id>
+ <activity:published><%= to_rfc3339(@user.inserted_at) %></activity:published>
+ <activity:updated><%= to_rfc3339(@user.updated_at) %></activity:updated>
+ <activity:url><%= @user.ap_id %></activity:url>
<poco:preferredUsername><%= @user.nickname %></poco:preferredUsername>
<poco:displayName><%= @user.name %></poco:displayName>
<poco:note><%= escape(@user.bio) %></poco:note>
- <summary><%= escape(@user.bio) %></summary>
- <name><%= @user.nickname %></name>
- <link rel="avatar" href="<%= User.avatar_url(@user) %>"/>
- <%= if User.banner_url(@user) do %>
- <link rel="header" href="<%= User.banner_url(@user) %>"/>
- <% end %>
- <%= if @user.local do %>
- <ap_enabled>true</ap_enabled>
- <% end %>
</author>
diff --git a/lib/pleroma/web/templates/feed/feed/_author.rss.eex b/lib/pleroma/web/templates/feed/feed/_author.rss.eex
index 526aeddcf..22477e6b1 100644
--- a/lib/pleroma/web/templates/feed/feed/_author.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/_author.rss.eex
@@ -1,17 +1,10 @@
-<managingEditor>
- <guid><%= @user.ap_id %></guid>
- <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><%= escape(@user.bio) %></poco:note>
- <description><%= escape(@user.bio) %></description>
- <name><%= @user.nickname %></name>
- <link rel="avatar"><%= User.avatar_url(@user) %></link>
- <%= if User.banner_url(@user) do %>
- <link rel="header"><%= User.banner_url(@user) %></link>
- <% end %>
- <%= if @user.local do %>
- <ap_enabled>true</ap_enabled>
- <% end %>
-</managingEditor>
+<managingEditor><%= "#{email(@user)} (#{escape(@user.name)})" %></managingEditor>
+<activity:object>http://activitystrea.ms/schema/1.0/person</activity:object>
+<activity:displayName><%= @user.name %></activity:displayName>
+<activity:image><%= User.avatar_url(@user) %></activity:image>
+<activity:id><%= @user.ap_id %></activity:id>
+<activity:published><%= to_rfc3339(@user.inserted_at) %></activity:published>
+<activity:updated><%= to_rfc3339(@user.updated_at) %></activity:updated>
+<poco:preferredUsername><%= @user.nickname %></poco:preferredUsername>
+<poco:displayName><%= @user.name %></poco:displayName>
+<poco:note><%= escape(@user.bio) %></poco:note>
diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex
index c2de28fe4..03c222975 100644
--- a/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.atom.eex
@@ -1,12 +1,22 @@
<entry>
- <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
- <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
-
- <%= render @view_module, "_tag_author.atom", assigns %>
-
- <id><%= @data["id"] %></id>
- <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
- <content type="html"><%= activity_content(@data) %></content>
+ <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+ <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+
+ <%= render Phoenix.Controller.view_module(@conn), "_tag_author.atom", assigns %>
+
+ <id><%= @data["id"] %></id>
+ <title><%= activity_title(@data, Keyword.get(@feed_config, :post_title, %{})) %></title>
+ <content type="html"><%= activity_description(@data) %></content>
+ <published><%= to_rfc3339(@data["published"]) %></published>
+ <updated><%= to_rfc3339(@data["published"]) %></updated>
+ <ostatus:conversation ref="<%= activity_context(@activity) %>">
+ <%= activity_context(@activity) %>
+ </ostatus:conversation>
+ <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
+
+ <%= if @data["summary"] != "" do %>
+ <summary><%= @data["summary"] %></summary>
+ <% end %>
<%= if @activity.local do %>
<link type="application/atom+xml" href='<%= @data["id"] %>' rel="self"/>
@@ -15,37 +25,25 @@
<link type="text/html" href='<%= @data["external_url"] %>' rel="alternate"/>
<% end %>
- <published><%= @activity.data["published"] %></published>
- <updated><%= @activity.data["published"] %></updated>
-
- <ostatus:conversation ref="<%= activity_context(@activity) %>">
- <%= activity_context(@activity) %>
- </ostatus:conversation>
- <link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
-
- <%= if @data["summary"] do %>
- <summary><%= @data["summary"] %></summary>
- <% end %>
-
- <%= for id <- @activity.recipients do %>
- <%= if id == Pleroma.Constants.as_public() do %>
+ <%= for id <- @activity.recipients do %>
+ <%= if id == Pleroma.Constants.as_public() do %>
+ <link rel="mentioned"
+ ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"
+ href="http://activityschema.org/collection/public"/>
+ <% else %>
+ <%= unless Regex.match?(~r/^#{Pleroma.Web.Endpoint.url()}.+followers$/, id) do %>
<link rel="mentioned"
- ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"
- href="http://activityschema.org/collection/public"/>
- <% else %>
- <%= unless Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) do %>
- <link rel="mentioned"
- ostatus:object-type="http://activitystrea.ms/schema/1.0/person"
- href="<%= id %>" />
- <% end %>
+ ostatus:object-type="http://activitystrea.ms/schema/1.0/person"
+ href="<%= id %>" />
<% end %>
<% end %>
+ <% end %>
- <%= for tag <- Pleroma.Object.hashtags(@object) do %>
- <category term="<%= tag %>"></category>
- <% end %>
+ <%= for tag <- Pleroma.Object.hashtags(@object) do %>
+ <category term="<%= tag %>"></category>
+ <% end %>
- <%= for {emoji, file} <- @data["emoji"] || %{} do %>
- <link name="<%= emoji %>" rel="emoji" href="<%= file %>"/>
- <% end %>
+ <%= for {emoji, file} <- @data["emoji"] || %{} do %>
+ <link name="<%= emoji %>" rel="emoji" href="<%= file %>"/>
+ <% end %>
</entry>
diff --git a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex
index 2334e24a2..1b8c34b87 100644
--- a/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex
+++ b/lib/pleroma/web/templates/feed/feed/_tag_activity.xml.eex
@@ -4,9 +4,9 @@
<guid isPermalink="true"><%= activity_context(@activity) %></guid>
<link><%= activity_context(@activity) %></link>
- <pubDate><%= pub_date(@activity.data["published"]) %></pubDate>
+ <pubDate><%= to_rfc2822(@data["published"]) %></pubDate>
- <description><%= activity_content(@data) %></description>
+ <description><%= activity_description(@data) %></description>
<%= for attachment <- @data["attachment"] || [] do %>
<enclosure url="<%= attachment_href(attachment) %>" type="<%= attachment_type(attachment) %>"/>
<% end %>
diff --git a/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex
index 997c4936e..71c696832 100644
--- a/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/_tag_author.atom.eex
@@ -1,18 +1,14 @@
<author>
- <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
- <id><%= @actor.ap_id %></id>
- <uri><%= @actor.ap_id %></uri>
- <name><%= @actor.nickname %></name>
- <summary><%= escape(@actor.bio) %></summary>
- <link rel="avatar" href="<%= User.avatar_url(@actor) %>"/>
- <%= if User.banner_url(@actor) do %>
- <link rel="header" href="<%= User.banner_url(@actor) %>"/>
- <% end %>
- <%= if @actor.local do %>
- <ap_enabled>true</ap_enabled>
- <% end %>
-
- <poco:preferredUsername><%= @actor.nickname %></poco:preferredUsername>
- <poco:displayName><%= @actor.name %></poco:displayName>
- <poco:note><%= escape(@actor.bio) %></poco:note>
+ <uri><%= @actor.ap_id %></uri>
+ <name><%= @actor.nickname %></name>
+ <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
+ <activity:displayName><%= @actor.name %></activity:displayName>
+ <activity:image><%= User.avatar_url(@actor) %></activity:image>
+ <activity:id><%= @actor.ap_id %></activity:id>
+ <activity:published><%= to_rfc3339(@actor.inserted_at) %></activity:published>
+ <activity:updated><%= to_rfc3339(@actor.updated_at) %></activity:updated>
+ <activity:url><%= @actor.ap_id %></activity:url>
+ <poco:preferredUsername><%= @actor.nickname %></poco:preferredUsername>
+ <poco:displayName><%= @actor.name %></poco:displayName>
+ <poco:note><%= escape(@actor.bio) %></poco:note>
</author>
diff --git a/lib/pleroma/web/templates/feed/feed/tag.atom.eex b/lib/pleroma/web/templates/feed/feed/tag.atom.eex
index a288539ed..14b0ee594 100644
--- a/lib/pleroma/web/templates/feed/feed/tag.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/tag.atom.eex
@@ -1,22 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
+<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"
+ xmlns:statusnet="http://status.net/schema/api/1/">
-<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"
- xmlns:thr="http://purl.org/syndication/thread/1.0"
- xmlns:georss="http://www.georss.org/georss"
- xmlns:activity="http://activitystrea.ms/spec/1.0/"
- xmlns:media="http://purl.org/syndication/atommedia"
- xmlns:poco="http://portablecontacts.net/spec/1.0"
- xmlns:ostatus="http://ostatus.org/schema/1.0"
- xmlns:statusnet="http://status.net/schema/api/1/">
+ <id><%= Routes.tag_feed_url(@conn, :feed, @tag) <> ".atom" %></id>
+ <title>#<%= @tag %></title>
+ <subtitle><%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %></subtitle>
+ <logo><%= feed_logo() %></logo>
+ <updated><%= most_recent_update(@activities) %></updated>
+ <link rel="self" href="<%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.atom' %>" type="application/atom+xml"/>
- <id><%= '#{tag_feed_url(@conn, :feed, @tag)}.rss' %></id>
- <title>#<%= @tag %></title>
-
- <subtitle>These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse.</subtitle>
- <logo><%= feed_logo() %></logo>
- <updated><%= most_recent_update(@activities) %></updated>
- <link rel="self" href="<%= '#{tag_feed_url(@conn, :feed, @tag)}.atom' %>" type="application/atom+xml"/>
- <%= for activity <- @activities do %>
- <%= render @view_module, "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %>
- <% end %>
+ <%= for activity <- @activities do %>
+ <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.atom", Map.merge(assigns, prepare_activity(activity, actor: true)) %>
+ <% end %>
</feed>
diff --git a/lib/pleroma/web/templates/feed/feed/tag.rss.eex b/lib/pleroma/web/templates/feed/feed/tag.rss.eex
index eeda01a04..27dde5627 100644
--- a/lib/pleroma/web/templates/feed/feed/tag.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/tag.rss.eex
@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
-<rss version="2.0" xmlns:webfeeds="http://webfeeds.org/rss/1.0">
+<rss version="2.0"
+ xmlns:webfeeds="http://webfeeds.org/rss/1.0"
+ xmlns:thr="http://purl.org/syndication/thread/1.0">
<channel>
-
<title>#<%= @tag %></title>
- <description>These are public toots tagged with #<%= @tag %>. You can interact with them if you have an account anywhere in the fediverse.</description>
- <link><%= '#{tag_feed_url(@conn, :feed, @tag)}.rss' %></link>
+ <description><%= Gettext.dpgettext("static_pages", "tag feed description", "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse.", tag: @tag) %></description>
+ <link><%= '#{Routes.tag_feed_url(@conn, :feed, @tag)}.rss' %></link>
<webfeeds:logo><%= feed_logo() %></webfeeds:logo>
<webfeeds:accentColor>2b90d9</webfeeds:accentColor>
<%= for activity <- @activities do %>
- <%= render @view_module, "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %>
+ <%= render Phoenix.Controller.view_module(@conn), "_tag_activity.xml", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</channel>
</rss>
diff --git a/lib/pleroma/web/templates/feed/feed/user.atom.eex b/lib/pleroma/web/templates/feed/feed/user.atom.eex
index c6acd848f..e36bfc66c 100644
--- a/lib/pleroma/web/templates/feed/feed/user.atom.eex
+++ b/lib/pleroma/web/templates/feed/feed/user.atom.eex
@@ -6,19 +6,20 @@
xmlns:poco="http://portablecontacts.net/spec/1.0"
xmlns:ostatus="http://ostatus.org/schema/1.0">
- <id><%= user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id>
+ <id><%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %></id>
<title><%= @user.nickname <> "'s timeline" %></title>
- <updated><%= most_recent_update(@activities, @user) %></updated>
+ <subtitle><%= escape(@user.bio) %></subtitle>
+ <updated><%= most_recent_update(@activities, @user, :atom) %></updated>
<logo><%= logo(@user) %></logo>
- <link rel="self" href="<%= '#{user_feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
+ <link rel="self" href="<%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.atom' %>" type="application/atom+xml"/>
- <%= render @view_module, "_author.atom", assigns %>
+ <%= render Phoenix.Controller.view_module(@conn), "_author.atom", assigns %>
<%= if last_activity(@activities) do %>
- <link rel="next" href="<%= '#{user_feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/>
+ <link rel="next" href="<%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.atom?max_id=#{last_activity(@activities).id}' %>" type="application/atom+xml"/>
<% end %>
<%= for activity <- @activities do %>
- <%= render @view_module, "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %>
+ <%= render Phoenix.Controller.view_module(@conn), "_activity.atom", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</feed>
diff --git a/lib/pleroma/web/templates/feed/feed/user.rss.eex b/lib/pleroma/web/templates/feed/feed/user.rss.eex
index d69120480..fae3fcf3d 100644
--- a/lib/pleroma/web/templates/feed/feed/user.rss.eex
+++ b/lib/pleroma/web/templates/feed/feed/user.rss.eex
@@ -1,20 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<rss version="2.0">
+<rss version="2.0"
+ xmlns:atom="http://www.w3.org/2005/Atom"
+ xmlns:thr="http://purl.org/syndication/thread/1.0"
+ xmlns:activity="http://activitystrea.ms/spec/1.0/"
+ xmlns:ostatus="http://ostatus.org/schema/1.0"
+ xmlns:poco="http://portablecontacts.net/spec/1.0">
<channel>
- <guid><%= user_feed_url(@conn, :feed, @user.nickname) <> ".rss" %></guid>
<title><%= @user.nickname <> "'s timeline" %></title>
- <updated><%= most_recent_update(@activities, @user) %></updated>
- <image><%= logo(@user) %></image>
- <link><%= '#{user_feed_url(@conn, :feed, @user.nickname)}.rss' %></link>
+ <link><%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %></link>
+ <atom:link href="<%= Routes.user_feed_url(@conn, :feed, @user.nickname) <> ".atom" %>"
+ rel="self" type="application/rss+xml" />
+ <description><%= escape(@user.bio) %></description>
+ <image>
+ <url><%= logo(@user) %></url>
+ <title><%= @user.nickname <> "'s timeline" %></title>
+ <link><%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss' %></link>
+ </image>
- <%= render @view_module, "_author.rss", assigns %>
+ <%= render Phoenix.Controller.view_module(@conn), "_author.rss", assigns %>
<%= if last_activity(@activities) do %>
- <link rel="next"><%= '#{user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}' %></link>
+ <link rel="next"><%= '#{Routes.user_feed_url(@conn, :feed, @user.nickname)}.rss?max_id=#{last_activity(@activities).id}' %></link>
<% end %>
<%= for activity <- @activities do %>
- <%= render @view_module, "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %>
+ <%= render Phoenix.Controller.view_module(@conn), "_activity.rss", Map.merge(assigns, prepare_activity(activity)) %>
<% end %>
</channel>
</rss>
diff --git a/lib/pleroma/web/templates/layout/app.html.eex b/lib/pleroma/web/templates/layout/app.html.eex
index 1ede59fd8..e33bada85 100644
--- a/lib/pleroma/web/templates/layout/app.html.eex
+++ b/lib/pleroma/web/templates/layout/app.html.eex
@@ -1,5 +1,5 @@
<!DOCTYPE html>
-<html lang="en">
+<html lang="<%= Pleroma.Web.Gettext.language_tag() %>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui">
diff --git a/lib/pleroma/web/templates/layout/email.html.eex b/lib/pleroma/web/templates/layout/email.html.eex
index f6dcd7f0f..5858e48b4 100644
--- a/lib/pleroma/web/templates/layout/email.html.eex
+++ b/lib/pleroma/web/templates/layout/email.html.eex
@@ -1,10 +1,10 @@
<!DOCTYPE html>
-<html lang="en">
+<html lang="<%= Pleroma.Web.Gettext.language_tag() %>">
<head>
<meta charset="utf-8">
<title><%= @email.subject %></title>
</head>
<body>
- <%= render @view_module, @view_template, assigns %>
+ <%= render Phoenix.Controller.view_module(@conn), Phoenix.Controller.view_template(@conn), assigns %>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/lib/pleroma/web/templates/layout/embed.html.eex b/lib/pleroma/web/templates/layout/embed.html.eex
index 8b905f070..1197288e5 100644
--- a/lib/pleroma/web/templates/layout/embed.html.eex
+++ b/lib/pleroma/web/templates/layout/embed.html.eex
@@ -10,6 +10,6 @@
<base target="_parent">
</head>
<body>
- <%= render @view_module, @view_template, assigns %>
+ <%= render Phoenix.Controller.view_module(@conn), Phoenix.Controller.view_template(@conn), assigns %>
</body>
</html>
diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
index 7b476f02d..df090ffcd 100644
--- a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
+++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex
@@ -1 +1 @@
-<h1>UNSUBSCRIBE FAILURE</h1>
+<h1><%= Gettext.dpgettext("static_pages", "mailer unsubscribe failed message", "UNSUBSCRIBE FAILURE") %></h1>
diff --git a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
index 6dfa2c185..cbce495d4 100644
--- a/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
+++ b/lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex
@@ -1 +1 @@
-<h1>UNSUBSCRIBE SUCCESSFUL</h1>
+<h1><%= Gettext.dpgettext("static_pages", "mailer unsubscribe successful message", "UNSUBSCRIBE SUCCESSFUL") %></h1>
diff --git a/lib/pleroma/web/templates/masto_fe/index.html.eex b/lib/pleroma/web/templates/masto_fe/index.html.eex
deleted file mode 100644
index c330960fa..000000000
--- a/lib/pleroma/web/templates/masto_fe/index.html.eex
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<html lang='en'>
-<head>
-<meta charset='utf-8'>
-<meta content='width=device-width, initial-scale=1' name='viewport'>
-<title>
-<%= 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>
-
-<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/getting_started.js'>
-<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/compose.js'>
-<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/home_timeline.js'>
-<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/notifications.js'>
-<script id='initial-state' type='application/json'><%= initial_state(@token, @user, @custom_emojis) %></script>
-
-<script src="/packs/core/common.js"></script>
-<link rel="stylesheet" media="all" href="/packs/core/common.css" />
-
-<script src="/packs/flavours/glitch/common.js"></script>
-<link rel="stylesheet" media="all" href="/packs/flavours/glitch/common.css" />
-
-<script src="/packs/flavours/glitch/home.js"></script>
-</head>
-<body class='app-body no-reduce-motion system-font'>
- <div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'>
- </div>
-</body>
-</html>
diff --git a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex
index 5ab59b57b..e45d13bdf 100644
--- a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex
+++ b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex
@@ -5,11 +5,11 @@
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<% end %>
-<h2>Two-factor recovery</h2>
+<h2><%= Gettext.dpgettext("static_pages", "mfa recover page title", "Two-factor recovery") %></h2>
-<%= form_for @conn, mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %>
+<%= form_for @conn, Routes.mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %>
<div class="input">
- <%= label f, :code, "Recovery code" %>
+ <%= label f, :code, Gettext.dpgettext("static_pages", "mfa recover recovery code prompt", "Recovery code") %>
<%= text_input f, :code, [autocomplete: false, autocorrect: "off", autocapitalize: "off", autofocus: true, spellcheck: false] %>
<%= hidden_input f, :mfa_token, value: @mfa_token %>
<%= hidden_input f, :state, value: @state %>
@@ -17,8 +17,8 @@
<%= hidden_input f, :challenge_type, value: "recovery" %>
</div>
-<%= submit "Verify" %>
+<%= submit Gettext.dpgettext("static_pages", "mfa recover verify recovery code button", "Verify") %>
<% end %>
-<a href="<%= mfa_path(@conn, :show, %{challenge_type: "totp", mfa_token: @mfa_token, state: @state, redirect_uri: @redirect_uri}) %>">
- Enter a two-factor code
+<a href="<%= Routes.mfa_path(@conn, :show, %{challenge_type: "totp", mfa_token: @mfa_token, state: @state, redirect_uri: @redirect_uri}) %>">
+ <%= Gettext.dpgettext("static_pages", "mfa recover use 2fa code link", "Enter a two-factor code") %>
</a>
diff --git a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex
index af85777eb..50e6c04b6 100644
--- a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex
+++ b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex
@@ -5,20 +5,20 @@
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<% end %>
-<h2>Two-factor authentication</h2>
+<h2><%= Gettext.dpgettext("static_pages", "mfa auth page title", "Two-factor authentication") %></h2>
-<%= form_for @conn, mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %>
+<%= form_for @conn, Routes.mfa_verify_path(@conn, :verify), [as: "mfa"], fn f -> %>
<div class="input">
- <%= label f, :code, "Authentication code" %>
- <%= text_input f, :code, [autocomplete: false, autocorrect: "off", autocapitalize: "off", autofocus: true, pattern: "[0-9]*", spellcheck: false] %>
+ <%= label f, :code, Gettext.dpgettext("static_pages", "mfa auth code prompt", "Authentication code") %>
+ <%= text_input f, :code, [autocomplete: "one-time-code", autocorrect: "off", autocapitalize: "off", autofocus: true, pattern: "[0-9]*", spellcheck: false] %>
<%= hidden_input f, :mfa_token, value: @mfa_token %>
<%= hidden_input f, :state, value: @state %>
<%= hidden_input f, :redirect_uri, value: @redirect_uri %>
<%= hidden_input f, :challenge_type, value: "totp" %>
</div>
-<%= submit "Verify" %>
+<%= submit Gettext.dpgettext("static_pages", "mfa auth verify code button", "Verify") %>
<% end %>
-<a href="<%= mfa_path(@conn, :show, %{challenge_type: "recovery", mfa_token: @mfa_token, state: @state, redirect_uri: @redirect_uri}) %>">
- Enter a two-factor recovery code
+<a href="<%= Routes.mfa_path(@conn, :show, %{challenge_type: "recovery", mfa_token: @mfa_token, state: @state, redirect_uri: @redirect_uri}) %>">
+ <%= Gettext.dpgettext("static_pages", "mfa auth page use recovery code link", "Enter a two-factor recovery code") %>
</a>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex
index c9ec1ecbf..7585c4d3e 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex
@@ -1,5 +1,5 @@
<div class="scopes-input">
- <%= label @form, :scope, "The following permissions will be granted" %>
+ <%= label @form, :scope, Gettext.dpgettext("static_pages", "oauth scopes message", "The following permissions will be granted") %>
<div class="scopes">
<%= for scope <- @available_scopes do %>
<%# Note: using hidden input with `unchecked_value` in order to distinguish user's empty selection from `scope` param being omitted %>
@@ -8,7 +8,7 @@
<%= checkbox @form, :"scope_#{scope}", value: scope in @scopes && scope, checked_value: scope, unchecked_value: "", name: "authorization[scope][]" %>
<%= label @form, :"scope_#{scope}", String.capitalize(scope) %>
<%= if scope in @scopes && scope do %>
- <%= String.capitalize(scope) %>
+ <code><%= scope %></code> <%= :"Elixir.Gettext".dgettext(Gettext, "oauth_scopes", scope) %>
<% end %>
</div>
<% else %>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex
index 4a0718851..98904ad64 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex
@@ -1,8 +1,8 @@
-<h2>Sign in with external provider</h2>
+<h2><%= Gettext.dpgettext("static_pages", "oauth external provider page title", "Sign in with external provider") %></h2>
-<%= form_for @conn, o_auth_path(@conn, :prepare_request), [as: "authorization", method: "get"], fn f -> %>
+<%= form_for @conn, Routes.o_auth_path(@conn, :prepare_request), [as: "authorization", method: "get"], fn f -> %>
<div style="display: none">
- <%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %>
+ <%= render Phoenix.Controller.view_module(@conn), "_scopes.html", Map.merge(assigns, %{form: f}) %>
</div>
<%= hidden_input f, :client_id, value: @client_id %>
@@ -10,6 +10,6 @@
<%= hidden_input f, :state, value: @state %>
<%= for strategy <- Pleroma.Config.oauth_consumer_strategies() do %>
- <%= submit "Sign in with #{String.capitalize(strategy)}", name: "provider", value: strategy %>
+ <%= submit Gettext.dpgettext("static_pages", "oauth external provider sign in button", "Sign in with %{strategy}", strategy: String.capitalize(strategy)), name: "provider", value: strategy %>
<% end %>
<% end %>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex
index ffabe29a6..76ed3fda5 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex
@@ -1,2 +1,2 @@
-<h1>Successfully authorized</h1>
-<h2>Token code is <br><%= @auth.token %></h2>
+<h1><%= Gettext.dpgettext("static_pages", "oauth authorized page title", "Successfully authorized") %></h1>
+<h2><%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is <br>%{token}", token: safe_to_string(html_escape(@auth.token))) %></h2>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex
index 82785c4b9..754bf2eb0 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex
@@ -1,2 +1,2 @@
-<h1>Authorization exists</h1>
-<h2>Access token is <br><%= @token.token %></h2>
+<h1><%= Gettext.dpgettext("static_pages", "oauth authorization exists page title", "Authorization exists") %></h1>
+<h2><%= raw Gettext.dpgettext("static_pages", "oauth token code message", "Token code is <br>%{token}", token: safe_to_string(html_escape(@token.token))) %></h2>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex
index facedc8db..1f661efb2 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex
@@ -5,34 +5,34 @@
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<% end %>
-<h2>Registration Details</h2>
+<h2><%= Gettext.dpgettext("static_pages", "oauth register page title", "Registration Details") %></h2>
-<p>If you'd like to register a new account, please provide the details below.</p>
-<%= form_for @conn, o_auth_path(@conn, :register), [as: "authorization"], fn f -> %>
+<p><%= Gettext.dpgettext("static_pages", "oauth register page fill form prompt", "If you'd like to register a new account, please provide the details below.") %></p>
+<%= form_for @conn, Routes.o_auth_path(@conn, :register), [as: "authorization"], fn f -> %>
<div class="input">
- <%= label f, :nickname, "Nickname" %>
- <%= text_input f, :nickname, value: @nickname %>
+ <%= label f, :nickname, Gettext.dpgettext("static_pages", "oauth register page nickname prompt", "Nickname") %>
+ <%= text_input f, :nickname, value: @nickname, autocomplete: "username" %>
</div>
<div class="input">
- <%= label f, :email, "Email" %>
- <%= text_input f, :email, value: @email %>
+ <%= label f, :email, Gettext.dpgettext("static_pages", "oauth register page email prompt", "Email") %>
+ <%= text_input f, :email, value: @email, autocomplete: "email" %>
</div>
-<%= submit "Proceed as new user", name: "op", value: "register" %>
+<%= submit Gettext.dpgettext("static_pages", "oauth register page register button", "Proceed as new user"), name: "op", value: "register" %>
-<p>Alternatively, sign in to connect to existing account.</p>
+<p><%= Gettext.dpgettext("static_pages", "oauth register page login prompt", "Alternatively, sign in to connect to existing account.") %></p>
<div class="input">
- <%= label f, :name, "Name or email" %>
- <%= text_input f, :name %>
+ <%= label f, :name, Gettext.dpgettext("static_pages", "oauth register page login username prompt", "Name or email") %>
+ <%= text_input f, :name, autocomplete: "username" %>
</div>
<div class="input">
- <%= label f, :password, "Password" %>
- <%= password_input f, :password %>
+ <%= label f, :password, Gettext.dpgettext("static_pages", "oauth register page login password prompt", "Password") %>
+ <%= password_input f, :password, autocomplete: "password" %>
</div>
-<%= submit "Proceed as existing user", name: "op", value: "connect" %>
+<%= submit Gettext.dpgettext("static_pages", "oauth register page login button", "Proceed as existing user"), name: "op", value: "connect" %>
<%= hidden_input f, :client_id, value: @client_id %>
<%= hidden_input f, :redirect_uri, value: @redirect_uri %>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex
index 1a85818ec..b3654f3eb 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex
@@ -5,7 +5,7 @@
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<% end %>
-<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
+<%= form_for @conn, Routes.o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
<%= if @user do %>
<div class="account-header">
@@ -20,36 +20,38 @@
<div class="container__content">
<%= if @app do %>
- <p>Application <strong><%= @app.client_name %></strong> is requesting access to your account.</p>
- <%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %>
+ <p><%= raw Gettext.dpgettext("static_pages", "oauth authorize message", "Application <strong>%{client_name}</strong> is requesting access to your account.", client_name: safe_to_string(html_escape(@app.client_name))) %></p>
+ <%= render Phoenix.Controller.view_module(@conn), "_scopes.html", Map.merge(assigns, %{form: f}) %>
<% end %>
<%= if @user do %>
<div class="actions">
- <a class="button button--cancel" href="/">Cancel</a>
- <%= submit "Approve", class: "button--approve" %>
+ <a class="button button--cancel" href="/">
+ <%= Gettext.dpgettext("static_pages", "oauth authorize cancel button", "Cancel") %>
+ </a>
+ <%= submit Gettext.dpgettext("static_pages", "oauth authorize approve button", "Approve"), class: "button--approve" %>
</div>
<% else %>
<%= if @params["registration"] in ["true", true] do %>
- <h3>This is the first time you visit! Please enter your Pleroma handle.</h3>
- <p>Choose carefully! You won't be able to change this later. You will be able to change your display name, though.</p>
+ <h3><%= Gettext.dpgettext("static_pages", "oauth register page title", "This is the first time you visit! Please enter your Pleroma handle.") %></h3>
+ <p><%= Gettext.dpgettext("static_pages", "oauth register nickname unchangeable warning", "Choose carefully! You won't be able to change this later. You will be able to change your display name, though.") %></p>
<div class="input">
- <%= label f, :nickname, "Pleroma Handle" %>
- <%= text_input f, :nickname, placeholder: "lain" %>
+ <%= label f, :nickname, Gettext.dpgettext("static_pages", "oauth register nickname prompt", "Pleroma Handle") %>
+ <%= text_input f, :nickname, placeholder: "lain", autocomplete: "username" %>
</div>
<%= hidden_input f, :name, value: @params["name"] %>
<%= hidden_input f, :password, value: @params["password"] %>
<br>
<% else %>
<div class="input">
- <%= label f, :name, "Username" %>
+ <%= label f, :name, Gettext.dpgettext("static_pages", "oauth login username prompt", "Username") %>
<%= text_input f, :name %>
</div>
<div class="input">
- <%= label f, :password, "Password" %>
+ <%= label f, :password, Gettext.dpgettext("static_pages", "oauth login password prompt", "Password") %>
<%= password_input f, :password %>
</div>
- <%= submit "Log In" %>
+ <%= submit Gettext.dpgettext("static_pages", "oauth login button", "Log In") %>
<% end %>
<% end %>
</div>
@@ -61,5 +63,5 @@
<% end %>
<%= if Pleroma.Config.oauth_consumer_enabled?() do %>
- <%= render @view_module, Pleroma.Web.Auth.Authenticator.oauth_consumer_template(), assigns %>
+ <%= render Phoenix.Controller.view_module(@conn), Pleroma.Web.Auth.WrapperAuthenticator.oauth_consumer_template(), assigns %>
<% end %>
diff --git a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex
index 3191bf450..a14ca305e 100644
--- a/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex
+++ b/lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex
@@ -5,7 +5,7 @@
<form class="pull-right collapse" method="POST" action="<%= Helpers.util_path(@conn, :remote_subscribe) %>">
<input type="hidden" name="nickname" value="<%= @user.nickname %>">
<input type="hidden" name="profile" value="">
- <button type="submit" class="collapse">Remote follow</button>
+ <button type="submit" class="collapse"><%= Gettext.dpgettext("static_pages", "static fe profile page remote follow button", "Remote follow") %></button>
</form>
<%= raw Formatter.emojify(@user.name, @user.emoji) %> |
<%= link "@#{@user.nickname}@#{Endpoint.host()}", to: (@user.uri || @user.ap_id) %>
diff --git a/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex
index ee84750c7..5ac0aa4e0 100644
--- a/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex
@@ -1 +1 @@
-<h2>Invalid Token</h2>
+<h2><%= Gettext.dpgettext("static_pages", "password reset invalid token message", "Invalid Token") %></h2>
diff --git a/lib/pleroma/web/templates/twitter_api/password/reset.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex
index 7d3ef6b0d..6a544af51 100644
--- a/lib/pleroma/web/templates/twitter_api/password/reset.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/reset.html.eex
@@ -1,13 +1,13 @@
<h2>Password Reset for <%= @user.nickname %></h2>
-<%= form_for @conn, reset_password_path(@conn, :do_reset), [as: "data"], fn f -> %>
+<%= form_for @conn, Routes.reset_password_path(@conn, :do_reset), [as: "data"], fn f -> %>
<div class="form-row">
- <%= label f, :password, "Password" %>
+ <%= label f, :password, Gettext.dpgettext("static_pages", "password reset form password prompt", "Password") %>
<%= password_input f, :password %>
</div>
<div class="form-row">
- <%= label f, :password_confirmation, "Confirmation" %>
+ <%= label f, :password_confirmation, Gettext.dpgettext("static_pages", "password reset form confirm password prompt", "Confirmation") %>
<%= password_input f, :password_confirmation %>
</div>
<%= hidden_input f, :token, value: @token.token %>
- <%= submit "Reset" %>
+ <%= submit Gettext.dpgettext("static_pages", "password reset button", "Reset") %>
<% end %>
diff --git a/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex
index df037c01e..774e3462a 100644
--- a/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex
@@ -1,2 +1,6 @@
-<h2>Password reset failed</h2>
-<h3><a href="<%= Pleroma.Web.base_url() %>">Homepage</a></h3>
+<h2><%= Gettext.dpgettext("static_pages", "password reset failed message", "Password reset failed") %></h2>
+<h3>
+ <a href="<%= Pleroma.Web.Endpoint.url() %>">
+ <%= Gettext.dpgettext("static_pages", "password reset failed homepage link", "Homepage") %>
+ </a>
+</h3>
diff --git a/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex
index f30ba3274..40f6bb3fc 100644
--- a/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex
@@ -1,2 +1,2 @@
-<h2>Password changed!</h2>
-<h3><a href="<%= Pleroma.Web.base_url() %>">Homepage</a></h3>
+<h2><%= Gettext.dpgettext("static_pages", "password reset successful message", "Password changed!") %></h2>
+<h3><a href="<%= Pleroma.Web.Endpoint.url() %>"><%= Gettext.dpgettext("static_pages", "password reset successful homepage link", "Homepage") %></a></h3>
diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex
index 5ba192cd7..e2d251fac 100644
--- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex
@@ -1,11 +1,11 @@
<%= if @error == :error do %>
- <h2>Error fetching user</h2>
+ <h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error fetching user") %></h2>
<% else %>
- <h2>Remote follow</h2>
+ <h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remote follow") %></h2>
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
<p><%= @followee.nickname %></p>
- <%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %>
+ <%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "user"], fn f -> %>
<%= hidden_input f, :id, value: @followee.id %>
- <%= submit "Authorize" %>
+ <%= submit Gettext.dpgettext("static_pages", "remote follow authorization button", "Authorize") %>
<% end %>
<% end %>
diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex
index df44988ee..26340a906 100644
--- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex
@@ -1,14 +1,14 @@
<%= if @error do %>
<h2><%= @error %></h2>
<% end %>
-<h2>Log in to follow</h2>
+<h2><%= Gettext.dpgettext("static_pages", "remote follow header, need login", "Log in to follow") %></h2>
<p><%= @followee.nickname %></p>
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
-<%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
-<%= text_input f, :name, placeholder: "Username", required: true %>
+<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "authorization"], fn f -> %>
+<%= text_input f, :name, placeholder: Gettext.dpgettext("static_pages", "placeholder text for username entry", "Username"), required: true, autocomplete: "username" %>
<br>
-<%= password_input f, :password, placeholder: "Password", required: true %>
+<%= password_input f, :password, placeholder: Gettext.dpgettext("static_pages", "placeholder text for password entry", "Password"), required: true, autocomplete: "password" %>
<br>
<%= hidden_input f, :id, value: @followee.id %>
-<%= submit "Authorize" %>
+<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for login", "Authorize") %>
<% end %>
diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex
index adc3a3e3d..638212c1e 100644
--- a/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex
@@ -1,13 +1,13 @@
<%= if @error do %>
<h2><%= @error %></h2>
<% end %>
-<h2>Two-factor authentication</h2>
+<h2><%= Gettext.dpgettext("static_pages", "remote follow mfa header", "Two-factor authentication") %></h2>
<p><%= @followee.nickname %></p>
<img height="128" width="128" src="<%= avatar_url(@followee) %>">
-<%= form_for @conn, remote_follow_path(@conn, :do_follow), [as: "mfa"], fn f -> %>
-<%= text_input f, :code, placeholder: "Authentication code", required: true %>
+<%= form_for @conn, Routes.remote_follow_path(@conn, :do_follow), [as: "mfa"], fn f -> %>
+<%= text_input f, :code, placeholder: Gettext.dpgettext("static_pages", "placeholder text for auth code entry", "Authentication code"), required: true %>
<br>
<%= hidden_input f, :id, value: @followee.id %>
<%= hidden_input f, :token, value: @mfa_token %>
-<%= submit "Authorize" %>
+<%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for mfa", "Authorize") %>
<% end %>
diff --git a/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex
index da473d502..2fb4cc5d3 100644
--- a/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex
@@ -1,6 +1,5 @@
<%= if @error do %>
-<p>Error following account</p>
+<p><%= Gettext.dpgettext("static_pages", "remote follow error", "Error following account") %></p>
<% else %>
-<h2>Account followed!</h2>
+<h2><%= Gettext.dpgettext("static_pages", "remote follow success", "Account followed!") %></h2>
<% end %>
-
diff --git a/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex
new file mode 100644
index 000000000..d77174967
--- /dev/null
+++ b/lib/pleroma/web/templates/twitter_api/util/status_interact.html.eex
@@ -0,0 +1,10 @@
+<%= if @error do %>
+ <h2><%= Gettext.dpgettext("static_pages", "status interact error", "Error: %{error}", error: @error) %></h2>
+<% else %>
+ <h2><%= raw Gettext.dpgettext("static_pages", "status interact header", "Interacting with %{nickname}'s %{status_link}", nickname: safe_to_string(html_escape(@nickname)), status_link: safe_to_string(link(Gettext.dpgettext("static_pages", "status interact header - status link text", "status"), to: @status_link))) %></h2>
+ <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "status"], fn f -> %>
+ <%= hidden_input f, :status_id, value: @status_id %>
+ <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
+ <%= submit Gettext.dpgettext("static_pages", "status interact authorization button", "Interact") %>
+ <% end %>
+<% end %>
diff --git a/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex b/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex
index f60accebf..848660f26 100644
--- a/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex
+++ b/lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex
@@ -1,10 +1,10 @@
<%= if @error do %>
- <h2>Error: <%= @error %></h2>
+ <h2><%= Gettext.dpgettext("static_pages", "remote follow error", "Error: %{error}", error: @error) %></h2>
<% else %>
- <h2>Remotely follow <%= @nickname %></h2>
- <%= form_for @conn, util_path(@conn, :remote_subscribe), [as: "user"], fn f -> %>
+ <h2><%= Gettext.dpgettext("static_pages", "remote follow header", "Remotely follow %{nickname}", nickname: @nickname) %></h2>
+ <%= form_for @conn, Routes.util_path(@conn, :remote_subscribe), [as: "user"], fn f -> %>
<%= hidden_input f, :nickname, value: @nickname %>
- <%= text_input f, :profile, placeholder: "Your account ID, e.g. lain@quitter.se" %>
- <%= submit "Follow" %>
+ <%= text_input f, :profile, placeholder: Gettext.dpgettext("static_pages", "placeholder text for account id", "Your account ID, e.g. lain@quitter.se") %>
+ <%= submit Gettext.dpgettext("static_pages", "remote follow authorization button for following with a remote account", "Follow") %>
<% end %>
<% end %>
diff --git a/lib/pleroma/web/translation_helpers.ex b/lib/pleroma/web/translation_helpers.ex
index 0fe31d189..e9638b00a 100644
--- a/lib/pleroma/web/translation_helpers.ex
+++ b/lib/pleroma/web/translation_helpers.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TranslationHelpers do
diff --git a/lib/pleroma/web/twitter_api/controller.ex b/lib/pleroma/web/twitter_api/controller.ex
index 077bfa70d..6db3d6067 100644
--- a/lib/pleroma/web/twitter_api/controller.ex
+++ b/lib/pleroma/web/twitter_api/controller.ex
@@ -1,29 +1,18 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.Controller do
use Pleroma.Web, :controller
- alias Pleroma.Notification
alias Pleroma.User
alias Pleroma.Web.OAuth.Token
- alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.TwitterAPI.TokenView
require Logger
- plug(
- OAuthScopesPlug,
- %{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
- )
-
- plug(
- :skip_plug,
- [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirm_email
- )
-
+ plug(:skip_auth when action == :confirm_email)
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
action_fallback(:errors)
@@ -67,31 +56,4 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|> put_resp_content_type("application/json")
|> send_resp(status, json)
end
-
- def mark_notifications_as_read(
- %{assigns: %{user: user}} = conn,
- %{"latest_id" => latest_id} = params
- ) do
- Notification.set_read_up_to(user, latest_id)
-
- notifications = Notification.for_user(user, params)
-
- conn
- # XXX: This is a hack because pleroma-fe still uses that API.
- |> put_view(Pleroma.Web.MastodonAPI.NotificationView)
- |> render("index.json", %{notifications: notifications, for: user})
- end
-
- def mark_notifications_as_read(%{assigns: %{user: _user}} = conn, _) do
- bad_request_reply(conn, "You need to specify latest_id")
- end
-
- defp bad_request_reply(conn, error_message) do
- json = error_json(conn, error_message)
- json_reply(conn, 400, json)
- end
-
- defp error_json(conn, error_message) do
- %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!()
- end
end
diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
index bc04a4d49..31b7dd728 100644
--- a/lib/pleroma/web/twitter_api/controllers/password_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.PasswordController do
@@ -11,9 +11,23 @@ defmodule Pleroma.Web.TwitterAPI.PasswordController do
require Logger
+ import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+
alias Pleroma.PasswordResetToken
alias Pleroma.Repo
alias Pleroma.User
+ alias Pleroma.Web.TwitterAPI.TwitterAPI
+
+ plug(Pleroma.Web.Plugs.RateLimiter, [name: :request] when action == :request)
+
+ @doc "POST /auth/password"
+ def request(conn, params) do
+ nickname_or_email = params["email"] || params["nickname"]
+
+ TwitterAPI.password_reset(nickname_or_email)
+
+ json_response(conn, :no_content, "")
+ end
def reset(conn, %{"token" => token}) do
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex
index 6ca02fbd7..6229d5d05 100644
--- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
@@ -11,8 +11,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
alias Pleroma.MFA
alias Pleroma.Object.Fetcher
alias Pleroma.User
- alias Pleroma.Web.Auth.Authenticator
alias Pleroma.Web.Auth.TOTPAuthenticator
+ alias Pleroma.Web.Auth.WrapperAuthenticator
alias Pleroma.Web.CommonAPI
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
@@ -38,7 +38,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
defp follow_status(conn, _user, acct) do
with {:ok, object} <- Fetcher.fetch_object_from_id(acct),
%Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object.data["id"]) do
- redirect(conn, to: o_status_path(conn, :notice, activity_id))
+ redirect(conn, to: Routes.o_status_path(conn, :notice, activity_id))
else
error ->
handle_follow_error(conn, error)
@@ -88,7 +88,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
#
def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" => id}}) do
with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
- {_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
+ {_, {:ok, user}, _} <- {:auth, WrapperAuthenticator.get_user(conn), followee},
{_, _, _, false} <- {:mfa_required, followee, user, MFA.require?(user)},
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
redirect(conn, to: "/users/#{followee.id}")
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 940a645bb..d5a24ae6c 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.UtilController do
@@ -7,16 +7,26 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
require Logger
+ alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Emoji
alias Pleroma.Healthcheck
- alias Pleroma.Notification
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.WebFinger
- plug(Pleroma.Web.Plugs.FederatingPlug when action == :remote_subscribe)
+ plug(
+ Pleroma.Web.ApiSpec.CastAndValidate
+ when action != :remote_subscribe and action != :show_subscribe_form
+ )
+
+ plug(
+ Pleroma.Web.Plugs.FederatingPlug
+ when action == :remote_subscribe
+ when action == :show_subscribe_form
+ )
plug(
OAuthScopesPlug,
@@ -26,13 +36,24 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
:change_password,
:delete_account,
:update_notificaton_settings,
- :disable_account
+ :disable_account,
+ :move_account,
+ :add_alias,
+ :delete_alias
]
)
- plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["read:accounts"]}
+ when action in [
+ :list_aliases
+ ]
+ )
- def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation
+
+ def show_subscribe_form(conn, %{"nickname" => nick}) do
with %User{} = user <- User.get_cached_by_nickname(nick),
avatar = User.avatar_url(user) do
conn
@@ -42,11 +63,52 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
render(conn, "subscribe.html", %{
nickname: nick,
avatar: nil,
- error: "Could not find user"
+ error:
+ Pleroma.Web.Gettext.dpgettext(
+ "static_pages",
+ "remote follow error message - user not found",
+ "Could not find user"
+ )
+ })
+ end
+ end
+
+ def show_subscribe_form(conn, %{"status_id" => id}) do
+ with %Activity{} = activity <- Activity.get_by_id(id),
+ {:ok, ap_id} <- get_ap_id(activity),
+ %User{} = user <- User.get_cached_by_ap_id(activity.actor),
+ avatar = User.avatar_url(user) do
+ conn
+ |> render("status_interact.html", %{
+ status_link: ap_id,
+ status_id: id,
+ nickname: user.nickname,
+ avatar: avatar,
+ error: false
+ })
+ else
+ _e ->
+ render(conn, "status_interact.html", %{
+ status_id: id,
+ avatar: nil,
+ error:
+ Pleroma.Web.Gettext.dpgettext(
+ "static_pages",
+ "status interact error message - status not found",
+ "Could not find status"
+ )
})
end
end
+ def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
+ show_subscribe_form(conn, %{"nickname" => nick})
+ end
+
+ def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
+ show_subscribe_form(conn, %{"status_id" => id})
+ end
+
def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
%User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
@@ -57,19 +119,52 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
render(conn, "subscribe.html", %{
nickname: nick,
avatar: nil,
- error: "Something went wrong."
+ error:
+ Pleroma.Web.Gettext.dpgettext(
+ "static_pages",
+ "remote follow error message - unknown error",
+ "Something went wrong."
+ )
})
end
end
- def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
- with {:ok, _} <- Notification.read_one(user, notification_id) do
- json(conn, %{status: "success"})
+ def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
+ with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
+ %Activity{} = activity <- Activity.get_by_id(id),
+ {:ok, ap_id} <- get_ap_id(activity) do
+ conn
+ |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
else
- {:error, message} ->
- conn
- |> put_resp_content_type("application/json")
- |> send_resp(403, Jason.encode!(%{"error" => message}))
+ _e ->
+ render(conn, "status_interact.html", %{
+ status_id: id,
+ avatar: nil,
+ error:
+ Pleroma.Web.Gettext.dpgettext(
+ "static_pages",
+ "status interact error message - unknown error",
+ "Something went wrong."
+ )
+ })
+ end
+ end
+
+ def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do
+ with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
+ conn
+ |> json(%{url: String.replace(template, "{uri}", ap_id)})
+ else
+ _e -> json(conn, %{error: "Couldn't find user"})
+ end
+ end
+
+ defp get_ap_id(activity) do
+ object = Pleroma.Object.normalize(activity, fetch: false)
+
+ case object do
+ %{data: %{"id" => ap_id}} -> {:ok, ap_id}
+ _ -> {:no_ap_id, nil}
end
end
@@ -92,13 +187,13 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
end
- def change_password(%{assigns: %{user: user}} = conn, params) do
- case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
+ def change_password(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
+ case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
{:ok, user} ->
with {:ok, _user} <-
User.reset_password(user, %{
- password: params["new_password"],
- password_confirmation: params["new_password_confirmation"]
+ password: body_params.new_password,
+ password_confirmation: body_params.new_password_confirmation
}) do
json(conn, %{status: "success"})
else
@@ -115,10 +210,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
end
- def change_email(%{assigns: %{user: user}} = conn, params) do
- case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
+ def change_email(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
+ case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
{:ok, user} ->
- with {:ok, _user} <- User.change_email(user, params["email"]) do
+ with {:ok, _user} <- User.change_email(user, body_params.email) do
json(conn, %{status: "success"})
else
{:error, changeset} ->
@@ -134,8 +229,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
end
- def delete_account(%{assigns: %{user: user}} = conn, params) do
- password = params["password"] || ""
+ def delete_account(%{assigns: %{user: user}, body_params: body_params} = conn, params) do
+ # This endpoint can accept a query param or JSON body for backwards-compatibility.
+ # Submitting a JSON body is recommended, so passwords don't end up in server logs.
+ password = body_params[:password] || params[:password] || ""
case CommonAPI.Utils.confirm_current_password(user, password) do
{:ok, user} ->
@@ -148,7 +245,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
def disable_account(%{assigns: %{user: user}} = conn, params) do
- case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
+ case CommonAPI.Utils.confirm_current_password(user, params[:password]) do
{:ok, user} ->
User.set_activation_async(user, false)
json(conn, %{status: "success"})
@@ -158,6 +255,91 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
end
+ def move_account(%{assigns: %{user: user}, body_params: body_params} = conn, %{}) do
+ case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
+ {:ok, user} ->
+ with {:ok, target_user} <- find_or_fetch_user_by_nickname(body_params.target_account),
+ {:ok, _user} <- ActivityPub.move(user, target_user) do
+ json(conn, %{status: "success"})
+ else
+ {:not_found, _} ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Target account not found."})
+
+ {:error, error} ->
+ json(conn, %{error: error})
+ end
+
+ {:error, msg} ->
+ json(conn, %{error: msg})
+ end
+ end
+
+ def add_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) do
+ with {:ok, alias_user} <- find_user_by_nickname(body_params.alias),
+ {:ok, _user} <- user |> User.add_alias(alias_user) do
+ json(conn, %{status: "success"})
+ else
+ {:not_found, _} ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Target account does not exist."})
+
+ {:error, error} ->
+ json(conn, %{error: error})
+ end
+ end
+
+ def delete_alias(%{assigns: %{user: user}, body_params: body_params} = conn, _) do
+ with {:ok, alias_user} <- find_user_by_nickname(body_params.alias),
+ {:ok, _user} <- user |> User.delete_alias(alias_user) do
+ json(conn, %{status: "success"})
+ else
+ {:error, :no_such_alias} ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Account has no such alias."})
+
+ {:error, error} ->
+ json(conn, %{error: error})
+ end
+ end
+
+ def list_aliases(%{assigns: %{user: user}} = conn, %{}) do
+ alias_nicks =
+ user
+ |> User.alias_users()
+ |> Enum.map(&User.full_nickname/1)
+
+ json(conn, %{aliases: alias_nicks})
+ end
+
+ defp find_user_by_nickname(nickname) do
+ user = User.get_cached_by_nickname(nickname)
+
+ if user == nil do
+ {:not_found, nil}
+ else
+ {:ok, user}
+ end
+ end
+
+ defp find_or_fetch_user_by_nickname(nickname) do
+ user = User.get_by_nickname(nickname)
+
+ if user != nil and user.local do
+ {:ok, user}
+ else
+ with {:ok, user} <- User.fetch_by_nickname(nickname) do
+ {:ok, user}
+ else
+ _ ->
+ {:not_found, nil}
+ end
+ end
+ end
+
def captcha(conn, _params) do
json(conn, Pleroma.Captcha.new())
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 76ca82d20..ef2eb75f4 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
@@ -12,6 +12,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
alias Pleroma.UserInviteToken
def register_user(params, opts \\ []) do
+ fallback_language = Gettext.get_locale()
+
params =
params
|> Map.take([:email, :token, :password])
@@ -20,6 +22,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|> Map.put(:name, Map.get(params, :fullname, params[:username]))
|> Map.put(:password_confirmation, params[:password])
|> Map.put(:registration_reason, params[:reason])
+ |> Map.put(:birthday, params[:birthday])
+ |> Map.put(
+ :language,
+ Pleroma.Web.Gettext.normalize_locale(params[:language]) || fallback_language
+ )
if Pleroma.Config.get([:instance, :registrations_open]) do
create_user(params, opts)
diff --git a/lib/pleroma/web/twitter_api/views/password_view.ex b/lib/pleroma/web/twitter_api/views/password_view.ex
index a9bb95a2c..55790941f 100644
--- a/lib/pleroma/web/twitter_api/views/password_view.ex
+++ b/lib/pleroma/web/twitter_api/views/password_view.ex
@@ -1,8 +1,9 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.PasswordView do
use Pleroma.Web, :view
import Phoenix.HTML.Form
+ alias Pleroma.Web.Gettext
end
diff --git a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex
index ac3f15eec..8902261b0 100644
--- a/lib/pleroma/web/twitter_api/views/remote_follow_view.ex
+++ b/lib/pleroma/web/twitter_api/views/remote_follow_view.ex
@@ -1,10 +1,15 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.RemoteFollowView do
use Pleroma.Web, :view
import Phoenix.HTML.Form
+ alias Pleroma.Web.Gettext
- defdelegate avatar_url(user), to: Pleroma.User
+ def avatar_url(user) do
+ user
+ |> Pleroma.User.avatar_url()
+ |> Pleroma.Web.MediaProxy.url()
+ end
end
diff --git a/lib/pleroma/web/twitter_api/views/token_view.ex b/lib/pleroma/web/twitter_api/views/token_view.ex
index 99884e714..2e492c13f 100644
--- a/lib/pleroma/web/twitter_api/views/token_view.ex
+++ b/lib/pleroma/web/twitter_api/views/token_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.TokenView do
diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex
index 9b13c09b3..31b7c0c0c 100644
--- a/lib/pleroma/web/twitter_api/views/util_view.ex
+++ b/lib/pleroma/web/twitter_api/views/util_view.ex
@@ -1,19 +1,22 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.UtilView do
use Pleroma.Web, :view
+ import Phoenix.HTML
import Phoenix.HTML.Form
+ import Phoenix.HTML.Link
alias Pleroma.Config
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.Gettext
def status_net_config(instance) do
"""
<config>
<site>
<name>#{Keyword.get(instance, :name)}</name>
- <site>#{Web.base_url()}</site>
+ <site>#{Endpoint.url()}</site>
<textlimit>#{Keyword.get(instance, :limit)}</textlimit>
<closed>#{!Keyword.get(instance, :registrations_open)}</closed>
</site>
diff --git a/lib/pleroma/web/uploader_controller.ex b/lib/pleroma/web/uploader_controller.ex
index 0d42c7ec3..d5c804932 100644
--- a/lib/pleroma/web/uploader_controller.ex
+++ b/lib/pleroma/web/uploader_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.UploaderController do
diff --git a/lib/pleroma/web/utils/guards.ex b/lib/pleroma/web/utils/guards.ex
new file mode 100644
index 000000000..8a61421fa
--- /dev/null
+++ b/lib/pleroma/web/utils/guards.ex
@@ -0,0 +1,13 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Utils.Guards do
+ @moduledoc """
+ Project-wide custom guards.
+ See: https://hexdocs.pm/elixir/master/patterns-and-guards.html#custom-patterns-and-guards-expressions
+ """
+
+ @doc "Checks for non-empty string"
+ defguard not_empty_string(string) when is_binary(string) and string != ""
+end
diff --git a/lib/pleroma/web/utils/params.ex b/lib/pleroma/web/utils/params.ex
new file mode 100644
index 000000000..636e3de33
--- /dev/null
+++ b/lib/pleroma/web/utils/params.ex
@@ -0,0 +1,16 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Utils.Params do
+ # As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
+ @falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
+
+ defp explicitly_falsy_param?(value), do: value in @falsy_param_values
+
+ # Note: `nil` and `""` are considered falsy values in Pleroma
+ defp falsy_param?(value),
+ do: explicitly_falsy_param?(value) or value in [nil, ""]
+
+ def truthy_param?(value), do: not falsy_param?(value)
+end
diff --git a/lib/pleroma/web/views/email_view.ex b/lib/pleroma/web/views/email_view.ex
index f7659b994..9ab708212 100644
--- a/lib/pleroma/web/views/email_view.ex
+++ b/lib/pleroma/web/views/email_view.ex
@@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.EmailView do
use Pleroma.Web, :view
import Phoenix.HTML
import Phoenix.HTML.Link
+ alias Pleroma.Web.Gettext
def avatar_url(user) do
Pleroma.User.avatar_url(user)
diff --git a/lib/pleroma/web/views/embed_view.ex b/lib/pleroma/web/views/embed_view.ex
index 81e196730..1bfd8c523 100644
--- a/lib/pleroma/web/views/embed_view.ex
+++ b/lib/pleroma/web/views/embed_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.EmbedView do
diff --git a/lib/pleroma/web/views/error_helpers.ex b/lib/pleroma/web/views/error_helpers.ex
index d282c04b7..fd85f7c15 100644
--- a/lib/pleroma/web/views/error_helpers.ex
+++ b/lib/pleroma/web/views/error_helpers.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ErrorHelpers do
diff --git a/lib/pleroma/web/views/error_view.ex b/lib/pleroma/web/views/error_view.ex
index c9715dc4b..9ab16323d 100644
--- a/lib/pleroma/web/views/error_view.ex
+++ b/lib/pleroma/web/views/error_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ErrorView do
diff --git a/lib/pleroma/web/views/layout_view.ex b/lib/pleroma/web/views/layout_view.ex
index c2da10f04..3161bb1ae 100644
--- a/lib/pleroma/web/views/layout_view.ex
+++ b/lib/pleroma/web/views/layout_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.LayoutView do
diff --git a/lib/pleroma/web/views/mailer/subscription_view.ex b/lib/pleroma/web/views/mailer/subscription_view.ex
index 1dc80987b..037fb6578 100644
--- a/lib/pleroma/web/views/mailer/subscription_view.ex
+++ b/lib/pleroma/web/views/mailer/subscription_view.ex
@@ -1,7 +1,8 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Mailer.SubscriptionView do
use Pleroma.Web, :view
+ alias Pleroma.Web.Gettext
end
diff --git a/lib/pleroma/web/views/manifest_view.ex b/lib/pleroma/web/views/manifest_view.ex
new file mode 100644
index 000000000..2ae82191e
--- /dev/null
+++ b/lib/pleroma/web/views/manifest_view.ex
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ManifestView do
+ use Pleroma.Web, :view
+ alias Pleroma.Config
+ alias Pleroma.Web.Endpoint
+
+ 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: Endpoint.url(),
+ start_url: "/",
+ categories: [
+ "social"
+ ],
+ serviceworker: %{
+ src: "/sw.js"
+ }
+ }
+ end
+end
diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex
deleted file mode 100644
index b9055cb7f..000000000
--- a/lib/pleroma/web/views/masto_fe_view.ex
+++ /dev/null
@@ -1,91 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MastoFEView do
- use Pleroma.Web, :view
- alias Pleroma.Config
- alias Pleroma.User
- alias Pleroma.Web.MastodonAPI.AccountView
- alias Pleroma.Web.MastodonAPI.CustomEmojiView
-
- def initial_state(token, user, custom_emojis) do
- limit = Config.get([:instance, :limit])
-
- %{
- meta: %{
- streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(),
- access_token: token,
- locale: "en",
- domain: Pleroma.Web.Endpoint.host(),
- admin: "1",
- me: "#{user.id}",
- unfollow_modal: false,
- boost_modal: false,
- delete_modal: true,
- auto_play_gif: false,
- display_sensitive_media: false,
- reduce_motion: false,
- max_toot_chars: limit,
- mascot: User.get_mascot(user)["url"]
- },
- poll_limits: Config.get([:instance, :poll_limits]),
- rights: %{
- delete_others_notice: present?(user.is_moderator),
- admin: present?(user.is_admin)
- },
- compose: %{
- me: "#{user.id}",
- default_privacy: user.default_scope,
- default_sensitive: false,
- allow_content_types: Config.get([:instance, :allowed_post_formats])
- },
- media_attachments: %{
- accept_content_types: [
- ".jpg",
- ".jpeg",
- ".png",
- ".gif",
- ".webm",
- ".mp4",
- ".m4v",
- "image\/jpeg",
- "image\/png",
- "image\/gif",
- "video\/webm",
- "video\/mp4"
- ]
- },
- settings: user.mastofe_settings || %{},
- push_subscription: nil,
- accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
- custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),
- char_limit: limit
- }
- |> Jason.encode!()
- |> Phoenix.HTML.raw()
- end
-
- 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
diff --git a/lib/pleroma/web/views/streamer_view.ex b/lib/pleroma/web/views/streamer_view.ex
index 7706035e9..6a55242b0 100644
--- a/lib/pleroma/web/views/streamer_view.ex
+++ b/lib/pleroma/web/views/streamer_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.StreamerView do
@@ -25,6 +25,20 @@ defmodule Pleroma.Web.StreamerView do
|> Jason.encode!()
end
+ def render("status_update.json", %Activity{} = activity, %User{} = user) do
+ %{
+ event: "status.update",
+ payload:
+ Pleroma.Web.MastodonAPI.StatusView.render(
+ "show.json",
+ activity: activity,
+ for: user
+ )
+ |> Jason.encode!()
+ }
+ |> Jason.encode!()
+ end
+
def render("notification.json", %Notification{} = notify, %User{} = user) do
%{
event: "notification",
@@ -51,6 +65,19 @@ defmodule Pleroma.Web.StreamerView do
|> Jason.encode!()
end
+ def render("status_update.json", %Activity{} = activity) do
+ %{
+ event: "status.update",
+ payload:
+ Pleroma.Web.MastodonAPI.StatusView.render(
+ "show.json",
+ activity: activity
+ )
+ |> Jason.encode!()
+ }
+ |> Jason.encode!()
+ end
+
def render("chat_update.json", %{chat_message_reference: cm_ref}) do
# Explicitly giving the cmr for the object here, so we don't accidentally
# send a later 'last_message' that was inserted between inserting this and
diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex
index 21b10e654..f95dc2458 100644
--- a/lib/pleroma/web/web_finger.ex
+++ b/lib/pleroma/web/web_finger.ex
@@ -1,11 +1,11 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebFinger do
alias Pleroma.HTTP
alias Pleroma.User
- alias Pleroma.Web
+ alias Pleroma.Web.Endpoint
alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.XML
alias Pleroma.XmlBuilder
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.WebFinger do
require Logger
def host_meta do
- base_url = Web.base_url()
+ base_url = Endpoint.url()
{
:XRD,
@@ -32,7 +32,13 @@ defmodule Pleroma.Web.WebFinger do
def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do
host = Pleroma.Web.Endpoint.host()
- regex = ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@#{host}/
+
+ regex =
+ if webfinger_domain = Pleroma.Config.get([__MODULE__, :domain]) do
+ ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@(#{host}|#{webfinger_domain})/
+ else
+ ~r/(acct:)?(?<username>[a-z0-9A-Z_\.-]+)@#{host}/
+ end
with %{"username" => username} <- Regex.named_captures(regex, resource),
%User{} = user <- User.get_cached_by_nickname(username) do
@@ -63,18 +69,14 @@ defmodule Pleroma.Web.WebFinger do
end
def represent_user(user, "JSON") do
- {:ok, user} = User.ensure_keys_present(user)
-
%{
- "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
+ "subject" => "acct:#{user.nickname}@#{domain()}",
"aliases" => gather_aliases(user),
"links" => gather_links(user)
}
end
def represent_user(user, "XML") do
- {:ok, user} = User.ensure_keys_present(user)
-
aliases =
user
|> gather_aliases()
@@ -88,12 +90,16 @@ defmodule Pleroma.Web.WebFinger do
:XRD,
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
[
- {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}
+ {:Subject, "acct:#{user.nickname}@#{domain()}"}
] ++ aliases ++ links
}
|> XmlBuilder.to_doc()
end
+ defp domain do
+ Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host()
+ end
+
defp webfinger_from_xml(body) do
with {:ok, doc} <- XML.parse_document(body) do
subject = XML.string_from_xpath("//Subject", doc)
@@ -150,17 +156,15 @@ defmodule Pleroma.Web.WebFinger do
end
def find_lrdd_template(domain) do
- with {:ok, %{status: status, body: body}} when status in 200..299 <-
- HTTP.get("http://#{domain}/.well-known/host-meta") do
+ # WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1
+ meta_url = "https://#{domain}/.well-known/host-meta"
+
+ with {:ok, %{status: status, body: body}} when status in 200..299 <- HTTP.get(meta_url) do
get_template_from_xml(body)
else
- _ ->
- with {:ok, %{body: body, status: status}} when status in 200..299 <-
- HTTP.get("https://#{domain}/.well-known/host-meta") do
- get_template_from_xml(body)
- else
- e -> {:error, "Can't find LRDD template: #{inspect(e)}"}
- end
+ error ->
+ Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
+ {:error, :lrdd_not_found}
end
end
@@ -174,7 +178,7 @@ defmodule Pleroma.Web.WebFinger do
end
end
- defp get_address_from_domain(_, _), do: nil
+ defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain}
@spec finger(String.t()) :: {:ok, map()} | {:error, any()}
def finger(account) do
@@ -191,13 +195,11 @@ defmodule Pleroma.Web.WebFinger do
encoded_account = URI.encode("acct:#{account}")
with address when is_binary(address) <- get_address_from_domain(domain, encoded_account),
- response <-
+ {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
HTTP.get(
address,
[{"accept", "application/xrd+xml,application/jrd+json"}]
- ),
- {:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
- response do
+ ) do
case List.keyfind(headers, "content-type", 0) do
{_, content_type} ->
case Plug.Conn.Utils.media_type(content_type) do
@@ -215,10 +217,9 @@ defmodule Pleroma.Web.WebFinger do
{:error, {:content_type, nil}}
end
else
- e ->
- Logger.debug(fn -> "Couldn't finger #{account}" end)
- Logger.debug(fn -> inspect(e) end)
- {:error, e}
+ error ->
+ Logger.debug("Couldn't finger #{account}: #{inspect(error)}")
+ error
end
end
end
diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex
index 7944c50ad..9e5efb77f 100644
--- a/lib/pleroma/web/web_finger/web_finger_controller.ex
+++ b/lib/pleroma/web/web_finger/web_finger_controller.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebFinger.WebFingerController do
diff --git a/lib/pleroma/web/xml.ex b/lib/pleroma/web/xml.ex
index 0ab6e9d32..b699446b0 100644
--- a/lib/pleroma/web/xml.ex
+++ b/lib/pleroma/web/xml.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.XML do
diff --git a/lib/pleroma/workers/attachments_cleanup_worker.ex b/lib/pleroma/workers/attachments_cleanup_worker.ex
index f5090dae7..4c1764053 100644
--- a/lib/pleroma/workers/attachments_cleanup_worker.ex
+++ b/lib/pleroma/workers/attachments_cleanup_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.AttachmentsCleanupWorker do
@@ -31,6 +31,9 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do
def perform(%Job{args: %{"op" => "cleanup_attachments", "object" => _object}}), do: {:ok, :skip}
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(900)
+
defp do_clean({object_ids, attachment_urls}) do
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex
index 1e28384cb..794417612 100644
--- a/lib/pleroma/workers/background_worker.ex
+++ b/lib/pleroma/workers/background_worker.ex
@@ -1,8 +1,9 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.BackgroundWorker do
+ alias Pleroma.Instances.Instance
alias Pleroma.User
use Pleroma.Workers.WorkerHelper, queue: "background"
@@ -38,4 +39,11 @@ defmodule Pleroma.Workers.BackgroundWorker do
Pleroma.FollowingRelationship.move_following(origin, target)
end
+
+ def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do
+ Instance.perform(:delete_instance, host)
+ end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(900)
end
diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex
index 9b763b04b..12ee70f00 100644
--- a/lib/pleroma/workers/backup_worker.ex
+++ b/lib/pleroma/workers/backup_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.BackupWorker do
@@ -30,6 +30,7 @@ defmodule Pleroma.Workers.BackupWorker do
|> Oban.insert()
end
+ @impl Oban.Worker
def perform(%Job{
args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id}
}) do
@@ -37,10 +38,7 @@ defmodule Pleroma.Workers.BackupWorker do
backup_id |> Backup.get() |> Backup.process(),
{:ok, _job} <- schedule_deletion(backup),
:ok <- Backup.remove_outdated(backup),
- {:ok, _} <-
- backup
- |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
- |> Pleroma.Emails.Mailer.deliver() do
+ :ok <- maybe_deliver_email(backup, admin_user_id) do
{:ok, backup}
end
end
@@ -51,4 +49,26 @@ defmodule Pleroma.Workers.BackupWorker do
nil -> :ok
end
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(900)
+
+ defp has_email?(user) do
+ not is_nil(user.email) and user.email != ""
+ end
+
+ defp maybe_deliver_email(backup, admin_user_id) do
+ has_mailer = Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled])
+ backup = backup |> Pleroma.Repo.preload(:user)
+
+ if has_email?(backup.user) and has_mailer do
+ backup
+ |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
+ |> Pleroma.Emails.Mailer.deliver()
+
+ :ok
+ else
+ :ok
+ end
+ end
end
diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex
index 83dc75d60..1540c1605 100644
--- a/lib/pleroma/workers/cron/digest_emails_worker.ex
+++ b/lib/pleroma/workers/cron/digest_emails_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.DigestEmailsWorker do
diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex
index 9dfd92228..267fe2837 100644
--- a/lib/pleroma/workers/cron/new_users_digest_worker.ex
+++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
diff --git a/lib/pleroma/workers/mailer_worker.ex b/lib/pleroma/workers/mailer_worker.ex
index 592230e7a..940716558 100644
--- a/lib/pleroma/workers/mailer_worker.ex
+++ b/lib/pleroma/workers/mailer_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.MailerWorker do
@@ -12,4 +12,7 @@ defmodule Pleroma.Workers.MailerWorker do
|> :erlang.binary_to_term()
|> Pleroma.Emails.Mailer.deliver(config)
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
end
diff --git a/lib/pleroma/workers/mute_expire_worker.ex b/lib/pleroma/workers/mute_expire_worker.ex
index 8da903e76..8ce458d48 100644
--- a/lib/pleroma/workers/mute_expire_worker.ex
+++ b/lib/pleroma/workers/mute_expire_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.MuteExpireWorker do
@@ -17,4 +17,7 @@ defmodule Pleroma.Workers.MuteExpireWorker do
Pleroma.Web.CommonAPI.remove_mute(user_id, activity_id)
:ok
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
end
diff --git a/lib/pleroma/workers/poll_worker.ex b/lib/pleroma/workers/poll_worker.ex
new file mode 100644
index 000000000..022d026f8
--- /dev/null
+++ b/lib/pleroma/workers/poll_worker.ex
@@ -0,0 +1,48 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.PollWorker do
+ @moduledoc """
+ Generates notifications when a poll ends.
+ """
+ use Pleroma.Workers.WorkerHelper, queue: "poll_notifications"
+
+ alias Pleroma.Activity
+ alias Pleroma.Notification
+ alias Pleroma.Object
+
+ @impl Oban.Worker
+ def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
+ with %Activity{} = activity <- find_poll_activity(activity_id) do
+ Notification.create_poll_notifications(activity)
+ end
+ end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
+
+ defp find_poll_activity(activity_id) do
+ with nil <- Activity.get_by_id(activity_id) do
+ {:error, :poll_activity_not_found}
+ end
+ end
+
+ def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
+ with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
+ Object.normalize(activity),
+ {:ok, end_time} <- NaiveDateTime.from_iso8601(closed),
+ :gt <- NaiveDateTime.compare(end_time, NaiveDateTime.utc_now()) do
+ %{
+ op: "poll_end",
+ activity_id: activity_id
+ }
+ |> new(scheduled_at: end_time)
+ |> Oban.insert()
+ else
+ _ -> {:error, activity}
+ end
+ end
+
+ def schedule_poll_end(activity), do: {:error, activity}
+end
diff --git a/lib/pleroma/workers/publisher_worker.ex b/lib/pleroma/workers/publisher_worker.ex
index 6209715b3..598ae3779 100644
--- a/lib/pleroma/workers/publisher_worker.ex
+++ b/lib/pleroma/workers/publisher_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.PublisherWorker do
@@ -22,4 +22,7 @@ defmodule Pleroma.Workers.PublisherWorker do
params = Map.new(params, fn {k, v} -> {String.to_atom(k), v} end)
Federator.perform(:publish_one, String.to_atom(module_name), params)
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(10)
end
diff --git a/lib/pleroma/workers/purge_expired_activity.ex b/lib/pleroma/workers/purge_expired_activity.ex
index 027171c1e..e554684fe 100644
--- a/lib/pleroma/workers/purge_expired_activity.ex
+++ b/lib/pleroma/workers/purge_expired_activity.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.PurgeExpiredActivity do
@@ -35,6 +35,9 @@ defmodule Pleroma.Workers.PurgeExpiredActivity do
end
end
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
+
defp enabled? do
with false <- Pleroma.Config.get([__MODULE__, :enabled], false) do
{:error, :expired_activities_disabled}
diff --git a/lib/pleroma/workers/purge_expired_filter.ex b/lib/pleroma/workers/purge_expired_filter.ex
index 4740d52e9..9114aeb7f 100644
--- a/lib/pleroma/workers/purge_expired_filter.ex
+++ b/lib/pleroma/workers/purge_expired_filter.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.PurgeExpiredFilter do
@@ -31,6 +31,9 @@ defmodule Pleroma.Workers.PurgeExpiredFilter do
|> Repo.delete()
end
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
+
@spec get_expiration(pos_integer()) :: Job.t() | nil
def get_expiration(id) do
from(j in Job,
diff --git a/lib/pleroma/workers/purge_expired_token.ex b/lib/pleroma/workers/purge_expired_token.ex
index cfdf5c6dc..2ccd9e80b 100644
--- a/lib/pleroma/workers/purge_expired_token.ex
+++ b/lib/pleroma/workers/purge_expired_token.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.PurgeExpiredToken do
@@ -26,4 +26,7 @@ defmodule Pleroma.Workers.PurgeExpiredToken do
|> Pleroma.Repo.get(id)
|> Pleroma.Repo.delete()
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
end
diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex
index 69125dcd0..cf1bb62b4 100644
--- a/lib/pleroma/workers/receiver_worker.ex
+++ b/lib/pleroma/workers/receiver_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ReceiverWorker do
@@ -9,6 +9,18 @@ defmodule Pleroma.Workers.ReceiverWorker do
@impl Oban.Worker
def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do
- Federator.perform(:incoming_ap_doc, params)
+ with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
+ {:ok, res}
+ else
+ {:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
+ {:error, :already_present} -> {:cancel, :already_present}
+ {:error, {:validate_object, reason}} -> {:cancel, reason}
+ {:error, {:error, {:validate, reason}}} -> {:cancel, reason}
+ {:error, {:reject, reason}} -> {:cancel, reason}
+ e -> e
+ end
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
end
diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex
index ad4d785a1..d2a77aa17 100644
--- a/lib/pleroma/workers/remote_fetcher_worker.ex
+++ b/lib/pleroma/workers/remote_fetcher_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.RemoteFetcherWorker do
@@ -11,4 +11,7 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do
def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do
{:ok, _object} = Fetcher.fetch_object_from_id(id, depth: args["depth"])
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(10)
end
diff --git a/lib/pleroma/workers/scheduled_activity_worker.ex b/lib/pleroma/workers/scheduled_activity_worker.ex
index a4ab9928d..4df84d00f 100644
--- a/lib/pleroma/workers/scheduled_activity_worker.ex
+++ b/lib/pleroma/workers/scheduled_activity_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ScheduledActivityWorker do
@@ -37,6 +37,9 @@ defmodule Pleroma.Workers.ScheduledActivityWorker do
end
end
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
+
defp find_scheduled_activity(id) do
with nil <- Repo.get(ScheduledActivity, id) do
{:error, :scheduled_activity_not_found}
diff --git a/lib/pleroma/workers/transmogrifier_worker.ex b/lib/pleroma/workers/transmogrifier_worker.ex
deleted file mode 100644
index b39c1ea62..000000000
--- a/lib/pleroma/workers/transmogrifier_worker.ex
+++ /dev/null
@@ -1,15 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.TransmogrifierWorker do
- alias Pleroma.User
-
- use Pleroma.Workers.WorkerHelper, queue: "transmogrifier"
-
- @impl Oban.Worker
- def perform(%Job{args: %{"op" => "user_upgrade", "user_id" => user_id}}) do
- user = User.get_cached_by_id(user_id)
- Pleroma.Web.ActivityPub.Transmogrifier.perform(:user_upgrade, user)
- end
-end
diff --git a/lib/pleroma/workers/web_pusher_worker.ex b/lib/pleroma/workers/web_pusher_worker.ex
index 8fc2aff26..67e84b0c9 100644
--- a/lib/pleroma/workers/web_pusher_worker.ex
+++ b/lib/pleroma/workers/web_pusher_worker.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.WebPusherWorker do
@@ -17,4 +17,7 @@ defmodule Pleroma.Workers.WebPusherWorker do
Pleroma.Web.Push.Impl.perform(notification)
end
+
+ @impl Oban.Worker
+ def timeout(_job), do: :timer.seconds(5)
end
diff --git a/lib/pleroma/workers/worker_helper.ex b/lib/pleroma/workers/worker_helper.ex
index 4befbeb3b..1d20cbd89 100644
--- a/lib/pleroma/workers/worker_helper.ex
+++ b/lib/pleroma/workers/worker_helper.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.WorkerHelper do
diff --git a/lib/pleroma/xml_builder.ex b/lib/pleroma/xml_builder.ex
index 922d3f6ee..cd74cfbec 100644
--- a/lib/pleroma/xml_builder.ex
+++ b/lib/pleroma/xml_builder.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.XmlBuilder do