PluginProbe
Friends / 2.7.4
Friends v2.7.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.7.4, at includes/class-blocks.php

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