PluginProbe
ActivityPub / 3.2.4
ActivityPub v3.2.4
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-blocks.php

class-blocks.php in ActivityPub 3.2.4, at includes/class-blocks.php

202 lines 6.9 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 Activitypub\Collection\Followers;
5 use Activitypub\Collection\Users as User_Collection;
6
7 use function Activitypub\object_to_uri;
8 use function Activitypub\is_user_type_disabled;
9
10 class Blocks {
11 public static function init() {
12 // this is already being called on the init hook, so just add it.
13 self::register_blocks();
14 \add_action( 'wp_enqueue_scripts', array( self::class, 'add_data' ) );
15 \add_action( 'enqueue_block_editor_assets', array( self::class, 'add_data' ) );
16 \add_action( 'load-post-new.php', array( self::class, 'handle_in_reply_to_get_param' ) );
17 }
18
19 /**
20 * Enqueue the reply handle script if the in_reply_to GET param is set.
21 */
22 public static function handle_in_reply_to_get_param() {
23 // only load the script if the in_reply_to GET param is set, action happens there, not here.
24 if ( ! isset( $_GET['in_reply_to'] ) ) {
25 return;
26 }
27
28 $asset_data = include ACTIVITYPUB_PLUGIN_DIR . 'build/reply-intent/plugin.asset.php';
29 $plugin_url = plugins_url( 'build/reply-intent/plugin.js', ACTIVITYPUB_PLUGIN_FILE );
30 wp_enqueue_script( 'activitypub-reply-intent', $plugin_url, $asset_data['dependencies'], $asset_data['version'], true );
31 }
32
33 public static function add_data() {
34 $context = is_admin() ? 'editor' : 'view';
35 $followers_handle = 'activitypub-followers-' . $context . '-script';
36 $follow_me_handle = 'activitypub-follow-me-' . $context . '-script';
37 $data = array(
38 'namespace' => ACTIVITYPUB_REST_NAMESPACE,
39 'enabled' => array(
40 'site' => ! is_user_type_disabled( 'blog' ),
41 'users' => ! is_user_type_disabled( 'user' ),
42 ),
43 );
44 $js = sprintf( 'var _activityPubOptions = %s;', wp_json_encode( $data ) );
45 \wp_add_inline_script( $followers_handle, $js, 'before' );
46 \wp_add_inline_script( $follow_me_handle, $js, 'before' );
47 }
48
49 public static function register_blocks() {
50 \register_block_type_from_metadata(
51 ACTIVITYPUB_PLUGIN_DIR . '/build/followers',
52 array(
53 'render_callback' => array( self::class, 'render_follower_block' ),
54 )
55 );
56 \register_block_type_from_metadata(
57 ACTIVITYPUB_PLUGIN_DIR . '/build/follow-me',
58 array(
59 'render_callback' => array( self::class, 'render_follow_me_block' ),
60 )
61 );
62 \register_block_type_from_metadata(
63 ACTIVITYPUB_PLUGIN_DIR . '/build/reply',
64 array(
65 'render_callback' => array( self::class, 'render_reply_block' ),
66 )
67 );
68 }
69
70 private static function get_user_id( $user_string ) {
71 if ( is_numeric( $user_string ) ) {
72 return absint( $user_string );
73 }
74 // any other non-numeric falls back to 0, including the `site` string used in the UI
75 return 0;
76 }
77
78 /**
79 * Filter an array by a list of keys.
80 * @param array $array The array to filter.
81 * @param array $keys The keys to keep.
82 * @return array The filtered array.
83 */
84 protected static function filter_array_by_keys( $array, $keys ) {
85 return array_intersect_key( $array, array_flip( $keys ) );
86 }
87
88 /**
89 * Render the follow me block.
90 * @param array $attrs The block attributes.
91 * @return string The HTML to render.
92 */
93 public static function render_follow_me_block( $attrs ) {
94 $user_id = self::get_user_id( $attrs['selectedUser'] );
95 $user = User_Collection::get_by_id( $user_id );
96 if ( ! is_wp_error( $user ) ) {
97 $attrs['profileData'] = self::filter_array_by_keys(
98 $user->to_array(),
99 array( 'icon', 'name', 'webfinger' )
100 );
101 }
102
103 // add `@` prefix if it's missing
104 if ( '@' !== substr( $attrs['profileData']['webfinger'], 0, 1 ) ) {
105 $attrs['profileData']['webfinger'] = '@' . $attrs['profileData']['webfinger'];
106 }
107
108 $wrapper_attributes = get_block_wrapper_attributes(
109 array(
110 'aria-label' => __( 'Follow me on the Fediverse', 'activitypub' ),
111 'class' => 'activitypub-follow-me-block-wrapper',
112 'data-attrs' => wp_json_encode( $attrs ),
113 )
114 );
115 // todo: render more than an empty div?
116 return '<div ' . $wrapper_attributes . '></div>';
117 }
118
119 public static function render_follower_block( $attrs ) {
120 $followee_user_id = self::get_user_id( $attrs['selectedUser'] );
121 $per_page = absint( $attrs['per_page'] );
122 $follower_data = Followers::get_followers_with_count( $followee_user_id, $per_page );
123
124 $attrs['followerData']['total'] = $follower_data['total'];
125 $attrs['followerData']['followers'] = array_map(
126 function ( $follower ) {
127 return self::filter_array_by_keys(
128 $follower->to_array(),
129 array( 'icon', 'name', 'preferredUsername', 'url' )
130 );
131 },
132 $follower_data['followers']
133 );
134 $wrapper_attributes = get_block_wrapper_attributes(
135 array(
136 'aria-label' => __( 'Fediverse Followers', 'activitypub' ),
137 'class' => 'activitypub-follower-block',
138 'data-attrs' => wp_json_encode( $attrs ),
139 )
140 );
141
142 $html = '<div ' . $wrapper_attributes . '>';
143 if ( $attrs['title'] ) {
144 $html .= '<h3>' . esc_html( $attrs['title'] ) . '</h3>';
145 }
146 $html .= '<ul>';
147 foreach ( $follower_data['followers'] as $follower ) {
148 $html .= '<li>' . self::render_follower( $follower ) . '</li>';
149 }
150 // We are only pagination on the JS side. Could be revisited but we gotta ship!
151 $html .= '</ul></div>';
152 return $html;
153 }
154
155 /**
156 * Render the reply block.
157 *
158 * @param array $attrs The block attributes.
159 *
160 * @return void
161 */
162 public static function render_reply_block( $attrs ) {
163 return apply_filters(
164 'activitypub_reply_block',
165 sprintf(
166 '<p><a title="%2$s" aria-label="%2$s" href="%1$s" class="u-in-reply-to" target="_blank">%3$s</a></p>',
167 esc_url( $attrs['url'] ),
168 esc_attr__( 'This post is a response to the referenced content.', 'activitypub' ),
169 // translators: %s is the URL of the post being replied to.
170 sprintf( __( '&#8620;%s', 'activitypub' ), \str_replace( array( 'https://', 'http://' ), '', $attrs['url'] ) )
171 ),
172 $attrs
173 );
174 }
175
176 public static function render_follower( $follower ) {
177 $external_svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" class="components-external-link__icon css-rvs7bx esh4a730" aria-hidden="true" focusable="false"><path d="M18.2 17c0 .7-.6 1.2-1.2 1.2H7c-.7 0-1.2-.6-1.2-1.2V7c0-.7.6-1.2 1.2-1.2h3.2V4.2H7C5.5 4.2 4.2 5.5 4.2 7v10c0 1.5 1.2 2.8 2.8 2.8h10c1.5 0 2.8-1.2 2.8-2.8v-3.6h-1.5V17zM14.9 3v1.5h3.7l-6.4 6.4 1.1 1.1 6.4-6.4v3.7h1.5V3h-6.3z"></path></svg>';
178 $template =
179 '<a href="%s" title="%s" class="components-external-link activitypub-link" target="_blank" rel="external noreferrer noopener">
180 <img width="40" height="40" src="%s" class="avatar activitypub-avatar" />
181 <span class="activitypub-actor">
182 <strong class="activitypub-name">%s</strong>
183 <span class="sep">/</span>
184 <span class="activitypub-handle">@%s</span>
185 </span>
186 %s
187 </a>';
188
189 $data = $follower->to_array();
190
191 return sprintf(
192 $template,
193 esc_url( object_to_uri( $data['url'] ) ),
194 esc_attr( $data['name'] ),
195 esc_attr( $data['icon']['url'] ),
196 esc_html( $data['name'] ),
197 esc_html( $data['preferredUsername'] ),
198 $external_svg
199 );
200 }
201 }
202