Merge branch 'develop' into feature/1885-MRF-metadata

This commit is contained in:
Mark Felder 2020-07-02 09:57:57 -05:00
commit 6b5563ed2c
61 changed files with 1363 additions and 330 deletions

View file

@ -14,7 +14,7 @@
* Pleroma version (could be found in the "Version" tab of settings in Pleroma-FE): * 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): * Elixir version (`elixir -v` for from source installations, N/A for OTP):
* Operating system: * Operating system:
* PostgreSQL version (`postgres -V`): * PostgreSQL version (`psql -V`):
### Bug description ### Bug description

View file

@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
<summary>API Changes</summary> <summary>API Changes</summary>
- **Breaking:** Emoji API: changed methods and renamed routes. - **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.
</details> </details>
<details> <details>
@ -51,6 +52,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
<details> <details>
<summary>API Changes</summary> <summary>API Changes</summary>
- Mastodon API: Add pleroma.parents_visible field to statuses.
- Mastodon API: Extended `/api/v1/instance`. - Mastodon API: Extended `/api/v1/instance`.
- Mastodon API: Support for `include_types` in `/api/v1/notifications`. - Mastodon API: Support for `include_types` in `/api/v1/notifications`.
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint. - Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.

View file

@ -437,8 +437,6 @@
config :pleroma, Pleroma.Web.Preload, config :pleroma, Pleroma.Web.Preload,
providers: [ providers: [
Pleroma.Web.Preload.Providers.Instance, Pleroma.Web.Preload.Providers.Instance,
Pleroma.Web.Preload.Providers.User,
Pleroma.Web.Preload.Providers.Timelines,
Pleroma.Web.Preload.Providers.StatusNet Pleroma.Web.Preload.Providers.StatusNet
] ]

View file

@ -40,12 +40,13 @@
key: :link_name, key: :link_name,
type: :boolean, type: :boolean,
description: 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, key: :base_url,
label: "Base URL",
type: :string, 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: [ suggestions: [
"https://cdn-host.com" "https://cdn-host.com"
] ]
@ -58,6 +59,7 @@
}, },
%{ %{
key: :proxy_opts, key: :proxy_opts,
label: "Proxy Options",
type: :keyword, type: :keyword,
description: "Options for Pleroma.ReverseProxy", description: "Options for Pleroma.ReverseProxy",
suggestions: [ suggestions: [
@ -85,6 +87,7 @@
}, },
%{ %{
key: :http, key: :http,
label: "HTTP",
type: :keyword, type: :keyword,
description: "HTTP options", description: "HTTP options",
children: [ children: [
@ -481,6 +484,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :uri_schemes, key: :uri_schemes,
label: "URI Schemes",
type: :group, type: :group,
description: "URI schemes related settings", description: "URI schemes related settings",
children: [ children: [
@ -653,17 +657,17 @@
key: :invites_enabled, key: :invites_enabled,
type: :boolean, type: :boolean,
description: 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, key: :account_activation_required,
type: :boolean, type: :boolean,
description: "Require users to confirm their emails before signing in." description: "Require users to confirm their emails before signing in"
}, },
%{ %{
key: :federating, key: :federating,
type: :boolean, type: :boolean,
description: "Enable federation with other instances." description: "Enable federation with other instances"
}, },
%{ %{
key: :federation_incoming_replies_max_depth, key: :federation_incoming_replies_max_depth,
@ -681,7 +685,7 @@
label: "Fed. reachability timeout days", label: "Fed. reachability timeout days",
type: :integer, type: :integer,
description: 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: [ suggestions: [
7 7
] ]
@ -803,6 +807,7 @@
}, },
%{ %{
key: :safe_dm_mentions, key: :safe_dm_mentions,
label: "Safe DM mentions",
type: :boolean, type: :boolean,
description: description:
"If enabled, only mentions at the beginning of a post will be used to address people in direct messages." <> "If enabled, only mentions at the beginning of a post will be used to address people in direct messages." <>
@ -842,7 +847,7 @@
%{ %{
key: :skip_thread_containment, key: :skip_thread_containment,
type: :boolean, type: :boolean,
description: "Skip filtering out broken threads. Default: enabled" description: "Skip filtering out broken threads. Default: enabled."
}, },
%{ %{
key: :limit_to_local_content, key: :limit_to_local_content,
@ -906,6 +911,7 @@
children: [ children: [
%{ %{
key: :totp, key: :totp,
label: "TOTP settings",
type: :keyword, type: :keyword,
description: "TOTP settings", description: "TOTP settings",
suggestions: [digits: 6, period: 30], suggestions: [digits: 6, period: 30],
@ -922,7 +928,7 @@
type: :integer, type: :integer,
suggestions: [30], suggestions: [30],
description: 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."
} }
] ]
}, },
@ -936,7 +942,7 @@
key: :number, key: :number,
type: :integer, type: :integer,
suggestions: [5], suggestions: [5],
description: "number of backup codes to generate." description: "Number of backup codes to generate."
}, },
%{ %{
key: :length, key: :length,
@ -976,6 +982,7 @@
group: :logger, group: :logger,
type: :group, type: :group,
key: :ex_syslogger, key: :ex_syslogger,
label: "ExSyslogger",
description: "ExSyslogger-related settings", description: "ExSyslogger-related settings",
children: [ children: [
%{ %{
@ -994,7 +1001,7 @@
%{ %{
key: :format, key: :format,
type: :string, type: :string,
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".", description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"",
suggestions: ["$metadata[$level] $message"] suggestions: ["$metadata[$level] $message"]
}, },
%{ %{
@ -1008,6 +1015,7 @@
group: :logger, group: :logger,
type: :group, type: :group,
key: :console, key: :console,
label: "Console Logger",
description: "Console logger settings", description: "Console logger settings",
children: [ children: [
%{ %{
@ -1019,7 +1027,7 @@
%{ %{
key: :format, key: :format,
type: :string, type: :string,
description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".", description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"",
suggestions: ["$metadata[$level] $message"] suggestions: ["$metadata[$level] $message"]
}, },
%{ %{
@ -1032,6 +1040,7 @@
%{ %{
group: :quack, group: :quack,
type: :group, type: :group,
label: "Quack Logger",
description: "Quack-related settings", description: "Quack-related settings",
children: [ children: [
%{ %{
@ -1142,19 +1151,19 @@
key: :greentext, key: :greentext,
label: "Greentext", label: "Greentext",
type: :boolean, type: :boolean,
description: "Enables green text on lines prefixed with the > character." description: "Enables green text on lines prefixed with the > character"
}, },
%{ %{
key: :hideFilteredStatuses, key: :hideFilteredStatuses,
label: "Hide Filtered Statuses", label: "Hide Filtered Statuses",
type: :boolean, type: :boolean,
description: "Hides filtered statuses from timelines." description: "Hides filtered statuses from timelines"
}, },
%{ %{
key: :hideMutedPosts, key: :hideMutedPosts,
label: "Hide Muted Posts", label: "Hide Muted Posts",
type: :boolean, type: :boolean,
description: "Hides muted statuses from timelines." description: "Hides muted statuses from timelines"
}, },
%{ %{
key: :hidePostStats, key: :hidePostStats,
@ -1166,7 +1175,7 @@
key: :hideSitename, key: :hideSitename,
label: "Hide Sitename", label: "Hide Sitename",
type: :boolean, type: :boolean,
description: "Hides instance name from PleromaFE banner." description: "Hides instance name from PleromaFE banner"
}, },
%{ %{
key: :hideUserStats, key: :hideUserStats,
@ -1211,14 +1220,14 @@
label: "NSFW Censor Image", label: "NSFW Censor Image",
type: :string, type: :string,
description: 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"] suggestions: ["/static/img/nsfw.74818f9.png"]
}, },
%{ %{
key: :postContentType, key: :postContentType,
label: "Post Content Type", label: "Post Content Type",
type: {:dropdown, :atom}, type: {:dropdown, :atom},
description: "Default post formatting option.", description: "Default post formatting option",
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"] suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"]
}, },
%{ %{
@ -1247,14 +1256,14 @@
key: :sidebarRight, key: :sidebarRight,
label: "Sidebar on Right", label: "Sidebar on Right",
type: :boolean, type: :boolean,
description: "Change alignment of sidebar and panels to the right." description: "Change alignment of sidebar and panels to the right"
}, },
%{ %{
key: :showFeaturesPanel, key: :showFeaturesPanel,
label: "Show instance features panel", label: "Show instance features panel",
type: :boolean, type: :boolean,
description: 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, key: :showInstanceSpecificPanel,
@ -1312,7 +1321,7 @@
key: :mascots, key: :mascots,
type: {:keyword, :map}, type: {:keyword, :map},
description: 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: [ suggestions: [
pleroma_fox_tan: %{ pleroma_fox_tan: %{
url: "/images/pleroma-fox-tan-smol.png", url: "/images/pleroma-fox-tan-smol.png",
@ -1336,7 +1345,7 @@
%{ %{
key: :default_user_avatar, key: :default_user_avatar,
type: :string, type: :string,
description: "URL of the default user avatar.", description: "URL of the default user avatar",
suggestions: ["/images/avi.png"] suggestions: ["/images/avi.png"]
} }
] ]
@ -1346,7 +1355,7 @@
key: :manifest, key: :manifest,
type: :group, type: :group,
description: 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: [ children: [
%{ %{
key: :icons, key: :icons,
@ -1384,7 +1393,7 @@
group: :pleroma, group: :pleroma,
key: :mrf_simple, key: :mrf_simple,
tab: :mrf, tab: :mrf,
label: "MRF simple", label: "MRF Simple",
type: :group, type: :group,
description: "Message Rewrite Facility", description: "Message Rewrite Facility",
children: [ children: [
@ -1466,7 +1475,7 @@
group: :pleroma, group: :pleroma,
key: :mrf_subchain, key: :mrf_subchain,
tab: :mrf, tab: :mrf,
label: "MRF subchain", label: "MRF Subchain",
type: :group, type: :group,
description: description:
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <> "This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
@ -1490,7 +1499,7 @@
tab: :mrf, tab: :mrf,
description: description:
"MRF RejectNonPublic settings. RejectNonPublic drops posts with non-public visibility settings.", "MRF RejectNonPublic settings. RejectNonPublic drops posts with non-public visibility settings.",
label: "MRF reject non public", label: "MRF Reject Non Public",
type: :group, type: :group,
children: [ children: [
%{ %{
@ -1510,7 +1519,7 @@
group: :pleroma, group: :pleroma,
key: :mrf_hellthread, key: :mrf_hellthread,
tab: :mrf, tab: :mrf,
label: "MRF hellthread", label: "MRF Hellthread",
type: :group, type: :group,
description: "Block messages with too much mentions", description: "Block messages with too much mentions",
children: [ children: [
@ -1535,7 +1544,7 @@
group: :pleroma, group: :pleroma,
key: :mrf_keyword, key: :mrf_keyword,
tab: :mrf, tab: :mrf,
label: "MRF keyword", label: "MRF Keyword",
type: :group, type: :group,
description: "Reject or Word-Replace messages with a keyword or regex", description: "Reject or Word-Replace messages with a keyword or regex",
children: [ children: [
@ -1566,14 +1575,14 @@
group: :pleroma, group: :pleroma,
key: :mrf_mention, key: :mrf_mention,
tab: :mrf, tab: :mrf,
label: "MRF mention", label: "MRF Mention",
type: :group, type: :group,
description: "Block messages which mention a user", description: "Block messages which mention a user",
children: [ children: [
%{ %{
key: :actors, key: :actors,
type: {:list, :string}, 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"] suggestions: ["actor1", "actor2"]
} }
] ]
@ -1582,7 +1591,7 @@
group: :pleroma, group: :pleroma,
key: :mrf_vocabulary, key: :mrf_vocabulary,
tab: :mrf, tab: :mrf,
label: "MRF vocabulary", label: "MRF Vocabulary",
type: :group, type: :group,
description: "Filter messages which belong to certain activity vocabularies", description: "Filter messages which belong to certain activity vocabularies",
children: [ children: [
@ -1590,14 +1599,14 @@
key: :accept, key: :accept,
type: {:list, :string}, type: {:list, :string},
description: 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"] suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
}, },
%{ %{
key: :reject, key: :reject,
type: {:list, :string}, type: {:list, :string},
description: 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"] suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
} }
] ]
@ -1627,6 +1636,7 @@
}, },
%{ %{
key: :base_url, key: :base_url,
label: "Base URL",
type: :string, type: :string,
description: description:
"The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.", "The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.",
@ -1659,6 +1669,7 @@
}, },
%{ %{
key: :proxy_opts, key: :proxy_opts,
label: "Proxy Options",
type: :keyword, type: :keyword,
description: "Options for Pleroma.ReverseProxy", description: "Options for Pleroma.ReverseProxy",
suggestions: [ suggestions: [
@ -1686,6 +1697,7 @@
}, },
%{ %{
key: :http, key: :http,
label: "HTTP",
type: :keyword, type: :keyword,
description: "HTTP options", description: "HTTP options",
children: [ children: [
@ -1781,6 +1793,7 @@
}, },
%{ %{
key: :ip, key: :ip,
label: "IP",
type: :tuple, type: :tuple,
description: "IP address to bind to", description: "IP address to bind to",
suggestions: [{0, 0, 0, 0}] suggestions: [{0, 0, 0, 0}]
@ -1794,7 +1807,7 @@
%{ %{
key: :dstport, key: :dstport,
type: :integer, type: :integer,
description: "Port advertised in urls (optional, defaults to port)", description: "Port advertised in URLs (optional, defaults to port)",
suggestions: [9999] suggestions: [9999]
} }
] ]
@ -1802,6 +1815,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :activitypub, key: :activitypub,
label: "ActivityPub",
type: :group, type: :group,
description: "ActivityPub-related settings", description: "ActivityPub-related settings",
children: [ children: [
@ -1824,7 +1838,7 @@
key: :note_replies_output_limit, key: :note_replies_output_limit,
type: :integer, type: :integer,
description: 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, key: :follow_handshake_timeout,
@ -1837,6 +1851,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :http_security, key: :http_security,
label: "HTTP security",
type: :group, type: :group,
description: "HTTP security settings", description: "HTTP security settings",
children: [ children: [
@ -1875,7 +1890,7 @@
key: :report_uri, key: :report_uri,
label: "Report URI", label: "Report URI",
type: :string, 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"] suggestions: ["https://example.com/report-uri"]
} }
] ]
@ -1883,9 +1898,10 @@
%{ %{
group: :web_push_encryption, group: :web_push_encryption,
key: :vapid_details, key: :vapid_details,
label: "Vapid Details",
type: :group, type: :group,
description: 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: [ children: [
%{ %{
key: :subject, key: :subject,
@ -1952,6 +1968,7 @@
}, },
%{ %{
group: :pleroma, group: :pleroma,
label: "Pleroma Admin Token",
type: :group, type: :group,
description: 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", "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",
@ -1959,7 +1976,7 @@
%{ %{
key: :admin_token, key: :admin_token,
type: :string, type: :string,
description: "Token", description: "Admin token",
suggestions: ["We recommend a secure random string or UUID"] suggestions: ["We recommend a secure random string or UUID"]
} }
] ]
@ -2124,24 +2141,24 @@
key: :rich_media, key: :rich_media,
type: :group, type: :group,
description: 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: [ children: [
%{ %{
key: :enabled, key: :enabled,
type: :boolean, type: :boolean,
description: "Enables RichMedia parsing of URLs." description: "Enables RichMedia parsing of URLs"
}, },
%{ %{
key: :ignore_hosts, key: :ignore_hosts,
type: {:list, :string}, 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"] suggestions: ["accounts.google.com", "xss.website"]
}, },
%{ %{
key: :ignore_tld, key: :ignore_tld,
label: "Ignore TLD", label: "Ignore TLD",
type: {:list, :string}, 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"] suggestions: ["local", "localdomain", "lan"]
}, },
%{ %{
@ -2169,31 +2186,32 @@
%{ %{
group: :auto_linker, group: :auto_linker,
key: :opts, key: :opts,
label: "Auto Linker",
type: :group, type: :group,
description: "Configuration for the auto_linker library", description: "Configuration for the auto_linker library",
children: [ children: [
%{ %{
key: :class, key: :class,
type: [:string, false], 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] suggestions: ["auto-linker", false]
}, },
%{ %{
key: :rel, key: :rel,
type: [:string, false], type: [:string, false],
description: "Override the rel attribute. Disable to clear", description: "Override the rel attribute. Disable to clear.",
suggestions: ["ugc", "noopener noreferrer", false] suggestions: ["ugc", "noopener noreferrer", false]
}, },
%{ %{
key: :new_window, key: :new_window,
type: :boolean, type: :boolean,
description: "Link urls will open in new window/tab" description: "Link URLs will open in new window/tab"
}, },
%{ %{
key: :truncate, key: :truncate,
type: [:integer, false], type: [:integer, false],
description: 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] suggestions: [15, false]
}, },
%{ %{
@ -2204,7 +2222,7 @@
%{ %{
key: :extra, key: :extra,
type: :boolean, type: :boolean,
description: "Link urls with rarely used schemes (magnet, ipfs, irc, etc.)" description: "Link URLs with rarely used schemes (magnet, ipfs, irc, etc.)"
} }
] ]
}, },
@ -2250,6 +2268,7 @@
}, },
%{ %{
group: :pleroma, group: :pleroma,
label: "Pleroma Authenticator",
type: :group, type: :group,
description: "Authenticator", description: "Authenticator",
children: [ children: [
@ -2263,6 +2282,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :ldap, key: :ldap,
label: "LDAP",
type: :group, type: :group,
description: description:
"Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <> "Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <>
@ -2349,6 +2369,7 @@
}, },
%{ %{
key: :uid, key: :uid,
label: "UID",
type: :string, type: :string,
description: description:
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"", "LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
@ -2364,11 +2385,12 @@
children: [ children: [
%{ %{
key: :enforce_oauth_admin_scope_usage, key: :enforce_oauth_admin_scope_usage,
label: "Enforce OAuth admin scope usage",
type: :boolean, type: :boolean,
description: description:
"OAuth admin scope requirement toggle. " <> "OAuth admin scope requirement toggle. " <>
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <> "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." "`is_admin` user flag grants access to admin-specific actions."
}, },
%{ %{
@ -2380,6 +2402,7 @@
}, },
%{ %{
key: :oauth_consumer_template, key: :oauth_consumer_template,
label: "OAuth consumer template",
type: :string, type: :string,
description: description:
"OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to" <> "OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to" <>
@ -2388,6 +2411,7 @@
}, },
%{ %{
key: :oauth_consumer_strategies, key: :oauth_consumer_strategies,
label: "OAuth consumer strategies",
type: {:list, :string}, type: {:list, :string},
description: description:
"The list of enabled OAuth consumer strategies. By default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <> "The list of enabled OAuth consumer strategies. By default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
@ -2516,7 +2540,7 @@
%{ %{
key: :enabled, key: :enabled,
type: :boolean, type: :boolean,
description: "enables new users admin digest email when `true`", description: "Enables new users admin digest email when `true`",
suggestions: [false] suggestions: [false]
} }
] ]
@ -2524,6 +2548,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :oauth2, key: :oauth2,
label: "OAuth2",
type: :group, type: :group,
description: "Configure OAuth 2 provider capabilities", description: "Configure OAuth 2 provider capabilities",
children: [ children: [
@ -2542,7 +2567,7 @@
%{ %{
key: :clean_expired_tokens, key: :clean_expired_tokens,
type: :boolean, 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."
} }
] ]
}, },
@ -2626,6 +2651,7 @@
}, },
%{ %{
key: :relation_id_action, key: :relation_id_action,
label: "Relation ID action",
type: [:tuple, {:list, :tuple}], type: [:tuple, {:list, :tuple}],
description: "For actions on relation with a specific user (follow, unfollow)", description: "For actions on relation with a specific user (follow, unfollow)",
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
@ -2639,6 +2665,7 @@
}, },
%{ %{
key: :status_id_action, key: :status_id_action,
label: "Status ID action",
type: [:tuple, {:list, :tuple}], type: [:tuple, {:list, :tuple}],
description: description:
"For fav / unfav or reblog / unreblog actions on the same status by the same user", "For fav / unfav or reblog / unreblog actions on the same status by the same user",
@ -2654,6 +2681,7 @@
}, },
%{ %{
group: :esshd, group: :esshd,
label: "ESSHD",
type: :group, type: :group,
description: description:
"Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <> "Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <>
@ -2692,8 +2720,9 @@
}, },
%{ %{
group: :mime, group: :mime,
label: "Mime Types",
type: :group, type: :group,
description: "Mime types", description: "Mime Types settings",
children: [ children: [
%{ %{
key: :types, key: :types,
@ -2752,6 +2781,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :http, key: :http,
label: "HTTP",
type: :group, type: :group,
description: "HTTP settings", description: "HTTP settings",
children: [ children: [
@ -2800,6 +2830,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :markup, key: :markup,
label: "Markup Settings",
type: :group, type: :group,
children: [ children: [
%{ %{
@ -2842,7 +2873,7 @@
group: :pleroma, group: :pleroma,
tab: :mrf, tab: :mrf,
key: :mrf_normalize_markup, key: :mrf_normalize_markup,
label: "MRF normalize markup", label: "MRF Normalize Markup",
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.", description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
type: :group, type: :group,
children: [ children: [
@ -2898,6 +2929,7 @@
}, },
%{ %{
group: :cors_plug, group: :cors_plug,
label: "CORS plug config",
type: :group, type: :group,
children: [ children: [
%{ %{
@ -2970,6 +3002,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :web_cache_ttl, key: :web_cache_ttl,
label: "Web cache TTL",
type: :group, type: :group,
description: description:
"The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.", "The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.",
@ -2992,9 +3025,10 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :static_fe, key: :static_fe,
label: "Static FE",
type: :group, type: :group,
description: 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: [ children: [
%{ %{
key: :enabled, key: :enabled,
@ -3012,18 +3046,18 @@
%{ %{
key: :post_title, key: :post_title,
type: :map, type: :map,
description: "Configure title rendering.", description: "Configure title rendering",
children: [ children: [
%{ %{
key: :max_length, key: :max_length,
type: :integer, type: :integer,
description: "Maximum number of characters before truncating title.", description: "Maximum number of characters before truncating title",
suggestions: [100] suggestions: [100]
}, },
%{ %{
key: :omission, key: :omission,
type: :string, type: :string,
description: "Replacement which will be used after truncating string.", description: "Replacement which will be used after truncating string",
suggestions: ["..."] suggestions: ["..."]
} }
] ]
@ -3033,7 +3067,7 @@
%{ %{
group: :pleroma, group: :pleroma,
key: :mrf_object_age, key: :mrf_object_age,
label: "MRF object age", label: "MRF Object Age",
tab: :mrf, tab: :mrf,
type: :group, type: :group,
description: "Rejects or delists posts based on their age when received.", description: "Rejects or delists posts based on their age when received.",
@ -3077,13 +3111,13 @@
%{ %{
key: :workers, key: :workers,
type: :integer, type: :integer,
description: "Number of workers to send notifications.", description: "Number of workers to send notifications",
suggestions: [3] suggestions: [3]
}, },
%{ %{
key: :overflow_workers, key: :overflow_workers,
type: :integer, type: :integer,
description: "Maximum number of workers created if pool is empty.", description: "Maximum number of workers created if pool is empty",
suggestions: [2] suggestions: [2]
} }
] ]

View file

@ -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 - `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 - `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. - `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 ## 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: 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 - `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_moderator`: boolean, nullable, true if user is a moderator
- `is_admin`: boolean, nullable, true if user is an admin - `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 - `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_followers`: boolean, true when the user has follower hiding enabled
- `hide_follows`: boolean, true when the user has follow 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 - `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 - `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_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. - `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 ### Source

View file

@ -109,7 +109,7 @@ def extract_first_external_url(object, content) do
result = result =
content content
|> Floki.parse_fragment!() |> 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") |> Floki.attribute("a", "href")
|> Enum.at(0) |> Enum.at(0)

View file

@ -367,6 +367,7 @@ defp do_create_notifications(%Activity{} = activity, options) do
do_send = do_send && user in enabled_receivers do_send = do_send && user in enabled_receivers
create_notification(activity, user, do_send) create_notification(activity, user, do_send)
end) end)
|> Enum.reject(&is_nil/1)
{:ok, notifications} {:ok, notifications}
end end

View file

@ -83,8 +83,8 @@ def fetch_object_from_id(id, options \\ []) do
{:transmogrifier, {:error, {:reject, nil}}} -> {:transmogrifier, {:error, {:reject, nil}}} ->
{:reject, nil} {:reject, nil}
{:transmogrifier, _} -> {:transmogrifier, _} = e ->
{:error, "Transmogrifier failure."} {:error, e}
{:object, data, nil} -> {:object, data, nil} ->
reinject_object(%Object{}, data) reinject_object(%Object{}, data)

View file

@ -115,7 +115,7 @@ defmodule Pleroma.User do
field(:is_moderator, :boolean, default: false) field(:is_moderator, :boolean, default: false)
field(:is_admin, :boolean, default: false) field(:is_admin, :boolean, default: false)
field(:show_role, :boolean, default: true) field(:show_role, :boolean, default: true)
field(:settings, :map, default: nil) field(:mastofe_settings, :map, default: nil)
field(:uri, ObjectValidators.Uri, default: nil) field(:uri, ObjectValidators.Uri, default: nil)
field(:hide_followers_count, :boolean, default: false) field(:hide_followers_count, :boolean, default: false)
field(:hide_follows_count, :boolean, default: false) field(:hide_follows_count, :boolean, default: false)
@ -1309,7 +1309,8 @@ def block(%User{} = blocker, %User{} = blocked) do
unsubscribe(blocked, blocker) 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} = update_follower_count(blocker)
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked) {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
@ -1527,8 +1528,7 @@ def perform(:blocks_import, %User{} = blocker, blocked_identifiers)
blocked_identifiers, blocked_identifiers,
fn blocked_identifier -> fn blocked_identifier ->
with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier), with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier),
{:ok, _user_block} <- block(blocker, blocked), {:ok, _block} <- CommonAPI.block(blocker, blocked) do
{:ok, _} <- ActivityPub.block(blocker, blocked) do
blocked blocked
else else
err -> err ->
@ -2118,8 +2118,8 @@ def mascot_update(user, url) do
def mastodon_settings_update(user, settings) do def mastodon_settings_update(user, settings) do
user user
|> cast(%{settings: settings}, [:settings]) |> cast(%{mastofe_settings: settings}, [:mastofe_settings])
|> validate_required([:settings]) |> validate_required([:mastofe_settings])
|> update_and_set_cache() |> update_and_set_cache()
end end

View file

@ -366,33 +366,6 @@ defp do_unfollow(follower, followed, activity_id, local) do
end end
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()} @spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
def flag( def flag(
%{ %{
@ -1398,6 +1371,16 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
end end
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 def make_user_from_ap_id(ap_id) do
user = User.get_cached_by_ap_id(ap_id) user = User.get_cached_by_ap_id(ap_id)
@ -1410,6 +1393,8 @@ def make_user_from_ap_id(ap_id) do
|> User.remote_user_changeset(data) |> User.remote_user_changeset(data)
|> User.update_and_set_cache() |> User.update_and_set_cache()
else else
maybe_handle_clashing_nickname(data[:nickname])
data data
|> User.remote_user_changeset() |> User.remote_user_changeset()
|> Repo.insert() |> Repo.insert()

View file

@ -138,6 +138,18 @@ def update(actor, object) do
}, []} }, []}
end 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()} @spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
def announce(actor, object, options \\ []) do def announce(actor, object, options \\ []) do
public? = Keyword.get(options, :public, false) public? = Keyword.get(options, :public, false)

View file

@ -27,11 +27,14 @@ defp contains_links?(_), do: false
@impl true @impl true
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do 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)}, {:contains_links, true} <- {:contains_links, contains_links?(object)},
{:old_user, true} <- {:old_user, old_user?(u)} do {:old_user, true} <- {:old_user, old_user?(u)} do
{:ok, message} {:ok, message}
else else
{:ok, %User{local: true}} ->
{:ok, message}
{:contains_links, false} -> {:contains_links, false} ->
{:ok, message} {:ok, message}

View file

@ -13,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator 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()} @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
def validate(object, meta) 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 def validate(%{"type" => "Update"} = update_activity, meta) do
with {:ok, update_activity} <- with {:ok, update_activity} <-
update_activity update_activity

View file

@ -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

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
collection, and so on. collection, and so on.
""" """
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Activity.Ir.Topics
alias Pleroma.Chat alias Pleroma.Chat
alias Pleroma.Chat.MessageReference alias Pleroma.Chat.MessageReference
alias Pleroma.Notification alias Pleroma.Notification
@ -20,6 +21,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
def handle(object, meta \\ []) 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: # Tasks this handles:
# - Update the user # - Update the user
# #
@ -82,7 +98,10 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) do
if !User.is_internal_user?(user) do if !User.is_internal_user?(user) do
Notification.create_notifications(object) Notification.create_notifications(object)
ActivityPub.stream_out(object)
object
|> Topics.get_activity_topics()
|> Streamer.stream(object)
end end
{:ok, object, meta} {:ok, object, meta}

View file

@ -446,12 +446,9 @@ def handle_incoming(
when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer", "Audio"] do when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer", "Audio"] do
actor = Containment.get_actor(data) actor = Containment.get_actor(data)
data =
Map.put(data, "actor", actor)
|> fix_addressing
with nil <- Activity.get_create_by_object_ap_id(object["id"]), 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) object = fix_object(object, options)
params = %{ params = %{
@ -673,7 +670,7 @@ def handle_incoming(
end end
def handle_incoming(%{"type" => type} = data, _options) 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), with :ok <- ObjectValidator.fetch_actor_and_object(data),
{:ok, activity, _meta} <- {:ok, activity, _meta} <-
Pipeline.common_pipeline(data, local: false) do Pipeline.common_pipeline(data, local: false) do
@ -684,9 +681,10 @@ def handle_incoming(%{"type" => type} = data, _options)
end end
def handle_incoming( def handle_incoming(
%{"type" => "Update"} = data, %{"type" => type} = data,
_options _options
) do )
when type in ~w{Update Block} do
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity} {:ok, activity}
@ -765,21 +763,6 @@ def handle_incoming(
end end
end 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( def handle_incoming(
%{ %{
"type" => "Move", "type" => "Move",

View file

@ -47,6 +47,10 @@ def is_list?(_), do: false
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean() @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?(%{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 def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
user.ap_id in activity.data["to"] || user.ap_id in activity.data["to"] ||
list_ap_id list_ap_id
@ -54,8 +58,6 @@ def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{
|> Pleroma.List.member?(user) |> Pleroma.List.member?(user)
end end
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
def visible_for_user?(%{local: local} = activity, nil) do def visible_for_user?(%{local: local} = activity, nil) do
cfg_key = cfg_key =
if local, if local,

View file

@ -40,7 +40,7 @@ def call(%{private: %{open_api_spex: private_data}} = conn, %{
|> List.first() |> List.first()
_ -> _ ->
nil "application/json"
end end
private_data = Map.put(private_data, :operation_id, operation_id) private_data = Map.put(private_data, :operation_id, operation_id)

View file

@ -40,20 +40,53 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
pleroma: %Schema{ pleroma: %Schema{
type: :object, type: :object,
properties: %{ properties: %{
allow_following_move: %Schema{type: :boolean}, allow_following_move: %Schema{
background_image: %Schema{type: :string, nullable: true}, 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}, 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_favorites: %Schema{type: :boolean},
hide_followers_count: %Schema{type: :boolean}, hide_followers_count: %Schema{
hide_followers: %Schema{type: :boolean}, type: :boolean,
hide_follows_count: %Schema{type: :boolean}, description: "whether the user has follower stat hiding enabled"
hide_follows: %Schema{type: :boolean}, },
is_admin: %Schema{type: :boolean}, hide_followers: %Schema{
is_moderator: %Schema{type: :boolean}, 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}, skip_thread_containment: %Schema{type: :boolean},
tags: %Schema{type: :array, items: %Schema{type: :string}}, tags: %Schema{
unread_conversation_count: %Schema{type: :integer}, 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{ notification_settings: %Schema{
type: :object, type: :object,
properties: %{ properties: %{
@ -66,7 +99,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
}, },
relationship: AccountRelationship, relationship: AccountRelationship,
settings_store: %Schema{ 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, type: :object,
properties: %{ properties: %{
fields: %Schema{type: :array, items: AccountField}, 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, privacy: VisibilityScope,
sensitive: %Schema{type: :boolean}, sensitive: %Schema{type: :boolean},
pleroma: %Schema{ pleroma: %Schema{
type: :object, type: :object,
properties: %{ properties: %{
actor_type: ActorType, actor_type: ActorType,
discoverable: %Schema{type: :boolean}, discoverable: %Schema{
no_rich_text: %Schema{type: :boolean}, type: :boolean,
show_role: %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"
}
} }
} }
} }

View file

@ -184,6 +184,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
thread_muted: %Schema{ thread_muted: %Schema{
type: :boolean, type: :boolean,
description: "`true` if the thread the post belongs to is muted" 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"
} }
} }
}, },

View file

@ -25,6 +25,13 @@ defmodule Pleroma.Web.CommonAPI do
require Pleroma.Constants require Pleroma.Constants
require Logger 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 def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]), with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
:ok <- validate_chat_content_length(content, !!maybe_attachment), :ok <- validate_chat_content_length(content, !!maybe_attachment),

View file

@ -385,8 +385,7 @@ def unmute(%{assigns: %{user: muter, account: muted}} = conn, _params) do
@doc "POST /api/v1/accounts/:id/block" @doc "POST /api/v1/accounts/:id/block"
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
with {:ok, _user_block} <- User.block(blocker, blocked), with {:ok, _activity} <- CommonAPI.block(blocker, blocked) do
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
render(conn, "relationship.json", user: blocker, target: blocked) render(conn, "relationship.json", user: blocker, target: blocked)
else else
{:error, message} -> json_response(conn, :forbidden, %{error: message}) {:error, message} -> json_response(conn, :forbidden, %{error: message})

View file

@ -21,7 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy 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. # TODO: Add cached version.
defp get_replied_to_activities([]), do: %{} defp get_replied_to_activities([]), do: %{}
@ -364,7 +364,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
expires_at: expires_at, expires_at: expires_at,
direct_conversation_id: direct_conversation_id, direct_conversation_id: direct_conversation_id,
thread_muted: thread_muted?, thread_muted: thread_muted?,
emoji_reactions: emoji_reactions emoji_reactions: emoji_reactions,
parent_visible: visible_for_user?(reply_to, opts[:for])
} }
} }
end end

View file

@ -19,35 +19,7 @@ def get_nodeinfo("2.0") do
|> Enum.map(fn u -> u.ap_id end) |> Enum.map(fn u -> u.ap_id end)
federation = InstanceView.federation() federation = InstanceView.federation()
features = InstanceView.features()
features =
[
"pleroma_api",
"mastodon_api",
"mastodon_api_streaming",
"polls",
"pleroma_explicit_addressing",
"shareable_emoji_packs",
"multifetch",
"pleroma:api/v1/notifications:include_types_filter",
if Config.get([:media_proxy, :enabled]) do
"media_proxy"
end,
if Config.get([:gopher, :enabled]) do
"gopher"
end,
if Config.get([:chat, :enabled]) do
"chat"
end,
if Config.get([:instance, :allow_relay]) do
"relay"
end,
if Config.get([:instance, :safe_dm_mentions]) do
"safe_dm_mentions"
end,
"pleroma_emoji_reactions"
]
|> Enum.filter(& &1)
%{ %{
version: "2.0", version: "2.0",

View file

@ -3,14 +3,15 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.Instance do defmodule Pleroma.Web.Preload.Providers.Instance do
alias Pleroma.Plugs.InstanceStatic
alias Pleroma.Web.MastodonAPI.InstanceView alias Pleroma.Web.MastodonAPI.InstanceView
alias Pleroma.Web.Nodeinfo.Nodeinfo alias Pleroma.Web.Nodeinfo.Nodeinfo
alias Pleroma.Web.Preload.Providers.Provider alias Pleroma.Web.Preload.Providers.Provider
@behaviour Provider @behaviour Provider
@instance_url :"/api/v1/instance" @instance_url "/api/v1/instance"
@panel_url :"/instance/panel.html" @panel_url "/instance/panel.html"
@nodeinfo_url :"/nodeinfo/2.0" @nodeinfo_url "/nodeinfo/2.0.json"
@impl Provider @impl Provider
def generate_terms(_params) do def generate_terms(_params) do
@ -27,7 +28,7 @@ defp build_info_tag(acc) do
end end
defp build_panel_tag(acc) do 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 if File.exists?(instance_path) do
panel_data = File.read!(instance_path) panel_data = File.read!(instance_path)

View file

@ -4,10 +4,10 @@
defmodule Pleroma.Web.Preload.Providers.StatusNet do defmodule Pleroma.Web.Preload.Providers.StatusNet do
alias Pleroma.Web.Preload.Providers.Provider alias Pleroma.Web.Preload.Providers.Provider
alias Pleroma.Web.TwitterAPI.UtilView alias Pleroma.Web.TwitterAPI.UtilController
@behaviour Provider @behaviour Provider
@config_url :"/api/statusnet/config.json" @config_url "/api/statusnet/config.json"
@impl Provider @impl Provider
def generate_terms(_params) do def generate_terms(_params) do
@ -16,9 +16,10 @@ def generate_terms(_params) do
end end
defp build_config_tag(acc) do defp build_config_tag(acc) do
instance = Pleroma.Config.get(:instance) resp =
info_data = UtilView.status_net_config(instance) 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
end end

View file

@ -8,7 +8,7 @@ defmodule Pleroma.Web.Preload.Providers.Timelines do
alias Pleroma.Web.Preload.Providers.Provider alias Pleroma.Web.Preload.Providers.Provider
@behaviour Provider @behaviour Provider
@public_url :"/api/v1/timelines/public" @public_url "/api/v1/timelines/public"
@impl Provider @impl Provider
def generate_terms(params) do def generate_terms(params) do

View file

@ -3,11 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.User do defmodule Pleroma.Web.Preload.Providers.User do
alias Pleroma.User
alias Pleroma.Web.MastodonAPI.AccountView alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.Preload.Providers.Provider alias Pleroma.Web.Preload.Providers.Provider
@behaviour Provider @behaviour Provider
@account_url :"/api/v1/accounts" @account_url_base "/api/v1/accounts"
@impl Provider @impl Provider
def generate_terms(%{user: user}) do def generate_terms(%{user: user}) do
@ -16,10 +17,10 @@ def generate_terms(%{user: user}) do
def generate_terms(_params), do: %{} def generate_terms(_params), do: %{}
def build_accounts_tag(acc, nil), do: acc def build_accounts_tag(acc, %User{} = user) do
def build_accounts_tag(acc, user) do
account_data = AccountView.render("show.json", %{user: user, for: user}) 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 end
def build_accounts_tag(acc, _), do: acc
end end

View file

@ -116,6 +116,7 @@ def filtered_by_user?(%User{} = user, %Activity{} = item) do
true <- true <-
Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)), 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 <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,
true <- !(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 <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),
true <- MapSet.disjoint?(recipients, recipient_blocks), true <- MapSet.disjoint?(recipients, recipient_blocks),
%{host: item_host} <- URI.parse(item.actor), %{host: item_host} <- URI.parse(item.actor),

View file

@ -86,7 +86,7 @@ def initial_state(token, user, custom_emojis) do
"video\/mp4" "video\/mp4"
] ]
}, },
settings: user.settings || @default_settings, settings: user.mastofe_settings || @default_settings,
push_subscription: nil, push_subscription: nil,
accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)}, accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis), custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),

View file

@ -159,7 +159,10 @@ defp deps do
{:cors_plug, "~> 1.5"}, {:cors_plug, "~> 1.5"},
{:ex_doc, "~> 0.21", only: :dev, runtime: false}, {:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"}, {: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"}, {:phoenix_swoosh, "~> 0.2"},
{:gen_smtp, "~> 0.13"}, {:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}, {:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},

View file

@ -104,9 +104,9 @@
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, "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"}, "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"}, "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"}, "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"]}, "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"}, "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"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},

View file

@ -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

View file

@ -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&apos;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&apos;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"
}

View file

@ -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&#39;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"
}

43
test/fixtures/fetch_mocks/eal.json vendored Normal file
View file

@ -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"
}

View file

@ -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"
}

View file

@ -0,0 +1 @@
HEY!

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -237,5 +237,19 @@ test "does not crash when there is an HTML entity in a link" do
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"]) assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"])
end 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
end end

View file

@ -22,6 +22,16 @@ defmodule Pleroma.NotificationTest do
alias Pleroma.Web.Streamer alias Pleroma.Web.Streamer
describe "create_notifications" do 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 test "creates a notification for an emoji reaction" do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)

View file

@ -26,6 +26,46 @@ defmodule Pleroma.Object.FetcherTest do
:ok :ok
end 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 describe "max thread distance restriction" do
@ap_id "http://mastodon.example.org/@admin/99541947525187367" @ap_id "http://mastodon.example.org/@admin/99541947525187367"
setup do: clear_config([:instance, :federation_incoming_replies_max_depth]) setup do: clear_config([:instance, :federation_incoming_replies_max_depth])

View file

@ -597,6 +597,31 @@ test "updates an existing user, if stale" do
refute user.last_refreshed_at == orig_user.last_refreshed_at refute user.last_refreshed_at == orig_user.last_refreshed_at
end 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 @tag capture_log: true
test "it returns the old user if stale, but unfetchable" do test "it returns the old user if stale, but unfetchable" do
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800) a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)

View file

@ -992,54 +992,6 @@ test "creates an undo activity for a pending follow request" do
end end
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 describe "timeline post-processing" do
test "it filters broken threads" do test "it filters broken threads" do
user1 = insert(:user) user1 = insert(:user)

View file

@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
describe "with new user" do describe "with new user" do
test "it allows posts without links" do test "it allows posts without links" do
user = insert(:user) user = insert(:user, local: false)
assert user.note_count == 0 assert user.note_count == 0
@ -45,7 +45,7 @@ test "it allows posts without links" do
end end
test "it disallows posts with links" do test "it disallows posts with links" do
user = insert(:user) user = insert(:user, local: false)
assert user.note_count == 0 assert user.note_count == 0
@ -55,6 +55,18 @@ test "it disallows posts with links" do
{:reject, _} = AntiLinkSpamPolicy.filter(message) {:reject, _} = AntiLinkSpamPolicy.filter(message)
end 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 end
describe "with old user" do describe "with old user" do

View file

@ -654,4 +654,31 @@ test "returns an error if the object can't be updated by the actor", %{
assert {:error, _cng} = ObjectValidator.validate(update, []) assert {:error, _cng} = ObjectValidator.validate(update, [])
end end
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 end

View file

@ -64,6 +64,47 @@ test "it streams out notifications and streams" do
end end
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 describe "update users" do
setup do setup do
user = insert(:user) user = insert(:user)
@ -242,8 +283,7 @@ test "when activation is required", %{delete: delete, user: user} do
{:ok, like} = CommonAPI.favorite(user, post.id) {:ok, like} = CommonAPI.favorite(user, post.id)
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
{:ok, announce} = CommonAPI.repeat(post.id, user) {:ok, announce} = CommonAPI.repeat(post.id, user)
{:ok, block} = ActivityPub.block(user, poster) {:ok, block} = CommonAPI.block(user, poster)
User.block(user, poster)
{:ok, undo_data, _meta} = Builder.undo(user, like) {:ok, undo_data, _meta} = Builder.undo(user, like)
{:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
@ -549,10 +589,29 @@ test "creates a notification", %{announce: announce, poster: poster} do
end end
test "it streams out the announce", %{announce: announce} do 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) {: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 end
end end

View file

@ -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

View file

@ -445,56 +445,6 @@ test "it works for incoming follows to locked account" do
assert [^pending_follower] = User.get_follow_requests(user) assert [^pending_follower] = User.get_follow_requests(user)
end 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 test "it works for incoming accepts which were pre-accepted" do
follower = insert(:user) follower = insert(:user)
followed = insert(:user) followed = insert(:user)

View file

@ -27,16 +27,6 @@ test "fetches the latest Follow activity" do
end end
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 describe "determine_explicit_mentions()" do
test "works with an object that has mentions" do test "works with an object that has mentions" do
object = %{ object = %{
@ -344,9 +334,9 @@ test "fetches last block activities" do
user1 = insert(:user) user1 = insert(:user)
user2 = insert(:user) user2 = insert(:user)
assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2) assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)
assert Utils.fetch_latest_block(user1, user2) == activity assert Utils.fetch_latest_block(user1, user2) == activity
end end

View file

@ -25,6 +25,52 @@ defmodule Pleroma.Web.CommonAPITest do
setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :limit])
setup do: clear_config([:instance, :max_pinned_statuses]) 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 describe "posting chat messages" do
setup do: clear_config([:instance, :chat_limit]) setup do: clear_config([:instance, :chat_limit])

View file

@ -24,7 +24,7 @@ test "put settings", %{conn: conn} do
assert _result = json_response(conn, 200) assert _result = json_response(conn, 200)
user = User.get_cached_by_ap_id(user.ap_id) user = User.get_cached_by_ap_id(user.ap_id)
assert user.settings == %{"programming" => "socks"} assert user.mastofe_settings == %{"programming" => "socks"}
end end
describe "index/2 redirections" do describe "index/2 redirections" do

View file

@ -780,7 +780,6 @@ test "with notifications", %{conn: conn} do
assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
conn conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{other_user.id}/mute") |> post("/api/v1/accounts/#{other_user.id}/mute")
|> json_response_and_validate_schema(200) |> json_response_and_validate_schema(200)

View file

@ -226,7 +226,8 @@ test "a note activity" do
expires_at: nil, expires_at: nil,
direct_conversation_id: nil, direct_conversation_id: nil,
thread_muted: false, thread_muted: false,
emoji_reactions: [] emoji_reactions: [],
parent_visible: false
} }
} }
@ -620,4 +621,20 @@ test "visibility/list" do
assert status.visibility == "list" assert status.visibility == "list"
end 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 end

View file

@ -142,7 +142,8 @@ test "it shows default features flags", %{conn: conn} do
"shareable_emoji_packs", "shareable_emoji_packs",
"multifetch", "multifetch",
"pleroma_emoji_reactions", "pleroma_emoji_reactions",
"pleroma:api/v1/notifications:include_types_filter" "pleroma:api/v1/notifications:include_types_filter",
"pleroma_chat_messages"
] ]
assert MapSet.subset?( assert MapSet.subset?(

View file

@ -8,7 +8,7 @@ defmodule Pleroma.Web.Preload.Providers.InstanceTest do
setup do: {:ok, Instance.generate_terms(nil)} 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 %{ assert %{
description: description, description: description,
email: "admin@example.com", email: "admin@example.com",
@ -18,14 +18,25 @@ test "it renders the info", %{"/api/v1/instance": info} do
assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server")
end end
test "it renders the panel", %{"/instance/panel.html": panel} do test "it renders the panel", %{"/instance/panel.html" => panel} do
assert String.contains?( assert String.contains?(
panel, panel,
"<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" "<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>"
) )
end 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, metadata: metadata,
version: "2.0" version: "2.0"

View file

@ -8,7 +8,8 @@ defmodule Pleroma.Web.Preload.Providers.StatusNetTest do
setup do: {:ok, StatusNet.generate_terms(nil)} setup do: {:ok, StatusNet.generate_terms(nil)}
test "it renders the info", %{"/api/statusnet/config.json": info} do test "it renders the info", %{"/api/statusnet/config.json" => info} do
assert info =~ "<name>Pleroma</name>" assert {:ok, res} = Jason.decode(info)
assert res["site"]
end end
end end

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.Preload.Providers.TimelineTest do
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Preload.Providers.Timelines alias Pleroma.Web.Preload.Providers.Timelines
@public_url :"/api/v1/timelines/public" @public_url "/api/v1/timelines/public"
describe "unauthenticated timeliness when restricted" do describe "unauthenticated timeliness when restricted" do
setup do setup do

View file

@ -9,13 +9,11 @@ defmodule Pleroma.Web.Preload.Providers.UserTest do
describe "returns empty when user doesn't exist" do describe "returns empty when user doesn't exist" do
test "nil user specified" do test "nil user specified" do
refute User.generate_terms(%{user: nil}) assert User.generate_terms(%{user: nil}) == %{}
|> Map.has_key?("/api/v1/accounts")
end end
test "missing user specified" do test "missing user specified" do
refute User.generate_terms(%{user: :not_a_user}) assert User.generate_terms(%{user: :not_a_user}) == %{}
|> Map.has_key?("/api/v1/accounts")
end end
end end
@ -23,11 +21,13 @@ test "missing user specified" do
setup do setup do
user = insert(:user) user = insert(:user)
{:ok, User.generate_terms(%{user: user})} terms = User.generate_terms(%{user: user})
%{terms: terms, user: user}
end end
test "account is rendered", %{"/api/v1/accounts": accounts} do test "account is rendered", %{terms: terms, user: user} do
assert %{acct: user, username: user} = accounts account = terms["/api/v1/accounts/#{user.id}"]
assert %{acct: user, username: user} = account
end end
end end
end end

View file

@ -116,6 +116,18 @@ test "it streams boosts of the user in the 'user' stream", %{user: user} do
refute Streamer.filtered_by_user?(user, announce) refute Streamer.filtered_by_user?(user, announce)
end 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 streams boosts of mastodon user in the 'user' stream", %{user: user} do test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do
Streamer.get_topic_and_add_socket("user", user) Streamer.get_topic_and_add_socket("user", user)