Merge remote-tracking branch 'origin/develop' into live-dashboard
This commit is contained in:
commit
e1b89fe3aa
3842 changed files with 4336 additions and 12293 deletions
|
@ -134,6 +134,7 @@ unit-testing-rum:
|
|||
- mix test --preload-modules
|
||||
|
||||
lint:
|
||||
image: elixir:1.12
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
|
@ -243,7 +244,7 @@ stop_review_app:
|
|||
|
||||
amd64:
|
||||
stage: release
|
||||
image: elixir:1.10.3
|
||||
image: elixir:1.10.4
|
||||
only: &release-only
|
||||
- stable@pleroma/pleroma
|
||||
- develop@pleroma/pleroma
|
||||
|
@ -281,7 +282,7 @@ amd64-musl:
|
|||
stage: release
|
||||
artifacts: *release-artifacts
|
||||
only: *release-only
|
||||
image: elixir:1.10.3-alpine
|
||||
image: elixir:1.10.4-alpine
|
||||
cache: *release-cache
|
||||
variables: *release-variables
|
||||
before_script: &before-release-musl
|
||||
|
@ -297,7 +298,7 @@ arm:
|
|||
only: *release-only
|
||||
tags:
|
||||
- arm32-specified
|
||||
image: arm32v7/elixir:1.10.3
|
||||
image: arm32v7/elixir:1.10.4
|
||||
cache: *release-cache
|
||||
variables: *release-variables
|
||||
before_script: *before-release
|
||||
|
@ -309,7 +310,7 @@ arm-musl:
|
|||
only: *release-only
|
||||
tags:
|
||||
- arm32-specified
|
||||
image: arm32v7/elixir:1.10.3-alpine
|
||||
image: arm32v7/elixir:1.10.4-alpine
|
||||
cache: *release-cache
|
||||
variables: *release-variables
|
||||
before_script: *before-release-musl
|
||||
|
@ -321,7 +322,7 @@ arm64:
|
|||
only: *release-only
|
||||
tags:
|
||||
- arm
|
||||
image: arm64v8/elixir:1.10.3
|
||||
image: arm64v8/elixir:1.10.4
|
||||
cache: *release-cache
|
||||
variables: *release-variables
|
||||
before_script: *before-release
|
||||
|
@ -333,7 +334,7 @@ arm64-musl:
|
|||
only: *release-only
|
||||
tags:
|
||||
- arm
|
||||
image: arm64v8/elixir:1.10.3-alpine
|
||||
image: arm64v8/elixir:1.10.4-alpine
|
||||
cache: *release-cache
|
||||
variables: *release-variables
|
||||
before_script: *before-release-musl
|
||||
|
@ -351,8 +352,8 @@ docker:
|
|||
IMAGE_TAG_SLUG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
|
||||
IMAGE_TAG_LATEST: $CI_REGISTRY_IMAGE:latest
|
||||
IMAGE_TAG_LATEST_STABLE: $CI_REGISTRY_IMAGE:latest-stable
|
||||
DOCKER_BUILDX_URL: https://github.com/docker/buildx/releases/download/v0.4.1/buildx-v0.4.1.linux-amd64
|
||||
DOCKER_BUILDX_HASH: 71a7d01439aa8c165a25b59c44d3f016fddbd98b
|
||||
DOCKER_BUILDX_URL: https://github.com/docker/buildx/releases/download/v0.6.3/buildx-v0.6.3.linux-amd64
|
||||
DOCKER_BUILDX_HASH: 980e6b9655f971991fbbb5fd6cd19f1672386195
|
||||
before_script: &before-docker
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- docker pull $IMAGE_TAG_SLUG || true
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -6,11 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Removed
|
||||
|
||||
- MastoFE
|
||||
|
||||
### Changed
|
||||
- Allow users to remove their emails if instance does not need email to register
|
||||
|
||||
### Added
|
||||
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
|
||||
|
||||
### Fixed
|
||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -39,6 +46,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
### Changed
|
||||
|
||||
- **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
|
||||
- **Breaking** Entries for simple_policy, transparency_exclusions and quarantined_instances now list both the instance and a reason.
|
||||
- Support for Erlang/OTP 24
|
||||
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
|
||||
- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
|
||||
|
@ -52,8 +60,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
|
||||
- Return OAuth token `id` (primary key) in POST `/oauth/token`.
|
||||
- AdminAPI: return `created_at` date with users.
|
||||
- AdminAPI: add DELETE `/api/v1/pleroma/admin/instances/:instance` to delete all content from a remote instance.
|
||||
- `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time.
|
||||
- Attachment dimensions and blurhashes are federated when available.
|
||||
- Mastodon API: support `poll` notification.
|
||||
- Pinned posts federation
|
||||
|
||||
### Fixed
|
||||
|
@ -61,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Checking activated Upload Filters for required commands.
|
||||
- Remote users can no longer reappear after being deleted.
|
||||
- Deactivated users may now be deleted.
|
||||
- Deleting an activity with a lot of likes/boosts no longer causes a database timeout.
|
||||
- Mix task `pleroma.database prune_objects`
|
||||
- Fixed rendering of JSON errors on ActivityPub endpoints.
|
||||
- Linkify: Parsing crash with URLs ending in unbalanced closed paren, no path separator, and no query parameters
|
||||
|
@ -125,6 +136,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Support pagination of blocks and mutes.
|
||||
- Account backup.
|
||||
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
|
||||
- `[:activitypub, :blockers_visible]` config to control visibility of blockers.
|
||||
- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`.
|
||||
- The site title is now injected as a `title` tag like preloads or metadata.
|
||||
- Password reset tokens now are not accepted after a certain age.
|
||||
|
|
|
@ -30,7 +30,7 @@ If your platform is not supported, or you just want to be able to edit the sourc
|
|||
- [OpenBSD (fi)](https://docs-develop.pleroma.social/backend/installation/openbsd_fi/)
|
||||
|
||||
### OS/Distro packages
|
||||
Currently Pleroma is not packaged by any OS/Distros, but if you want to package it for one, we can guide you through the process on our [community channels](#community-channels). If you want to change default options in your Pleroma package, please **discuss it with us first**.
|
||||
Currently Pleroma is packaged for [YunoHost](https://yunohost.org). If you want to package Pleroma for any OS/Distros, we can guide you through the process on our [community channels](#community-channels). If you want to change default options in your Pleroma package, please **discuss it with us first**.
|
||||
|
||||
### Docker
|
||||
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
|
||||
|
|
|
@ -394,7 +394,7 @@ defp get_actor(group, users), do: Enum.random(users[group])
|
|||
|
||||
defp other_data(actor, content) do
|
||||
%{host: host} = URI.parse(actor.ap_id)
|
||||
datetime = DateTime.utc_now()
|
||||
datetime = DateTime.utc_now() |> to_string()
|
||||
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
||||
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
||||
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
||||
|
|
|
@ -99,15 +99,16 @@ defp hashtag_fetching(params, user, local_only) do
|
|||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
_activities =
|
||||
params
|
||||
|> Map.put(:type, "Create")
|
||||
|> Map.put(:local_only, local_only)
|
||||
|> Map.put(:blocking_user, user)
|
||||
|> Map.put(:muting_user, user)
|
||||
|> Map.put(:user, user)
|
||||
|> Map.put(:tag, tags)
|
||||
|> Map.put(:tag_all, tag_all)
|
||||
|> Map.put(:tag_reject, tag_reject)
|
||||
%{
|
||||
type: "Create",
|
||||
local_only: local_only,
|
||||
blocking_user: user,
|
||||
muting_user: user,
|
||||
user: user,
|
||||
tag: tags,
|
||||
tag_all: tag_all,
|
||||
tag_reject: tag_reject,
|
||||
}
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,14 +17,14 @@ def run(_args) do
|
|||
# Let the user make 100 posts
|
||||
|
||||
1..100
|
||||
|> Enum.each(fn i -> CommonAPI.post(user, %{"status" => to_string(i)}) end)
|
||||
|> Enum.each(fn i -> CommonAPI.post(user, %{status: to_string(i)}) end)
|
||||
|
||||
# Let 10 random users post
|
||||
posts =
|
||||
users
|
||||
|> Enum.take_random(10)
|
||||
|> Enum.map(fn {:ok, random_user} ->
|
||||
{:ok, activity} = CommonAPI.post(random_user, %{"status" => "."})
|
||||
{:ok, activity} = CommonAPI.post(random_user, %{status: "."})
|
||||
activity
|
||||
end)
|
||||
|
||||
|
@ -42,7 +42,7 @@ def run(_args) do
|
|||
|> Conn.assign(:user, reading_user)
|
||||
|> Conn.assign(:skip_link_headers, true)
|
||||
|
||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
|
||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||
end
|
||||
},
|
||||
inputs: %{"user" => user, "no user" => nil},
|
||||
|
@ -50,7 +50,7 @@ def run(_args) do
|
|||
)
|
||||
|
||||
users
|
||||
|> Enum.each(fn {:ok, follower, user} -> Pleroma.User.follow(follower, user) end)
|
||||
|> Enum.each(fn {:ok, follower} -> Pleroma.User.follow(follower, user) end)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
|
@ -60,7 +60,7 @@ def run(_args) do
|
|||
|> Conn.assign(:user, reading_user)
|
||||
|> Conn.assign(:skip_link_headers, true)
|
||||
|
||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{"id" => user.id})
|
||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||
end
|
||||
},
|
||||
inputs: %{"user" => user, "no user" => nil},
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
# you can enable the server option below.
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [port: 4001],
|
||||
url: [port: 4001],
|
||||
server: true
|
||||
url: [port: 4001]
|
||||
|
||||
# Disable captha for tests
|
||||
config :pleroma, Pleroma.Captcha,
|
||||
|
@ -44,7 +43,7 @@
|
|||
pool_size: 10
|
||||
|
||||
# Reduce hash rounds for testing
|
||||
config :pbkdf2_elixir, rounds: 1
|
||||
config :pleroma, :password, iterations: 1
|
||||
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
|
||||
|
|
|
@ -322,9 +322,6 @@
|
|||
subjectLineBehavior: "email",
|
||||
theme: "pleroma-dark",
|
||||
webPushNotifications: false
|
||||
},
|
||||
masto_fe: %{
|
||||
showInstanceSpecificPanel: true
|
||||
}
|
||||
|
||||
config :pleroma, :assets,
|
||||
|
@ -353,6 +350,7 @@
|
|||
config :pleroma, :activitypub,
|
||||
unfollow_blocked: true,
|
||||
outgoing_blocks: true,
|
||||
blockers_visible: true,
|
||||
follow_handshake_timeout: 500,
|
||||
note_replies_output_limit: 5,
|
||||
sign_object_fetches: true,
|
||||
|
@ -561,6 +559,7 @@
|
|||
mailer: 10,
|
||||
transmogrifier: 20,
|
||||
scheduled_activities: 10,
|
||||
poll_notifications: 10,
|
||||
background: 5,
|
||||
remote_fetcher: 2,
|
||||
attachments_cleanup: 1,
|
||||
|
|
|
@ -687,12 +687,14 @@
|
|||
},
|
||||
%{
|
||||
key: :quarantined_instances,
|
||||
type: {:list, :string},
|
||||
type: {:list, :tuple},
|
||||
key_placeholder: "instance",
|
||||
value_placeholder: "reason",
|
||||
description:
|
||||
"List of ActivityPub instances where private (DMs, followers-only) activities will not be sent",
|
||||
"List of ActivityPub instances where private (DMs, followers-only) activities will not be sent and the reason for doing so",
|
||||
suggestions: [
|
||||
"quarantined.com",
|
||||
"*.quarantined.com"
|
||||
{"quarantined.com", "Reason"},
|
||||
{"*.quarantined.com", "Reason"}
|
||||
]
|
||||
},
|
||||
%{
|
||||
|
@ -1162,7 +1164,7 @@
|
|||
type: :group,
|
||||
description:
|
||||
"This form can be used to configure a keyword list that keeps the configuration data for any " <>
|
||||
"kind of frontend. By default, settings for pleroma_fe and masto_fe are configured. If you want to " <>
|
||||
"kind of frontend. By default, settings for pleroma_fe are configured. If you want to " <>
|
||||
"add your own configuration your settings all fields must be complete.",
|
||||
children: [
|
||||
%{
|
||||
|
@ -1362,25 +1364,6 @@
|
|||
suggestions: ["pleroma-dark"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :masto_fe,
|
||||
label: "Masto FE",
|
||||
type: :map,
|
||||
description: "Settings for Masto FE",
|
||||
suggestions: [
|
||||
%{
|
||||
showInstanceSpecificPanel: true
|
||||
}
|
||||
],
|
||||
children: [
|
||||
%{
|
||||
key: :showInstanceSpecificPanel,
|
||||
label: "Show instance specific panel",
|
||||
type: :boolean,
|
||||
description: "Whenether to show the instance's specific panel"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1687,6 +1670,11 @@
|
|||
type: :boolean,
|
||||
description: "Whether to federate blocks to other instances"
|
||||
},
|
||||
%{
|
||||
key: :blockers_visible,
|
||||
type: :boolean,
|
||||
description: "Whether a user can see someone who has blocked them"
|
||||
},
|
||||
%{
|
||||
key: :sign_object_fetches,
|
||||
type: :boolean,
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
import_config "dev.secret.exs"
|
||||
else
|
||||
IO.puts(
|
||||
:stderr,
|
||||
"!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
|
||||
)
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
||||
* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
|
||||
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
|
||||
* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
||||
* `quarantined_instances`: ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
||||
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
||||
older software for theses nicknames.
|
||||
|
@ -135,15 +135,16 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
Configuring MRF policies is not enough for them to take effect. You have to enable them by specifying their module in `policies` under [:mrf](#mrf) section.
|
||||
|
||||
#### :mrf_simple
|
||||
* `media_removal`: List of instances to remove media from.
|
||||
* `media_nsfw`: List of instances to put media as NSFW(sensitive) from.
|
||||
* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline.
|
||||
* `reject`: List of instances to reject any activities from.
|
||||
* `accept`: List of instances to accept any activities from.
|
||||
* `followers_only`: List of instances to decrease post visibility to only the followers, including for DM mentions.
|
||||
* `report_removal`: List of instances to reject reports from.
|
||||
* `avatar_removal`: List of instances to strip avatars from.
|
||||
* `banner_removal`: List of instances to strip banners from.
|
||||
* `media_removal`: List of instances to strip media attachments from and the reason for doing so.
|
||||
* `media_nsfw`: List of instances to tag all media as NSFW (sensitive) from and the reason for doing so.
|
||||
* `federated_timeline_removal`: List of instances to remove from the Federated Timeline (aka The Whole Known Network) and the reason for doing so.
|
||||
* `reject`: List of instances to reject activities (except deletes) from and the reason for doing so.
|
||||
* `accept`: List of instances to only accept activities (except deletes) from and the reason for doing so.
|
||||
* `followers_only`: Force posts from the given instances to be visible by followers only and the reason for doing so.
|
||||
* `report_removal`: List of instances to reject reports from and the reason for doing so.
|
||||
* `avatar_removal`: List of instances to strip avatars from and the reason for doing so.
|
||||
* `banner_removal`: List of instances to strip banners from and the reason for doing so.
|
||||
* `reject_deletes`: List of instances to reject deletions from and the reason for doing so.
|
||||
|
||||
#### :mrf_subchain
|
||||
This policy processes messages through an alternate pipeline when a given message matches certain criteria.
|
||||
|
@ -229,6 +230,7 @@ Notes:
|
|||
### :activitypub
|
||||
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
|
||||
* `outgoing_blocks`: Whether to federate blocks to other instances
|
||||
* `blockers_visible`: Whether a user can see the posts of users who blocked them
|
||||
* `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question
|
||||
* `sign_object_fetches`: Sign object fetches with HTTP signatures
|
||||
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
|
||||
|
@ -246,7 +248,7 @@ Notes:
|
|||
|
||||
### :frontend_configurations
|
||||
|
||||
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options).
|
||||
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options).
|
||||
|
||||
Frontends can access these settings at `/api/v1/pleroma/frontend_configurations`
|
||||
|
||||
|
@ -257,10 +259,7 @@ config :pleroma, :frontend_configurations,
|
|||
pleroma_fe: %{
|
||||
theme: "pleroma-dark",
|
||||
# ... see /priv/static/static/config.json for the available keys.
|
||||
},
|
||||
masto_fe: %{
|
||||
showInstanceSpecificPanel: true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These settings **need to be complete**, they will override the defaults.
|
||||
|
|
|
@ -55,18 +55,18 @@ Servers should be configured as lists.
|
|||
|
||||
### Example
|
||||
|
||||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
|
||||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`. We also give a reason why the moderation was done:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :mrf,
|
||||
policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||
|
||||
config :pleroma, :mrf_simple,
|
||||
media_removal: ["illegalporn.biz"],
|
||||
media_nsfw: ["porn.biz", "porn.business"],
|
||||
reject: ["spam.com"],
|
||||
federated_timeline_removal: ["spam.university"],
|
||||
report_removal: ["whiny.whiner"]
|
||||
media_removal: [{"illegalporn.biz", "Media can contain illegal contant"}],
|
||||
media_nsfw: [{"porn.biz", "unmarked nsfw media"}, {"porn.business", "A lot of unmarked nsfw media"}],
|
||||
reject: [{"spam.com", "They keep spamming our users"}],
|
||||
federated_timeline_removal: [{"spam.university", "Annoying low-quality posts who otherwise fill up TWKN"}],
|
||||
report_removal: [{"whiny.whiner", "Keep spamming us with irrelevant reports"}]
|
||||
```
|
||||
|
||||
### Use with Care
|
||||
|
|
|
@ -319,6 +319,22 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
}
|
||||
```
|
||||
|
||||
## `DELETE /api/v1/pleroma/admin/instances/:instance`
|
||||
|
||||
### Delete all users and activities from a remote instance
|
||||
|
||||
Note: this will trigger a job to remove instance content in the background.
|
||||
It may take some time.
|
||||
|
||||
- Params:
|
||||
- `instance`: remote instance host
|
||||
- Response:
|
||||
- The `instance` name as a string
|
||||
|
||||
```json
|
||||
"lain.com"
|
||||
```
|
||||
|
||||
## `GET /api/v1/pleroma/admin/statuses`
|
||||
|
||||
### Retrives all latest statuses
|
||||
|
|
|
@ -16,11 +16,4 @@ Installation instructions can be found in the installation section of these docs
|
|||
Great! Now you can explore the fediverse! Open the login page for your Pleroma instance (e.g. <https://pleroma.soykaf.com>) and login with your username and password. (If you don't have an account yet, click on Register)
|
||||
|
||||
### Pleroma-FE
|
||||
The default front-end used by Pleroma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](../frontend).
|
||||
|
||||
### Mastodon interface
|
||||
If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too!
|
||||
Just add a "/web" after your instance url (e.g. <https://pleroma.soykaf.com/web>) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC!
|
||||
The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
|
||||
|
||||
Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma.
|
||||
The default front-end used by Pleroma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](../frontend).
|
|
@ -9,7 +9,7 @@
|
|||
* GNU make
|
||||
* CMake
|
||||
|
||||
## Optionnal dependencies
|
||||
## Optional dependencies
|
||||
|
||||
* ImageMagick
|
||||
* FFmpeg
|
||||
|
|
9
docs/installation/yunohost_en.md
Normal file
9
docs/installation/yunohost_en.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Installing on Yunohost
|
||||
|
||||
[YunoHost](https://yunohost.org) is a server operating system aimed at self-hosting. The YunoHost community maintains a package of Pleroma which allows you to install Pleroma on YunoHost. You can install it via the normal way through the admin web interface, or through the CLI. More information can be found at [the repo of the package](https://github.com/YunoHost-Apps/pleroma_ynh).
|
||||
|
||||
## Questions
|
||||
|
||||
Questions and problems related to the YunoHost parts can be done through the [regular YunoHost channels](https://yunohost.org/en/help).
|
||||
|
||||
For questions about Pleroma, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
|
@ -1,48 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
project_id="74"
|
||||
project_branch="rebase/glitch-soc"
|
||||
static_dir="instance/static"
|
||||
# For bundling:
|
||||
# project_branch="pleroma"
|
||||
# static_dir="priv/static"
|
||||
|
||||
if [ ! -d "${static_dir}" ]
|
||||
then
|
||||
echo "Error: ${static_dir} directory is missing, are you sure you are running this script at the root of pleroma’s repository?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
last_modified="$(curl --fail -s -I 'https://git.pleroma.social/api/v4/projects/'${project_id}'/jobs/artifacts/'${project_branch}'/download?job=build' | grep '^Last-Modified:' | cut -d: -f2-)"
|
||||
|
||||
echo "branch:${project_branch}"
|
||||
echo "Last-Modified:${last_modified}"
|
||||
|
||||
artifact="mastofe.zip"
|
||||
|
||||
if [ "${last_modified}x" = "x" ]
|
||||
then
|
||||
echo "ERROR: Couldn't get the modification date of the latest build archive, maybe it expired, exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e mastofe.timestamp ] && [ "$(cat mastofe.timestamp)" = "${last_modified}" ]
|
||||
then
|
||||
echo "MastoFE is up-to-date, exiting..."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl --fail -c - "https://git.pleroma.social/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=build" -o "${artifact}" || exit
|
||||
|
||||
# TODO: Update the emoji as well
|
||||
rm -fr "${static_dir}/sw.js" "${static_dir}/packs" || exit
|
||||
unzip -q "${artifact}" || exit
|
||||
|
||||
cp public/assets/sw.js "${static_dir}/sw.js" || exit
|
||||
cp -r public/packs "${static_dir}/packs" || exit
|
||||
|
||||
echo "${last_modified}" > mastofe.timestamp
|
||||
rm -fr public
|
||||
rm -i "${artifact}"
|
|
@ -286,9 +286,7 @@ defp migrate_from_db(opts) do
|
|||
file = File.open!(tmp_config_path)
|
||||
|
||||
shell_info(
|
||||
"Saving database configuration settings to #{tmp_config_path}. Copy it to the #{
|
||||
Path.dirname(config_path)
|
||||
} manually."
|
||||
"Saving database configuration settings to #{tmp_config_path}. Copy it to the #{Path.dirname(config_path)} manually."
|
||||
)
|
||||
|
||||
write_config(file, tmp_config_path, opts)
|
||||
|
|
|
@ -51,9 +51,7 @@ def run(["new", nickname, email | rest]) do
|
|||
A user will be created with the following information:
|
||||
- nickname: #{nickname}
|
||||
- email: #{email}
|
||||
- password: #{
|
||||
if(generated_password?, do: "[generated; a reset link will be created]", else: password)
|
||||
}
|
||||
- password: #{if(generated_password?, do: "[generated; a reset link will be created]", else: password)}
|
||||
- name: #{name}
|
||||
- bio: #{bio}
|
||||
- moderator: #{if(moderator?, do: "true", else: "false")}
|
||||
|
@ -114,15 +112,9 @@ def run(["reset_password", nickname]) do
|
|||
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
|
||||
shell_info("Generated password reset token for #{user.nickname}")
|
||||
|
||||
IO.puts(
|
||||
"URL: #{
|
||||
Pleroma.Web.Router.Helpers.reset_password_url(
|
||||
Pleroma.Web.Endpoint,
|
||||
:reset,
|
||||
token.token
|
||||
)
|
||||
}"
|
||||
)
|
||||
IO.puts("URL: #{Pleroma.Web.Router.Helpers.reset_password_url(Pleroma.Web.Endpoint,
|
||||
:reset,
|
||||
token.token)}")
|
||||
else
|
||||
_ ->
|
||||
shell_error("No local user #{nickname}")
|
||||
|
@ -321,9 +313,7 @@ def run(["invites"]) do
|
|||
end
|
||||
|
||||
shell_info(
|
||||
"ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{
|
||||
invite.used
|
||||
}#{expire_info}#{using_info}"
|
||||
"ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{invite.used}#{expire_info}#{using_info}"
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
@ -424,9 +414,7 @@ def run(["list"]) do
|
|||
users
|
||||
|> Enum.each(fn user ->
|
||||
shell_info(
|
||||
"#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{
|
||||
user.is_locked
|
||||
}, is_active: #{user.is_active}"
|
||||
"#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{user.is_locked}, is_active: #{user.is_active}"
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
|
|
@ -302,7 +302,7 @@ def delete_all_by_object_ap_id(id) when is_binary(id) 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
|
||||
|
@ -362,11 +362,9 @@ def following_requests_for_actor(%User{ap_id: ap_id}) do
|
|||
end
|
||||
|
||||
def restrict_deactivated_users(query) do
|
||||
deactivated_users =
|
||||
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||
|> Repo.all()
|
||||
deactivated_users_query = from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||
|
||||
Activity.Queries.exclude_authors(query, deactivated_users)
|
||||
from(activity in query, where: activity.actor not in subquery(deactivated_users_query))
|
||||
end
|
||||
|
||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
||||
|
|
|
@ -19,9 +19,7 @@ def on_shell(username, _pubkey, _ip, _port) do
|
|||
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)}
|
||||
Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{inspect(port)} using #{inspect(method)}
|
||||
"""
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -20,6 +20,140 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
|
||||
]
|
||||
|
||||
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,20 +168,24 @@ def check_hellthread_threshold 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(),
|
||||
:ok <- check_old_chat_shoutbox() 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()
|
||||
]
|
||||
|> Enum.reduce(:ok, fn
|
||||
:ok, :ok -> :ok
|
||||
_, _ -> :error
|
||||
end)
|
||||
end
|
||||
|
||||
def check_welcome_message_config do
|
||||
|
|
|
@ -21,9 +21,7 @@ def warn 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()
|
||||
|
||||
|
|
|
@ -148,9 +148,7 @@ defp update({group, key, value, merged}) 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)
|
||||
|
||||
|
|
|
@ -60,9 +60,7 @@ def load 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
|
||||
|
||||
|
@ -122,9 +120,7 @@ defp load_pack(pack_dir, emoji_groups) do
|
|||
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)
|
||||
|
|
|
@ -57,9 +57,7 @@ defp do_open(uri, %{proxy: {proxy_host, proxy_port}} = opts) 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 @@ defp do_open(uri, %{proxy: {proxy_type, proxy_host, proxy_port}} = opts) 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
|
||||
|
|
|
@ -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 @@ defp scrape_favicon(%URI{} = instance_uri) 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
|
||||
|
|
|
@ -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 @@ def vacuum(args) 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."
|
||||
|
|
|
@ -481,9 +481,7 @@ def get_log_entry_message(%ModerationLog{
|
|||
"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 +521,7 @@ def get_log_entry_message(%ModerationLog{
|
|||
"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{
|
||||
|
|
|
@ -72,6 +72,7 @@ def unread_notifications_count(%User{id: user_id}) do
|
|||
pleroma:emoji_reaction
|
||||
pleroma:report
|
||||
reblog
|
||||
poll
|
||||
}
|
||||
|
||||
def changeset(%Notification{} = notification, attrs) do
|
||||
|
@ -127,6 +128,7 @@ def for_user_query(user, opts \\ %{}) 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 +142,17 @@ defp exclude_blocked(query, user, opts) 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
|
||||
|
@ -379,7 +392,7 @@ defp do_create_notifications(%Activity{} = activity, options) 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)
|
||||
|
||||
|
@ -435,15 +448,18 @@ defp type_from_activity_object(%{data: %{"type" => "Create"}} = activity) 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 +473,28 @@ def create_notification(%Activity{} = activity, %User{} = user, do_send \\ true)
|
|||
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)}
|
||||
|
@ -572,8 +610,10 @@ def exclude_thread_muter_ap_ids(ap_ids, %Activity{} = activity) 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 +621,21 @@ def skip?(%Activity{} = activity, %User{} = user) 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 +644,27 @@ def skip?(:invisible, %Activity{} = activity, _) 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?(follower, user) -> 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 +674,10 @@ def skip?(:recently_followed, %Activity{data: %{"type" => "Follow"}} = activity,
|
|||
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 +695,7 @@ def skip?(:filtered, activity, user) 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)
|
||||
|
|
|
@ -29,9 +29,7 @@ def handle_event(
|
|||
_
|
||||
) 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
|
||||
|
||||
|