PluginProbe
ActivityPub / 1.0.9
ActivityPub v1.0.9
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.9, at includes/class-mention.php

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