PluginProbe
Friends / 2.8.4
Friends v2.8.4
4.3.2 4.3.1 4.3.0 4.2.2 4.2.1 4.2.0 4.1.0 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.1 All 88 releases
friends / includes / class-blocks.php

class-blocks.php in Friends 2.8.4, at includes/class-blocks.php

557 lines 15.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friends Blocks
4 *
5 * This contains the functions for blocks.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 /**
13 * This is the class for the Friends Plugin blocks.
14 *
15 * @since 0.8
16 *
17 * @package Friends
18 * @author Alex Kirk
19 */
20 class Blocks {
21 /**
22 * Contains a reference to the Friends class.
23 *
24 * @var Friends
25 */
26 private $friends = null;
27
28 /**
29 * Whether an excerpt is currently being generated.
30 *
31 * @var int
32 */
33 private $current_excerpt = null;
34
35 /**
36 * Constructor
37 *
38 * @param Friends $friends A reference to the Friends object.
39 */
40 public function __construct( Friends $friends ) {
41 $this->friends = $friends;
42 $this->register_hooks();
43 }
44
45 /**
46 * Register the WordPress hooks
47 */
48 private function register_hooks() {
49 if ( ! function_exists( 'register_block_type' ) ) {
50 return;
51 }
52 if ( function_exists( 'classicpress_version' ) ) {
53 return;
54 }
55 add_action( 'admin_enqueue_scripts', array( $this, 'language_data' ) );
56 add_filter( 'render_block', array( $this, 'render_friends_block_visibility' ), 10, 2 );
57 add_filter( 'get_the_excerpt', array( $this, 'current_excerpt_start' ), 9, 2 );
58 add_filter( 'get_the_excerpt', array( $this, 'current_excerpt_end' ), 11, 2 );
59 add_filter( 'wp_loaded', array( $this, 'add_block_visibility_attribute' ), 10, 2 );
60 add_filter( 'template_redirect', array( $this, 'handle_follow_me' ), 10, 2 );
61 add_action( 'enqueue_block_editor_assets', array( $this, 'register_friends_block_visibility' ) );
62 add_action( 'init', array( $this, 'register_blocks' ) );
63 }
64
65 /**
66 * Register our blocks.
67 */
68 public function register_blocks() {
69 if ( ! function_exists( 'register_block_type_from_metadata' ) ) {
70 // Blocks is not active.
71 return;
72 }
73 register_block_type_from_metadata(
74 FRIENDS_PLUGIN_DIR . '/blocks/friends-list',
75 array(
76 'render_callback' => array( $this, 'render_friends_list_block' ),
77 )
78 );
79
80 register_block_type_from_metadata(
81 FRIENDS_PLUGIN_DIR . '/blocks/friend-posts',
82 array(
83 'render_callback' => array( $this, 'render_friend_posts_block' ),
84 )
85 );
86
87 register_block_type_from_metadata(
88 FRIENDS_PLUGIN_DIR . '/blocks/follow-me',
89 array(
90 'render_callback' => array( $this, 'render_follow_me_block' ),
91 )
92 );
93
94 register_block_type_from_metadata(
95 FRIENDS_PLUGIN_DIR . '/blocks/message'
96 );
97 }
98
99 /**
100 * Render the Friends List block
101 *
102 * @param array $attributes Attributes set by Blocks.
103 * @return string The new block content.
104 */
105 public function render_friends_list_block( $attributes ) {
106 if ( ! isset( $attributes['user_types'] ) ) {
107 $attributes['user_types'] = 'friends';
108 }
109 switch ( $attributes['user_types'] ) {
110 case 'friend_requests':
111 $friends = User_Query::all_friend_requests();
112 $no_users = __( "You currently don't have any friend requests.", 'friends' );
113 break;
114
115 case 'friends_subscriptions':
116 $friends = User_Query::all_associated_users();
117 $no_users = __( "You don't have any friends or subscriptions yet.", 'friends' );
118 break;
119
120 case 'subscriptions':
121 $friends = User_Query::all_subscriptions();
122 $no_users = __( "You don't have any subscriptions yet.", 'friends' );
123 break;
124
125 case 'friends':
126 default:
127 $friends = User_Query::all_friends();
128 $no_users = __( "You don't have any friends yet.", 'friends' );
129 }
130
131 if ( $friends->get_total() === 0 ) {
132 return '<span class="wp-block-friends-friends-list no-users">' . $no_users . '</span>';
133 }
134
135 if ( $attributes['users_inline'] ) {
136 $out = '';
137 $first = true;
138 } else {
139 $out = '<ul>';
140 }
141 $count = 0;
142 foreach ( $friends->get_results() as $friend_user ) {
143 $count += 1;
144 if ( $attributes['users_inline'] ) {
145 if ( ! $first ) {
146 $out .= ', ';
147 }
148 $first = false;
149 } else {
150 $out .= '<li>';
151 }
152
153 if ( friends::has_required_privileges() ) {
154 $url = $friend_user->get_local_friends_page_url();
155 } else {
156 $url = $friend_user->user_url;
157 }
158
159 $out .= sprintf(
160 '<a class="wp-block-friends-friends-list wp-user" href="%1$s">%2$s</a></li>',
161 esc_url( $url ),
162 esc_html( $friend_user->display_name )
163 );
164 if ( ! $attributes['users_inline'] ) {
165 $out .= '</li>';
166 }
167 }
168 if ( ! $attributes['users_inline'] ) {
169 $out .= '</ul>';
170 }
171
172 return $out;
173 }
174
175 /**
176 * Render the Friend Posts block
177 *
178 * @param array $attributes Attributes set by Blocks.
179 * @return string The new block content.
180 */
181 public function render_friend_posts_block( $attributes ) {
182 $date_formats = array(
183 'Y m d H' => 'human',
184 'Y m d' => 'H:i',
185 'Y w' => 'D j, H:i',
186 'Y' => 'M j',
187 '' => 'M j, Y',
188 );
189
190 $last_author = false;
191
192 $only_users = array();
193 if ( isset( $attributes['only_users'] ) ) {
194 $only_users = array_flip( array_filter( preg_split( '/[, ]+/', $attributes['only_users'] ) ) );
195 }
196 $exclude_users = array();
197 if ( isset( $attributes['exclude_users'] ) ) {
198 $exclude_users = array_flip( array_filter( preg_split( '/[, ]+/', $attributes['exclude_users'] ) ) );
199 }
200
201 $count = max( 1, min( 100, $attributes['count'] ) );
202 $remaining = $count;
203 $offset = 0;
204 $out = '<ul class="friend-posts">';
205 while ( $remaining > 0 ) {
206 $recent_posts = wp_get_recent_posts(
207 array(
208 'numberposts' => $count,
209 'offset' => $offset,
210 'post_type' => apply_filters( 'friends_frontend_post_types', array() ),
211 )
212 );
213 if ( count( $recent_posts ) === 0 ) {
214 break;
215 }
216 $offset += $count;
217
218 foreach ( $recent_posts as $post ) {
219 $friend_user = new User( $post['post_author'] );
220
221 if ( ! empty( $only_users ) && ! isset( $only_users[ $friend_user->user_login ] ) && ! isset( $only_users[ $friend_user->ID ] ) && ! isset( $only_users[ $friend_user->display_name ] ) ) {
222 continue;
223 }
224
225 if ( ! empty( $exclude_users ) && isset( $exclude_users[ $friend_user->user_login ] ) && isset( $exclude_users[ $friend_user->ID ] ) && isset( $exclude_users[ $friend_user->display_name ] ) ) {
226 continue;
227 }
228
229 if ( $remaining <= 0 ) {
230 break 2;
231 }
232 $remaining -= 1;
233
234 $author = $friend_user->display_name;
235 if ( $attributes['author_name'] || $attributes['author_avatar'] || $attributes['author_inline'] ) {
236 if ( $attributes['author_inline'] || $last_author !== $author ) {
237 if ( $last_author && ! $attributes['author_inline'] ) {
238 $out .= '</li></ul></li>';
239 }
240 $last_author = $author;
241
242 $out .= '<li>';
243 if ( $attributes['author_avatar'] ) {
244 $out .= '<img src="' . esc_url( get_avatar_url( $post['post_author'] ) ) . '" width="20" height="20" class="avatar" />';
245 }
246 if ( $attributes['author_name'] ) {
247 $out .= esc_html( $author );
248 }
249
250 if ( $attributes['author_inline'] ) {
251 $out .= ' ';
252 } else {
253 $out .= '<ul><li>';
254 }
255 } else {
256 $out .= '<li>';
257 }
258 } else {
259 $out .= '<li>';
260 }
261 $title = get_the_title( $post['ID'] );
262 if ( empty( $title ) ) {
263 $title = get_the_excerpt( $post['ID'] );
264 }
265 $out .= sprintf(
266 '<a class="wp-block-friends-friend-posts" href="%1$s">%2$s</a>',
267 esc_url( $attributes['internal_link'] ? $friend_user->get_local_friends_page_url( $post['ID'] ) : get_permalink( $post['ID'] ) ),
268 esc_html( $title )
269 );
270
271 if ( $attributes['show_date'] ) {
272 $post_date = strtotime( $post['post_date_gmt'] );
273 foreach ( $date_formats as $compare => $date_format ) {
274 if ( gmdate( $compare ) === gmdate( $compare, $post_date ) ) {
275 break;
276 }
277 }
278 $out .= ' <span class="date" data-date="' . esc_attr( $post['post_date'] ) . '">';
279 if ( 'human' === $date_format ) {
280 /* translators: %s is a time span */
281 $out .= sprintf( __( '%s ago' ), human_time_diff( $post_date ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
282 } else {
283 $out .= date_i18n( $date_format, strtotime( $post['post_date'] ) );
284 }
285 $out .= '</span>';
286 }
287 $out .= '</li>';
288 }
289 }
290
291 if ( $last_author && ( $attributes['author_name'] || $attributes['author_avatar'] ) && ! $attributes['author_inline'] ) {
292 $out .= '</ul></li>';
293 }
294 $out .= '</ul>';
295
296 return $out;
297 }
298
299 /**
300 * Register the Block Visibility script.
301 */
302 public function register_friends_block_visibility() {
303 wp_enqueue_script(
304 'friends-block-visibility',
305 plugins_url( 'blocks/block-visibility/build/index.js', FRIENDS_PLUGIN_FILE ),
306 array( 'wp-blocks', 'wp-element', 'wp-i18n', 'wp-editor' ),
307 Friends::VERSION
308 );
309
310 wp_enqueue_style(
311 'friends-blocks',
312 plugins_url( 'friends-blocks.css', FRIENDS_PLUGIN_FILE ),
313 array(),
314 Friends::VERSION
315 );
316 }
317
318 /**
319 * Adds a block visibility attribute.
320 */
321 public function add_block_visibility_attribute() {
322 $registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();
323
324 foreach ( $registered_blocks as $block ) {
325 $block->attributes['friendsVisibility'] = array(
326 'type' => 'string',
327 'default' => '',
328 );
329 }
330 }
331
332 /**
333 * Handle the follow me button click.
334 */
335 public function handle_follow_me() {
336 if ( ! isset( $_REQUEST['friends_friend_request_url'] ) ) {
337 return;
338 }
339 if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'friends_follow_me' ) ) {
340 wp_safe_redirect( add_query_arg( 'error', __( 'Unable to verify nonce, please try again.', 'friends' ) ) );
341 exit;
342 }
343 $access_transient_key = 'friends_follow_me_' . crc32( $_SERVER['REMOTE_ADDR'] );
344 $access_count = get_transient( $access_transient_key );
345 if ( $access_count >= 10 ) {
346 header( 'HTTP/1.0 529 Too Many Requests' );
347 wp_die( 'Too Many Requests' );
348 }
349 set_transient( $access_transient_key, $access_count + 1, 3600 );
350
351 $url = $_REQUEST['friends_friend_request_url'];
352
353 $fqdn_regex = '(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)';
354 if ( false === strpos( $url, 'https://' ) ) {
355 $url = 'https://' . $url;
356 }
357 $parts = parse_url( $url );
358
359 if ( ! preg_match( '/' . $fqdn_regex . '/', $parts['host'] ) ) {
360 wp_safe_redirect( add_query_arg( 'error', __( 'You specified an invalid URL.', 'friends' ) ) );
361 exit;
362 }
363
364 $response = wp_safe_remote_head(
365 $url,
366 array(
367 'timeout' => 5,
368 'redirection' => 5,
369 'headers' => array(
370 'Accept: text/html',
371 ),
372 )
373 );
374 if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
375 wp_safe_redirect(
376 add_query_arg(
377 'error',
378 sprintf( /* translators: %s is an HTTP status code */
379 __( 'The server returned an HTTP status: %s', 'friends' ),
380 wp_remote_retrieve_response_code( $response )
381 )
382 )
383 );
384 exit;
385 }
386
387 $links = (array) wp_remote_retrieve_header( $response, 'link' );
388 if ( ! empty( $links ) ) {
389 $friends_base_url_endpoints = array_filter(
390 $links,
391 function ( $link ) {
392 return preg_match( '/rel="friends-base-url"/', $link );
393 }
394 );
395
396 if ( ! empty( $friends_base_url_endpoints ) ) {
397 header( 'Location: ' . $url . '?add-friend=' . urlencode( home_url() ) );
398 exit;
399 }
400 }
401
402 // Try again for servers that filter out the Link headers.
403 $response = wp_safe_remote_get(
404 $url,
405 array(
406 'timeout' => 5,
407 'redirection' => 5,
408 )
409 );
410 if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
411 wp_safe_redirect(
412 add_query_arg(
413 'error',
414 sprintf( /* translators: %s is an HTTP status code */
415 __( 'The server returned an HTTP status: %s', 'friends' ),
416 wp_remote_retrieve_response_code( $response )
417 )
418 )
419 );
420 exit;
421 }
422
423 $content = wp_remote_retrieve_body( $response );
424 $mf = Mf2\parse( $content, $url );
425
426 if ( isset( $mf['rel-urls'] ) ) {
427 foreach ( $mf['rel-urls'] as $link_url => $link ) {
428 if ( in_array( 'friends-base-url', $link['rels'] ) ) {
429 header( 'Location: ' . $link_url . '?add-friend=' . urlencode( home_url() ) );
430 exit;
431 }
432 }
433 }
434
435 header( 'Location: https://wpfriends.at/follow-me?url=' . urlencode( $_REQUEST['friends_friend_request_url'] ) );
436 exit;
437 }
438
439 /**
440 * Add a CSRF to the Follow Me block.
441 *
442 * @param array $attributes Block attributes.
443 * @param string $content Block default content.
444 * @param \WP_Block $block Block instance.
445 * @return string The rendered content.
446 */
447 public function render_follow_me_block( $attributes, $content, $block ) {
448 $input = '<input type="text" name="friends_friend_request_url"';
449 $nonce = wp_nonce_field( 'friends_follow_me', '_wpnonce', true, false );
450 return str_replace( $input, $nonce . $input, $content );
451 }
452
453 /**
454 * Render the "Only visible for friends" Blocks block
455 *
456 * @param string $content The content provided by the user.
457 * @param object $block Attributes for the block.
458 * @return string The rendered content.
459 */
460 public function render_friends_block_visibility( $content, $block ) {
461 $css_class = ( empty( $block['attrs'] ) || empty( $block['attrs']['className'] ) ) ? 'default' : $block['attrs']['className'];
462 $visibility = '';
463 if ( preg_match( '/\bonly-friends\b/', $css_class ) ) {
464 $visibility = 'only-friends';
465 } elseif ( preg_match( '/\bnot-friends\b/', $css_class ) ) {
466 $visibility = 'not-friends';
467 }
468
469 if ( ! $visibility ) {
470 return $content;
471 }
472 $class_only_friends = ' class="only-friends" style="background-color: #efe; padding-left: .5em;"';
473 $class_not_friends = ' class="not-friends" style="background-color: #fee; padding-left: .5em;"';
474 $class_watermark = ' class="watermark" style="float: right; padding-top: .5em; padding-right: .5em; font-size: 80%; color: #ccc;"';
475
476 switch ( $visibility ) {
477 case 'only-friends':
478 if ( friends::has_required_privileges() ) {
479 if ( $this->current_excerpt ) {
480 return $content;
481 }
482 return '<div' . $class_only_friends . '><span' . $class_watermark . '>' . __( 'Only friends', 'friends' ) . '</span>' . $content . '</div>';
483 }
484 if ( current_user_can( 'friend' ) ) {
485 return $content;
486 }
487 return '';
488
489 case 'not-friends':
490 if ( friends::has_required_privileges() ) {
491 if ( $this->current_excerpt ) {
492 return $content;
493 }
494
495 return '<div' . $class_not_friends . '><span' . $class_watermark . '>' . __( 'Not friends', 'friends' ) . '</span>' . $content . '</span></div>';
496 }
497 if ( current_user_can( 'friend' ) ) {
498 return '';
499 }
500 return $content;
501
502 }
503
504 return $content;
505 }
506
507 /**
508 * Remember the current post being excerpted. With this we can change the visibility rendering.
509 *
510 * @param string $text The text.
511 * @param \WP_Post $post The post.
512 *
513 * @return string The text.
514 */
515 public function current_excerpt_start( $text = '', $post = null ) {
516 if ( $post ) {
517 $this->current_excerpt = $post->ID;
518 }
519 return $text;
520 }
521
522 /**
523 * Stop remembering the current post being excerpted.
524 *
525 * @param string $text The text.
526 * @param \WP_Post $post The post.
527 *
528 * @return string The text.
529 */
530 public function current_excerpt_end( $text = '', $post = null ) {
531 if ( $post && $this->current_excerpt === $post->ID ) {
532 $this->current_excerpt = null;
533 }
534 return $text;
535 }
536
537 /**
538 * Load up the language data for the Blocks blocks
539 */
540 public function language_data() {
541 $locale_data = array();
542 if ( function_exists( 'wp_get_jed_locale_data' ) ) {
543 $locale_data = wp_get_jed_locale_data( 'friends' );
544 } elseif ( function_exists( 'blocks_get_jed_locale_data' ) ) {
545 $locale_data = blocks_get_jed_locale_data( 'friends' );
546 }
547
548 if ( ! empty( $locale_data ) ) {
549 wp_add_inline_script(
550 'friends-block-not-friends',
551 'wp.i18n.setLocaleData( ' . wp_json_encode( $locale_data ) . ', "friends" );',
552 'before'
553 );
554 }
555 }
556 }
557