PluginProbe
ActivityPub / 8.0.2
ActivityPub v8.0.2
9.3.1 9.3.0 9.2.2 9.2.1 9.2.0 9.1.0 9.0.2 9.0.1 9.0.0 8.3.0 8.2.1 8.2.0 8.1.1 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.2.0 1.3.0 2.0.0 2.0.1 2.1.0 2.1.1 All 160 releases
← All changes | includes/functions.php +33 -172 9.3.08.0.2 View file →
@@ -9,29 +9,8 @@
9 9
10 10 namespace Activitypub;
11 11
12 12 /**
13 - * Get the ActivityPub ID for a WordPress object.
14 - *
15 - * Returns the canonical ActivityPub URI for a WP_Post or WP_Comment.
16 - *
17 - * @param \WP_Post|\WP_Comment $wp_object The WordPress post or comment.
18 - *
19 - * @return string|null The ActivityPub ID (a URL), or null if unsupported type.
20 - */
21 -function get_object_id( $wp_object ) {
22 - if ( $wp_object instanceof \WP_Post ) {
23 - return get_post_id( $wp_object->ID );
24 - }
25 -
26 - if ( $wp_object instanceof \WP_Comment ) {
27 - return get_comment_id( $wp_object );
28 - }
29 -
30 - return null;
31 -}
32 -
33 -/**
34 13 * Convert a string from camelCase to snake_case.
35 14 *
36 15 * @param string $input The string to convert.
37 16 *
@@ -37,9 +16,9 @@
37 16 *
38 17 * @return string The converted string.
39 18 */
40 19 function camel_to_snake_case( $input ) {
41 - return \strtolower( \preg_replace( '/(?<!^)[A-Z]/', '_$0', $input ) );
20 + return strtolower( preg_replace( '/(?<!^)[A-Z]/', '_$0', $input ) );
42 21 }
43 22
44 23 /**
45 24 * Convert a string from snake_case to camelCase.
@@ -48,9 +27,9 @@
48 27 *
49 28 * @return string The converted string.
50 29 */
51 30 function snake_to_camel_case( $input ) {
52 - return \lcfirst( \str_replace( '_', '', \ucwords( $input, '_' ) ) );
31 + return lcfirst( str_replace( '_', '', ucwords( $input, '_' ) ) );
53 32 }
54 33
55 34 /**
56 35 * Convert seconds to ISO 8601 duration format.
@@ -65,10 +44,10 @@
65 44 if ( $seconds <= 0 ) {
66 45 return 'PT0S';
67 46 }
68 47
69 - $hours = \floor( $seconds / 3600 );
70 - $minutes = \floor( ( $seconds % 3600 ) / 60 );
48 + $hours = floor( $seconds / 3600 );
49 + $minutes = floor( ( $seconds % 3600 ) / 60 );
71 50 $secs = $seconds % 60;
72 51
73 52 $duration = 'PT';
74 53
@@ -98,48 +77,24 @@
98 77 * thus disabling blocks registered by the ActivityPub plugin.
99 78 *
100 79 * @param boolean $supports_blocks True if the site supports the block editor, false otherwise.
101 80 */
102 - return \apply_filters( 'activitypub_site_supports_blocks', true );
81 + return apply_filters( 'activitypub_site_supports_blocks', true );
103 82 }
104 83
105 84 /**
106 - * Get the icon Image object for site-wide ActivityPub actors.
85 + * Check if data is valid JSON.
107 86 *
108 - * Tries the site icon first, then the custom logo, and falls back to the
109 - * bundled WordPress logo.
87 + * @deprecated 7.1.0 Use {@see \json_decode}.
110 88 *
111 - * @since 9.1.0
89 + * @param string $data The data to check.
112 90 *
113 - * @return array The icon array with 'type' and 'url'.
91 + * @return boolean True if the data is JSON, false otherwise.
114 92 */
115 -function site_icon() {
116 - // Try site icon first.
117 - $icon_id = \get_option( 'site_icon' );
93 +function is_json( $data ) {
94 + \_deprecated_function( __FUNCTION__, '7.1.0', 'json_decode' );
118 95
119 - // Try custom logo second.
120 - if ( ! $icon_id ) {
121 - $icon_id = \get_theme_mod( 'custom_logo' );
122 - }
123 -
124 - $icon_url = false;
125 -
126 - if ( $icon_id ) {
127 - $icon = \wp_get_attachment_image_src( $icon_id, 'full' );
128 - if ( $icon ) {
129 - $icon_url = $icon[0];
130 - }
131 - }
132 -
133 - if ( ! $icon_url ) {
134 - // Fallback to default icon.
135 - $icon_url = \plugins_url( '/assets/img/wp-logo.png', ACTIVITYPUB_PLUGIN_FILE );
136 - }
137 -
138 - return array(
139 - 'type' => 'Image',
140 - 'url' => \esc_url_raw( $icon_url ),
141 - );
96 + return \is_array( \json_decode( $data, true ) );
142 97 }
143 98
144 99 /**
145 100 * Check whether a blog is public based on the `blog_public` option.
@@ -151,9 +106,9 @@
151 106 * Filter whether the blog is public.
152 107 *
153 108 * @param bool $public Whether the blog is public.
154 109 */
155 - return (bool) \apply_filters( 'activitypub_is_blog_public', \get_option( 'blog_public', 1 ) );
110 + return (bool) apply_filters( 'activitypub_is_blog_public', \get_option( 'blog_public', 1 ) );
156 111 }
157 112
158 113 /**
159 114 * Get the masked WordPress version to only show the major and minor version.
@@ -161,15 +116,15 @@
161 116 * @return string The masked version.
162 117 */
163 118 function get_masked_wp_version() {
164 119 // Only show the major and minor version.
165 - $version = \get_bloginfo( 'version' );
120 + $version = get_bloginfo( 'version' );
166 121 // Strip the RC or beta part.
167 - $version = \preg_replace( '/-.*$/', '', $version );
168 - $version = \explode( '.', $version );
169 - $version = \array_slice( $version, 0, 2 );
122 + $version = preg_replace( '/-.*$/', '', $version );
123 + $version = explode( '.', $version );
124 + $version = array_slice( $version, 0, 2 );
170 125
171 - return \implode( '.', $version );
126 + return implode( '.', $version );
172 127 }
173 128
174 129 /**
175 130 * Check if a plugin is active, loading plugin.php if necessary.
@@ -201,9 +156,9 @@
201 156 return null;
202 157 }
203 158
204 159 $domains = \get_option( 'activitypub_attribution_domains', home_host() );
205 - $domains = \explode( PHP_EOL, $domains );
160 + $domains = explode( PHP_EOL, $domains );
206 161
207 162 if ( ! $domains ) {
208 163 $domains = null;
209 164 }
@@ -259,19 +214,19 @@
259 214 // Remove all characters that are not letters, numbers, or hyphens.
260 215 $hashtag = \preg_replace( '/[^\p{L}\p{Nd}-]+/u', '-', $hashtag );
261 216
262 217 // Capitalize every letter that is preceded by a hyphen.
263 - $hashtag = \preg_replace_callback(
218 + $hashtag = preg_replace_callback(
264 219 '/-+(.)/',
265 220 static function ( $matches ) {
266 - return \strtoupper( $matches[1] );
221 + return strtoupper( $matches[1] );
267 222 },
268 223 $hashtag
269 224 );
270 225
271 226 // Add a hashtag to the beginning of the string.
272 - $hashtag = \ltrim( $hashtag, '#' );
273 - $hashtag = \trim( $hashtag, '-' );
227 + $hashtag = ltrim( $hashtag, '#' );
228 + $hashtag = trim( $hashtag, '-' );
274 229 $hashtag = '#' . $hashtag;
275 230
276 231 /**
277 232 * Allow defining your own custom hashtag generation rules.
@@ -278,11 +233,11 @@
278 233 *
279 234 * @param string $hashtag The hashtag to be returned.
280 235 * @param string $input The original string.
281 236 */
282 - $hashtag = \apply_filters( 'activitypub_esc_hashtag', $hashtag, $input );
237 + $hashtag = apply_filters( 'activitypub_esc_hashtag', $hashtag, $input );
283 238
284 - return \esc_html( $hashtag );
239 + return esc_html( $hashtag );
285 240 }
286 241
287 242 /**
288 243 * Replace content with links, mentions or hashtags by Regex callback and not affect protected tags.
@@ -294,9 +249,9 @@
294 249 * @return string The content with links, mentions, hashtags, etc.
295 250 */
296 251 function enrich_content_data( $content, $regex, $regex_callback ) {
297 252 // Small protection against execution timeouts: limit to 1 MB.
298 - if ( \mb_strlen( $content ) > MB_IN_BYTES ) {
253 + if ( mb_strlen( $content ) > MB_IN_BYTES ) {
299 254 return $content;
300 255 }
301 256 $tag_stack = array();
302 257 $protected_tags = array(
@@ -307,22 +262,22 @@
307 262 'a',
308 263 );
309 264 $content_with_links = '';
310 265 $in_protected_tag = false;
311 - foreach ( \wp_html_split( $content ) as $chunk ) {
312 - if ( \preg_match( '#^<!--[\s\S]*-->$#i', $chunk, $m ) ) {
266 + foreach ( wp_html_split( $content ) as $chunk ) {
267 + if ( preg_match( '#^<!--[\s\S]*-->$#i', $chunk, $m ) ) {
313 268 $content_with_links .= $chunk;
314 269 continue;
315 270 }
316 271
317 - if ( \preg_match( '#^<(/)?([a-z-]+)\b[^>]*>$#i', $chunk, $m ) ) {
318 - $tag = \strtolower( $m[2] );
272 + if ( preg_match( '#^<(/)?([a-z-]+)\b[^>]*>$#i', $chunk, $m ) ) {
273 + $tag = strtolower( $m[2] );
319 274 if ( '/' === $m[1] ) {
320 275 // Closing tag.
321 - $i = \array_search( $tag, $tag_stack, true );
276 + $i = array_search( $tag, $tag_stack, true );
322 277 // We can only remove the tag from the stack if it is in the stack.
323 278 if ( false !== $i ) {
324 - $tag_stack = \array_slice( $tag_stack, 0, $i );
279 + $tag_stack = array_slice( $tag_stack, 0, $i );
325 280 }
326 281 } else {
327 282 // Opening tag, add it to the stack.
328 283 $tag_stack[] = $tag;
@@ -329,9 +284,9 @@
329 284 }
330 285
331 286 // If we're in a protected tag, the tag_stack contains at least one protected tag string.
332 287 // The protected tag state can only change when we encounter a start or end tag.
333 - $in_protected_tag = \array_intersect( $tag_stack, $protected_tags );
288 + $in_protected_tag = array_intersect( $tag_stack, $protected_tags );
334 289
335 290 // Never inspect tags.
336 291 $content_with_links .= $chunk;
337 292 continue;
@@ -359,99 +314,5 @@
359 314 * @return string|false The embed HTML or false if not found.
360 315 */
361 316 function get_embed_html( $url, $inline_css = true ) {
362 317 return Embed::get_html( $url, $inline_css );
363 -}
364 -
365 -/**
366 - * Get the client IP address for rate-limiting purposes.
367 - *
368 - * Walks the ordered list of $_SERVER keys returned by the
369 - * `activitypub_client_ip_sources` filter (default: `['REMOTE_ADDR']`) and
370 - * returns the first value that parses as a valid IP literal, validated via
371 - * `filter_var( ..., FILTER_VALIDATE_IP )`. The result can be overridden
372 - * outright via the `activitypub_client_ip` filter; that filter's output is
373 - * also validated and replaced with `''` when it isn't a valid IP, so a
374 - * misbehaving filter can't collide all callers into the same rate-limit
375 - * bucket.
376 - *
377 - * Trusting any source other than `REMOTE_ADDR` is only safe behind a
378 - * reverse proxy that sets and overwrites the corresponding header — see
379 - * the `activitypub_client_ip_sources` filter docblock for guidance.
380 - *
381 - * Callers using the return value as a rate-limit key should treat an
382 - * empty return as "client unidentifiable" and fail closed rather than
383 - * share a single bucket across every such request.
384 - *
385 - * @since 8.1.0
386 - *
387 - * @return string A valid IP address, or '' when no IP could be determined.
388 - */
389 -function get_client_ip() {
390 - // phpcs:disable WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
391 - $ip = '';
392 -
393 - /**
394 - * Filter the ordered list of $_SERVER keys to consult as a source for the
395 - * client IP. The first key whose value parses as a valid IP wins.
396 - *
397 - * Default: array( 'REMOTE_ADDR' ) — the actual TCP peer, the only value
398 - * that an HTTP client cannot spoof. Trusting any other $_SERVER key is
399 - * only safe when a reverse proxy in front of the site sets that key and
400 - * overwrites any client-supplied version; otherwise an attacker can spoof
401 - * the value and bypass the per-IP rate limits that depend on it.
402 - *
403 - * Common operator overrides:
404 - * array( 'HTTP_CF_CONNECTING_IP' ) on Cloudflare.
405 - * array( 'HTTP_TRUE_CLIENT_IP', 'REMOTE_ADDR' ) Akamai with a fallback.
406 - * array( 'HTTP_X_REAL_IP' ) nginx that strips the client copy.
407 - *
408 - * X-Forwarded-For pitfall: even with a trusted proxy, an attacker can
409 - * prepend their own value before the proxy appends the real client IP.
410 - * This helper takes the leftmost entry, which is correct only when the
411 - * trusted proxy fully overwrites the header. If you trust X-Forwarded-For
412 - * end-to-end, prefer to resolve from the right by your known proxy count
413 - * via the activitypub_client_ip filter.
414 - *
415 - * @since 8.2.0
416 - *
417 - * @param string[] $sources $_SERVER keys to consult, in priority order.
418 - */
419 - $sources = \apply_filters( 'activitypub_client_ip_sources', array( 'REMOTE_ADDR' ) );
420 -
421 - if ( ! \is_array( $sources ) ) {
422 - $sources = array( 'REMOTE_ADDR' );
423 - }
424 -
425 - foreach ( $sources as $source ) {
426 - if ( ! \is_string( $source ) || empty( $_SERVER[ $source ] ) ) {
427 - continue;
428 - }
429 -
430 - // Some headers (e.g. X-Forwarded-For) may contain a comma-separated list; use the first IP.
431 - $ip_list = \sanitize_text_field( \wp_unslash( $_SERVER[ $source ] ) );
432 - $candidate = \trim( \explode( ',', $ip_list )[0] );
433 -
434 - if ( \filter_var( $candidate, FILTER_VALIDATE_IP ) ) {
435 - $ip = $candidate;
436 - break;
437 - }
438 - }
439 - // phpcs:enable WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
440 -
441 - /**
442 - * Filter the client IP address used for rate limiting.
443 - *
444 - * @since 8.1.0
445 - *
446 - * @param string $ip The detected client IP address (empty when none could be determined).
447 - */
448 - $ip = \apply_filters( 'activitypub_client_ip', $ip );
449 -
450 - // Tolerate surrounding whitespace from filter callbacks; FILTER_VALIDATE_IP would otherwise reject it.
451 - if ( \is_string( $ip ) ) {
452 - $ip = \trim( $ip );
453 - }
454 -
455 - // Re-validate so a misbehaving filter can't return a sentinel string that would collapse all callers into one bucket.
456 - return \is_string( $ip ) && \filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : '';
457 318 }