API Changes
+- Mastodon API: Add pleroma.parents_visible field to statuses.
- Mastodon API: Extended `/api/v1/instance`.
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
diff --git a/config/config.exs b/config/config.exs
index 5b1c576e7..9b550920c 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -437,8 +437,6 @@
config :pleroma, Pleroma.Web.Preload,
providers: [
Pleroma.Web.Preload.Providers.Instance,
- Pleroma.Web.Preload.Providers.User,
- Pleroma.Web.Preload.Providers.Timelines,
Pleroma.Web.Preload.Providers.StatusNet
]
diff --git a/config/description.exs b/config/description.exs
index f54ac2a2a..370af80a6 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -40,12 +40,13 @@
key: :link_name,
type: :boolean,
description:
- "If enabled, a name parameter will be added to the url of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`."
+ "If enabled, a name parameter will be added to the URL of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`."
},
%{
key: :base_url,
+ label: "Base URL",
type: :string,
- description: "Base url for the uploads, needed if you use CDN",
+ description: "Base URL for the uploads, needed if you use CDN",
suggestions: [
"https://cdn-host.com"
]
@@ -58,6 +59,7 @@
},
%{
key: :proxy_opts,
+ label: "Proxy Options",
type: :keyword,
description: "Options for Pleroma.ReverseProxy",
suggestions: [
@@ -85,6 +87,7 @@
},
%{
key: :http,
+ label: "HTTP",
type: :keyword,
description: "HTTP options",
children: [
@@ -193,7 +196,9 @@
%{
key: :args,
type: [:string, {:list, :string}, {:list, :tuple}],
- description: "List of actions for the mogrify command",
+ description:
+ "List of actions for the mogrify command. It's possible to add self-written settings as string. " <>
+ "For example `[\"auto-orient\", \"strip\", {\"resize\", \"3840x1080>\"}]` string will be parsed into list of the settings.",
suggestions: [
"strip",
"auto-orient",
@@ -479,6 +484,7 @@
%{
group: :pleroma,
key: :uri_schemes,
+ label: "URI Schemes",
type: :group,
description: "URI schemes related settings",
children: [
@@ -651,17 +657,17 @@
key: :invites_enabled,
type: :boolean,
description:
- "Enable user invitations for admins (depends on `registrations_open` being disabled)."
+ "Enable user invitations for admins (depends on `registrations_open` being disabled)"
},
%{
key: :account_activation_required,
type: :boolean,
- description: "Require users to confirm their emails before signing in."
+ description: "Require users to confirm their emails before signing in"
},
%{
key: :federating,
type: :boolean,
- description: "Enable federation with other instances."
+ description: "Enable federation with other instances"
},
%{
key: :federation_incoming_replies_max_depth,
@@ -679,7 +685,7 @@
label: "Fed. reachability timeout days",
type: :integer,
description:
- "Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.",
+ "Timeout (in days) of each external federation target being unreachable prior to pausing federating to it",
suggestions: [
7
]
@@ -801,6 +807,7 @@
},
%{
key: :safe_dm_mentions,
+ label: "Safe DM mentions",
type: :boolean,
description:
"If enabled, only mentions at the beginning of a post will be used to address people in direct messages." <>
@@ -840,7 +847,7 @@
%{
key: :skip_thread_containment,
type: :boolean,
- description: "Skip filtering out broken threads. Default: enabled"
+ description: "Skip filtering out broken threads. Default: enabled."
},
%{
key: :limit_to_local_content,
@@ -904,6 +911,7 @@
children: [
%{
key: :totp,
+ label: "TOTP settings",
type: :keyword,
description: "TOTP settings",
suggestions: [digits: 6, period: 30],
@@ -920,7 +928,7 @@
type: :integer,
suggestions: [30],
description:
- "a period for which the TOTP code will be valid, in seconds. Defaults to 30 seconds."
+ "A period for which the TOTP code will be valid, in seconds. Defaults to 30 seconds."
}
]
},
@@ -934,7 +942,7 @@
key: :number,
type: :integer,
suggestions: [5],
- description: "number of backup codes to generate."
+ description: "Number of backup codes to generate."
},
%{
key: :length,
@@ -974,6 +982,7 @@
group: :logger,
type: :group,
key: :ex_syslogger,
+ label: "ExSyslogger",
description: "ExSyslogger-related settings",
children: [
%{
@@ -992,7 +1001,7 @@
%{
key: :format,
type: :string,
- description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".",
+ description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"",
suggestions: ["$metadata[$level] $message"]
},
%{
@@ -1006,6 +1015,7 @@
group: :logger,
type: :group,
key: :console,
+ label: "Console Logger",
description: "Console logger settings",
children: [
%{
@@ -1017,7 +1027,7 @@
%{
key: :format,
type: :string,
- description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\".",
+ description: "Default: \"$date $time [$level] $levelpad$node $metadata $message\"",
suggestions: ["$metadata[$level] $message"]
},
%{
@@ -1030,6 +1040,7 @@
%{
group: :quack,
type: :group,
+ label: "Quack Logger",
description: "Quack-related settings",
children: [
%{
@@ -1140,19 +1151,19 @@
key: :greentext,
label: "Greentext",
type: :boolean,
- description: "Enables green text on lines prefixed with the > character."
+ description: "Enables green text on lines prefixed with the > character"
},
%{
key: :hideFilteredStatuses,
label: "Hide Filtered Statuses",
type: :boolean,
- description: "Hides filtered statuses from timelines."
+ description: "Hides filtered statuses from timelines"
},
%{
key: :hideMutedPosts,
label: "Hide Muted Posts",
type: :boolean,
- description: "Hides muted statuses from timelines."
+ description: "Hides muted statuses from timelines"
},
%{
key: :hidePostStats,
@@ -1164,7 +1175,7 @@
key: :hideSitename,
label: "Hide Sitename",
type: :boolean,
- description: "Hides instance name from PleromaFE banner."
+ description: "Hides instance name from PleromaFE banner"
},
%{
key: :hideUserStats,
@@ -1209,14 +1220,14 @@
label: "NSFW Censor Image",
type: :string,
description:
- "URL of the image to use for hiding NSFW media attachments in the timeline.",
+ "URL of the image to use for hiding NSFW media attachments in the timeline",
suggestions: ["/static/img/nsfw.74818f9.png"]
},
%{
key: :postContentType,
label: "Post Content Type",
type: {:dropdown, :atom},
- description: "Default post formatting option.",
+ description: "Default post formatting option",
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"]
},
%{
@@ -1245,14 +1256,14 @@
key: :sidebarRight,
label: "Sidebar on Right",
type: :boolean,
- description: "Change alignment of sidebar and panels to the right."
+ description: "Change alignment of sidebar and panels to the right"
},
%{
key: :showFeaturesPanel,
label: "Show instance features panel",
type: :boolean,
description:
- "Enables panel displaying functionality of the instance on the About page."
+ "Enables panel displaying functionality of the instance on the About page"
},
%{
key: :showInstanceSpecificPanel,
@@ -1310,7 +1321,7 @@
key: :mascots,
type: {:keyword, :map},
description:
- "Keyword of mascots, each element must contain both an url and a mime_type key",
+ "Keyword of mascots, each element must contain both an URL and a mime_type key",
suggestions: [
pleroma_fox_tan: %{
url: "/images/pleroma-fox-tan-smol.png",
@@ -1334,7 +1345,7 @@
%{
key: :default_user_avatar,
type: :string,
- description: "URL of the default user avatar.",
+ description: "URL of the default user avatar",
suggestions: ["/images/avi.png"]
}
]
@@ -1344,7 +1355,7 @@
key: :manifest,
type: :group,
description:
- "This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE",
+ "This section describe PWA manifest instance-specific values. Currently this option relate only for MastoFE.",
children: [
%{
key: :icons,
@@ -1380,10 +1391,49 @@
},
%{
group: :pleroma,
- key: :mrf_simple,
- label: "MRF simple",
+ key: :mrf,
+ tab: :mrf,
+ label: "MRF",
type: :group,
- description: "Message Rewrite Facility",
+ description: "General MRF settings",
+ children: [
+ %{
+ key: :policies,
+ type: [:module, {:list, :module}],
+ description:
+ "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
+ suggestions:
+ Generator.list_modules_in_dir(
+ "lib/pleroma/web/activity_pub/mrf",
+ "Elixir.Pleroma.Web.ActivityPub.MRF."
+ )
+ },
+ %{
+ key: :transparency,
+ label: "MRF transparency",
+ type: :boolean,
+ description:
+ "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
+ },
+ %{
+ key: :transparency_exclusions,
+ label: "MRF transparency exclusions",
+ type: {:list, :string},
+ description:
+ "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
+ suggestions: [
+ "exclusion.com"
+ ]
+ }
+ ]
+ },
+ %{
+ group: :pleroma,
+ key: :mrf_simple,
+ tab: :mrf,
+ label: "MRF Simple",
+ type: :group,
+ description: "Simple ingress policies",
children: [
%{
key: :media_removal,
@@ -1402,7 +1452,7 @@
key: :federated_timeline_removal,
type: {:list, :string},
description:
- "List of instances to remove from Federated (aka The Whole Known Network) Timeline",
+ "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
suggestions: ["example.com", "*.example.com"]
},
%{
@@ -1446,14 +1496,15 @@
%{
group: :pleroma,
key: :mrf_activity_expiration,
+ tab: :mrf,
label: "MRF Activity Expiration Policy",
type: :group,
- description: "Adds expiration to all local Create Note activities",
+ description: "Adds automatic expiration to all local activities",
children: [
%{
key: :days,
type: :integer,
- description: "Default global expiration time for all local Create activities (in days)",
+ description: "Default global expiration time for all local activities (in days)",
suggestions: [90, 365]
}
]
@@ -1461,7 +1512,8 @@
%{
group: :pleroma,
key: :mrf_subchain,
- label: "MRF subchain",
+ tab: :mrf,
+ label: "MRF Subchain",
type: :group,
description:
"This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
@@ -1482,9 +1534,9 @@
%{
group: :pleroma,
key: :mrf_rejectnonpublic,
- description:
- "MRF RejectNonPublic settings. RejectNonPublic drops posts with non-public visibility settings.",
- label: "MRF reject non public",
+ tab: :mrf,
+ description: "RejectNonPublic drops posts with non-public visibility settings.",
+ label: "MRF Reject Non Public",
type: :group,
children: [
%{
@@ -1503,16 +1555,17 @@
%{
group: :pleroma,
key: :mrf_hellthread,
- label: "MRF hellthread",
+ tab: :mrf,
+ label: "MRF Hellthread",
type: :group,
- description: "Block messages with too much mentions",
+ description: "Block messages with excessive user mentions",
children: [
%{
key: :delist_threshold,
type: :integer,
description:
- "Number of mentioned users after which the message gets delisted (the message can still be seen, " <>
- " but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable.",
+ "Number of mentioned users after which the message gets removed from timelines and" <>
+ "disables notifications. Set to 0 to disable.",
suggestions: [10]
},
%{
@@ -1527,7 +1580,8 @@
%{
group: :pleroma,
key: :mrf_keyword,
- label: "MRF keyword",
+ tab: :mrf,
+ label: "MRF Keyword",
type: :group,
description: "Reject or Word-Replace messages with a keyword or regex",
children: [
@@ -1557,14 +1611,15 @@
%{
group: :pleroma,
key: :mrf_mention,
- label: "MRF mention",
+ tab: :mrf,
+ label: "MRF Mention",
type: :group,
- description: "Block messages which mention a user",
+ description: "Block messages which mention a specific user",
children: [
%{
key: :actors,
type: {:list, :string},
- description: "A list of actors for which any post mentioning them will be dropped.",
+ description: "A list of actors for which any post mentioning them will be dropped",
suggestions: ["actor1", "actor2"]
}
]
@@ -1572,7 +1627,8 @@
%{
group: :pleroma,
key: :mrf_vocabulary,
- label: "MRF vocabulary",
+ tab: :mrf,
+ label: "MRF Vocabulary",
type: :group,
description: "Filter messages which belong to certain activity vocabularies",
children: [
@@ -1580,14 +1636,14 @@
key: :accept,
type: {:list, :string},
description:
- "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted",
+ "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
},
%{
key: :reject,
type: {:list, :string},
description:
- "A list of ActivityStreams terms to reject. If empty, no messages are rejected",
+ "A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
}
]
@@ -1617,6 +1673,7 @@
},
%{
key: :base_url,
+ label: "Base URL",
type: :string,
description:
"The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.",
@@ -1649,6 +1706,7 @@
},
%{
key: :proxy_opts,
+ label: "Proxy Options",
type: :keyword,
description: "Options for Pleroma.ReverseProxy",
suggestions: [
@@ -1676,6 +1734,7 @@
},
%{
key: :http,
+ label: "HTTP",
type: :keyword,
description: "HTTP options",
children: [
@@ -1771,6 +1830,7 @@
},
%{
key: :ip,
+ label: "IP",
type: :tuple,
description: "IP address to bind to",
suggestions: [{0, 0, 0, 0}]
@@ -1784,7 +1844,7 @@
%{
key: :dstport,
type: :integer,
- description: "Port advertised in urls (optional, defaults to port)",
+ description: "Port advertised in URLs (optional, defaults to port)",
suggestions: [9999]
}
]
@@ -1792,6 +1852,7 @@
%{
group: :pleroma,
key: :activitypub,
+ label: "ActivityPub",
type: :group,
description: "ActivityPub-related settings",
children: [
@@ -1814,7 +1875,7 @@
key: :note_replies_output_limit,
type: :integer,
description:
- "The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)."
+ "The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)"
},
%{
key: :follow_handshake_timeout,
@@ -1827,6 +1888,7 @@
%{
group: :pleroma,
key: :http_security,
+ label: "HTTP security",
type: :group,
description: "HTTP security settings",
children: [
@@ -1865,7 +1927,7 @@
key: :report_uri,
label: "Report URI",
type: :string,
- description: "Adds the specified url to report-uri and report-to group in CSP header",
+ description: "Adds the specified URL to report-uri and report-to group in CSP header",
suggestions: ["https://example.com/report-uri"]
}
]
@@ -1873,9 +1935,10 @@
%{
group: :web_push_encryption,
key: :vapid_details,
+ label: "Vapid Details",
type: :group,
description:
- "Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it",
+ "Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it.",
children: [
%{
key: :subject,
@@ -1942,6 +2005,7 @@
},
%{
group: :pleroma,
+ label: "Pleroma Admin Token",
type: :group,
description:
"Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter",
@@ -1949,7 +2013,7 @@
%{
key: :admin_token,
type: :string,
- description: "Token",
+ description: "Admin token",
suggestions: ["We recommend a secure random string or UUID"]
}
]
@@ -2114,24 +2178,24 @@
key: :rich_media,
type: :group,
description:
- "If enabled the instance will parse metadata from attached links to generate link previews.",
+ "If enabled the instance will parse metadata from attached links to generate link previews",
children: [
%{
key: :enabled,
type: :boolean,
- description: "Enables RichMedia parsing of URLs."
+ description: "Enables RichMedia parsing of URLs"
},
%{
key: :ignore_hosts,
type: {:list, :string},
- description: "List of hosts which will be ignored by the metadata parser.",
+ description: "List of hosts which will be ignored by the metadata parser",
suggestions: ["accounts.google.com", "xss.website"]
},
%{
key: :ignore_tld,
label: "Ignore TLD",
type: {:list, :string},
- description: "List TLDs (top-level domains) which will ignore for parse metadata.",
+ description: "List TLDs (top-level domains) which will ignore for parse metadata",
suggestions: ["local", "localdomain", "lan"]
},
%{
@@ -2159,31 +2223,32 @@
%{
group: :auto_linker,
key: :opts,
+ label: "Auto Linker",
type: :group,
description: "Configuration for the auto_linker library",
children: [
%{
key: :class,
type: [:string, false],
- description: "Specify the class to be added to the generated link. Disable to clear",
+ description: "Specify the class to be added to the generated link. Disable to clear.",
suggestions: ["auto-linker", false]
},
%{
key: :rel,
type: [:string, false],
- description: "Override the rel attribute. Disable to clear",
+ description: "Override the rel attribute. Disable to clear.",
suggestions: ["ugc", "noopener noreferrer", false]
},
%{
key: :new_window,
type: :boolean,
- description: "Link urls will open in new window/tab"
+ description: "Link URLs will open in new window/tab"
},
%{
key: :truncate,
type: [:integer, false],
description:
- "Set to a number to truncate urls longer then the number. Truncated urls will end in `..`",
+ "Set to a number to truncate URLs longer then the number. Truncated URLs will end in `..`",
suggestions: [15, false]
},
%{
@@ -2194,7 +2259,7 @@
%{
key: :extra,
type: :boolean,
- description: "Link urls with rarely used schemes (magnet, ipfs, irc, etc.)"
+ description: "Link URLs with rarely used schemes (magnet, ipfs, irc, etc.)"
}
]
},
@@ -2240,6 +2305,7 @@
},
%{
group: :pleroma,
+ label: "Pleroma Authenticator",
type: :group,
description: "Authenticator",
children: [
@@ -2253,6 +2319,7 @@
%{
group: :pleroma,
key: :ldap,
+ label: "LDAP",
type: :group,
description:
"Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <>
@@ -2339,6 +2406,7 @@
},
%{
key: :uid,
+ label: "UID",
type: :string,
description:
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
@@ -2354,11 +2422,12 @@
children: [
%{
key: :enforce_oauth_admin_scope_usage,
+ label: "Enforce OAuth admin scope usage",
type: :boolean,
description:
"OAuth admin scope requirement toggle. " <>
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
- "(client app must support admin scopes). If disabled and token doesn't have admin scope(s)," <>
+ "(client app must support admin scopes). If disabled and token doesn't have admin scope(s), " <>
"`is_admin` user flag grants access to admin-specific actions."
},
%{
@@ -2370,6 +2439,7 @@
},
%{
key: :oauth_consumer_template,
+ label: "OAuth consumer template",
type: :string,
description:
"OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to" <>
@@ -2378,6 +2448,7 @@
},
%{
key: :oauth_consumer_strategies,
+ label: "OAuth consumer strategies",
type: {:list, :string},
description:
"The list of enabled OAuth consumer strategies. By default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
@@ -2506,7 +2577,7 @@
%{
key: :enabled,
type: :boolean,
- description: "enables new users admin digest email when `true`",
+ description: "Enables new users admin digest email when `true`",
suggestions: [false]
}
]
@@ -2514,6 +2585,7 @@
%{
group: :pleroma,
key: :oauth2,
+ label: "OAuth2",
type: :group,
description: "Configure OAuth 2 provider capabilities",
children: [
@@ -2532,7 +2604,7 @@
%{
key: :clean_expired_tokens,
type: :boolean,
- description: "Enable a background job to clean expired oauth tokens. Default: disabled."
+ description: "Enable a background job to clean expired OAuth tokens. Default: disabled."
}
]
},
@@ -2616,6 +2688,7 @@
},
%{
key: :relation_id_action,
+ label: "Relation ID action",
type: [:tuple, {:list, :tuple}],
description: "For actions on relation with a specific user (follow, unfollow)",
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
@@ -2629,6 +2702,7 @@
},
%{
key: :status_id_action,
+ label: "Status ID action",
type: [:tuple, {:list, :tuple}],
description:
"For fav / unfav or reblog / unreblog actions on the same status by the same user",
@@ -2644,6 +2718,7 @@
},
%{
group: :esshd,
+ label: "ESSHD",
type: :group,
description:
"Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <>
@@ -2682,8 +2757,9 @@
},
%{
group: :mime,
+ label: "Mime Types",
type: :group,
- description: "Mime types",
+ description: "Mime Types settings",
children: [
%{
key: :types,
@@ -2742,6 +2818,7 @@
%{
group: :pleroma,
key: :http,
+ label: "HTTP",
type: :group,
description: "HTTP settings",
children: [
@@ -2790,6 +2867,7 @@
%{
group: :pleroma,
key: :markup,
+ label: "Markup Settings",
type: :group,
children: [
%{
@@ -2830,8 +2908,9 @@
},
%{
group: :pleroma,
+ tab: :mrf,
key: :mrf_normalize_markup,
- label: "MRF normalize markup",
+ label: "MRF Normalize Markup",
description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
type: :group,
children: [
@@ -2887,6 +2966,7 @@
},
%{
group: :cors_plug,
+ label: "CORS plug config",
type: :group,
children: [
%{
@@ -2959,6 +3039,7 @@
%{
group: :pleroma,
key: :web_cache_ttl,
+ label: "Web cache TTL",
type: :group,
description:
"The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.",
@@ -2981,9 +3062,10 @@
%{
group: :pleroma,
key: :static_fe,
+ label: "Static FE",
type: :group,
description:
- "Render profiles and posts using server-generated HTML that is viewable without using JavaScript.",
+ "Render profiles and posts using server-generated HTML that is viewable without using JavaScript",
children: [
%{
key: :enabled,
@@ -3001,18 +3083,18 @@
%{
key: :post_title,
type: :map,
- description: "Configure title rendering.",
+ description: "Configure title rendering",
children: [
%{
key: :max_length,
type: :integer,
- description: "Maximum number of characters before truncating title.",
+ description: "Maximum number of characters before truncating title",
suggestions: [100]
},
%{
key: :omission,
type: :string,
- description: "Replacement which will be used after truncating string.",
+ description: "Replacement which will be used after truncating string",
suggestions: ["..."]
}
]
@@ -3022,8 +3104,11 @@
%{
group: :pleroma,
key: :mrf_object_age,
+ label: "MRF Object Age",
+ tab: :mrf,
type: :group,
- description: "Rejects or delists posts based on their age when received.",
+ description:
+ "Rejects or delists posts based on their timestamp deviance from your server's clock.",
children: [
%{
key: :threshold,
@@ -3036,7 +3121,7 @@
type: {:list, :atom},
description:
"A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
- "`:strip_followers` removes followers from the ActivityPub recipient list, ensuring they won't be delivered to home timelines; " <>
+ "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
"`:reject` rejects the message entirely",
suggestions: [:delist, :strip_followers, :reject]
}
@@ -3064,13 +3149,13 @@
%{
key: :workers,
type: :integer,
- description: "Number of workers to send notifications.",
+ description: "Number of workers to send notifications",
suggestions: [3]
},
%{
key: :overflow_workers,
type: :integer,
- description: "Maximum number of workers created if pool is empty.",
+ description: "Maximum number of workers created if pool is empty",
suggestions: [2]
}
]
@@ -3361,41 +3446,5 @@
suggestions: [false]
}
]
- },
- %{
- group: :pleroma,
- key: :mrf,
- type: :group,
- description: "General MRF settings",
- children: [
- %{
- key: :policies,
- type: [:module, {:list, :module}],
- description:
- "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
- suggestions:
- Generator.list_modules_in_dir(
- "lib/pleroma/web/activity_pub/mrf",
- "Elixir.Pleroma.Web.ActivityPub.MRF."
- )
- },
- %{
- key: :transparency,
- label: "MRF transparency",
- type: :boolean,
- description:
- "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
- },
- %{
- key: :transparency_exclusions,
- label: "MRF transparency exclusions",
- type: {:list, :string},
- description:
- "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
- suggestions: [
- "exclusion.com"
- ]
- }
- ]
}
]
diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md
index 7c3546f4f..72b5984ae 100644
--- a/docs/API/differences_in_mastoapi_responses.md
+++ b/docs/API/differences_in_mastoapi_responses.md
@@ -27,6 +27,7 @@ Has these additional fields under the `pleroma` object:
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
- `thread_muted`: true if the thread the post belongs to is muted
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
+- `parent_visible`: If the parent of this post is visible to the user or not.
## Media Attachments
@@ -51,11 +52,14 @@ The `id` parameter can also be the `nickname` of the user. This only works in th
Has these additional fields under the `pleroma` object:
+- `ap_id`: nullable URL string, ActivityPub id of the user
+- `background_image`: nullable URL string, background image of the user
- `tags`: Lists an array of tags for the user
-- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/
+- `relationship` (object): Includes fields as documented for Mastodon API https://docs.joinmastodon.org/entities/relationship/
- `is_moderator`: boolean, nullable, true if user is a moderator
- `is_admin`: boolean, nullable, true if user is an admin
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
+- `hide_favorites`: boolean, true when the user has hiding favorites enabled
- `hide_followers`: boolean, true when the user has follower hiding enabled
- `hide_follows`: boolean, true when the user has follow hiding enabled
- `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
@@ -66,6 +70,7 @@ Has these additional fields under the `pleroma` object:
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
- `unread_notifications_count`: The count of unread notifications. Only returned to the account owner.
+- `notification_settings`: object, can be absent. See `/api/pleroma/notification_settings` for the parameters/keys returned.
### Source
@@ -223,6 +228,7 @@ Has theses additional parameters (which are the same as in Pleroma-API):
- `background_image`: A background image that frontends can use
- `pleroma.metadata.features`: A list of supported features
- `pleroma.metadata.federation`: The federation restrictions of this instance
+- `pleroma.metadata.fields_limits`: A list of values detailing the length and count limitation for various instance-configurable fields.
- `vapid_public_key`: The public key needed for push messages
## Markers
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index d78c5f202..dc1b9b840 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -109,7 +109,7 @@ def extract_first_external_url(object, content) do
result =
content
|> Floki.parse_fragment!()
- |> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"]")
+ |> Floki.filter_out("a.mention,a.hashtag,a.attachment,a[rel~=\"tag\"]")
|> Floki.attribute("a", "href")
|> Enum.at(0)
diff --git a/lib/pleroma/migration_helper/notification_backfill.ex b/lib/pleroma/migration_helper/notification_backfill.ex
index b3770307a..d260e62ca 100644
--- a/lib/pleroma/migration_helper/notification_backfill.ex
+++ b/lib/pleroma/migration_helper/notification_backfill.ex
@@ -3,7 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MigrationHelper.NotificationBackfill do
- alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
@@ -25,18 +24,27 @@ def fill_in_notification_types do
|> type_from_activity()
notification
- |> Notification.changeset(%{type: type})
+ |> Ecto.Changeset.change(%{type: type})
|> Repo.update()
end)
end
+ defp get_by_ap_id(ap_id) do
+ q =
+ from(u in User,
+ select: u.id
+ )
+
+ Repo.get_by(q, ap_id: ap_id)
+ end
+
# This is copied over from Notifications to keep this stable.
defp type_from_activity(%{data: %{"type" => type}} = activity) do
case type do
"Follow" ->
accepted_function = fn activity ->
- with %User{} = follower <- User.get_by_ap_id(activity.data["actor"]),
- %User{} = followed <- User.get_by_ap_id(activity.data["object"]) do
+ with %User{} = follower <- get_by_ap_id(activity.data["actor"]),
+ %User{} = followed <- get_by_ap_id(activity.data["object"]) do
Pleroma.FollowingRelationship.following?(follower, followed)
end
end
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index 9ee9606be..2ef1a80c5 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -367,6 +367,7 @@ defp do_create_notifications(%Activity{} = activity, options) do
do_send = do_send && user in enabled_receivers
create_notification(activity, user, do_send)
end)
+ |> Enum.reject(&is_nil/1)
{:ok, notifications}
end
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index 263ded5dd..3e2949ee2 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -83,8 +83,8 @@ def fetch_object_from_id(id, options \\ []) do
{:transmogrifier, {:error, {:reject, nil}}} ->
{:reject, nil}
- {:transmogrifier, _} ->
- {:error, "Transmogrifier failure."}
+ {:transmogrifier, _} = e ->
+ {:error, e}
{:object, data, nil} ->
reinject_object(%Object{}, data)
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 1d70a37ef..8a54546d6 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -115,7 +115,7 @@ defmodule Pleroma.User do
field(:is_moderator, :boolean, default: false)
field(:is_admin, :boolean, default: false)
field(:show_role, :boolean, default: true)
- field(:settings, :map, default: nil)
+ field(:mastofe_settings, :map, default: nil)
field(:uri, ObjectValidators.Uri, default: nil)
field(:hide_followers_count, :boolean, default: false)
field(:hide_follows_count, :boolean, default: false)
@@ -1309,7 +1309,8 @@ def block(%User{} = blocker, %User{} = blocked) do
unsubscribe(blocked, blocker)
- if following?(blocked, blocker), do: unfollow(blocked, blocker)
+ unfollowing_blocked = Config.get([:activitypub, :unfollow_blocked], true)
+ if unfollowing_blocked && following?(blocked, blocker), do: unfollow(blocked, blocker)
{:ok, blocker} = update_follower_count(blocker)
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
@@ -1527,8 +1528,7 @@ def perform(:blocks_import, %User{} = blocker, blocked_identifiers)
blocked_identifiers,
fn blocked_identifier ->
with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier),
- {:ok, _user_block} <- block(blocker, blocked),
- {:ok, _} <- ActivityPub.block(blocker, blocked) do
+ {:ok, _block} <- CommonAPI.block(blocker, blocked) do
blocked
else
err ->
@@ -2118,8 +2118,8 @@ def mascot_update(user, url) do
def mastodon_settings_update(user, settings) do
user
- |> cast(%{settings: settings}, [:settings])
- |> validate_required([:settings])
+ |> cast(%{mastofe_settings: settings}, [:mastofe_settings])
+ |> validate_required([:mastofe_settings])
|> update_and_set_cache()
end
diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex
index cec59c372..42ff1de78 100644
--- a/lib/pleroma/user/search.ex
+++ b/lib/pleroma/user/search.ex
@@ -52,6 +52,7 @@ defp search_query(query_string, for_user, following) do
|> base_query(following)
|> filter_blocked_user(for_user)
|> filter_invisible_users()
+ |> filter_internal_users()
|> filter_blocked_domains(for_user)
|> fts_search(query_string)
|> trigram_rank(query_string)
@@ -109,6 +110,10 @@ defp filter_invisible_users(query) do
from(q in query, where: q.invisible == false)
end
+ defp filter_internal_users(query) do
+ from(q in query, where: q.actor_type != "Application")
+ end
+
defp filter_blocked_user(query, %User{} = blocker) do
query
|> join(:left, [u], b in Pleroma.UserRelationship,
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 7cd3eab39..94117202c 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -366,33 +366,6 @@ defp do_unfollow(follower, followed, activity_id, local) do
end
end
- @spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
- {:ok, Activity.t()} | {:error, any()}
- def block(blocker, blocked, activity_id \\ nil, local \\ true) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
- result
- end
- end
-
- defp do_block(blocker, blocked, activity_id, local) do
- unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
-
- if unfollow_blocked and fetch_latest_follow(blocker, blocked) do
- unfollow(blocker, blocked, nil, local)
- end
-
- block_data = make_block_data(blocker, blocked, activity_id)
-
- with {:ok, activity} <- insert(block_data, local),
- _ <- notify_and_stream(activity),
- :ok <- maybe_federate(activity) do
- {:ok, activity}
- else
- {:error, error} -> Repo.rollback(error)
- end
- end
-
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
def flag(
%{
@@ -1398,6 +1371,16 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
end
end
+ def maybe_handle_clashing_nickname(nickname) do
+ with %User{} = old_user <- User.get_by_nickname(nickname) do
+ Logger.info("Found an old user for #{nickname}, ap id is #{old_user.ap_id}, renaming.")
+
+ old_user
+ |> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"})
+ |> User.update_and_set_cache()
+ end
+ end
+
def make_user_from_ap_id(ap_id) do
user = User.get_cached_by_ap_id(ap_id)
@@ -1410,6 +1393,8 @@ def make_user_from_ap_id(ap_id) do
|> User.remote_user_changeset(data)
|> User.update_and_set_cache()
else
+ maybe_handle_clashing_nickname(data[:nickname])
+
data
|> User.remote_user_changeset()
|> Repo.insert()
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 135a5c431..cabc28de9 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -138,6 +138,18 @@ def update(actor, object) do
}, []}
end
+ @spec block(User.t(), User.t()) :: {:ok, map(), keyword()}
+ def block(blocker, blocked) do
+ {:ok,
+ %{
+ "id" => Utils.generate_activity_id(),
+ "type" => "Block",
+ "actor" => blocker.ap_id,
+ "object" => blocked.ap_id,
+ "to" => [blocked.ap_id]
+ }, []}
+ end
+
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
def announce(actor, object, options \\ []) do
public? = Keyword.get(options, :public, false)
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
index 9e7800997..a7e187b5e 100644
--- a/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/anti_link_spam_policy.ex
@@ -27,11 +27,14 @@ defp contains_links?(_), do: false
@impl true
def filter(%{"type" => "Create", "actor" => actor, "object" => object} = message) do
- with {:ok, %User{} = u} <- User.get_or_fetch_by_ap_id(actor),
+ with {:ok, %User{local: false} = u} <- User.get_or_fetch_by_ap_id(actor),
{:contains_links, true} <- {:contains_links, contains_links?(object)},
{:old_user, true} <- {:old_user, old_user?(u)} do
{:ok, message}
else
+ {:ok, %User{local: true}} ->
+ {:ok, message}
+
{:contains_links, false} ->
{:ok, message}
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index 2c657b467..bb6324460 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -13,6 +13,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
+ alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
@@ -24,6 +25,25 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
def validate(object, meta)
+ def validate(%{"type" => "Block"} = block_activity, meta) do
+ with {:ok, block_activity} <-
+ block_activity
+ |> BlockValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ block_activity = stringify_keys(block_activity)
+ outgoing_blocks = Pleroma.Config.get([:activitypub, :outgoing_blocks])
+
+ meta =
+ if !outgoing_blocks do
+ Keyword.put(meta, :do_not_federate, true)
+ else
+ meta
+ end
+
+ {:ok, block_activity, meta}
+ end
+ end
+
def validate(%{"type" => "Update"} = update_activity, meta) do
with {:ok, update_activity} <-
update_activity
diff --git a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex
new file mode 100644
index 000000000..1dde77198
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex
@@ -0,0 +1,42 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index de143b8f0..61feeae4d 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
collection, and so on.
"""
alias Pleroma.Activity
+ alias Pleroma.Activity.Ir.Topics
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
alias Pleroma.Notification
@@ -20,6 +21,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
def handle(object, meta \\ [])
+ # Tasks this handles:
+ # - Unfollow and block
+ def handle(
+ %{data: %{"type" => "Block", "object" => blocked_user, "actor" => blocking_user}} =
+ object,
+ meta
+ ) do
+ with %User{} = blocker <- User.get_cached_by_ap_id(blocking_user),
+ %User{} = blocked <- User.get_cached_by_ap_id(blocked_user) do
+ User.block(blocker, blocked)
+ end
+
+ {:ok, object, meta}
+ end
+
# Tasks this handles:
# - Update the user
#
@@ -82,7 +98,10 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) do
if !User.is_internal_user?(user) do
Notification.create_notifications(object)
- ActivityPub.stream_out(object)
+
+ object
+ |> Topics.get_activity_topics()
+ |> Streamer.stream(object)
end
{:ok, object, meta}
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 4e318e89c..bc6fc4bd8 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -446,12 +446,9 @@ def handle_incoming(
when objtype in ["Article", "Event", "Note", "Video", "Page", "Question", "Answer", "Audio"] do
actor = Containment.get_actor(data)
- data =
- Map.put(data, "actor", actor)
- |> fix_addressing
-
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
- {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
+ {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor),
+ data <- Map.put(data, "actor", actor) |> fix_addressing() do
object = fix_object(object, options)
params = %{
@@ -673,7 +670,7 @@ def handle_incoming(
end
def handle_incoming(%{"type" => type} = data, _options)
- when type in ["Like", "EmojiReact", "Announce"] do
+ when type in ~w{Like EmojiReact Announce} do
with :ok <- ObjectValidator.fetch_actor_and_object(data),
{:ok, activity, _meta} <-
Pipeline.common_pipeline(data, local: false) do
@@ -684,9 +681,10 @@ def handle_incoming(%{"type" => type} = data, _options)
end
def handle_incoming(
- %{"type" => "Update"} = data,
+ %{"type" => type} = data,
_options
- ) do
+ )
+ when type in ~w{Update Block} do
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
@@ -765,21 +763,6 @@ def handle_incoming(
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(
%{
"type" => "Move",
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 453a6842e..343f41caa 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -47,6 +47,10 @@ def is_list?(_), do: false
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
+ def visible_for_user?(nil, _), do: false
+
+ def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
+
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
user.ap_id in activity.data["to"] ||
list_ap_id
@@ -54,8 +58,6 @@ def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{
|> Pleroma.List.member?(user)
end
- def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
-
def visible_for_user?(%{local: local} = activity, nil) do
cfg_key =
if local,
diff --git a/lib/pleroma/web/api_spec/cast_and_validate.ex b/lib/pleroma/web/api_spec/cast_and_validate.ex
index bd9026237..fbfc27d6f 100644
--- a/lib/pleroma/web/api_spec/cast_and_validate.ex
+++ b/lib/pleroma/web/api_spec/cast_and_validate.ex
@@ -40,7 +40,7 @@ def call(%{private: %{open_api_spex: private_data}} = conn, %{
|> List.first()
_ ->
- nil
+ "application/json"
end
private_data = Map.put(private_data, :operation_id, operation_id)
diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex
index d54e2158d..84f18f1b6 100644
--- a/lib/pleroma/web/api_spec/schemas/account.ex
+++ b/lib/pleroma/web/api_spec/schemas/account.ex
@@ -40,20 +40,53 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
pleroma: %Schema{
type: :object,
properties: %{
- allow_following_move: %Schema{type: :boolean},
- background_image: %Schema{type: :string, nullable: true},
+ allow_following_move: %Schema{
+ type: :boolean,
+ description: "whether the user allows automatically follow moved following accounts"
+ },
+ background_image: %Schema{type: :string, nullable: true, format: :uri},
chat_token: %Schema{type: :string},
- confirmation_pending: %Schema{type: :boolean},
+ confirmation_pending: %Schema{
+ type: :boolean,
+ description:
+ "whether the user account is waiting on email confirmation to be activated"
+ },
hide_favorites: %Schema{type: :boolean},
- hide_followers_count: %Schema{type: :boolean},
- hide_followers: %Schema{type: :boolean},
- hide_follows_count: %Schema{type: :boolean},
- hide_follows: %Schema{type: :boolean},
- is_admin: %Schema{type: :boolean},
- is_moderator: %Schema{type: :boolean},
+ hide_followers_count: %Schema{
+ type: :boolean,
+ description: "whether the user has follower stat hiding enabled"
+ },
+ hide_followers: %Schema{
+ type: :boolean,
+ description: "whether the user has follower hiding enabled"
+ },
+ hide_follows_count: %Schema{
+ type: :boolean,
+ description: "whether the user has follow stat hiding enabled"
+ },
+ hide_follows: %Schema{
+ type: :boolean,
+ description: "whether the user has follow hiding enabled"
+ },
+ is_admin: %Schema{
+ type: :boolean,
+ description: "whether the user is an admin of the local instance"
+ },
+ is_moderator: %Schema{
+ type: :boolean,
+ description: "whether the user is a moderator of the local instance"
+ },
skip_thread_containment: %Schema{type: :boolean},
- tags: %Schema{type: :array, items: %Schema{type: :string}},
- unread_conversation_count: %Schema{type: :integer},
+ tags: %Schema{
+ type: :array,
+ items: %Schema{type: :string},
+ description:
+ "List of tags being used for things like extra roles or moderation(ie. marking all media as nsfw all)."
+ },
+ unread_conversation_count: %Schema{
+ type: :integer,
+ description: "The count of unread conversations. Only returned to the account owner."
+ },
notification_settings: %Schema{
type: :object,
properties: %{
@@ -66,7 +99,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
},
relationship: AccountRelationship,
settings_store: %Schema{
- type: :object
+ type: :object,
+ description:
+ "A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`"
}
}
},
@@ -74,16 +109,32 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
type: :object,
properties: %{
fields: %Schema{type: :array, items: AccountField},
- note: %Schema{type: :string},
+ note: %Schema{
+ type: :string,
+ description:
+ "Plaintext version of the bio without formatting applied by the backend, used for editing the bio."
+ },
privacy: VisibilityScope,
sensitive: %Schema{type: :boolean},
pleroma: %Schema{
type: :object,
properties: %{
actor_type: ActorType,
- discoverable: %Schema{type: :boolean},
- no_rich_text: %Schema{type: :boolean},
- show_role: %Schema{type: :boolean}
+ discoverable: %Schema{
+ type: :boolean,
+ description:
+ "whether the user allows discovery of the account in search results and other services."
+ },
+ no_rich_text: %Schema{
+ type: :boolean,
+ description:
+ "whether the HTML tags for rich-text formatting are stripped from all statuses requested from the API."
+ },
+ show_role: %Schema{
+ type: :boolean,
+ description:
+ "whether the user wants their role (e.g admin, moderator) to be shown"
+ }
}
}
}
diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex
index a38b5b40f..947e42890 100644
--- a/lib/pleroma/web/api_spec/schemas/status.ex
+++ b/lib/pleroma/web/api_spec/schemas/status.ex
@@ -189,6 +189,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
thread_muted: %Schema{
type: :boolean,
description: "`true` if the thread the post belongs to is muted"
+ },
+ parent_visible: %Schema{
+ type: :boolean,
+ description: "`true` if the parent post is visible to the user"
}
}
},
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 04e081a8e..fd7149079 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -25,6 +25,13 @@ defmodule Pleroma.Web.CommonAPI do
require Pleroma.Constants
require Logger
+ def block(blocker, blocked) do
+ with {:ok, block_data, _} <- Builder.block(blocker, blocked),
+ {:ok, block, _} <- Pipeline.common_pipeline(block_data, local: true) do
+ {:ok, block}
+ end
+ end
+
def post_chat_message(%User{} = user, %User{} = recipient, content, opts \\ []) do
with maybe_attachment <- opts[:media_id] && Object.get_by_id(opts[:media_id]),
:ok <- validate_chat_content_length(content, !!maybe_attachment),
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 7a88a847c..b5008d69b 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -385,8 +385,7 @@ def unmute(%{assigns: %{user: muter, account: muted}} = conn, _params) do
@doc "POST /api/v1/accounts/:id/block"
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
- with {:ok, _user_block} <- User.block(blocker, blocked),
- {:ok, _activity} <- ActivityPub.block(blocker, blocked) do
+ with {:ok, _activity} <- CommonAPI.block(blocker, blocked) do
render(conn, "relationship.json", user: blocker, target: blocked)
else
{:error, message} -> json_response(conn, :forbidden, %{error: message})
diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex
index 35c2fc25c..89e48fba5 100644
--- a/lib/pleroma/web/mastodon_api/views/instance_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex
@@ -36,8 +36,10 @@ def render("show.json", _) do
background_image: Keyword.get(instance, :background_image),
pleroma: %{
metadata: %{
+ account_activation_required: Keyword.get(instance, :account_activation_required),
features: features(),
- federation: federation()
+ federation: federation(),
+ fields_limits: fields_limits()
},
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
}
@@ -88,4 +90,13 @@ def federation do
end
|> Map.put(:enabled, Config.get([:instance, :federating]))
end
+
+ def fields_limits do
+ %{
+ max_fields: Config.get([:instance, :max_account_fields]),
+ max_remote_fields: Config.get([:instance, :max_remote_account_fields]),
+ name_length: Config.get([:instance, :account_field_name_length]),
+ value_length: Config.get([:instance, :account_field_value_length])
+ }
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 4df47f584..fa9d695f3 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -21,7 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy
- import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1]
+ import Pleroma.Web.ActivityPub.Visibility, only: [get_visibility: 1, visible_for_user?: 2]
# TODO: Add cached version.
defp get_replied_to_activities([]), do: %{}
@@ -365,7 +365,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
expires_at: expires_at,
direct_conversation_id: direct_conversation_id,
thread_muted: thread_muted?,
- emoji_reactions: emoji_reactions
+ emoji_reactions: emoji_reactions,
+ parent_visible: visible_for_user?(reply_to, opts[:for])
}
}
end
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex
index f7ab6d86a..47fa46376 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex
@@ -19,35 +19,7 @@ def get_nodeinfo("2.0") do
|> Enum.map(fn u -> u.ap_id end)
federation = InstanceView.federation()
-
- 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)
+ features = InstanceView.features()
%{
version: "2.0",
diff --git a/lib/pleroma/web/preload/instance.ex b/lib/pleroma/web/preload/instance.ex
index 0b6fd3313..50d1f3382 100644
--- a/lib/pleroma/web/preload/instance.ex
+++ b/lib/pleroma/web/preload/instance.ex
@@ -3,14 +3,15 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.Instance do
+ alias Pleroma.Plugs.InstanceStatic
alias Pleroma.Web.MastodonAPI.InstanceView
alias Pleroma.Web.Nodeinfo.Nodeinfo
alias Pleroma.Web.Preload.Providers.Provider
@behaviour Provider
- @instance_url :"/api/v1/instance"
- @panel_url :"/instance/panel.html"
- @nodeinfo_url :"/nodeinfo/2.0"
+ @instance_url "/api/v1/instance"
+ @panel_url "/instance/panel.html"
+ @nodeinfo_url "/nodeinfo/2.0.json"
@impl Provider
def generate_terms(_params) do
@@ -27,7 +28,7 @@ defp build_info_tag(acc) do
end
defp build_panel_tag(acc) do
- instance_path = Path.join(:code.priv_dir(:pleroma), "static/instance/panel.html")
+ instance_path = InstanceStatic.file_path(@panel_url |> to_string())
if File.exists?(instance_path) do
panel_data = File.read!(instance_path)
diff --git a/lib/pleroma/web/preload/status_net.ex b/lib/pleroma/web/preload/status_net.ex
index 367442d5c..9b62f87a2 100644
--- a/lib/pleroma/web/preload/status_net.ex
+++ b/lib/pleroma/web/preload/status_net.ex
@@ -4,10 +4,10 @@
defmodule Pleroma.Web.Preload.Providers.StatusNet do
alias Pleroma.Web.Preload.Providers.Provider
- alias Pleroma.Web.TwitterAPI.UtilView
+ alias Pleroma.Web.TwitterAPI.UtilController
@behaviour Provider
- @config_url :"/api/statusnet/config.json"
+ @config_url "/api/statusnet/config.json"
@impl Provider
def generate_terms(_params) do
@@ -16,9 +16,10 @@ def generate_terms(_params) do
end
defp build_config_tag(acc) do
- instance = Pleroma.Config.get(:instance)
- info_data = UtilView.status_net_config(instance)
+ resp =
+ Plug.Test.conn(:get, @config_url |> to_string())
+ |> UtilController.config(nil)
- Map.put(acc, @config_url, info_data)
+ Map.put(acc, @config_url, resp.resp_body)
end
end
diff --git a/lib/pleroma/web/preload/timelines.ex b/lib/pleroma/web/preload/timelines.ex
index e531b8960..57de04051 100644
--- a/lib/pleroma/web/preload/timelines.ex
+++ b/lib/pleroma/web/preload/timelines.ex
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.Preload.Providers.Timelines do
alias Pleroma.Web.Preload.Providers.Provider
@behaviour Provider
- @public_url :"/api/v1/timelines/public"
+ @public_url "/api/v1/timelines/public"
@impl Provider
def generate_terms(params) do
diff --git a/lib/pleroma/web/preload/user.ex b/lib/pleroma/web/preload/user.ex
index 3a244845b..b3d2e9b8d 100644
--- a/lib/pleroma/web/preload/user.ex
+++ b/lib/pleroma/web/preload/user.ex
@@ -3,11 +3,12 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Preload.Providers.User do
+ alias Pleroma.User
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.Preload.Providers.Provider
@behaviour Provider
- @account_url :"/api/v1/accounts"
+ @account_url_base "/api/v1/accounts"
@impl Provider
def generate_terms(%{user: user}) do
@@ -16,10 +17,10 @@ def generate_terms(%{user: user}) do
def generate_terms(_params), do: %{}
- def build_accounts_tag(acc, nil), do: acc
-
- def build_accounts_tag(acc, user) do
+ def build_accounts_tag(acc, %User{} = user) do
account_data = AccountView.render("show.json", %{user: user, for: user})
- Map.put(acc, @account_url, account_data)
+ Map.put(acc, "#{@account_url_base}/#{user.id}", account_data)
end
+
+ def build_accounts_tag(acc, _), do: acc
end
diff --git a/lib/pleroma/web/streamer/streamer.ex b/lib/pleroma/web/streamer/streamer.ex
index d1d2c9b9c..73ee3e1e1 100644
--- a/lib/pleroma/web/streamer/streamer.ex
+++ b/lib/pleroma/web/streamer/streamer.ex
@@ -116,6 +116,7 @@ def filtered_by_user?(%User{} = user, %Activity{} = item) do
true <-
Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)),
true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,
+ true <- !(item.data["type"] == "Announce" && parent.data["actor"] == user.ap_id),
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),
true <- MapSet.disjoint?(recipients, recipient_blocks),
%{host: item_host} <- URI.parse(item.actor),
diff --git a/lib/pleroma/web/views/masto_fe_view.ex b/lib/pleroma/web/views/masto_fe_view.ex
index c3096006e..f739dacb6 100644
--- a/lib/pleroma/web/views/masto_fe_view.ex
+++ b/lib/pleroma/web/views/masto_fe_view.ex
@@ -86,7 +86,7 @@ def initial_state(token, user, custom_emojis) do
"video\/mp4"
]
},
- settings: user.settings || @default_settings,
+ settings: user.mastofe_settings || @default_settings,
push_subscription: nil,
accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),
diff --git a/mix.exs b/mix.exs
index b638be541..e2ab53bde 100644
--- a/mix.exs
+++ b/mix.exs
@@ -159,7 +159,10 @@ defp deps do
{:cors_plug, "~> 1.5"},
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:web_push_encryption, "~> 0.2.1"},
- {:swoosh, "~> 0.23.2"},
+ {:swoosh,
+ git: "https://github.com/swoosh/swoosh",
+ ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5",
+ override: true},
{:phoenix_swoosh, "~> 0.2"},
{:gen_smtp, "~> 0.13"},
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test},
diff --git a/mix.lock b/mix.lock
index 5ad49391d..4f2777fa7 100644
--- a/mix.lock
+++ b/mix.lock
@@ -104,9 +104,9 @@
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
- "swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "e3928e1d2889a308aaf3e42755809ac21cffd77cb58eef01cbfdab4ce2fd1e21"},
+ "swoosh": {:git, "https://github.com/swoosh/swoosh", "c96e0ca8a00d8f211ec1f042a4626b09f249caa5", [ref: "c96e0ca8a00d8f211ec1f042a4626b09f249caa5"]},
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
- "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
+ "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
"tesla": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/tesla.git", "61b7503cef33f00834f78ddfafe0d5d9dec2270b", [ref: "61b7503cef33f00834f78ddfafe0d5d9dec2270b"]},
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "f354efb2400dd7a80fd9eb6c8419068c4f632da4ac47f3d8822d6e33f08bc852"},
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
diff --git a/priv/repo/migrations/20200630162024_rename_user_settings_col.exs b/priv/repo/migrations/20200630162024_rename_user_settings_col.exs
new file mode 100644
index 000000000..2355eb681
--- /dev/null
+++ b/priv/repo/migrations/20200630162024_rename_user_settings_col.exs
@@ -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
diff --git a/priv/static/adminfe/app.6684eb28.css b/priv/static/adminfe/app.01bdb34a.css
similarity index 100%
rename from priv/static/adminfe/app.6684eb28.css
rename to priv/static/adminfe/app.01bdb34a.css
diff --git a/priv/static/adminfe/chunk-43ca.0de86b6d.css b/priv/static/adminfe/chunk-43ca.0de86b6d.css
deleted file mode 100644
index 817a6be44..000000000
Binary files a/priv/static/adminfe/chunk-43ca.0de86b6d.css and /dev/null differ
diff --git a/priv/static/adminfe/chunk-43ca.af749c6c.css b/priv/static/adminfe/chunk-43ca.af749c6c.css
new file mode 100644
index 000000000..504affb93
Binary files /dev/null and b/priv/static/adminfe/chunk-43ca.af749c6c.css differ
diff --git a/priv/static/adminfe/chunk-c5f4.0827b1ce.css b/priv/static/adminfe/chunk-c5f4.0827b1ce.css
deleted file mode 100644
index eb59ca31a..000000000
Binary files a/priv/static/adminfe/chunk-c5f4.0827b1ce.css and /dev/null differ
diff --git a/priv/static/adminfe/chunk-c5f4.b1112f18.css b/priv/static/adminfe/chunk-c5f4.b1112f18.css
new file mode 100644
index 000000000..d3b7604aa
Binary files /dev/null and b/priv/static/adminfe/chunk-c5f4.b1112f18.css differ
diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html
index c8f62d0c7..22b3143d2 100644
--- a/priv/static/adminfe/index.html
+++ b/priv/static/adminfe/index.html
@@ -1 +1 @@
-Admin FE
\ No newline at end of file
+Admin FE
\ No newline at end of file
diff --git a/priv/static/adminfe/static/js/app.3fcec8f6.js b/priv/static/adminfe/static/js/app.3fcec8f6.js
deleted file mode 100644
index 9a6fb1307..000000000
Binary files a/priv/static/adminfe/static/js/app.3fcec8f6.js and /dev/null differ
diff --git a/priv/static/adminfe/static/js/app.3fcec8f6.js.map b/priv/static/adminfe/static/js/app.3fcec8f6.js.map
deleted file mode 100644
index cc4ce87b3..000000000
Binary files a/priv/static/adminfe/static/js/app.3fcec8f6.js.map and /dev/null differ
diff --git a/priv/static/adminfe/static/js/app.f220ac13.js b/priv/static/adminfe/static/js/app.f220ac13.js
new file mode 100644
index 000000000..e5e1eda91
Binary files /dev/null and b/priv/static/adminfe/static/js/app.f220ac13.js differ
diff --git a/priv/static/adminfe/static/js/app.f220ac13.js.map b/priv/static/adminfe/static/js/app.f220ac13.js.map
new file mode 100644
index 000000000..90c22121e
Binary files /dev/null and b/priv/static/adminfe/static/js/app.f220ac13.js.map differ
diff --git a/priv/static/adminfe/static/js/chunk-0cbc.43ff796f.js b/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js
similarity index 61%
rename from priv/static/adminfe/static/js/chunk-0cbc.43ff796f.js
rename to priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js
index 232f0d447..d29070b62 100644
Binary files a/priv/static/adminfe/static/js/chunk-0cbc.43ff796f.js and b/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js differ
diff --git a/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js.map b/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js.map
new file mode 100644
index 000000000..7c99d9d48
Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js.map differ
diff --git a/priv/static/adminfe/static/js/chunk-0cbc.43ff796f.js.map b/priv/static/adminfe/static/js/chunk-0cbc.43ff796f.js.map
deleted file mode 100644
index dbca0ba8e..000000000
Binary files a/priv/static/adminfe/static/js/chunk-0cbc.43ff796f.js.map and /dev/null differ
diff --git a/priv/static/adminfe/static/js/chunk-43ca.3debeff7.js b/priv/static/adminfe/static/js/chunk-43ca.3debeff7.js
deleted file mode 100644
index 6d653cf62..000000000
Binary files a/priv/static/adminfe/static/js/chunk-43ca.3debeff7.js and /dev/null differ
diff --git a/priv/static/adminfe/static/js/chunk-43ca.3debeff7.js.map b/priv/static/adminfe/static/js/chunk-43ca.3debeff7.js.map
deleted file mode 100644
index f7976891f..000000000
Binary files a/priv/static/adminfe/static/js/chunk-43ca.3debeff7.js.map and /dev/null differ
diff --git a/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js b/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js
new file mode 100644
index 000000000..f9fcbc288
Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js differ
diff --git a/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js.map b/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js.map
new file mode 100644
index 000000000..3c71ad178
Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js.map differ
diff --git a/priv/static/adminfe/static/js/chunk-c5f4.304479e7.js b/priv/static/adminfe/static/js/chunk-c5f4.304479e7.js
deleted file mode 100644
index 4220621be..000000000
Binary files a/priv/static/adminfe/static/js/chunk-c5f4.304479e7.js and /dev/null differ
diff --git a/priv/static/adminfe/static/js/chunk-c5f4.304479e7.js.map b/priv/static/adminfe/static/js/chunk-c5f4.304479e7.js.map
deleted file mode 100644
index 2ab89731d..000000000
Binary files a/priv/static/adminfe/static/js/chunk-c5f4.304479e7.js.map and /dev/null differ
diff --git a/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js b/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js
new file mode 100644
index 000000000..2d5308031
Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js differ
diff --git a/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js.map b/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js.map
new file mode 100644
index 000000000..d5fc047ee
Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js.map differ
diff --git a/priv/static/adminfe/static/js/runtime.5bae86dc.js b/priv/static/adminfe/static/js/runtime.0a70a9f5.js
similarity index 91%
rename from priv/static/adminfe/static/js/runtime.5bae86dc.js
rename to priv/static/adminfe/static/js/runtime.0a70a9f5.js
index e5fb1554b..a99d1d369 100644
Binary files a/priv/static/adminfe/static/js/runtime.5bae86dc.js and b/priv/static/adminfe/static/js/runtime.0a70a9f5.js differ
diff --git a/priv/static/adminfe/static/js/runtime.5bae86dc.js.map b/priv/static/adminfe/static/js/runtime.0a70a9f5.js.map
similarity index 98%
rename from priv/static/adminfe/static/js/runtime.5bae86dc.js.map
rename to priv/static/adminfe/static/js/runtime.0a70a9f5.js.map
index 46c6380d9..62e726b22 100644
Binary files a/priv/static/adminfe/static/js/runtime.5bae86dc.js.map and b/priv/static/adminfe/static/js/runtime.0a70a9f5.js.map differ
diff --git a/test/fixtures/fetch_mocks/104410921027210069.json b/test/fixtures/fetch_mocks/104410921027210069.json
new file mode 100644
index 000000000..583f7a4dc
--- /dev/null
+++ b/test/fixtures/fetch_mocks/104410921027210069.json
@@ -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" : "@fixpoint @blobyoumu @grips @igel there's a difference between not liking nukes and not liking nuclear power nukes are pretty bad as are all WMDs in general but disliking nuclear power just indicates you are unable of thought
",
+ "contentMap" : {
+ "en" : "@fixpoint @blobyoumu @grips @igel there's a difference between not liking nukes and not liking nuclear power nukes are pretty bad as are all WMDs in general but disliking nuclear power just indicates you are unable of thought
"
+ },
+ "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"
+}
diff --git a/test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json b/test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json
new file mode 100644
index 000000000..0226b058a
--- /dev/null
+++ b/test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json
@@ -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" : "@tuxcrafting @fixpoint @blobyoumu @grips @igel What's bad about nukes?",
+ "context" : "https://cawfee.club/contexts/ad6c73d8-efc2-4e74-84ea-2dacf1a27a5e",
+ "conversation" : "https://cawfee.club/contexts/ad6c73d8-efc2-4e74-84ea-2dacf1a27a5e",
+ "id" : "https://social.sakamoto.gq/objects/f20f2497-66d9-4a52-a2e1-1be2a39c32c1",
+ "inReplyTo" : "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069",
+ "published" : "2020-06-26T15:20:15.975737Z",
+ "sensitive" : false,
+ "summary" : "",
+ "tag" : [
+ {
+ "href" : "https://blob.cat/users/blobyoumu",
+ "name" : "@blobyoumu@blob.cat",
+ "type" : "Mention"
+ },
+ {
+ "href" : "https://busshi.moe/users/tuxcrafting",
+ "name" : "@tuxcrafting@busshi.moe",
+ "type" : "Mention"
+ },
+ {
+ "href" : "https://cawfee.club/users/grips",
+ "name" : "@grips@cawfee.club",
+ "type" : "Mention"
+ },
+ {
+ "href" : "https://jaeger.website/users/igel",
+ "name" : "@igel@jaeger.website",
+ "type" : "Mention"
+ },
+ {
+ "href" : "https://stereophonic.space/users/fixpoint",
+ "name" : "@fixpoint@stereophonic.space",
+ "type" : "Mention"
+ }
+ ],
+ "to" : [
+ "https://busshi.moe/users/tuxcrafting",
+ "https://www.w3.org/ns/activitystreams#Public",
+ "https://blob.cat/users/blobyoumu",
+ "https://stereophonic.space/users/fixpoint",
+ "https://cawfee.club/users/grips",
+ "https://jaeger.website/users/igel"
+ ],
+ "type" : "Note"
+}
diff --git a/test/fixtures/fetch_mocks/eal.json b/test/fixtures/fetch_mocks/eal.json
new file mode 100644
index 000000000..a605476e6
--- /dev/null
+++ b/test/fixtures/fetch_mocks/eal.json
@@ -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. Any artworks posted here that are good are not mine.",
+ "tag" : [],
+ "type" : "Person",
+ "url" : "https://social.sakamoto.gq/users/eal"
+}
diff --git a/test/fixtures/fetch_mocks/tuxcrafting.json b/test/fixtures/fetch_mocks/tuxcrafting.json
new file mode 100644
index 000000000..5dce2a16d
--- /dev/null
+++ b/test/fixtures/fetch_mocks/tuxcrafting.json
@@ -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" : "expert procrastinator
trans(humanist|gender|istorized)
web: https:// tuxcrafting.port0.org pronouns: she/they languages: french (native)/english (fluent)/hebrew (ok-ish)/esperanto (barely)
",
+ "tag" : [],
+ "type" : "Person",
+ "url" : "https://busshi.moe/@tuxcrafting"
+}
diff --git a/test/fixtures/preload_static/instance/panel.html b/test/fixtures/preload_static/instance/panel.html
new file mode 100644
index 000000000..fc58e4e93
--- /dev/null
+++ b/test/fixtures/preload_static/instance/panel.html
@@ -0,0 +1 @@
+HEY!
diff --git a/test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json b/test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json
index 3f3f0f4fb..b76ba96a5 100644
--- a/test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json
+++ b/test/fixtures/tesla_mock/baptiste.gelex.xyz-article.json
@@ -1 +1,227 @@
-{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"Emoji":"toot:Emoji","Hashtag":"as:Hashtag","atomUri":"ostatus:atomUri","conversation":"ostatus:conversation","featured":"toot:featured","focalPoint":{"@container":"@list","@id":"toot:focalPoint"},"inReplyToAtomUri":"ostatus:inReplyToAtomUri","manuallyApprovesFollowers":"as:manuallyApprovesFollowers","movedTo":"as:movedTo","ostatus":"http://ostatus.org#","sensitive":"as:sensitive","toot":"http://joinmastodon.org/ns#"}],"attributedTo":["https://baptiste.gelez.xyz/@/BaptisteGelez"],"cc":[],"content":"It has been one month since the last \"This Month in Plume\" article, so it is time for another edition of our monthly changelog!
\nBug Fixes and Security \nLet's start with the hidden, but still (very) important changes: bug fixes and security patches.
\nFirst of all, @Trinity protected us against two major security flaws, called XSS and CSRF . The first one allows the attacker to run malicious code if you visit a Plume page where some of their personal data is present. The second one lets them post data with your Plume account by visiting one of their own website. It is two very common attack, and it is great we are now protected against them!
\nThe other big change in this area, is that we are now validating the data you are sending before doing anything with it. It means that, for instance, you will no longer be able to register with an empty username and to break everything.
\nOn the federation side, many issues were reported by @kaniini and redmatrix (respectively contributing to Pleroma and Hubzilla). By fixing some of them, we made it possible to federate Plume articles to Pleroma !
\n@Trinity hopefully noticed that there was a bug in our password check code: we were not checking that your password was correct, but only that the verification process went without errors. Concretely, it means that you could login to any account with any password. I wrote this part of the code when I was still the only contributor to the project, so nobody could review my work. We will now be trying to check every change, especially when it deals with critical parts of Plume, to avoid similar issues in the future, and we I'm really sorry this happened (even if I think nobody exploited it).
\nZanfib and stephenburgess8 also commited some small bugfixes, improving the general experience.
\nNew Features \nLet's now talk about the features that we introduced during this month.
\nOne of the most easy to spot is the redesign of Plume, made by @Madeorsk. I personaly love what he did, it really improved the readability and gave Plume a bit more of identity than the previous design. And he is still improving it .
\nWe also enabled Mardown in comment, to let you write more structured and nicely formatted responses.
\nAs you may have noticed, I have used mentions in this post. Indeed, it is now possible to mention someone in your articles or in comments. It works exactly the same way as in other apps, and you should receive a notification if someone mentionned you.
\nA dashboard to manage your blogs has also been introduced. In the future it may be used to manage your drafts, and eventually to show some statistics. The goal is to have a more specific homepage for authors.
\nThe federation with other ActivityPub softwares, like Mastodon or Pleroma is starting to work quite well, but the federation between Plume instances is far from being complete. However, we started to work on it, and it is now possible to view a distant user profile or blog from your instance, even if only basic informations are fetched yet (the articles are not loaded for instance).
\nAnother new feature that may not be visible for everyone, is the new NodeInfo endpoint. NodeInfo is a protocol allowing to get informations about a specific federated instance (whatever software it runs). It means that Plume instances can now be listed on sites like fediverse.network .
\nMaybe you wanted to host a Plume instance, but you don't like long install process during which you are just copy/pasting commands that you don't really understand from the documentation. That's why we introduced a setup script: the first you'll launch Plume, it will ask you a few questions and automatically setup your instance in a few minutes. We hope that this feature will help to host small instances, run by non-professional adminsys. You can see a demo of this tool on asciinema .
\nLast but not least, Plume is now translatable! It is already available in English, French, Polish (thanks to @m4sk1n) ) and German (thanks to bitkeks ). If your browser is configured to display pages in these languages, you should normally see the interface in your language. And if your language is not present yet, feel free to add your translation .
\nOther Changes \nWe also improved the code a lot. We tried to separate each part as much as possible, making it easier to re-use for other projects. For instance, our database code is now isolated from the rest of the app, which means it will be easier to make import tools from other blogging engines. Some parts of the code are even shared with another project, Aardwolf a federated Facebook alternative. For instance, both of our projects use the same internationalization code, and once Aardwolf will implement federation, this part of the code will probably be shared too. Since the WebFinger module (used to find new users and blogs) and the CSRF protection code (see the \"Bug fixes and Security\" section) have been isolated in their own modules, they may be shared by both projects too.
\nWe also worked a lot on documentation. We now have articles explaining how to setup your Plume instance on various operating systems, but also documenting the translation process. I want to thank BanjoFox (who imported some documentation from their project, Aardwolf, as the setup is quite similar), Kushal and @gled@plume.mastodon.host for working on this.
\nAs you can see, there were many changes this month, but there still a lot to do. Your help will of course be welcome. If you want to contribute to the code, translate Plume in your language, write some documentation, or anything else (or even if you're just curious about the project), feel free to join our Matrix room: #plume:disroot.org . Otherwise, as BanjoFox said on the Aardwolf Team Mastodon account , talking about the project around you is one of the easiest way to help.
\n","id":"https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/","likes":null,"name":"This Month in Plume: June 2018","published":"2018-07-10T20:16:24.087622Z","shares":null,"source":null,"tag":[{"href":"https://baptiste.gelez.xyz/@/Trinity","name":"@Trinity","type":"Mention"},{"href":"https://baptiste.gelez.xyz/@/kaniini/","name":"@kaniini","type":"Mention"},{"href":"https://baptiste.gelez.xyz/@/Trinity","name":"@Trinity","type":"Mention"}],"to":["https://unixcorn.xyz/users/Bat","https://mastodon.host/users/federationbot","https://social.tcit.fr/users/tcit","https://framapiaf.org/users/qwerty","https://mastodon.social/users/lthms","https://eldritch.cafe/users/Nausicaa","https://imaginair.es/users/Elanndelh","https://framapiaf.org/users/Drulac","https://mastodon.partipirate.org/users/NicolasConstant","https://aleph.land/users/Madeorsk","https://maly.io/users/Troll","https://hostux.social/users/superjey","https://mamot.fr/users/Phigger","https://mastodon.social/users/wakest","https://social.coop/users/wakest","https://unixcorn.xyz/users/Ce_lo","https://social.art-software.fr/users/Electron","https://framapiaf.org/users/Quenti","https://toot.plus.yt/users/Djyp","https://mastodon.social/users/brainblasted","https://social.mochi.academy/users/Ambraven","https://social.hacktivis.me/users/lanodan","https://mastodon.eliotberriot.com/users/eliotberriot","https://edolas.world/users/0x1C3B00DA","https://toot.cafe/users/zack","https://manowar.social/users/zatnosk","https://eldritch.cafe/users/fluffy","https://mastodon.social/users/david_ross","https://kosmos.social/users/xiroux","https://mastodon.art/users/EmergencyBattle","https://mastodon.social/users/trwnh","https://octodon.social/users/pybyte","https://anticapitalist.party/users/Trinity","https://mstdn.mx/users/xavavu","https://baptiste.gelez.xyz/@/m4sk1n","https://eldritch.cafe/users/milia","https://mastodon.zaclys.com/users/arx","https://toot.cafe/users/sivy","https://mastodon.social/users/ortegacmanuel","https://mastodon.observer/users/stephen","https://octodon.social/users/chloe","https://unixcorn.xyz/users/AmauryPi","https://cybre.space/users/rick_777","https://mastodon.social/users/wezm","https://baptiste.gelez.xyz/@/idlesong","https://mamot.fr/users/dr4Ke","https://imaginair.es/users/Phigger","https://mamot.fr/users/dlink","https://anticapitalist.party/users/a000d4f7a91939d0e71df1646d7a48","https://framapiaf.org/users/PhieLaidMignon","https://mastodon.social/users/y6nH","https://crazynoisybizarre.town/users/FederationBot","https://social.weho.st/users/dvn","https://mastodon.art/users/Wolthera","https://diaspodon.fr/users/dada","https://pachyder.me/users/Lanza","https://mastodon.xyz/users/ag","https://aleph.land/users/yahananxie","https://mstdn.io/users/chablis_social","https://mastodon.gougere.fr/users/fabien","https://functional.cafe/users/otini","https://social.coop/users/bhaugen","https://octodon.social/users/donblanco","https://chaos.social/users/astro","https://pachyder.me/users/sibear","https://mamot.fr/users/yohann","https://social.wxcafe.net/users/Bat","https://mastodon.social/users/dansup","https://chaos.social/users/juh","https://scifi.fyi/users/paeneultima","https://hostux.social/users/Deuchnord","https://mstdn.fr/users/taziden","https://mamot.fr/users/PifyZ","https://mastodon.social/users/plantabaja","https://mastodon.social/users/gitzgrog","https://mastodon.social/users/Syluban","https://masto.pt/users/eloisa","https://pleroma.soykaf.com/users/notclacke","https://mastodon.social/users/SiegfriedEhret","https://writing.exchange/users/write_as","https://mstdn.io/users/shellkr","https://mastodon.uy/users/jorge","https://mastodon.technology/users/bobstechsite","https://mastodon.social/users/hinterwaeldler","https://mastodon.xyz/users/mgdelacroix","https://mastodon.cloud/users/jjatria","https://baptiste.gelez.xyz/@/Jade/","https://edolas.world/users/pfm","https://mstdn.io/users/jort","https://mastodon.social/users/andreipetcu","https://mastodon.technology/users/0xf00fc7c8","https://mastodon.social/users/khanate","https://mastodon.technology/users/francois","https://mastodon.social/users/glherrmann","https://mastodon.host/users/gled","https://social.holdmybeer.solutions/users/kemonine","https://scholar.social/users/bgcarlisle","https://mastodon.social/users/oldgun","https://baptiste.gelez.xyz/@/snoe/","https://mastodon.at/users/switchingsocial","https://scifi.fyi/users/BrokenBiscuit","https://dev.glitch.social/users/hoodie","https://todon.nl/users/paulfree14","https://mastodon.social/users/aadilayub","https://social.fsck.club/users/anarchosaurus","https://mastodonten.de/users/GiantG","https://mastodon.technology/users/cj","https://cybre.space/users/sam","https://layer8.space/users/silkevicious","https://mastodon.xyz/users/Jimmyrwx","https://fosstodon.org/users/danyspin97","https://mstdn.io/users/cristhyano","https://mastodon.social/users/vanyok","https://hulvr.com/users/rook","https://niu.moe/users/Lucifer","https://mamot.fr/users/Thibaut","https://mastodont.cat/users/bgta","https://mstdn.io/users/hontoni","https://niu.moe/users/lionirdeadman","https://functional.cafe/users/phoe","https://mastodon.social/users/toontoet","https://mastodon.social/users/danipozo","https://scholar.social/users/robertson","https://mastodon.social/users/aldatsa","https://elekk.xyz/users/maloki","https://kitty.town/users/nursemchurt","https://neigh.horse/users/commagray","https://mastodon.social/users/hirojin","https://mastodon.xyz/users/mareklach","https://chaos.social/users/benthor","https://mastodon.social/users/djperreault","https://mastodon.art/users/eylul","https://mastodon.opportunis.me/users/bob","https://tootplanet.space/users/Shutsumon","https://toot.cat/users/woozle","https://mastodon.social/users/StephenLB","https://sleeping.town/users/oct2pus","https://mastodon.indie.host/users/stragu","https://social.coop/users/gilscottfitzgerald","https://icosahedron.website/users/joeld","https://mastodon.social/users/hellion","https://cybre.space/users/cooler_ranch","https://mastodon.social/users/kelsonv","https://mastodon.lat/users/scalpol","https://writing.exchange/users/hnb","https://hex.bz/users/Horst","https://mastodon.social/users/weddle","https://maly.io/users/sonya","https://social.coop/users/medusa","https://mastodon.social/users/DystopianK","https://mstdn.io/users/d_io","https://fosstodon.org/users/brandon","https://fosstodon.org/users/Cando","https://mastodon.host/users/panina","https://floss.social/users/tuxether","https://social.tchncs.de/users/suitbertmonz","https://mastodon.social/users/jrt","https://mastodon.social/users/sirikon","https://mstdn.io/users/yabirgb","https://mastodon.cloud/users/FerdiZ","https://mastodon.social/users/carlchenet","https://social.polonkai.eu/users/calendar_social","https://social.polonkai.eu/users/gergely","https://mastodon.social/users/Jelv","https://mastodon.social/users/srinicame","https://cybre.space/users/mastoabed","https://mastodon.social/users/tagomago","https://lgbt.io/users/bootblackCub","https://niu.moe/users/Nopplyy","https://mastodon.social/users/bpugh","https://www.w3.org/ns/activitystreams#Public"],"type":"Article","uploadMedia":null,"url":"https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"}
\ No newline at end of file
+{
+ "@context" : [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "Emoji" : "toot:Emoji",
+ "Hashtag" : "as:Hashtag",
+ "atomUri" : "ostatus:atomUri",
+ "conversation" : "ostatus:conversation",
+ "featured" : "toot:featured",
+ "focalPoint" : {
+ "@container" : "@list",
+ "@id" : "toot:focalPoint"
+ },
+ "inReplyToAtomUri" : "ostatus:inReplyToAtomUri",
+ "manuallyApprovesFollowers" : "as:manuallyApprovesFollowers",
+ "movedTo" : "as:movedTo",
+ "ostatus" : "http://ostatus.org#",
+ "sensitive" : "as:sensitive",
+ "toot" : "http://joinmastodon.org/ns#"
+ }
+ ],
+ "attributedTo" : [
+ "https://baptiste.gelez.xyz/@/BaptisteGelez"
+ ],
+ "cc" : [],
+ "content" : "It has been one month since the last \"This Month in Plume\" article, so it is time for another edition of our monthly changelog!
\nBug Fixes and Security \nLet's start with the hidden, but still (very) important changes: bug fixes and security patches.
\nFirst of all, @Trinity protected us against two major security flaws, called XSS and CSRF . The first one allows the attacker to run malicious code if you visit a Plume page where some of their personal data is present. The second one lets them post data with your Plume account by visiting one of their own website. It is two very common attack, and it is great we are now protected against them!
\nThe other big change in this area, is that we are now validating the data you are sending before doing anything with it. It means that, for instance, you will no longer be able to register with an empty username and to break everything.
\nOn the federation side, many issues were reported by @kaniini and redmatrix (respectively contributing to Pleroma and Hubzilla). By fixing some of them, we made it possible to federate Plume articles to Pleroma !
\n@Trinity hopefully noticed that there was a bug in our password check code: we were not checking that your password was correct, but only that the verification process went without errors. Concretely, it means that you could login to any account with any password. I wrote this part of the code when I was still the only contributor to the project, so nobody could review my work. We will now be trying to check every change, especially when it deals with critical parts of Plume, to avoid similar issues in the future, and we I'm really sorry this happened (even if I think nobody exploited it).
\nZanfib and stephenburgess8 also commited some small bugfixes, improving the general experience.
\nNew Features \nLet's now talk about the features that we introduced during this month.
\nOne of the most easy to spot is the redesign of Plume, made by @Madeorsk. I personaly love what he did, it really improved the readability and gave Plume a bit more of identity than the previous design. And he is still improving it .
\nWe also enabled Mardown in comment, to let you write more structured and nicely formatted responses.
\nAs you may have noticed, I have used mentions in this post. Indeed, it is now possible to mention someone in your articles or in comments. It works exactly the same way as in other apps, and you should receive a notification if someone mentionned you.
\nA dashboard to manage your blogs has also been introduced. In the future it may be used to manage your drafts, and eventually to show some statistics. The goal is to have a more specific homepage for authors.
\nThe federation with other ActivityPub softwares, like Mastodon or Pleroma is starting to work quite well, but the federation between Plume instances is far from being complete. However, we started to work on it, and it is now possible to view a distant user profile or blog from your instance, even if only basic informations are fetched yet (the articles are not loaded for instance).
\nAnother new feature that may not be visible for everyone, is the new NodeInfo endpoint. NodeInfo is a protocol allowing to get informations about a specific federated instance (whatever software it runs). It means that Plume instances can now be listed on sites like fediverse.network .
\nMaybe you wanted to host a Plume instance, but you don't like long install process during which you are just copy/pasting commands that you don't really understand from the documentation. That's why we introduced a setup script: the first you'll launch Plume, it will ask you a few questions and automatically setup your instance in a few minutes. We hope that this feature will help to host small instances, run by non-professional adminsys. You can see a demo of this tool on asciinema .
\nLast but not least, Plume is now translatable! It is already available in English, French, Polish (thanks to @m4sk1n) ) and German (thanks to bitkeks ). If your browser is configured to display pages in these languages, you should normally see the interface in your language. And if your language is not present yet, feel free to add your translation .
\nOther Changes \nWe also improved the code a lot. We tried to separate each part as much as possible, making it easier to re-use for other projects. For instance, our database code is now isolated from the rest of the app, which means it will be easier to make import tools from other blogging engines. Some parts of the code are even shared with another project, Aardwolf a federated Facebook alternative. For instance, both of our projects use the same internationalization code, and once Aardwolf will implement federation, this part of the code will probably be shared too. Since the WebFinger module (used to find new users and blogs) and the CSRF protection code (see the \"Bug fixes and Security\" section) have been isolated in their own modules, they may be shared by both projects too.
\nWe also worked a lot on documentation. We now have articles explaining how to setup your Plume instance on various operating systems, but also documenting the translation process. I want to thank BanjoFox (who imported some documentation from their project, Aardwolf, as the setup is quite similar), Kushal and @gled@plume.mastodon.host for working on this.
\nAs you can see, there were many changes this month, but there still a lot to do. Your help will of course be welcome. If you want to contribute to the code, translate Plume in your language, write some documentation, or anything else (or even if you're just curious about the project), feel free to join our Matrix room: #plume:disroot.org . Otherwise, as BanjoFox said on the Aardwolf Team Mastodon account , talking about the project around you is one of the easiest way to help.
\n",
+ "id" : "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/",
+ "likes" : null,
+ "name" : "This Month in Plume: June 2018",
+ "published" : "2018-07-10T20:16:24.087622Z",
+ "shares" : null,
+ "source" : null,
+ "tag" : [
+ {
+ "href" : "https://baptiste.gelez.xyz/@/Trinity",
+ "name" : "@Trinity",
+ "type" : "Mention"
+ },
+ {
+ "href" : "https://baptiste.gelez.xyz/@/kaniini/",
+ "name" : "@kaniini",
+ "type" : "Mention"
+ },
+ {
+ "href" : "https://baptiste.gelez.xyz/@/Trinity",
+ "name" : "@Trinity",
+ "type" : "Mention"
+ }
+ ],
+ "to" : [
+ "https://unixcorn.xyz/users/Bat",
+ "https://mastodon.host/users/federationbot",
+ "https://social.tcit.fr/users/tcit",
+ "https://framapiaf.org/users/qwerty",
+ "https://mastodon.social/users/lthms",
+ "https://eldritch.cafe/users/Nausicaa",
+ "https://imaginair.es/users/Elanndelh",
+ "https://framapiaf.org/users/Drulac",
+ "https://mastodon.partipirate.org/users/NicolasConstant",
+ "https://aleph.land/users/Madeorsk",
+ "https://maly.io/users/Troll",
+ "https://hostux.social/users/superjey",
+ "https://mamot.fr/users/Phigger",
+ "https://mastodon.social/users/wakest",
+ "https://social.coop/users/wakest",
+ "https://unixcorn.xyz/users/Ce_lo",
+ "https://social.art-software.fr/users/Electron",
+ "https://framapiaf.org/users/Quenti",
+ "https://toot.plus.yt/users/Djyp",
+ "https://mastodon.social/users/brainblasted",
+ "https://social.mochi.academy/users/Ambraven",
+ "https://social.hacktivis.me/users/lanodan",
+ "https://mastodon.eliotberriot.com/users/eliotberriot",
+ "https://edolas.world/users/0x1C3B00DA",
+ "https://toot.cafe/users/zack",
+ "https://manowar.social/users/zatnosk",
+ "https://eldritch.cafe/users/fluffy",
+ "https://mastodon.social/users/david_ross",
+ "https://kosmos.social/users/xiroux",
+ "https://mastodon.art/users/EmergencyBattle",
+ "https://mastodon.social/users/trwnh",
+ "https://octodon.social/users/pybyte",
+ "https://anticapitalist.party/users/Trinity",
+ "https://mstdn.mx/users/xavavu",
+ "https://baptiste.gelez.xyz/@/m4sk1n",
+ "https://eldritch.cafe/users/milia",
+ "https://mastodon.zaclys.com/users/arx",
+ "https://toot.cafe/users/sivy",
+ "https://mastodon.social/users/ortegacmanuel",
+ "https://mastodon.observer/users/stephen",
+ "https://octodon.social/users/chloe",
+ "https://unixcorn.xyz/users/AmauryPi",
+ "https://cybre.space/users/rick_777",
+ "https://mastodon.social/users/wezm",
+ "https://baptiste.gelez.xyz/@/idlesong",
+ "https://mamot.fr/users/dr4Ke",
+ "https://imaginair.es/users/Phigger",
+ "https://mamot.fr/users/dlink",
+ "https://anticapitalist.party/users/a000d4f7a91939d0e71df1646d7a48",
+ "https://framapiaf.org/users/PhieLaidMignon",
+ "https://mastodon.social/users/y6nH",
+ "https://crazynoisybizarre.town/users/FederationBot",
+ "https://social.weho.st/users/dvn",
+ "https://mastodon.art/users/Wolthera",
+ "https://diaspodon.fr/users/dada",
+ "https://pachyder.me/users/Lanza",
+ "https://mastodon.xyz/users/ag",
+ "https://aleph.land/users/yahananxie",
+ "https://mstdn.io/users/chablis_social",
+ "https://mastodon.gougere.fr/users/fabien",
+ "https://functional.cafe/users/otini",
+ "https://social.coop/users/bhaugen",
+ "https://octodon.social/users/donblanco",
+ "https://chaos.social/users/astro",
+ "https://pachyder.me/users/sibear",
+ "https://mamot.fr/users/yohann",
+ "https://social.wxcafe.net/users/Bat",
+ "https://mastodon.social/users/dansup",
+ "https://chaos.social/users/juh",
+ "https://scifi.fyi/users/paeneultima",
+ "https://hostux.social/users/Deuchnord",
+ "https://mstdn.fr/users/taziden",
+ "https://mamot.fr/users/PifyZ",
+ "https://mastodon.social/users/plantabaja",
+ "https://mastodon.social/users/gitzgrog",
+ "https://mastodon.social/users/Syluban",
+ "https://masto.pt/users/eloisa",
+ "https://pleroma.soykaf.com/users/notclacke",
+ "https://mastodon.social/users/SiegfriedEhret",
+ "https://writing.exchange/users/write_as",
+ "https://mstdn.io/users/shellkr",
+ "https://mastodon.uy/users/jorge",
+ "https://mastodon.technology/users/bobstechsite",
+ "https://mastodon.social/users/hinterwaeldler",
+ "https://mastodon.xyz/users/mgdelacroix",
+ "https://mastodon.cloud/users/jjatria",
+ "https://baptiste.gelez.xyz/@/Jade/",
+ "https://edolas.world/users/pfm",
+ "https://mstdn.io/users/jort",
+ "https://mastodon.social/users/andreipetcu",
+ "https://mastodon.technology/users/0xf00fc7c8",
+ "https://mastodon.social/users/khanate",
+ "https://mastodon.technology/users/francois",
+ "https://mastodon.social/users/glherrmann",
+ "https://mastodon.host/users/gled",
+ "https://social.holdmybeer.solutions/users/kemonine",
+ "https://scholar.social/users/bgcarlisle",
+ "https://mastodon.social/users/oldgun",
+ "https://baptiste.gelez.xyz/@/snoe/",
+ "https://mastodon.at/users/switchingsocial",
+ "https://scifi.fyi/users/BrokenBiscuit",
+ "https://dev.glitch.social/users/hoodie",
+ "https://todon.nl/users/paulfree14",
+ "https://mastodon.social/users/aadilayub",
+ "https://social.fsck.club/users/anarchosaurus",
+ "https://mastodonten.de/users/GiantG",
+ "https://mastodon.technology/users/cj",
+ "https://cybre.space/users/sam",
+ "https://layer8.space/users/silkevicious",
+ "https://mastodon.xyz/users/Jimmyrwx",
+ "https://fosstodon.org/users/danyspin97",
+ "https://mstdn.io/users/cristhyano",
+ "https://mastodon.social/users/vanyok",
+ "https://hulvr.com/users/rook",
+ "https://niu.moe/users/Lucifer",
+ "https://mamot.fr/users/Thibaut",
+ "https://mastodont.cat/users/bgta",
+ "https://mstdn.io/users/hontoni",
+ "https://niu.moe/users/lionirdeadman",
+ "https://functional.cafe/users/phoe",
+ "https://mastodon.social/users/toontoet",
+ "https://mastodon.social/users/danipozo",
+ "https://scholar.social/users/robertson",
+ "https://mastodon.social/users/aldatsa",
+ "https://elekk.xyz/users/maloki",
+ "https://kitty.town/users/nursemchurt",
+ "https://neigh.horse/users/commagray",
+ "https://mastodon.social/users/hirojin",
+ "https://mastodon.xyz/users/mareklach",
+ "https://chaos.social/users/benthor",
+ "https://mastodon.social/users/djperreault",
+ "https://mastodon.art/users/eylul",
+ "https://mastodon.opportunis.me/users/bob",
+ "https://tootplanet.space/users/Shutsumon",
+ "https://toot.cat/users/woozle",
+ "https://mastodon.social/users/StephenLB",
+ "https://sleeping.town/users/oct2pus",
+ "https://mastodon.indie.host/users/stragu",
+ "https://social.coop/users/gilscottfitzgerald",
+ "https://icosahedron.website/users/joeld",
+ "https://mastodon.social/users/hellion",
+ "https://cybre.space/users/cooler_ranch",
+ "https://mastodon.social/users/kelsonv",
+ "https://mastodon.lat/users/scalpol",
+ "https://writing.exchange/users/hnb",
+ "https://hex.bz/users/Horst",
+ "https://mastodon.social/users/weddle",
+ "https://maly.io/users/sonya",
+ "https://social.coop/users/medusa",
+ "https://mastodon.social/users/DystopianK",
+ "https://mstdn.io/users/d_io",
+ "https://fosstodon.org/users/brandon",
+ "https://fosstodon.org/users/Cando",
+ "https://mastodon.host/users/panina",
+ "https://floss.social/users/tuxether",
+ "https://social.tchncs.de/users/suitbertmonz",
+ "https://mastodon.social/users/jrt",
+ "https://mastodon.social/users/sirikon",
+ "https://mstdn.io/users/yabirgb",
+ "https://mastodon.cloud/users/FerdiZ",
+ "https://mastodon.social/users/carlchenet",
+ "https://social.polonkai.eu/users/calendar_social",
+ "https://social.polonkai.eu/users/gergely",
+ "https://mastodon.social/users/Jelv",
+ "https://mastodon.social/users/srinicame",
+ "https://cybre.space/users/mastoabed",
+ "https://mastodon.social/users/tagomago",
+ "https://lgbt.io/users/bootblackCub",
+ "https://niu.moe/users/Nopplyy",
+ "https://mastodon.social/users/bpugh",
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type" : "Article",
+ "uploadMedia" : null,
+ "url" : "https://baptiste.gelez.xyz/~/PlumeDevelopment/this-month-in-plume-june-2018/"
+}
diff --git a/test/fixtures/tesla_mock/peertube.moe-vid.json b/test/fixtures/tesla_mock/peertube.moe-vid.json
index 76296eb7d..ceebb90b7 100644
--- a/test/fixtures/tesla_mock/peertube.moe-vid.json
+++ b/test/fixtures/tesla_mock/peertube.moe-vid.json
@@ -1 +1,187 @@
-{"type":"Video","id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3","name":"Friday Night","duration":"PT29S","uuid":"df5f464b-be8d-46fb-ad81-2d4c2d1630e3","tag":[{"type":"Hashtag","name":"feels"}],"views":12,"sensitive":false,"commentsEnabled":true,"published":"2018-03-23T16:43:22.988Z","updated":"2018-03-24T16:28:46.002Z","mediaType":"text/markdown","content":"tfw\r\n\r\n\r\nsong is 'my old piano' by diana ross","support":null,"icon":{"type":"Image","url":"https://peertube.moe/static/thumbnails/df5f464b-be8d-46fb-ad81-2d4c2d1630e3.jpg","mediaType":"image/jpeg","width":200,"height":110},"url":[{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4","width":480,"size":5015880},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.torrent","width":480},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.torrent&xt=urn:btih:11d3af6b5c812a376c2b29cdbd46e5fb42ee730e&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4","width":480},{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.mp4","width":360,"size":3620040},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.torrent","width":360},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.torrent&xt=urn:btih:1c3885b4d7cdb46193b62b9b76e72b1409cfb297&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.mp4","width":360},{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.mp4","width":240,"size":2305488},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.torrent","width":240},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.torrent&xt=urn:btih:ac5773352d9e26f982d2da63acfb244f01ccafa4&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.mp4","width":240},{"type":"Link","mimeType":"video/mp4","href":"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.mp4","width":720,"size":7928231},{"type":"Link","mimeType":"application/x-bittorrent","href":"https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.torrent","width":720},{"type":"Link","mimeType":"application/x-bittorrent;x-scheme-handler/magnet","href":"magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.torrent&xt=urn:btih:b591068f4533c4e2865bb4cbb89887aecccdc523&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.mp4","width":720},{"type":"Link","mimeType":"text/html","href":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"}],"likes":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/likes","type":"OrderedCollection","totalItems":0,"orderedItems":[]},"dislikes":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/dislikes","type":"OrderedCollection","totalItems":0,"orderedItems":[]},"shares":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces","type":"OrderedCollection","totalItems":2,"orderedItems":["https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces/465","https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces/1"]},"comments":{"id":"https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/comments","type":"OrderedCollection","totalItems":0,"orderedItems":[]},"attributedTo":[{"type":"Group","id":"https://peertube.moe/video-channels/5224869f-aa63-4c83-ab3a-87c3a5ac440e"},{"type":"Person","id":"https://peertube.moe/accounts/7even"}],"to":["https://www.w3.org/ns/activitystreams#Public"],"cc":[],"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"RsaSignature2017":"https://w3id.org/security#RsaSignature2017","Hashtag":"as:Hashtag","uuid":"http://schema.org/identifier","category":"http://schema.org/category","licence":"http://schema.org/license","sensitive":"as:sensitive","language":"http://schema.org/inLanguage","views":"http://schema.org/Number","size":"http://schema.org/Number","commentsEnabled":"http://schema.org/Boolean","support":"http://schema.org/Text"},{"likes":{"@id":"as:likes","@type":"@id"},"dislikes":{"@id":"as:dislikes","@type":"@id"},"shares":{"@id":"as:shares","@type":"@id"},"comments":{"@id":"as:comments","@type":"@id"}}]}
\ No newline at end of file
+{
+ "@context" : [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "Hashtag" : "as:Hashtag",
+ "RsaSignature2017" : "https://w3id.org/security#RsaSignature2017",
+ "category" : "http://schema.org/category",
+ "commentsEnabled" : "http://schema.org/Boolean",
+ "language" : "http://schema.org/inLanguage",
+ "licence" : "http://schema.org/license",
+ "sensitive" : "as:sensitive",
+ "size" : "http://schema.org/Number",
+ "support" : "http://schema.org/Text",
+ "uuid" : "http://schema.org/identifier",
+ "views" : "http://schema.org/Number"
+ },
+ {
+ "comments" : {
+ "@id" : "as:comments",
+ "@type" : "@id"
+ },
+ "dislikes" : {
+ "@id" : "as:dislikes",
+ "@type" : "@id"
+ },
+ "likes" : {
+ "@id" : "as:likes",
+ "@type" : "@id"
+ },
+ "shares" : {
+ "@id" : "as:shares",
+ "@type" : "@id"
+ }
+ }
+ ],
+ "attributedTo" : [
+ {
+ "id" : "https://peertube.moe/video-channels/5224869f-aa63-4c83-ab3a-87c3a5ac440e",
+ "type" : "Group"
+ },
+ {
+ "id" : "https://peertube.moe/accounts/7even",
+ "type" : "Person"
+ }
+ ],
+ "cc" : [],
+ "comments" : {
+ "id" : "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/comments",
+ "orderedItems" : [],
+ "totalItems" : 0,
+ "type" : "OrderedCollection"
+ },
+ "commentsEnabled" : true,
+ "content" : "tfw\r\n\r\n\r\nsong is 'my old piano' by diana ross",
+ "dislikes" : {
+ "id" : "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/dislikes",
+ "orderedItems" : [],
+ "totalItems" : 0,
+ "type" : "OrderedCollection"
+ },
+ "duration" : "PT29S",
+ "icon" : {
+ "height" : 110,
+ "mediaType" : "image/jpeg",
+ "type" : "Image",
+ "url" : "https://peertube.moe/static/thumbnails/df5f464b-be8d-46fb-ad81-2d4c2d1630e3.jpg",
+ "width" : 200
+ },
+ "id" : "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3",
+ "likes" : {
+ "id" : "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/likes",
+ "orderedItems" : [],
+ "totalItems" : 0,
+ "type" : "OrderedCollection"
+ },
+ "mediaType" : "text/markdown",
+ "name" : "Friday Night",
+ "published" : "2018-03-23T16:43:22.988Z",
+ "sensitive" : false,
+ "shares" : {
+ "id" : "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces",
+ "orderedItems" : [
+ "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces/465",
+ "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3/announces/1"
+ ],
+ "totalItems" : 2,
+ "type" : "OrderedCollection"
+ },
+ "support" : null,
+ "tag" : [
+ {
+ "name" : "feels",
+ "type" : "Hashtag"
+ }
+ ],
+ "to" : [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type" : "Video",
+ "updated" : "2018-03-24T16:28:46.002Z",
+ "url" : [
+ {
+ "href" : "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
+ "mimeType" : "video/mp4",
+ "size" : 5015880,
+ "type" : "Link",
+ "width" : 480
+ },
+ {
+ "href" : "https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.torrent",
+ "mimeType" : "application/x-bittorrent",
+ "type" : "Link",
+ "width" : 480
+ },
+ {
+ "href" : "magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.torrent&xt=urn:btih:11d3af6b5c812a376c2b29cdbd46e5fb42ee730e&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
+ "mimeType" : "application/x-bittorrent;x-scheme-handler/magnet",
+ "type" : "Link",
+ "width" : 480
+ },
+ {
+ "href" : "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.mp4",
+ "mimeType" : "video/mp4",
+ "size" : 3620040,
+ "type" : "Link",
+ "width" : 360
+ },
+ {
+ "href" : "https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.torrent",
+ "mimeType" : "application/x-bittorrent",
+ "type" : "Link",
+ "width" : 360
+ },
+ {
+ "href" : "magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.torrent&xt=urn:btih:1c3885b4d7cdb46193b62b9b76e72b1409cfb297&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-360.mp4",
+ "mimeType" : "application/x-bittorrent;x-scheme-handler/magnet",
+ "type" : "Link",
+ "width" : 360
+ },
+ {
+ "href" : "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.mp4",
+ "mimeType" : "video/mp4",
+ "size" : 2305488,
+ "type" : "Link",
+ "width" : 240
+ },
+ {
+ "href" : "https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.torrent",
+ "mimeType" : "application/x-bittorrent",
+ "type" : "Link",
+ "width" : 240
+ },
+ {
+ "href" : "magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.torrent&xt=urn:btih:ac5773352d9e26f982d2da63acfb244f01ccafa4&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-240.mp4",
+ "mimeType" : "application/x-bittorrent;x-scheme-handler/magnet",
+ "type" : "Link",
+ "width" : 240
+ },
+ {
+ "href" : "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.mp4",
+ "mimeType" : "video/mp4",
+ "size" : 7928231,
+ "type" : "Link",
+ "width" : 720
+ },
+ {
+ "href" : "https://peertube.moe/static/torrents/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.torrent",
+ "mimeType" : "application/x-bittorrent",
+ "type" : "Link",
+ "width" : 720
+ },
+ {
+ "href" : "magnet:?xs=https%3A%2F%2Fpeertube.moe%2Fstatic%2Ftorrents%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.torrent&xt=urn:btih:b591068f4533c4e2865bb4cbb89887aecccdc523&dn=Friday+Night&tr=wss%3A%2F%2Fpeertube.moe%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube.moe%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube.moe%2Fstatic%2Fwebseed%2Fdf5f464b-be8d-46fb-ad81-2d4c2d1630e3-720.mp4",
+ "mimeType" : "application/x-bittorrent;x-scheme-handler/magnet",
+ "type" : "Link",
+ "width" : 720
+ },
+ {
+ "href" : "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3",
+ "mimeType" : "text/html",
+ "type" : "Link"
+ }
+ ],
+ "uuid" : "df5f464b-be8d-46fb-ad81-2d4c2d1630e3",
+ "views" : 12
+}
diff --git a/test/html_test.exs b/test/html_test.exs
index 0a4b4ebbc..f8907c8b4 100644
--- a/test/html_test.exs
+++ b/test/html_test.exs
@@ -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"])
end
+
+ test "skips attachment links" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status:
+ "image.png "
+ })
+
+ object = Object.normalize(activity)
+
+ assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"])
+ end
end
end
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 526f43fab..6add3f7eb 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -22,6 +22,16 @@ defmodule Pleroma.NotificationTest do
alias Pleroma.Web.Streamer
describe "create_notifications" do
+ test "never returns nil" do
+ user = insert(:user)
+ other_user = insert(:user, %{invisible: true})
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
+ {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
+
+ refute {:ok, [nil]} == Notification.create_notifications(activity)
+ end
+
test "creates a notification for an emoji reaction" do
user = insert(:user)
other_user = insert(:user)
diff --git a/test/object/fetcher_test.exs b/test/object/fetcher_test.exs
index c06e91f12..d9098ea1b 100644
--- a/test/object/fetcher_test.exs
+++ b/test/object/fetcher_test.exs
@@ -26,6 +26,46 @@ defmodule Pleroma.Object.FetcherTest do
:ok
end
+ describe "error cases" do
+ setup do
+ mock(fn
+ %{method: :get, url: "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"} ->
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/fetch_mocks/9wTkLEnuq47B25EehM.json")
+ }
+
+ %{method: :get, url: "https://social.sakamoto.gq/users/eal"} ->
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/fetch_mocks/eal.json")
+ }
+
+ %{method: :get, url: "https://busshi.moe/users/tuxcrafting/statuses/104410921027210069"} ->
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/fetch_mocks/104410921027210069.json")
+ }
+
+ %{method: :get, url: "https://busshi.moe/users/tuxcrafting"} ->
+ %Tesla.Env{
+ status: 500
+ }
+ end)
+
+ :ok
+ end
+
+ @tag capture_log: true
+ test "it works when fetching the OP actor errors out" do
+ # Here we simulate a case where the author of the OP can't be read
+ assert {:ok, _} =
+ Fetcher.fetch_object_from_id(
+ "https://social.sakamoto.gq/notice/9wTkLEnuq47B25EehM"
+ )
+ end
+ end
+
describe "max thread distance restriction" do
@ap_id "http://mastodon.example.org/@admin/99541947525187367"
setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
diff --git a/test/user_search_test.exs b/test/user_search_test.exs
index 17c63322a..f030523d3 100644
--- a/test/user_search_test.exs
+++ b/test/user_search_test.exs
@@ -17,7 +17,7 @@ defmodule Pleroma.UserSearchTest do
describe "User.search" do
setup do: clear_config([:instance, :limit_to_local_content])
- test "excluded invisible users from results" do
+ test "excludes invisible users from results" do
user = insert(:user, %{nickname: "john t1000"})
insert(:user, %{invisible: true, nickname: "john t800"})
@@ -25,6 +25,15 @@ test "excluded invisible users from results" do
assert found_user.id == user.id
end
+ test "excludes service actors from results" do
+ insert(:user, actor_type: "Application", nickname: "user1")
+ service = insert(:user, actor_type: "Service", nickname: "user2")
+ person = insert(:user, actor_type: "Person", nickname: "user3")
+
+ assert [found_user1, found_user2] = User.search("user")
+ assert [found_user1.id, found_user2.id] -- [service.id, person.id] == []
+ end
+
test "accepts limit parameter" do
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
assert length(User.search("john", limit: 3)) == 3
diff --git a/test/user_test.exs b/test/user_test.exs
index 9b66f3f51..7126bb539 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -597,6 +597,31 @@ test "updates an existing user, if stale" do
refute user.last_refreshed_at == orig_user.last_refreshed_at
end
+ test "if nicknames clash, the old user gets a prefix with the old id to the nickname" do
+ a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
+
+ orig_user =
+ insert(
+ :user,
+ local: false,
+ nickname: "admin@mastodon.example.org",
+ ap_id: "http://mastodon.example.org/users/harinezumigari",
+ last_refreshed_at: a_week_ago
+ )
+
+ assert orig_user.last_refreshed_at == a_week_ago
+
+ {:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
+
+ assert user.inbox
+
+ refute user.id == orig_user.id
+
+ orig_user = User.get_by_id(orig_user.id)
+
+ assert orig_user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
+ end
+
@tag capture_log: true
test "it returns the old user if stale, but unfetchable" do
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index be7ab2ae4..575e0c5db 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -992,54 +992,6 @@ test "creates an undo activity for a pending follow request" do
end
end
- describe "blocking" do
- test "reverts block activity on error" do
- [blocker, blocked] = insert_list(2, :user)
-
- with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
- assert {:error, :reverted} = ActivityPub.block(blocker, blocked)
- end
-
- assert Repo.aggregate(Activity, :count, :id) == 0
- assert Repo.aggregate(Object, :count, :id) == 0
- end
-
- test "creates a block activity" do
- clear_config([:instance, :federating], true)
- blocker = insert(:user)
- blocked = insert(:user)
-
- with_mock Pleroma.Web.Federator,
- publish: fn _ -> nil end do
- {:ok, activity} = ActivityPub.block(blocker, blocked)
-
- assert activity.data["type"] == "Block"
- assert activity.data["actor"] == blocker.ap_id
- assert activity.data["object"] == blocked.ap_id
-
- assert called(Pleroma.Web.Federator.publish(activity))
- end
- end
-
- test "works with outgoing blocks disabled, but doesn't federate" do
- clear_config([:instance, :federating], true)
- clear_config([:activitypub, :outgoing_blocks], false)
- blocker = insert(:user)
- blocked = insert(:user)
-
- with_mock Pleroma.Web.Federator,
- publish: fn _ -> nil end do
- {:ok, activity} = ActivityPub.block(blocker, blocked)
-
- assert activity.data["type"] == "Block"
- assert activity.data["actor"] == blocker.ap_id
- assert activity.data["object"] == blocked.ap_id
-
- refute called(Pleroma.Web.Federator.publish(:_))
- end
- end
- end
-
describe "timeline post-processing" do
test "it filters broken threads" do
user1 = insert(:user)
diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
index 1a13699be..6867c9853 100644
--- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
+++ b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs
@@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do
describe "with new user" do
test "it allows posts without links" do
- user = insert(:user)
+ user = insert(:user, local: false)
assert user.note_count == 0
@@ -45,7 +45,7 @@ test "it allows posts without links" do
end
test "it disallows posts with links" do
- user = insert(:user)
+ user = insert(:user, local: false)
assert user.note_count == 0
@@ -55,6 +55,18 @@ test "it disallows posts with links" do
{:reject, _} = AntiLinkSpamPolicy.filter(message)
end
+
+ test "it allows posts with links for local users" do
+ user = insert(:user)
+
+ assert user.note_count == 0
+
+ message =
+ @linkful_message
+ |> Map.put("actor", user.ap_id)
+
+ {:ok, _message} = AntiLinkSpamPolicy.filter(message)
+ end
end
describe "with old user" do
diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs
index 770a8dcf8..f38bf7e08 100644
--- a/test/web/activity_pub/object_validator_test.exs
+++ b/test/web/activity_pub/object_validator_test.exs
@@ -654,4 +654,31 @@ test "returns an error if the object can't be updated by the actor", %{
assert {:error, _cng} = ObjectValidator.validate(update, [])
end
end
+
+ describe "blocks" do
+ setup do
+ user = insert(:user, local: false)
+ blocked = insert(:user)
+
+ {:ok, valid_block, []} = Builder.block(user, blocked)
+
+ %{user: user, valid_block: valid_block}
+ end
+
+ test "validates a basic object", %{
+ valid_block: valid_block
+ } do
+ assert {:ok, _block, []} = ObjectValidator.validate(valid_block, [])
+ end
+
+ test "returns an error if we don't know the blocked user", %{
+ valid_block: valid_block
+ } do
+ block =
+ valid_block
+ |> Map.put("object", "https://gensokyo.2hu/users/raymoo")
+
+ assert {:error, _cng} = ObjectValidator.validate(block, [])
+ end
+ end
end
diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs
index 12c9ef1da..2649b060a 100644
--- a/test/web/activity_pub/side_effects_test.exs
+++ b/test/web/activity_pub/side_effects_test.exs
@@ -64,6 +64,47 @@ test "it streams out notifications and streams" do
end
end
+ describe "blocking users" do
+ setup do
+ user = insert(:user)
+ blocked = insert(:user)
+ User.follow(blocked, user)
+ User.follow(user, blocked)
+
+ {:ok, block_data, []} = Builder.block(user, blocked)
+ {:ok, block, _meta} = ActivityPub.persist(block_data, local: true)
+
+ %{user: user, blocked: blocked, block: block}
+ end
+
+ test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do
+ assert User.following?(user, blocked)
+ assert User.following?(blocked, user)
+
+ {:ok, _, _} = SideEffects.handle(block)
+
+ refute User.following?(user, blocked)
+ refute User.following?(blocked, user)
+ assert User.blocks?(user, blocked)
+ end
+
+ test "it blocks but does not unfollow if the relevant setting is set", %{
+ user: user,
+ blocked: blocked,
+ block: block
+ } do
+ clear_config([:activitypub, :unfollow_blocked], false)
+ assert User.following?(user, blocked)
+ assert User.following?(blocked, user)
+
+ {:ok, _, _} = SideEffects.handle(block)
+
+ refute User.following?(user, blocked)
+ assert User.following?(blocked, user)
+ assert User.blocks?(user, blocked)
+ end
+ end
+
describe "update users" do
setup do
user = insert(:user)
@@ -242,8 +283,7 @@ test "when activation is required", %{delete: delete, user: user} do
{:ok, like} = CommonAPI.favorite(user, post.id)
{:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍")
{:ok, announce} = CommonAPI.repeat(post.id, user)
- {:ok, block} = ActivityPub.block(user, poster)
- User.block(user, poster)
+ {:ok, block} = CommonAPI.block(user, poster)
{:ok, undo_data, _meta} = Builder.undo(user, like)
{:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
@@ -549,10 +589,29 @@ test "creates a notification", %{announce: announce, poster: poster} do
end
test "it streams out the announce", %{announce: announce} do
- with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do
+ with_mocks([
+ {
+ Pleroma.Web.Streamer,
+ [],
+ [
+ stream: fn _, _ -> nil end
+ ]
+ },
+ {
+ Pleroma.Web.Push,
+ [],
+ [
+ send: fn _ -> nil end
+ ]
+ }
+ ]) do
{:ok, announce, _} = SideEffects.handle(announce)
- assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce))
+ assert called(
+ Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce)
+ )
+
+ assert called(Pleroma.Web.Push.send(:_))
end
end
end
diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs
new file mode 100644
index 000000000..71f1a0ed5
--- /dev/null
+++ b/test/web/activity_pub/transmogrifier/block_handling_test.exs
@@ -0,0 +1,63 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors
+# 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
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 100821056..6a53fd3f0 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -445,56 +445,6 @@ test "it works for incoming follows to locked account" do
assert [^pending_follower] = User.get_follow_requests(user)
end
- test "it works for incoming blocks" do
- user = insert(:user)
-
- data =
- File.read!("test/fixtures/mastodon-block-activity.json")
- |> Poison.decode!()
- |> Map.put("object", user.ap_id)
-
- {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-
- assert data["type"] == "Block"
- assert data["object"] == user.ap_id
- assert data["actor"] == "http://mastodon.example.org/users/admin"
-
- blocker = User.get_cached_by_ap_id(data["actor"])
-
- assert User.blocks?(blocker, user)
- end
-
- test "incoming blocks successfully tear down any follow relationship" do
- blocker = insert(:user)
- blocked = insert(:user)
-
- data =
- File.read!("test/fixtures/mastodon-block-activity.json")
- |> Poison.decode!()
- |> Map.put("object", blocked.ap_id)
- |> Map.put("actor", blocker.ap_id)
-
- {:ok, blocker} = User.follow(blocker, blocked)
- {:ok, blocked} = User.follow(blocked, blocker)
-
- assert User.following?(blocker, blocked)
- assert User.following?(blocked, blocker)
-
- {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
-
- assert data["type"] == "Block"
- assert data["object"] == blocked.ap_id
- assert data["actor"] == blocker.ap_id
-
- blocker = User.get_cached_by_ap_id(data["actor"])
- blocked = User.get_cached_by_ap_id(data["object"])
-
- assert User.blocks?(blocker, blocked)
-
- refute User.following?(blocker, blocked)
- refute User.following?(blocked, blocker)
- end
-
test "it works for incoming accepts which were pre-accepted" do
follower = insert(:user)
followed = insert(:user)
diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs
index 15f03f193..2f9ecb5a3 100644
--- a/test/web/activity_pub/utils_test.exs
+++ b/test/web/activity_pub/utils_test.exs
@@ -27,16 +27,6 @@ test "fetches the latest Follow activity" do
end
end
- describe "fetch the latest Block" do
- test "fetches the latest Block activity" do
- blocker = insert(:user)
- blocked = insert(:user)
- {:ok, activity} = ActivityPub.block(blocker, blocked)
-
- assert activity == Utils.fetch_latest_block(blocker, blocked)
- end
- end
-
describe "determine_explicit_mentions()" do
test "works with an object that has mentions" do
object = %{
@@ -344,9 +334,9 @@ test "fetches last block activities" do
user1 = insert(:user)
user2 = insert(:user)
- assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
- assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2)
- assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2)
+ assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
+ assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2)
+ assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2)
assert Utils.fetch_latest_block(user1, user2) == activity
end
diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs
index cbdd994a9..908ee5484 100644
--- a/test/web/common_api/common_api_test.exs
+++ b/test/web/common_api/common_api_test.exs
@@ -25,6 +25,52 @@ defmodule Pleroma.Web.CommonAPITest do
setup do: clear_config([:instance, :limit])
setup do: clear_config([:instance, :max_pinned_statuses])
+ describe "blocking" do
+ setup do
+ blocker = insert(:user)
+ blocked = insert(:user)
+ User.follow(blocker, blocked)
+ User.follow(blocked, blocker)
+ %{blocker: blocker, blocked: blocked}
+ end
+
+ test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
+ clear_config([:instance, :federating], true)
+
+ with_mock Pleroma.Web.Federator,
+ publish: fn _ -> nil end do
+ assert {:ok, block} = CommonAPI.block(blocker, blocked)
+
+ assert block.local
+ assert User.blocks?(blocker, blocked)
+ refute User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+
+ assert called(Pleroma.Web.Federator.publish(block))
+ end
+ end
+
+ test "it blocks and does not federate if outgoing blocks are disabled", %{
+ blocker: blocker,
+ blocked: blocked
+ } do
+ clear_config([:instance, :federating], true)
+ clear_config([:activitypub, :outgoing_blocks], false)
+
+ with_mock Pleroma.Web.Federator,
+ publish: fn _ -> nil end do
+ assert {:ok, block} = CommonAPI.block(blocker, blocked)
+
+ assert block.local
+ assert User.blocks?(blocker, blocked)
+ refute User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+
+ refute called(Pleroma.Web.Federator.publish(block))
+ end
+ end
+ end
+
describe "posting chat messages" do
setup do: clear_config([:instance, :chat_limit])
diff --git a/test/web/masto_fe_controller_test.exs b/test/web/masto_fe_controller_test.exs
index 1d107d56c..f3b54b5f2 100644
--- a/test/web/masto_fe_controller_test.exs
+++ b/test/web/masto_fe_controller_test.exs
@@ -24,7 +24,7 @@ test "put settings", %{conn: conn} do
assert _result = json_response(conn, 200)
user = User.get_cached_by_ap_id(user.ap_id)
- assert user.settings == %{"programming" => "socks"}
+ assert user.mastofe_settings == %{"programming" => "socks"}
end
describe "index/2 redirections" do
diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs
index ebfcedd01..260ad2306 100644
--- a/test/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/web/mastodon_api/controllers/account_controller_test.exs
@@ -780,7 +780,6 @@ test "with notifications", %{conn: conn} do
assert %{"id" => _id, "muting" => true, "muting_notifications" => true} =
conn
- |> put_req_header("content-type", "application/json")
|> post("/api/v1/accounts/#{other_user.id}/mute")
|> json_response_and_validate_schema(200)
diff --git a/test/web/mastodon_api/controllers/instance_controller_test.exs b/test/web/mastodon_api/controllers/instance_controller_test.exs
index 8bdfdddd1..95ee26416 100644
--- a/test/web/mastodon_api/controllers/instance_controller_test.exs
+++ b/test/web/mastodon_api/controllers/instance_controller_test.exs
@@ -35,8 +35,10 @@ test "get instance information", %{conn: conn} do
"background_image" => _
} = result
+ assert result["pleroma"]["metadata"]["account_activation_required"] != nil
assert result["pleroma"]["metadata"]["features"]
assert result["pleroma"]["metadata"]["federation"]
+ assert result["pleroma"]["metadata"]["fields_limits"]
assert result["pleroma"]["vapid_public_key"]
assert email == from_config_email
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs
index b6ae4d343..fa26b3129 100644
--- a/test/web/mastodon_api/views/status_view_test.exs
+++ b/test/web/mastodon_api/views/status_view_test.exs
@@ -227,7 +227,8 @@ test "a note activity" do
expires_at: nil,
direct_conversation_id: nil,
thread_muted: false,
- emoji_reactions: []
+ emoji_reactions: [],
+ parent_visible: false
}
}
@@ -621,4 +622,20 @@ test "visibility/list" do
assert status.visibility == "list"
end
+
+ test "has a field for parent visibility" do
+ user = insert(:user)
+ poster = insert(:user)
+
+ {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
+
+ {:ok, visible} =
+ CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id})
+
+ status = StatusView.render("show.json", activity: visible, for: user)
+ refute status.pleroma.parent_visible
+
+ status = StatusView.render("show.json", activity: visible, for: poster)
+ assert status.pleroma.parent_visible
+ end
end
diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs
index 8b3b6177d..06b33607f 100644
--- a/test/web/node_info_test.exs
+++ b/test/web/node_info_test.exs
@@ -142,7 +142,8 @@ test "it shows default features flags", %{conn: conn} do
"shareable_emoji_packs",
"multifetch",
"pleroma_emoji_reactions",
- "pleroma:api/v1/notifications:include_types_filter"
+ "pleroma:api/v1/notifications:include_types_filter",
+ "pleroma_chat_messages"
]
assert MapSet.subset?(
diff --git a/test/web/preload/instance_test.exs b/test/web/preload/instance_test.exs
index 42a0d87bc..a46f28312 100644
--- a/test/web/preload/instance_test.exs
+++ b/test/web/preload/instance_test.exs
@@ -8,7 +8,7 @@ defmodule Pleroma.Web.Preload.Providers.InstanceTest do
setup do: {:ok, Instance.generate_terms(nil)}
- test "it renders the info", %{"/api/v1/instance": info} do
+ test "it renders the info", %{"/api/v1/instance" => info} do
assert %{
description: description,
email: "admin@example.com",
@@ -18,14 +18,25 @@ test "it renders the info", %{"/api/v1/instance": info} do
assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server")
end
- test "it renders the panel", %{"/instance/panel.html": panel} do
+ test "it renders the panel", %{"/instance/panel.html" => panel} do
assert String.contains?(
panel,
"Welcome to Pleroma!
"
)
end
- test "it renders the node_info", %{"/nodeinfo/2.0": nodeinfo} do
+ test "it works with overrides" do
+ clear_config([:instance, :static_dir], "test/fixtures/preload_static")
+
+ %{"/instance/panel.html" => panel} = Instance.generate_terms(nil)
+
+ assert String.contains?(
+ panel,
+ "HEY!"
+ )
+ end
+
+ test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do
%{
metadata: metadata,
version: "2.0"
diff --git a/test/web/preload/status_net_test.exs b/test/web/preload/status_net_test.exs
index ab6823a7e..df7acdb11 100644
--- a/test/web/preload/status_net_test.exs
+++ b/test/web/preload/status_net_test.exs
@@ -8,7 +8,8 @@ defmodule Pleroma.Web.Preload.Providers.StatusNetTest do
setup do: {:ok, StatusNet.generate_terms(nil)}
- test "it renders the info", %{"/api/statusnet/config.json": info} do
- assert info =~ "Pleroma "
+ test "it renders the info", %{"/api/statusnet/config.json" => info} do
+ assert {:ok, res} = Jason.decode(info)
+ assert res["site"]
end
end
diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs
index da6a3aded..fea95a6a4 100644
--- a/test/web/preload/timeline_test.exs
+++ b/test/web/preload/timeline_test.exs
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.Preload.Providers.TimelineTest do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Preload.Providers.Timelines
- @public_url :"/api/v1/timelines/public"
+ @public_url "/api/v1/timelines/public"
describe "unauthenticated timeliness when restricted" do
setup do
diff --git a/test/web/preload/user_test.exs b/test/web/preload/user_test.exs
index 99232cdfa..83f065e27 100644
--- a/test/web/preload/user_test.exs
+++ b/test/web/preload/user_test.exs
@@ -9,13 +9,11 @@ defmodule Pleroma.Web.Preload.Providers.UserTest do
describe "returns empty when user doesn't exist" do
test "nil user specified" do
- refute User.generate_terms(%{user: nil})
- |> Map.has_key?("/api/v1/accounts")
+ assert User.generate_terms(%{user: nil}) == %{}
end
test "missing user specified" do
- refute User.generate_terms(%{user: :not_a_user})
- |> Map.has_key?("/api/v1/accounts")
+ assert User.generate_terms(%{user: :not_a_user}) == %{}
end
end
@@ -23,11 +21,13 @@ test "missing user specified" do
setup do
user = insert(:user)
- {:ok, User.generate_terms(%{user: user})}
+ terms = User.generate_terms(%{user: user})
+ %{terms: terms, user: user}
end
- test "account is rendered", %{"/api/v1/accounts": accounts} do
- assert %{acct: user, username: user} = accounts
+ test "account is rendered", %{terms: terms, user: user} do
+ account = terms["/api/v1/accounts/#{user.id}"]
+ assert %{acct: user, username: user} = account
end
end
end
diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs
index 245f6e63f..dfe341b34 100644
--- a/test/web/streamer/streamer_test.exs
+++ b/test/web/streamer/streamer_test.exs
@@ -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)
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
Streamer.get_topic_and_add_socket("user", user)