PluginProbe
ActivityPub / 1.0.5
ActivityPub v1.0.5
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
activitypub / includes / class-mention.php

class-mention.php in ActivityPub 1.0.5, at includes/class-mention.php

169 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Activitypub;
3
4 use WP_Error;
5
6 /**
7 * ActivityPub Mention Class
8 *
9 * @author Alex Kirk
10 */
11 class Mention {
12 /**
13 * Initialize the class, registering WordPress hooks
14 */
15 public static function init() {
16 \add_filter( 'the_content', array( self::class, 'the_content' ), 99, 2 );
17 \add_filter( 'activitypub_extract_mentions', array( self::class, 'extract_mentions' ), 99, 2 );
18 }
19
20 /**
21 * Filter to replace the mentions in the content with links
22 *
23 * @param string $the_content the post-content
24 *
25 * @return string the filtered post-content
26 */
27 public static function the_content( $the_content ) {
28 $tag_stack = array();
29 $protected_tags = array(
30 'pre',
31 'code',
32 'textarea',
33 'style',
34 'a',
35 );
36 $content_with_links = '';
37 $in_protected_tag = false;
38 foreach ( wp_html_split( $the_content ) as $chunk ) {
39 if ( preg_match( '#^<!--[\s\S]*-->$#i', $chunk, $m ) ) {
40 $content_with_links .= $chunk;
41 continue;
42 }
43
44 if ( preg_match( '#^<(/)?([a-z-]+)\b[^>]*>$#i', $chunk, $m ) ) {
45 $tag = strtolower( $m[2] );
46 if ( '/' === $m[1] ) {
47 // Closing tag.
48 $i = array_search( $tag, $tag_stack );
49 // We can only remove the tag from the stack if it is in the stack.
50 if ( false !== $i ) {
51 $tag_stack = array_slice( $tag_stack, 0, $i );
52 }
53 } else {
54 // Opening tag, add it to the stack.
55 $tag_stack[] = $tag;
56 }
57
58 // If we're in a protected tag, the tag_stack contains at least one protected tag string.
59 // The protected tag state can only change when we encounter a start or end tag.
60 $in_protected_tag = array_intersect( $tag_stack, $protected_tags );
61
62 // Never inspect tags.
63 $content_with_links .= $chunk;
64 continue;
65 }
66
67 if ( $in_protected_tag ) {
68 // Don't inspect a chunk inside an inspected tag.
69 $content_with_links .= $chunk;
70 continue;
71 }
72
73 // Only reachable when there is no protected tag in the stack.
74 $content_with_links .= \preg_replace_callback( '/@' . ACTIVITYPUB_USERNAME_REGEXP . '/', array( self::class, 'replace_with_links' ), $chunk );
75 }
76
77 return $content_with_links;
78 }
79
80 /**
81 * A callback for preg_replace to build the user links
82 *
83 * @param array $result the preg_match results
84 *
85 * @return string the final string
86 */
87 public static function replace_with_links( $result ) {
88 $metadata = get_remote_metadata_by_actor( $result[0] );
89
90 if ( ! empty( $metadata ) && ! is_wp_error( $metadata ) && ! empty( $metadata['url'] ) ) {
91 $username = ltrim( $result[0], '@' );
92 if ( ! empty( $metadata['name'] ) ) {
93 $username = $metadata['name'];
94 }
95 if ( ! empty( $metadata['preferredUsername'] ) ) {
96 $username = $metadata['preferredUsername'];
97 }
98 return \sprintf( '<a rel="mention" class="u-url mention" href="%s">@<span>%s</span></a>', esc_url( $metadata['url'] ), esc_html( $username ) );
99 }
100
101 return $result[0];
102 }
103
104 /**
105 * Get the Inboxes for the mentioned Actors
106 *
107 * @param array $mentioned The list of Actors that were mentioned
108 *
109 * @return array The list of Inboxes
110 */
111 public static function get_inboxes( $mentioned ) {
112 $inboxes = array();
113
114 foreach ( $mentioned as $actor ) {
115 $inbox = self::get_inbox_by_mentioned_actor( $actor );
116
117 if ( ! is_wp_error( $inbox ) && $inbox ) {
118 $inboxes[] = $inbox;
119 }
120 }
121
122 return $inboxes;
123 }
124
125 /**
126 * Get the inbox from the Remote-Profile of a mentioned Actor
127 *
128 * @param string $actor The Actor-URL
129 *
130 * @return string The Inbox-URL
131 */
132 public static function get_inbox_by_mentioned_actor( $actor ) {
133 $metadata = get_remote_metadata_by_actor( $actor );
134
135 if ( \is_wp_error( $metadata ) ) {
136 return $metadata;
137 }
138
139 if ( isset( $metadata['endpoints'] ) && isset( $metadata['endpoints']['sharedInbox'] ) ) {
140 return $metadata['endpoints']['sharedInbox'];
141 }
142
143 if ( \array_key_exists( 'inbox', $metadata ) ) {
144 return $metadata['inbox'];
145 }
146
147 return new WP_Error( 'activitypub_no_inbox', \__( 'No "Inbox" found', 'activitypub' ), $metadata );
148 }
149
150 /**
151 * Extract the mentions from the post_content.
152 *
153 * @param array $mentions The already found mentions.
154 * @param string $post_content The post content.
155 *
156 * @return mixed The discovered mentions.
157 */
158 public static function extract_mentions( $mentions, $post_content ) {
159 \preg_match_all( '/@' . ACTIVITYPUB_USERNAME_REGEXP . '/i', $post_content, $matches );
160 foreach ( $matches[0] as $match ) {
161 $link = Webfinger::resolve( $match );
162 if ( ! is_wp_error( $link ) ) {
163 $mentions[ $match ] = $link;
164 }
165 }
166 return $mentions;
167 }
168 }
169