Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
7947236575
112 changed files with 1711 additions and 749 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -10,12 +10,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`)
|
||||
- Mix task option for force-unfollowing relays
|
||||
- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
|
||||
- Reports now generate notifications for admins and mods.
|
||||
- Pleroma API: Importing the mutes users from CSV files.
|
||||
- Experimental websocket-based federation between Pleroma instances.
|
||||
- Support pagination of blocks and mutes
|
||||
- App metrics: ability to restrict access to specified IP whitelist.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -30,6 +33,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Users with the `discoverable` field set to false will not show up in searches.
|
||||
- Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option).
|
||||
- Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`.
|
||||
- Polls now always return a `voters_count`, even if they are single-choice
|
||||
- Admin Emails: The ap id is used as the user link in emails now.
|
||||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
@ -40,6 +45,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `unconfirmed` status
|
||||
- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `actor_type`
|
||||
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
||||
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
||||
- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
|
||||
|
||||
</details>
|
||||
|
||||
|
@ -58,12 +65,17 @@ switched to a new configuration mechanism, however it was not officially removed
|
|||
- Allow sending chat messages to yourself.
|
||||
- Fix remote users with a whitespace name.
|
||||
- OStatus / static FE endpoints: fixed inaccessibility for anonymous users on non-federating instances, switched to handling per `:restrict_unauthenticated` setting.
|
||||
- Mastodon API: Current user is now included in conversation if it's the only participant
|
||||
- Mastodon API: Fixed last_status.account being not filled with account data
|
||||
|
||||
## Unreleased (Patch)
|
||||
|
||||
### Changed
|
||||
- API: Empty parameter values for integer parameters are now ignored in non-strict validaton mode.
|
||||
|
||||
### Fixes
|
||||
- Config generation: rename `Pleroma.Upload.Filter.ExifTool` to `Pleroma.Upload.Filter.Exiftool`
|
||||
|
||||
## [2.1.2] - 2020-09-17
|
||||
|
||||
### Security
|
||||
|
|
|
@ -4,7 +4,7 @@ COPY . .
|
|||
|
||||
ENV MIX_ENV=prod
|
||||
|
||||
RUN apk add git gcc g++ musl-dev make cmake &&\
|
||||
RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
|
||||
echo "import Mix.Config" > config/prod.secret.exs &&\
|
||||
mix local.hex --force &&\
|
||||
mix local.rebar --force &&\
|
||||
|
|
|
@ -565,7 +565,8 @@ config :pleroma, Oban,
|
|||
background: 5,
|
||||
remote_fetcher: 2,
|
||||
attachments_cleanup: 5,
|
||||
new_users_digest: 1
|
||||
new_users_digest: 1,
|
||||
mute_expire: 5
|
||||
],
|
||||
plugins: [Oban.Plugins.Pruner],
|
||||
crontab: [
|
||||
|
@ -823,7 +824,7 @@ config :pleroma, :restrict_unauthenticated,
|
|||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
|
||||
|
||||
config :pleroma, :mrf,
|
||||
policies: Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy,
|
||||
policies: [Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy, Pleroma.Web.ActivityPub.MRF.TagPolicy],
|
||||
transparency: true,
|
||||
transparency_exclusions: []
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use Mix.Config
|
||||
alias Pleroma.Docs.Generator
|
||||
|
||||
websocket_config = [
|
||||
path: "/websocket",
|
||||
|
@ -1555,298 +1554,6 @@ config :pleroma, :config_description, [
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf,
|
||||
tab: :mrf,
|
||||
label: "MRF",
|
||||
type: :group,
|
||||
description: "General MRF settings",
|
||||
children: [
|
||||
%{
|
||||
key: :policies,
|
||||
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}
|
||||
},
|
||||
%{
|
||||
key: :transparency,
|
||||
label: "MRF transparency",
|
||||
type: :boolean,
|
||||
description:
|
||||
"Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
|
||||
},
|
||||
%{
|
||||
key: :transparency_exclusions,
|
||||
label: "MRF transparency exclusions",
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
|
||||
suggestions: [
|
||||
"exclusion.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_simple,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
|
||||
label: "MRF Simple",
|
||||
type: :group,
|
||||
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"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_activity_expiration,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
|
||||
label: "MRF Activity Expiration Policy",
|
||||
type: :group,
|
||||
description: "Adds automatic expiration to all local activities",
|
||||
children: [
|
||||
%{
|
||||
key: :days,
|
||||
type: :integer,
|
||||
description: "Default global expiration time for all local activities (in days)",
|
||||
suggestions: [90, 365]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_subchain,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
|
||||
label: "MRF Subchain",
|
||||
type: :group,
|
||||
description:
|
||||
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
|
||||
" All criteria are configured as a map of regular expressions to lists of policy modules.",
|
||||
children: [
|
||||
%{
|
||||
key: :match_actor,
|
||||
type: {:map, {:list, :string}},
|
||||
description: "Matches a series of regular expressions against the actor field",
|
||||
suggestions: [
|
||||
%{
|
||||
~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_rejectnonpublic,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
|
||||
description: "RejectNonPublic drops posts with non-public visibility settings.",
|
||||
label: "MRF Reject Non Public",
|
||||
type: :group,
|
||||
children: [
|
||||
%{
|
||||
key: :allow_followersonly,
|
||||
label: "Allow followers-only",
|
||||
type: :boolean,
|
||||
description: "Whether to allow followers-only posts"
|
||||
},
|
||||
%{
|
||||
key: :allow_direct,
|
||||
type: :boolean,
|
||||
description: "Whether to allow direct messages"
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_hellthread,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
|
||||
label: "MRF Hellthread",
|
||||
type: :group,
|
||||
description: "Block messages with excessive user mentions",
|
||||
children: [
|
||||
%{
|
||||
key: :delist_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the message gets removed from timelines and" <>
|
||||
"disables notifications. Set to 0 to disable.",
|
||||
suggestions: [10]
|
||||
},
|
||||
%{
|
||||
key: :reject_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
|
||||
suggestions: [20]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_keyword,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
|
||||
label: "MRF Keyword",
|
||||
type: :group,
|
||||
description:
|
||||
"Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
|
||||
children: [
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message being rejected.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :replace,
|
||||
type: {:list, :tuple},
|
||||
description: """
|
||||
**Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
|
||||
**Replacement**: a string. Leaving the field empty is permitted.
|
||||
"""
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_mention,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
|
||||
label: "MRF Mention",
|
||||
type: :group,
|
||||
description: "Block messages which mention a specific user",
|
||||
children: [
|
||||
%{
|
||||
key: :actors,
|
||||
type: {:list, :string},
|
||||
description: "A list of actors for which any post mentioning them will be dropped",
|
||||
suggestions: ["actor1", "actor2"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_vocabulary,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
|
||||
label: "MRF Vocabulary",
|
||||
type: :group,
|
||||
description: "Filter messages which belong to certain activity vocabularies",
|
||||
children: [
|
||||
%{
|
||||
key: :accept,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
},
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
}
|
||||
]
|
||||
},
|
||||
# %{
|
||||
# group: :pleroma,
|
||||
# key: :mrf_user_allowlist,
|
||||
# tab: :mrf,
|
||||
# related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
|
||||
# type: :map,
|
||||
# description:
|
||||
# "The keys in this section are the domain names that the policy should apply to." <>
|
||||
# " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID",
|
||||
# suggestions: [
|
||||
# %{"example.org" => ["https://example.org/users/admin"]}
|
||||
# ]
|
||||
# ]
|
||||
# },
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :media_proxy,
|
||||
|
@ -3159,22 +2866,6 @@ config :pleroma, :config_description, [
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_normalize_markup,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
|
||||
label: "MRF Normalize Markup",
|
||||
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
|
||||
type: :group,
|
||||
children: [
|
||||
%{
|
||||
key: :scrub_policy,
|
||||
type: :module,
|
||||
suggestions: [Pleroma.HTML.Scrubber.Default]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: Pleroma.User,
|
||||
|
@ -3364,33 +3055,6 @@ config :pleroma, :config_description, [
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf_object_age,
|
||||
tab: :mrf,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
|
||||
label: "MRF Object Age",
|
||||
type: :group,
|
||||
description:
|
||||
"Rejects or delists posts based on their timestamp deviance from your server's clock.",
|
||||
children: [
|
||||
%{
|
||||
key: :threshold,
|
||||
type: :integer,
|
||||
description: "Required age (in seconds) of a post before actions are taken.",
|
||||
suggestions: [172_800]
|
||||
},
|
||||
%{
|
||||
key: :actions,
|
||||
type: {:list, :atom},
|
||||
description:
|
||||
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
|
||||
"`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
|
||||
"`:reject` rejects the message entirely",
|
||||
suggestions: [:delist, :strip_followers, :reject]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :modules,
|
||||
|
|
|
@ -116,6 +116,10 @@ The modified chat message
|
|||
This will return a list of chats that you have been involved in, sorted by their
|
||||
last update (so new chats will be at the top).
|
||||
|
||||
Parameters:
|
||||
|
||||
- with_muted: Include chats from muted users (boolean).
|
||||
|
||||
Returned data:
|
||||
|
||||
```json
|
||||
|
|
|
@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas
|
|||
## Timelines
|
||||
|
||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||
|
||||
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
|
||||
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
|
||||
|
||||
Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
|
||||
|
||||
## Statuses
|
||||
|
||||
- `visibility`: has an additional possible value `list`
|
||||
|
@ -125,12 +129,30 @@ The `type` value is `pleroma:emoji_reaction`. Has these fields:
|
|||
- `account`: The account of the user who reacted
|
||||
- `status`: The status that was reacted on
|
||||
|
||||
### ChatMention Notification (not default)
|
||||
|
||||
This notification has to be requested explicitly.
|
||||
|
||||
The `type` value is `pleroma:chat_mention`
|
||||
|
||||
- `account`: The account who sent the message
|
||||
- `chat_message`: The chat message
|
||||
|
||||
### Report Notification (not default)
|
||||
|
||||
This notification has to be requested explicitly.
|
||||
|
||||
The `type` value is `pleroma:report`
|
||||
|
||||
- `account`: The account who reported
|
||||
- `report`: The report
|
||||
|
||||
## GET `/api/v1/notifications`
|
||||
|
||||
Accepts additional parameters:
|
||||
|
||||
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
|
||||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:chat_mention`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
|
||||
|
||||
## DELETE `/api/v1/notifications/destroy_multiple`
|
||||
|
||||
|
@ -249,6 +271,12 @@ Has these additional fields under the `pleroma` object:
|
|||
|
||||
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
||||
|
||||
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
||||
|
||||
## User muting and thread muting
|
||||
|
||||
Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
|
||||
|
||||
## Not implemented
|
||||
|
||||
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
# Generate release environment file
|
||||
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl release_env gen
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.release_env gen
|
||||
```
|
136
docs/configuration/howto_ejabberd.md
Normal file
136
docs/configuration/howto_ejabberd.md
Normal file
|
@ -0,0 +1,136 @@
|
|||
# Configuring Ejabberd (XMPP Server) to use Pleroma for authentication
|
||||
|
||||
If you want to give your Pleroma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
|
||||
|
||||
In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully.
|
||||
|
||||
Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `PLEROMA_HOST` and `PLEROMA_PORT`, if necessary.
|
||||
|
||||
```bash
|
||||
cp pleroma_ejabberd_auth.py /etc/ejabberd/pleroma_ejabberd_auth.py
|
||||
chown ejabberd /etc/ejabberd/pleroma_ejabberd_auth.py
|
||||
chmod 700 /etc/ejabberd/pleroma_ejabberd_auth.py
|
||||
```
|
||||
|
||||
Set external auth params in ejabberd.yaml file:
|
||||
|
||||
```bash
|
||||
auth_method: [external]
|
||||
extauth_program: "python3 /etc/ejabberd/pleroma_ejabberd_auth.py"
|
||||
extauth_instances: 3
|
||||
auth_use_cache: false
|
||||
```
|
||||
|
||||
Restart / reload your ejabberd service.
|
||||
|
||||
After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials.
|
||||
|
||||
|
||||
```python
|
||||
import sys
|
||||
import struct
|
||||
import http.client
|
||||
from base64 import b64encode
|
||||
import logging
|
||||
|
||||
|
||||
PLEROMA_HOST = "127.0.0.1"
|
||||
PLEROMA_PORT = "4000"
|
||||
AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials"
|
||||
USER_ENDPOINT = "/api/v1/accounts"
|
||||
LOGFILE = "/var/log/ejabberd/pleroma_auth.log"
|
||||
|
||||
logging.basicConfig(filename=LOGFILE, level=logging.INFO)
|
||||
|
||||
|
||||
# Pleroma functions
|
||||
def create_connection():
|
||||
return http.client.HTTPConnection(PLEROMA_HOST, PLEROMA_PORT)
|
||||
|
||||
|
||||
def verify_credentials(user: str, password: str) -> bool:
|
||||
user_pass_b64 = b64encode("{}:{}".format(
|
||||
user, password).encode('utf-8')).decode("ascii")
|
||||
params = {}
|
||||
headers = {
|
||||
"Authorization": "Basic {}".format(user_pass_b64)
|
||||
}
|
||||
|
||||
try:
|
||||
conn = create_connection()
|
||||
conn.request("GET", AUTH_ENDPOINT, params, headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
if response.status == 200:
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.info("Can not connect: %s", str(e))
|
||||
return False
|
||||
|
||||
|
||||
def does_user_exist(user: str) -> bool:
|
||||
conn = create_connection()
|
||||
conn.request("GET", "{}/{}".format(USER_ENDPOINT, user))
|
||||
|
||||
response = conn.getresponse()
|
||||
if response.status == 200:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def auth(username: str, server: str, password: str) -> bool:
|
||||
return verify_credentials(username, password)
|
||||
|
||||
|
||||
def isuser(username, server):
|
||||
return does_user_exist(username)
|
||||
|
||||
|
||||
def read():
|
||||
(pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8'))
|
||||
pkt = sys.stdin.read(pkt_size)
|
||||
cmd = pkt.split(':')[0]
|
||||
if cmd == 'auth':
|
||||
username, server, password = pkt.split(':', 3)[1:]
|
||||
write(auth(username, server, password))
|
||||
elif cmd == 'isuser':
|
||||
username, server = pkt.split(':', 2)[1:]
|
||||
write(isuser(username, server))
|
||||
elif cmd == 'setpass':
|
||||
# u, s, p = pkt.split(':', 3)[1:]
|
||||
write(False)
|
||||
elif cmd == 'tryregister':
|
||||
# u, s, p = pkt.split(':', 3)[1:]
|
||||
write(False)
|
||||
elif cmd == 'removeuser':
|
||||
# u, s = pkt.split(':', 2)[1:]
|
||||
write(False)
|
||||
elif cmd == 'removeuser3':
|
||||
# u, s, p = pkt.split(':', 3)[1:]
|
||||
write(False)
|
||||
else:
|
||||
write(False)
|
||||
|
||||
|
||||
def write(result):
|
||||
if result:
|
||||
sys.stdout.write('\x00\x02\x00\x01')
|
||||
else:
|
||||
sys.stdout.write('\x00\x02\x00\x00')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.info("Starting pleroma ejabberd auth daemon...")
|
||||
while True:
|
||||
try:
|
||||
read()
|
||||
except Exception as e:
|
||||
logging.info(
|
||||
"Error while processing data from ejabberd %s", str(e))
|
||||
pass
|
||||
|
||||
```
|
66
docs/configuration/optimizing_beam.md
Normal file
66
docs/configuration/optimizing_beam.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Optimizing the BEAM
|
||||
|
||||
Pleroma is built upon the Erlang/OTP VM known as BEAM. The BEAM VM is highly optimized for latency, but this has drawbacks in environments without dedicated hardware. One of the tricks used by the BEAM VM is [busy waiting](https://en.wikipedia.org/wiki/Busy_waiting). This allows the application to pretend to be busy working so the OS kernel does not pause the application process and switch to another process waiting for the CPU to execute its workload. It does this by spinning for a period of time which inflates the apparent CPU usage of the application so it is immediately ready to execute another task. This can be observed with utilities like **top(1)** which will show consistently high CPU usage for the process. Switching between procesess is a rather expensive operation and also clears CPU caches further affecting latency and performance. The goal of busy waiting is to avoid this penalty.
|
||||
|
||||
This strategy is very successful in making a performant and responsive application, but is not desirable on Virtual Machines or hardware with few CPU cores. Pleroma instances are often deployed on the same server as the required PostgreSQL database which can lead to situations where the Pleroma application is holding the CPU in a busy-wait loop and as a result the database cannot process requests in a timely manner. The fewer CPUs available, the more this problem is exacerbated. The latency is further amplified by the OS being installed on a Virtual Machine as the Hypervisor uses CPU time-slicing to pause the entire OS and switch between other tasks.
|
||||
|
||||
More adventurous admins can be creative with CPU affinity (e.g., *taskset* for Linux and *cpuset* on FreeBSD) to pin processes to specific CPUs and eliminate much of this contention. The most important advice is to run as few processes as possible on your server to achieve the best performance. Even idle background processes can occasionally create [software interrupts](https://en.wikipedia.org/wiki/Interrupt) and take attention away from the executing process creating latency spikes and invalidation of the CPU caches as they must be cleared when switching between processes for security.
|
||||
|
||||
Please only change these settings if you are experiencing issues or really know what you are doing. In general, there's no need to change these settings.
|
||||
|
||||
## VPS Provider Recommendations
|
||||
|
||||
### Good
|
||||
|
||||
* Hetzner Cloud
|
||||
|
||||
### Bad
|
||||
|
||||
* AWS (known to use burst scheduling)
|
||||
|
||||
|
||||
## Example configurations
|
||||
|
||||
Tuning the BEAM requires you provide a config file normally called [vm.args](http://erlang.org/doc/man/erl.html#emulator-flags). If you are using systemd to manage the service you can modify the unit file as such:
|
||||
|
||||
`ExecStart=/usr/bin/elixir --erl '-args_file /opt/pleroma/config/vm.args' -S /usr/bin/mix phx.server`
|
||||
|
||||
Check your OS documentation to adopt a similar strategy on other platforms.
|
||||
|
||||
### Virtual Machine and/or few CPU cores
|
||||
|
||||
Disable the busy-waiting. This should generally only be done if you're on a platform that does burst scheduling, like AWS.
|
||||
|
||||
**vm.args:**
|
||||
|
||||
```
|
||||
+sbwt none
|
||||
+sbwtdcpu none
|
||||
+sbwtdio none
|
||||
```
|
||||
|
||||
### Dedicated Hardware
|
||||
|
||||
Enable more busy waiting, increase the internal maximum limit of BEAM processes and ports. You can use this if you run on dedicated hardware, but it is not necessary.
|
||||
|
||||
**vm.args:**
|
||||
|
||||
```
|
||||
+P 16777216
|
||||
+Q 16777216
|
||||
+K true
|
||||
+A 128
|
||||
+sbt db
|
||||
+sbwt very_long
|
||||
+swt very_low
|
||||
+sub true
|
||||
+Mulmbcs 32767
|
||||
+Mumbcgs 1
|
||||
+Musmbcs 2047
|
||||
```
|
||||
|
||||
## Additional Reading
|
||||
|
||||
* [WhatsApp: Scaling to Millions of Simultaneous Connections](https://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf)
|
||||
* [Preemptive Scheduling and Spinlocks](https://www.uio.no/studier/emner/matnat/ifi/nedlagte-emner/INF3150/h03/annet/slides/preemptive.pdf)
|
||||
* [The Curious Case of BEAM CPU Usage](https://stressgrid.com/blog/beam_cpu_usage/)
|
23
docs/dev.md
23
docs/dev.md
|
@ -21,3 +21,26 @@ This document contains notes and guidelines for Pleroma developers.
|
|||
## Auth-related configuration, OAuth consumer mode etc.
|
||||
|
||||
See `Authentication` section of [the configuration cheatsheet](configuration/cheatsheet.md#authentication).
|
||||
|
||||
## MRF policies descriptions
|
||||
|
||||
If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns map with special structure.
|
||||
|
||||
Example:
|
||||
|
||||
```elixir
|
||||
%{
|
||||
key: :mrf_activity_expiration,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
|
||||
label: "MRF Activity Expiration Policy",
|
||||
description: "Adds automatic expiration to all local activities",
|
||||
children: [
|
||||
%{
|
||||
key: :days,
|
||||
type: :integer,
|
||||
description: "Default global expiration time for all local activities (in days)",
|
||||
suggestions: [90, 365]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
|
@ -182,7 +182,6 @@ sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.se
|
|||
```
|
||||
|
||||
* Edit the service file and make sure that all paths fit your installation
|
||||
* Check that `EnvironmentFile` contains the correct path to the env file. Or generate the env file: `sudo -Hu pleroma mix pleroma.release_env gen`
|
||||
* Enable and start `pleroma.service`:
|
||||
|
||||
```shell
|
||||
|
|
|
@ -149,9 +149,6 @@ chown -R pleroma /etc/pleroma
|
|||
# Run the config generator
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql"
|
||||
|
||||
# Run the environment file generator.
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl release_env gen"
|
||||
|
||||
# Create the postgres database
|
||||
su postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql"
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ pidfile="/var/run/pleroma.pid"
|
|||
directory=/opt/pleroma
|
||||
healthcheck_delay=60
|
||||
healthcheck_timer=30
|
||||
export $(cat /opt/pleroma/config/pleroma.env)
|
||||
|
||||
: ${pleroma_port:-4000}
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ Environment="MIX_ENV=prod"
|
|||
Environment="HOME=/var/lib/pleroma"
|
||||
; Path to the folder containing the Pleroma installation.
|
||||
WorkingDirectory=/opt/pleroma
|
||||
; Path to the environment file. the file contains RELEASE_COOKIE and etc
|
||||
EnvironmentFile=/opt/pleroma/config/pleroma.env
|
||||
; Path to the Mix binary.
|
||||
ExecStart=/usr/bin/mix phx.server
|
||||
|
||||
|
|
|
@ -36,9 +36,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
listen_port: :string,
|
||||
strip_uploads: :string,
|
||||
anonymize_uploads: :string,
|
||||
dedupe_uploads: :string,
|
||||
skip_release_env: :boolean,
|
||||
release_env_file: :string
|
||||
dedupe_uploads: :string
|
||||
],
|
||||
aliases: [
|
||||
o: :output,
|
||||
|
@ -243,24 +241,6 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
|
||||
write_robots_txt(static_dir, indexable, template_dir)
|
||||
|
||||
if Keyword.get(options, :skip_release_env, false) do
|
||||
shell_info("""
|
||||
Release environment file is skip. Please generate the release env file before start.
|
||||
`MIX_ENV=#{Mix.env()} mix pleroma.release_env gen`
|
||||
""")
|
||||
else
|
||||
shell_info("Generation the environment file:")
|
||||
|
||||
release_env_args =
|
||||
with path when not is_nil(path) <- Keyword.get(options, :release_env_file) do
|
||||
["gen", "--path", path]
|
||||
else
|
||||
_ -> ["gen"]
|
||||
end
|
||||
|
||||
Mix.Tasks.Pleroma.ReleaseEnv.run(release_env_args)
|
||||
end
|
||||
|
||||
shell_info(
|
||||
"\n All files successfully written! Refer to the installation instructions for your platform for next steps."
|
||||
)
|
||||
|
@ -304,7 +284,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
defp upload_filters(filters) when is_map(filters) do
|
||||
enabled_filters =
|
||||
if filters.strip do
|
||||
[Pleroma.Upload.Filter.ExifTool]
|
||||
[Pleroma.Upload.Filter.Exiftool]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.ReleaseEnv do
|
||||
use Mix.Task
|
||||
import Mix.Pleroma
|
||||
|
||||
@shortdoc "Generate Pleroma environment file."
|
||||
@moduledoc File.read!("docs/administration/CLI_tasks/release_environments.md")
|
||||
|
||||
def run(["gen" | rest]) do
|
||||
{options, [], []} =
|
||||
OptionParser.parse(
|
||||
rest,
|
||||
strict: [
|
||||
force: :boolean,
|
||||
path: :string
|
||||
],
|
||||
aliases: [
|
||||
p: :path,
|
||||
f: :force
|
||||
]
|
||||
)
|
||||
|
||||
file_path =
|
||||
get_option(
|
||||
options,
|
||||
:path,
|
||||
"Environment file path",
|
||||
"./config/pleroma.env"
|
||||
)
|
||||
|
||||
env_path = Path.expand(file_path)
|
||||
|
||||
proceed? =
|
||||
if File.exists?(env_path) do
|
||||
get_option(
|
||||
options,
|
||||
:force,
|
||||
"Environment file already exists. Do you want to overwrite the #{env_path} file? (y/n)",
|
||||
"n"
|
||||
) === "y"
|
||||
else
|
||||
true
|
||||
end
|
||||
|
||||
if proceed? do
|
||||
case do_generate(env_path) do
|
||||
{:error, reason} ->
|
||||
shell_error(
|
||||
File.Error.message(%{action: "write to file", reason: reason, path: env_path})
|
||||
)
|
||||
|
||||
_ ->
|
||||
shell_info("\nThe file generated: #{env_path}.\n")
|
||||
|
||||
shell_info("""
|
||||
WARNING: before start pleroma app please make sure to make the file read-only and non-modifiable.
|
||||
Example:
|
||||
chmod 0444 #{file_path}
|
||||
chattr +i #{file_path}
|
||||
""")
|
||||
end
|
||||
else
|
||||
shell_info("\nThe file is exist. #{env_path}.\n")
|
||||
end
|
||||
end
|
||||
|
||||
def do_generate(path) do
|
||||
content = "RELEASE_COOKIE=#{Base.encode32(:crypto.strong_rand_bytes(32))}"
|
||||
|
||||
File.mkdir_p!(Path.dirname(path))
|
||||
File.write(path, content)
|
||||
end
|
||||
end
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Activity do
|
|||
alias Pleroma.ReportNote
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
@ -153,6 +154,18 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def get_bookmark(_, _), do: nil
|
||||
|
||||
def get_report(activity_id) do
|
||||
opts = %{
|
||||
type: "Flag",
|
||||
skip_preload: true,
|
||||
preload_report_notes: true
|
||||
}
|
||||
|
||||
ActivityPub.fetch_activities_query([], opts)
|
||||
|> where(id: ^activity_id)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def change(struct, params \\ %{}) do
|
||||
struct
|
||||
|> cast(params, [:data, :recipients])
|
||||
|
|
|
@ -40,7 +40,8 @@ defmodule Pleroma.Activity.Ir.Topics do
|
|||
end
|
||||
|
||||
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
|
||||
tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
||||
tags ++
|
||||
remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
||||
end
|
||||
|
||||
defp item_creation_tags(tags, _, _) do
|
||||
|
@ -55,9 +56,19 @@ defmodule Pleroma.Activity.Ir.Topics do
|
|||
|
||||
defp hashtags_to_topics(_), do: []
|
||||
|
||||
defp remote_topics(%{local: true}), do: []
|
||||
|
||||
defp remote_topics(%{actor: actor}) when is_binary(actor),
|
||||
do: ["public:remote:" <> URI.parse(actor).host]
|
||||
|
||||
defp remote_topics(_), do: []
|
||||
|
||||
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
|
||||
|
||||
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
|
||||
|
||||
defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
|
||||
do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
|
||||
|
||||
defp attachment_topics(_object, _act), do: ["public:media"]
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
|
|||
def new do
|
||||
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
||||
|
||||
case Tesla.get(endpoint <> "/new") do
|
||||
case Pleroma.HTTP.get(endpoint <> "/new") do
|
||||
{:error, _} ->
|
||||
%{error: :kocaptcha_service_unavailable}
|
||||
|
||||
|
|
|
@ -11,7 +11,11 @@ defmodule Pleroma.Docs.JSON do
|
|||
|
||||
@spec compile :: :ok
|
||||
def compile do
|
||||
:persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(@raw_descriptions))
|
||||
descriptions =
|
||||
Pleroma.Web.ActivityPub.MRF.config_descriptions()
|
||||
|> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end)
|
||||
|
||||
:persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(descriptions))
|
||||
end
|
||||
|
||||
@spec compiled_descriptions :: Map.t()
|
||||
|
|
|
@ -18,10 +18,6 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
Keyword.get(instance_config(), :notify_email, instance_config()[:email])
|
||||
end
|
||||
|
||||
defp user_url(user) do
|
||||
Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id)
|
||||
end
|
||||
|
||||
def test_email(mail_to \\ nil) do
|
||||
html_body = """
|
||||
<h3>Instance Test Email</h3>
|
||||
|
@ -69,8 +65,8 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
end
|
||||
|
||||
html_body = """
|
||||
<p>Reported by: <a href="#{user_url(reporter)}">#{reporter.nickname}</a></p>
|
||||
<p>Reported Account: <a href="#{user_url(account)}">#{account.nickname}</a></p>
|
||||
<p>Reported by: <a href="#{reporter.ap_id}">#{reporter.nickname}</a></p>
|
||||
<p>Reported Account: <a href="#{account.ap_id}">#{account.nickname}</a></p>
|
||||
#{comment_html}
|
||||
#{statuses_html}
|
||||
<p>
|
||||
|
@ -86,7 +82,7 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
|
||||
def new_unapproved_registration(to, account) do
|
||||
html_body = """
|
||||
<p>New account for review: <a href="#{user_url(account)}">@#{account.nickname}</a></p>
|
||||
<p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
|
||||
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
|
||||
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
|
||||
"""
|
||||
|
|
|
@ -594,7 +594,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
|
||||
defp download_archive(url, sha) do
|
||||
with {:ok, %{body: archive}} <- Tesla.get(url) do
|
||||
with {:ok, %{body: archive}} <- Pleroma.HTTP.get(url) do
|
||||
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
|
||||
{:ok, archive}
|
||||
else
|
||||
|
@ -617,7 +617,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
|
||||
defp update_sha_and_save_metadata(pack, data) do
|
||||
with {:ok, %{body: zip}} <- Tesla.get(data[:"fallback-src"]),
|
||||
with {:ok, %{body: zip}} <- Pleroma.HTTP.get(data[:"fallback-src"]),
|
||||
:ok <- validate_has_all_files(pack, zip) do
|
||||
fallback_sha = :sha256 |> :crypto.hash(zip) |> Base.encode16()
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Instances do
|
|||
defdelegate reachable?(url_or_host), to: @adapter
|
||||
defdelegate set_reachable(url_or_host), to: @adapter
|
||||
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
||||
defdelegate get_consistently_unreachable(), to: @adapter
|
||||
|
||||
def set_consistently_unreachable(url_or_host),
|
||||
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||
|
|
|
@ -119,6 +119,17 @@ defmodule Pleroma.Instances.Instance do
|
|||
|
||||
def set_unreachable(_, _), do: {:error, nil}
|
||||
|
||||
def get_consistently_unreachable do
|
||||
reachability_datetime_threshold = Instances.reachability_datetime_threshold()
|
||||
|
||||
from(i in Instance,
|
||||
where: ^reachability_datetime_threshold > i.unreachable_since,
|
||||
order_by: i.unreachable_since,
|
||||
select: {i.host, i.unreachable_since}
|
||||
)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
defp parse_datetime(datetime) when is_binary(datetime) do
|
||||
NaiveDateTime.from_iso8601(datetime)
|
||||
end
|
||||
|
|
|
@ -70,6 +70,7 @@ defmodule Pleroma.Notification do
|
|||
move
|
||||
pleroma:chat_mention
|
||||
pleroma:emoji_reaction
|
||||
pleroma:report
|
||||
reblog
|
||||
}
|
||||
|
||||
|
@ -367,7 +368,7 @@ defmodule Pleroma.Notification do
|
|||
end
|
||||
|
||||
def create_notifications(%Activity{data: %{"type" => type}} = activity, options)
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact"] do
|
||||
when type in ["Follow", "Like", "Announce", "Move", "EmojiReact", "Flag"] do
|
||||
do_create_notifications(activity, options)
|
||||
end
|
||||
|
||||
|
@ -410,6 +411,9 @@ defmodule Pleroma.Notification do
|
|||
"EmojiReact" ->
|
||||
"pleroma:emoji_reaction"
|
||||
|
||||
"Flag" ->
|
||||
"pleroma:report"
|
||||
|
||||
# Compatibility with old reactions
|
||||
"EmojiReaction" ->
|
||||
"pleroma:emoji_reaction"
|
||||
|
@ -467,7 +471,7 @@ defmodule Pleroma.Notification do
|
|||
def get_notified_from_activity(activity, local_only \\ true)
|
||||
|
||||
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
|
||||
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do
|
||||
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact", "Flag"] do
|
||||
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
|
||||
|
||||
potential_receivers =
|
||||
|
@ -503,6 +507,10 @@ defmodule Pleroma.Notification do
|
|||
[object_id]
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(%{data: %{"type" => "Flag"}}) do
|
||||
User.all_superusers() |> Enum.map(fn user -> user.ap_id end)
|
||||
end
|
||||
|
||||
def get_potential_receiver_ap_ids(activity) do
|
||||
[]
|
||||
|> Utils.maybe_notify_to_recipients(activity)
|
||||
|
|
|
@ -232,9 +232,25 @@ defmodule Pleroma.Object.Fetcher do
|
|||
|> sign_fetch(id, date)
|
||||
|
||||
case HTTP.get(id, headers) do
|
||||
{:ok, %{body: body, status: code}} when code in 200..299 ->
|
||||
{:ok, %{body: body, status: code, headers: headers}} when code in 200..299 ->
|
||||
case List.keyfind(headers, "content-type", 0) do
|
||||
{_, content_type} ->
|
||||
case Plug.Conn.Utils.media_type(content_type) do
|
||||
{:ok, "application", "activity+json", _} ->
|
||||
{:ok, body}
|
||||
|
||||
{:ok, "application", "ld+json",
|
||||
%{"profile" => "https://www.w3.org/ns/activitystreams"}} ->
|
||||
{:ok, body}
|
||||
|
||||
_ ->
|
||||
{:error, {:content_type, content_type}}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, {:content_type, nil}}
|
||||
end
|
||||
|
||||
{:ok, %{status: code}} when code in [404, 410] ->
|
||||
{:error, "Object has been deleted"}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ defmodule Pleroma.Stats do
|
|||
|
||||
@impl true
|
||||
def init(_args) do
|
||||
if Pleroma.Config.get(:env) == :test, do: :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
||||
{:ok, nil, {:continue, :calculate_stats}}
|
||||
end
|
||||
|
||||
|
@ -32,11 +31,6 @@ defmodule Pleroma.Stats do
|
|||
GenServer.call(__MODULE__, :force_update)
|
||||
end
|
||||
|
||||
@doc "Performs collect stats"
|
||||
def do_collect do
|
||||
GenServer.cast(__MODULE__, :run_update)
|
||||
end
|
||||
|
||||
@doc "Returns stats data"
|
||||
@spec get_stats() :: %{
|
||||
domain_count: non_neg_integer(),
|
||||
|
@ -111,7 +105,11 @@ defmodule Pleroma.Stats do
|
|||
@impl true
|
||||
def handle_continue(:calculate_stats, _) do
|
||||
stats = calculate_stat_data()
|
||||
|
||||
unless Pleroma.Config.get(:env) == :test do
|
||||
Process.send_after(self(), :run_update, @interval)
|
||||
end
|
||||
|
||||
{:noreply, stats}
|
||||
end
|
||||
|
||||
|
@ -126,13 +124,6 @@ defmodule Pleroma.Stats do
|
|||
{:reply, state, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast(:run_update, _state) do
|
||||
new_stats = calculate_stat_data()
|
||||
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:run_update, _) do
|
||||
new_stats = calculate_stat_data()
|
||||
|
|
|
@ -1324,14 +1324,48 @@ defmodule Pleroma.User do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec mute(User.t(), User.t(), boolean()) ::
|
||||
@spec mute(User.t(), User.t(), map()) ::
|
||||
{:ok, list(UserRelationship.t())} | {:error, String.t()}
|
||||
def mute(%User{} = muter, %User{} = mutee, notifications? \\ true) do
|
||||
add_to_mutes(muter, mutee, notifications?)
|
||||
def mute(%User{} = muter, %User{} = mutee, params \\ %{}) do
|
||||
notifications? = Map.get(params, :notifications, true)
|
||||
expires_in = Map.get(params, :expires_in, 0)
|
||||
|
||||
with {:ok, user_mute} <- UserRelationship.create_mute(muter, mutee),
|
||||
{:ok, user_notification_mute} <-
|
||||
(notifications? && UserRelationship.create_notification_mute(muter, mutee)) ||
|
||||
{:ok, nil} do
|
||||
if expires_in > 0 do
|
||||
Pleroma.Workers.MuteExpireWorker.enqueue(
|
||||
"unmute_user",
|
||||
%{"muter_id" => muter.id, "mutee_id" => mutee.id},
|
||||
schedule_in: expires_in
|
||||
)
|
||||
end
|
||||
|
||||
{:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
|
||||
end
|
||||
end
|
||||
|
||||
def unmute(%User{} = muter, %User{} = mutee) do
|
||||
remove_from_mutes(muter, mutee)
|
||||
with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee),
|
||||
{:ok, user_notification_mute} <-
|
||||
UserRelationship.delete_notification_mute(muter, mutee) do
|
||||
{:ok, [user_mute, user_notification_mute]}
|
||||
end
|
||||
end
|
||||
|
||||
def unmute(muter_id, mutee_id) do
|
||||
with {:muter, %User{} = muter} <- {:muter, User.get_by_id(muter_id)},
|
||||
{:mutee, %User{} = mutee} <- {:mutee, User.get_by_id(mutee_id)} do
|
||||
unmute(muter, mutee)
|
||||
else
|
||||
{who, result} = error ->
|
||||
Logger.warn(
|
||||
"User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}"
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def subscribe(%User{} = subscriber, %User{} = target) do
|
||||
|
@ -2320,23 +2354,6 @@ defmodule Pleroma.User do
|
|||
UserRelationship.delete_block(user, blocked)
|
||||
end
|
||||
|
||||
defp add_to_mutes(%User{} = user, %User{} = muted_user, notifications?) do
|
||||
with {:ok, user_mute} <- UserRelationship.create_mute(user, muted_user),
|
||||
{:ok, user_notification_mute} <-
|
||||
(notifications? && UserRelationship.create_notification_mute(user, muted_user)) ||
|
||||
{:ok, nil} do
|
||||
{:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
|
||||
end
|
||||
end
|
||||
|
||||
defp remove_from_mutes(user, %User{} = muted_user) do
|
||||
with {:ok, user_mute} <- UserRelationship.delete_mute(user, muted_user),
|
||||
{:ok, user_notification_mute} <-
|
||||
UserRelationship.delete_notification_mute(user, muted_user) do
|
||||
{:ok, [user_mute, user_notification_mute]}
|
||||
end
|
||||
end
|
||||
|
||||
def set_invisible(user, invisible) do
|
||||
params = %{invisible: invisible}
|
||||
|
||||
|
|
|
@ -937,16 +937,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_muted_reblogs(query, _), do: query
|
||||
|
||||
defp restrict_instance(query, %{instance: instance}) do
|
||||
users =
|
||||
defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do
|
||||
from(
|
||||
u in User,
|
||||
select: u.ap_id,
|
||||
where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
|
||||
activity in query,
|
||||
where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance)
|
||||
)
|
||||
|> Repo.all()
|
||||
|
||||
from(activity in query, where: activity.actor in ^users)
|
||||
end
|
||||
|
||||
defp restrict_instance(query, _), do: query
|
||||
|
|
|
@ -3,7 +3,62 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
require Logger
|
||||
|
||||
@mrf_config_descriptions [
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :mrf,
|
||||
tab: :mrf,
|
||||
label: "MRF",
|
||||
type: :group,
|
||||
description: "General MRF settings",
|
||||
children: [
|
||||
%{
|
||||
key: :policies,
|
||||
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}
|
||||
},
|
||||
%{
|
||||
key: :transparency,
|
||||
label: "MRF transparency",
|
||||
type: :boolean,
|
||||
description:
|
||||
"Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
|
||||
},
|
||||
%{
|
||||
key: :transparency_exclusions,
|
||||
label: "MRF transparency exclusions",
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
|
||||
suggestions: [
|
||||
"exclusion.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@default_description %{
|
||||
label: "",
|
||||
description: ""
|
||||
}
|
||||
|
||||
@required_description_keys [:key, :related_policy]
|
||||
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
related_policy: String.t(),
|
||||
label: String.t(),
|
||||
description: String.t()
|
||||
}
|
||||
@optional_callbacks config_description: 0
|
||||
|
||||
def filter(policies, %{} = message) do
|
||||
policies
|
||||
|
@ -51,8 +106,6 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
|
||||
end
|
||||
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
|
||||
def describe(policies) do
|
||||
{:ok, policy_configs} =
|
||||
policies
|
||||
|
@ -82,4 +135,41 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
end
|
||||
|
||||
def describe, do: get_policies() |> describe()
|
||||
|
||||
def config_descriptions do
|
||||
Pleroma.Web.ActivityPub.MRF
|
||||
|> Pleroma.Docs.Generator.list_behaviour_implementations()
|
||||
|> config_descriptions()
|
||||
end
|
||||
|
||||
def config_descriptions(policies) do
|
||||
Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc ->
|
||||
if function_exported?(policy, :config_description, 0) do
|
||||
description =
|
||||
@default_description
|
||||
|> Map.merge(policy.config_description)
|
||||
|> Map.put(:group, :pleroma)
|
||||
|> Map.put(:tab, :mrf)
|
||||
|> Map.put(:type, :group)
|
||||
|
||||
if Enum.all?(@required_description_keys, &Map.has_key?(description, &1)) do
|
||||
[description | acc]
|
||||
else
|
||||
Logger.warn(
|
||||
"#{policy} config description doesn't have one or all required keys #{
|
||||
inspect(@required_description_keys)
|
||||
}"
|
||||
)
|
||||
|
||||
acc
|
||||
end
|
||||
else
|
||||
Logger.debug(
|
||||
"#{policy} is excluded from config descriptions, because does not implement `config_description/0` method."
|
||||
)
|
||||
|
||||
acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,4 +40,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
|
|||
_ -> Map.put(activity, "expires_at", expires_at)
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_activity_expiration,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
|
||||
label: "MRF Activity Expiration Policy",
|
||||
description: "Adds automatic expiration to all local activities",
|
||||
children: [
|
||||
%{
|
||||
key: :days,
|
||||
type: :integer,
|
||||
description: "Default global expiration time for all local activities (in days)",
|
||||
suggestions: [90, 365]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -97,4 +97,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
|||
@impl true
|
||||
def describe,
|
||||
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_hellthread,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
|
||||
label: "MRF Hellthread",
|
||||
description: "Block messages with excessive user mentions",
|
||||
children: [
|
||||
%{
|
||||
key: :delist_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the message gets removed from timelines and" <>
|
||||
"disables notifications. Set to 0 to disable.",
|
||||
suggestions: [10]
|
||||
},
|
||||
%{
|
||||
key: :reject_threshold,
|
||||
type: :integer,
|
||||
description:
|
||||
"Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
|
||||
suggestions: [20]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -126,4 +126,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
|
||||
{:ok, %{mrf_keyword: mrf_keyword}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_keyword,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
|
||||
label: "MRF Keyword",
|
||||
description:
|
||||
"Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
|
||||
children: [
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message being rejected.
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal,
|
||||
type: {:list, :string},
|
||||
description: """
|
||||
A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
|
||||
|
||||
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
""",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :replace,
|
||||
type: {:list, :tuple},
|
||||
description: """
|
||||
**Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||
|
||||
**Replacement**: a string. Leaving the field empty is permitted.
|
||||
"""
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,4 +25,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
|
|||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_mention,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
|
||||
label: "MRF Mention",
|
||||
description: "Block messages which mention a specific user",
|
||||
children: [
|
||||
%{
|
||||
key: :actors,
|
||||
type: {:list, :string},
|
||||
description: "A list of actors for which any post mentioning them will be dropped",
|
||||
suggestions: ["actor1", "actor2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
|||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "object" => child_object} = object) do
|
||||
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
|
||||
|
||||
|
@ -22,5 +23,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
|||
|
||||
def filter(object), do: {:ok, object}
|
||||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_normalize_markup,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
|
||||
label: "MRF Normalize Markup",
|
||||
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
|
||||
children: [
|
||||
%{
|
||||
key: :scrub_policy,
|
||||
type: :module,
|
||||
suggestions: [Pleroma.HTML.Scrubber.Default]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -106,4 +106,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
|||
|
||||
{:ok, %{mrf_object_age: mrf_object_age}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_object_age,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
|
||||
label: "MRF Object Age",
|
||||
description:
|
||||
"Rejects or delists posts based on their timestamp deviance from your server's clock.",
|
||||
children: [
|
||||
%{
|
||||
key: :threshold,
|
||||
type: :integer,
|
||||
description: "Required age (in seconds) of a post before actions are taken.",
|
||||
suggestions: [172_800]
|
||||
},
|
||||
%{
|
||||
key: :actions,
|
||||
type: {:list, :atom},
|
||||
description:
|
||||
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
|
||||
"`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
|
||||
"`:reject` rejects the message entirely",
|
||||
suggestions: [:delist, :strip_followers, :reject]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,4 +48,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
|||
@impl true
|
||||
def describe,
|
||||
do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_rejectnonpublic,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
|
||||
description: "RejectNonPublic drops posts with non-public visibility settings.",
|
||||
label: "MRF Reject Non Public",
|
||||
children: [
|
||||
%{
|
||||
key: :allow_followersonly,
|
||||
label: "Allow followers-only",
|
||||
type: :boolean,
|
||||
description: "Whether to allow followers-only posts"
|
||||
},
|
||||
%{
|
||||
key: :allow_direct,
|
||||
type: :boolean,
|
||||
description: "Whether to allow direct messages"
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -244,4 +244,78 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
{:ok, %{mrf_simple: mrf_simple}}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_simple,
|
||||
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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,4 +39,28 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
|||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_subchain,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
|
||||
label: "MRF Subchain",
|
||||
description:
|
||||
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
|
||||
" All criteria are configured as a map of regular expressions to lists of policy modules.",
|
||||
children: [
|
||||
%{
|
||||
key: :match_actor,
|
||||
type: {:map, {:list, :string}},
|
||||
description: "Matches a series of regular expressions against the actor field",
|
||||
suggestions: [
|
||||
%{
|
||||
~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,4 +41,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
|||
|
||||
{:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
|
||||
end
|
||||
|
||||
# TODO: change way of getting settings on `lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex:18` to use `hosts` subkey
|
||||
# @impl true
|
||||
# def config_description do
|
||||
# %{
|
||||
# key: :mrf_user_allowlist,
|
||||
# related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
|
||||
# description: "Accept-list of users from specified instances",
|
||||
# children: [
|
||||
# %{
|
||||
# key: :hosts,
|
||||
# type: :map,
|
||||
# description:
|
||||
# "The keys in this section are the domain names that the policy should apply to." <>
|
||||
# " Each key should be assigned a list of users that should be allowed " <>
|
||||
# "through by their ActivityPub ID",
|
||||
# suggestions: [%{"example.org" => ["https://example.org/users/admin"]}]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
|||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Undo", "object" => child_message} = message) do
|
||||
with {:ok, _} <- filter(child_message) do
|
||||
{:ok, message}
|
||||
|
@ -36,6 +37,33 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
|||
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
def describe,
|
||||
do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :mrf_vocabulary,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
|
||||
label: "MRF Vocabulary",
|
||||
description: "Filter messages which belong to certain activity vocabularies",
|
||||
children: [
|
||||
%{
|
||||
key: :accept,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
},
|
||||
%{
|
||||
key: :reject,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
|
||||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
|||
field(:type, :string)
|
||||
field(:mediaType, :string, default: "application/octet-stream")
|
||||
field(:name, :string)
|
||||
field(:blurhash, :string)
|
||||
|
||||
embeds_many :url, UrlObjectValidator, primary_key: false do
|
||||
field(:type, :string)
|
||||
|
@ -41,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
|||
|> fix_url()
|
||||
|
||||
struct
|
||||
|> cast(data, [:type, :mediaType, :name])
|
||||
|> cast(data, [:type, :mediaType, :name, :blurhash])
|
||||
|> cast_embed(:url, with: &url_changeset/2)
|
||||
|> validate_inclusion(:type, ~w[Link Document Audio Image Video])
|
||||
|> validate_required([:type, :mediaType, :url])
|
||||
|
|
|
@ -187,7 +187,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
||||
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
||||
|
||||
if in_reply_to = object.data["inReplyTo"] do
|
||||
if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
|
||||
Object.increase_replies_count(in_reply_to)
|
||||
end
|
||||
|
||||
|
|
|
@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
}
|
||||
|> Maps.put_if_present("mediaType", media_type)
|
||||
|> Maps.put_if_present("name", data["name"])
|
||||
|> Maps.put_if_present("blurhash", data["blurhash"])
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
|||
end
|
||||
|
||||
def show(conn, %{id: id}) do
|
||||
with %Activity{} = report <- Activity.get_by_id(id) do
|
||||
with %Activity{} = report <- Activity.get_report(id) do
|
||||
render(conn, "show.json", Report.extract_report_info(report))
|
||||
else
|
||||
_ -> {:error, :not_found}
|
||||
|
|
|
@ -262,6 +262,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
:query,
|
||||
%Schema{allOf: [BooleanLike], default: true},
|
||||
"Mute notifications in addition to statuses? Defaults to `true`."
|
||||
),
|
||||
Operation.parameter(
|
||||
:expires_in,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 0},
|
||||
"Expire the mute in `expires_in` seconds. Default 0 for infinity"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
|
@ -723,10 +729,17 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||
nullable: true,
|
||||
description: "Mute notifications in addition to statuses? Defaults to true.",
|
||||
default: true
|
||||
},
|
||||
expires_in: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"notifications" => true
|
||||
"notifications" => true,
|
||||
"expires_in" => 86_400
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
|||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Chat
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
|
||||
|
||||
|
@ -132,7 +133,10 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
|||
tags: ["chat"],
|
||||
summary: "Get a list of chats that you participated in",
|
||||
operationId: "ChatController.index",
|
||||
parameters: pagination_params(),
|
||||
parameters: [
|
||||
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
|
||||
| pagination_params()
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("The chats of the user", "application/json", chats_response())
|
||||
},
|
||||
|
|
|
@ -193,6 +193,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
"mention",
|
||||
"pleroma:emoji_reaction",
|
||||
"pleroma:chat_mention",
|
||||
"pleroma:report",
|
||||
"move",
|
||||
"follow_request"
|
||||
],
|
||||
|
@ -206,6 +207,8 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
|
|||
- `poll` - A poll you have voted in or created has ended
|
||||
- `move` - Someone moved their account
|
||||
- `pleroma:emoji_reaction` - Someone reacted with emoji to your status
|
||||
- `pleroma:chat_mention` - Someone mentioned you in a chat message
|
||||
- `pleroma:report` - Someone was reported
|
||||
"""
|
||||
}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["PleromaInstances"],
|
||||
summary: "Instances federation status",
|
||||
description: "Information about instances deemed unreachable by the server",
|
||||
operationId: "PleromaInstances.show",
|
||||
responses: %{
|
||||
200 => Operation.response("PleromaInstances", "application/json", pleroma_instances())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def pleroma_instances do
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
unreachable: %Schema{
|
||||
type: :object,
|
||||
properties: %{hostname: %Schema{type: :string, format: :"date-time"}}
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"unreachable" => %{"consistently-unreachable.name" => "2020-10-14 22:07:58.216473"}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
|
@ -223,7 +223,27 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||
security: [%{"oAuth" => ["write:mutes"]}],
|
||||
description: "Do not receive notifications for the thread that this status is part of.",
|
||||
operationId: "StatusController.mute_conversation",
|
||||
parameters: [id_param()],
|
||||
requestBody:
|
||||
request_body("Parameters", %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
expires_in: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "Expire the mute in `expires_in` seconds. Default 0 for infinity",
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}),
|
||||
parameters: [
|
||||
id_param(),
|
||||
Operation.parameter(
|
||||
:expires_in,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 0},
|
||||
"Expire the mute in `expires_in` seconds. Default 0 for infinity"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => status_response(),
|
||||
400 => Operation.response("Error", "application/json", ApiError)
|
||||
|
|
|
@ -59,6 +59,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [
|
||||
local_param(),
|
||||
instance_param(),
|
||||
only_media_param(),
|
||||
with_muted_param(),
|
||||
exclude_visibilities_param(),
|
||||
|
@ -158,6 +159,15 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||
)
|
||||
end
|
||||
|
||||
defp instance_param do
|
||||
Operation.parameter(
|
||||
:instance,
|
||||
:query,
|
||||
%Schema{type: :string},
|
||||
"Show only statuses from the given domain"
|
||||
)
|
||||
end
|
||||
|
||||
defp with_muted_param do
|
||||
Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users")
|
||||
end
|
||||
|
|
|
@ -28,8 +28,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
|||
},
|
||||
votes_count: %Schema{
|
||||
type: :integer,
|
||||
nullable: true,
|
||||
description: "How many votes have been received. Number, or null if `multiple` is false."
|
||||
description: "How many votes have been received. Number."
|
||||
},
|
||||
voters_count: %Schema{
|
||||
type: :integer,
|
||||
description: "How many unique accounts have voted. Number."
|
||||
},
|
||||
voted: %Schema{
|
||||
type: :boolean,
|
||||
|
@ -61,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
|||
expired: true,
|
||||
multiple: false,
|
||||
votes_count: 10,
|
||||
voters_count: nil,
|
||||
voters_count: 10,
|
||||
voted: true,
|
||||
own_votes: [
|
||||
1
|
||||
|
|
|
@ -454,20 +454,46 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
end
|
||||
|
||||
def add_mute(user, activity) do
|
||||
def add_mute(user, activity, params \\ %{}) do
|
||||
expires_in = Map.get(params, :expires_in, 0)
|
||||
|
||||
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]),
|
||||
_ <- Pleroma.Notification.mark_context_as_read(user, activity.data["context"]) do
|
||||
if expires_in > 0 do
|
||||
Pleroma.Workers.MuteExpireWorker.enqueue(
|
||||
"unmute_conversation",
|
||||
%{"user_id" => user.id, "activity_id" => activity.id},
|
||||
schedule_in: expires_in
|
||||
)
|
||||
end
|
||||
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
|
||||
end
|
||||
end
|
||||
|
||||
def remove_mute(user, activity) do
|
||||
def remove_mute(%User{} = user, %Activity{} = activity) do
|
||||
ThreadMute.remove_mute(user.id, activity.data["context"])
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
def remove_mute(user_id, activity_id) do
|
||||
with {:user, %User{} = user} <- {:user, User.get_by_id(user_id)},
|
||||
{:activity, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)} do
|
||||
remove_mute(user, activity)
|
||||
else
|
||||
{what, result} = error ->
|
||||
Logger.warn(
|
||||
"CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{
|
||||
activity_id
|
||||
}"
|
||||
)
|
||||
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def thread_muted?(%User{id: user_id}, %{data: %{"context" => context}})
|
||||
when is_binary(context) do
|
||||
ThreadMute.exists?(user_id, context)
|
||||
|
|
|
@ -37,10 +37,11 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
|
||||
tags = build_tags(conn, params)
|
||||
preloads = preload_data(conn, params)
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", tags <> preloads)
|
||||
|> String.replace("<!--server-generated-meta-->", tags <> preloads <> title)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
@ -54,10 +55,11 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
def redirector_with_preload(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
preloads = preload_data(conn, params)
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", preloads)
|
||||
|> String.replace("<!--server-generated-meta-->", preloads <> title)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|
|||
|
||||
def activity_content(_), do: ""
|
||||
|
||||
def activity_context(activity), do: activity.data["context"]
|
||||
def activity_context(activity), do: escape(activity.data["context"])
|
||||
|
||||
def attachment_href(attachment) do
|
||||
attachment["url"]
|
||||
|
|
|
@ -394,7 +394,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
@doc "POST /api/v1/accounts/:id/mute"
|
||||
def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
|
|
|
@ -284,9 +284,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
end
|
||||
|
||||
@doc "POST /api/v1/statuses/:id/mute"
|
||||
def mute_conversation(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
def mute_conversation(%{assigns: %{user: user}, body_params: params} = conn, %{id: id}) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
{:ok, activity} <- CommonAPI.add_mute(user, activity) do
|
||||
{:ok, activity} <- CommonAPI.add_mute(user, activity, params) do
|
||||
try_render(conn, "show.json", activity: activity, for: user, as: :activity)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -111,6 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|> Map.put(:blocking_user, user)
|
||||
|> Map.put(:muting_user, user)
|
||||
|> Map.put(:reply_filtering_user, user)
|
||||
|> Map.put(:instance, params[:instance])
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
conn
|
||||
|
|
|
@ -33,8 +33,15 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
|||
end
|
||||
|
||||
activity = Activity.get_by_id_with_object(last_activity_id)
|
||||
# Conversations return all users except the current user.
|
||||
users = Enum.reject(participation.recipients, &(&1.id == user.id))
|
||||
|
||||
# Conversations return all users except the current user,
|
||||
# except when the current user is the only participant
|
||||
users =
|
||||
if length(participation.recipients) > 1 do
|
||||
Enum.reject(participation.recipients, &(&1.id == user.id))
|
||||
else
|
||||
participation.recipients
|
||||
end
|
||||
|
||||
%{
|
||||
id: participation.id |> to_string(),
|
||||
|
@ -43,7 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
|||
last_status:
|
||||
render(StatusView, "show.json",
|
||||
activity: activity,
|
||||
direct_conversation_id: participation.id
|
||||
direct_conversation_id: participation.id,
|
||||
for: user
|
||||
)
|
||||
}
|
||||
end
|
||||
|
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.AdminAPI.Report
|
||||
alias Pleroma.Web.AdminAPI.ReportView
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.NotificationView
|
||||
|
@ -118,11 +120,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||
"pleroma:chat_mention" ->
|
||||
put_chat_message(response, activity, reading_user, status_render_opts)
|
||||
|
||||
"pleroma:report" ->
|
||||
put_report(response, activity)
|
||||
|
||||
type when type in ["follow", "follow_request"] ->
|
||||
response
|
||||
end
|
||||
end
|
||||
|
||||
defp put_report(response, activity) do
|
||||
report_render = ReportView.render("show.json", Report.extract_report_info(activity))
|
||||
|
||||
Map.put(response, :report, report_render)
|
||||
end
|
||||
|
||||
defp put_emoji(response, activity) do
|
||||
Map.put(response, :emoji, activity.data["content"])
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.PollView do
|
|||
expired: expired,
|
||||
multiple: multiple,
|
||||
votes_count: votes_count,
|
||||
voters_count: (multiple || nil) && voters_count(object),
|
||||
voters_count: voters_count(object),
|
||||
options: options,
|
||||
voted: voted?(params),
|
||||
emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
|
||||
|
|
|
@ -435,7 +435,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
text_url: href,
|
||||
type: type,
|
||||
description: attachment["name"],
|
||||
pleroma: %{mime_type: media_type}
|
||||
pleroma: %{mime_type: media_type},
|
||||
blurhash: attachment["blurhash"]
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
||||
alias Pleroma.Web.PleromaAPI.ChatView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
import Ecto.Query
|
||||
|
@ -121,9 +120,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
) do
|
||||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
|
||||
{_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
render(conn, "show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -141,33 +138,30 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
|||
end
|
||||
end
|
||||
|
||||
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
|
||||
blocked_ap_ids = User.blocked_users_ap_ids(user)
|
||||
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do
|
||||
exclude_users =
|
||||
User.blocked_users_ap_ids(user) ++
|
||||
if params[:with_muted], do: [], else: User.muted_users_ap_ids(user)
|
||||
|
||||
chats =
|
||||
Chat.for_user_query(user_id)
|
||||
|> where([c], c.recipient not in ^blocked_ap_ids)
|
||||
user_id
|
||||
|> Chat.for_user_query()
|
||||
|> where([c], c.recipient not in ^exclude_users)
|
||||
|> Repo.all()
|
||||
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("index.json", chats: chats)
|
||||
render(conn, "index.json", chats: chats)
|
||||
end
|
||||
|
||||
def create(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with %User{ap_id: recipient} <- User.get_cached_by_id(id),
|
||||
{:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
render(conn, "show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
|
||||
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
|
||||
conn
|
||||
|> put_view(ChatView)
|
||||
|> render("show.json", chat: chat)
|
||||
render(conn, "show.json", chat: chat)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.InstancesController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Instances
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesOperation
|
||||
|
||||
def show(conn, _params) do
|
||||
unreachable =
|
||||
Instances.get_consistently_unreachable()
|
||||
|> Map.new(fn {host, date} -> {host, to_string(date)} end)
|
||||
|
||||
json(conn, %{"unreachable" => unreachable})
|
||||
end
|
||||
end
|
|
@ -398,6 +398,7 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:api)
|
||||
get("/accounts/:id/scrobbles", ScrobbleController, :index)
|
||||
get("/federation_status", InstancesController, :show)
|
||||
end
|
||||
|
||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
|
|
|
@ -57,6 +57,15 @@ defmodule Pleroma.Web.Streamer do
|
|||
{:ok, "hashtag:" <> tag}
|
||||
end
|
||||
|
||||
# Allow remote instance streams.
|
||||
def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||
{:ok, "public:remote:" <> instance}
|
||||
end
|
||||
|
||||
def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||
{:ok, "public:remote:media:" <> instance}
|
||||
end
|
||||
|
||||
# Expand user streams.
|
||||
def get_topic(
|
||||
stream,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<link href="<%= activity_context(@activity) %>" rel="ostatus:conversation"/>
|
||||
|
||||
<%= if @data["summary"] do %>
|
||||
<summary><%= @data["summary"] %></summary>
|
||||
<summary><%= escape(@data["summary"]) %></summary>
|
||||
<% end %>
|
||||
|
||||
<%= if @activity.local do %>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<link rel="ostatus:conversation"><%= activity_context(@activity) %></link>
|
||||
|
||||
<%= if @data["summary"] do %>
|
||||
<description><%= @data["summary"] %></description>
|
||||
<description><%= escape(@data["summary"]) %></description>
|
||||
<% end %>
|
||||
|
||||
<%= if @activity.local do %>
|
||||
|
|
20
lib/pleroma/workers/mute_expire_worker.ex
Normal file
20
lib/pleroma/workers/mute_expire_worker.ex
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.MuteExpireWorker do
|
||||
use Pleroma.Workers.WorkerHelper, queue: "mute_expire"
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Job{args: %{"op" => "unmute_user", "muter_id" => muter_id, "mutee_id" => mutee_id}}) do
|
||||
Pleroma.User.unmute(muter_id, mutee_id)
|
||||
:ok
|
||||
end
|
||||
|
||||
def perform(%Job{
|
||||
args: %{"op" => "unmute_conversation", "user_id" => user_id, "activity_id" => activity_id}
|
||||
}) do
|
||||
Pleroma.Web.CommonAPI.remove_mute(user_id, activity_id)
|
||||
:ok
|
||||
end
|
||||
end
|
4
mix.exs
4
mix.exs
|
@ -134,7 +134,7 @@ defmodule Pleroma.Mixfile do
|
|||
{:cachex, "~> 3.2"},
|
||||
{:poison, "~> 3.0", override: true},
|
||||
{:tesla,
|
||||
git: "https://github.com/teamon/tesla/",
|
||||
git: "https://github.com/teamon/tesla.git",
|
||||
ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30",
|
||||
override: true},
|
||||
{:castore, "~> 0.1"},
|
||||
|
@ -196,7 +196,7 @@ defmodule Pleroma.Mixfile do
|
|||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||
{:restarter, path: "./restarter"},
|
||||
{:majic,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic", branch: "develop"},
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", branch: "develop"},
|
||||
{:open_api_spex,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
||||
ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"},
|
||||
|
|
4
mix.lock
4
mix.lock
|
@ -66,7 +66,7 @@
|
|||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
||||
"linkify": {:hex, :linkify, "0.2.0", "2518bbbea21d2caa9d372424e1ad845b640c6630e2d016f1bd1f518f9ebcca28", [:mix], [], "hexpm", "b8ca8a68b79e30b7938d6c996085f3db14939f29538a59ca5101988bb7f917f6"},
|
||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]},
|
||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]},
|
||||
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
|
||||
|
@ -115,7 +115,7 @@
|
|||
"swoosh": {:hex, :swoosh, "1.0.6", "6765e334c67dacabe721f0d701c7e5a6f06e4595c90df6f91e73ebd54d555833", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "7c50ef78e4acfd1cbd4907dc1fa87b5540675a6be9dc979d04890f49d7ec1830"},
|
||||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
||||
"tesla": {:git, "https://github.com/teamon/tesla/", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]},
|
||||
"tesla": {:git, "https://github.com/teamon/tesla.git", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]},
|
||||
"timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
"tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"},
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
defmodule Pleroma.Repo.Migrations.AddPleromaReportTypeToEnumForNotifications do
|
||||
use Ecto.Migration
|
||||
|
||||
@disable_ddl_transaction true
|
||||
|
||||
def up do
|
||||
"""
|
||||
alter type notification_type add value 'pleroma:report'
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:notifications) do
|
||||
modify(:type, :string)
|
||||
end
|
||||
|
||||
"""
|
||||
delete from notifications where type = 'pleroma:report'
|
||||
"""
|
||||
|> 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'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter table notifications
|
||||
alter column type type notification_type using (type::notification_type)
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
defmodule Pleroma.Repo.Migrations.RemovePurgeExpiredActivityWorkerFromObanConfig do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
with %Pleroma.ConfigDB{} = config <-
|
||||
Pleroma.ConfigDB.get_by_params(%{group: :pleroma, key: Oban}),
|
||||
crontab when is_list(crontab) <- config.value[:crontab],
|
||||
index when is_integer(index) <-
|
||||
Enum.find_index(crontab, fn {_, worker} ->
|
||||
worker == Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker
|
||||
end) do
|
||||
updated_value = Keyword.put(config.value, :crontab, List.delete_at(crontab, index))
|
||||
|
||||
config
|
||||
|> Ecto.Changeset.change(value: updated_value)
|
||||
|> Pleroma.Repo.update()
|
||||
end
|
||||
end
|
||||
end
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.77b1644622e3bae24b6b.css rel=stylesheet><link href=/static/fontello.1600365488745.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.90c4af83c1ae68f4cd95.js></script><script type=text/javascript src=/static/js/app.826c44232e0a76bbd9ba.js></script></body></html>
|
||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.77b1644622e3bae24b6b.css rel=stylesheet><link href=/static/fontello.1600365488745.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.90c4af83c1ae68f4cd95.js></script><script type=text/javascript src=/static/js/app.826c44232e0a76bbd9ba.js></script></body></html>
|
||||
|
|
19
test/fixtures/modules/good_mrf.ex
vendored
Normal file
19
test/fixtures/modules/good_mrf.ex
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule Fixtures.Modules.GoodMRF do
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@impl true
|
||||
def filter(a), do: {:ok, a}
|
||||
|
||||
@impl true
|
||||
def describe, do: %{}
|
||||
|
||||
@impl true
|
||||
def config_description do
|
||||
%{
|
||||
key: :good_mrf,
|
||||
related_policy: "Fixtures.Modules.GoodMRF",
|
||||
label: "Good MRF",
|
||||
description: "Some description"
|
||||
}
|
||||
end
|
||||
end
|
26
test/fixtures/spoofed-object.json
vendored
Normal file
26
test/fixtures/spoofed-object.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://patch.cx/schemas/litepub-0.1.jsonld",
|
||||
{
|
||||
"@language": "und"
|
||||
}
|
||||
],
|
||||
"actor": "https://patch.cx/users/rin",
|
||||
"attachment": [],
|
||||
"attributedTo": "https://patch.cx/users/rin",
|
||||
"cc": [
|
||||
"https://patch.cx/users/rin/followers"
|
||||
],
|
||||
"content": "Oracle Corporation (NYSE: ORCL) today announced that it has signed a definitive merger agreement to acquire Pleroma AG (FRA: PLA), for $26.50 per share (approximately $10.3 billion). The transaction has been approved by the boards of directors of both companies and should close by early January.",
|
||||
"context": "https://patch.cx/contexts/spoof",
|
||||
"id": "https://patch.cx/objects/spoof",
|
||||
"published": "2020-10-23T18:02:06.038856Z",
|
||||
"sensitive": false,
|
||||
"summary": "Oracle buys Pleroma",
|
||||
"tag": [],
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"type": "Note"
|
||||
}
|
|
@ -5,8 +5,6 @@
|
|||
defmodule Mix.Tasks.Pleroma.InstanceTest do
|
||||
use ExUnit.Case
|
||||
|
||||
@release_env_file "./test/pleroma.test.env"
|
||||
|
||||
setup do
|
||||
File.mkdir_p!(tmp_path())
|
||||
|
||||
|
@ -18,8 +16,6 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do
|
|||
File.rm_rf(Path.join(static_dir, "robots.txt"))
|
||||
end
|
||||
|
||||
if File.exists?(@release_env_file), do: File.rm_rf(@release_env_file)
|
||||
|
||||
Pleroma.Config.put([:instance, :static_dir], static_dir)
|
||||
end)
|
||||
|
||||
|
@ -73,9 +69,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do
|
|||
"--dedupe-uploads",
|
||||
"n",
|
||||
"--anonymize-uploads",
|
||||
"n",
|
||||
"--release-env-file",
|
||||
@release_env_file
|
||||
"n"
|
||||
])
|
||||
end
|
||||
|
||||
|
@ -94,12 +88,9 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do
|
|||
assert generated_config =~ "password: \"dbpass\""
|
||||
assert generated_config =~ "configurable_from_database: true"
|
||||
assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]"
|
||||
assert generated_config =~ "filters: [Pleroma.Upload.Filter.ExifTool]"
|
||||
assert generated_config =~ "filters: [Pleroma.Upload.Filter.Exiftool]"
|
||||
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
|
||||
assert File.exists?(Path.expand("./test/instance/static/robots.txt"))
|
||||
assert File.exists?(@release_env_file)
|
||||
|
||||
assert File.read!(@release_env_file) =~ ~r/^RELEASE_COOKIE=.*/
|
||||
end
|
||||
|
||||
defp generated_setup_psql do
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.ReleaseEnvTest do
|
||||
use ExUnit.Case
|
||||
import ExUnit.CaptureIO, only: [capture_io: 1]
|
||||
|
||||
@path "config/pleroma.test.env"
|
||||
|
||||
def do_clean do
|
||||
if File.exists?(@path) do
|
||||
File.rm_rf(@path)
|
||||
end
|
||||
end
|
||||
|
||||
setup do
|
||||
do_clean()
|
||||
on_exit(fn -> do_clean() end)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "generate pleroma.env" do
|
||||
assert capture_io(fn ->
|
||||
Mix.Tasks.Pleroma.ReleaseEnv.run(["gen", "--path", @path, "--force"])
|
||||
end) =~ "The file generated"
|
||||
|
||||
assert File.read!(@path) =~ "RELEASE_COOKIE="
|
||||
end
|
||||
end
|
|
@ -97,6 +97,20 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
|
|||
|
||||
refute Enum.member?(topics, "hashtag:2")
|
||||
end
|
||||
|
||||
test "non-local action produces public:remote topic", %{activity: activity} do
|
||||
activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
|
||||
topics = Topics.get_activity_topics(activity)
|
||||
|
||||
assert Enum.member?(topics, "public:remote:lain.com")
|
||||
end
|
||||
|
||||
test "local action doesn't produce public:remote topic", %{activity: activity} do
|
||||
activity = %{activity | local: true, actor: "https://lain.com/users/lain"}
|
||||
topics = Topics.get_activity_topics(activity)
|
||||
|
||||
refute Enum.member?(topics, "public:remote:lain.com")
|
||||
end
|
||||
end
|
||||
|
||||
describe "public visibility create events with attachments" do
|
||||
|
@ -128,6 +142,13 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
|
|||
|
||||
refute Enum.member?(topics, "public:local:media")
|
||||
end
|
||||
|
||||
test "non-local action produces public:remote:media topic", %{activity: activity} do
|
||||
activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
|
||||
topics = Topics.get_activity_topics(activity)
|
||||
|
||||
assert Enum.member?(topics, "public:remote:media:lain.com")
|
||||
end
|
||||
end
|
||||
|
||||
describe "non-public visibility" do
|
||||
|
|
|
@ -19,8 +19,8 @@ defmodule Pleroma.Emails.AdminEmailTest do
|
|||
AdminEmail.report(to_user, reporter, account, [%{name: "Test", id: "12"}], "Test comment")
|
||||
|
||||
status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, "12")
|
||||
reporter_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, reporter.id)
|
||||
account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
|
||||
reporter_url = reporter.ap_id
|
||||
account_url = account.ap_id
|
||||
|
||||
assert res.to == [{to_user.name, to_user.email}]
|
||||
assert res.from == {config[:name], config[:notify_email]}
|
||||
|
@ -54,7 +54,7 @@ defmodule Pleroma.Emails.AdminEmailTest do
|
|||
|
||||
res = AdminEmail.new_unapproved_registration(to_user, account)
|
||||
|
||||
account_url = Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, account.id)
|
||||
account_url = account.ap_id
|
||||
|
||||
assert res.to == [{to_user.name, to_user.email}]
|
||||
assert res.from == {config[:name], config[:notify_email]}
|
||||
|
|
|
@ -49,6 +49,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
test "allows public streams without authentication" do
|
||||
assert {:ok, _} = start_socket("?stream=public")
|
||||
assert {:ok, _} = start_socket("?stream=public:local")
|
||||
assert {:ok, _} = start_socket("?stream=public:remote&instance=lain.com")
|
||||
assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
|
||||
end
|
||||
|
||||
|
|
|
@ -32,6 +32,19 @@ defmodule Pleroma.NotificationTest do
|
|||
refute {:ok, [nil]} == Notification.create_notifications(activity)
|
||||
end
|
||||
|
||||
test "creates a notification for a report" do
|
||||
reporting_user = insert(:user)
|
||||
reported_user = insert(:user)
|
||||
{:ok, moderator_user} = insert(:user) |> User.admin_api_update(%{is_moderator: true})
|
||||
|
||||
{:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
|
||||
|
||||
{:ok, [notification]} = Notification.create_notifications(activity)
|
||||
|
||||
assert notification.user_id == moderator_user.id
|
||||
assert notification.type == "pleroma:report"
|
||||
end
|
||||
|
||||
test "creates a notification for an emoji reaction" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
@ -229,7 +242,7 @@ defmodule Pleroma.NotificationTest do
|
|||
muter = insert(:user)
|
||||
muted = insert(:user)
|
||||
|
||||
{:ok, _user_relationships} = User.mute(muter, muted, false)
|
||||
{:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
|
||||
|
||||
{:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
|
||||
|
||||
|
@ -1015,7 +1028,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
test "it returns notifications for muted user without notifications", %{user: user} do
|
||||
muted = insert(:user)
|
||||
{:ok, _user_relationships} = User.mute(user, muted, false)
|
||||
{:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
|
||||
|
||||
|
|
|
@ -21,6 +21,17 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
%{method: :get, url: "https://mastodon.example.org/users/userisgone404"} ->
|
||||
%Tesla.Env{status: 404}
|
||||
|
||||
%{
|
||||
method: :get,
|
||||
url:
|
||||
"https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/json"}],
|
||||
body: File.read!("test/fixtures/spoofed-object.json")
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
@ -34,19 +45,22 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
%{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json")
|
||||
body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
%{method: :get, url: "https://social.sakamoto.gq/users/eal"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/fetch_mocks/eal.json")
|
||||
body: File.read!("test/fixtures/fetch_mocks/eal.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
%{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json")
|
||||
body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
%{method: :get, url: "https://busshi.moe/users/tuxcrafting"} ->
|
||||
|
@ -132,6 +146,13 @@ defmodule Pleroma.Object.FetcherTest do
|
|||
"http://mastodon.example.org/@admin/99541947525187367"
|
||||
)
|
||||
end
|
||||
|
||||
test "it does not fetch a spoofed object uploaded on an instance as an attachment" do
|
||||
assert {:error, _} =
|
||||
Fetcher.fetch_object_from_id(
|
||||
"https://patch.cx/media/03ca3c8b4ac3ddd08bf0f84be7885f2f88de0f709112131a22d83650819e36c2.json"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "implementation quirks" do
|
||||
|
|
|
@ -281,7 +281,11 @@ defmodule Pleroma.ObjectTest do
|
|||
setup do
|
||||
mock(fn
|
||||
%{method: :get, url: "https://patch.cx/objects/9a172665-2bc5-452d-8428-2361d4c33b1d"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/poll_original.json")}
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_original.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
|
@ -315,7 +319,8 @@ defmodule Pleroma.ObjectTest do
|
|||
|
||||
mock_modified.(%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
|
||||
|
@ -359,7 +364,8 @@ defmodule Pleroma.ObjectTest do
|
|||
|
||||
mock_modified.(%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: 100)
|
||||
|
@ -387,7 +393,8 @@ defmodule Pleroma.ObjectTest do
|
|||
|
||||
mock_modified.(%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/poll_modified.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
})
|
||||
|
||||
updated_object = Object.get_by_id_and_maybe_refetch(object.id, interval: -1)
|
||||
|
|
|
@ -1008,6 +1008,27 @@ defmodule Pleroma.UserTest do
|
|||
assert User.muted_notifications?(user, muted_user)
|
||||
end
|
||||
|
||||
test "expiring" do
|
||||
user = insert(:user)
|
||||
muted_user = insert(:user)
|
||||
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user, %{expires_in: 60})
|
||||
assert User.mutes?(user, muted_user)
|
||||
|
||||
worker = Pleroma.Workers.MuteExpireWorker
|
||||
args = %{"op" => "unmute_user", "muter_id" => user.id, "mutee_id" => muted_user.id}
|
||||
|
||||
assert_enqueued(
|
||||
worker: worker,
|
||||
args: args
|
||||
)
|
||||
|
||||
assert :ok = perform_job(worker, args)
|
||||
|
||||
refute User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
end
|
||||
|
||||
test "it unmutes users" do
|
||||
user = insert(:user)
|
||||
muted_user = insert(:user)
|
||||
|
@ -1019,6 +1040,17 @@ defmodule Pleroma.UserTest do
|
|||
refute User.muted_notifications?(user, muted_user)
|
||||
end
|
||||
|
||||
test "it unmutes users by id" do
|
||||
user = insert(:user)
|
||||
muted_user = insert(:user)
|
||||
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user)
|
||||
{:ok, _user_mute} = User.unmute(user.id, muted_user.id)
|
||||
|
||||
refute User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
end
|
||||
|
||||
test "it mutes user without notifications" do
|
||||
user = insert(:user)
|
||||
muted_user = insert(:user)
|
||||
|
@ -1026,7 +1058,7 @@ defmodule Pleroma.UserTest do
|
|||
refute User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user, false)
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user, %{notifications: false})
|
||||
|
||||
assert User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
|
|
|
@ -1426,19 +1426,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
mock(fn env ->
|
||||
case env.url do
|
||||
"http://localhost:4001/users/masto_hidden_counters/following" ->
|
||||
json(%{
|
||||
json(
|
||||
%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "http://localhost:4001/users/masto_hidden_counters/followers"
|
||||
})
|
||||
},
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
)
|
||||
|
||||
"http://localhost:4001/users/masto_hidden_counters/following?page=1" ->
|
||||
%Tesla.Env{status: 403, body: ""}
|
||||
|
||||
"http://localhost:4001/users/masto_hidden_counters/followers" ->
|
||||
json(%{
|
||||
json(
|
||||
%{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => "http://localhost:4001/users/masto_hidden_counters/following"
|
||||
})
|
||||
},
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
)
|
||||
|
||||
"http://localhost:4001/users/masto_hidden_counters/followers?page=1" ->
|
||||
%Tesla.Env{status: 403, body: ""}
|
||||
|
@ -2278,7 +2284,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
Tesla.Mock.mock(fn
|
||||
%{method: :get, url: "https://princess.cat/users/mewmew"} ->
|
||||
file = File.read!("test/fixtures/mewmew_no_name.json")
|
||||
%Tesla.Env{status: 200, body: file}
|
||||
%Tesla.Env{status: 200, body: file, headers: HttpRequestMock.activitypub_object_headers()}
|
||||
end)
|
||||
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew")
|
||||
|
|
|
@ -87,4 +87,20 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
{:ok, ^expected} = MRF.describe()
|
||||
end
|
||||
end
|
||||
|
||||
test "config_descriptions/0" do
|
||||
descriptions = MRF.config_descriptions()
|
||||
|
||||
good_mrf = Enum.find(descriptions, fn %{key: key} -> key == :good_mrf end)
|
||||
|
||||
assert good_mrf == %{
|
||||
key: :good_mrf,
|
||||
related_policy: "Fixtures.Modules.GoodMRF",
|
||||
label: "Good MRF",
|
||||
description: "Some description",
|
||||
group: :pleroma,
|
||||
tab: :mrf,
|
||||
type: :group
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,7 +33,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
|
|||
"http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg",
|
||||
"type" => "Document",
|
||||
"name" => nil,
|
||||
"mediaType" => "image/jpeg"
|
||||
"mediaType" => "image/jpeg",
|
||||
"blurhash" => "UD9jJz~VSbR#xT$~%KtQX9R,WAs9RjWBs:of"
|
||||
}
|
||||
|
||||
{:ok, attachment} =
|
||||
|
@ -50,6 +51,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do
|
|||
] = attachment.url
|
||||
|
||||
assert attachment.mediaType == "image/jpeg"
|
||||
assert attachment.blurhash == "UD9jJz~VSbR#xT$~%KtQX9R,WAs9RjWBs:of"
|
||||
end
|
||||
|
||||
test "it handles our own uploads" do
|
||||
|
|
|
@ -60,7 +60,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do
|
|||
|
||||
Tesla.Mock.mock(fn
|
||||
%{method: :get} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/mastodon-note-object.json")}
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/mastodon-note-object.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
_user = insert(:user, local: false, ap_id: data["actor"])
|
||||
|
|
|
@ -27,6 +27,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do
|
|||
})
|
||||
|
||||
object = Object.normalize(activity)
|
||||
assert object.data["repliesCount"] == nil
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-vote.json")
|
||||
|
@ -41,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do
|
|||
assert answer_object.data["inReplyTo"] == object.data["id"]
|
||||
|
||||
new_object = Object.get_by_ap_id(object.data["id"])
|
||||
assert new_object.data["replies_count"] == object.data["replies_count"]
|
||||
assert new_object.data["repliesCount"] == nil
|
||||
|
||||
assert Enum.any?(
|
||||
new_object.data["oneOf"],
|
||||
|
|
|
@ -13,7 +13,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do
|
|||
|
||||
test "Pterotype (Wordpress Plugin) Article" do
|
||||
Tesla.Mock.mock(fn %{url: "https://wedistribute.org/wp-json/pterotype/v1/actor/-blog"} ->
|
||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json")}
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/wedistribute-user.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
data =
|
||||
|
@ -36,13 +40,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do
|
|||
%{url: "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
%{url: "https://baptiste.gelez.xyz/@/BaptisteGelez"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/baptiste.gelex.xyz-user.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
|
@ -61,7 +67,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.ArticleHandlingTest do
|
|||
Tesla.Mock.mock(fn %{url: "https://prismo.news/@mxb"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/https___prismo.news__mxb.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
|
|
|
@ -48,7 +48,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do
|
|||
%{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
|
@ -69,6 +70,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do
|
|||
"mediaType" => "audio/ogg",
|
||||
"type" => "Link",
|
||||
"name" => nil,
|
||||
"blurhash" => nil,
|
||||
"url" => [
|
||||
%{
|
||||
"href" =>
|
||||
|
|
|
@ -13,13 +13,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EventHandlingTest do
|
|||
%{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
%{url: "https://mobilizon.org/@tcit"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json")
|
||||
body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json"),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do
|
|||
"type" => "Link",
|
||||
"mediaType" => "video/mp4",
|
||||
"name" => nil,
|
||||
"blurhash" => nil,
|
||||
"url" => [
|
||||
%{
|
||||
"href" =>
|
||||
|
@ -76,6 +77,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.VideoHandlingTest do
|
|||
"type" => "Link",
|
||||
"mediaType" => "video/mp4",
|
||||
"name" => nil,
|
||||
"blurhash" => nil,
|
||||
"url" => [
|
||||
%{
|
||||
"href" =>
|
||||
|
|
|
@ -844,8 +844,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
describe "instances" do
|
||||
test "GET /instances/:instance/statuses", %{conn: conn} do
|
||||
user = insert(:user, local: false, nickname: "archaeme@archae.me")
|
||||
user2 = insert(:user, local: false, nickname: "test@test.com")
|
||||
user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme")
|
||||
user2 = insert(:user, local: false, ap_id: "https://test.com/users/test")
|
||||
insert_pair(:note_activity, user: user)
|
||||
activity = insert(:note_activity, user: user2)
|
||||
|
||||
|
|
|
@ -37,12 +37,21 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|
|||
status_ids: [activity.id]
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
|
||||
content: "this is an admin note"
|
||||
})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/api/pleroma/admin/reports/#{report_id}")
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
assert response["id"] == report_id
|
||||
|
||||
[notes] = response["notes"]
|
||||
assert notes["content"] == "this is an admin note"
|
||||
end
|
||||
|
||||
test "returns 404 when report id is invalid", %{conn: conn} do
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.CommonAPITest do
|
||||
use Pleroma.DataCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Chat
|
||||
|
@ -922,12 +922,34 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
assert CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
test "add expiring mute", %{user: user, activity: activity} do
|
||||
{:ok, _} = CommonAPI.add_mute(user, activity, %{expires_in: 60})
|
||||
assert CommonAPI.thread_muted?(user, activity)
|
||||
|
||||
worker = Pleroma.Workers.MuteExpireWorker
|
||||
args = %{"op" => "unmute_conversation", "user_id" => user.id, "activity_id" => activity.id}
|
||||
|
||||
assert_enqueued(
|
||||
worker: worker,
|
||||
args: args
|
||||
)
|
||||
|
||||
assert :ok = perform_job(worker, args)
|
||||
refute CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
test "remove mute", %{user: user, activity: activity} do
|
||||
CommonAPI.add_mute(user, activity)
|
||||
{:ok, _} = CommonAPI.remove_mute(user, activity)
|
||||
refute CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
test "remove mute by ids", %{user: user, activity: activity} do
|
||||
CommonAPI.add_mute(user, activity)
|
||||
{:ok, _} = CommonAPI.remove_mute(user.id, activity.id)
|
||||
refute CommonAPI.thread_muted?(user, activity)
|
||||
end
|
||||
|
||||
test "check that mutes can't be duplicate", %{user: user, activity: activity} do
|
||||
CommonAPI.add_mute(user, activity)
|
||||
{:error, _} = CommonAPI.add_mute(user, activity)
|
||||
|
|
|
@ -20,15 +20,26 @@ defmodule Pleroma.Web.FallbackTest do
|
|||
end
|
||||
end
|
||||
|
||||
test "GET /*path adds a title", %{conn: conn} do
|
||||
clear_config([:instance, :name], "a cool title")
|
||||
|
||||
assert conn
|
||||
|> get("/")
|
||||
|> html_response(200) =~ "<title>a cool title</title>"
|
||||
end
|
||||
|
||||
describe "preloaded data and metadata attached to" do
|
||||
test "GET /:maybe_nickname_or_id", %{conn: conn} do
|
||||
clear_config([:instance, :name], "a cool title")
|
||||
|
||||
user = insert(:user)
|
||||
user_missing = get(conn, "/foo")
|
||||
user_present = get(conn, "/#{user.nickname}")
|
||||
|
||||
assert(html_response(user_missing, 200) =~ "<!--server-generated-meta-->")
|
||||
assert html_response(user_missing, 200) =~ "<!--server-generated-meta-->"
|
||||
refute html_response(user_present, 200) =~ "<!--server-generated-meta-->"
|
||||
assert html_response(user_present, 200) =~ "initial-results"
|
||||
assert html_response(user_present, 200) =~ "<title>a cool title</title>"
|
||||
end
|
||||
|
||||
test "GET /*path", %{conn: conn} do
|
||||
|
@ -44,10 +55,13 @@ defmodule Pleroma.Web.FallbackTest do
|
|||
|
||||
describe "preloaded data is attached to" do
|
||||
test "GET /main/public", %{conn: conn} do
|
||||
clear_config([:instance, :name], "a cool title")
|
||||
|
||||
public_page = get(conn, "/main/public")
|
||||
|
||||
refute html_response(public_page, 200) =~ "<!--server-generated-meta-->"
|
||||
assert html_response(public_page, 200) =~ "initial-results"
|
||||
assert html_response(public_page, 200) =~ "<title>a cool title</title>"
|
||||
end
|
||||
|
||||
test "GET /main/all", %{conn: conn} do
|
||||
|
|
|
@ -12,16 +12,17 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
setup do: clear_config([:static_fe, :enabled], false)
|
||||
|
||||
describe "feed" do
|
||||
setup do: clear_config([:feed])
|
||||
|
||||
test "gets an atom feed", %{conn: conn} do
|
||||
setup do
|
||||
Config.put(
|
||||
[:feed, :post_title],
|
||||
%{max_length: 10, omission: "..."}
|
||||
%{max_length: 15, omission: "..."}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity)
|
||||
|
@ -29,7 +30,8 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
note =
|
||||
insert(:note,
|
||||
data: %{
|
||||
"content" => "This is :moominmamma: note ",
|
||||
"content" => "This & this is :moominmamma: note ",
|
||||
"source" => "This & this is :moominmamma: note ",
|
||||
"attachment" => [
|
||||
%{
|
||||
"url" => [
|
||||
|
@ -37,7 +39,9 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
]
|
||||
}
|
||||
],
|
||||
"inReplyTo" => activity.data["id"]
|
||||
"inReplyTo" => activity.data["id"],
|
||||
"context" => "2hu & as",
|
||||
"summary" => "2hu & as"
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -48,7 +52,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
insert(:note,
|
||||
user: user,
|
||||
data: %{
|
||||
"content" => "42 This is :moominmamma: note ",
|
||||
"content" => "42 & This is :moominmamma: note ",
|
||||
"inReplyTo" => activity.data["id"]
|
||||
}
|
||||
)
|
||||
|
@ -56,6 +60,10 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
note_activity2 = insert(:note_activity, note: note2)
|
||||
object = Object.normalize(note_activity)
|
||||
|
||||
[user: user, object: object, max_id: note_activity2.id]
|
||||
end
|
||||
|
||||
test "gets an atom feed", %{conn: conn, user: user, object: object, max_id: max_id} do
|
||||
resp =
|
||||
conn
|
||||
|> put_req_header("accept", "application/atom+xml")
|
||||
|
@ -67,13 +75,15 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
|> SweetXml.parse()
|
||||
|> SweetXml.xpath(~x"//entry/title/text()"l)
|
||||
|
||||
assert activity_titles == ['42 This...', 'This is...']
|
||||
assert resp =~ object.data["content"]
|
||||
assert activity_titles == ['42 & Thi...', 'This & t...']
|
||||
assert resp =~ FeedView.escape(object.data["content"])
|
||||
assert resp =~ FeedView.escape(object.data["summary"])
|
||||
assert resp =~ FeedView.escape(object.data["context"])
|
||||
|
||||
resp =
|
||||
conn
|
||||
|> put_req_header("accept", "application/atom+xml")
|
||||
|> get("/users/#{user.nickname}/feed", %{"max_id" => note_activity2.id})
|
||||
|> get("/users/#{user.nickname}/feed", %{"max_id" => max_id})
|
||||
|> response(200)
|
||||
|
||||
activity_titles =
|
||||
|
@ -81,47 +91,10 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
|> SweetXml.parse()
|
||||
|> SweetXml.xpath(~x"//entry/title/text()"l)
|
||||
|
||||
assert activity_titles == ['This is...']
|
||||
assert activity_titles == ['This & t...']
|
||||
end
|
||||
|
||||
test "gets a rss feed", %{conn: conn} do
|
||||
Pleroma.Config.put(
|
||||
[:feed, :post_title],
|
||||
%{max_length: 10, omission: "..."}
|
||||
)
|
||||
|
||||
activity = insert(:note_activity)
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
data: %{
|
||||
"content" => "This is :moominmamma: note ",
|
||||
"attachment" => [
|
||||
%{
|
||||
"url" => [
|
||||
%{"mediaType" => "image/png", "href" => "https://pleroma.gov/image.png"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"inReplyTo" => activity.data["id"]
|
||||
}
|
||||
)
|
||||
|
||||
note_activity = insert(:note_activity, note: note)
|
||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
||||
|
||||
note2 =
|
||||
insert(:note,
|
||||
user: user,
|
||||
data: %{
|
||||
"content" => "42 This is :moominmamma: note ",
|
||||
"inReplyTo" => activity.data["id"]
|
||||
}
|
||||
)
|
||||
|
||||
note_activity2 = insert(:note_activity, note: note2)
|
||||
object = Object.normalize(note_activity)
|
||||
|
||||
test "gets a rss feed", %{conn: conn, user: user, object: object, max_id: max_id} do
|
||||
resp =
|
||||
conn
|
||||
|> put_req_header("accept", "application/rss+xml")
|
||||
|
@ -133,13 +106,15 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
|> SweetXml.parse()
|
||||
|> SweetXml.xpath(~x"//item/title/text()"l)
|
||||
|
||||
assert activity_titles == ['42 This...', 'This is...']
|
||||
assert resp =~ object.data["content"]
|
||||
assert activity_titles == ['42 & Thi...', 'This & t...']
|
||||
assert resp =~ FeedView.escape(object.data["content"])
|
||||
assert resp =~ FeedView.escape(object.data["summary"])
|
||||
assert resp =~ FeedView.escape(object.data["context"])
|
||||
|
||||
resp =
|
||||
conn
|
||||
|> put_req_header("accept", "application/rss+xml")
|
||||
|> get("/users/#{user.nickname}/feed.rss", %{"max_id" => note_activity2.id})
|
||||
|> get("/users/#{user.nickname}/feed.rss", %{"max_id" => max_id})
|
||||
|> response(200)
|
||||
|
||||
activity_titles =
|
||||
|
@ -147,7 +122,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
|||
|> SweetXml.parse()
|
||||
|> SweetXml.xpath(~x"//item/title/text()"l)
|
||||
|
||||
assert activity_titles == ['This is...']
|
||||
assert activity_titles == ['This & t...']
|
||||
end
|
||||
|
||||
test "returns 404 for a missing feed", %{conn: conn} do
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue