@@ -14,7 +14,7 @@ | |||
* Pleroma version (could be found in the "Version" tab of settings in Pleroma-FE): | |||
* Elixir version (`elixir -v` for from source installations, N/A for OTP): | |||
* Operating system: | |||
* PostgreSQL version (`postgres -V`): | |||
* PostgreSQL version (`psql -V`): | |||
### Bug description |
@@ -17,6 +17,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
<summary>API Changes</summary> | |||
- **Breaking:** Emoji API: changed methods and renamed routes. | |||
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream. | |||
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance | |||
- Mastodon API: On deletion, returns the original post text. | |||
</details> | |||
<details> | |||
@@ -51,6 +54,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
<details> | |||
<summary>API Changes</summary> | |||
- Mastodon API: Add pleroma.parents_visible field to statuses. | |||
- Mastodon API: Extended `/api/v1/instance`. | |||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`. | |||
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint. | |||
@@ -437,8 +437,6 @@ config :pleroma, Pleroma.Web.Metadata, | |||
config :pleroma, Pleroma.Web.Preload, | |||
providers: [ | |||
Pleroma.Web.Preload.Providers.Instance, | |||
Pleroma.Web.Preload.Providers.User, | |||
Pleroma.Web.Preload.Providers.Timelines, | |||
Pleroma.Web.Preload.Providers.StatusNet | |||
] | |||
@@ -40,12 +40,13 @@ config :pleroma, :config_description, [ | |||
key: :link_name, | |||
type: :boolean, | |||
description: | |||
"If enabled, a name parameter will be added to the url of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`." | |||
"If enabled, a name parameter will be added to the URL of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`." | |||
}, | |||
%{ | |||
key: :base_url, | |||
label: "Base URL", | |||
type: :string, | |||
description: "Base url for the uploads, needed if you use CDN", | |||
description: "Base URL for the uploads, needed if you use CDN", | |||
suggestions: [ | |||
"https://cdn-host.com" | |||
] | |||
@@ -58,6 +59,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :proxy_opts, | |||
label: "Proxy Options", | |||
type: :keyword, | |||
description: "Options for Pleroma.ReverseProxy", | |||
suggestions: [ | |||
@@ -85,6 +87,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :http, | |||
label: "HTTP", | |||
type: :keyword, | |||
description: "HTTP options", | |||
children: [ | |||
@@ -193,7 +196,9 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :args, | |||
type: [:string, {:list, :string}, {:list, :tuple}], | |||
description: "List of actions for the mogrify command", | |||
description: | |||
"List of actions for the mogrify command. It's possible to add self-written settings as string. " <> | |||
"For example `[\"auto-orient\", \"strip\", {\"resize\", \"3840x1080>\"}]` string will be parsed into list of the settings.", | |||
suggestions: [ | |||
"strip", | |||
"auto-orient", | |||
@@ -479,6 +484,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :uri_schemes, | |||
label: "URI Schemes", | |||
type: :group, | |||
description: "URI schemes related settings", | |||
children: [ | |||
@@ -651,17 +657,17 @@ config :pleroma, :config_description, [ | |||
key: :invites_enabled, | |||
type: :boolean, | |||
description: | |||
"Enable user invitations for admins (depends on `registrations_open` being disabled)." | |||
"Enable user invitations for admins (depends on `registrations_open` being disabled)" | |||
}, | |||
%{ | |||
key: :account_activation_required, | |||
type: :boolean, | |||
description: "Require users to confirm their emails before signing in." | |||
description: "Require users to confirm their emails before signing in" | |||
}, | |||
%{ | |||
key: :federating, | |||
type: :boolean, | |||
description: "Enable federation with other instances." | |||
description: "Enable federation with other instances" | |||
}, | |||
%{ | |||
key: :federation_incoming_replies_max_depth, | |||
@@ -679,7 +685,7 @@ config :pleroma, :config_description, [ | |||
label: "Fed. reachability timeout days", | |||
type: :integer, | |||
description: | |||
"Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.", | |||
"Timeout (in days) of each external federation target being unreachable prior to pausing federating to it", | |||
suggestions: [ | |||
7 | |||
] | |||
@@ -801,6 +807,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :safe_dm_mentions, | |||
label: "Safe DM mentions", | |||
type: :boolean, | |||
description: | |||
"If enabled, only mentions at the beginning of a post will be used to address people in direct messages." <> | |||
@@ -840,7 +847,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :skip_thread_containment, | |||
type: :boolean, | |||
description: "Skip filtering out broken threads. Default: enabled" | |||
description: "Skip filtering out broken threads. Default: enabled." | |||
}, | |||
%{ | |||
key: :limit_to_local_content, | |||
@@ -904,6 +911,7 @@ config :pleroma, :config_description, [ | |||
children: [ | |||
%{ | |||
key: :totp, | |||
label: "TOTP settings", | |||
type: :keyword, | |||
description: "TOTP settings", | |||
suggestions: [digits: 6, period: 30], | |||
@@ -920,7 +928,7 @@ config :pleroma, :config_description, [ | |||
type: :integer, | |||
suggestions: [30], | |||
description: | |||
"a period for which the TOTP code will be valid, in seconds. Defaults to 30 seconds." | |||
"A period for which the TOTP code will be valid, in seconds. Defaults to 30 seconds." | |||
} | |||
] | |||
}, | |||
@@ -934,7 +942,7 @@ config :pleroma, :config_description, [ | |||
key: :number, | |||
type: :integer, | |||
suggestions: [5], | |||
description: "number of backup codes to generate." | |||
description: "Number of backup codes to generate." | |||
}, | |||
%{ | |||
key: :length, | |||
@@ -974,6 +982,7 @@ config :pleroma, :config_description, [ | |||
group: :logger, | |||
type: :group, | |||
key: :ex_syslogger, | |||
label: "ExSyslogger", | |||
description: "ExSyslogger-related settings", | |||
children: [ | |||
%{ | |||
@@ -992,7 +1001,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :format, | |||
type: :string, | |||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".", | |||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"", | |||
suggestions: ["$metadata[$level] $message"] | |||
}, | |||
%{ | |||
@@ -1006,6 +1015,7 @@ config :pleroma, :config_description, [ | |||
group: :logger, | |||
type: :group, | |||
key: :console, | |||
label: "Console Logger", | |||
description: "Console logger settings", | |||
children: [ | |||
%{ | |||
@@ -1017,7 +1027,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :format, | |||
type: :string, | |||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".", | |||
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"", | |||
suggestions: ["$metadata[$level] $message"] | |||
}, | |||
%{ | |||
@@ -1030,6 +1040,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :quack, | |||
type: :group, | |||
label: "Quack Logger", | |||
description: "Quack-related settings", | |||
children: [ | |||
%{ | |||
@@ -1140,19 +1151,19 @@ config :pleroma, :config_description, [ | |||
key: :greentext, | |||
label: "Greentext", | |||
type: :boolean, | |||
description: "Enables green text on lines prefixed with the > character." | |||
description: "Enables green text on lines prefixed with the > character" | |||
}, | |||
%{ | |||
key: :hideFilteredStatuses, | |||
label: "Hide Filtered Statuses", | |||
type: :boolean, | |||
description: "Hides filtered statuses from timelines." | |||
description: "Hides filtered statuses from timelines" | |||
}, | |||
%{ | |||
key: :hideMutedPosts, | |||
label: "Hide Muted Posts", | |||
type: :boolean, | |||
description: "Hides muted statuses from timelines." | |||
description: "Hides muted statuses from timelines" | |||
}, | |||
%{ | |||
key: :hidePostStats, | |||
@@ -1164,7 +1175,7 @@ config :pleroma, :config_description, [ | |||
key: :hideSitename, | |||
label: "Hide Sitename", | |||
type: :boolean, | |||
description: "Hides instance name from PleromaFE banner." | |||
description: "Hides instance name from PleromaFE banner" | |||
}, | |||
%{ | |||
key: :hideUserStats, | |||
@@ -1209,14 +1220,14 @@ config :pleroma, :config_description, [ | |||
label: "NSFW Censor Image", | |||
type: :string, | |||
description: | |||
"URL of the image to use for hiding NSFW media attachments in the timeline.", | |||
"URL of the image to use for hiding NSFW media attachments in the timeline", | |||
suggestions: ["/static/img/nsfw.74818f9.png"] | |||
}, | |||
%{ | |||
key: :postContentType, | |||
label: "Post Content Type", | |||
type: {:dropdown, :atom}, | |||
description: "Default post formatting option.", | |||
description: "Default post formatting option", | |||
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"] | |||
}, | |||
%{ | |||
@@ -1245,14 +1256,14 @@ config :pleroma, :config_description, [ | |||
key: :sidebarRight, | |||
label: "Sidebar on Right", | |||
type: :boolean, | |||
description: "Change alignment of sidebar and panels to the right." | |||
description: "Change alignment of sidebar and panels to the right" | |||
}, | |||
%{ | |||
key: :showFeaturesPanel, | |||
label: "Show instance features panel", | |||
type: :boolean, | |||
description: | |||
"Enables panel displaying functionality of the instance on the About page." | |||
"Enables panel displaying functionality of the instance on the About page" | |||
}, | |||
%{ | |||
key: :showInstanceSpecificPanel, | |||
@@ -1310,7 +1321,7 @@ config :pleroma, :config_description, [ | |||
key: :mascots, | |||
type: {:keyword, :map}, | |||
description: | |||
"Keyword of mascots, each element must contain both an url and a mime_type key", | |||
"Keyword of mascots, each element must contain both an URL and a mime_type key", | |||
suggestions: [ | |||
pleroma_fox_tan: %{ | |||
url: "/images/pleroma-fox-tan-smol.png", | |||
@@ -1334,7 +1345,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :default_user_avatar, | |||
type: :string, | |||
description: "URL of the default user avatar.", | |||
description: "URL of the default user avatar", | |||
suggestions: ["/images/avi.png"] | |||
} | |||
] | |||
@@ -1344,7 +1355,7 @@ config :pleroma, :config_description, [ | |||
key: :manifest, | |||
type: :group, | |||
description: | |||
"This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE", | |||
"This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.", | |||
children: [ | |||
%{ | |||
key: :icons, | |||
@@ -1380,10 +1391,49 @@ 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: | |||
Generator.list_modules_in_dir( | |||
"lib/pleroma/web/activity_pub/mrf", | |||
"Elixir.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, | |||
label: "MRF simple", | |||
tab: :mrf, | |||
label: "MRF Simple", | |||
type: :group, | |||
description: "Message Rewrite Facility", | |||
description: "Simple ingress policies", | |||
children: [ | |||
%{ | |||
key: :media_removal, | |||
@@ -1402,7 +1452,7 @@ config :pleroma, :config_description, [ | |||
key: :federated_timeline_removal, | |||
type: {:list, :string}, | |||
description: | |||
"List of instances to remove from Federated (aka The Whole Known Network) Timeline", | |||
"List of instances to remove from the Federated (aka The Whole Known Network) Timeline", | |||
suggestions: ["example.com", "*.example.com"] | |||
}, | |||
%{ | |||
@@ -1446,14 +1496,15 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_activity_expiration, | |||
tab: :mrf, | |||
label: "MRF Activity Expiration Policy", | |||
type: :group, | |||
description: "Adds expiration to all local Create Note activities", | |||
description: "Adds automatic expiration to all local activities", | |||
children: [ | |||
%{ | |||
key: :days, | |||
type: :integer, | |||
description: "Default global expiration time for all local Create activities (in days)", | |||
description: "Default global expiration time for all local activities (in days)", | |||
suggestions: [90, 365] | |||
} | |||
] | |||
@@ -1461,7 +1512,8 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_subchain, | |||
label: "MRF subchain", | |||
tab: :mrf, | |||
label: "MRF Subchain", | |||
type: :group, | |||
description: | |||
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <> | |||
@@ -1482,9 +1534,9 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_rejectnonpublic, | |||
description: | |||
"MRF RejectNonPublic settings. RejectNonPublic drops posts with non-public visibility settings.", | |||
label: "MRF reject non public", | |||
tab: :mrf, | |||
description: "RejectNonPublic drops posts with non-public visibility settings.", | |||
label: "MRF Reject Non Public", | |||
type: :group, | |||
children: [ | |||
%{ | |||
@@ -1503,16 +1555,17 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_hellthread, | |||
label: "MRF hellthread", | |||
tab: :mrf, | |||
label: "MRF Hellthread", | |||
type: :group, | |||
description: "Block messages with too much mentions", | |||
description: "Block messages with excessive user mentions", | |||
children: [ | |||
%{ | |||
key: :delist_threshold, | |||
type: :integer, | |||
description: | |||
"Number of mentioned users after which the message gets delisted (the message can still be seen, " <> | |||
" but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable.", | |||
"Number of mentioned users after which the message gets removed from timelines and" <> | |||
"disables notifications. Set to 0 to disable.", | |||
suggestions: [10] | |||
}, | |||
%{ | |||
@@ -1527,7 +1580,8 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_keyword, | |||
label: "MRF keyword", | |||
tab: :mrf, | |||
label: "MRF Keyword", | |||
type: :group, | |||
description: "Reject or Word-Replace messages with a keyword or regex", | |||
children: [ | |||
@@ -1557,14 +1611,15 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_mention, | |||
label: "MRF mention", | |||
tab: :mrf, | |||
label: "MRF Mention", | |||
type: :group, | |||
description: "Block messages which mention a user", | |||
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.", | |||
description: "A list of actors for which any post mentioning them will be dropped", | |||
suggestions: ["actor1", "actor2"] | |||
} | |||
] | |||
@@ -1572,7 +1627,8 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_vocabulary, | |||
label: "MRF vocabulary", | |||
tab: :mrf, | |||
label: "MRF Vocabulary", | |||
type: :group, | |||
description: "Filter messages which belong to certain activity vocabularies", | |||
children: [ | |||
@@ -1580,14 +1636,14 @@ config :pleroma, :config_description, [ | |||
key: :accept, | |||
type: {:list, :string}, | |||
description: | |||
"A list of ActivityStreams terms to accept. If empty, all supported messages are accepted", | |||
"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", | |||
"A list of ActivityStreams terms to reject. If empty, no messages are rejected.", | |||
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"] | |||
} | |||
] | |||
@@ -1617,6 +1673,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :base_url, | |||
label: "Base URL", | |||
type: :string, | |||
description: | |||
"The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.", | |||
@@ -1649,6 +1706,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :proxy_opts, | |||
label: "Proxy Options", | |||
type: :keyword, | |||
description: "Options for Pleroma.ReverseProxy", | |||
suggestions: [ | |||
@@ -1676,6 +1734,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :http, | |||
label: "HTTP", | |||
type: :keyword, | |||
description: "HTTP options", | |||
children: [ | |||
@@ -1771,6 +1830,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :ip, | |||
label: "IP", | |||
type: :tuple, | |||
description: "IP address to bind to", | |||
suggestions: [{0, 0, 0, 0}] | |||
@@ -1784,7 +1844,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :dstport, | |||
type: :integer, | |||
description: "Port advertised in urls (optional, defaults to port)", | |||
description: "Port advertised in URLs (optional, defaults to port)", | |||
suggestions: [9999] | |||
} | |||
] | |||
@@ -1792,6 +1852,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :activitypub, | |||
label: "ActivityPub", | |||
type: :group, | |||
description: "ActivityPub-related settings", | |||
children: [ | |||
@@ -1814,7 +1875,7 @@ config :pleroma, :config_description, [ | |||
key: :note_replies_output_limit, | |||
type: :integer, | |||
description: | |||
"The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)." | |||
"The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)" | |||
}, | |||
%{ | |||
key: :follow_handshake_timeout, | |||
@@ -1827,6 +1888,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :http_security, | |||
label: "HTTP security", | |||
type: :group, | |||
description: "HTTP security settings", | |||
children: [ | |||
@@ -1865,7 +1927,7 @@ config :pleroma, :config_description, [ | |||
key: :report_uri, | |||
label: "Report URI", | |||
type: :string, | |||
description: "Adds the specified url to report-uri and report-to group in CSP header", | |||
description: "Adds the specified URL to report-uri and report-to group in CSP header", | |||
suggestions: ["https://example.com/report-uri"] | |||
} | |||
] | |||
@@ -1873,9 +1935,10 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :web_push_encryption, | |||
key: :vapid_details, | |||
label: "Vapid Details", | |||
type: :group, | |||
description: | |||
"Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it", | |||
"Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it.", | |||
children: [ | |||
%{ | |||
key: :subject, | |||
@@ -1942,6 +2005,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :pleroma, | |||
label: "Pleroma Admin Token", | |||
type: :group, | |||
description: | |||
"Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter", | |||
@@ -1949,7 +2013,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :admin_token, | |||
type: :string, | |||
description: "Token", | |||
description: "Admin token", | |||
suggestions: ["We recommend a secure random string or UUID"] | |||
} | |||
] | |||
@@ -2114,24 +2178,24 @@ config :pleroma, :config_description, [ | |||
key: :rich_media, | |||
type: :group, | |||
description: | |||
"If enabled the instance will parse metadata from attached links to generate link previews.", | |||
"If enabled the instance will parse metadata from attached links to generate link previews", | |||
children: [ | |||
%{ | |||
key: :enabled, | |||
type: :boolean, | |||
description: "Enables RichMedia parsing of URLs." | |||
description: "Enables RichMedia parsing of URLs" | |||
}, | |||
%{ | |||
key: :ignore_hosts, | |||
type: {:list, :string}, | |||
description: "List of hosts which will be ignored by the metadata parser.", | |||
description: "List of hosts which will be ignored by the metadata parser", | |||
suggestions: ["accounts.google.com", "xss.website"] | |||
}, | |||
%{ | |||
key: :ignore_tld, | |||
label: "Ignore TLD", | |||
type: {:list, :string}, | |||
description: "List TLDs (top-level domains) which will ignore for parse metadata.", | |||
description: "List TLDs (top-level domains) which will ignore for parse metadata", | |||
suggestions: ["local", "localdomain", "lan"] | |||
}, | |||
%{ | |||
@@ -2159,31 +2223,32 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :auto_linker, | |||
key: :opts, | |||
label: "Auto Linker", | |||
type: :group, | |||
description: "Configuration for the auto_linker library", | |||
children: [ | |||
%{ | |||
key: :class, | |||
type: [:string, false], | |||
description: "Specify the class to be added to the generated link. Disable to clear", | |||
description: "Specify the class to be added to the generated link. Disable to clear.", | |||
suggestions: ["auto-linker", false] | |||
}, | |||
%{ | |||
key: :rel, | |||
type: [:string, false], | |||
description: "Override the rel attribute. Disable to clear", | |||
description: "Override the rel attribute. Disable to clear.", | |||
suggestions: ["ugc", "noopener noreferrer", false] | |||
}, | |||
%{ | |||
key: :new_window, | |||
type: :boolean, | |||
description: "Link urls will open in new window/tab" | |||
description: "Link URLs will open in new window/tab" | |||
}, | |||
%{ | |||
key: :truncate, | |||
type: [:integer, false], | |||
description: | |||
"Set to a number to truncate urls longer then the number. Truncated urls will end in `..`", | |||
"Set to a number to truncate URLs longer then the number. Truncated URLs will end in `..`", | |||
suggestions: [15, false] | |||
}, | |||
%{ | |||
@@ -2194,7 +2259,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :extra, | |||
type: :boolean, | |||
description: "Link urls with rarely used schemes (magnet, ipfs, irc, etc.)" | |||
description: "Link URLs with rarely used schemes (magnet, ipfs, irc, etc.)" | |||
} | |||
] | |||
}, | |||
@@ -2240,6 +2305,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :pleroma, | |||
label: "Pleroma Authenticator", | |||
type: :group, | |||
description: "Authenticator", | |||
children: [ | |||
@@ -2253,6 +2319,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :ldap, | |||
label: "LDAP", | |||
type: :group, | |||
description: | |||
"Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <> | |||
@@ -2339,6 +2406,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :uid, | |||
label: "UID", | |||
type: :string, | |||
description: | |||
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"", | |||
@@ -2354,11 +2422,12 @@ config :pleroma, :config_description, [ | |||
children: [ | |||
%{ | |||
key: :enforce_oauth_admin_scope_usage, | |||
label: "Enforce OAuth admin scope usage", | |||
type: :boolean, | |||
description: | |||
"OAuth admin scope requirement toggle. " <> | |||
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <> | |||
"(client app must support admin scopes). If disabled and token doesn't have admin scope(s)," <> | |||
"(client app must support admin scopes). If disabled and token doesn't have admin scope(s), " <> | |||
"`is_admin` user flag grants access to admin-specific actions." | |||
}, | |||
%{ | |||
@@ -2370,6 +2439,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :oauth_consumer_template, | |||
label: "OAuth consumer template", | |||
type: :string, | |||
description: | |||
"OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to" <> | |||
@@ -2378,6 +2448,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :oauth_consumer_strategies, | |||
label: "OAuth consumer strategies", | |||
type: {:list, :string}, | |||
description: | |||
"The list of enabled OAuth consumer strategies. By default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <> | |||
@@ -2506,7 +2577,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :enabled, | |||
type: :boolean, | |||
description: "enables new users admin digest email when `true`", | |||
description: "Enables new users admin digest email when `true`", | |||
suggestions: [false] | |||
} | |||
] | |||
@@ -2514,6 +2585,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :oauth2, | |||
label: "OAuth2", | |||
type: :group, | |||
description: "Configure OAuth 2 provider capabilities", | |||
children: [ | |||
@@ -2532,7 +2604,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :clean_expired_tokens, | |||
type: :boolean, | |||
description: "Enable a background job to clean expired oauth tokens. Default: disabled." | |||
description: "Enable a background job to clean expired OAuth tokens. Default: disabled." | |||
} | |||
] | |||
}, | |||
@@ -2616,6 +2688,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :relation_id_action, | |||
label: "Relation ID action", | |||
type: [:tuple, {:list, :tuple}], | |||
description: "For actions on relation with a specific user (follow, unfollow)", | |||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] | |||
@@ -2629,6 +2702,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
key: :status_id_action, | |||
label: "Status ID action", | |||
type: [:tuple, {:list, :tuple}], | |||
description: | |||
"For fav / unfav or reblog / unreblog actions on the same status by the same user", | |||
@@ -2644,6 +2718,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :esshd, | |||
label: "ESSHD", | |||
type: :group, | |||
description: | |||
"Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <> | |||
@@ -2682,8 +2757,9 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :mime, | |||
label: "Mime Types", | |||
type: :group, | |||
description: "Mime types", | |||
description: "Mime Types settings", | |||
children: [ | |||
%{ | |||
key: :types, | |||
@@ -2742,6 +2818,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :http, | |||
label: "HTTP", | |||
type: :group, | |||
description: "HTTP settings", | |||
children: [ | |||
@@ -2790,6 +2867,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :markup, | |||
label: "Markup Settings", | |||
type: :group, | |||
children: [ | |||
%{ | |||
@@ -2830,8 +2908,9 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :pleroma, | |||
tab: :mrf, | |||
key: :mrf_normalize_markup, | |||
label: "MRF normalize markup", | |||
label: "MRF Normalize Markup", | |||
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.", | |||
type: :group, | |||
children: [ | |||
@@ -2887,6 +2966,7 @@ config :pleroma, :config_description, [ | |||
}, | |||
%{ | |||
group: :cors_plug, | |||
label: "CORS plug config", | |||
type: :group, | |||
children: [ | |||
%{ | |||
@@ -2959,6 +3039,7 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :web_cache_ttl, | |||
label: "Web cache TTL", | |||
type: :group, | |||
description: | |||
"The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.", | |||
@@ -2981,9 +3062,10 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :static_fe, | |||
label: "Static FE", | |||
type: :group, | |||
description: | |||
"Render profiles and posts using server-generated HTML that is viewable without using JavaScript.", | |||
"Render profiles and posts using server-generated HTML that is viewable without using JavaScript", | |||
children: [ | |||
%{ | |||
key: :enabled, | |||
@@ -3001,18 +3083,18 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :post_title, | |||
type: :map, | |||
description: "Configure title rendering.", | |||
description: "Configure title rendering", | |||
children: [ | |||
%{ | |||
key: :max_length, | |||
type: :integer, | |||
description: "Maximum number of characters before truncating title.", | |||
description: "Maximum number of characters before truncating title", | |||
suggestions: [100] | |||
}, | |||
%{ | |||
key: :omission, | |||
type: :string, | |||
description: "Replacement which will be used after truncating string.", | |||
description: "Replacement which will be used after truncating string", | |||
suggestions: ["..."] | |||
} | |||
] | |||
@@ -3022,8 +3104,11 @@ config :pleroma, :config_description, [ | |||
%{ | |||
group: :pleroma, | |||
key: :mrf_object_age, | |||
label: "MRF Object Age", | |||
tab: :mrf, | |||
type: :group, | |||
description: "Rejects or delists posts based on their age when received.", | |||
description: | |||
"Rejects or delists posts based on their timestamp deviance from your server's clock.", | |||
children: [ | |||
%{ | |||
key: :threshold, | |||
@@ -3036,7 +3121,7 @@ config :pleroma, :config_description, [ | |||
type: {:list, :atom}, | |||
description: | |||
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <> | |||
"`:strip_followers` removes followers from the ActivityPub recipient list, ensuring they won't be delivered to home timelines; " <> | |||
"`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <> | |||
"`:reject` rejects the message entirely", | |||
suggestions: [:delist, :strip_followers, :reject] | |||
} | |||
@@ -3064,13 +3149,13 @@ config :pleroma, :config_description, [ | |||
%{ | |||
key: :workers, | |||
type: :integer, | |||
description: "Number of workers to send notifications.", | |||
description: "Number of workers to send notifications", | |||
suggestions: [3] | |||
}, | |||
%{ | |||
key: :overflow_workers, | |||
type: :integer, | |||
description: "Maximum number of workers created if pool is empty.", | |||
description: "Maximum number of workers created if pool is empty", | |||
suggestions: [2] | |||
} | |||
] | |||
@@ -3361,41 +3446,5 @@ config :pleroma, :config_description, [ | |||
suggestions: [false] | |||
} | |||
] | |||
}, | |||
%{ | |||
group: :pleroma, | |||
key: :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: | |||
Generator.list_modules_in_dir( | |||
"lib/pleroma/web/activity_pub/mrf", | |||
"Elixir.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" | |||
] | |||
} | |||
] | |||
} | |||
] |
@@ -27,6 +27,7 @@ Has these additional fields under the `pleroma` object: | |||
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire | |||
- `thread_muted`: true if the thread the post belongs to is muted | |||
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint. | |||
- `parent_visible`: If the parent of this post is visible to the user or not. | |||
## Media Attachments | |||
@@ -51,11 +52,14 @@ The `id` parameter can also be the `nickname` of the user. This only works in th | |||
Has these additional fields under the `pleroma` object: | |||
- `ap_id`: nullable URL string, ActivityPub id of the user | |||
- `background_image`: nullable URL string, background image of the user | |||
- `tags`: Lists an array of tags for the user | |||
- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/ | |||
- `relationship` (object): Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/ | |||
- `is_moderator`: boolean, nullable, true if user is a moderator | |||
- `is_admin`: boolean, nullable, true if user is an admin | |||
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated | |||
- `hide_favorites`: boolean, true when the user has hiding favorites enabled | |||
- `hide_followers`: boolean, true when the user has follower hiding enabled | |||
- `hide_follows`: boolean, true when the user has follow hiding enabled | |||
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled | |||
@@ -66,6 +70,7 @@ Has these additional fields under the `pleroma` object: | |||
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts | |||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner. | |||
- `unread_notifications_count`: The count of unread notifications. Only returned to the account owner. | |||
- `notification_settings`: object, can be absent. See `/api/pleroma/notification_settings` for the parameters/keys returned. | |||
### Source | |||
@@ -223,6 +228,7 @@ Has theses additional parameters (which are the same as in Pleroma-API): | |||
- `background_image`: A background image that frontends can use | |||
- `pleroma.metadata.features`: A list of supported features | |||
- `pleroma.metadata.federation`: The federation restrictions of this instance | |||
- `pleroma.metadata.fields_limits`: A list of values detailing the length and count limitation for various instance-configurable fields. | |||
- `vapid_public_key`: The public key needed for push messages | |||
## Markers | |||
@@ -12,6 +12,11 @@ defmodule Pleroma.Config.Loader do | |||
:swarm | |||
] | |||
@reject_groups [ | |||
:postgrex, | |||
:tesla | |||
] | |||
if Code.ensure_loaded?(Config.Reader) do | |||
@reader Config.Reader | |||
@@ -47,7 +52,8 @@ defmodule Pleroma.Config.Loader do | |||
@spec filter_group(atom(), keyword()) :: keyword() | |||
def filter_group(group, configs) do | |||
Enum.reject(configs[group], fn {key, _v} -> | |||
key in @reject_keys or (group == :phoenix and key == :serve_endpoints) or group == :postgrex | |||
key in @reject_keys or group in @reject_groups or | |||
(group == :phoenix and key == :serve_endpoints) | |||
end) | |||
end | |||
end |
@@ -109,7 +109,7 @@ defmodule Pleroma.HTML do | |||
result = | |||
content | |||
|> Floki.parse_fragment!() | |||
|> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"]") | |||
|> Floki.filter_out("a.mention,a.hashtag,a.attachment,a[rel~=\"tag\"]") | |||
|> Floki.attribute("a", "href") | |||
|> Enum.at(0) | |||
@@ -3,7 +3,6 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.MigrationHelper.NotificationBackfill do | |||
alias Pleroma.Notification | |||
alias Pleroma.Object | |||
alias Pleroma.Repo | |||
alias Pleroma.User | |||
@@ -25,18 +24,27 @@ defmodule Pleroma.MigrationHelper.NotificationBackfill do | |||
|> type_from_activity() | |||
notification | |||
|> Notification.changeset(%{type: type}) | |||
|> Ecto.Changeset.change(%{type: type}) | |||
|> Repo.update() | |||
end) | |||
end | |||
defp get_by_ap_id(ap_id) do | |||
q = | |||
from(u in User, | |||
select: u.id | |||
) | |||
Repo.get_by(q, ap_id: ap_id) | |||
end | |||
# This is copied over from Notifications to keep this stable. | |||
defp type_from_activity(%{data: %{"type" => type}} = activity) do | |||
case type do | |||
"Follow" -> | |||
accepted_function = fn activity -> | |||
with %User{} = follower <- User.get_by_ap_id(activity.data["actor"]), | |||
%User{} = followed <- User.get_by_ap_id(activity.data["object"]) do | |||
with %User{} = follower <- get_by_ap_id(activity.data["actor"]), | |||
%User{} = followed <- get_by_ap_id(activity.data["object"]) do | |||
Pleroma.FollowingRelationship.following?(follower, followed) | |||
end | |||
end | |||
@@ -367,6 +367,7 @@ defmodule Pleroma.Notification do | |||
do_send = do_send && user in enabled_receivers | |||
create_notification(activity, user, do_send) | |||
end) | |||
|> Enum.reject(&is_nil/1) | |||
{:ok, notifications} | |||
end | |||
@@ -83,8 +83,8 @@ defmodule Pleroma.Object.Fetcher do | |||
{:transmogrifier, {:error, {:reject, nil}}} -> | |||
{:reject, nil} | |||
{:transmogrifier, _} -> | |||
{:error, "Transmogrifier failure."} | |||
{:transmogrifier, _} = e -> | |||
{:error, e} | |||
{:object, data, nil} -> | |||
reinject_object(%Object{}, data) | |||
@@ -115,7 +115,7 @@ defmodule Pleroma.User do | |||
field(:is_moderator, :boolean, default: false) | |||
field(:is_admin, :boolean, default: false) | |||
field(:show_role, :boolean, default: true) | |||
field(:settings, :map, default: nil) | |||
field(:mastofe_settings, :map, default: nil) | |||
field(:uri, ObjectValidators.Uri, default: nil) | |||
field(:hide_followers_count, :boolean, default: false) | |||
field(:hide_follows_count, :boolean, default: false) | |||
@@ -1309,7 +1309,8 @@ defmodule Pleroma.User do | |||
unsubscribe(blocked, blocker) | |||
if following?(blocked, blocker), do: unfollow(blocked, blocker) | |||
unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true) | |||
if unfollowing_blocked && following?(blocked, blocker), do: unfollow(blocked, blocker) | |||
{:ok, blocker} = update_follower_count(blocker) | |||
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked) | |||
@@ -1527,8 +1528,7 @@ defmodule Pleroma.User do | |||
blocked_identifiers, | |||
fn blocked_identifier -> | |||
with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier), | |||
{:ok, _user_block} <- block(blocker, blocked), | |||
{:ok, _} <- ActivityPub.block(blocker, blocked) do | |||
{:ok, _block} <- CommonAPI.block(blocker, blocked) do | |||
blocked | |||
else | |||
err -> | |||
@@ -2118,8 +2118,8 @@ defmodule Pleroma.User do | |||
def mastodon_settings_update(user, settings) do | |||
user | |||
|> cast(%{settings: settings}, [:settings]) | |||
|> validate_required([:settings]) | |||
|> cast(%{mastofe_settings: settings}, [:mastofe_settings]) | |||
|> validate_required([:mastofe_settings]) | |||
|> update_and_set_cache() | |||
end | |||
@@ -52,6 +52,7 @@ defmodule Pleroma.User.Search do | |||
|> base_query(following) | |||
|> filter_blocked_user(for_user) | |||
|> filter_invisible_users() | |||
|> filter_internal_users() | |||
|> filter_blocked_domains(for_user) | |||
|> fts_search(query_string) | |||
|> trigram_rank(query_string) | |||
@@ -109,6 +110,10 @@ defmodule Pleroma.User.Search do | |||
from(q in query, where: q.invisible == false) | |||
end | |||
defp filter_internal_users(query) do | |||
from(q in query, where: q.actor_type != "Application") | |||
end | |||
defp filter_blocked_user(query, %User{} = blocker) do | |||
query | |||
|> join(:left, [u], b in Pleroma.UserRelationship, | |||
@@ -366,33 +366,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
end | |||
end | |||
@spec block(User.t(), User.t(), String.t() | nil, boolean()) :: | |||
{:ok, Activity.t()} | {:error, any()} | |||
def block(blocker, blocked, activity_id \\ nil, local \\ true) do | |||
with {:ok, result} <- | |||
Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do | |||
result | |||
end | |||
end | |||
defp do_block(blocker, blocked, activity_id, local) do | |||
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked]) | |||
if unfollow_blocked and fetch_latest_follow(blocker, blocked) do | |||
unfollow(blocker, blocked, nil, local) | |||
end | |||
block_data = make_block_data(blocker, blocked, activity_id) | |||
with {:ok, activity} <- insert(block_data, local), | |||
_ <- notify_and_stream(activity), | |||
:ok <- maybe_federate(activity) do | |||
{:ok, activity} | |||
else | |||
{:error, error} -> Repo.rollback(error) | |||
end | |||
end | |||
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()} | |||
def flag( | |||
%{ | |||
@@ -1398,6 +1371,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
end | |||
end | |||
def maybe_handle_clashing_nickname(nickname) do | |||
with %User{} = old_user <- User.get_by_nickname(nickname) do | |||
Logger.info("Found an old user for #{nickname}, ap id is #{old_user.ap_id}, renaming.") | |||
old_user | |||
|> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"}) | |||
|> User.update_and_set_cache() | |||
end | |||
end | |||
def make_user_from_ap_id(ap_id) do | |||
user = User.get_cached_by_ap_id(ap_id) | |||
@@ -1410,6 +1393,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
|> User.remote_user_changeset(data) | |||
|> User.update_and_set_cache() | |||
else | |||
maybe_handle_clashing_nickname(data[:nickname]) | |||
data | |||
|> User.remote_user_changeset() | |||
|> Repo.insert() | |||
@@ -138,6 +138,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do | |||
}, []} | |||
end | |||
@spec block(User.t(), User.t()) :: {:ok, map(), keyword()} | |||
def block(blocker, blocked) do | |||
{:ok, | |||
%{ | |||
"id" => Utils.generate_activity_id(), | |||
"type" => "Block", | |||
"actor" => blocker.ap_id, | |||
"object" => blocked.ap_id, | |||
"to" => [blocked.ap_id] | |||
}, []} | |||
end | |||
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()} | |||
def announce(actor, object, options \\ []) do | |||
public? = Keyword.get(options, :public, false) | |||
@@ -27,11 +27,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do | |||
@impl true | |||
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do | |||
with {:ok, %User{} = u} <- User.get_or_fetch_by_ap_id(actor), | |||
with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor), | |||
{:contains_links, true} <- {:contains_links, contains_links?(object)}, | |||
{:old_user, true} <- {:old_user, old_user?(u)} do | |||
{:ok, message} | |||
else | |||
{:ok, %User{local: true}} -> | |||
{:ok, message} | |||
{:contains_links, false} -> | |||
{:ok, message} | |||
@@ -13,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do | |||
alias Pleroma.Object | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator | |||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator | |||
@@ -24,6 +25,25 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do | |||
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} | |||
def validate(object, meta) | |||
def validate(%{"type" => "Block"} = block_activity, meta) do | |||
with {:ok, block_activity} <- | |||
block_activity | |||
|> BlockValidator.cast_and_validate() | |||
|> Ecto.Changeset.apply_action(:insert) do | |||
block_activity = stringify_keys(block_activity) | |||
outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks]) | |||
meta = | |||
if !outgoing_blocks do | |||
Keyword.put(meta, :do_not_federate, true) | |||
else | |||
meta | |||
end | |||
{:ok, block_activity, meta} | |||
end | |||
end | |||
def validate(%{"type" => "Update"} = update_activity, meta) do | |||
with {:ok, update_activity} <- | |||
update_activity | |||
@@ -0,0 +1,42 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do | |||
use Ecto.Schema | |||
alias Pleroma.EctoType.ActivityPub.ObjectValidators | |||
import Ecto.Changeset | |||
import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations | |||
@primary_key false | |||
embedded_schema do | |||
field(:id, ObjectValidators.ObjectID, primary_key: true) | |||
field(:type, :string) | |||
field(:actor, ObjectValidators.ObjectID) | |||
field(:to, ObjectValidators.Recipients, default: []) | |||
field(:cc, ObjectValidators.Recipients, default: []) | |||
field(:object, ObjectValidators.ObjectID) | |||
end | |||
def cast_data(data) do | |||
%__MODULE__{} | |||
|> cast(data, __schema__(:fields)) | |||
end | |||
def validate_data(cng) do | |||
cng | |||
|> validate_required([:id, :type, :actor, :to, :cc, :object]) | |||
|> validate_inclusion(:type, ["Block"]) | |||
|> validate_actor_presence() | |||
|> validate_actor_presence(field_name: :object) | |||
end | |||
def cast_and_validate(data) do | |||
data | |||
|> cast_data | |||
|> validate_data | |||
end | |||
end |
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do | |||
collection, and so on. | |||
""" | |||
alias Pleroma.Activity | |||
alias Pleroma.Activity.Ir.Topics | |||
alias Pleroma.Chat | |||
alias Pleroma.Chat.MessageReference | |||
alias Pleroma.Notification | |||
@@ -21,6 +22,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do | |||
def handle(object, meta \\ []) | |||
# Tasks this handles: | |||
# - Unfollow and block | |||
def handle( | |||
%{data: %{"type" => "Block", "object" => blocked_user, "actor" => blocking_user}} = | |||
object, | |||
meta | |||
) do | |||
with %User{} = blocker <- User.get_cached_by_ap_id(blocking_user), | |||
%User{} = blocked <- User.get_cached_by_ap_id(blocked_user) do | |||
User.block(blocker, blocked) | |||
end | |||
{:ok, object, meta} | |||
end | |||
# Tasks this handles: | |||
# - Update the user | |||
# | |||
# For a local user, we also get a changeset with the full information, so we | |||
@@ -82,7 +98,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do | |||
if !User.is_internal_user?(user) do | |||
Notification.create_notifications(object) | |||
ActivityPub.stream_out(object) | |||
object | |||
|> Topics.get_activity_topics() | |||
|> Streamer.stream(object) | |||
end | |||
{:ok, object, meta} | |||
@@ -446,12 +446,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer", "Audio"] do | |||
actor = Containment.get_actor(data) | |||
data = | |||
Map.put(data, "actor", actor) | |||
|> fix_addressing | |||
with nil <- Activity.get_create_by_object_ap_id(object["id"]), | |||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do | |||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor), | |||
data <- Map.put(data, "actor", actor) |> fix_addressing() do | |||
object = fix_object(object, options) | |||
params = %{ | |||
@@ -673,7 +670,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
end | |||
def handle_incoming(%{"type" => type} = data, _options) | |||
when type in ["Like", "EmojiReact", "Announce"] do | |||
when type in ~w{Like EmojiReact Announce} do | |||
with :ok <- ObjectValidator.fetch_actor_and_object(data), | |||
{:ok, activity, _meta} <- | |||
Pipeline.common_pipeline(data, local: false) do | |||
@@ -684,9 +681,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
end | |||
def handle_incoming( | |||
%{"type" => "Update"} = data, | |||
%{"type" => type} = data, | |||
_options | |||
) do | |||
) | |||
when type in ~w{Update Block} do | |||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), | |||
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do | |||
{:ok, activity} | |||
@@ -766,21 +764,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do | |||
end | |||
def handle_incoming( | |||
%{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data, | |||
_options | |||
) do | |||
with %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), | |||
{:ok, %User{} = blocker} = User.get_or_fetch_by_ap_id(blocker), | |||
{:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do | |||
User.unfollow(blocker, blocked) | |||
User.block(blocker, blocked) | |||
{:ok, activity} | |||
else | |||
_e -> :error | |||
end | |||
end | |||
def handle_incoming( | |||
%{ | |||
"type" => "Move", | |||
"actor" => origin_actor, | |||
@@ -47,6 +47,10 @@ defmodule Pleroma.Web.ActivityPub.Visibility do | |||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean() | |||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true | |||
def visible_for_user?(nil, _), do: false | |||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false | |||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do | |||
user.ap_id in activity.data["to"] || | |||
list_ap_id | |||
@@ -54,8 +58,6 @@ defmodule Pleroma.Web.ActivityPub.Visibility do | |||
|> Pleroma.List.member?(user) | |||
end | |||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false | |||
def visible_for_user?(%{local: local} = activity, nil) do | |||
cfg_key = | |||
if local, | |||
@@ -40,7 +40,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do | |||
|> List.first() | |||
_ -> | |||
nil | |||
"application/json" | |||
end | |||
private_data = Map.put(private_data, :operation_id, operation_id) | |||
@@ -84,7 +84,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do | |||
operationId: "StatusController.delete", | |||
parameters: [id_param()], | |||
responses: %{ | |||
200 => empty_object_response(), | |||
200 => status_response(), | |||
403 => Operation.response("Forbidden", "application/json", ApiError), | |||
404 => Operation.response("Not Found", "application/json", ApiError) | |||
} | |||
@@ -40,20 +40,53 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do | |||
pleroma: %Schema{ | |||
type: :object, | |||
properties: %{ | |||
allow_following_move: %Schema{type: :boolean}, | |||
background_image: %Schema{type: :string, nullable: true}, | |||
allow_following_move: %Schema{ | |||
type: :boolean, | |||
description: "whether the user allows automatically follow moved following accounts" | |||
}, | |||
background_image: %Schema{type: :string, nullable: true, format: :uri}, | |||
chat_token: %Schema{type: :string}, | |||
confirmation_pending: %Schema{type: :boolean}, | |||
confirmation_pending: %Schema{ | |||
type: :boolean, | |||
description: | |||
"whether the user account is waiting on email confirmation to be activated" | |||
}, | |||
hide_favorites: %Schema{type: :boolean}, | |||
hide_followers_count: %Schema{type: :boolean}, | |||
hide_followers: %Schema{type: :boolean}, | |||
hide_follows_count: %Schema{type: :boolean}, | |||
hide_follows: %Schema{type: :boolean}, | |||
is_admin: %Schema{type: :boolean}, | |||
is_moderator: %Schema{type: :boolean}, | |||
hide_followers_count: %Schema{ | |||
type: :boolean, | |||
description: "whether the user has follower stat hiding enabled" | |||
}, | |||
hide_followers: %Schema{ | |||
type: :boolean, | |||
description: "whether the user has follower hiding enabled" | |||
}, | |||
hide_follows_count: %Schema{ | |||
type: :boolean, | |||
description: "whether the user has follow stat hiding enabled" | |||
}, | |||
hide_follows: %Schema{ | |||
type: :boolean, | |||
description: "whether the user has follow hiding enabled" | |||
}, | |||
is_admin: %Schema{ | |||
type: :boolean, | |||
description: "whether the user is an admin of the local instance" | |||
}, | |||
is_moderator: %Schema{ | |||
type: :boolean, | |||
description: "whether the user is a moderator of the local instance" | |||
}, | |||
skip_thread_containment: %Schema{type: :boolean}, | |||
tags: %Schema{type: :array, items: %Schema{type: :string}}, | |||
unread_conversation_count: %Schema{type: :integer}, | |||
tags: %Schema{ | |||
type: :array, | |||
items: %Schema{type: :string}, | |||
description: | |||
"List of tags being used for things like extra roles or moderation(ie. marking all media as nsfw all)." | |||
}, | |||
unread_conversation_count: %Schema{ | |||
type: :integer, | |||
description: "The count of unread conversations. Only returned to the account owner." | |||
}, | |||
notification_settings: %Schema{ | |||
type: :object, | |||
properties: %{ | |||
@@ -66,7 +99,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do | |||
}, | |||
relationship: AccountRelationship, | |||
settings_store: %Schema{ | |||
type: :object | |||
type: :object, | |||
description: | |||
"A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`" | |||
} | |||
} | |||
}, | |||
@@ -74,16 +109,32 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do | |||
type: :object, | |||
properties: %{ | |||
fields: %Schema{type: :array, items: AccountField}, | |||
note: %Schema{type: :string}, | |||
note: %Schema{ | |||
type: :string, | |||
description: | |||
"Plaintext version of the bio without formatting applied by the backend, used for editing the bio." | |||
}, | |||
privacy: VisibilityScope, | |||
sensitive: %Schema{type: :boolean}, | |||
pleroma: %Schema{ | |||
type: :object, | |||
properties: %{ | |||
actor_type: ActorType, | |||
discoverable: %Schema{type: :boolean}, | |||
no_rich_text: %Schema{type: :boolean}, | |||
show_role: %Schema{type: :boolean} | |||
discoverable: %Schema{ | |||
type: :boolean, | |||
description: | |||
"whether the user allows discovery of the account in search results and other services." | |||
}, | |||
no_rich_text: %Schema{ | |||
type: :boolean, | |||
description: | |||
"whether the HTML tags for rich-text formatting are stripped from all statuses requested from the API." | |||
}, | |||
show_role: %Schema{ | |||
type: :boolean, | |||
description: | |||
"whether the user wants their role (e.g admin, moderator) to be shown" | |||
} | |||
} | |||
} | |||
} | |||
@@ -62,6 +62,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do | |||
} | |||
}, | |||
content: %Schema{type: :string, format: :html, description: "HTML-encoded status content"}, | |||
text: %Schema{ | |||
type: :string, | |||
description: "Original unformatted content in plain text", | |||
nullable: true | |||
}, | |||
created_at: %Schema{ | |||
type: :string, | |||
format: "date-time", | |||
@@ -184,6 +189,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do | |||
thread_muted: %Schema{ | |||
type: :boolean, | |||
description: "`true` if the thread the post belongs to is muted" | |||
}, | |||
parent_visible: %Schema{ | |||
type: :boolean, | |||
description: "`true` if the parent post is visible to the user" | |||
} | |||
} | |||
}, | |||
@@ -186,6 +186,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do | |||
draft.poll | |||
) | |||
|> Map.put("emoji", emoji) | |||
|> Map.put("source", draft.status) | |||
%__MODULE__{draft | object: object} | |||
end | |||
@@ -25,6 +25,13 @@ defmodule Pleroma.Web.CommonAPI do | |||
require Pleroma.Constants | |||
require Logger | |||
def block(blocker, blocked) do | |||
with {:ok, block_data, _} <- Builder.block(blocker, blocked), | |||
{:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do | |||
{:ok, block} | |||
end | |||
end | |||
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do | |||
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]), | |||
:ok <- validate_chat_content_length(content, !!maybe_attachment), | |||
@@ -385,8 +385,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do | |||
@doc "POST /api/v1/accounts/:id/block" | |||
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do | |||
with {:ok, _user_block} <- User.block(blocker, blocked), | |||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do | |||
with {:ok, _activity} <- CommonAPI.block(blocker, blocked) do | |||
render(conn, "relationship.json", user: blocker, target: blocked) | |||
else | |||
{:error, message} -> json_response(conn, :forbidden, %{error: message}) | |||
@@ -44,6 +44,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do | |||
def search(conn, params), do: do_search(:v1, conn, params) | |||
defp do_search(version, %{assigns: %{user: user}} = conn, %{q: query} = params) do | |||
query = String.trim(query) | |||
options = search_options(params, user) | |||
timeout = Keyword.get(Repo.config(), :timeout, 15_000) | |||
default_values = %{"statuses" => [], "accounts" => [], "hashtags" => []} | |||
@@ -200,11 +200,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do | |||
@doc "DELETE /api/v1/statuses/:id" | |||
def delete(%{assigns: %{user: user}} = conn, %{id: id}) do | |||
with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do | |||
json(conn, %{}) | |||
with %Activity{} = activity <- Activity.get_by_id_with_object(id), | |||
render <- | |||
try_render(conn, "show.json", | |||
activity: activity, | |||
for: user, | |||
with_direct_conversation_id: true, | |||
with_source: true | |||
), | |||
{:ok, %Activity{}} <- CommonAPI.delete(id, user) do | |||
render | |||
else | |||
{:error, :not_found} = e -> e | |||
_e -> render_error(conn, :forbidden, "Can't delete this post") | |||
_e -> {:error, :not_found} | |||
end | |||
end | |||
@@ -36,8 +36,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do | |||
background_image: Keyword.get(instance, :background_image), | |||
pleroma: %{ | |||
metadata: %{ | |||
account_activation_required: Keyword.get(instance, :account_activation_required), | |||
features: features(), | |||
federation: federation() | |||
federation: federation(), | |||
fields_limits: fields_limits() | |||
}, | |||
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) | |||
} | |||
@@ -88,4 +90,13 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do | |||
end | |||
|> Map.put(:enabled, Config.get([:instance, :federating])) | |||
end | |||
def fields_limits do | |||
%{ | |||
max_fields: Config.get([:instance, :max_account_fields]), | |||
max_remote_fields: Config.get([:instance, :max_remote_account_fields]), | |||
name_length: Config.get([:instance, :account_field_name_length]), | |||
value_length: Config.get([:instance, :account_field_value_length]) | |||
} | |||
end | |||
end |
@@ -21,7 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
alias Pleroma.Web.MastodonAPI.StatusView | |||
alias Pleroma.Web.MediaProxy | |||
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1] | |||
import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1, visible_for_user?: 2] | |||
# TODO: Add cached version. | |||
defp get_replied_to_activities([]), do: %{} | |||
@@ -333,6 +333,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
reblog: nil, | |||
card: card, | |||
content: content_html, | |||
text: opts[:with_source] && object.data["source"], | |||
created_at: created_at, | |||
reblogs_count: announcement_count, | |||
replies_count: object.data["repliesCount"] || 0, | |||
@@ -364,7 +365,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do | |||
expires_at: expires_at, | |||
direct_conversation_id: direct_conversation_id, | |||
thread_muted: thread_muted?, | |||
emoji_reactions: emoji_reactions | |||
emoji_reactions: emoji_reactions, | |||
parent_visible: visible_for_user?(reply_to, opts[:for]) | |||
} | |||
} | |||
end | |||
@@ -3,14 +3,15 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.Preload.Providers.Instance do | |||
alias Pleroma.Plugs.InstanceStatic | |||
alias Pleroma.Web.MastodonAPI.InstanceView | |||
alias Pleroma.Web.Nodeinfo.Nodeinfo | |||
alias Pleroma.Web.Preload.Providers.Provider | |||
@behaviour Provider | |||
@instance_url :"/api/v1/instance" | |||
@panel_url :"/instance/panel.html" | |||
@nodeinfo_url :"/nodeinfo/2.0" | |||
@instance_url "/api/v1/instance" | |||
@panel_url "/instance/panel.html" | |||
@nodeinfo_url "/nodeinfo/2.0.json" | |||
@impl Provider | |||
def generate_terms(_params) do | |||
@@ -27,7 +28,7 @@ defmodule Pleroma.Web.Preload.Providers.Instance do | |||
end | |||
defp build_panel_tag(acc) do | |||
instance_path = Path.join(:code.priv_dir(:pleroma), "static/instance/panel.html") | |||
instance_path = InstanceStatic.file_path(@panel_url |> to_string()) | |||
if File.exists?(instance_path) do | |||
panel_data = File.read!(instance_path) | |||
@@ -4,10 +4,10 @@ | |||
defmodule Pleroma.Web.Preload.Providers.StatusNet do | |||
alias Pleroma.Web.Preload.Providers.Provider | |||
alias Pleroma.Web.TwitterAPI.UtilView | |||
alias Pleroma.Web.TwitterAPI.UtilController | |||
@behaviour Provider | |||
@config_url :"/api/statusnet/config.json" | |||
@config_url "/api/statusnet/config.json" | |||
@impl Provider | |||
def generate_terms(_params) do | |||
@@ -16,9 +16,10 @@ defmodule Pleroma.Web.Preload.Providers.StatusNet do | |||
end | |||
defp build_config_tag(acc) do | |||
instance = Pleroma.Config.get(:instance) | |||
info_data = UtilView.status_net_config(instance) | |||
resp = | |||
Plug.Test.conn(:get, @config_url |> to_string()) | |||
|> UtilController.config(nil) | |||
Map.put(acc, @config_url, info_data) | |||
Map.put(acc, @config_url, resp.resp_body) | |||
end | |||
end |
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.Preload.Providers.Timelines do | |||
alias Pleroma.Web.Preload.Providers.Provider | |||
@behaviour Provider | |||
@public_url :"/api/v1/timelines/public" | |||
@public_url "/api/v1/timelines/public" | |||
@impl Provider | |||
def generate_terms(params) do | |||
@@ -3,11 +3,12 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.Preload.Providers.User do | |||
alias Pleroma.User | |||
alias Pleroma.Web.MastodonAPI.AccountView | |||
alias Pleroma.Web.Preload.Providers.Provider | |||
@behaviour Provider | |||
@account_url :"/api/v1/accounts" | |||
@account_url_base "/api/v1/accounts" | |||
@impl Provider | |||
def generate_terms(%{user: user}) do | |||
@@ -16,10 +17,10 @@ defmodule Pleroma.Web.Preload.Providers.User do | |||
def generate_terms(_params), do: %{} | |||
def build_accounts_tag(acc, nil), do: acc | |||
def build_accounts_tag(acc, user) do | |||
def build_accounts_tag(acc, %User{} = user) do | |||
account_data = AccountView.render("show.json", %{user: user, for: user}) | |||
Map.put(acc, @account_url, account_data) | |||
Map.put(acc, "#{@account_url_base}/#{user.id}", account_data) | |||
end | |||
def build_accounts_tag(acc, _), do: acc | |||
end |
@@ -104,7 +104,9 @@ defmodule Pleroma.Web.Streamer do | |||
:ok | |||
end | |||
def filtered_by_user?(%User{} = user, %Activity{} = item) do | |||
def filtered_by_user?(user, item, streamed_type \\ :activity) | |||
def filtered_by_user?(%User{} = user, %Activity{} = item, streamed_type) do | |||
%{block: blocked_ap_ids, mute: muted_ap_ids, reblog_mute: reblog_muted_ap_ids} = | |||
User.outgoing_relationships_ap_ids(user, [:block, :mute, :reblog_mute]) | |||
@@ -116,6 +118,9 @@ defmodule Pleroma.Web.Streamer do | |||
true <- | |||
Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)), | |||
true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids, | |||
true <- | |||
!(streamed_type == :activity && item.data["type"] == "Announce" && | |||
parent.data["actor"] == user.ap_id), | |||
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)), | |||
true <- MapSet.disjoint?(recipients, recipient_blocks), | |||
%{host: item_host} <- URI.parse(item.actor), | |||
@@ -130,8 +135,8 @@ defmodule Pleroma.Web.Streamer do | |||
end | |||
end | |||
def filtered_by_user?(%User{} = user, %Notification{activity: activity}) do | |||
filtered_by_user?(user, activity) | |||
def filtered_by_user?(%User{} = user, %Notification{activity: activity}, _) do | |||
filtered_by_user?(user, activity, :notification) | |||
end | |||
defp do_stream("direct", item) do | |||
@@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastoFEView do | |||
"video\/mp4" | |||
] | |||
}, | |||
settings: user.settings || @default_settings, | |||
settings: user.mastofe_settings || @default_settings, | |||
push_subscription: nil, | |||
accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)}, | |||
custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis), | |||
@@ -159,7 +159,10 @@ defmodule Pleroma.Mixfile do | |||
{:cors_plug, "~> 1.5"}, | |||
{:ex_doc, "~> 0.21", only: :dev, runtime: false}, | |||
{:web_push_encryption, "~> 0.2.1"}, | |||
{:swoosh, "~> 0.23.2"}, | |||
{:swoosh, | |||
git: "https://github.com/swoosh/swoosh", | |||
ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5", | |||
override: true}, | |||
{:phoenix_swoosh, "~> 0.2"}, | |||
{:gen_smtp, "~> 0.13"}, | |||
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}, | |||
@@ -104,9 +104,9 @@ | |||
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, | |||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, | |||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"}, | |||
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {: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", "e3928e1d2889a308aaf3e42755809ac21cffd77cb58eef01cbfdab4ce2fd1e21"}, | |||
"swoosh": {:git, "https://github.com/swoosh/swoosh", "c96e0ca8a00d8f211ec1f042a4626b09f249caa5", [ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5"]}, | |||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"}, | |||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"}, | |||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"}, | |||
"tesla": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git", "61b7503cef33f00834f78ddfafe0d5d9dec2270b", [ref: "61b7503cef33f00834f78ddfafe0d5d9dec2270b"]}, | |||
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [: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", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"}, | |||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"}, | |||
@@ -0,0 +1,11 @@ | |||
defmodule Pleroma.Repo.Migrations.RenameUserSettingsCol do | |||
use Ecto.Migration | |||
def up do | |||
rename(table(:users), :settings, to: :mastofe_settings) | |||
end | |||
def down do | |||
rename(table(:users), :mastofe_settings, to: :settings) | |||
end | |||
end |
@@ -0,0 +1,10 @@ | |||
defmodule Pleroma.Repo.Migrations.RemoveTeslaFromConfig do | |||
use Ecto.Migration | |||
def up do | |||
execute("DELETE FROM config WHERE config.group = ':tesla'") | |||
end | |||
def down do | |||
end | |||
end |
@@ -1 +1 @@ | |||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.6684eb28.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.5bae86dc.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.3fcec8f6.js></script></body></html> | |||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.01bdb34a.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.0a70a9f5.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.f220ac13.js></script></body></html> |
@@ -1,2 +1,2 @@ | |||
!function(e){function n(n){for(var t,r,a=n[0],f=n[1],h=n[2],i=0,l=[];i<a.length;i++)r=a[i],u[r]&&l.push(u[r][0]),u[r]=0;for(t in f)Object.prototype.hasOwnProperty.call(f,t)&&(e[t]=f[t]);for(d&&d(n);l.length;)l.shift()();return o.push.apply(o,h||[]),c()}function c(){for(var e,n=0;n<o.length;n++){for(var c=o[n],t=!0,r=1;r<c.length;r++){var f=c[r];0!==u[f]&&(t=!1)}t&&(o.splice(n--,1),e=a(a.s=c[0]))}return e}var t={},r={runtime:0},u={runtime:0},o=[];function a(n){if(t[n])return t[n].exports;var c=t[n]={i:n,l:!1,exports:{}};return e[n].call(c.exports,c,c.exports,a),c.l=!0,c.exports}a.e=function(e){var n=[];r[e]?n.push(r[e]):0!==r[e]&&{"chunk-070d":1,"chunk-6e81":1,"chunk-commons":1,"chunk-143c":1,"chunk-0cbc":1,"chunk-43ca":1,"chunk-7c6b":1,"chunk-c5f4":1,"chunk-176e":1,"chunk-e404":1,"chunk-1609":1,"chunk-5882":1,"chunk-7506":1,"chunk-4e7e":1}[e]&&n.push(r[e]=new Promise(function(n,c){for(var t=({"chunk-commons":"chunk-commons"}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0","chunk-070d":"d2dd6533","chunk-6e81":"ca3b222f","chunk-commons":"7f6d2d11",ZhIB:"31d6cfe0","chunk-143c":"43ada4fc","chunk-0cbc":"60bba79b","chunk-5118":"31d6cfe0","chunk-43ca":"0de86b6d","chunk-7c6b":"d9e7180a","chunk-c5f4":"0827b1ce","chunk-df62":"31d6cfe0","chunk-176e":"5d7d957b","chunk-e404":"a56021ae","chunk-1609":"408dae86","chunk-5882":"f65db7f2","chunk-7506":"f01f6c2a",oAJy:"31d6cfe0","chunk-16d0":"31d6cfe0","chunk-4e7e":"5afe1978"}[e]+".css",r=a.p+t,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(i=u[o]).getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(f===t||f===r))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){var i;if((f=(i=h[o]).getAttribute("data-href"))===t||f===r)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var t=n&&n.target&&n.target.src||r,u=new Error("Loading CSS chunk "+e+" failed.\n("+t+")");u.request=t,c(u)},d.href=r,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){r[e]=0}));var c=u[e];if(0!==c)if(c)n.push(c[2]);else{var t=new Promise(function(n,t){c=u[e]=[n,t]});n.push(c[2]=t);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({"chunk-commons":"chunk-commons"}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594","chunk-070d":"7e10a520","chunk-6e81":"6efb01f4","chunk-commons":"5a106955",ZhIB:"861df339","chunk-143c":"fc1825bf","chunk-0cbc":"43ff796f","chunk-5118":"7c48ad58","chunk-43ca":"3debeff7","chunk-7c6b":"e63ae1da","chunk-c5f4":"304479e7","chunk-df62":"6c5105a6","chunk-176e":"c4995511","chunk-e404":"554bc2e3","chunk-1609":"98da6b01","chunk-5882":"7cbc4c1b","chunk-7506":"a3364e53",oAJy:"840fb1c2","chunk-16d0":"6ce78978","chunk-4e7e":"91b5e73a"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(h);var c=u[e];if(0!==c){if(c){var t=n&&("load"===n.type?"missing":n.type),r=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+t+": "+r+")");o.type=t,o.request=r,c[1](o)}u[e]=void 0}};var h=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=t,a.d=function(e,n,c){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:c})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var c=Object.create(null);if(a.r(c),Object.defineProperty(c,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var t in e)a.d(c,t,function(n){return e[n]}.bind(null,t));return c},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],h=f.push.bind(f);f.push=n,f=f.slice();for(var i=0;i<f.length;i++)n(f[i]);var d=h;c()}([]); | |||
//# sourceMappingURL=runtime.5bae86dc.js.map | |||
!function(e){function n(n){for(var t,r,a=n[0],f=n[1],h=n[2],i=0,l=[];i<a.length;i++)r=a[i],u[r]&&l.push(u[r][0]),u[r]=0;for(t in f)Object.prototype.hasOwnProperty.call(f,t)&&(e[t]=f[t]);for(d&&d(n);l.length;)l.shift()();return o.push.apply(o,h||[]),c()}function c(){for(var e,n=0;n<o.length;n++){for(var c=o[n],t=!0,r=1;r<c.length;r++){var f=c[r];0!==u[f]&&(t=!1)}t&&(o.splice(n--,1),e=a(a.s=c[0]))}return e}var t={},r={runtime:0},u={runtime:0},o=[];function a(n){if(t[n])return t[n].exports;var c=t[n]={i:n,l:!1,exports:{}};return e[n].call(c.exports,c,c.exports,a),c.l=!0,c.exports}a.e=function(e){var n=[];r[e]?n.push(r[e]):0!==r[e]&&{"chunk-070d":1,"chunk-6e81":1,"chunk-commons":1,"chunk-143c":1,"chunk-0cbc":1,"chunk-43ca":1,"chunk-7c6b":1,"chunk-c5f4":1,"chunk-176e":1,"chunk-e404":1,"chunk-1609":1,"chunk-5882":1,"chunk-7506":1,"chunk-4e7e":1}[e]&&n.push(r[e]=new Promise(function(n,c){for(var t=({"chunk-commons":"chunk-commons"}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0","chunk-070d":"d2dd6533","chunk-6e81":"ca3b222f","chunk-commons":"7f6d2d11",ZhIB:"31d6cfe0","chunk-143c":"43ada4fc","chunk-0cbc":"60bba79b","chunk-5118":"31d6cfe0","chunk-43ca":"af749c6c","chunk-7c6b":"d9e7180a","chunk-c5f4":"b1112f18","chunk-df62":"31d6cfe0","chunk-176e":"5d7d957b","chunk-e404":"a56021ae","chunk-1609":"408dae86","chunk-5882":"f65db7f2","chunk-7506":"f01f6c2a",oAJy:"31d6cfe0","chunk-16d0":"31d6cfe0","chunk-4e7e":"5afe1978"}[e]+".css",r=a.p+t,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(i=u[o]).getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(f===t||f===r))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){var i;if((f=(i=h[o]).getAttribute("data-href"))===t||f===r)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var t=n&&n.target&&n.target.src||r,u=new Error("Loading CSS chunk "+e+" failed.\n("+t+")");u.request=t,c(u)},d.href=r,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){r[e]=0}));var c=u[e];if(0!==c)if(c)n.push(c[2]);else{var t=new Promise(function(n,t){c=u[e]=[n,t]});n.push(c[2]=t);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({"chunk-commons":"chunk-commons"}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594","chunk-070d":"7e10a520","chunk-6e81":"6efb01f4","chunk-commons":"5a106955",ZhIB:"861df339","chunk-143c":"fc1825bf","chunk-0cbc":"2b0f8802","chunk-5118":"7c48ad58","chunk-43ca":"aceb457c","chunk-7c6b":"e63ae1da","chunk-c5f4":"cf269f9b","chunk-df62":"6c5105a6","chunk-176e":"c4995511","chunk-e404":"554bc2e3","chunk-1609":"98da6b01","chunk-5882":"7cbc4c1b","chunk-7506":"a3364e53",oAJy:"840fb1c2","chunk-16d0":"6ce78978","chunk-4e7e":"91b5e73a"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(h);var c=u[e];if(0!==c){if(c){var t=n&&("load"===n.type?"missing":n.type),r=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+t+": "+r+")");o.type=t,o.request=r,c[1](o)}u[e]=void 0}};var h=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=t,a.d=function(e,n,c){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:c})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var c=Object.create(null);if(a.r(c),Object.defineProperty(c,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var t in e)a.d(c,t,function(n){return e[n]}.bind(null,t));return c},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],h=f.push.bind(f);f.push=n,f=f.slice();for(var i=0;i<f.length;i++)n(f[i]);var d=h;c()}([]); | |||
//# sourceMappingURL=runtime.0a70a9f5.js.map |
@@ -10,7 +10,6 @@ defmodule Pleroma.Config.HolderTest do | |||
test "default_config/0" do | |||
config = Holder.default_config() | |||
assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads" | |||
assert config[:tesla][:adapter] == Tesla.Mock | |||
refute config[:pleroma][Pleroma.Repo] | |||
refute config[:pleroma][Pleroma.Web.Endpoint] | |||
@@ -18,17 +17,15 @@ defmodule Pleroma.Config.HolderTest do | |||
refute config[:pleroma][:configurable_from_database] | |||
refute config[:pleroma][:database] | |||
refute config[:phoenix][:serve_endpoints] | |||
refute config[:tesla][:adapter] | |||
end | |||
test "default_config/1" do | |||
pleroma_config = Holder.default_config(:pleroma) | |||
assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads" | |||
tesla_config = Holder.default_config(:tesla) | |||
assert tesla_config[:adapter] == Tesla.Mock | |||
end | |||
test "default_config/2" do | |||
assert Holder.default_config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"] | |||
assert Holder.default_config(:tesla, :adapter) == Tesla.Mock | |||
end | |||
end |
@@ -0,0 +1,72 @@ | |||
{ | |||
"@context" : [ | |||
"https://www.w3.org/ns/activitystreams", | |||
{ | |||
"atomUri" : "ostatus:atomUri", | |||
"conversation" : "ostatus:conversation", | |||
"inReplyToAtomUri" : "ostatus:inReplyToAtomUri", | |||
"ostatus" : "http://ostatus.org#", | |||
"sensitive" : "as:sensitive", | |||
"toot" : "http://joinmastodon.org/ns#", | |||
"votersCount" : "toot:votersCount" | |||
} | |||
], | |||
"atomUri" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069", | |||
"attachment" : [], | |||
"attributedTo" : "https://busshi.moe/users/tuxcrafting", | |||
"cc" : [ | |||
"https://busshi.moe/users/tuxcrafting/followers", | |||
"https://stereophonic.space/users/fixpoint", | |||
"https://blob.cat/users/blobyoumu", | |||
"https://cawfee.club/users/grips", | |||
"https://jaeger.website/users/igel" | |||
], | |||
"content" : "<p><span class=\"h-card\"><a href=\"https://stereophonic.space/users/fixpoint\" class=\"u-url mention\">@<span>fixpoint</span></a></span> <span class=\"h-card\"><a href=\"https://blob.cat/users/blobyoumu\" class=\"u-url mention\">@<span>blobyoumu</span></a></span> <span class=\"h-card\"><a href=\"https://cawfee.club/users/grips\" class=\"u-url mention\">@<span>grips</span></a></span> <span class=\"h-card\"><a href=\"https://jaeger.website/users/igel\" class=\"u-url mention\">@<span>igel</span></a></span> there's a difference between not liking nukes and not liking nuclear power<br />nukes are pretty bad as are all WMDs in general but disliking nuclear power just indicates you are unable of thought</p>", | |||
"contentMap" : { | |||
"en" : "<p><span class=\"h-card\"><a href=\"https://stereophonic.space/users/fixpoint\" class=\"u-url mention\">@<span>fixpoint</span></a></span> <span class=\"h-card\"><a href=\"https://blob.cat/users/blobyoumu\" class=\"u-url mention\">@<span>blobyoumu</span></a></span> <span class=\"h-card\"><a href=\"https://cawfee.club/users/grips\" class=\"u-url mention\">@<span>grips</span></a></span> <span class=\"h-card\"><a href=\"https://jaeger.website/users/igel\" class=\"u-url mention\">@<span>igel</span></a></span> there's a difference between not liking nukes and not liking nuclear power<br />nukes are pretty bad as are all WMDs in general but disliking nuclear power just indicates you are unable of thought</p>" | |||
}, | |||
"conversation" : "https://cawfee.club/contexts/ad6c73d8-efc2-4e74-84ea-2dacf1a27a5e", | |||
"id" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069", | |||
"inReplyTo" : "https://stereophonic.space/objects/02997b83-3ea7-4b63-94af-ef3aa2d4ed17", | |||
"inReplyToAtomUri" : "https://stereophonic.space/objects/02997b83-3ea7-4b63-94af-ef3aa2d4ed17", | |||
"published" : "2020-06-26T15:10:19Z", | |||
"replies" : { | |||
"first" : { | |||
"items" : [], | |||
"next" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069/replies?only_other_accounts=true&page=true", | |||
"partOf" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069/replies", | |||
"type" : "CollectionPage" | |||
}, | |||
"id" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069/replies", | |||
"type" : "Collection" | |||
}, | |||
"sensitive" : false, | |||
"summary" : null, | |||
"tag" : [ | |||
{ | |||
"href" : "https://stereophonic.space/users/fixpoint", | |||
"name" : "@fixpoint@stereophonic.space", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://blob.cat/users/blobyoumu", | |||
"name" : "@blobyoumu@blob.cat", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://cawfee.club/users/grips", | |||
"name" : "@grips@cawfee.club", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://jaeger.website/users/igel", | |||
"name" : "@igel@jaeger.website", | |||
"type" : "Mention" | |||
} | |||
], | |||
"to" : [ | |||
"https://www.w3.org/ns/activitystreams#Public" | |||
], | |||
"type" : "Note", | |||
"url" : "https://busshi.moe/@tuxcrafting/104410921027210069" | |||
} |
@@ -0,0 +1,59 @@ | |||
{ | |||
"@context" : [ | |||
"https://www.w3.org/ns/activitystreams", | |||
"https://social.sakamoto.gq/schemas/litepub-0.1.jsonld", | |||
{ | |||
"@language" : "und" | |||
} | |||
], | |||
"actor" : "https://social.sakamoto.gq/users/eal", | |||
"attachment" : [], | |||
"attributedTo" : "https://social.sakamoto.gq/users/eal", | |||
"cc" : [ | |||
"https://social.sakamoto.gq/users/eal/followers" | |||
], | |||
"content" : "<span class=\"h-card\"><a data-user=\"9uw2wH0iTYAMV7XnLU\" class=\"u-url mention\" href=\"https://busshi.moe/@tuxcrafting\" rel=\"ugc\">@<span>tuxcrafting</span></a></span> <span class=\"h-card\"><a data-user=\"9r5l8j8x23NI9KUFu4\" class=\"u-url mention\" href=\"https://stereophonic.space/users/fixpoint\" rel=\"ugc\">@<span>fixpoint</span></a></span> <span class=\"h-card\"><a data-user=\"9orDK545JwjY4Lxjge\" class=\"u-url mention\" href=\"https://blob.cat/users/blobyoumu\" rel=\"ugc\">@<span>blobyoumu</span></a></span> <span class=\"h-card\"><a data-user=\"68184\" class=\"u-url mention\" href=\"https://cawfee.club/users/grips\" rel=\"ugc\">@<span>grips</span></a></span> <span class=\"h-card\"><a data-user=\"9sAmMgHVKjTXKpgx84\" class=\"u-url mention\" href=\"https://jaeger.website/users/igel\" rel=\"ugc\">@<span>igel</span></a></span> What's bad about nukes?", | |||
"context" : "https://cawfee.club/contexts/ad6c73d8-efc2-4e74-84ea-2dacf1a27a5e", | |||
"conversation" : "https://cawfee.club/contexts/ad6c73d8-efc2-4e74-84ea-2dacf1a27a5e", | |||
"id" : "https://social.sakamoto.gq/objects/f20f2497-66d9-4a52-a2e1-1be2a39c32c1", | |||
"inReplyTo" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069", | |||
"published" : "2020-06-26T15:20:15.975737Z", | |||
"sensitive" : false, | |||
"summary" : "", | |||
"tag" : [ | |||
{ | |||
"href" : "https://blob.cat/users/blobyoumu", | |||
"name" : "@blobyoumu@blob.cat", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://busshi.moe/users/tuxcrafting", | |||
"name" : "@tuxcrafting@busshi.moe", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://cawfee.club/users/grips", | |||
"name" : "@grips@cawfee.club", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://jaeger.website/users/igel", | |||
"name" : "@igel@jaeger.website", | |||
"type" : "Mention" | |||
}, | |||
{ | |||
"href" : "https://stereophonic.space/users/fixpoint", | |||
"name" : "@fixpoint@stereophonic.space", | |||
"type" : "Mention" | |||
} | |||
], | |||
"to" : [ | |||
"https://busshi.moe/users/tuxcrafting", | |||
"https://www.w3.org/ns/activitystreams#Public", | |||
"https://blob.cat/users/blobyoumu", | |||
"https://stereophonic.space/users/fixpoint", | |||
"https://cawfee.club/users/grips", | |||
"https://jaeger.website/users/igel" | |||
], | |||
"type" : "Note" | |||
} |
@@ -0,0 +1,43 @@ | |||
{ | |||
"@context" : [ | |||
"https://www.w3.org/ns/activitystreams", | |||
"https://social.sakamoto.gq/schemas/litepub-0.1.jsonld", | |||
{ | |||
"@language" : "und" | |||
} | |||
], | |||
"attachment" : [], | |||
"discoverable" : true, | |||
"endpoints" : { | |||
"oauthAuthorizationEndpoint" : "https://social.sakamoto.gq/oauth/authorize", | |||
"oauthRegistrationEndpoint" : "https://social.sakamoto.gq/api/v1/apps", | |||
"oauthTokenEndpoint" : "https://social.sakamoto.gq/oauth/token", | |||
"sharedInbox" : "https://social.sakamoto.gq/inbox", | |||
"uploadMedia" : "https://social.sakamoto.gq/api/ap/upload_media" | |||
}, | |||
"followers" : "https://social.sakamoto.gq/users/eal/followers", | |||
"following" : "https://social.sakamoto.gq/users/eal/following", | |||
"icon" : { | |||
"type" : "Image", | |||
"url" : "https://social.sakamoto.gq/media/f1cb6f79bf6839f3223ca240441f766056b74ddd23c69bcaf8bb1ba1ecff6eec.jpg" | |||
}, | |||
"id" : "https://social.sakamoto.gq/users/eal", | |||
"image" : { | |||
"type" : "Image", | |||
"url" : "https://social.sakamoto.gq/media/e5cccf26421e8366f4e34be3c9d5042b8bc8dcceccc7c8e89785fa312dd9632c.jpg" | |||
}, | |||
"inbox" : "https://social.sakamoto.gq/users/eal/inbox", | |||
"manuallyApprovesFollowers" : false, | |||
"name" : "ìì", | |||
"outbox" : "https://social.sakamoto.gq/users/eal/outbox", | |||
"preferredUsername" : "eal", | |||
"publicKey" : { | |||
"id" : "https://social.sakamoto.gq/users/eal#main-key", | |||
"owner" : "https://social.sakamoto.gq/users/eal", | |||
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz3pF85YOhhv2Zaxv9YQ7\nrCe1aEhetCMVHtrK63tUVGoGdsblyKnVeJNbFcr6k3y35OpHS3HXIi6GzgihYcTu\nONLP4eQMHTnLUNAQZi03mjJA4iIq8v/tm8ZkL2mXsQSAbWj6Iq518mHNN7OvCoNt\n3Xjepl/0kgkc2gsund7m8r+Wu0Fusx6UlUyyAk3PexdDRdSSlVLeskqtP8jtdQDo\nL70pMyL+VD+Qb9RKFdtgJ+M4OqYP+7FVzCqXN0QIPhFf/kvHSLr+c4Y3Wm0nAKHU\n9CwXWXz5Xqscpv41KlgnUCOkTXb5eBSt23lNulae5srVzWBiFb6guiCpNzBGa+Sq\nrwIDAQAB\n-----END PUBLIC KEY-----\n\n" | |||
}, | |||
"summary" : "Pizza napoletana supremacist.<br><br>Any artworks posted here that are good are not mine.", | |||
"tag" : [], | |||
"type" : "Person", | |||
"url" : "https://social.sakamoto.gq/users/eal" | |||
} |
@@ -0,0 +1,59 @@ | |||
{ | |||
"@context" : [ | |||
"https://www.w3.org/ns/activitystreams", | |||
"https://w3id.org/security/v1", | |||
{ | |||
"IdentityProof" : "toot:IdentityProof", | |||
"PropertyValue" : "schema:PropertyValue", | |||
"alsoKnownAs" : { | |||
"@id" : "as:alsoKnownAs", | |||
"@type" : "@id" | |||
}, | |||
"discoverable" : "toot:discoverable", | |||
"featured" : { | |||
"@id" : "toot:featured", | |||
"@type" : "@id" | |||
}, | |||
"focalPoint" : { | |||
"@container" : "@list", | |||
"@id" : "toot:focalPoint" | |||
}, | |||
"manuallyApprovesFollowers" : "as:manuallyApprovesFollowers", | |||
"movedTo" : { | |||
"@id" : "as:movedTo", | |||
"@type" : "@id" | |||
}, | |||
"schema" : "http://schema.org#", | |||
"toot" : "http://joinmastodon.org/ns#", | |||
"value" : "schema:value" | |||
} | |||
], | |||
"attachment" : [], | |||
"discoverable" : true, | |||
"endpoints" : { | |||
"sharedInbox" : "https://busshi.moe/inbox" | |||
}, | |||
"featured" : "https://busshi.moe/users/tuxcrafting/collections/featured", | |||
"followers" : "https://busshi.moe/users/tuxcrafting/followers", | |||
"following" : "https://busshi.moe/users/tuxcrafting/following", | |||
"icon" : { | |||
"mediaType" : "image/jpeg", | |||
"type" : "Image", | |||
"url" : "https://blobcdn.busshi.moe/busshifiles/accounts/avatars/000/046/872/original/054f0806ccb303d0.jpg" | |||
}, | |||
"id" : "https://busshi.moe/users/tuxcrafting", | |||
"inbox" : "https://busshi.moe/users/tuxcrafting/inbox", | |||
"manuallyApprovesFollowers" : true, | |||
"name" : "@tuxcrafting@localhost:8080", | |||
"outbox" : "https://busshi.moe/users/tuxcrafting/outbox", | |||
"preferredUsername" : "tuxcrafting", | |||
"publicKey" : { | |||
"id" : "https://busshi.moe/users/tuxcrafting#main-key", | |||
"owner" : "https://busshi.moe/users/tuxcrafting", | |||
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwqWWTBf9OizsBiBhGS/M\nQTT6fB1VvQP6vvxouGZ5cGg1a97V67ouhjJ+nGMuWr++DNYjJYkk2TOynfykk0H/\n8rRSujSe3BNRKYGNzdnRJu/4XxgIE847Fqx5SijSP23JGYcn8TjeSUsN2u2YYVXK\n+Eb3Bu7DjGiqwNon6YB0h5qkGjkMSMVIFn0hZx6Z21bkfYWgra96Ok5OWf7Ck3je\nCuErlCMZcbQcHtFpBueJAxYchjNvm6fqwZxLX/NtaHdr7Fm2kin89mqzliapBlFH\nCXk7Jln6xV5I6ryggPAMzm3fuHzeo0RWlu8lrxLfARBVwaQQZS99bwqp6N9O2aUp\nYwIDAQAB\n-----END PUBLIC KEY-----\n" | |||
}, | |||
"summary" : "<p>expert procrastinator</p><p>trans(humanist|gender|istorized)</p><p>web: <a href=\"https://tuxcrafting.port0.org\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">tuxcrafting.port0.org</span><span class=\"invisible\"></span></a><br />pronouns: she/they<br />languages: french (native)/english (fluent)/hebrew (ok-ish)/esperanto (barely)</p>", | |||
"tag" : [], | |||
"type" : "Person", | |||
"url" : "https://busshi.moe/@tuxcrafting" | |||
} |
@@ -0,0 +1 @@ | |||
HEY! |
@@ -237,5 +237,19 @@ defmodule Pleroma.HTMLTest do | |||
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"]) | |||
end | |||
test "skips attachment links" do | |||
user = insert(:user) | |||
{:ok, activity} = | |||
CommonAPI.post(user, %{ | |||
status: | |||
"<a href=\"https://pleroma.gov/media/d24caa3a498e21e0298377a9ca0149a4f4f8b767178aacf837542282e2d94fb1.png?name=image.png\" class=\"attachment\">image.png</a>" | |||
}) | |||
object = Object.normalize(activity) | |||
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"]) | |||
end | |||
end | |||
end |
@@ -22,6 +22,16 @@ defmodule Pleroma.NotificationTest do | |||
alias Pleroma.Web.Streamer | |||
describe "create_notifications" do | |||
test "never returns nil" do | |||
user = insert(:user) | |||
other_user = insert(:user, %{invisible: true}) | |||
{:ok, activity} = CommonAPI.post(user, %{status: "yeah"}) | |||
{:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") | |||
refute {:ok, [nil]} == Notification.create_notifications(activity) | |||
end | |||
test "creates a notification for an emoji reaction" do | |||
user = insert(:user) | |||
other_user = insert(:user) | |||
@@ -26,6 +26,46 @@ defmodule Pleroma.Object.FetcherTest do | |||
:ok | |||
end | |||
describe "error cases" do | |||
setup do | |||
mock(fn | |||
%{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} -> | |||
%Tesla.Env{ | |||
status: 200, | |||
body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json") | |||
} | |||
%{method: :get, url: "https://social.sakamoto.gq/users/eal"} -> | |||
%Tesla.Env{ | |||
status: 200, | |||
body: File.read!("test/fixtures/fetch_mocks/eal.json") | |||
} | |||
%{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} -> | |||
%Tesla.Env{ | |||
status: 200, | |||
body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json") | |||
} | |||
%{method: :get, url: "https://busshi.moe/users/tuxcrafting"} -> | |||
%Tesla.Env{ | |||
status: 500 | |||
} | |||
end) | |||
:ok | |||
end | |||
@tag capture_log: true | |||
test "it works when fetching the OP actor errors out" do | |||
# Here we simulate a case where the author of the OP can't be read | |||
assert {:ok, _} = | |||
Fetcher.fetch_object_from_id( | |||
"https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM" | |||
) | |||
end | |||
end | |||
describe "max thread distance restriction" do | |||
@ap_id "http://mastodon.example.org/@admin/99541947525187367" | |||
setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) | |||
@@ -67,6 +67,7 @@ defmodule Pleroma.Factory do | |||
data = %{ | |||
"type" => "Note", | |||
"content" => text, | |||
"source" => text, | |||
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), | |||
"actor" => user.ap_id, | |||
"to" => ["https://www.w3.org/ns/activitystreams#Public"], | |||
@@ -17,7 +17,7 @@ defmodule Pleroma.UserSearchTest do | |||
describe "User.search" do | |||
setup do: clear_config([:instance, :limit_to_local_content]) | |||
test "excluded invisible users from results" do | |||
test "excludes invisible users from results" do | |||
user = insert(:user, %{nickname: "john t1000"}) | |||
insert(:user, %{invisible: true, nickname: "john t800"}) | |||
@@ -25,6 +25,15 @@ defmodule Pleroma.UserSearchTest do | |||
assert found_user.id == user.id | |||
end | |||
test "excludes service actors from results" do | |||
insert(:user, actor_type: "Application", nickname: "user1") | |||
service = insert(:user, actor_type: "Service", nickname: "user2") | |||
person = insert(:user, actor_type: "Person", nickname: "user3") | |||
assert [found_user1, found_user2] = User.search("user") | |||
assert [found_user1.id, found_user2.id] -- [service.id, person.id] == [] | |||
end | |||
test "accepts limit parameter" do | |||
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"})) | |||
assert length(User.search("john", limit: 3)) == 3 | |||
@@ -597,6 +597,31 @@ defmodule Pleroma.UserTest do | |||
refute user.last_refreshed_at == orig_user.last_refreshed_at | |||
end | |||
test "if nicknames clash, the old user gets a prefix with the old id to the nickname" do | |||
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) | |||
orig_user = | |||
insert( | |||
:user, | |||
local: false, | |||
nickname: "admin@mastodon.example.org", | |||
ap_id: "http://mastodon.example.org/users/harinezumigari", | |||
last_refreshed_at: a_week_ago | |||
) | |||
assert orig_user.last_refreshed_at == a_week_ago | |||
{:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin") | |||
assert user.inbox | |||
refute user.id == orig_user.id | |||
orig_user = User.get_by_id(orig_user.id) | |||
assert orig_user.nickname == "#{orig_user.id}.admin@mastodon.example.org" | |||
end | |||
@tag capture_log: true | |||
test "it returns the old user if stale, but unfetchable" do | |||
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) | |||
@@ -992,54 +992,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do | |||
end | |||
end | |||
describe "blocking" do | |||
test "reverts block activity on error" do | |||
[blocker, blocked] = insert_list(2, :user) | |||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do | |||
assert {:error, :reverted} = ActivityPub.block(blocker, blocked) | |||
end | |||
assert Repo.aggregate(Activity, :count, :id) == 0 | |||
assert Repo.aggregate(Object, :count, :id) == 0 | |||
end | |||
test "creates a block activity" do | |||
clear_config([:instance, :federating], true) | |||
blocker = insert(:user) | |||
blocked = insert(:user) | |||
with_mock Pleroma.Web.Federator, | |||
publish: fn _ -> nil end do | |||
{:ok, activity} = ActivityPub.block(blocker, blocked) | |||
assert activity.data["type"] == "Block" | |||
assert activity.data["actor"] == blocker.ap_id | |||
assert activity.data["object"] == blocked.ap_id | |||
assert called(Pleroma.Web.Federator.publish(activity)) | |||
end | |||
end | |||
test "works with outgoing blocks disabled, but doesn't federate" do | |||
clear_config([:instance, :federating], true) | |||
clear_config([:activitypub, :outgoing_blocks], false) | |||
blocker = insert(:user) | |||
blocked = insert(:user) | |||
with_mock Pleroma.Web.Federator, | |||
publish: fn _ -> nil end do | |||
{:ok, activity} = ActivityPub.block(blocker, blocked) | |||
assert activity.data["type"] == "Block" | |||
assert activity.data["actor"] == blocker.ap_id | |||
assert activity.data["object"] == blocked.ap_id | |||
refute called(Pleroma.Web.Federator.publish(:_)) | |||
end | |||
end | |||
end | |||
describe "timeline post-processing" do | |||
test "it filters broken threads" do | |||
user1 = insert(:user) | |||
@@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do | |||
describe "with new user" do | |||
test "it allows posts without links" do | |||
user = insert(:user) | |||
user = insert(:user, local: false) | |||
assert user.note_count == 0 | |||
@@ -45,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do | |||
end | |||
test "it disallows posts with links" do | |||
user = insert(:user) | |||
user = insert(:user, local: false) | |||
assert user.note_count == 0 | |||
@@ -55,6 +55,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do | |||
{:reject, _} = AntiLinkSpamPolicy.filter(message) | |||
end | |||
test "it allows posts with links for local users" do | |||
user = insert(:user) | |||
assert user.note_count == 0 | |||
message = | |||
@linkful_message | |||
|> Map.put("actor", user.ap_id) | |||
{:ok, _message} = AntiLinkSpamPolicy.filter(message) | |||
end | |||
end | |||
describe "with old user" do | |||
@@ -654,4 +654,31 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do | |||
assert {:error, _cng} = ObjectValidator.validate(update, []) | |||
end | |||
end | |||
describe "blocks" do | |||
setup do | |||
user = insert(:user, local: false) | |||
blocked = insert(:user) | |||
{:ok, valid_block, []} = Builder.block(user, blocked) | |||
%{user: user, valid_block: valid_block} | |||
end | |||
test "validates a basic object", %{ | |||
valid_block: valid_block | |||
} do | |||
assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) | |||
end | |||
test "returns an error if we don't know the blocked user", %{ | |||
valid_block: valid_block | |||
} do | |||
block = | |||
valid_block | |||
|> Map.put("object", "https://gensokyo.2hu/users/raymoo") | |||
assert {:error, _cng} = ObjectValidator.validate(block, []) | |||
end | |||
end | |||
end |
@@ -64,6 +64,47 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do | |||
end | |||
end | |||
describe "blocking users" do | |||
setup do | |||
user = insert(:user) | |||
blocked = insert(:user) | |||
User.follow(blocked, user) | |||
User.follow(user, blocked) | |||
{:ok, block_data, []} = Builder.block(user, blocked) | |||
{:ok, block, _meta} = ActivityPub.persist(block_data, local: true) | |||
%{user: user, blocked: blocked, block: block} | |||
end | |||
test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do | |||
assert User.following?(user, blocked) | |||
assert User.following?(blocked, user) | |||
{:ok, _, _} = SideEffects.handle(block) | |||
refute User.following?(user, blocked) | |||
refute User.following?(blocked, user) | |||
assert User.blocks?(user, blocked) | |||
end | |||
test "it blocks but does not unfollow if the relevant setting is set", %{ | |||
user: user, | |||
blocked: blocked, | |||
block: block | |||
} do | |||
clear_config([:activitypub, :unfollow_blocked], false) | |||
assert User.following?(user, blocked) | |||
assert User.following?(blocked, user) | |||
{:ok, _, _} = SideEffects.handle(block) | |||
refute User.following?(user, blocked) | |||
assert User.following?(blocked, user) | |||
assert User.blocks?(user, blocked) | |||
end | |||
end | |||
describe "update users" do | |||
setup do | |||
user = insert(:user) | |||
@@ -242,8 +283,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do | |||
{:ok, like} = CommonAPI.favorite(user, post.id) | |||
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") | |||
{:ok, announce} = CommonAPI.repeat(post.id, user) | |||
{:ok, block} = ActivityPub.block(user, poster) | |||
User.block(user, poster) | |||
{:ok, block} = CommonAPI.block(user, poster) | |||
{:ok, undo_data, _meta} = Builder.undo(user, like) | |||
{:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) | |||
@@ -549,10 +589,29 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do | |||
end | |||
test "it streams out the announce", %{announce: announce} do | |||
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do | |||
with_mocks([ | |||
{ | |||
Pleroma.Web.Streamer, | |||
[], | |||
[ | |||
stream: fn _, _ -> nil end | |||
] | |||
}, | |||
{ | |||
Pleroma.Web.Push, | |||
[], | |||
[ | |||
send: fn _ -> nil end | |||
] | |||
} | |||
]) do | |||
{:ok, announce, _} = SideEffects.handle(announce) | |||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce)) | |||
assert called( | |||
Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce) | |||
) | |||
assert called(Pleroma.Web.Push.send(:_)) | |||
end | |||
end | |||
end | |||
@@ -0,0 +1,63 @@ | |||
# Pleroma: A lightweight social networking server | |||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.BlockHandlingTest do | |||
use Pleroma.DataCase | |||
alias Pleroma.Activity | |||
alias Pleroma.User | |||
alias Pleroma.Web.ActivityPub.Transmogrifier | |||
import Pleroma.Factory | |||
test "it works for incoming blocks" do | |||
user = insert(:user) | |||
data = | |||
File.read!("test/fixtures/mastodon-block-activity.json") | |||
|> Poison.decode!() | |||
|> Map.put("object", user.ap_id) | |||
blocker = insert(:user, ap_id: data["actor"]) | |||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) | |||
assert data["type"] == "Block" | |||
assert data["object"] == user.ap_id | |||
assert data["actor"] == "http://mastodon.example.org/users/admin" | |||
assert User.blocks?(blocker, user) | |||
end | |||
test "incoming blocks successfully tear down any follow relationship" do | |||
blocker = insert(:user) | |||
blocked = insert(:user) | |||
data = | |||
File.read!("test/fixtures/mastodon-block-activity.json") | |||
|> Poison.decode!() | |||
|> Map.put("object", blocked.ap_id) | |||
|> Map.put("actor", blocker.ap_id) | |||
{:ok, blocker} = User.follow(blocker, blocked) | |||
{:ok, blocked} = User.follow(blocked, blocker) | |||
assert User.following?(blocker, blocked) | |||
assert User.following?(blocked, blocker) | |||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) | |||
assert data["type"] == "Block" | |||
assert data["object"] == blocked.ap_id | |||
assert data["actor"] == blocker.ap_id | |||
blocker = User.get_cached_by_ap_id(data["actor"]) | |||
blocked = User.get_cached_by_ap_id(data["object"]) | |||
assert User.blocks?(blocker, blocked) | |||
refute User.following?(blocker, blocked) | |||
refute User.following?(blocked, blocker) | |||
end | |||
end |
@@ -445,56 +445,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do | |||
assert [^pending_follower] = User.get_follow_requests(user) | |||
end | |||
test "it works for incoming blocks" do | |||
user = insert(:user) | |||
data = | |||
File.read!("test/fixtures/mastodon-block-activity.json") | |||
|> Poison.decode!() | |||
|> Map.put("object", user.ap_id) | |||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) | |||
assert data["type"] == "Block" | |||
assert data["object"] == user.ap_id | |||
assert data["actor"] == "http://mastodon.example.org/users/admin" | |||
blocker = User.get_cached_by_ap_id(data["actor"]) | |||
assert User.blocks?(blocker, user) | |||
end | |||
test "incoming blocks successfully tear down any follow relationship" do | |||
blocker = insert(:user) | |||
blocked = insert(:user) | |||
data = | |||
File.read!("test/fixtures/mastodon-block-activity.json") | |||
|> Poison.decode!() | |||
|> Map.put("object", blocked.ap_id) | |||
|> Map.put("actor", blocker.ap_id) | |||
{:ok, blocker} = User.follow(blocker, blocked) | |||
{:ok, blocked} = User.follow(blocked, blocker) | |||
assert User.following?(blocker, blocked) | |||
assert User.following?(blocked, blocker) | |||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) | |||
assert data["type"] == "Block" | |||
assert data["object"] == blocked.ap_id | |||
assert data["actor"] == blocker.ap_id | |||
blocker = User.get_cached_by_ap_id(data["actor"]) | |||
blocked = User.get_cached_by_ap_id(data["object"]) | |||
assert User.blocks?(blocker, blocked) | |||
refute User.following?(blocker, blocked) | |||
refute User.following?(blocked, blocker) | |||
end | |||
test "it works for incoming accepts which were pre-accepted" do | |||
follower = insert(:user) | |||
followed = insert(:user) | |||
@@ -27,16 +27,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do | |||
end | |||
end | |||
describe "fetch the latest Block" do | |||
test "fetches the latest Block activity" do | |||
blocker = insert(:user) | |||
blocked = insert(:user) | |||
{:ok, activity} = ActivityPub.block(blocker, blocked) | |||
assert activity == Utils.fetch_latest_block(blocker, blocked) | |||
end | |||
end | |||
describe "determine_explicit_mentions()" do | |||
test "works with an object that has mentions" do | |||
object = %{ | |||
@@ -344,9 +334,9 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do | |||
user1 = insert(:user) | |||
user2 = insert(:user) | |||
assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) | |||
assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) | |||
assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2) | |||
assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) | |||
assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) | |||
assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2) | |||
assert Utils.fetch_latest_block(user1, user2) == activity | |||
end | |||
@@ -25,6 +25,52 @@ defmodule Pleroma.Web.CommonAPITest do | |||
setup do: clear_config([:instance, :limit]) | |||
setup do: clear_config([:instance, :max_pinned_statuses]) | |||
describe "blocking" do | |||
setup do | |||
blocker = insert(:user) | |||
blocked = insert(:user) | |||
User.follow(blocker, blocked) | |||
User.follow(blocked, blocker) | |||
%{blocker: blocker, blocked: blocked} | |||
end | |||
test "it blocks and federates", %{blocker: blocker, blocked: blocked} do | |||
clear_config([:instance, :federating], true) | |||
with_mock Pleroma.Web.Federator, | |||
publish: fn _ -> nil end do | |||
assert {:ok, block} = CommonAPI.block(blocker, blocked) | |||
assert block.local | |||
assert User.blocks?(blocker, blocked) | |||
refute User.following?(blocker, blocked) | |||
refute User.following?(blocked, blocker) | |||
assert called(Pleroma.Web.Federator.publish(block)) | |||
end | |||
end | |||
test "it blocks and does not federate if outgoing blocks are disabled", %{ | |||
blocker: blocker, | |||
blocked: blocked | |||
} do | |||
clear_config([:instance, :federating], true) | |||
clear_config([:activitypub, :outgoing_blocks], false) | |||
with_mock Pleroma.Web.Federator, | |||
publish: fn _ -> nil end do | |||
assert {:ok, block} = CommonAPI.block(blocker, blocked) | |||
assert block.local | |||
assert User.blocks?(blocker, blocked) | |||
refute User.following?(blocker, blocked) | |||
refute User.following?(blocked, blocker) | |||
refute called(Pleroma.Web.Federator.publish(block)) | |||
end | |||
end | |||
end | |||
describe "posting chat messages" do | |||
setup do: clear_config([:instance, :chat_limit]) | |||
@@ -445,6 +491,7 @@ defmodule Pleroma.Web.CommonAPITest do | |||
object = Object.normalize(activity) | |||
assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" | |||
assert object.data["source"] == post | |||
end | |||
test "it filters out obviously bad tags when accepting a post as Markdown" do | |||
@@ -461,6 +508,7 @@ defmodule Pleroma.Web.CommonAPITest do | |||
object = Object.normalize(activity) | |||
assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" | |||
assert object.data["source"] == post | |||
end | |||
test "it does not allow replies to direct messages that are not direct messages themselves" do | |||
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastoFEController do | |||
assert _result = json_response(conn, 200) | |||
user = User.get_cached_by_ap_id(user.ap_id) | |||
assert user.settings == %{"programming" => "socks"} | |||
assert user.mastofe_settings == %{"programming" => "socks"} | |||
end | |||
describe "index/2 redirections" do | |||
@@ -780,7 +780,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do | |||
assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = | |||
conn | |||
|> put_req_header("content-type", "application/json") | |||
|> post("/api/v1/accounts/#{other_user.id}/mute") | |||
|> json_response_and_validate_schema(200) | |||
@@ -35,8 +35,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do | |||
"background_image" => _ | |||
} = result | |||
assert result["pleroma"]["metadata"]["account_activation_required"] != nil | |||
assert result["pleroma"]["metadata"]["features"] | |||
assert result["pleroma"]["metadata"]["federation"] | |||
assert result["pleroma"]["metadata"]["fields_limits"] | |||
assert result["pleroma"]["vapid_public_key"] | |||
assert email == from_config_email | |||
@@ -79,6 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do | |||
assert status["id"] == to_string(activity.id) | |||
end | |||
@tag capture_log: true | |||
test "constructs hashtags from search query", %{conn: conn} do | |||
results = | |||
conn | |||
@@ -318,11 +319,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do | |||
test "search fetches remote accounts", %{conn: conn} do | |||
user = insert(:user) | |||
query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true}) | |||
results = | |||
conn | |||
|> assign(:user, user) | |||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) | |||
|> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=true") | |||
|> get("/api/v1/search?#{query}") | |||
|> json_response_and_validate_schema(200) | |||
[account] = results["accounts"] | |||
@@ -760,13 +760,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do | |||
test "when you created it" do | |||
%{user: author, conn: conn} = oauth_access(["write:statuses"]) | |||
activity = insert(:note_activity, user: author) | |||
object = Object.normalize(activity) | |||
conn = | |||
content = object.data["content"] | |||
source = object.data["source"] | |||
result = | |||
conn | |||
|> assign(:user, author) | |||
|> delete("/api/v1/statuses/#{activity.id}") | |||
|> json_response_and_validate_schema(200) | |||
assert %{} = json_response_and_validate_schema(conn, 200) | |||
assert match?(%{"content" => ^content, "text" => ^source}, result) | |||
refute Activity.get_by_id(activity.id) | |||
end | |||
@@ -789,7 +794,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do | |||
conn = delete(conn, "/api/v1/statuses/#{activity.id}") | |||
assert %{"error" => _} = json_response_and_validate_schema(conn, 403) | |||
assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404) | |||
assert Activity.get_by_id(activity.id) == activity | |||
end | |||
@@ -183,6 +183,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||
card: nil, | |||
reblog: nil, | |||
content: HTML.filter_tags(object_data["content"]), | |||
text: nil, | |||
created_at: created_at, | |||
reblogs_count: 0, | |||
replies_count: 0, | |||
@@ -226,7 +227,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||
expires_at: nil, | |||
direct_conversation_id: nil, | |||
thread_muted: false, | |||
emoji_reactions: [] | |||
emoji_reactions: [], | |||
parent_visible: false | |||
} | |||
} | |||
@@ -620,4 +622,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do | |||
assert status.visibility == "list" | |||
end | |||
test "has a field for parent visibility" do | |||
user = insert(:user) | |||
poster = insert(:user) | |||
{:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) | |||
{:ok, visible} = | |||
CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id}) | |||
status = StatusView.render("show.json", activity: visible, for: user) | |||
refute status.pleroma.parent_visible | |||
status = StatusView.render("show.json", activity: visible, for: poster) | |||
assert status.pleroma.parent_visible | |||
end | |||
end |
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.Preload.Providers.InstanceTest do | |||
setup do: {:ok, Instance.generate_terms(nil)} | |||
test "it renders the info", %{"/api/v1/instance": info} do | |||
test "it renders the info", %{"/api/v1/instance" => info} do | |||
assert %{ | |||
description: description, | |||
email: "admin@example.com", | |||
@@ -18,14 +18,25 @@ defmodule Pleroma.Web.Preload.Providers.InstanceTest do | |||
assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") | |||
end | |||
test "it renders the panel", %{"/instance/panel.html": panel} do | |||
test "it renders the panel", %{"/instance/panel.html" => panel} do | |||
assert String.contains?( | |||
panel, | |||
"<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" | |||
) | |||
end | |||
test "it renders the node_info", %{"/nodeinfo/2.0": nodeinfo} do | |||
test "it works with overrides" do | |||
clear_config([:instance, :static_dir], "test/fixtures/preload_static") | |||
%{"/instance/panel.html" => panel} = Instance.generate_terms(nil) | |||
assert String.contains?( | |||
panel, | |||
"HEY!" | |||
) | |||
end | |||
test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do | |||
%{ | |||
metadata: metadata, | |||
version: "2.0" | |||
@@ -8,7 +8,8 @@ defmodule Pleroma.Web.Preload.Providers.StatusNetTest do | |||
setup do: {:ok, StatusNet.generate_terms(nil)} | |||
test "it renders the info", %{"/api/statusnet/config.json": info} do | |||
assert info =~ "<name>Pleroma</name>" | |||
test "it renders the info", %{"/api/statusnet/config.json" => info} do | |||
assert {:ok, res} = Jason.decode(info) | |||
assert res["site"] | |||
end | |||
end |
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.Preload.Providers.TimelineTest do | |||
alias Pleroma.Web.CommonAPI | |||
alias Pleroma.Web.Preload.Providers.Timelines | |||
@public_url :"/api/v1/timelines/public" | |||
@public_url "/api/v1/timelines/public" | |||
describe "unauthenticated timeliness when restricted" do | |||
setup do | |||
@@ -9,13 +9,11 @@ defmodule Pleroma.Web.Preload.Providers.UserTest do | |||
describe "returns empty when user doesn't exist" do | |||
test "nil user specified" do | |||
refute User.generate_terms(%{user: nil}) | |||
|> Map.has_key?("/api/v1/accounts") | |||
assert User.generate_terms(%{user: nil}) == %{} | |||
end | |||
test "missing user specified" do | |||
refute User.generate_terms(%{user: :not_a_user}) | |||
|> Map.has_key?("/api/v1/accounts") | |||
assert User.generate_terms(%{user: :not_a_user}) == %{} | |||
end | |||
end | |||
@@ -23,11 +21,13 @@ defmodule Pleroma.Web.Preload.Providers.UserTest do | |||
setup do | |||
user = insert(:user) | |||
{:ok, User.generate_terms(%{user: user})} | |||
terms = User.generate_terms(%{user: user}) | |||
%{terms: terms, user: user} | |||
end | |||
test "account is rendered", %{"/api/v1/accounts": accounts} do | |||
assert %{acct: user, username: user} = accounts | |||
test "account is rendered", %{terms: terms, user: user} do | |||
account = terms["/api/v1/accounts/#{user.id}"] | |||
assert %{acct: user, username: user} = account | |||
end | |||
end | |||
end |
@@ -116,6 +116,35 @@ defmodule Pleroma.Web.StreamerTest do | |||
refute Streamer.filtered_by_user?(user, announce) | |||
end | |||
test "it does not stream announces of the user's own posts in the 'user' stream", %{ | |||
user: user | |||
} do | |||
Streamer.get_topic_and_add_socket("user", user) | |||
other_user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"}) | |||
{:ok, announce} = CommonAPI.repeat(activity.id, other_user) | |||
assert Streamer.filtered_by_user?(user, announce) | |||
end | |||
test "it does stream notifications announces of the user's own posts in the 'user' stream", %{ | |||
user: user | |||
} do | |||
Streamer.get_topic_and_add_socket("user", user) | |||
other_user = insert(:user) | |||
{:ok, activity} = CommonAPI.post(user, %{status: "hey"}) | |||
{:ok, announce} = CommonAPI.repeat(activity.id, other_user) | |||
notification = | |||
Pleroma.Notification | |||
|> Repo.get_by(%{user_id: user.id, activity_id: announce.id}) | |||
|> Repo.preload(:activity) | |||
refute Streamer.filtered_by_user?(user, notification) | |||
end | |||
test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do | |||
Streamer.get_topic_and_add_socket("user", user) | |||