forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
b202cf15d6
314 changed files with 1943 additions and 494 deletions
48
CHANGELOG.md
48
CHANGELOG.md
|
@ -8,7 +8,39 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
### Changed
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||
|
||||
### Removed
|
||||
|
||||
## 2.4.1 - 2021-08-29
|
||||
|
||||
### Changed
|
||||
- Make `mix pleroma.database set_text_search_config` run concurrently and indefinitely
|
||||
|
||||
### Added
|
||||
- AdminAPI: Missing configuration description for StealEmojiPolicy
|
||||
|
||||
### Fixed
|
||||
- MastodonAPI: Stream out Create activities
|
||||
- MRF ObjectAgePolicy: Fix pattern matching on "published"
|
||||
- TwitterAPI: Make `change_password` and `change_email` require params on body instead of query
|
||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||
- AdminAPI: Fix rendering reports containing a `nil` object
|
||||
- Mastodon API: Activity Search fallbacks on status fetching after a DB Timeout/Error
|
||||
- Mastodon API: Fix crash in Streamer related to reblogging
|
||||
- AdminAPI: List available frontends when `static/frontends` folder is missing
|
||||
- Make activity search properly use language-aware GIN indexes
|
||||
- AdminAPI: Fix suggestions for MRF Policies
|
||||
|
||||
## 2.4.0 - 2021-08-08
|
||||
|
||||
### 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.
|
||||
|
@ -24,6 +56,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- AdminAPI: return `created_at` date with users.
|
||||
- `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
|
||||
|
@ -34,14 +67,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- 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
|
||||
|
||||
### Removed
|
||||
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
|
||||
|
||||
## Unreleased (Patch)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable.
|
||||
- Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image
|
||||
- Applying ConcurrentLimiter settings via AdminAPI
|
||||
|
@ -50,7 +75,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- MRF (`SimplePolicy`): Embedded objects are now checked. If any embedded object would be rejected, its parent is rejected. This fixes Announces leaking posts from blocked domains.
|
||||
- Fixed some Markdown issues, including trailing slash in links.
|
||||
|
||||
## [2.3.0] - 2020-03-01
|
||||
### Removed
|
||||
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
|
||||
|
||||
## [2.3.0] - 2021-03-01
|
||||
|
||||
### Security
|
||||
|
||||
|
@ -151,7 +179,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mastodon API: Support for expires_in/expires_at in the Filters.
|
||||
</details>
|
||||
|
||||
## [2.2.2] - 2020-01-18
|
||||
## [2.2.2] - 2021-01-18
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -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>.
|
||||
|
|
|
@ -563,6 +563,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"}
|
||||
]
|
||||
},
|
||||
%{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -5,7 +5,7 @@ Pleroma's full text search feature is powered by PostgreSQL's native [text searc
|
|||
|
||||
## Setup and test the new search config
|
||||
|
||||
In most cases, you would need an extension installed to support parsing CJK text. Here are a few extension you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
|
||||
In most cases, you would need an extension installed to support parsing CJK text. Here are a few extensions you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
|
||||
|
||||
* [a generic n-gram parser](https://github.com/huangjimmy/pg_cjk_parser) supports Simplifed/Traditional Chinese, Japanese, and Korean
|
||||
* [a Korean parser](https://github.com/i0seph/textsearch_ko) based on mecab
|
||||
|
@ -34,7 +34,7 @@ Check output of the query, and see if it matches your expectation.
|
|||
mix pleroma.database set_text_search_config YOUR.CONFIG
|
||||
```
|
||||
|
||||
Note: index update may take a while.
|
||||
Note: index update may take a while, and it can be done while the instance is up and running, so you may restart db connection as soon as you see `Recreate index` in task output.
|
||||
|
||||
## Restart database connection
|
||||
Since some changes above will only apply with a new database connection, you will have to restart either Pleroma or PostgreSQL process, or use `pg_terminate_backend` SQL command without restarting either.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# Installing on Alpine Linux
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide is a step-by-step installation guide for Alpine Linux. The instructions were verified against Alpine v3.10 standard image. You might miss additional dependencies if you use `netboot` instead.
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# Installing on Arch Linux
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.archlinux.org/index.php/Sudo). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# Installing on Debian Based Distributions
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide will assume you are on Debian 11 (“bullseye”) or later. This guide should also work with Ubuntu 18.04 (“Bionic Beaver”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# Installing on Gentoo GNU/Linux
|
||||
|
||||
{! backend/installation/otp_vs_from_source_source.include !}
|
||||
|
||||
## Installation
|
||||
|
||||
This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.gentoo.org/wiki/Sudo). Lines that begin with `#` indicate that they should be run as the superuser. Lines using `$` should be run as the indicated user, e.g. `pleroma$` should be run as the `pleroma` user.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Switching a from-source install to OTP releases
|
||||
|
||||
## What are OTP releases?
|
||||
OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more.
|
||||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
In this guide we cover how you can migrate from a from source installation to one using OTP releases.
|
||||
|
||||
## Pre-requisites
|
||||
You will be running commands as root. If you aren't root already, please elevate your priviledges by executing `sudo su`/`su`.
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
# Installing on Linux using OTP releases
|
||||
|
||||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
This guide covers a installation using an OTP release. To install Pleroma from source, please check out the corresponding guide for your distro.
|
||||
|
||||
## Pre-requisites
|
||||
* A machine running Linux with GNU (e.g. Debian, Ubuntu) or musl (e.g. Alpine) libc and `x86_64`, `aarch64` or `armv7l` CPU, you have root access to. If you are not sure if it's compatible see [Detecting flavour section](#detecting-flavour) below
|
||||
* A (sub)domain pointed to the machine
|
||||
|
|
3
docs/installation/otp_vs_from_source.include
Normal file
3
docs/installation/otp_vs_from_source.include
Normal file
|
@ -0,0 +1,3 @@
|
|||
## OTP releases vs from-source installations
|
||||
|
||||
There are two ways to install Pleroma. You can use OTP releases or do a from-source installation. OTP releases are as close as you can get to binary releases with Erlang/Elixir. The release is self-contained, and provides everything needed to boot it, it is easily administered via the provided shell script to open up a remote console, start/stop/restart the release, start in the background, send remote commands, and more. With from source installations you install Pleroma from source, meaning you have to install certain dependencies like Erlang+Elixir and compile Pleroma yourself.
|
3
docs/installation/otp_vs_from_source_source.include
Normal file
3
docs/installation/otp_vs_from_source_source.include
Normal file
|
@ -0,0 +1,3 @@
|
|||
{! backend/installation/otp_vs_from_source.include !}
|
||||
|
||||
This guide covers a from-source installation. To install using OTP releases, please check out [the OTP guide](./otp_en.md).
|
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.
|
|
@ -209,7 +209,9 @@ def run(["set_text_search_config", tsconfig]) do
|
|||
new.fts_content := to_tsvector(new.data->>'content');
|
||||
RETURN new;
|
||||
END
|
||||
$$ LANGUAGE plpgsql"
|
||||
$$ LANGUAGE plpgsql",
|
||||
[],
|
||||
timeout: :infinity
|
||||
)
|
||||
|
||||
shell_info("Refresh RUM index")
|
||||
|
@ -219,7 +221,9 @@ def run(["set_text_search_config", tsconfig]) do
|
|||
|
||||
Ecto.Adapters.SQL.query!(
|
||||
Pleroma.Repo,
|
||||
"CREATE INDEX objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); "
|
||||
"CREATE INDEX CONCURRENTLY objects_fts ON objects USING gin(to_tsvector('#{tsconfig}', data->>'content')); ",
|
||||
[],
|
||||
timeout: :infinity
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -26,19 +26,23 @@ def search(user, search_query, options \\ []) 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()
|
||||
|> 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
|
||||
|
@ -61,10 +65,17 @@ defp restrict_public(q) 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 +83,17 @@ defp query_with(q, :gin, search_query, :plain) 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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -379,7 +380,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 +436,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 +461,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 +598,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 +609,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 +632,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 +662,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 +683,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)
|
||||
|
|
|
@ -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
|
||||
|
@ -288,6 +289,7 @@ defp do_create(%{to: to, actor: actor, context: context, object: object} = param
|
|||
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
|
||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_schedule_poll_notifications(activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
|
@ -302,6 +304,11 @@ defp do_create(%{to: to, actor: actor, context: context, object: object} = param
|
|||
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] || %{}
|
||||
|
|
|
@ -15,6 +15,7 @@ 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
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
|
@ -125,6 +126,37 @@ def create(actor, object, recipients) 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(),
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
@ -100,6 +102,11 @@ def subdomain_match?(domains, host) 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
|
||||
|
|
|
@ -159,6 +159,8 @@ def config_description 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/`.
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ defp check_delist(message, actions) 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 @@ defp check_strip_followers(message, actions) 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 @@ defp check_strip_followers(message, actions) 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),
|
||||
|
|
|
@ -47,7 +47,7 @@ def filter(object), do: {:ok, object}
|
|||
|
||||
@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
|
||||
|
|
|
@ -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 @@ defp check_accept(%{host: actor_host} = _actor_info, object) 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
|
||||
|
@ -44,7 +44,7 @@ defp check_media_removal(
|
|||
)
|
||||
when length(child_attachment) > 0 do
|
||||
media_removal =
|
||||
Config.get([:mrf_simple, :media_removal])
|
||||
instance_list(:media_removal)
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
object =
|
||||
|
@ -68,7 +68,7 @@ defp check_media_nsfw(
|
|||
} = object
|
||||
) do
|
||||
media_nsfw =
|
||||
Config.get([:mrf_simple, :media_nsfw])
|
||||
instance_list(:media_nsfw)
|
||||
|> MRF.subdomains_regex()
|
||||
|
||||
object =
|
||||
|
@ -85,7 +85,7 @@ defp check_media_nsfw(_actor_info, object), do: {:ok, object}
|
|||
|
||||
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 +112,7 @@ defp intersection(list1, list2) 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 +137,7 @@ defp check_followers_only(%{host: actor_host} = _actor_info, object) 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 +151,7 @@ defp check_report_removal(_actor_info, object), do: {:ok, object}
|
|||
|
||||
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 +165,7 @@ defp check_avatar_removal(_actor_info, object), do: {:ok, object}
|
|||
|
||||
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
|
||||
|
@ -185,12 +185,17 @@ defp check_object(%{"object" => object} = activity) do
|
|||
|
||||
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
|
||||
|
@ -253,14 +258,42 @@ 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_excluded =
|
||||
Config.get(:mrf_simple)
|
||||
|> Enum.map(fn {rule, instances} ->
|
||||
{rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)}
|
||||
end)
|
||||
|
||||
mrf_simple =
|
||||
Config.get(:mrf_simple)
|
||||
|> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end)
|
||||
|> Enum.into(%{})
|
||||
mrf_simple_excluded
|
||||
|> Enum.map(fn {rule, instances} ->
|
||||
{rule, Enum.map(instances, fn {host, _} -> host end)}
|
||||
end)
|
||||
|> Map.new()
|
||||
|
||||
{:ok, %{mrf_simple: mrf_simple}}
|
||||
# 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
|
||||
|
@ -270,70 +303,67 @@ def config_description 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
|
||||
|
|
|
@ -92,6 +92,51 @@ def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = messa
|
|||
|
||||
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: "Regex-list of shortcodes to reject",
|
||||
suggestions: [""]
|
||||
},
|
||||
%{
|
||||
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, %{}}
|
||||
|
|
|
@ -37,7 +37,7 @@ def filter(object), do: {:ok, object}
|
|||
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
|
||||
|
|
|
@ -39,7 +39,7 @@ def filter(message), do: {:ok, message}
|
|||
|
||||
@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
|
||||
|
|
|
@ -213,6 +213,7 @@ def stringify_keys(%{__struct__: _} = object) 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
|
||||
|
||||
|
|
|
@ -112,6 +112,7 @@ defp should_federate?(inbox, public) 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)
|
||||
|
|
|
@ -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,6 +23,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.Push
|
||||
alias Pleroma.Web.Streamer
|
||||
alias Pleroma.Workers.PollWorker
|
||||
|
||||
require Logger
|
||||
|
||||
|
@ -195,7 +195,7 @@ def handle(%{data: %{"type" => "Like"}} = object, meta) 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)
|
||||
|
@ -225,6 +225,8 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
|||
meta
|
||||
|> add_notifications(notifications)
|
||||
|
||||
ap_streamer().stream_out(activity)
|
||||
|
||||
{:ok, activity, meta}
|
||||
else
|
||||
e -> Repo.rollback(e)
|
||||
|
@ -245,9 +247,7 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) 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}
|
||||
|
@ -389,7 +389,7 @@ def handle(object, meta) do
|
|||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do
|
||||
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"]))
|
||||
|
@ -424,7 +424,14 @@ def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) 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"],
|
||||
|
@ -436,15 +443,15 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_object_creation(%{"type" => objtype} = object, meta)
|
||||
when objtype in ~w[Audio Video Question Event Article Note Page] do
|
||||
def handle_object_creation(%{"type" => objtype} = object, _activity, meta)
|
||||
when objtype in ~w[Audio Video 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
|
||||
|
||||
|
|
|
@ -35,6 +35,12 @@ def install(%{body_params: params} = conn, _params) 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
|
||||
end
|
||||
|
|
|
@ -13,7 +13,9 @@ def extract_report_info(
|
|||
account = User.get_cached_by_ap_id(account_ap_id)
|
||||
|
||||
statuses =
|
||||
Enum.map(status_ap_ids, fn
|
||||
status_ap_ids
|
||||
|> Enum.reject(&is_nil(&1))
|
||||
|> Enum.map(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)
|
||||
end)
|
||||
|
|
|
@ -195,7 +195,8 @@ defp notification_type do
|
|||
"pleroma:chat_mention",
|
||||
"pleroma:report",
|
||||
"move",
|
||||
"follow_request"
|
||||
"follow_request",
|
||||
"poll"
|
||||
],
|
||||
description: """
|
||||
The type of event that resulted in the notification.
|
||||
|
|
|
@ -8,6 +8,8 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
|||
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, [])
|
||||
|
@ -63,17 +65,7 @@ def change_password_operation do
|
|||
summary: "Change account password",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
operationId: "UtilController.change_password",
|
||||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Current password", required: true),
|
||||
Operation.parameter(:new_password, :query, :string, "New password", required: true),
|
||||
Operation.parameter(
|
||||
:new_password_confirmation,
|
||||
:query,
|
||||
:string,
|
||||
"New password, confirmation",
|
||||
required: true
|
||||
)
|
||||
],
|
||||
requestBody: request_body("Parameters", change_password_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Success", "application/json", %Schema{
|
||||
|
@ -86,17 +78,30 @@ def change_password_operation do
|
|||
}
|
||||
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",
|
||||
parameters: [
|
||||
Operation.parameter(:password, :query, :string, "Current password", required: true),
|
||||
Operation.parameter(:email, :query, :string, "New email", required: true)
|
||||
],
|
||||
requestBody: nil,
|
||||
requestBody: request_body("Parameters", change_email_request(), required: true),
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("Success", "application/json", %Schema{
|
||||
|
@ -109,6 +114,19 @@ def change_email_operation do
|
|||
}
|
||||
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"},
|
||||
password: %Schema{type: :string, description: "Current password"}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_notificaton_settings_operation do
|
||||
%Operation{
|
||||
tags: ["Accounts"],
|
||||
|
|
|
@ -6,6 +6,7 @@ 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
|
||||
|
||||
|
@ -213,8 +214,10 @@ defp object(draft) 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("generator", draft.params[:generator])
|
||||
|
|
|
@ -291,33 +291,6 @@ def format_input(text, "text/markdown", options) do
|
|||
|> 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 +385,14 @@ def maybe_notify_mentioned_recipients(
|
|||
|
||||
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()
|
||||
|
|
|
@ -50,6 +50,7 @@ def index(conn, %{account_id: account_id} = params) do
|
|||
favourite
|
||||
move
|
||||
pleroma:emoji_reaction
|
||||
poll
|
||||
}
|
||||
def index(%{assigns: %{user: user}} = conn, params) do
|
||||
params =
|
||||
|
|
|
@ -95,7 +95,20 @@ def federation 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
|
||||
|
|
|
@ -112,6 +112,9 @@ def render(
|
|||
"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)
|
||||
|
|
|
@ -65,11 +65,19 @@ defp get_context_id(%{data: %{"context" => context}}) when is_binary(context),
|
|||
|
||||
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]
|
||||
|
||||
|
|
|
@ -124,8 +124,8 @@ def build_content(notification, actor, object, mastodon_type) 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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -81,17 +81,13 @@ def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
|
|||
end
|
||||
end
|
||||
|
||||
def change_password(%{assigns: %{user: user}} = conn, %{
|
||||
password: password,
|
||||
new_password: new_password,
|
||||
new_password_confirmation: new_password_confirmation
|
||||
}) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, 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: new_password,
|
||||
password_confirmation: new_password_confirmation
|
||||
password: body_params.new_password,
|
||||
password_confirmation: body_params.new_password_confirmation
|
||||
}) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
|
@ -108,10 +104,10 @@ def change_password(%{assigns: %{user: user}} = conn, %{
|
|||
end
|
||||
end
|
||||
|
||||
def change_email(%{assigns: %{user: user}} = conn, %{password: password, email: email}) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, 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, email) do
|
||||
with {:ok, _user} <- User.change_email(user, body_params.email) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, changeset} ->
|
||||
|
|
45
lib/pleroma/workers/poll_worker.ex
Normal file
45
lib/pleroma/workers/poll_worker.ex
Normal file
|
@ -0,0 +1,45 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 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
|
||||
|
||||
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
|
2
mix.exs
2
mix.exs
|
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
|||
def project do
|
||||
[
|
||||
app: :pleroma,
|
||||
version: version("2.3.50"),
|
||||
version: version("2.4.51"),
|
||||
elixir: "~> 1.9",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||
|
|
|
@ -3,8 +3,8 @@ msgstr ""
|
|||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-05-13 16:37+0000\n"
|
||||
"PO-Revision-Date: 2020-07-09 14:40+0000\n"
|
||||
"Last-Translator: Ben Is <srsbzns@cock.li>\n"
|
||||
"PO-Revision-Date: 2021-08-13 15:42+0000\n"
|
||||
"Last-Translator: marcin mikołajczak <me@mkljczk.pl>\n"
|
||||
"Language-Team: Polish <https://translate.pleroma.social/projects/pleroma/"
|
||||
"pleroma/pl/>\n"
|
||||
"Language: pl\n"
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.0.4\n"
|
||||
"X-Generator: Weblate 4.6.2\n"
|
||||
|
||||
## This file is a PO Template file.
|
||||
##
|
||||
|
@ -68,49 +68,49 @@ msgstr[2] "powinno mieć %{count} znaków"
|
|||
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
msgstr[0] "powinno zawierać %{count} element"
|
||||
msgstr[1] "powinno zawierać %{count} elementy"
|
||||
msgstr[2] "powinno zawierać %{count} elementów"
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
msgstr[0] "powinno zawierać przynajmniej %{count} znak"
|
||||
msgstr[1] "powinno zawierać przynajmniej %{count} znaki"
|
||||
msgstr[2] "powinno zawierać przynajmniej %{count} znaków"
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
msgstr[0] "powinno zawierać przynajmniej %{count} element"
|
||||
msgstr[1] "powinno zawierać przynajmniej %{count} elementy"
|
||||
msgstr[2] "powinno zawierać przynajmniej %{count} elementów"
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
msgstr[0] "powinno zawierać najwyżej %{count} znak"
|
||||
msgstr[1] "powinno zawierać najwyżej %{count} znaki"
|
||||
msgstr[2] "powinno zawierać najwyżej %{count} znaków"
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
msgstr[0] "powinno zawierać najwyżej %{count} element"
|
||||
msgstr[1] "powinno zawierać najwyżej %{count} elementy"
|
||||
msgstr[2] "powinno zawierać najwyżej %{count} elementów"
|
||||
|
||||
## From Ecto.Changeset.validate_number/3
|
||||
msgid "must be less than %{number}"
|
||||
msgstr ""
|
||||
msgstr "musi wynosić mniej niż %{number}"
|
||||
|
||||
msgid "must be greater than %{number}"
|
||||
msgstr ""
|
||||
msgstr "musi wynosić więcej niż %{number}"
|
||||
|
||||
msgid "must be less than or equal to %{number}"
|
||||
msgstr ""
|
||||
msgstr "musi być mniejsze lub równe %{number}"
|
||||
|
||||
msgid "must be greater than or equal to %{number}"
|
||||
msgstr ""
|
||||
msgstr "musi być większe lub równe %{number}"
|
||||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
||||
msgstr "musi być równe %{number}"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:421
|
||||
#, elixir-format
|
||||
|
@ -152,7 +152,7 @@ msgstr "Nie znaleziono użytkownika"
|
|||
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
||||
#, elixir-format
|
||||
msgid "Can't get favorites"
|
||||
msgstr ""
|
||||
msgstr "Nie można uzyskać ulubionych"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
||||
#, elixir-format
|
||||
|
@ -172,7 +172,7 @@ msgstr "Komentarz może mieć co najwyżej %{max_size} znaków"
|
|||
#: lib/pleroma/config/config_db.ex:222
|
||||
#, elixir-format
|
||||
msgid "Config with params %{params} not found"
|
||||
msgstr ""
|
||||
msgstr "Nie znaleziono konfiguracji z parametrami %{params}"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:95
|
||||
#, elixir-format
|
||||
|
@ -213,38 +213,38 @@ msgstr "Nie udało się cofnąć powtórzenia"
|
|||
#: lib/pleroma/web/common_api/common_api.ex:437
|
||||
#, elixir-format
|
||||
msgid "Could not update state"
|
||||
msgstr ""
|
||||
msgstr "Nie można zaktualizować stanu"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202
|
||||
#, elixir-format
|
||||
msgid "Error."
|
||||
msgstr ""
|
||||
msgstr "Błąd."
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:106
|
||||
#, elixir-format
|
||||
msgid "Invalid CAPTCHA"
|
||||
msgstr ""
|
||||
msgstr "Niewłaściwa CAPTCHA"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:569
|
||||
#, elixir-format
|
||||
msgid "Invalid credentials"
|
||||
msgstr ""
|
||||
msgstr "Nieprawidłowe dane uwierzytelniania"
|
||||
|
||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
|
||||
#, elixir-format
|
||||
msgid "Invalid credentials."
|
||||
msgstr ""
|
||||
msgstr "Nieprawidłowe dane uwierzytelniania."
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:265
|
||||
#, elixir-format
|
||||
msgid "Invalid indices"
|
||||
msgstr ""
|
||||
msgstr "Nieprawidłowe indeksy"
|
||||
|
||||
#: lib/pleroma/web/admin_api/admin_api_controller.ex:1147
|
||||
#, elixir-format
|
||||
msgid "Invalid parameters"
|
||||
msgstr ""
|
||||
msgstr "Nieprawidłowe parametry"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:411
|
||||
#, elixir-format
|
||||
|
@ -307,7 +307,7 @@ msgstr "Coś się zepsuło"
|
|||
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||
#, elixir-format
|
||||
msgid "The message visibility must be direct"
|
||||
msgstr ""
|
||||
msgstr "Widoczność wiadomości musi być „Bezpośrednia”"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:566
|
||||
#, elixir-format
|
||||
|
@ -317,17 +317,17 @@ msgstr "Ten status przekracza limit znaków"
|
|||
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||
#, elixir-format
|
||||
msgid "This resource requires authentication."
|
||||
msgstr ""
|
||||
msgstr "Ten zasób wymaga uwierzytelnienia."
|
||||
|
||||
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||
#, elixir-format
|
||||
msgid "Throttled"
|
||||
msgstr ""
|
||||
msgstr "Ograniczono"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:266
|
||||
#, elixir-format
|
||||
msgid "Too many choices"
|
||||
msgstr ""
|
||||
msgstr "Zbyt wiele wyborów"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:442
|
||||
#, elixir-format
|
||||
|
@ -349,17 +349,18 @@ msgstr "Twoje konto jest obecnie nieaktywne"
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:332
|
||||
#, elixir-format
|
||||
msgid "Your login is missing a confirmed e-mail address"
|
||||
msgstr ""
|
||||
msgstr "Twój adres e-mail nie został potwierdzony"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389
|
||||
#, elixir-format
|
||||
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
msgstr "Nie można odczytać skrzynki odbiorczej %{nickname} jako %{as_nickname}"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472
|
||||
#, elixir-format
|
||||
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
"Nie można zaktualizować skrzynki nadawczcej %{nickname} jako %{as_nickname}"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:388
|
||||
#, elixir-format
|
||||
|
@ -405,12 +406,12 @@ msgstr "Nie udało się"
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:411
|
||||
#, elixir-format
|
||||
msgid "Failed to authenticate: %{message}."
|
||||
msgstr ""
|
||||
msgstr "Nie udało się uwierzytelnić: %{message}."
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:442
|
||||
#, elixir-format
|
||||
msgid "Failed to set up user account."
|
||||
msgstr ""
|
||||
msgstr "Nie udało się skonfigurować konta użytkownika."
|
||||
|
||||
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||
#, elixir-format
|
||||
|
@ -431,7 +432,7 @@ msgstr "Nieprawidłowa nazwa użytkownika lub hasło"
|
|||
#: lib/pleroma/web/twitter_api/twitter_api.ex:118
|
||||
#, elixir-format
|
||||
msgid "Invalid answer data"
|
||||
msgstr ""
|
||||
msgstr "Nieprawidłowe dane odpowiedzi"
|
||||
|
||||
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128
|
||||
#, elixir-format
|
||||
|
@ -441,7 +442,7 @@ msgstr "Nieobsługiwana wersja schematu Nodeinfo"
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:169
|
||||
#, elixir-format
|
||||
msgid "This action is outside the authorized scopes"
|
||||
msgstr ""
|
||||
msgstr "Ta akcja wykracza poza dozwolone zakresy"
|
||||
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||
#, elixir-format
|
||||
|
@ -477,12 +478,12 @@ msgstr "Błąd CAPTCHA"
|
|||
#: lib/pleroma/web/common_api/common_api.ex:200
|
||||
#, elixir-format
|
||||
msgid "Could not add reaction emoji"
|
||||
msgstr ""
|
||||
msgstr "Nie można dodać reakcji emoji"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:211
|
||||
#, elixir-format
|
||||
msgid "Could not remove reaction emoji"
|
||||
msgstr ""
|
||||
msgstr "Nie można usunąć reakcji emoji"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:129
|
||||
#, elixir-format
|
||||
|
@ -535,6 +536,8 @@ msgstr "Wymagany reset hasła"
|
|||
#, elixir-format
|
||||
msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||
msgstr ""
|
||||
"Naruszenie bezpieczeństwa: sprawdzanie zakresów OAuth nie zostało ani "
|
||||
"wykonane, ani celowo pominięte."
|
||||
|
||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||
#, elixir-format
|
||||
|
@ -569,7 +572,7 @@ msgstr "Nieoczekiwany błąd podczas zmieniania metadanych paczki."
|
|||
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
||||
#, elixir-format
|
||||
msgid "User is not an admin."
|
||||
msgstr ""
|
||||
msgstr "Użytkownik nie jest administratorem."
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||
#, elixir-format
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
defmodule Pleroma.Repo.Migrations.SimplePolicyStringToTuple do
|
||||
use Ecto.Migration
|
||||
|
||||
alias Pleroma.ConfigDB
|
||||
|
||||
def up, do: ConfigDB.get_by_params(%{group: :pleroma, key: :mrf_simple}) |> update_to_tuples
|
||||
def down, do: ConfigDB.get_by_params(%{group: :pleroma, key: :mrf_simple}) |> update_to_strings
|
||||
|
||||
defp update_to_tuples(%{value: value}) do
|
||||
new_value =
|
||||
value
|
||||
|> Enum.map(fn {k, v} ->
|
||||
{k,
|
||||
Enum.map(v, fn
|
||||
{instance, reason} -> {instance, reason}
|
||||
instance -> {instance, ""}
|
||||
end)}
|
||||
end)
|
||||
|
||||
ConfigDB.update_or_create(%{group: :pleroma, key: :mrf_simple, value: new_value})
|
||||
end
|
||||
|
||||
defp update_to_tuples(nil), do: {:ok, nil}
|
||||
|
||||
defp update_to_strings(%{value: value}) do
|
||||
new_value =
|
||||
value
|
||||
|> Enum.map(fn {k, v} ->
|
||||
{k,
|
||||
Enum.map(v, fn
|
||||
{instance, _} -> instance
|
||||
instance -> instance
|
||||
end)}
|
||||
end)
|
||||
|
||||
ConfigDB.update_or_create(%{group: :pleroma, key: :mrf_simple, value: new_value})
|
||||
end
|
||||
|
||||
defp update_to_strings(nil), do: {:ok, nil}
|
||||
end
|
|
@ -0,0 +1,61 @@
|
|||
defmodule Pleroma.Repo.Migrations.QuarantainedStringToTuple do
|
||||
use Ecto.Migration
|
||||
|
||||
alias Pleroma.ConfigDB
|
||||
|
||||
def up,
|
||||
do:
|
||||
ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
|
||||
|> update_quarantined_instances_to_tuples
|
||||
|
||||
def down,
|
||||
do:
|
||||
ConfigDB.get_by_params(%{group: :pleroma, key: :instance})
|
||||
|> update_quarantined_instances_to_strings
|
||||
|
||||
defp update_quarantined_instances_to_tuples(%{value: settings}) do
|
||||
settings |> List.keyfind(:quarantined_instances, 0) |> update_to_tuples
|
||||
end
|
||||
|
||||
defp update_quarantined_instances_to_tuples(nil), do: {:ok, nil}
|
||||
|
||||
defp update_to_tuples({:quarantined_instances, instance_list}) do
|
||||
new_value =
|
||||
instance_list
|
||||
|> Enum.map(fn
|
||||
{v, r} -> {v, r}
|
||||
v -> {v, ""}
|
||||
end)
|
||||
|
||||
ConfigDB.update_or_create(%{
|
||||
group: :pleroma,
|
||||
key: :instance,
|
||||
value: [quarantined_instances: new_value]
|
||||
})
|
||||
end
|
||||
|
||||
defp update_to_tuples(nil), do: {:ok, nil}
|
||||
|
||||
defp update_quarantined_instances_to_strings(%{value: settings}) do
|
||||
settings |> List.keyfind(:quarantined_instances, 0) |> update_to_strings
|
||||
end
|
||||
|
||||
defp update_quarantined_instances_to_strings(nil), do: {:ok, nil}
|
||||
|
||||
defp update_to_strings({:quarantined_instances, instance_list}) do
|
||||
new_value =
|
||||
instance_list
|
||||
|> Enum.map(fn
|
||||
{v, _} -> v
|
||||
v -> v
|
||||
end)
|
||||
|
||||
ConfigDB.update_or_create(%{
|
||||
group: :pleroma,
|
||||
key: :instance,
|
||||
value: [quarantined_instances: new_value]
|
||||
})
|
||||
end
|
||||
|
||||
defp update_to_strings(nil), do: {:ok, nil}
|
||||
end
|
|
@ -0,0 +1,61 @@
|
|||
defmodule Pleroma.Repo.Migrations.TransparencyExclusionsStringToTuple do
|
||||
use Ecto.Migration
|
||||
|
||||
alias Pleroma.ConfigDB
|
||||
|
||||
def up,
|
||||
do:
|
||||
ConfigDB.get_by_params(%{group: :pleroma, key: :mrf})
|
||||
|> update_transparency_exclusions_instances_to_tuples
|
||||
|
||||
def down,
|
||||
do:
|
||||
ConfigDB.get_by_params(%{group: :pleroma, key: :mrf})
|
||||
|> update_transparency_exclusions_instances_to_strings
|
||||
|
||||
defp update_transparency_exclusions_instances_to_tuples(%{value: settings}) do
|
||||
settings |> List.keyfind(:transparency_exclusions, 0) |> update_to_tuples
|
||||
end
|
||||
|
||||
defp update_transparency_exclusions_instances_to_tuples(nil), do: {:ok, nil}
|
||||
|
||||
defp update_to_tuples({:transparency_exclusions, instance_list}) do
|
||||
new_value =
|
||||
instance_list
|
||||
|> Enum.map(fn
|
||||
{v, r} -> {v, r}
|
||||
v -> {v, ""}
|
||||
end)
|
||||
|
||||
ConfigDB.update_or_create(%{
|
||||
group: :pleroma,
|
||||
key: :mrf,
|
||||
value: [transparency_exclusions: new_value]
|
||||
})
|
||||
end
|
||||
|
||||
defp update_to_tuples(nil), do: {:ok, nil}
|
||||
|
||||
defp update_transparency_exclusions_instances_to_strings(%{value: settings}) do
|
||||
settings |> List.keyfind(:transparency_exclusions, 0) |> update_to_strings
|
||||
end
|
||||
|
||||
defp update_transparency_exclusions_instances_to_strings(nil), do: {:ok, nil}
|
||||
|
||||
defp update_to_strings({:transparency_exclusions, instance_list}) do
|
||||
new_value =
|
||||
instance_list
|
||||
|> Enum.map(fn
|
||||
{v, _} -> v
|
||||
v -> v
|
||||
end)
|
||||
|
||||
ConfigDB.update_or_create(%{
|
||||
group: :pleroma,
|
||||
key: :mrf,
|
||||
value: [transparency_exclusions: new_value]
|
||||
})
|
||||
end
|
||||
|
||||
defp update_to_strings(nil), do: {:ok, nil}
|
||||
end
|
|
@ -0,0 +1,49 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddPollToNotificationsEnum do
|
||||
use Ecto.Migration
|
||||
|
||||
@disable_ddl_transaction true
|
||||
|
||||
def up do
|
||||
"""
|
||||
alter type notification_type add value 'poll'
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:notifications) do
|
||||
modify(:type, :string)
|
||||
end
|
||||
|
||||
"""
|
||||
delete from notifications where type = 'poll'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
drop type if exists notification_type
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
create type notification_type as enum (
|
||||
'follow',
|
||||
'follow_request',
|
||||
'mention',
|
||||
'move',
|
||||
'pleroma:emoji_reaction',
|
||||
'pleroma:chat_mention',
|
||||
'reblog',
|
||||
'favourite',
|
||||
'pleroma:report'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter table notifications
|
||||
alter column type type notification_type using (type::notification_type)
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
end
|
BIN
priv/static/adminfe/app.143a1409.css
Normal file
BIN
priv/static/adminfe/app.143a1409.css
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/chunk-0c60.487f66a2.css
Normal file
BIN
priv/static/adminfe/chunk-0c60.487f66a2.css
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/chunk-45ed.3bf8b200.css
Normal file
BIN
priv/static/adminfe/chunk-45ed.3bf8b200.css
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/chunk-4a1e.4b10c06f.css
Normal file
BIN
priv/static/adminfe/chunk-4a1e.4b10c06f.css
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/chunk-5ff8.4ccacc86.css
Normal file
BIN
priv/static/adminfe/chunk-5ff8.4ccacc86.css
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/chunk-71b5.a479f25b.css
Normal file
BIN
priv/static/adminfe/chunk-71b5.a479f25b.css
Normal file
Binary file not shown.
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.f77689d7.css rel=stylesheet><link href=chunk-libs.5cf7f50a.css rel=stylesheet><link href=app.6fb984d1.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.6b30c658.js></script><script type=text/javascript src=static/js/chunk-elementUI.21957ec8.js></script><script type=text/javascript src=static/js/chunk-libs.5ca2c8e8.js></script><script type=text/javascript src=static/js/app.1428845f.js></script></body></html>
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.f1f2be85.css rel=stylesheet><link href=chunk-libs.74976a6a.css rel=stylesheet><link href=app.143a1409.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.2a586239.js></script><script type=text/javascript src=static/js/chunk-elementUI.4c32a355.js></script><script type=text/javascript src=static/js/chunk-libs.55b24a78.js></script><script type=text/javascript src=static/js/app.f02f5ebc.js></script></body></html>
|
Binary file not shown.
Before Width: | Height: | Size: 160 KiB |
Binary file not shown.
Before Width: | Height: | Size: 96 KiB |
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/static/js/app.f02f5ebc.js
Normal file
BIN
priv/static/adminfe/static/js/app.f02f5ebc.js
Normal file
Binary file not shown.
BIN
priv/static/adminfe/static/js/app.f02f5ebc.js.map
Normal file
BIN
priv/static/adminfe/static/js/app.f02f5ebc.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/static/js/chunk-0c60.9b12ac3f.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-0c60.9b12ac3f.js
Normal file
Binary file not shown.
BIN
priv/static/adminfe/static/js/chunk-0c60.9b12ac3f.js.map
Normal file
BIN
priv/static/adminfe/static/js/chunk-0c60.9b12ac3f.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/static/js/chunk-1719.d152ff7e.js
Normal file
BIN
priv/static/adminfe/static/js/chunk-1719.d152ff7e.js
Normal file
Binary file not shown.
BIN
priv/static/adminfe/static/js/chunk-1719.d152ff7e.js.map
Normal file
BIN
priv/static/adminfe/static/js/chunk-1719.d152ff7e.js.map
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/adminfe/static/js/chunk-176e.c2ac32bc.js.map
Normal file
BIN
priv/static/adminfe/static/js/chunk-176e.c2ac32bc.js.map
Normal file
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue