PluginProbe
Friends / 4.3.2
Friends v4.3.2
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
← All changes | includes/class-admin.php +1979 -1555 2.7.54.3.2 View file →
@@ -39,41 +39,43 @@
39 39 * Register the WordPress hooks
40 40 */
41 41 private function register_hooks() {
42 42 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
43 - add_action( 'friends_own_site_menu_top', array( $this, 'friends_add_menu_open_friend_request' ), 10, 2 );
44 43 add_filter( 'users_list_table_query_args', array( $this, 'allow_role_multi_select' ) );
45 - add_filter( 'user_row_actions', array( get_called_class(), 'user_row_actions' ), 10, 2 );
46 - add_filter( 'handle_bulk_actions-users', array( $this, 'handle_bulk_friend_request_approval' ), 10, 3 );
47 - add_filter( 'bulk_actions-users', array( $this, 'add_user_bulk_options' ) );
48 - add_filter( 'manage_users_columns', array( $this, 'user_list_columns' ) );
49 - add_filter( 'manage_users_custom_column', array( get_called_class(), 'user_list_custom_column' ), 10, 3 );
50 44 add_filter( 'the_title', array( $this, 'override_post_format_title' ), 10, 2 );
51 45 add_filter( 'get_edit_user_link', array( $this, 'admin_edit_user_link' ), 10, 2 );
52 46 add_action( 'admin_bar_menu', array( $this, 'admin_bar_friends_menu' ), 39 );
53 47 add_action( 'admin_bar_menu', array( $this, 'admin_bar_new_content' ), 71 );
54 48 add_action( 'wp_head', array( $this, 'admin_bar_mobile' ) );
49 + add_action( 'admin_head', array( $this, 'admin_bar_mobile' ) );
55 50 add_action( 'current_screen', array( $this, 'register_help' ) );
56 51 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 39 );
57 52 add_action( 'gettext_with_context', array( $this->friends, 'translate_user_role' ), 10, 4 );
58 53 add_action( 'wp_ajax_friends_preview_rules', array( $this, 'ajax_preview_friend_rules' ) );
59 - add_action( 'wp_ajax_friends_refresh_link_token', array( $this, 'ajax_refresh_link_token' ) );
60 54 add_action( 'wp_ajax_friends_fetch_feeds', array( $this, 'ajax_fetch_feeds' ) );
61 55 add_action( 'wp_ajax_friends_set_avatar', array( $this, 'ajax_set_avatar' ) );
56 + add_action( 'wp_ajax_friends-refresh-feeds', array( $this, 'ajax_refresh_feeds' ) );
57 + add_action( 'wp_ajax_friends-preview-subscription', array( $this, 'ajax_preview_subscription' ) );
58 + add_action( 'wp_ajax_friends-preview-subscription-feed', array( $this, 'ajax_preview_subscription_feed' ) );
59 + add_action( 'wp_ajax_friends-subscribe-frontend', array( $this, 'ajax_subscribe_frontend' ) );
62 60 add_action( 'delete_user_form', array( $this, 'delete_user_form' ), 10, 2 );
63 61 add_action( 'delete_user', array( $this, 'delete_user' ) );
64 62 add_action( 'remove_user_from_blog', array( $this, 'delete_user' ) );
65 63 add_action( 'tool_box', array( $this, 'toolbox_bookmarklets' ) );
66 64 add_action( 'dashboard_glance_items', array( $this, 'dashboard_glance_items' ) );
67 - add_filter( 'site_status_tests', array( $this, 'site_status_tests' ) );
65 + add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widgets' ), 8 );
66 + add_action( 'wp_ajax_friends_dashboard', array( $this, 'ajax_friends_dashboard' ) );
68 67 add_filter( 'site_status_test_php_modules', array( $this, 'site_status_test_php_modules' ) );
69 - add_filter( 'debug_information', array( $this, 'site_health_debug' ) );
70 68 add_filter( 'friends_create_and_follow', array( $this, 'create_and_follow' ), 10, 4 );
69 + add_action( 'friends_edit_feed_content_top', array( $this, 'maybe_render_activitypub_inactive_notice' ), 10, 3 );
71 70
72 71 if ( ! get_option( 'permalink_structure' ) ) {
73 72 add_action( 'admin_notices', array( $this, 'admin_notice_unsupported_permalink_structure' ) );
74 73 }
75 - add_filter( 'friends_unread_count', array( $this, 'friends_unread_friend_request_count' ) );
74 + if ( get_option( 'friends_welcome_version' ) ) {
75 + add_action( 'admin_notices', array( $this, 'admin_notice_welcome' ) );
76 + }
77 + add_filter( 'pre_get_posts', array( $this, 'admin_friend_posts_query' ) );
76 78 }
77 79
78 80 /**
79 81 * Display admin notice about an unsupported permalink structure
@@ -107,9 +109,9 @@
107 109 /**
108 110 * Registers the admin menus
109 111 */
110 112 public function admin_menu() {
111 - if ( isset( $_REQUEST['rerun-activate'] ) && isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'friends-settings' ) ) {
113 + if ( isset( $_REQUEST['rerun-activate'] ) && isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends-settings' ) ) {
112 114 Friends::activate_plugin();
113 115 wp_safe_redirect( add_query_arg( array( 'reran-activation' => 'friends' ), wp_get_referer() ) );
114 116 exit;
115 117 }
@@ -117,48 +119,82 @@
117 119 $unread_badge = $this->get_unread_badge();
118 120
119 121 $menu_title = __( 'Friends', 'friends' ) . $unread_badge;
120 122 $page_type = sanitize_title( $menu_title );
121 - add_menu_page( 'friends', $menu_title, $required_role, 'friends', null, 'dashicons-groups', 3 );
123 + $current_page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : '';
124 + add_menu_page( __( 'Friends', 'friends' ), $menu_title, $required_role, 'friends', null, 'dashicons-groups', 3 );
125 + add_submenu_page( 'friends', __( 'Friends', 'friends' ), __( 'Home', 'friends' ), $required_role, 'friends', array( $this, 'render_admin_home' ) );
126 + add_action( 'load-' . $page_type . '_page_friends-page', array( $this, 'redirect_to_friends_page' ) );
127 + add_submenu_page( 'friends', __( 'Add Friend', 'friends' ), __( 'Add Friend', 'friends' ), $required_role, 'add-friend', array( $this, 'render_admin_add_friend' ) );
122 128 // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
123 - add_submenu_page( 'friends', __( 'Home' ), __( 'Home' ), $required_role, 'friends', array( $this, 'render_admin_home' ) );
124 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
125 129 add_submenu_page( 'friends', __( 'Settings' ), __( 'Settings' ), $required_role, 'friends-settings', array( $this, 'render_admin_settings' ) );
126 - add_action( 'load-' . $page_type . '_page_friends-page', array( $this, 'redirect_to_friends_page' ) );
127 - add_submenu_page( 'friends', __( 'Notification Manager', 'friends' ), __( 'Notification Manager', 'friends' ), $required_role, 'friends-notification-manager', array( $this, 'render_admin_notification_manager' ) );
130 + if (
131 + in_array(
132 + $current_page,
133 + apply_filters( 'friends_admin_settings_slugs', array( 'friends-settings', 'friends-notification-manager', 'friends-wp-friendships', 'friends-import-export', 'friends-migrations' ) )
134 + )
135 + ) {
136 + add_submenu_page( 'friends', __( 'Notifications', 'friends' ), '- ' . __( 'Notifications', 'friends' ), $required_role, 'friends-notification-manager', array( $this, 'render_admin_notification_manager' ) );
137 + add_submenu_page( 'friends', __( 'Import/Export', 'friends' ), '- ' . __( 'Import/Export', 'friends' ), $required_role, 'friends-import-export', array( $this, 'render_admin_import_export' ) );
138 + do_action( 'friends_admin_menu_settings', $page_type );
139 + }
140 +
141 + if ( 'friends-migrations' === $current_page && current_user_can( 'manage_options' ) ) {
142 + add_submenu_page( 'friends', __( 'Migrations', 'friends' ), __( 'Migrations', 'friends' ), 'manage_options', 'friends-migrations', array( Migration::class, 'render_admin_page' ) );
143 + }
128 144 add_action( 'load-' . $page_type . '_page_friends-notification-manager', array( $this, 'process_admin_notification_manager' ) );
129 - add_submenu_page( 'friends', __( 'Add New Friend', 'friends' ), __( 'Add New Friend', 'friends' ), $required_role, 'add-friend', array( $this, 'render_admin_add_friend' ) );
145 + add_action( 'load-' . $page_type . '_page_friends-import-export', array( $this, 'process_admin_import_export' ) );
130 146 add_action( 'load-' . $page_type . '_page_friends-settings', array( $this, 'process_admin_settings' ) );
131 147
132 - add_submenu_page( 'friends', __( 'Friends & Requests', 'friends' ), __( 'Friends & Requests', 'friends' ), $required_role, 'friends-list', array( $this, 'render_friends_list' ) );
148 + if (
149 + isset( $_GET['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'friends-refresh' ) && 'friends-refresh' === $current_page
150 + ) {
151 + add_submenu_page( 'friends', __( 'Refresh', 'friends' ), __( 'Refresh', 'friends' ), $required_role, 'friends-refresh', array( $this, 'admin_refresh_friend_posts' ) );
152 + }
133 153
134 - if ( $this->friends_unread_friend_request_count( 0 ) > 0 ) {
135 - add_submenu_page( 'friends', __( 'Friend Requests', 'friends' ), __( 'Friend Requests', 'friends' ) . $unread_badge, $required_role, 'friends-list-requests', array( $this, 'render_friends_list' ) );
136 - } elseif ( isset( $_GET['page'] ) && 'friends-list-requests' === $_GET['page'] ) {
137 - // Don't show a no permission page but redirect to the friends list.
138 - add_submenu_page( 'friends', __( 'Friend Requests', 'friends' ), __( 'Friend Requests', 'friends' ) . $unread_badge, $required_role, 'friends-list-requests', array( $this, 'render_friends_list' ) );
154 + $friend_submenu_items = array(
155 + 'edit-friend' => __( 'Edit User', 'friends' ),
156 + 'edit-friend-feeds' => __( 'Edit Feeds', 'friends' ),
157 + 'edit-friend-notifications' => __( 'Edit Notifications', 'friends' ),
158 + 'edit-friend-rules' => __( 'Edit Rules', 'friends' ),
159 + 'duplicate-remover' => __( 'Duplicates', 'friends' ),
160 + );
161 + if ( isset( $friend_submenu_items[ $current_page ] ) ) {
162 + foreach ( $friend_submenu_items as $slug => $title ) {
163 + $user_param = '';
164 + if ( isset( $_GET['user'] ) ) {
165 + $username = sanitize_user( wp_unslash( $_GET['user'] ) );
166 + $user_param = '&user=' . $username . '&_wpnonce=' . wp_create_nonce( $slug . '-' . $username );
167 + }
168 + $slug_ = strtr( $slug, '-', '_' );
169 +
170 + add_submenu_page(
171 + 'friends',
172 + $title,
173 + $title,
174 + $required_role,
175 + $slug . ( $slug === $current_page ? '' : $user_param ),
176 + array( $this, 'render_admin_' . $slug_ )
177 + );
178 +
179 + add_action(
180 + 'load-' . $page_type . '_page_' . $slug,
181 + array( $this, 'process_admin_' . $slug_ )
182 + );
183 + }
139 184 }
140 185
141 - if ( isset( $_GET['page'] ) && 'friends-refresh' === $_GET['page'] ) {
142 - add_submenu_page( 'friends', __( 'Refresh', 'friends' ), __( 'Refresh', 'friends' ), $required_role, 'friends-refresh', array( $this, 'admin_refresh_friend_posts' ) );
186 + if ( isset( $_GET['page'] ) && 'friends-logs' === $_GET['page'] ) {
187 + // translators: as in log file.
188 + $title = __( 'Log', 'friends' );
189 + add_submenu_page( 'friends', $title, $title, $required_role, 'friends-logs', array( $this, 'render_friends_logs' ) );
143 190 }
144 191
145 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
146 - add_submenu_page( 'friends', __( 'Plugins' ), __( 'Plugins' ), $required_role, 'friends-plugins', array( $this, 'admin_plugin_installer' ) );
192 + $title = __( 'Browser Extension', 'friends' );
193 + add_submenu_page( 'friends', $title, $title, $required_role, 'friends-browser-extension', array( $this, 'render_browser_extension' ) );
147 194
148 - if ( isset( $_GET['page'] ) && 0 === strpos( $_GET['page'], 'edit-friend' ) ) {
149 - add_submenu_page( 'friends', __( 'Edit User', 'friends' ), __( 'Edit User', 'friends' ), $required_role, 'edit-friend' . ( 'edit-friend' !== $_GET['page'] && isset( $_GET['user'] ) ? '&user=' . $_GET['user'] : '' ), array( $this, 'render_admin_edit_friend' ) );
150 - add_submenu_page( 'friends', __( 'Edit Feeds', 'friends' ), __( 'Edit Feeds', 'friends' ), $required_role, 'edit-friend-feeds' . ( 'edit-friend-feeds' !== $_GET['page'] && isset( $_GET['user'] ) ? '&user=' . $_GET['user'] : '' ), array( $this, 'render_admin_edit_friend_feeds' ) );
151 - add_submenu_page( 'friends', __( 'Edit Notifications', 'friends' ), __( 'Edit Notifications', 'friends' ), $required_role, 'edit-friend-notifications' . ( 'edit-friend-notifications' !== $_GET['page'] && isset( $_GET['user'] ) ? '&user=' . $_GET['user'] : '' ), array( $this, 'render_admin_edit_friend_notifications' ) );
152 - add_submenu_page( 'friends', __( 'Edit Rules', 'friends' ), __( 'Edit Rules', 'friends' ), $required_role, 'edit-friend-rules' . ( 'edit-friend-rules' !== $_GET['page'] && isset( $_GET['user'] ) ? '&user=' . $_GET['user'] : '' ), array( $this, 'render_admin_edit_friend_rules' ) );
153 - add_action( 'load-' . $page_type . '_page_edit-friend', array( $this, 'process_admin_edit_friend' ) );
154 - add_action( 'load-' . $page_type . '_page_edit-friend-feeds', array( $this, 'process_admin_edit_friend_feeds' ) );
155 - add_action( 'load-' . $page_type . '_page_edit-friend-notifications', array( $this, 'process_admin_edit_friend_notifications' ) );
156 - add_action( 'load-' . $page_type . '_page_edit-friend-rules', array( $this, 'process_admin_edit_friend_rules' ) );
157 - }
158 -
159 195 if ( isset( $_GET['page'] ) && 'unfriend' === $_GET['page'] ) {
160 - $user = new User( $_GET['user'] );
196 + $user = new User( intval( $_GET['user'] ) );
161 197 if ( $user ) {
162 198 $title = /* translators: %s is a username. */ sprintf( __( 'Unfriend %s', 'friends' ), $user->user_login );
163 199 add_submenu_page( 'friends', $title, $title, $required_role, 'unfriend', array( $this, 'render_admin_unfriend' ) );
164 200 add_action( 'load-' . $page_type . '_page_unfriend', array( $this, 'process_admin_unfriend' ) );
@@ -163,9 +199,8 @@
163 199 add_submenu_page( 'friends', $title, $title, $required_role, 'unfriend', array( $this, 'render_admin_unfriend' ) );
164 200 add_action( 'load-' . $page_type . '_page_unfriend', array( $this, 'process_admin_unfriend' ) );
165 201 }
166 202 }
167 -
168 203 }
169 204
170 205 /**
171 206 * Allow making use of the role__in query.
@@ -204,9 +239,8 @@
204 239 $( "#toplevel_page_friends-settings ul li a[href='<?php echo esc_html( self::get_users_url() ); ?>']" ).closest( 'li' ).addClass( 'current' );
205 240 } );
206 241 </script>
207 242 <?php
208 -
209 243 }
210 244
211 245 /**
212 246 * Add our help information
@@ -229,12 +263,12 @@
229 263 __( 'Welcome to the Friends Settings! You can configure the Friends plugin here to your liking.', 'friends' ) .
230 264 '</p>' .
231 265 '<p>' .
232 266 sprintf(
233 - // translators: %1$s is a URL, %2$s is the name of a wp-admin screen.
234 - __( 'There are more settings available for each friend or subscription individually. To get there, click on the user on the <a href=%1$s>%2$s</a> screen.', 'friends' ),
235 - '"' . esc_attr( self_admin_url( self::get_users_url() ) ) . '"',
236 - __( 'Friends &amp; Requests', 'friends' )
267 + // translators: %1$s is a URL, %2$s is the name of a page.
268 + __( 'There are more settings available for each friend or subscription individually. To get there, click on the user on the <a href=%1$s>%2$s</a> page.', 'friends' ),
269 + '"' . esc_attr( self::get_users_url() ) . '"',
270 + __( 'Following', 'friends' )
237 271 ) .
238 272 '</p>',
239 273 )
240 274 );
@@ -257,19 +291,17 @@
257 291 public function admin_enqueue_scripts() {
258 292 $handle = 'friends-admin';
259 293 $file = 'friends-admin.js';
260 294 $version = Friends::VERSION;
261 - wp_enqueue_script( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'jquery' ), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ) );
295 + wp_enqueue_script( $handle, plugins_url( $file, FRIENDS_PLUGIN_FILE ), array( 'jquery' ), apply_filters( 'friends_debug_enqueue', $version, $handle, dirname( FRIENDS_PLUGIN_FILE ) . '/' . $file ), true );
262 296
263 297 $variables = array(
264 298 'ajax_url' => admin_url( 'admin-ajax.php' ),
265 299 'add_friend_url' => self_admin_url( 'admin.php?page=add-friend' ),
266 300 'add_friend_text' => __( 'Add a Friend', 'friends' ),
301 + 'copy_text' => __( 'Copy', 'friends' ),
302 + 'copied_text' => __( 'Copied!', 'friends' ),
267 303 'delete_feed_question' => __( 'Delete the feed? You need to click "Save Changes" to really delete it.', 'friends' ),
268 - 'role_friend' => __( 'Friend', 'friends' ),
269 - 'role_acquaintance' => __( 'Acquaintance', 'friends' ),
270 - 'role_friend_request' => __( 'Friend Request', 'friends' ),
271 - 'role_pending_friend_request' => __( 'Pending Friend Request', 'friends' ),
272 304 'role_subscription' => __( 'Following', 'friends' ),
273 305 'role_connection' => __( 'Connection', 'friends' ),
274 306 'role_contact' => __( 'Contact', 'friends' ),
275 307 'role_connection_request' => __( 'Connection Request', 'friends' ),
@@ -296,10 +328,17 @@
296 328
297 329 add_filter(
298 330 'friends_friend_private_feed_url',
299 331 function ( $feed_url, $friend_user ) {
300 - // translators: %1s is the name of the friend, %2$s is the feed URL.
301 - printf( __( 'Refreshing %1$s at %2$s', 'friends' ) . '<br/>', '<a href="' . esc_url( $friend_user->get_local_friends_page_url() ) . '">' . esc_html( $friend_user->user_login ) . '</a>', '<a href="' . esc_url( $feed_url ) . '">' . esc_html( $feed_url ) . '</a>' );
332 + echo wp_kses(
333 + // translators: %1s is the name of the friend, %2$s is the feed URL.
334 + sprintf( __( 'Refreshing %1$s at %2$s', 'friends' ) . '<br/>', '<a href="' . esc_url( $friend_user->get_local_friends_page_url() ) . '">' . esc_html( $friend_user->user_login ) . '</a>', '<a href="' . esc_url( $feed_url ) . '">' . esc_html( $feed_url ) . '</a>' ),
335 + array(
336 + 'a' => array(
337 + 'href' => array(),
338 + ),
339 + )
340 + );
302 341 return $feed_url;
303 342 },
304 343 10,
305 344 2
@@ -340,10 +379,10 @@
340 379 10,
341 380 2
342 381 );
343 382
344 - if ( isset( $_GET['user'] ) ) {
345 - $friend_user = User::get_by_username( $_GET['user'] );
383 + if ( isset( $_GET['user'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
384 + $friend_user = User::get_by_username( sanitize_user( wp_unslash( $_GET['user'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
346 385 if ( ! $friend_user || is_wp_error( $friend_user ) || ! $friend_user->can_refresh_feeds() ) {
347 386 wp_die( esc_html__( 'Invalid user ID.' ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
348 387 }
349 388 $friend_user->retrieve_posts_from_active_feeds();
@@ -352,85 +391,8 @@
352 391 }
353 392 }
354 393
355 394 /**
356 - * Admin page for installing plugins.
357 - */
358 - public function admin_plugin_installer() {
359 - Friends::template_loader()->get_template_part( 'admin/plugin-installer-header' );
360 - Plugin_Installer::init();
361 - Friends::template_loader()->get_template_part( 'admin/plugin-installer-footer' );
362 - }
363 -
364 - /**
365 - * Send a friend request to another WordPress with the Friends plugin
366 - *
367 - * @param string $rest_url The site URL of the friend's
368 - * WordPress.
369 - * @param string $user_login The user login.
370 - * @param string $user_url The user url.
371 - * @param string $display_name The display name.
372 - * @param string $codeword A codeword to send along.
373 - * @param string $message A message to send along.
374 - *
375 - * @return \WP_User|\WP_error $user The new associated user or an error object.
376 - */
377 - public function send_friend_request( $rest_url, $user_login, $user_url, $display_name, $codeword = 'friends', $message = '' ) {
378 - if ( ! is_string( $rest_url ) || ! Friends::check_url( $rest_url ) ) {
379 - return new \WP_Error( 'invalid-url', __( 'You entered an invalid URL.', 'friends' ) );
380 - }
381 -
382 - $future_in_token = wp_generate_password( 128, false );
383 -
384 - $current_user = wp_get_current_user();
385 - $response = wp_safe_remote_post(
386 - $rest_url . '/friend-request',
387 - array(
388 - 'body' => array(
389 - 'version' => 2,
390 - 'codeword' => $codeword,
391 - 'name' => $current_user->display_name,
392 - 'url' => home_url(),
393 - 'icon_url' => get_avatar_url( $current_user->ID ),
394 - 'message' => mb_substr( trim( $message ), 0, 2000 ),
395 - 'key' => $future_in_token,
396 - ),
397 - 'timeout' => 20,
398 - 'redirection' => 5,
399 - )
400 - );
401 - if ( is_wp_error( $response ) ) {
402 - return $response;
403 - }
404 -
405 - $json = json_decode( wp_remote_retrieve_body( $response ) );
406 - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
407 - if ( $json && isset( $json->code ) && isset( $json->message ) ) {
408 - // translators: %s is the message from the other server.
409 - return new \WP_Error( $json->code, sprintf( __( 'The other side responded: %s', 'friends' ), $json->message ), $json->data );
410 - }
411 - }
412 -
413 - if ( ! $json || ! is_object( $json ) ) {
414 - return new \WP_Error( 'unexpected-rest-response', 'Unexpected remote response: ' . substr( wp_remote_retrieve_body( $response ), 0, 30 ), $response );
415 - }
416 -
417 - $friend_user = User::create( $user_login, 'pending_friend_request', $user_url, $display_name );
418 - if ( is_wp_error( $friend_user ) ) {
419 - return $friend_user;
420 - }
421 - $friend_user->update_user_option( 'friends_rest_url', $rest_url );
422 -
423 - if ( isset( $json->request ) ) {
424 - update_option( 'friends_request_' . sha1( $json->request ), $friend_user->ID );
425 - $friend_user->update_user_option( 'friends_future_in_token_' . sha1( $json->request ), $future_in_token );
426 - $friend_user->set_role( 'pending_friend_request' );
427 - }
428 -
429 - return $friend_user;
430 - }
431 -
432 - /**
433 395 * Don't show the edit link for friend posts
434 396 *
435 397 * @param string $link The edit link.
436 398 * @param int|User $user The user.
@@ -436,8 +398,21 @@
436 398 * @param int|User $user The user.
437 399 * @return string|bool The edit link or false.
438 400 */
439 401 public static function admin_edit_user_link( $link, $user ) {
402 + static $cache = array();
403 + if ( $user instanceof \WP_User ) {
404 + $cache_key = $user->ID;
405 + } else {
406 + $cache_key = $user;
407 + }
408 +
409 + if ( isset( $cache[ $cache_key ] ) ) {
410 + if ( false === $cache[ $cache_key ] ) {
411 + return $link;
412 + }
413 + return $cache[ $cache_key ];
414 + }
440 415 if ( ! $user instanceof \WP_User ) {
441 416 if ( is_string( $user ) ) {
442 417 $user = User::get_by_username( $user );
443 418 } else {
@@ -444,22 +419,37 @@
444 419 $user = new \WP_User( $user );
445 420 }
446 421 }
447 422
423 + if ( ! $user || is_wp_error( $user ) ) {
424 + $cache[ $cache_key ] = false;
425 + return $link;
426 + }
427 +
448 428 if ( is_multisite() && is_super_admin( $user->ID ) ) {
429 + $cache[ $cache_key ] = false;
449 430 return $link;
450 431 }
451 432 if ( ! $user->has_cap( 'friends_plugin' ) ) {
433 + $cache[ $cache_key ] = false;
452 434 return $link;
453 435 }
454 436
455 - return self_admin_url( 'admin.php?page=edit-friend&user=' . $user->user_login );
437 + $cache[ $cache_key ] = self_admin_url( 'admin.php?page=edit-friend&user=' . $user->user_login );
438 + return $cache[ $cache_key ];
456 439 }
457 440
458 441 public static function get_edit_friend_link( $user ) {
459 - if ( ! $user instanceof \WP_User ) {
460 - $user = new \WP_User( $user );
442 + if ( is_string( $user ) ) {
443 + $user = User::get_by_username( $user );
444 + } elseif ( ! $user instanceof User && ! $user instanceof Subscription ) {
445 + $user = new User( $user );
461 446 }
447 +
448 + if ( ! $user || is_wp_error( $user ) ) {
449 + return '';
450 + }
451 +
462 452 return apply_filters( 'get_edit_user_link', $user->user_url, $user->user_login );
463 453 }
464 454
465 455 public static function get_unfriend_link( $user ) {
@@ -494,15 +484,15 @@
494 484 if ( empty( $_REQUEST ) || ! isset( $_REQUEST['_wpnonce'] ) ) {
495 485 return;
496 486 }
497 487
498 - if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'friends-settings' ) ) {
488 + if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends-settings' ) ) {
499 489 return;
500 490 }
501 491
502 492 $this->check_admin_settings();
503 - foreach ( array( 'ignore_incoming_friend_requests' ) as $checkbox ) {
504 - if ( isset( $_POST[ $checkbox ] ) && $_POST[ $checkbox ] ) {
493 + foreach ( array( 'disable_auto_tagging', 'disable_link_previews' ) as $checkbox ) {
494 + if ( isset( $_POST[ $checkbox ] ) && boolval( $_POST[ $checkbox ] ) ) {
505 495 update_option( 'friends_' . $checkbox, true );
506 496 } else {
507 497 delete_option( 'friends_' . $checkbox );
508 498 }
@@ -507,19 +497,18 @@
507 497 delete_option( 'friends_' . $checkbox );
508 498 }
509 499 }
510 500
511 - foreach ( array( 'friend_request_notification' ) as $negative_user_checkbox ) {
512 - if ( isset( $_POST[ $negative_user_checkbox ] ) && $_POST[ $negative_user_checkbox ] ) {
513 - delete_user_option( get_current_user_id(), 'friends_no_' . $negative_user_checkbox );
514 - } else {
515 - update_user_option( get_current_user_id(), 'friends_no_' . $negative_user_checkbox, 1 );
501 + if ( current_user_can( 'manage_options' ) ) {
502 + if ( isset( $_POST['main_user_id'] ) ) {
503 + $main_user_id = absint( $_POST['main_user_id'] );
504 + if ( $main_user_id && user_can( $main_user_id, Friends::REQUIRED_ROLE ) ) {
505 + update_option( 'friends_main_user_id', $main_user_id );
506 + }
516 507 }
517 - }
518 508
519 - if ( current_user_can( 'manage_options' ) ) {
520 - foreach ( array( 'force_enable_post_formats', 'expose_post_format_feeds' ) as $checkbox ) {
521 - if ( isset( $_POST[ $checkbox ] ) && $_POST[ $checkbox ] ) {
509 + foreach ( array( 'force_enable_post_formats', 'expose_post_format_feeds', 'exclude_compose_format_from_feed' ) as $checkbox ) {
510 + if ( isset( $_POST[ $checkbox ] ) && boolval( $_POST[ $checkbox ] ) ) {
522 511 update_option( 'friends_' . $checkbox, true );
523 512 } else {
524 513 delete_option( 'friends_' . $checkbox );
525 514 }
@@ -524,67 +513,20 @@
524 513 delete_option( 'friends_' . $checkbox );
525 514 }
526 515 }
527 516
528 - if ( isset( $_POST['limit_homepage_post_format'] ) && $_POST['limit_homepage_post_format'] && in_array( $_POST['limit_homepage_post_format'], get_post_format_slugs() ) ) {
529 - update_option( 'friends_limit_homepage_post_format', $_POST['limit_homepage_post_format'] );
517 + $post_format_slugs = get_post_format_slugs();
518 + if ( isset( $_POST['friends_compose_post_format'] ) && in_array( sanitize_key( $_POST['friends_compose_post_format'] ), array_merge( array( 'standard' ), $post_format_slugs ), true ) ) {
519 + update_option( 'friends_compose_post_format', sanitize_key( $_POST['friends_compose_post_format'] ) );
530 520 } else {
531 - delete_option( 'friends_limit_homepage_post_format' );
521 + delete_option( 'friends_compose_post_format' );
532 522 }
533 -
534 - if ( isset( $_POST['main_user_id'] ) && is_numeric( $_POST['main_user_id'] ) ) {
535 - update_option( 'friends_main_user_id', intval( $_POST['main_user_id'] ) );
536 - } else {
537 - $main_user_id = Friends::get_main_friend_user_id();
538 - $main_user_id_exists = false;
539 - $users = User_Query::all_admin_users();
540 - foreach ( $users->get_results() as $user ) {
541 - if ( $user->ID === $main_user_id ) {
542 - $main_user_id_exists = true;
543 - break;
544 - }
545 - }
546 - if ( ! $main_user_id_exists ) {
547 - // Reset the main user id.
548 - delete_option( 'friends_main_user_id' );
549 - Friends::get_main_friend_user_id();
550 - }
551 - }
552 -
553 - if ( isset( $_POST['comment_registration'] ) && $_POST['comment_registration'] ) {
554 - update_option( 'comment_registration', true );
555 - } else {
556 - delete_option( 'comment_registration' );
557 - }
558 -
559 - if ( isset( $_POST['comment_registration_message'] ) && $_POST['comment_registration_message'] ) {
560 - update_option( 'friends_comment_registration_message', $_POST['comment_registration_message'] );
561 - } else {
562 - delete_option( 'friends_comment_registration_message' );
563 - }
564 523 }
565 524
566 - if ( isset( $_POST['require_codeword'] ) && $_POST['require_codeword'] ) {
567 - update_option( 'friends_require_codeword', true );
568 - } else {
569 - delete_option( 'friends_require_codeword' );
570 - }
571 -
572 - if ( isset( $_POST['codeword'] ) && $_POST['codeword'] ) {
573 - update_option( 'friends_codeword', $_POST['codeword'] );
574 - } else {
575 - delete_option( 'friends_codeword' );
576 - }
577 -
578 - if ( isset( $_POST['wrong_codeword_message'] ) && $_POST['wrong_codeword_message'] ) {
579 - update_option( 'friends_wrong_codeword_message', $_POST['wrong_codeword_message'] );
580 - } else {
581 - delete_option( 'friends_wrong_codeword_message' );
582 - }
583 -
584 - if ( isset( $_POST['available_emojis'] ) && $_POST['available_emojis'] ) {
525 + if ( isset( $_POST['available_emojis'] ) && is_array( $_POST['available_emojis'] ) ) {
585 526 $available_emojis = array();
586 - foreach ( $_POST['available_emojis'] as $id ) {
527 + foreach ( wp_unslash( $_POST['available_emojis'] ) as $id ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
528 + $id = sanitize_key( $id );
587 529 $data = Reactions::get_emoji_data( $id );
588 530 if ( $data ) {
589 531 $available_emojis[ $id ] = $data;
590 532 }
@@ -593,58 +535,59 @@
593 535 } else {
594 536 delete_option( 'friends_selected_emojis' );
595 537 }
596 538
597 - if ( isset( $_POST['notification_keywords'] ) && $_POST['notification_keywords'] ) {
598 - $keywords = array();
599 - foreach ( $_POST['notification_keywords'] as $i => $keyword ) {
600 - if ( trim( $keyword ) ) {
601 - $keywords[] = array(
602 - 'enabled' => isset( $_POST['notification_keywords_enabled'][ $i ] ) && $_POST['notification_keywords_enabled'][ $i ],
603 - 'keyword' => $keyword,
604 - );
605 - }
606 - }
607 - update_option( 'friends_notification_keywords', $keywords );
608 - }
609 -
610 - if ( isset( $_POST['default_role'] ) && in_array( $_POST['default_role'], array( 'friend', 'acquaintance' ), true ) ) {
611 - update_option( 'friends_default_friend_role', $_POST['default_role'] );
612 - }
613 -
614 - if ( isset( $_POST['new_post_notification'] ) && $_POST['new_post_notification'] ) {
615 - delete_user_option( get_current_user_id(), 'friends_no_new_post_notification' );
616 - } else {
617 - update_user_option( get_current_user_id(), 'friends_no_new_post_notification', 1 );
618 - }
619 -
620 539 // Global retention.
621 540 $retention_number_enabled = boolval( isset( $_POST['friends_enable_retention_number'] ) && $_POST['friends_enable_retention_number'] );
622 541 update_option( 'friends_enable_retention_number', $retention_number_enabled );
623 - if ( $retention_number_enabled ) {
542 + if ( $retention_number_enabled && isset( $_POST['friends_retention_number'] ) ) {
624 543 update_option( 'friends_retention_number', max( 1, intval( $_POST['friends_retention_number'] ) ) );
625 544 }
626 545 $retention_days_enabled = boolval( isset( $_POST['friends_enable_retention_days'] ) && $_POST['friends_enable_retention_days'] );
627 546 update_option( 'friends_enable_retention_days', $retention_days_enabled );
628 - if ( $retention_days_enabled ) {
547 + if ( $retention_days_enabled && isset( $_POST['friends_retention_days'] ) ) {
629 548 update_option( 'friends_retention_days', max( 1, intval( $_POST['friends_retention_days'] ) ) );
630 549 }
631 550
551 + if ( isset( $_POST['retention_delete_reacted'] ) && 1 === intval( $_POST['retention_delete_reacted'] ) ) {
552 + delete_option( 'friends_retention_delete_reacted' );
553 + } else {
554 + update_option( 'friends_retention_delete_reacted', true );
555 + }
556 +
632 557 if ( isset( $_POST['frontend_default_view'] ) && in_array(
633 - $_POST['frontend_default_view'],
558 + wp_unslash( $_POST['frontend_default_view'] ),
634 559 array(
635 560 'collapsed',
636 561 )
637 562 ) ) {
638 - update_option( 'friends_frontend_default_view', $_POST['frontend_default_view'] );
563 + update_user_option( get_current_user_id(), 'friends_frontend_default_view', wp_unslash( $_POST['frontend_default_view'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
639 564 } else {
640 - delete_option( 'friends_frontend_default_view' );
565 + delete_user_option( get_current_user_id(), 'friends_frontend_default_view' );
641 566 }
642 567
568 + foreach ( array_merge( array( '' ), get_post_format_slugs() ) as $post_type ) {
569 + $name = 'friends_frontend_theme';
570 + if ( $post_type ) {
571 + $name = 'friends_frontend_theme_' . $post_type;
572 + }
573 + $theme = 'default';
574 + if ( isset( $_POST[ $name ] ) && in_array( $theme, array_keys( Frontend::get_themes() ) ) ) {
575 + $theme = wp_unslash( $_POST[ $name ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
576 + }
577 + if ( 'default' === $theme ) {
578 + delete_user_option( get_current_user_id(), $name );
579 + } else {
580 + update_user_option( get_current_user_id(), $name, $theme );
581 + }
582 + }
583 +
584 + $redirect_args = array( 'updated' => '1' );
585 +
643 586 if ( isset( $_GET['_wp_http_referer'] ) ) {
644 587 wp_safe_redirect( wp_get_referer() );
645 588 } else {
646 - wp_safe_redirect( add_query_arg( 'updated', '1', remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
589 + wp_safe_redirect( add_query_arg( $redirect_args, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ) ) ) );
647 590 }
648 591 exit;
649 592 }
650 593
@@ -658,13 +601,121 @@
658 601 return empty( $locale ) ? 'en_US' : $locale;
659 602 }
660 603
661 604 /**
662 - * Render the Friends Admin home page
605 + * Get the registry of news entries, newest first.
606 + *
607 + * Each entry has: version, title, template, and optionally migration_version
608 + * for entries that should show migration status.
609 + *
610 + * @return array
663 611 */
612 + public static function get_news_entries() {
613 + return apply_filters(
614 + 'friends_news_entries',
615 + array(
616 + array(
617 + 'version' => '4.3',
618 + 'title' => __( '4.3: Link Previews', 'friends' ),
619 + 'template' => 'admin/news-4-3',
620 + ),
621 + array(
622 + 'version' => '4.2',
623 + 'title' => __( '4.2: Direct Messages', 'friends' ),
624 + 'template' => 'admin/news-4-2',
625 + ),
626 + array(
627 + 'version' => '4.1',
628 + 'title' => __( '4.1: Add Friend Frontend, Twitter Theme & Browser Extension', 'friends' ),
629 + 'template' => 'admin/news-4-1',
630 + ),
631 + array(
632 + 'version' => '4.0',
633 + 'title' => __( '4.0: A Major Update', 'friends' ),
634 + 'template' => 'admin/welcome-4-0',
635 + 'migration_version' => '4.0.0',
636 + ),
637 + array(
638 + 'version' => '3.3',
639 + 'title' => __( '3.3: Styling Overhaul', 'friends' ),
640 + 'template' => 'admin/news-3-3',
641 + ),
642 + array(
643 + 'version' => '3.0',
644 + 'title' => __( '3.0: Followers & Notifications', 'friends' ),
645 + 'template' => 'admin/news-3-0',
646 + ),
647 + array(
648 + 'version' => '2.4',
649 + 'title' => __( '2.4: Mastodon Compatibility', 'friends' ),
650 + 'template' => 'admin/news-2-4',
651 + ),
652 + array(
653 + 'version' => '2.1',
654 + 'title' => __( '2.1: Frontend & Plugins', 'friends' ),
655 + 'template' => 'admin/news-2-1',
656 + ),
657 + array(
658 + 'version' => '2.0',
659 + 'title' => __( '2.0: Revisions & Site Health', 'friends' ),
660 + 'template' => 'admin/news-2-0',
661 + ),
662 + array(
663 + 'version' => '0',
664 + 'title' => __( 'Welcome to the Friends Plugin!', 'friends' ),
665 + 'template' => 'admin/welcome',
666 + ),
667 + )
668 + );
669 + }
670 +
671 + /**
672 + * Get migration statuses for a specific version.
673 + *
674 + * @param string $migration_version The version to filter migrations for.
675 + * @return array With keys: statuses, all_complete, has_in_progress.
676 + */
677 + public static function get_migration_data( $migration_version ) {
678 + $all_statuses = Migration::get_all_statuses();
679 + $statuses = array();
680 + $all_complete = true;
681 + $has_in_progress = false;
682 +
683 + foreach ( $all_statuses as $id => $status ) {
684 + if ( $status['version'] !== $migration_version ) {
685 + continue;
686 + }
687 + $statuses[ $id ] = $status;
688 + if ( empty( $status['completed'] ) ) {
689 + $all_complete = false;
690 + }
691 + if ( ! empty( $status['in_progress'] ) ) {
692 + $has_in_progress = true;
693 + }
694 + }
695 +
696 + return array(
697 + 'statuses' => $statuses,
698 + 'all_complete' => $all_complete,
699 + 'has_in_progress' => $has_in_progress,
700 + );
701 + }
702 +
703 + /**
704 + * Render the Friends Admin home page.
705 + *
706 + * Shows the welcome page for new users (no subscriptions),
707 + * or a news/changelog view for existing users.
708 + */
664 709 public function render_admin_home() {
710 + // Dismiss the update notice permanently when visiting this page.
711 + if ( get_option( 'friends_welcome_version' ) ) {
712 + delete_option( 'friends_welcome_version' );
713 + }
714 +
665 715 $friends_subscriptions = User_Query::all_associated_users();
666 - $has_friend_users = $friends_subscriptions->get_total() > 0;
716 + $is_new_user = 0 === $friends_subscriptions->get_total();
717 +
667 718 wp_enqueue_script( 'plugin-install' );
668 719 add_thickbox();
669 720 wp_enqueue_script( 'updates' );
670 721
@@ -672,18 +723,548 @@
672 723 'admin/settings-header',
673 724 null,
674 725 array(
675 726 'active' => 'friends',
676 - 'title' => __( 'Friends', 'friends' ),
677 727 )
678 728 );
679 729
680 - Friends::template_loader()->get_template_part( 'admin/welcome', null, array( 'installed_plugins' => get_plugins() ) );
730 + $news_entries = self::get_news_entries();
681 731
732 + if ( $is_new_user ) {
733 + // New users: welcome entry first, rest after.
734 + $news_entries = array_reverse( $news_entries );
735 + }
736 +
737 + Friends::template_loader()->get_template_part(
738 + 'admin/news',
739 + null,
740 + array(
741 + 'entries' => $news_entries,
742 + )
743 + );
744 +
682 745 Friends::template_loader()->get_template_part( 'admin/settings-footer' );
683 746 }
684 747
685 748 /**
749 + * Process the response after adding a friend/subscription.
750 + *
751 + * @param User|\WP_Error $friend_user The friend user object.
752 + * @param array $vars The form variables.
753 + *
754 + * @return bool Whether the operation was successful.
755 + */
756 + private function process_admin_add_friend_response( $friend_user, $vars ) {
757 + if ( is_wp_error( $friend_user ) ) {
758 + $this->display_errors( $friend_user );
759 + return false;
760 + }
761 +
762 + if ( ! $friend_user instanceof User ) {
763 + ?>
764 + <div id="message" class="updated notice is-dismissible"><p>
765 + <?php esc_html_e( 'Unknown error', 'friends' ); ?>
766 + </p></div>
767 + <?php
768 + return false;
769 + }
770 +
771 + $feed_options = array();
772 + if ( ! isset( $vars['feeds'] ) ) {
773 + $vars['feeds'] = array();
774 + }
775 + foreach ( $vars['feeds'] as $feed ) {
776 + if ( isset( $feed['type'] ) ) {
777 + $feed['mime-type'] = $feed['type'];
778 + unset( $feed['type'] );
779 + }
780 + $feed_options[ $feed['url'] ] = $feed;
781 + }
782 +
783 + $friend_user->save_feeds( $feed_options );
784 +
785 + if ( ! isset( $vars['subscribe'] ) ) {
786 + $vars['subscribe'] = array();
787 + }
788 +
789 + $count = 0;
790 + foreach ( $vars['subscribe'] as $feed_url ) {
791 + if ( ! isset( $feed_options[ $feed_url ] ) ) {
792 + continue;
793 + }
794 + $new_feed = $friend_user->subscribe( $feed_url, $feed_options[ $feed_url ] );
795 + if ( ! is_wp_error( $new_feed ) ) {
796 + do_action( 'friends_user_feed_activated', $new_feed );
797 + ++$count;
798 + }
799 + }
800 +
801 + add_filter( 'notify_about_new_friend_post', '__return_false', 999 );
802 + wp_schedule_single_event( time(), 'friends_retrieve_user_feeds', array( $friend_user->ID ) );
803 +
804 + $friend_link = '<a href="' . esc_url( $this->admin_edit_user_link( $friend_user->get_local_friends_page_url(), $friend_user ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $friend_user->display_name ) . '</a>';
805 +
806 + // translators: %s is a Site URL.
807 + $message = sprintf( __( "You're now subscribed to %s.", 'friends' ), $friend_link );
808 +
809 + ?>
810 + <div id="message" class="updated notice is-dismissible"><p>
811 + <?php
812 + echo wp_kses( $message, array( 'a' => array( 'href' => array() ) ) );
813 + // translators: %s is the friends page URL.
814 + echo ' ', wp_kses( sprintf( __( 'Go to your <a href=%s>friends page</a> to view their posts.', 'friends' ), '"' . esc_url( $friend_user->get_local_friends_page_url() ) . '"' ), array( 'a' => array( 'href' => array() ) ) );
815 + echo ' <span id="fetch-feeds" data-nonce="', esc_attr( wp_create_nonce( 'fetch-feeds-' . sanitize_user( $friend_user->user_login ) ) ), '" data-friend=', esc_attr( $friend_user->user_login ), '>', esc_html__( 'Fetching feeds...', 'friends' ), '</span>';
816 + ?>
817 + </p></div>
818 + <?php
819 + return true;
820 + }
821 +
822 + /**
823 + * Process the Add Friend form.
824 + *
825 + * @param array $vars The POST or GET variables.
826 + *
827 + * @return \WP_Error|null|bool A \WP_Error, null, or true on success.
828 + */
829 + public function process_admin_add_friend( $vars ) {
830 + $errors = new \WP_Error();
831 +
832 + $friend_url = isset( $vars['friend_url'] ) ? trim( $vars['friend_url'] ) : '';
833 +
834 + $friend_user = false;
835 +
836 + $protocol = wp_parse_url( $friend_url, PHP_URL_SCHEME );
837 + if ( ! $protocol ) {
838 + if ( is_multisite() ) {
839 + $friend_user = get_user_by( 'login', $friend_url );
840 + if ( $friend_user ) {
841 + $site = get_active_blog_for_user( $friend_user->ID );
842 + $friend_url = set_url_scheme( $site->siteurl );
843 + }
844 + }
845 +
846 + if ( ! $friend_user ) {
847 + $friend_url = apply_filters( 'friends_rewrite_incoming_url', 'https://' . $friend_url, $friend_url );
848 + }
849 + }
850 + $friend_user_login = apply_filters( 'friends_suggest_user_login', User::get_user_login_for_url( $friend_url ), $friend_url );
851 + $friend_display_name = apply_filters( 'friends_suggest_display_name', User::get_display_name_for_url( $friend_url ), $friend_url );
852 +
853 + $friend_user = get_user_by( 'login', $friend_user_login );
854 +
855 + $args = array();
856 + if ( $friend_user ) {
857 + $args['friends_multisite_user_login'] = $friend_user_login;
858 + $args['friends_multisite_display_name'] = $friend_display_name;
859 + }
860 +
861 + if ( ( isset( $vars['step2'] ) && isset( $vars['feeds'] ) && is_array( $vars['feeds'] ) ) || isset( $vars['step3'] ) ) {
862 + $friend_user_login = trim( str_replace( ' ', '-', sanitize_user( $vars['user_login'] ) ), '-' );
863 + $friend_display_name = sanitize_text_field( $vars['display_name'] );
864 + if ( ! $friend_user_login ) {
865 + // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
866 + $errors->add( 'user_login', __( '<strong>Error</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
867 + } elseif ( ! is_multisite() && username_exists( $friend_user_login ) ) {
868 + // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
869 + $errors->add( 'user_login', __( '<strong>Error</strong>: This username is already registered. Please choose another one.' ) );
870 + }
871 +
872 + $feeds = $vars['feeds'];
873 + if ( ! $errors->has_errors() ) {
874 + $avatar = null;
875 + $description = null;
876 + foreach ( $feeds as $feed_details ) {
877 + if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
878 + $avatar = $feed_details['avatar'];
879 + }
880 + if ( ! $description && ! empty( $feed_details['description'] ) ) {
881 + $description = wp_encode_emoji( $feed_details['description'] );
882 + }
883 + }
884 +
885 + $friend_user = User::create( $friend_user_login, 'subscription', $friend_url, $friend_display_name, $avatar, $description );
886 +
887 + return $this->process_admin_add_friend_response( $friend_user, $vars );
888 + }
889 + } else {
890 + if ( str_starts_with( $friend_url, home_url() ) ) {
891 + return new \WP_Error( 'friend-yourself', __( 'It seems like you sent a friend request to yourself.', 'friends' ) );
892 + }
893 +
894 + if ( preg_match( '#https://.*?@threads.net#', $friend_url ) ) {
895 + return new \WP_Error(
896 + 'threads-net',
897 + sprintf(
898 + // translators: %s is a URL.
899 + __( '⚠️ This user has <a href="%s">not enabled Fediverse sharing on their Threads.net account</a>.', 'friends' ),
900 + 'https://about.fb.com/news/2023/07/introducing-threads-new-app-text-sharing/'
901 + )
902 + );
903 + }
904 +
905 + if ( ! Friends::check_url( $friend_url ) ) {
906 + return new \WP_Error( 'invalid-url', __( 'You entered an invalid URL.', 'friends' ) );
907 + }
908 +
909 + $friend_user = User::get_user( $friend_user_login );
910 + if ( $friend_user && ! is_wp_error( $friend_user ) ) {
911 + // translators: %s is the name of a friend / site.
912 + return new \WP_Error( 'already-subscribed', sprintf( __( 'You are already subscribed to this site: %s', 'friends' ), '<a href="' . esc_url( $this->admin_edit_user_link( $friend_user->get_local_friends_page_url(), $friend_user ) ) . '">' . esc_html( $friend_user->display_name ) . '</a>' ) );
913 + }
914 +
915 + $feeds = $this->friends->feed->discover_available_feeds( $friend_url );
916 + if ( is_wp_error( $feeds ) ) {
917 + return $feeds;
918 + }
919 + if ( ! $feeds ) {
920 + return new \WP_Error( 'no-feed-found', __( 'No suitable feed was found at the provided address.', 'friends' ) );
921 + }
922 + $has_subscribable_feeds = false;
923 + $has_threads_net = false;
924 + foreach ( $feeds as $url => $feed ) {
925 + if ( 0 === strpos( $url, 'https://threads.net/' ) ) {
926 + $has_threads_net = true;
927 + }
928 + if ( isset( $feed['autoselect'] ) && $feed['autoselect'] ) {
929 + $has_subscribable_feeds = true;
930 + break;
931 + }
932 + if ( 'unsupported' !== $feed['parser'] ) {
933 + $has_subscribable_feeds = true;
934 + break;
935 + }
936 + }
937 +
938 + if ( ! $has_subscribable_feeds && $has_threads_net ) {
939 + $args['feeds_notice'] = sprintf(
940 + // translators: %s is a URL.
941 + __( '⚠️ This user has <a href="%s">not enabled Fediverse sharing on their Threads.net account</a>.', 'friends' ),
942 + 'https://about.fb.com/news/2023/07/introducing-threads-new-app-text-sharing/'
943 + );
944 + }
945 +
946 + $better_user_login = User::get_user_login_from_feeds( $feeds );
947 + if ( $better_user_login ) {
948 + $friend_user_login = trim( $better_user_login, '-' );
949 + }
950 +
951 + $better_display_name = User::get_display_name_from_feeds( $feeds );
952 + if ( $better_display_name ) {
953 + $friend_display_name = $better_display_name;
954 + if ( ! $better_user_login ) {
955 + $friend_user_login = trim( strtolower( str_replace( ' ', '-', sanitize_user( $better_display_name ) ) ), '-' );
956 + }
957 + }
958 + }
959 +
960 + if ( isset( $vars['quick-subscribe'] ) ) {
961 + $vars['feeds'] = $feeds;
962 + $vars['subscribe'] = array();
963 + foreach ( $feeds as $feed_url => $details ) {
964 + if ( isset( $details['autoselect'] ) && $details['autoselect'] ) {
965 + $vars['subscribe'][] = $feed_url;
966 + }
967 + }
968 +
969 + $avatar = null;
970 + $description = null;
971 + foreach ( $feeds as $feed_details ) {
972 + if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
973 + $avatar = $feed_details['avatar'];
974 + }
975 + if ( ! $description && ! empty( $feed_details['description'] ) ) {
976 + $description = $feed_details['description'];
977 + }
978 + }
979 +
980 + $friend_user = User::create( $friend_user_login, 'subscription', $friend_url, $friend_display_name, $avatar, $description );
981 +
982 + return $this->process_admin_add_friend_response( $friend_user, $vars );
983 + }
984 +
985 + Friends::template_loader()->get_template_part(
986 + 'admin/settings-header',
987 + null,
988 + array(
989 + 'active' => 'add-friend-confirm',
990 + 'title' => __( 'Add Friend', 'friends' ),
991 + 'menu' => array(
992 + '1. ' . __( 'Enter Details', 'friends' ) => array(
993 + 'page' => 'add-friend',
994 + 'url' => ! empty( $friend_url ) ? $friend_url : false,
995 + ),
996 + '2. ' . __( 'Confirm', 'friends' ) => 'add-friend-confirm',
997 + ),
998 + )
999 + );
1000 +
1001 + if ( $errors->has_errors() ) {
1002 + ?>
1003 + <div id="message" class="updated notice is-dismissible"><p><?php echo wp_kses( $errors->get_error_message(), array( 'strong' => array() ) ); ?></p>
1004 + </div>
1005 + <?php
1006 + }
1007 +
1008 + Friends::template_loader()->get_template_part(
1009 + 'admin/select-feeds',
1010 + null,
1011 + array_merge(
1012 + $args,
1013 + array(
1014 + 'friend_url' => $friend_url,
1015 + 'friend_user_login' => $friend_user_login,
1016 + 'friend_display_name' => $friend_display_name,
1017 + 'post_formats' => array_merge( array( 'autodetect' => __( 'Autodetect Post Format', 'friends' ) ), get_post_format_strings() ),
1018 + 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1019 + 'feeds' => $feeds,
1020 + )
1021 + )
1022 + );
1023 + }
1024 +
1025 + /**
1026 + * Render the admin form for following someone.
1027 + */
1028 + public function render_admin_add_friend() {
1029 + if ( ! Friends::has_required_privileges() ) {
1030 + wp_die( esc_html__( 'Sorry, you are not allowed to do this.', 'friends' ) );
1031 + }
1032 +
1033 + if ( ! empty( $_GET['preview'] ) ) {
1034 + $url = sanitize_text_field( wp_unslash( $_GET['preview'] ) );
1035 +
1036 + ?>
1037 + <h1>
1038 + <?php
1039 + // translators: %s is a URL.
1040 + echo esc_html( sprintf( __( 'Preview for %s', 'friends' ), $url ) );
1041 + ?>
1042 + </h1>
1043 + <?php
1044 +
1045 + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'preview-feed' ) ) {
1046 + ?>
1047 + <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'For security reasons, this preview is not available.', 'friends' ); ?></p>
1048 + </div>
1049 + <?php
1050 + return;
1051 + }
1052 + $parser = false;
1053 + if ( isset( $_GET['parser'] ) ) {
1054 + $parser_name = $this->friends->feed->get_registered_parser( sanitize_text_field( wp_unslash( $_GET['parser'] ) ) );
1055 + $parser = $this->friends->feed->get_feed_parser( sanitize_text_field( wp_unslash( $_GET['parser'] ) ) );
1056 + }
1057 + if ( ! $parser ) {
1058 + ?>
1059 + <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'An unknown parser name was supplied.', 'friends' ); ?></p>
1060 + </div>
1061 + <?php
1062 + return;
1063 + }
1064 + ?>
1065 + <h3><?php esc_html_e( 'Parser Details', 'friends' ); ?></h3>
1066 + <ul id="parser">
1067 + <li>
1068 + <?php
1069 + echo wp_kses(
1070 + // translators: %s is the name of a parser, e.g. simplepie.
1071 + sprintf( __( 'Parser: %s', 'friends' ), $parser_name ),
1072 + array(
1073 + 'a' => array(
1074 + 'href' => array(),
1075 + 'rel' => array(),
1076 + 'target' => array(),
1077 + ),
1078 + )
1079 + );
1080 + ?>
1081 + </li>
1082 + </ul>
1083 + <h3><?php esc_html_e( 'Items in the Feed', 'friends' ); ?></h3>
1084 +
1085 + <?php
1086 + $feed_id = null;
1087 + if ( isset( $_GET['feed'] ) ) {
1088 + $feed_id = intval( $_GET['feed'] );
1089 + }
1090 + $items = $this->friends->feed->preview( $parser, $url, $feed_id );
1091 + if ( is_wp_error( $items ) ) {
1092 + ?>
1093 + <div id="message" class="updated notice is-dismissible"><p><?php echo esc_html( $items->get_error_message() ); ?></p>
1094 + </div>
1095 + <?php
1096 + return;
1097 + }
1098 + ?>
1099 +
1100 + <ul>
1101 + <?php
1102 + foreach ( $items as $item ) {
1103 + $title = $item->title;
1104 + if ( 'status' === $item->post_format ) {
1105 + $title = wp_strip_all_tags( $item->content );
1106 + }
1107 + ?>
1108 + <li>
1109 + <?php if ( $title ) : ?>
1110 + <details><summary>
1111 + <?php endif; ?>
1112 + <a href="<?php echo esc_url( $item->permalink ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $item->date ); ?></a> (author: <?php echo esc_html( $item->author ); ?>, type: <?php echo esc_html( $item->post_format ); ?>):
1113 + <?php if ( $title ) : ?>
1114 + <a href="<?php echo esc_url( $item->permalink ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $title ); ?></a> <?php echo esc_html( str_word_count( wp_strip_all_tags( $item->content ) ) ); ?> words</summary>
1115 + <?php else : ?>
1116 + <p>
1117 + <?php endif; ?>
1118 + <?php echo esc_textarea( $item->content ); ?>
1119 + <?php if ( $title ) : ?>
1120 + </details>
1121 + <?php else : ?>
1122 + </p>
1123 + <?php endif; ?>
1124 + </li>
1125 + <?php
1126 + }
1127 + ?>
1128 + </ul>
1129 + <?php
1130 + return;
1131 + }
1132 +
1133 + if ( apply_filters( 'friends_debug', false ) && isset( $_GET['next'] ) ) {
1134 + $_POST = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1135 + $_POST['_wpnonce'] = wp_create_nonce( 'add-friend' );
1136 + if ( ! empty( $_POST['url'] ) && ! isset( $_POST['friend_url'] ) ) {
1137 + $friend_url = sanitize_text_field( wp_unslash( $_POST['url'] ) );
1138 + $parsed_url = wp_parse_url( $friend_url );
1139 + if ( isset( $parsed_url['host'] ) ) {
1140 + if ( ! isset( $parsed_url['scheme'] ) ) {
1141 + $friend_url = 'https://' . ltrim( $friend_url, '/' );
1142 + }
1143 + }
1144 + $_POST['friend_url'] = $friend_url;
1145 + }
1146 + }
1147 +
1148 + $response = null;
1149 + $postdata = apply_filters( 'friends_add_friend_postdata', $_POST );
1150 + if ( ! empty( $postdata ) ) {
1151 + if ( ! wp_verify_nonce( sanitize_key( $postdata['_wpnonce'] ), 'add-friend' ) ) {
1152 + $response = new \WP_Error( 'invalid-nonce', __( 'For security reasons, please verify the URL and click next if you want to proceed.', 'friends' ) );
1153 + } else {
1154 + $response = $this->process_admin_add_friend( $postdata );
1155 + }
1156 + if ( is_wp_error( $response ) ) {
1157 + ?>
1158 + <div id="message" class="updated notice is-dismissible"><p>
1159 + <?php
1160 + $message = $response->get_error_message();
1161 + if ( $response->get_error_data() ) {
1162 + $message .= ' (' . $response->get_error_data() . ')';
1163 + }
1164 + echo wp_kses(
1165 + $message,
1166 + array(
1167 + 'strong' => array(),
1168 + 'a' => array(
1169 + 'href' => array(),
1170 + 'rel' => array(),
1171 + 'target' => array(),
1172 + ),
1173 + )
1174 + );
1175 + ?>
1176 + </p>
1177 + </div>
1178 + <?php
1179 + }
1180 + if ( is_null( $response ) ) {
1181 + return;
1182 + }
1183 + }
1184 +
1185 + $args = array(
1186 + 'friend_url' => '',
1187 + 'add-friends-placeholder' => apply_filters( 'friends_add_friends_input_placeholder', __( 'Enter URL', 'friends' ) ),
1188 + );
1189 +
1190 + if ( ! empty( $_REQUEST['url'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1191 + $friend_url = sanitize_text_field( wp_unslash( $_REQUEST['url'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1192 + $parsed_url = wp_parse_url( $friend_url );
1193 + if ( isset( $parsed_url['host'] ) ) {
1194 + if ( ! isset( $parsed_url['scheme'] ) ) {
1195 + $args['friend_url'] = apply_filters( 'friends_rewrite_incoming_url', 'https://' . ltrim( $friend_url, '/' ), $friend_url, $parsed_url );
1196 + } else {
1197 + $args['friend_url'] = $friend_url;
1198 + }
1199 + } elseif ( class_exists( 'Friends\Feed_Parser_ActivityPub' ) && preg_match( '/^@?' . Feed_Parser_ActivityPub::ACTIVITYPUB_USERNAME_REGEXP . '$/i', $friend_url ) ) {
1200 + $args['friend_url'] = $friend_url;
1201 + }
1202 + }
1203 +
1204 + Friends::template_loader()->get_template_part(
1205 + 'admin/settings-header',
1206 + null,
1207 + array(
1208 + 'active' => 'add-friend',
1209 + 'title' => __( 'Add Friend', 'friends' ),
1210 + 'menu' => array(
1211 + '1. ' . __( 'Enter Details', 'friends' ) => array(
1212 + 'page' => 'add-friend',
1213 + 'url' => ! empty( $friend_url ) ? $friend_url : false,
1214 + ),
1215 + '2. ' . __( 'Confirm', 'friends' ) => false,
1216 + ),
1217 + )
1218 + );
1219 +
1220 + Friends::template_loader()->get_template_part( 'admin/add-friend', null, $args );
1221 +
1222 + Friends::template_loader()->get_template_part(
1223 + 'admin/latest-friends',
1224 + null,
1225 + array(
1226 + 'friend_requests' => User_Query::recent_friends_subscriptions( 25 )->get_results(),
1227 + )
1228 + );
1229 + Friends::template_loader()->get_template_part( 'admin/settings-footer', null, $args );
1230 + }
1231 +
1232 + /**
1233 + * Display admin notice about a new version.
1234 + */
1235 + public function admin_notice_welcome() {
1236 + if ( ! current_user_can( 'manage_options' ) ) {
1237 + return;
1238 + }
1239 +
1240 + if ( isset( $_GET['page'] ) && 'friends' === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1241 + return;
1242 + }
1243 +
1244 + $version = get_option( 'friends_welcome_version' );
1245 + $url = admin_url( 'admin.php?page=friends' );
1246 + ?>
1247 + <div class="friends-notice notice notice-info">
1248 + <p>
1249 + <b><?php esc_html_e( 'Friends', 'friends' ); ?></b>
1250 + <?php
1251 + echo wp_kses(
1252 + sprintf(
1253 + // translators: %1$s is the version number, %2$s is a URL to the What's New page.
1254 + __( '&#151; You have been updated to version %1$s! <a href="%2$s">See what\'s new and check the migration status</a>.', 'friends' ),
1255 + esc_html( $version ),
1256 + esc_url( $url )
1257 + ),
1258 + array( 'a' => array( 'href' => array() ) )
1259 + );
1260 + ?>
1261 + </p>
1262 + </div>
1263 + <?php
1264 + }
1265 +
1266 + /**
686 1267 * Render the Friends Admin settings page
687 1268 */
688 1269 public function render_admin_settings() {
689 1270 Friends::template_loader()->get_template_part(
@@ -690,14 +1271,13 @@
690 1271 'admin/settings-header',
691 1272 null,
692 1273 array(
693 1274 'active' => 'friends-settings',
694 - 'title' => __( 'Friends', 'friends' ),
695 1275 )
696 1276 );
697 1277 $this->check_admin_settings();
698 1278
699 - if ( isset( $_GET['updated'] ) ) {
1279 + if ( isset( $_GET['updated'] ) && boolval( $_GET['updated'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
700 1280 ?>
701 1281 <div id="message" class="updated notice is-dismissible"><p>
702 1282 <?php
703 1283 esc_html_e( 'Your settings were updated.', 'friends' );
@@ -705,27 +1285,13 @@
705 1285 </p></div>
706 1286 <?php
707 1287 }
708 1288
709 - // In order to switch to the frontend locale, we need to first pretend that nothing was loaded yet.
710 - global $l10n;
711 - $l10n = array();
712 -
713 - switch_to_locale( $this->get_frontend_locale() );
714 - // Now while loading the next translations we need to ensure that determine_locale() doesn't return the admin language but the frontend language.
715 - add_filter( 'pre_determine_locale', array( $this, 'get_frontend_locale' ) );
716 -
717 - $wrong_codeword_message = __( 'An invalid codeword was provided.', 'friends' );
718 - $comment_registration_message = __( 'Only people in my network can comment.', 'friends' );
719 - $my_network = __( 'my network', 'friends' );
720 - $comment_registration_default = strip_tags(
721 - /* translators: %s: Login URL. */
722 - __( 'You must be <a href="%s">logged in</a> to post a comment.' ) // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
723 - );
724 - // Now let's switch back to the admin language.
725 - remove_filter( 'pre_determine_locale', array( $this, 'get_frontend_locale' ) );
726 - restore_previous_locale();
727 1289 $post_stats = Friends::get_post_stats();
1290 + $post_type_themes = array();
1291 + foreach ( get_post_format_slugs() as $slug ) {
1292 + $post_type_themes[ 'frontend_theme_' . $slug ] = get_user_option( 'friends_frontend_theme_' . $slug );
1293 + }
728 1294
729 1295 Friends::template_loader()->get_template_part(
730 1296 'admin/settings',
731 1297 null,
@@ -730,34 +1296,27 @@
730 1296 'admin/settings',
731 1297 null,
732 1298 array_merge(
733 1299 Friends::get_post_stats(),
1300 + $post_type_themes,
734 1301 array(
735 - 'potential_main_users' => User_Query::all_admin_users(),
736 - 'main_user_id' => Friends::get_main_friend_user_id(),
737 - 'friend_roles' => $this->get_friend_roles(),
738 - 'default_role' => get_option( 'friends_default_friend_role', 'friend' ),
739 - 'force_enable_post_formats' => get_option( 'friends_force_enable_post_formats' ),
740 - 'post_format_strings' => get_post_format_strings(),
741 - 'limit_homepage_post_format' => get_option( 'friends_limit_homepage_post_format', false ),
742 - 'expose_post_format_feeds' => get_option( 'friends_expose_post_format_feeds' ),
743 - 'private_rss_key' => get_option( 'friends_private_rss_key' ),
744 - 'comment_registration' => get_option( 'comment_registration' ), // WordPress option.
745 - 'comment_registration_message' => get_option( 'friends_comment_registration_message', $comment_registration_message ),
746 - 'comment_registration_default' => $comment_registration_default,
747 - 'my_network' => $my_network,
748 - 'public_profile_link' => home_url( '/friends/' ),
749 - 'codeword' => get_option( 'friends_codeword', 'friends' ),
750 - 'require_codeword' => get_option( 'friends_require_codeword' ),
751 - 'wrong_codeword_message' => get_option( 'friends_wrong_codeword_message', $wrong_codeword_message ),
752 - 'no_friend_request_notification' => get_user_option( 'friends_no_friend_request_notification' ),
753 - 'no_new_post_notification' => get_user_option( 'friends_no_new_post_notification' ),
754 - 'notification_keywords' => Feed::get_all_notification_keywords(),
755 - 'retention_days' => Friends::get_retention_days(),
756 - 'retention_number' => Friends::get_retention_number(),
757 - 'retention_days_enabled' => get_option( 'friends_enable_retention_days' ),
758 - 'retention_number_enabled' => get_option( 'friends_enable_retention_number' ),
759 - 'frontend_default_view' => get_option( 'friends_frontend_default_view', 'expanded' ),
1302 + 'force_enable_post_formats' => get_option( 'friends_force_enable_post_formats' ),
1303 + 'post_format_strings' => get_post_format_strings(),
1304 + 'limit_homepage_post_format' => get_option( 'friends_limit_homepage_post_format', false ),
1305 + 'expose_post_format_feeds' => get_option( 'friends_expose_post_format_feeds' ),
1306 + 'compose_post_format' => get_option( 'friends_compose_post_format', 'status' ),
1307 + 'exclude_compose_format_from_feed' => get_option( 'friends_exclude_compose_format_from_feed' ),
1308 + 'main_user_id' => Friends::get_main_friend_user_id(),
1309 + 'potential_main_users' => User_Query::all_admin_users(),
1310 + 'disable_auto_tagging' => get_option( 'friends_disable_auto_tagging' ),
1311 + 'disable_link_previews' => get_option( 'friends_disable_link_previews' ),
1312 + 'retention_days' => Friends::get_retention_days(),
1313 + 'retention_number' => Friends::get_retention_number(),
1314 + 'retention_days_enabled' => get_option( 'friends_enable_retention_days' ),
1315 + 'retention_number_enabled' => get_option( 'friends_enable_retention_number' ),
1316 + 'retention_delete_reacted' => get_option( 'friends_retention_delete_reacted' ),
1317 + 'frontend_default_view' => get_user_option( 'friends_frontend_default_view', get_current_user_id() ),
1318 + 'frontend_theme' => get_user_option( 'friends_frontend_theme' ),
760 1319 )
761 1320 )
762 1321 );
763 1322
@@ -775,19 +1334,18 @@
775 1334 if ( ! isset( $_GET['user'] ) ) {
776 1335 wp_die( esc_html__( 'Invalid user.', 'friends' ) );
777 1336 }
778 1337
779 - $friend = User::get_by_username( $_GET['user'] );
1338 + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'edit-friend-rules-' . sanitize_user( wp_unslash( $_GET['user'] ) ) ) ) {
1339 + wp_die( esc_html__( 'Invalid nonce.', 'friends' ) );
1340 + }
1341 +
1342 + $friend = User::get_by_username( sanitize_user( wp_unslash( $_GET['user'] ) ) );
780 1343 if ( ! $friend || is_wp_error( $friend ) ) {
781 1344 wp_die( esc_html__( 'Invalid username.', 'friends' ) );
782 1345 }
783 1346
784 - if (
785 - ! $friend->has_cap( 'friend_request' ) &&
786 - ! $friend->has_cap( 'pending_friend_request' ) &&
787 - ! $friend->has_cap( 'friend' ) &&
788 - ! $friend->has_cap( 'subscription' )
789 - ) {
1347 + if ( ! $friend->has_cap( 'subscription' ) ) {
790 1348 wp_die( esc_html__( 'This is not a user related to this plugin.', 'friends' ) );
791 1349 }
792 1350
793 1351 return $friend;
@@ -799,18 +1357,24 @@
799 1357 public function process_admin_edit_friend_rules() {
800 1358 $friend = $this->check_admin_edit_friend_rules();
801 1359 $arg = 'updated';
802 1360 $arg_value = 1;
803 - if ( isset( $_POST['friend-rules-raw'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'friend-rules-raw-' . $friend->user_login ) ) {
804 - $rules = $this->friends->feed->validate_feed_rules( json_decode( stripslashes( $_POST['rules'] ), true ) );
1361 + if ( isset( $_POST['_wpnonce'] ) && ! empty( $_POST['friend-rules-raw'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'friend-rules-raw-' . $friend->user_login ) ) {
1362 + $rules = validate_feed_rules( wp_unslash( $_POST['friend-rules-raw'] ) );
805 1363 if ( false === $rules ) {
806 1364 $arg = 'error';
807 1365 } else {
808 1366 $friend->update_user_option( 'friends_feed_rules', $rules );
809 1367 }
810 - } elseif ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-rules-' . $friend->user_login ) ) {
811 - $friend->update_user_option( 'friends_feed_catch_all', $this->friends->feed->validate_feed_catch_all( $_POST['catch_all'] ) );
812 - $friend->update_user_option( 'friends_feed_rules', $this->friends->feed->validate_feed_rules( $_POST['rules'] ) );
1368 + } elseif ( isset( $_POST['_wpnonce'] ) && ! empty( $_POST['rules'] ) && ! empty( $_POST['catch_all'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'edit-friend-rules-' . sanitize_user( $friend->user_login ) ) ) {
1369 + $friend->update_user_option(
1370 + 'friends_feed_catch_all',
1371 + validate_feed_catch_all( wp_unslash( $_POST['catch_all'] ) )
1372 + );
1373 + $friend->update_user_option(
1374 + 'friends_feed_rules',
1375 + validate_feed_rules( wp_unslash( $_POST['rules'] ) )
1376 + );
813 1377 } else {
814 1378 return;
815 1379 }
816 1380
@@ -816,9 +1380,9 @@
816 1380
817 1381 if ( isset( $_GET['_wp_http_referer'] ) ) {
818 1382 wp_safe_redirect( wp_get_referer() );
819 1383 } else {
820 - wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
1384 + wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( '_wp_http_referer' ) ) );
821 1385 }
822 1386 exit;
823 1387 }
824 1388
@@ -849,12 +1413,14 @@
849 1413 'action' => in_array( $catch_all, array( 'trash', 'delete' ), true ) ? 'accept' : 'trash',
850 1414 'replace' => '',
851 1415 );
852 1416
853 - if ( isset( $_GET['post'] ) && intval( $_GET['post'] ) ) {
854 - $post = get_post( intval( $_GET['post'] ) );
855 - } else {
856 - $post = null;
1417 + if ( isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'edit-friend-rules-' . sanitize_user( $friend->user_login ) ) ) {
1418 + if ( isset( $_GET['post'] ) && intval( $_GET['post'] ) ) {
1419 + $post = get_post( intval( $_GET['post'] ) );
1420 + } else {
1421 + $post = null;
1422 + }
857 1423 }
858 1424
859 1425 $args = array(
860 1426 'rules' => $rules,
@@ -878,16 +1444,28 @@
878 1444 public function ajax_preview_friend_rules() {
879 1445 if ( ! Friends::has_required_privileges() ) {
880 1446 wp_die( -1 );
881 1447 }
1448 + if ( ! isset( $_GET['user'] ) ) {
1449 + wp_die( esc_html__( 'Invalid user.', 'friends' ) );
1450 + }
882 1451
1452 + check_ajax_referer( 'edit-friend-rules-' . sanitize_user( wp_unslash( $_GET['user'] ) ) );
1453 +
883 1454 if ( isset( $_GET['post'] ) && intval( $_GET['post'] ) ) {
884 1455 $post = get_post( intval( $_GET['post'] ) );
885 1456 } else {
886 1457 $post = null;
887 1458 }
888 -
889 - $this->render_preview_friend_rules( $_POST['rules'], $_POST['catch_all'], $post );
1459 + $rules = array();
1460 + if ( isset( $_POST['rules'] ) ) {
1461 + $rules = validate_feed_rules( wp_unslash( $_POST['rules'] ) );
1462 + }
1463 + $catch_all = array();
1464 + if ( isset( $_POST['catch_all'] ) ) {
1465 + $catch_all = validate_feed_rules( wp_unslash( $_POST['catch_all'] ) );
1466 + }
1467 + $this->render_preview_friend_rules( $rules, $catch_all, $post );
890 1468 wp_die( 1 );
891 1469 }
892 1470
893 1471 /**
@@ -896,123 +1474,24 @@
896 1474 public function ajax_fetch_feeds() {
897 1475 if ( ! isset( $_POST['friend'] ) ) {
898 1476 wp_send_json_error( 'missing-parameters' );
899 1477 }
900 - check_ajax_referer( 'fetch-feeds-' . $_POST['friend'] );
901 1478
902 - $friend_user = User::get_by_username( $_POST['friend'] );
1479 + check_ajax_referer( 'fetch-feeds-' . sanitize_user( wp_unslash( $_POST['friend'] ) ) );
1480 +
1481 + $friend_user = User::get_by_username( sanitize_user( wp_unslash( $_POST['friend'] ) ) );
903 1482 if ( ! $friend_user ) {
904 1483 wp_send_json_error( 'unknown-user' );
905 1484 }
906 1485
1486 + add_filter( 'notify_about_new_friend_post', '__return_false', 999 );
1487 +
907 1488 $friend_user->retrieve_posts_from_active_feeds();
908 1489
909 1490 wp_send_json_success();
910 1491 }
911 1492
912 -
913 1493 /**
914 - * Respond to the Ajax request to refresh the link token
915 - */
916 - public function ajax_refresh_link_token() {
917 - if ( ! isset( $_POST['url'] ) || ! isset( $_POST['friend'] ) ) {
918 - wp_send_json_error( 'missing-parameters' );
919 - }
920 - $url = $_POST['url'];
921 - check_ajax_referer( 'auth-link-' . $url );
922 -
923 - if ( ! friends::has_required_privileges() ) {
924 - wp_send_json_error( 'missing-priviledges' );
925 - }
926 -
927 - $friend_user = User::get_user( $_POST['friend'] );
928 - if ( ! $friend_user ) {
929 - wp_send_json_error( 'unknown-user' );
930 - }
931 -
932 - wp_send_json_success(
933 - array(
934 - 'success' => true,
935 - 'data' => array(
936 - 'token' => $friend_user->get_friend_auth(),
937 - ),
938 - )
939 - );
940 - }
941 -
942 - public function render_friends_list() {
943 - Friends::template_loader()->get_template_part(
944 - 'admin/settings-header',
945 - null,
946 - array(
947 - 'menu' => array(
948 - __( 'Your Friends & Subscriptions', 'friends' ) => 'friends-list',
949 - __( 'Your Friend Requests', 'friends' ) => 'friends-list-requests',
950 - ),
951 - 'active' => $_GET['page'],
952 - 'title' => __( 'Friends', 'friends' ),
953 - )
954 - );
955 -
956 - if ( isset( $_GET['page'] ) && 'friends-list-requests' === $_GET['page'] ) {
957 - echo '<p>';
958 - echo wp_kses(
959 - sprintf(
960 - // translators: %1$s is a URL, %2$s is the translated text "Your Friends & Subscriptions".
961 - __( 'These are your current friend requests. To see all your friends and subscriptions, go to <a href="%1$s">%2$s</a>.', 'friends' ),
962 - self_admin_url( 'admin.php?page=friends-list' ),
963 - __( 'Your Friends & Subscriptions', 'friends' )
964 - ),
965 - array(
966 - 'a' => array(
967 - 'href' => array(),
968 - ),
969 - )
970 - );
971 - echo '</p>';
972 - $query = User_Query::all_friend_requests();
973 - } else {
974 - $query = User_Query::all_associated_users();
975 - }
976 -
977 - if ( isset( $_GET['deleted'] ) ) {
978 - ?>
979 - <div id="message" class="updated notice is-dismissible"><p>
980 - <?php
981 - echo esc_html(
982 - sprintf(
983 - // translators: % s is a username.
984 - __( '%s was deleted.', 'friends' ),
985 - $_GET['deleted']
986 - )
987 - );
988 - ?>
989 - </p></div>
990 - <?php
991 - } elseif ( isset( $_GET['error'] ) ) {
992 - ?>
993 - <div id="message" class="updated error is-dismissible"><p>
994 - <?php
995 - esc_html_e( 'An error occurred.', 'friends' );
996 - echo ' ';
997 - echo esc_html( $_GET['error'] );
998 - ?>
999 - </p></div>
1000 - <?php
1001 - }
1002 -
1003 - Friends::template_loader()->get_template_part(
1004 - 'admin/friends-list',
1005 - null,
1006 - array(
1007 - 'friends' => $query->get_results(),
1008 - )
1009 - );
1010 -
1011 - Friends::template_loader()->get_template_part( 'admin/settings-footer' );
1012 - }
1013 -
1014 - /**
1015 1494 * Render the Friend rules preview
1016 1495 *
1017 1496 * @param array $rules The rules to apply.
1018 1497 * @param string $catch_all The catch all behavior.
@@ -1017,9 +1496,9 @@
1017 1496 * @param array $rules The rules to apply.
1018 1497 * @param string $catch_all The catch all behavior.
1019 1498 * @param \WP_Post $post The post.
1020 1499 */
1021 - public function render_preview_friend_rules( $rules, $catch_all, \WP_Post $post = null ) {
1500 + public function render_preview_friend_rules( $rules, $catch_all, ?\WP_Post $post = null ) {
1022 1501 $friend = $this->check_admin_edit_friend_rules();
1023 1502 $friend_posts = new \WP_Query();
1024 1503
1025 1504 $friend_posts->set( 'post_type', Friends::CPT );
@@ -1047,13 +1526,13 @@
1047 1526 if ( ! friends::has_required_privileges() ) {
1048 1527 wp_die( esc_html__( 'Sorry, you are not allowed to edit this user.' ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1049 1528 }
1050 1529
1051 - if ( ! isset( $_GET['user'] ) ) {
1530 + if ( ! isset( $_GET['user'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
1052 1531 wp_die( esc_html__( 'Invalid user.', 'friends' ) );
1053 1532 }
1054 1533
1055 - $friend = User::get_by_username( $_GET['user'] );
1534 + $friend = User::get_by_username( sanitize_user( wp_unslash( $_GET['user'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
1056 1535 if ( ! $friend || is_wp_error( $friend ) ) {
1057 1536 wp_die( esc_html__( 'Invalid username.', 'friends' ) );
1058 1537 }
1059 1538
@@ -1071,95 +1550,40 @@
1071 1550 $friend = $this->check_admin_edit_friend();
1072 1551 $arg = 'updated';
1073 1552 $arg_value = 1;
1074 1553
1075 - if ( isset( $_GET['convert-to-user'] ) && wp_verify_nonce( $_GET['convert-to-user'], 'convert-to-user-' . $friend->user_login ) ) {
1076 - if ( $friend instanceof Subscription ) {
1077 - Subscription::convert_to_user( $friend );
1078 - }
1079 - } elseif ( isset( $_GET['convert-from-user'] ) && wp_verify_nonce( $_GET['convert-from-user'], 'convert-from-user-' . $friend->user_login ) ) {
1080 - if ( $friend instanceof User && ! $friend instanceof Subscription ) {
1081 - if ( $friend->has_cap( 'friends_plugin' ) && ! $friend->has_cap( 'friend' ) && ! $friend->has_cap( 'pending_friend_request' ) && ! $friend->has_cap( 'friend_request' ) ) {
1082 - Subscription::convert_from_user( $friend );
1083 - } else {
1084 - $arg = 'error';
1085 - $arg_value = __( 'A friend cannot be converted to a virtual user.', 'friends' );
1554 + if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'edit-friend-' . $friend->user_login ) ) {
1555 + if ( isset( $_POST['friends_display_name'] ) ) {
1556 + $friends_display_name = trim( sanitize_text_field( wp_unslash( $_POST['friends_display_name'] ) ) );
1557 + if ( $friends_display_name ) {
1558 + $friend->first_name = $friends_display_name;
1559 + $friend->display_name = $friends_display_name;
1086 1560 }
1087 1561 }
1088 - } elseif ( isset( $_GET['accept-friend-request'] ) && wp_verify_nonce( $_GET['accept-friend-request'], 'accept-friend-request-' . $friend->user_login ) ) {
1089 - if ( $friend->has_cap( 'friend_request' ) ) {
1090 - $friend->set_role( get_option( 'friends_default_friend_role', 'friend' ) );
1091 - $arg = 'friend';
1562 + if ( isset( $_POST['friends_description'] ) ) {
1563 + $friend->description = trim( sanitize_text_field( wp_unslash( $_POST['friends_description'] ) ) );
1092 1564 }
1093 - } elseif ( isset( $_GET['add-friend'] ) && wp_verify_nonce( $_GET['add-friend'], 'add-friend-' . $friend->user_login ) ) {
1094 - if ( $friend->has_cap( 'pending_friend_request' ) || $friend->has_cap( 'subscription' ) ) {
1095 - $rest_url = $this->friends->rest->discover_rest_url( $friend->user_url );
1096 - if ( ! is_wp_error( $rest_url ) ) {
1097 - $response = $this->send_friend_request( $rest_url, $friend->user_login, $friend->user_url, $friend->display_name );
1098 - } else {
1099 - $response = $rest_url;
1565 + if ( isset( $_POST['user_url'] ) ) {
1566 + $user_url = sanitize_text_field( wp_unslash( $_POST['user_url'] ) );
1567 + if ( filter_var( $user_url, FILTER_VALIDATE_URL ) ) {
1568 + $friend->user_url = $user_url;
1100 1569 }
1101 -
1102 - if ( is_wp_error( $response ) ) {
1103 - $arg = 'error';
1104 - } elseif ( $response instanceof User ) {
1105 - if ( $response->has_cap( 'pending_friend_request' ) ) {
1106 - $arg = 'sent-request';
1107 - // translators: %s is a Site URL.
1108 - $arg_value = wp_kses( sprintf( __( 'Friendship requested for site %s.', 'friends' ), $response->get_local_friends_page_url() ), array( 'a' => array( 'href' => array() ) ) );
1109 - } elseif ( $response->has_cap( 'friend' ) ) {
1110 - $arg = 'friend';
1111 - $arg_value = 1;
1112 - } elseif ( $response->has_cap( 'subscription' ) ) {
1113 - $arg = 'subscribed';
1114 - $arg_value = 1;
1115 - }
1570 + }
1571 + if ( isset( $_POST['friends_user_login'] ) ) {
1572 + $new_user_login = User::sanitize_username( sanitize_text_field( wp_unslash( $_POST['friends_user_login'] ) ) );
1573 + if ( $new_user_login && $new_user_login !== $friend->user_login ) {
1574 + $friend->update_user_login( $new_user_login );
1116 1575 }
1117 1576 }
1118 - } elseif ( isset( $_GET['change-to-restricted-friend'] ) && wp_verify_nonce( $_GET['change-to-restricted-friend'], 'change-to-restricted-friend-' . $friend->user_login ) ) {
1119 - if ( $friend->has_cap( 'friend' ) ) {
1120 - $friend->set_role( 'acquaintance' );
1121 - }
1122 - } elseif ( isset( $_GET['change-to-friend'] ) && wp_verify_nonce( $_GET['change-to-friend'], 'change-to-friend-' . $friend->user_login ) ) {
1123 - if ( $friend->has_cap( 'acquaintance' ) ) {
1124 - $friend->set_role( 'friend' );
1125 - }
1126 - } elseif ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-' . $friend->user_login ) ) {
1127 - if ( trim( $_POST['friends_display_name'] ) ) {
1128 - $friend->first_name = trim( $_POST['friends_display_name'] );
1129 - $friend->display_name = trim( $_POST['friends_display_name'] );
1130 - }
1131 -
1132 - $friend->description = trim( $_POST['friends_description'] );
1133 - if ( trim( $_POST['user_url'] ) && filter_var( $_POST['user_url'], FILTER_VALIDATE_URL ) ) {
1134 - $friend->user_url = $_POST['user_url'];
1135 - }
1136 1577 $friend->save();
1137 -
1138 - $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
1139 - if ( ! $hide_from_friends_page ) {
1140 - $hide_from_friends_page = array();
1141 - }
1142 - if ( ! isset( $_POST['show_on_friends_page'] ) || ! $_POST['show_on_friends_page'] ) {
1143 - if ( ! in_array( $friend->user_login, $hide_from_friends_page ) ) {
1144 - $hide_from_friends_page[] = $friend->user_login;
1145 - update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1146 - }
1147 - } else {
1148 - if ( in_array( $friend->user_login, $hide_from_friends_page ) ) {
1149 - $hide_from_friends_page = array_values( array_diff( $hide_from_friends_page, array( $friend->user_login ) ) );
1150 - update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1151 - }
1152 - }
1153 1578 } else {
1154 1579 return;
1155 1580 }
1156 1581
1157 - if ( isset( $_GET['_wp_http_referer'] ) ) {
1158 - wp_safe_redirect( add_query_arg( $arg, $arg_value, wp_get_referer() ) );
1159 - } else {
1160 - wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
1161 - }
1582 + do_action( 'friends_edit_friend_after_form_submit', $friend );
1583 +
1584 + $redirect_url = self_admin_url( 'admin.php?page=edit-friend&user=' . $friend->user_login );
1585 + wp_safe_redirect( add_query_arg( $arg, rawurlencode( $arg_value ), $redirect_url ) );
1162 1586 exit;
1163 1587 }
1164 1588
1165 1589 /**
@@ -1168,9 +1592,9 @@
1168 1592 * @param User $friend The friend.
1169 1593 * @param string $active The active menu entry.
1170 1594 */
1171 1595 public function header_edit_friend( User $friend, $active ) {
1172 - $append = '&user=' . $friend->user_login;
1596 + $append = '&user=' . sanitize_user( $friend->user_login );
1173 1597 Friends::template_loader()->get_template_part(
1174 1598 'admin/settings-header',
1175 1599 null,
1176 1600 array(
@@ -1176,12 +1600,13 @@
1176 1600 array(
1177 1601 'active' => $active . $append,
1178 1602 'title' => $friend->user_login,
1179 1603 'menu' => array(
1180 - 'Friend Settings' => 'edit-friend' . $append,
1181 - 'Feeds' => 'edit-friend-feeds' . $append,
1182 - 'Notifications' => 'edit-friend-notifications' . $append,
1183 - 'Rules' => 'edit-friend-rules' . $append,
1604 + __( 'Posts' ) => $friend->get_local_friends_page_url(), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1605 + __( 'Settings' ) => 'edit-friend' . $append, // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1606 + __( 'Feeds', 'friends' ) => 'edit-friend-feeds' . $append,
1607 + __( 'Notifications', 'friends' ) => 'edit-friend-notifications' . $append,
1608 + __( 'Rules', 'friends' ) => 'edit-friend-rules' . $append . '&_wpnonce=' . wp_create_nonce( 'edit-friend-rules-' . $friend->user_login ),
1184 1609 ),
1185 1610 )
1186 1611 );
1187 1612 }
@@ -1194,20 +1619,16 @@
1194 1619
1195 1620 $args = array_merge(
1196 1621 $friend->get_post_stats(),
1197 1622 array(
1198 - 'friend' => $friend,
1199 - 'friends_settings_url' => add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=friends-settings' ) ),
1200 - 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1201 - 'hide_from_friends_page' => get_user_option( 'friends_hide_from_friends_page' ),
1623 + 'friend' => $friend,
1624 + 'friends_settings_url' => add_query_arg( '_wp_http_referer', remove_query_arg( '_wp_http_referer' ), self_admin_url( 'admin.php?page=friends-settings' ) ),
1625 + 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1202 1626 )
1203 1627 );
1204 - if ( ! $args['hide_from_friends_page'] ) {
1205 - $args['hide_from_friends_page'] = array();
1206 - }
1207 1628
1208 1629 $this->header_edit_friend( $friend, 'edit-friend' );
1209 -
1630 + // phpcs:disable WordPress.Security.NonceVerification
1210 1631 if ( isset( $_GET['updated'] ) ) {
1211 1632 ?>
1212 1633 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'User was updated.', 'friends' ); ?></p></div>
1213 1634 <?php
@@ -1216,10 +1637,18 @@
1216 1637 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'You are now friends.', 'friends' ); ?></p></div>
1217 1638 <?php
1218 1639 } elseif ( isset( $_GET['error'] ) ) {
1219 1640 ?>
1220 - <div id="message" class="updated error is-dismissible"><p><?php esc_html_e( 'An error occurred.', 'friends' ); ?></p></div>
1641 + <div id="message" class="updated error is-dismissible"><p>
1221 1642 <?php
1643 + if ( 1 === intval( $_GET['error'] ) ) {
1644 + esc_html_e( 'An error occurred.', 'friends' );
1645 + } else {
1646 + echo esc_html( Rest::translate_error_message( sanitize_text_field( wp_unslash( $_GET['error'] ) ) ) );
1647 + }
1648 + ?>
1649 + </p></div>
1650 + <?php
1222 1651 } elseif ( isset( $_GET['sent-request'] ) ) {
1223 1652 ?>
1224 1653 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Your request was sent.', 'friends' ); ?></p></div>
1225 1654 <?php
@@ -1227,33 +1656,363 @@
1227 1656 ?>
1228 1657 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Subscription activated.', 'friends' ); ?></p></div>
1229 1658 <?php
1230 1659 }
1660 + // phpcs:enable WordPress.Security.NonceVerification
1231 1661
1232 1662 Friends::template_loader()->get_template_part( 'admin/edit-friend', null, $args );
1233 1663 }
1234 1664
1665 + public function ajax_refresh_feeds() {
1666 + check_ajax_referer( 'friends-refresh' );
1667 +
1668 + if ( ! Friends::has_required_privileges() ) {
1669 + wp_send_json_error( __( 'You do not have permission to do this.', 'friends' ) );
1670 + }
1671 +
1672 + add_filter( 'notify_about_new_friend_post', '__return_false', 999 );
1673 +
1674 + if ( ! empty( $_POST['user'] ) ) {
1675 + $friend_user = User::get_by_username( sanitize_user( wp_unslash( $_POST['user'] ) ) );
1676 + if ( ! $friend_user || is_wp_error( $friend_user ) || ! $friend_user->can_refresh_feeds() ) {
1677 + wp_send_json_error( __( 'Invalid user ID.' ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1678 + }
1679 + $friend_user->retrieve_posts_from_active_feeds();
1680 + } else {
1681 + $this->friends->feed->retrieve_friend_posts();
1682 + }
1683 +
1684 + wp_send_json_success();
1685 + }
1686 +
1687 + private function normalize_frontend_subscription_url( $url ) {
1688 + if ( ! is_string( $url ) ) {
1689 + return '';
1690 + }
1691 +
1692 + $url = trim( $url );
1693 + if ( '' === $url ) {
1694 + return '';
1695 + }
1696 +
1697 + $protocol = wp_parse_url( $url, PHP_URL_SCHEME );
1698 + if ( ! $protocol ) {
1699 + return apply_filters( 'friends_rewrite_incoming_url', 'https://' . $url, $url );
1700 + }
1701 +
1702 + return apply_filters( 'friends_rewrite_incoming_url', $url, $url );
1703 + }
1704 +
1705 + public function ajax_preview_subscription() {
1706 + if ( ! isset( $_POST['url'] ) || is_array( $_POST['url'] ) ) {
1707 + wp_send_json_error( __( 'No URL provided.', 'friends' ) );
1708 + }
1709 +
1710 + check_ajax_referer( 'friends_add_subscription' );
1711 +
1712 + if ( ! Friends::has_required_privileges() ) {
1713 + wp_send_json_error( __( 'You do not have permission to do this.', 'friends' ) );
1714 + }
1715 +
1716 + $incoming_url = trim( wp_unslash( $_POST['url'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1717 + if ( ! class_exists( '\Activitypub\Activitypub' ) && preg_match( '/^@?[A-Za-z0-9_.-]+@(?:[A-Za-z0-9_-]+\.)+[A-Za-z]+$/i', $incoming_url ) ) {
1718 + wp_send_json_error( __( 'The ActivityPub plugin is required to follow Mastodon handles. Please install and activate it first.', 'friends' ) );
1719 + }
1720 +
1721 + $url = $this->normalize_frontend_subscription_url( $incoming_url );
1722 +
1723 + if ( '' === $url ) {
1724 + wp_send_json_error( __( 'No URL provided.', 'friends' ) );
1725 + }
1726 +
1727 + if ( str_starts_with( $url, home_url() ) ) {
1728 + wp_send_json_error( __( 'It seems like you sent a friend request to yourself.', 'friends' ) );
1729 + }
1730 +
1731 + if ( ! Friends::check_url( $url ) ) {
1732 + wp_send_json_error( __( 'You entered an invalid URL.', 'friends' ) );
1733 + }
1734 +
1735 + $user_login = apply_filters( 'friends_suggest_user_login', User::get_user_login_for_url( $url ), $url );
1736 + $display_name = apply_filters( 'friends_suggest_display_name', User::get_display_name_for_url( $url ), $url );
1737 +
1738 + $feeds = $this->friends->feed->discover_available_feeds( $url );
1739 +
1740 + if ( is_wp_error( $feeds ) ) {
1741 + wp_send_json_error( $feeds->get_error_message() );
1742 + }
1743 +
1744 + if ( empty( $feeds ) ) {
1745 + wp_send_json_error( __( 'No suitable feed was found at the provided address.', 'friends' ) );
1746 + }
1747 +
1748 + $better_user_login = User::get_user_login_from_feeds( $feeds );
1749 + if ( $better_user_login ) {
1750 + $user_login = trim( $better_user_login, '-' );
1751 + }
1752 +
1753 + $better_display_name = User::get_display_name_from_feeds( $feeds );
1754 + if ( $better_display_name ) {
1755 + $display_name = $better_display_name;
1756 + if ( ! $better_user_login ) {
1757 + $user_login = trim( User::sanitize_username( $better_display_name ), '-' );
1758 + }
1759 + }
1760 +
1761 + $friend_user = User::get_user( $user_login );
1762 + if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1763 + $friend_user = Subscription::get_by_username( $user_login );
1764 + }
1765 +
1766 + if ( $friend_user && ! is_wp_error( $friend_user ) ) {
1767 + // translators: %s is the name of a friend / site.
1768 + wp_send_json_error( sprintf( __( 'You are already subscribed to this site: %s', 'friends' ), $friend_user->display_name ) );
1769 + }
1770 +
1771 + $avatar = null;
1772 + $description = null;
1773 + foreach ( $feeds as $feed_details ) {
1774 + if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
1775 + $avatar = $feed_details['avatar'];
1776 + }
1777 + if ( ! $description && ! empty( $feed_details['description'] ) ) {
1778 + $description = $feed_details['description'];
1779 + }
1780 + }
1781 +
1782 + wp_send_json_success(
1783 + array(
1784 + 'feeds' => $feeds,
1785 + 'display_name' => $display_name ? $display_name : '',
1786 + 'user_login' => $user_login ? $user_login : '',
1787 + 'avatar' => $avatar,
1788 + 'description' => $description,
1789 + 'url' => $url,
1790 + )
1791 + );
1792 + }
1793 +
1794 + public function ajax_preview_subscription_feed() {
1795 + check_ajax_referer( 'friends_add_subscription' );
1796 +
1797 + if ( ! Friends::has_required_privileges() ) {
1798 + wp_send_json_error( __( 'You do not have permission to do this.', 'friends' ) );
1799 + }
1800 +
1801 + $url = isset( $_POST['url'] ) && ! is_array( $_POST['url'] ) ? $this->normalize_frontend_subscription_url( wp_unslash( $_POST['url'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1802 + if ( '' === $url || ! Friends::check_url( $url ) ) {
1803 + wp_send_json_error( __( 'You entered an invalid URL.', 'friends' ) );
1804 + }
1805 +
1806 + $parser = isset( $_POST['parser'] ) && ! is_array( $_POST['parser'] ) ? sanitize_key( wp_unslash( $_POST['parser'] ) ) : '';
1807 + if ( ! $parser ) {
1808 + wp_send_json_error( __( 'An invalid parser was supplied.', 'friends' ) );
1809 + }
1810 +
1811 + $items = $this->friends->feed->preview( $parser, $url );
1812 + if ( is_wp_error( $items ) ) {
1813 + wp_send_json_error( $items->get_error_message() );
1814 + }
1815 +
1816 + $preview_items = array();
1817 + foreach ( array_slice( $items, 0, 5 ) as $item ) {
1818 + $title = $item->title;
1819 + if ( 'status' === $item->post_format || ! $title ) {
1820 + $title = wp_strip_all_tags( $item->content );
1821 + }
1822 +
1823 + $preview_items[] = array(
1824 + 'title' => wp_trim_words( wp_strip_all_tags( $title ), 16 ),
1825 + 'excerpt' => wp_trim_words( wp_strip_all_tags( $item->content ), 40 ),
1826 + 'permalink' => $item->permalink,
1827 + 'date' => $item->date,
1828 + 'author' => $item->author,
1829 + 'post_format' => $item->post_format,
1830 + );
1831 + }
1832 +
1833 + wp_send_json_success(
1834 + array(
1835 + 'items' => $preview_items,
1836 + )
1837 + );
1838 + }
1839 +
1840 + public function ajax_subscribe_frontend() {
1841 + check_ajax_referer( 'friends_add_subscription' );
1842 +
1843 + if ( ! Friends::has_required_privileges() ) {
1844 + wp_send_json_error( __( 'You do not have permission to do this.', 'friends' ) );
1845 + }
1846 +
1847 + $url = isset( $_POST['url'] ) && ! is_array( $_POST['url'] ) ? $this->normalize_frontend_subscription_url( wp_unslash( $_POST['url'] ) ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1848 + $display_name = isset( $_POST['display_name'] ) && ! is_array( $_POST['display_name'] ) ? sanitize_text_field( wp_unslash( $_POST['display_name'] ) ) : '';
1849 + $user_login = isset( $_POST['user_login'] ) && ! is_array( $_POST['user_login'] ) ? User::sanitize_username( wp_unslash( $_POST['user_login'] ) ) : User::get_user_login_for_url( $url ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1850 + $feeds = isset( $_POST['feeds'] ) && is_array( $_POST['feeds'] ) ? wp_unslash( $_POST['feeds'] ) : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1851 +
1852 + if ( empty( $url ) || empty( $feeds ) ) {
1853 + wp_send_json_error( __( 'Missing required data.', 'friends' ) );
1854 + }
1855 +
1856 + if ( ! Friends::check_url( $url ) ) {
1857 + wp_send_json_error( __( 'You entered an invalid URL.', 'friends' ) );
1858 + }
1859 +
1860 + $user_login = trim( $user_login, '-' );
1861 + if ( ! $user_login ) {
1862 + wp_send_json_error( __( 'Please enter a valid username.', 'friends' ) );
1863 + }
1864 +
1865 + if ( ! $display_name ) {
1866 + $display_name = User::get_display_name_for_url( $url );
1867 + }
1868 +
1869 + if ( ! is_multisite() && username_exists( $user_login ) ) {
1870 + wp_send_json_error( __( 'This username is already registered. Please choose another one.' ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1871 + }
1872 +
1873 + $avatar = null;
1874 + $description = null;
1875 + $feed_options = array();
1876 + $subscribe = array();
1877 + $post_formats = array_merge( array( 'autodetect' => true ), array_fill_keys( array_keys( get_post_format_strings() ), true ) );
1878 +
1879 + foreach ( $feeds as $feed ) {
1880 + if ( ! is_array( $feed ) ) {
1881 + continue;
1882 + }
1883 +
1884 + $feed_url = '';
1885 + if ( ! empty( $feed['url'] ) && is_scalar( $feed['url'] ) ) {
1886 + $feed_url = esc_url_raw( trim( $feed['url'] ) );
1887 + }
1888 +
1889 + if ( ! $feed_url || ! Friends::check_url( $feed_url ) ) {
1890 + continue;
1891 + }
1892 +
1893 + $parser = isset( $feed['parser'] ) && is_scalar( $feed['parser'] ) ? sanitize_key( $feed['parser'] ) : 'simplepie';
1894 + if ( ! $parser || 'unsupported' === $parser ) {
1895 + continue;
1896 + }
1897 +
1898 + $post_format = isset( $feed['post-format'] ) && is_scalar( $feed['post-format'] ) ? sanitize_key( $feed['post-format'] ) : 'standard';
1899 + if ( ! isset( $post_formats[ $post_format ] ) ) {
1900 + $post_format = 'standard';
1901 + }
1902 +
1903 + $mime_type = isset( $feed['mime-type'] ) && is_scalar( $feed['mime-type'] ) ? sanitize_text_field( $feed['mime-type'] ) : '';
1904 + if ( ! $mime_type && ! empty( $feed['type'] ) && is_scalar( $feed['type'] ) ) {
1905 + $mime_type = sanitize_text_field( $feed['type'] );
1906 + }
1907 +
1908 + $feed_options[ $feed_url ] = array(
1909 + 'url' => $feed_url,
1910 + 'parser' => $parser,
1911 + 'post-format' => $post_format,
1912 + 'title' => isset( $feed['title'] ) && is_scalar( $feed['title'] ) ? sanitize_text_field( $feed['title'] ) : $feed_url,
1913 + );
1914 +
1915 + if ( $mime_type ) {
1916 + $feed_options[ $feed_url ]['mime-type'] = $mime_type;
1917 + }
1918 +
1919 + $is_selected = isset( $feed['selected'] ) && in_array( $feed['selected'], array( true, 'true', '1', 1, 'on' ), true );
1920 + if ( $is_selected ) {
1921 + $subscribe[] = $feed_url;
1922 + }
1923 +
1924 + if ( ! $avatar && ! empty( $feed['avatar'] ) && is_scalar( $feed['avatar'] ) ) {
1925 + $avatar = esc_url_raw( $feed['avatar'] );
1926 + }
1927 + if ( ! $description && ! empty( $feed['description'] ) && is_scalar( $feed['description'] ) ) {
1928 + $description = wp_encode_emoji( sanitize_textarea_field( $feed['description'] ) );
1929 + }
1930 + }
1931 +
1932 + if ( empty( $feed_options ) ) {
1933 + wp_send_json_error( __( 'No suitable feed was found at the provided address.', 'friends' ) );
1934 + }
1935 +
1936 + if ( empty( $subscribe ) ) {
1937 + wp_send_json_error( __( 'Please select at least one feed.', 'friends' ) );
1938 + }
1939 +
1940 + $friend_user = User::get_user( $user_login );
1941 + if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1942 + $friend_user = Subscription::get_by_username( $user_login );
1943 + }
1944 +
1945 + if ( $friend_user && ! is_wp_error( $friend_user ) ) {
1946 + // translators: %s is the name of a friend / site.
1947 + wp_send_json_error( sprintf( __( 'You are already subscribed to this site: %s', 'friends' ), $friend_user->display_name ) );
1948 + }
1949 +
1950 + $friend_user = User::create( $user_login, 'subscription', $url, $display_name, $avatar, $description );
1951 +
1952 + if ( is_wp_error( $friend_user ) ) {
1953 + wp_send_json_error( $friend_user->get_error_message() );
1954 + }
1955 +
1956 + $saved_feeds = $friend_user->save_feeds( $feed_options );
1957 + if ( is_wp_error( $saved_feeds ) ) {
1958 + wp_send_json_error( $saved_feeds->get_error_message() );
1959 + }
1960 +
1961 + foreach ( $subscribe as $feed_url ) {
1962 + if ( ! isset( $feed_options[ $feed_url ] ) ) {
1963 + continue;
1964 + }
1965 + $new_feed = $friend_user->subscribe( $feed_url, $feed_options[ $feed_url ] );
1966 + if ( ! is_wp_error( $new_feed ) ) {
1967 + do_action( 'friends_user_feed_activated', $new_feed );
1968 + }
1969 + }
1970 +
1971 + add_filter( 'notify_about_new_friend_post', '__return_false', 999 );
1972 + wp_schedule_single_event( time(), 'friends_retrieve_user_feeds', array( $friend_user->ID ) );
1973 +
1974 + wp_send_json_success(
1975 + array(
1976 + 'message' => sprintf(
1977 + // translators: %s is the name of a friend.
1978 + __( 'You are now following %s.', 'friends' ),
1979 + $display_name
1980 + ),
1981 + 'url' => $friend_user->get_local_friends_page_url(),
1982 + )
1983 + );
1984 + }
1985 +
1235 1986 public function ajax_set_avatar() {
1236 - $user_id = isset( $_POST['user'] ) ? $_POST['user'] : 0;
1987 + if ( ! isset( $_POST['user'] ) ) {
1988 + wp_send_json_error( __( 'No user specified.', 'friends' ) );
1989 + }
1237 1990
1238 - check_ajax_referer( "set-avatar-$user_id" );
1991 + check_ajax_referer( 'set-avatar-' . sanitize_user( wp_unslash( $_POST['user'] ) ) );
1239 1992
1240 1993 if ( ! current_user_can( Friends::REQUIRED_ROLE ) ) {
1241 1994 wp_send_json_error();
1242 1995 exit;
1243 1996 }
1244 -
1245 - if ( empty( $_POST['avatar'] ) || ! Friends::check_url( $_POST['avatar'] ) ) {
1997 + if ( empty( $_POST['avatar'] ) ) {
1246 1998 wp_send_json_error();
1247 1999 exit;
1248 2000 }
2001 + $avatar = check_url( wp_unslash( $_POST['avatar'] ) );
2002 + if ( empty( $avatar ) ) {
2003 + wp_send_json_error();
2004 + exit;
2005 + }
1249 2006
1250 - $friend = User::get_user_by_id( $user_id );
2007 + $friend = User::get_by_username( sanitize_user( wp_unslash( $_POST['user'] ) ) );
2008 + if ( ! $friend || is_wp_error( $friend ) ) {
2009 + wp_send_json_error( __( 'Invalid user.', 'friends' ) );
2010 + exit;
2011 + }
1251 2012
1252 - $image = file_get_contents( $_POST['avatar'] );
1253 -
1254 2013 // Use WordPress functions to check the image dimensions.
1255 - $size = \wp_getimagesize( $_POST['avatar'] );
2014 + $size = \wp_getimagesize( $avatar );
1256 2015 if ( ! $size ) {
1257 2016 wp_send_json_error( __( 'Image is in an unknown format.', 'friends' ) );
1258 2017 exit;
1259 2018 }
@@ -1262,9 +2021,9 @@
1262 2021 wp_send_json_error( __( 'Image must be square and not larger than 512x512.', 'friends' ) );
1263 2022 exit;
1264 2023 }
1265 2024
1266 - $url = $friend->update_user_icon_url( $_POST['avatar'] );
2025 + $url = $friend->update_user_icon_url( $avatar );
1267 2026
1268 2027 if ( ! $url || is_wp_error( $url ) ) {
1269 2028 wp_send_json_error( $url );
1270 2029 exit;
@@ -1284,11 +2043,11 @@
1284 2043 $friend = $this->check_admin_edit_friend();
1285 2044 $arg = 'updated';
1286 2045 $arg_value = 1;
1287 2046
1288 - if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-notifications-' . $friend->user_login ) ) {
2047 + if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'edit-friend-notifications-' . $friend->user_login ) ) {
1289 2048 if ( ! get_user_option( 'friends_no_new_post_notification' ) ) {
1290 - if ( isset( $_POST['friends_new_post_notification'] ) && $_POST['friends_new_post_notification'] ) {
2049 + if ( isset( $_POST['friends_new_post_notification'] ) && boolval( $_POST['friends_new_post_notification'] ) ) {
1291 2050 delete_user_option( get_current_user_id(), 'friends_no_new_post_notification_' . $friend->user_login );
1292 2051 } else {
1293 2052 update_user_option( get_current_user_id(), 'friends_no_new_post_notification_' . $friend->user_login, 1 );
1294 2053 }
@@ -1294,9 +2053,9 @@
1294 2053 }
1295 2054 }
1296 2055
1297 2056 if ( ! get_user_option( 'friends_no_keyword_notification' ) ) {
1298 - if ( isset( $_POST['friends_keyword_notification'] ) && $_POST['friends_keyword_notification'] ) {
2057 + if ( isset( $_POST['friends_keyword_notification'] ) && boolval( $_POST['friends_keyword_notification'] ) ) {
1299 2058 delete_user_option( get_current_user_id(), 'friends_no_keyword_notification_' . $friend->user_login );
1300 2059 } else {
1301 2060 update_user_option( get_current_user_id(), 'friends_no_keyword_notification_' . $friend->user_login, 1 );
1302 2061 }
@@ -1309,9 +2068,9 @@
1309 2068
1310 2069 if ( isset( $_GET['_wp_http_referer'] ) ) {
1311 2070 wp_safe_redirect( wp_get_referer() );
1312 2071 } else {
1313 - wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
2072 + wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ) ) ) );
1314 2073 }
1315 2074 exit;
1316 2075 }
1317 2076
@@ -1323,8 +2082,9 @@
1323 2082 $post_stats = $friend->get_post_stats();
1324 2083
1325 2084 $this->header_edit_friend( $friend, 'edit-friend-notifications' );
1326 2085
2086 + // phpcs:disable WordPress.Security.NonceVerification
1327 2087 if ( isset( $_GET['updated'] ) ) {
1328 2088 ?>
1329 2089 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Notification Settings were updated.', 'friends' ); ?></p></div>
1330 2090 <?php
@@ -1332,8 +2092,9 @@
1332 2092 ?>
1333 2093 <div id="message" class="updated error is-dismissible"><p><?php esc_html_e( 'An error occurred.', 'friends' ); ?></p></div>
1334 2094 <?php
1335 2095 }
2096 + // phpcs:enable WordPress.Security.NonceVerification
1336 2097
1337 2098 Friends::template_loader()->get_template_part(
1338 2099 'admin/edit-notifications',
1339 2100 null,
@@ -1350,30 +2111,28 @@
1350 2111 $friend = $this->check_admin_edit_friend();
1351 2112 $arg = 'updated';
1352 2113 $arg_value = 1;
1353 2114
1354 - if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-feeds-' . $friend->user_login ) ) {
2115 + if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'edit-friend-feeds-' . $friend->user_login ) ) {
1355 2116 $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
1356 2117 if ( ! $hide_from_friends_page ) {
1357 2118 $hide_from_friends_page = array();
1358 2119 }
1359 - if ( ! isset( $_POST['show_on_friends_page'] ) || ! $_POST['show_on_friends_page'] ) {
2120 + if ( ! isset( $_POST['show_on_friends_page'] ) || ! boolval( $_POST['show_on_friends_page'] ) ) {
1360 2121 if ( ! in_array( $friend->user_login, $hide_from_friends_page ) ) {
1361 2122 $hide_from_friends_page[] = $friend->user_login;
1362 2123 update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1363 2124 }
1364 - } else {
1365 - if ( in_array( $friend->user_login, $hide_from_friends_page ) ) {
2125 + } elseif ( in_array( $friend->user_login, $hide_from_friends_page ) ) {
1366 2126 $hide_from_friends_page = array_values( array_diff( $hide_from_friends_page, array( $friend->user_login ) ) );
1367 2127 update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1368 - }
1369 2128 }
1370 2129
1371 - if ( $friend->set_retention_number_enabled( isset( $_POST['friends_enable_retention_number'] ) && $_POST['friends_enable_retention_number'] ) ) {
1372 - $friend->set_retention_number( $_POST['friends_retention_number'] );
2130 + if ( $friend->set_retention_number_enabled( boolval( filter_input( INPUT_POST, 'friends_enable_retention_number', FILTER_SANITIZE_NUMBER_INT ) ) ) && isset( $_POST['friends_retention_number'] ) ) {
2131 + $friend->set_retention_number( filter_input( INPUT_POST, 'friends_retention_number', FILTER_SANITIZE_NUMBER_INT ) );
1373 2132 }
1374 - if ( $friend->set_retention_days_enabled( isset( $_POST['friends_enable_retention_days'] ) && $_POST['friends_enable_retention_days'] ) ) {
1375 - $friend->set_retention_days( $_POST['friends_retention_days'] );
2133 + if ( $friend->set_retention_days_enabled( boolval( filter_input( INPUT_POST, 'friends_enable_retention_days', FILTER_SANITIZE_NUMBER_INT ) ) ) && isset( $_POST['friends_retention_days'] ) ) {
2134 + $friend->set_retention_days( filter_input( INPUT_POST, 'friends_retention_days', FILTER_SANITIZE_NUMBER_INT ) );
1376 2135 }
1377 2136
1378 2137 $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
1379 2138 if ( ! $hide_from_friends_page ) {
@@ -1379,32 +2138,61 @@
1379 2138 if ( ! $hide_from_friends_page ) {
1380 2139 $hide_from_friends_page = array();
1381 2140 }
1382 2141
2142 + $show_on_dashboard = filter_input( INPUT_POST, 'show_on_dashboard', FILTER_VALIDATE_BOOLEAN );
2143 + $already_on_dashboard = false;
2144 + $widgets = get_user_option( 'friends_dashboard_widgets', get_current_user_id() );
2145 + if ( ! $widgets ) {
2146 + $widgets = array();
2147 + }
2148 + foreach ( $widgets as $k => $widget ) {
2149 + if ( ! empty( $widget['friend'] ) && $widget['friend'] === $friend->user_login ) {
2150 + $already_on_dashboard = true;
2151 + if ( ! $show_on_dashboard ) {
2152 + unset( $widgets[ $k ] );
2153 + update_user_option( get_current_user_id(), 'friends_dashboard_widgets', $widgets );
2154 + }
2155 + break;
2156 + }
2157 + }
2158 + if ( $show_on_dashboard && ! $already_on_dashboard ) {
2159 + $widgets[] = array( 'friend' => $friend->user_login );
2160 + update_user_option( get_current_user_id(), 'friends_dashboard_widgets', $widgets );
2161 + }
2162 +
1383 2163 if ( isset( $_POST['feeds'] ) ) {
2164 + // Sanitized below.
2165 + $feeds = wp_unslash( $_POST['feeds'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1384 2166 $existing_feeds = $friend->get_feeds();
1385 - if ( '' === trim( $_POST['feeds']['new']['url'] ) ) {
1386 - unset( $_POST['feeds']['new'] );
1387 - } else {
1388 - foreach ( $existing_feeds as $term_id => $user_feed ) {
1389 - if ( $user_feed->get_url() === trim( $_POST['feeds']['new']['url'] ) ) {
1390 - if ( isset( $_POST['feeds'][ $term_id ] ) ) {
1391 - // Let a newly entered feed overrule an existing one.
1392 - $_POST['feeds'][ $term_id ] = array_merge( $_POST['feeds'][ $term_id ], $_POST['feeds']['new'] );
1393 - $_POST['feeds'][ $term_id ]['active'] = 1;
2167 + if ( isset( $feeds['new'] ) ) {
2168 + if ( ! isset( $feeds['new']['url'] ) || '' === trim( $feeds['new']['url'] ) ) {
2169 + unset( $feeds['new'] );
2170 + } else {
2171 + foreach ( $existing_feeds as $term_id => $user_feed ) {
2172 + if ( $user_feed->get_url() === trim( $feeds['new']['url'] ) ) {
2173 + if ( isset( $feeds[ $term_id ] ) ) {
2174 + // Let a newly entered feed overrule an existing one.
2175 + $feeds[ $term_id ] = array_merge( $feeds[ $term_id ], $feeds['new'] );
2176 + $feeds[ $term_id ]['active'] = 1;
2177 + }
2178 + unset( $feeds['new'] );
2179 + break;
1394 2180 }
1395 - unset( $_POST['feeds']['new'] );
1396 - break;
1397 2181 }
1398 2182 }
1399 2183 }
1400 - foreach ( $_POST['feeds'] as $term_id => $feed ) {
2184 + foreach ( $feeds as $term_id => $feed ) {
1401 2185 if ( 'new' === $term_id ) {
1402 - if ( '' === trim( $feed['url'] ) ) {
2186 + if ( ! isset( $feed['url'] ) || '' === trim( $feed['url'] ) ) {
1403 2187 continue;
1404 2188 }
1405 2189
1406 2190 $feed['active'] = true;
2191 + $protocol = wp_parse_url( $feed['url'], PHP_URL_SCHEME );
2192 + if ( ! $protocol ) {
2193 + $feed['url'] = apply_filters( 'friends_rewrite_incoming_url', 'https://' . $feed['url'], $feed['url'] );
2194 + }
1407 2195 $new_feed = $friend->subscribe( $feed['url'], $feed );
1408 2196 if ( is_wp_error( $new_feed ) ) {
1409 2197 do_action( 'friends_process_feed_item_submit_error', $new_feed, $feed );
1410 2198 continue;
@@ -1420,8 +2208,13 @@
1420 2208 }
1421 2209 $user_feed = $existing_feeds[ $term_id ];
1422 2210 unset( $existing_feeds[ $term_id ] );
1423 2211
2212 + $protocol = wp_parse_url( $feed['url'], PHP_URL_SCHEME );
2213 + if ( ! $protocol ) {
2214 + $feed['url'] = apply_filters( 'friends_rewrite_incoming_url', 'https://' . $feed['url'], $feed['url'] );
2215 + }
2216 +
1424 2217 if ( $user_feed->get_url() !== $feed['url'] ) {
1425 2218 do_action( 'friends_user_feed_deactivated', $user_feed );
1426 2219
1427 2220 if ( ! isset( $feed['mime-type'] ) ) {
@@ -1429,9 +2222,11 @@
1429 2222 }
1430 2223
1431 2224 if ( $feed['active'] ) {
1432 2225 $new_feed = $friend->subscribe( $feed['url'], $feed );
1433 - do_action( 'friends_user_feed_activated', $new_feed );
2226 + if ( ! is_wp_error( $new_feed ) ) {
2227 + do_action( 'friends_user_feed_activated', $new_feed );
2228 + }
1434 2229 } else {
1435 2230 $new_feed = $friend->save_feed( $feed['url'], $feed );
1436 2231 }
1437 2232
@@ -1481,11 +2276,10 @@
1481 2276 foreach ( $existing_feeds as $term_id => $user_feed ) {
1482 2277 do_action( 'friends_user_feed_deactivated', $user_feed );
1483 2278 $user_feed->delete();
1484 2279 }
1485 -
1486 - do_action( 'friends_edit_friend_after_form_submit', $friend );
1487 2280 }
2281 + do_action( 'friends_edit_feeds_after_form_submit', $friend );
1488 2282 } else {
1489 2283 return;
1490 2284 }
1491 2285
@@ -1491,9 +2285,9 @@
1491 2285
1492 2286 if ( isset( $_GET['_wp_http_referer'] ) ) {
1493 2287 wp_safe_redirect( wp_get_referer() );
1494 2288 } else {
1495 - wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
2289 + wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ) ) ) );
1496 2290 }
1497 2291 exit;
1498 2292 }
1499 2293
@@ -1502,8 +2296,21 @@
1502 2296 */
1503 2297 public function render_admin_edit_friend_feeds() {
1504 2298 $friend = $this->check_admin_edit_friend();
1505 2299
2300 + $already_on_dashboard = false;
2301 + $widgets = get_user_option( 'friends_dashboard_widgets', get_current_user_id() );
2302 +
2303 + if ( ! $widgets ) {
2304 + $widgets = array();
2305 + }
2306 + foreach ( $widgets as $widget ) {
2307 + if ( ! empty( $widget['friend'] ) && $widget['friend'] === $friend->user_login ) {
2308 + $already_on_dashboard = true;
2309 + break;
2310 + }
2311 + }
2312 +
1506 2313 $args = array_merge(
1507 2314 $friend->get_post_stats(),
1508 2315 array(
1509 2316 'friend' => $friend,
@@ -1509,14 +2316,15 @@
1509 2316 'friend' => $friend,
1510 2317 'rules' => $friend->get_feed_rules(),
1511 2318 'hide_from_friends_page' => get_user_option( 'friends_hide_from_friends_page' ),
1512 2319 'post_formats' => array_merge( array( 'autodetect' => __( 'Autodetect Post Format', 'friends' ) ), get_post_format_strings() ),
1513 - 'friends_settings_url' => add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=friends-settings' ) ),
2320 + 'friends_settings_url' => add_query_arg( '_wp_http_referer', remove_query_arg( '_wp_http_referer' ), self_admin_url( 'admin.php?page=friends-settings' ) ),
1514 2321 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1515 2322 'global_retention_days' => Friends::get_retention_days(),
1516 2323 'global_retention_number' => Friends::get_retention_number(),
1517 2324 'global_retention_days_enabled' => get_option( 'friends_enable_retention_days' ),
1518 2325 'global_retention_number_enabled' => get_option( 'friends_enable_retention_number' ),
2326 + 'show_on_dashboard' => $already_on_dashboard,
1519 2327 )
1520 2328 );
1521 2329 if ( ! $args['hide_from_friends_page'] ) {
1522 2330 $args['hide_from_friends_page'] = array();
@@ -1522,8 +2330,9 @@
1522 2330 $args['hide_from_friends_page'] = array();
1523 2331 }
1524 2332 $this->header_edit_friend( $friend, 'edit-friend-feeds' );
1525 2333
2334 + // phpcs:disable WordPress.Security.NonceVerification
1526 2335 if ( isset( $_GET['updated'] ) ) {
1527 2336 ?>
1528 2337 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Feeds were updated.', 'friends' ); ?></p></div>
1529 2338 <?php
@@ -1531,8 +2340,9 @@
1531 2340 ?>
1532 2341 <div id="message" class="updated error is-dismissible"><p><?php esc_html_e( 'An error occurred.', 'friends' ); ?></p></div>
1533 2342 <?php
1534 2343 }
2344 + // phpcs:enable WordPress.Security.NonceVerification
1535 2345
1536 2346 Friends::template_loader()->get_template_part( 'admin/edit-feeds', null, $args );
1537 2347 }
1538 2348
@@ -1543,9 +2353,9 @@
1543 2353 $friend = $this->check_admin_edit_friend();
1544 2354 $arg = 'deleted';
1545 2355 $arg_value = $friend->user_login;
1546 2356
1547 - if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'unfriend-' . $friend->user_login ) ) {
2357 + if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'unfriend-' . $friend->user_login ) ) {
1548 2358 $friend->delete();
1549 2359 } else {
1550 2360 return;
1551 2361 }
@@ -1552,9 +2362,9 @@
1552 2362
1553 2363 if ( isset( $_GET['_wp_http_referer'] ) ) {
1554 2364 wp_safe_redirect( wp_get_referer() );
1555 2365 } else {
1556 - wp_safe_redirect( add_query_arg( $arg, $arg_value, self_admin_url( 'admin.php?page=friends-list' ) ) );
2366 + wp_safe_redirect( add_query_arg( $arg, $arg_value, home_url( '/friends/following/' ) ) );
1557 2367 }
1558 2368 exit;
1559 2369 }
1560 2370
@@ -1566,10 +2376,10 @@
1566 2376 $post_stats = $friend->get_post_stats();
1567 2377
1568 2378 $args = array(
1569 2379 'friend' => $friend,
1570 - 'friend_posts' => $post_stats->post_count,
1571 - 'total_size' => $post_stats->total_size,
2380 + 'friend_posts' => $post_stats['post_count'],
2381 + 'total_size' => $post_stats['total_size'],
1572 2382 );
1573 2383
1574 2384 Friends::template_loader()->get_template_part( 'admin/unfriend', null, $args );
1575 2385 }
@@ -1588,14 +2398,14 @@
1588 2398 <div id="message" class="updated error is-dismissible"><p><?php echo esc_html( $errors->get_error_message() ); ?></p>
1589 2399 <?php
1590 2400 $error_data = $errors->get_error_data();
1591 2401 if ( isset( $error_data->error ) ) {
1592 - $error = unserialize( $error_data->error );
2402 + $error = unserialize( $error_data->error ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
1593 2403 if ( is_wp_error( $error ) ) {
1594 2404 ?>
1595 2405 <pre>
1596 2406 <?php
1597 - print_r( $error );
2407 + print_r( $error ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1598 2408 ?>
1599 2409 </pre>
1600 2410 <?php
1601 2411 } elseif ( is_array( $error ) && isset( $error['body'] ) ) {
@@ -1612,664 +2422,463 @@
1612 2422 </div>
1613 2423 <?php
1614 2424 }
1615 2425
1616 - public function create_and_follow( $user_id, $url, $type, $vars = array() ) {
1617 - $vars['friend_url'] = $url;
2426 + public function create_and_follow( $user_id, $url ) {
2427 + // TODO: replace with frontend functionality.
2428 + }
1618 2429
1619 - $vars['user_login'] = apply_filters( 'friends_suggest_user_login', User::get_user_login_for_url( $url ), $url );
1620 - if ( empty( $vars['display_name'] ) ) {
1621 - $vars['display_name'] = apply_filters( 'friends_suggest_display_name', User::get_display_name_for_url( $url ), $url );
2430 + /**
2431 + * Process the admin notification manager form submission.
2432 + */
2433 + public function process_admin_notification_manager() {
2434 + if ( empty( $_POST ) ) {
2435 + return;
1622 2436 }
1623 2437
1624 - $vars['step2'] = true;
2438 + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'notification-manager' ) ) {
2439 + return;
2440 + }
1625 2441
1626 - $vars['subscribe'] = array( $url );
1627 - $vars['feeds'] = $this->friends->feed->discover_available_feeds( $url );
2442 + $this->check_admin_settings();
1628 2443
1629 - ob_start();
1630 - $ret = $this->process_admin_add_friend( $vars );
1631 - ob_end_clean();
2444 + if ( ! empty( $_POST['notification_keywords'] ) && is_array( $_POST['notification_keywords'] ) ) {
2445 + $keywords = array();
2446 + foreach ( wp_unslash( $_POST['notification_keywords'] ) as $i => $keyword ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2447 + if ( trim( $keyword ) ) {
2448 + $keywords[] = array(
2449 + 'enabled' => isset( $_POST['notification_keywords_enabled'][ $i ] ) && boolval( $_POST['notification_keywords_enabled'][ $i ] ),
2450 + 'keyword' => sanitize_text_field( $keyword ),
2451 + );
2452 + }
2453 + }
2454 + update_option( 'friends_notification_keywords', $keywords );
2455 + }
1632 2456
1633 - if ( is_wp_error( $ret ) ) {
1634 - return $ret;
2457 + if ( isset( $_POST['keyword_notification_override'] ) && boolval( $_POST['keyword_notification_override'] ) ) {
2458 + delete_user_option( get_current_user_id(), 'friends_keyword_notification_override_disabled' );
2459 + } else {
2460 + update_user_option( get_current_user_id(), 'friends_keyword_notification_override_disabled', 1 );
1635 2461 }
1636 2462
1637 - $friend_user = User::get_by_username( $vars['user_login'] );
1638 - if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1639 - return new \WP_Error( 'friend_not_created', __( 'Friend could not be created.', 'friends' ) );
2463 + if ( isset( $_POST['new_post_notification'] ) && boolval( $_POST['new_post_notification'] ) ) {
2464 + delete_user_option( get_current_user_id(), 'friends_no_new_post_notification' );
2465 + } else {
2466 + update_user_option( get_current_user_id(), 'friends_no_new_post_notification', 1 );
1640 2467 }
1641 2468
1642 - return $friend_user->ID;
1643 - }
1644 -
1645 - /**
1646 - * Previous process the Add Friend form. Todo: re-integrate.
1647 - *
1648 - * @param User $friend_user The Friend user.
1649 - * @param array $vars The variables from the admin
1650 - * submission.
1651 - *
1652 - * @return boolean true when there was no error.
1653 - */
1654 - public function process_admin_add_friend_response( $friend_user, $vars ) {
1655 - if ( is_wp_error( $friend_user ) ) {
1656 - $this->display_errors( $friend_user );
1657 - return false;
2469 + if ( isset( $_POST['friend_follower_notification'] ) && boolval( $_POST['friend_follower_notification'] ) ) {
2470 + delete_user_option( get_current_user_id(), 'friends_no_friend_follower_notification' );
2471 + } else {
2472 + update_user_option( get_current_user_id(), 'friends_no_friend_follower_notification', 1 );
1658 2473 }
1659 2474
1660 - if ( ! $friend_user instanceof User ) {
1661 - ?>
1662 - <div id="message" class="updated notice is-dismissible"><p>
1663 - <?php
1664 - // translators: %s is a username.
1665 - esc_html_e( 'Unknown error', 'friends' );
1666 - ?>
1667 - </p></div>
1668 - <?php
1669 - return false;
2475 + foreach ( get_post_format_slugs() as $post_format ) {
2476 + if ( isset( $_POST[ 'new_post_format_notification_' . $post_format ] ) && boolval( $_POST[ 'new_post_format_notification_' . $post_format ] ) ) {
2477 + delete_user_option( get_current_user_id(), 'friends_no_new_post_format_notification_' . $post_format );
2478 + } else {
2479 + update_user_option( get_current_user_id(), 'friends_no_new_post_format_notification_' . $post_format, 1 );
2480 + }
1670 2481 }
1671 2482
1672 - $feed_options = array();
1673 - if ( ! isset( $vars['feeds'] ) ) {
1674 - $vars['feeds'] = array();
1675 - }
1676 - foreach ( $vars['feeds'] as $feed ) {
1677 - if ( isset( $feed['type'] ) ) {
1678 - $feed['mime-type'] = $feed['type'];
1679 - unset( $feed['type'] );
2483 + foreach ( array_keys( $this->friends->feed->get_registered_parsers() ) as $parser ) {
2484 + if ( isset( $_POST[ 'new_post_by_parser_notification_' . $parser ] ) && boolval( $_POST[ 'new_post_by_parser_notification_' . $parser ] ) ) {
2485 + delete_user_option( get_current_user_id(), 'friends_no_new_post_by_parser_notification_' . $parser );
2486 + } else {
2487 + update_user_option( get_current_user_id(), 'friends_no_new_post_by_parser_notification_' . $parser, 1 );
1680 2488 }
1681 - $feed_options[ $feed['url'] ] = $feed;
1682 2489 }
1683 2490
1684 - // Save the all feeds for possible later activation.
1685 - $friend_user->save_feeds( $feed_options );
1686 -
1687 - if ( ! isset( $vars['subscribe'] ) ) {
1688 - $vars['subscribe'] = array();
2491 + if ( empty( $_POST['friend_listed'] ) ) {
2492 + return;
1689 2493 }
2494 + // This is an array, it is checked before use below.
2495 + $friend_usernames = wp_unslash( $_POST['friend_listed'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2496 + $current_user_id = get_current_user_id();
2497 + $hide_from_friends_page = array();
1690 2498
1691 - $count = 0;
1692 - foreach ( $vars['subscribe'] as $feed_url ) {
1693 - if ( ! isset( $feed_options[ $feed_url ] ) ) {
2499 + foreach ( $friend_usernames as $friend_username ) {
2500 + $friend_user = User::get_by_username( $friend_username );
2501 + if ( ! $friend_user ) {
1694 2502 continue;
1695 2503 }
1696 - $new_feed = $friend_user->subscribe( $feed_url, $feed_options[ $feed_url ] );
1697 - if ( ! is_wp_error( $new_feed ) ) {
1698 - do_action( 'friends_user_feed_activated', $new_feed );
1699 - $count += 1;
2504 + $friend_username = $friend_user->user_login;
2505 + if ( ! isset( $_POST['show_on_friends_page'][ $friend_username ] ) ) {
2506 + $hide_from_friends_page[] = $friend_username;
1700 2507 }
1701 - }
1702 2508
1703 - add_filter( 'notify_about_new_friend_post', '__return_false', 999 );
2509 + $no_new_post_notification = ! isset( $_POST['new_friend_post_notification'][ $friend_username ] ) || '0' === $_POST['new_friend_post_notification'][ $friend_username ];
2510 + if ( get_user_option( 'friends_no_new_post_notification_' . $friend_username ) !== $no_new_post_notification ) {
2511 + update_user_option( $current_user_id, 'friends_no_new_post_notification_' . $friend_username, $no_new_post_notification );
2512 + }
1704 2513
1705 - wp_schedule_single_event( time(), 'friends_retrieve_user_feeds', array( $friend_user->ID ) );
1706 -
1707 - if ( isset( $vars['errors'] ) ) {
1708 - $this->display_errors( $vars['errors'] );
2514 + $no_keyword_notification = ! isset( $_POST['keyword_notification'][ $friend_username ] );
2515 + if ( get_user_option( 'friends_no_keyword_notification_' . $friend_username ) !== $no_keyword_notification ) {
2516 + update_user_option( $current_user_id, 'friends_no_keyword_notification_' . $friend_username, $no_keyword_notification );
2517 + }
1709 2518 }
1710 2519
1711 - $friend_link = '<a href="' . esc_url( $this->admin_edit_user_link( $friend_user->get_local_friends_page_url(), $friend_user ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $friend_user->display_name ) . '</a>';
1712 - $message = false;
2520 + update_user_option( $current_user_id, 'friends_hide_from_friends_page', $hide_from_friends_page );
1713 2521
1714 - if ( $friend_user->has_cap( 'pending_friend_request' ) ) {
1715 - // translators: %s is a Site URL.
1716 - $message = sprintf( __( 'Friendship requested for site %s.', 'friends' ), $friend_link );
1717 - $message .= ' ' . sprintf( __( 'Until they respond, we have already subscribed you to their updates.', 'friends' ), $friend_link );
1718 - } elseif ( $friend_user->has_cap( 'friend' ) ) {
1719 - // translators: %s is a Site URL.
1720 - $message = sprintf( __( "You're now a friend of site %s.", 'friends' ), $friend_link );
1721 - // translators: %s is the friends page URL.
1722 - } elseif ( $friend_user->has_cap( 'subscription' ) ) {
1723 - if ( isset( $vars['friendship'] ) ) {
1724 - // translators: %s is a Site URL.
1725 - $message = sprintf( __( 'No friends plugin installed at %s.', 'friends' ), $friend_link );
1726 - $message .= ' ' . esc_html__( 'We subscribed you to their updates.', 'friends' );
1727 - } else {
1728 - // translators: %s is a Site URL.
1729 - $message = sprintf( __( "You're now subscribed to %s.", 'friends' ), $friend_link );
1730 - }
1731 - }
2522 + do_action( 'friends_notification_manager_after_form_submit', $friend_usernames );
1732 2523
1733 - if ( $message ) {
1734 - ?>
1735 - <div id="message" class="updated notice is-dismissible"><p>
1736 - <?php
1737 - echo wp_kses( $message, array( 'a' => array( 'href' => array() ) ) );
1738 - // translators: %s is the friends page URL.
1739 - echo ' ', wp_kses( sprintf( __( 'Go to your <a href=%s>friends page</a> to view their posts.', 'friends' ), '"' . esc_url( $friend_user->get_local_friends_page_url() ) . '"' ), array( 'a' => array( 'href' => array() ) ) );
1740 - echo ' <span id="fetch-feeds" data-nonce="', esc_attr( wp_create_nonce( 'fetch-feeds-' . $friend_user->user_login ) ), '" data-friend=', esc_attr( $friend_user->user_login ), '>', __( 'Fetching feeds...', 'friends' ), '</span>';
1741 - ?>
1742 - </p></div>
1743 - <?php
1744 - return true;
2524 + if ( isset( $_GET['_wp_http_referer'] ) ) {
2525 + wp_safe_redirect( wp_get_referer() );
2526 + } else {
2527 + wp_safe_redirect( add_query_arg( 'updated', '1', remove_query_arg( array( '_wp_http_referer', '_wpnonce' ) ) ) );
1745 2528 }
1746 -
1747 - ?>
1748 - <div id="message" class="updated notice is-dismissible"><p>
1749 - <?php
1750 - // translators: %s is a username.
1751 - echo esc_html( sprintf( __( 'User %s could not be assigned the appropriate role.', 'friends' ), $friend_user->display_name ) );
1752 - ?>
1753 - </p></div>
1754 - <?php
1755 - return false;
2529 + exit;
1756 2530 }
1757 2531
1758 2532 /**
1759 - * Process the Add Friend form.
1760 - *
1761 - * @param array $vars The POST or GET variables.
1762 - *
1763 - * @return boolean A \WP_Error or void.
2533 + * Render the admin notification manager.
1764 2534 */
1765 - public function process_admin_add_friend( $vars ) {
1766 - $errors = new \WP_Error();
1767 - $args = array();
2535 + public function render_admin_notification_manager() {
2536 + Friends::template_loader()->get_template_part(
2537 + 'admin/settings-header',
2538 + null,
2539 + array(
2540 + 'active' => 'friends-notification-manager',
2541 + 'title' => __( 'Friends', 'friends' ),
2542 + )
2543 + );
2544 + $this->check_admin_settings();
1768 2545
1769 - $friend_url = isset( $vars['friend_url'] ) ? trim( $vars['friend_url'] ) : '';
1770 - $codeword = isset( $vars['codeword'] ) ? trim( $vars['codeword'] ) : '';
1771 - $message = isset( $vars['message'] ) ? trim( $vars['message'] ) : '';
2546 + $friend_users = User_Query::all_subscriptions();
1772 2547
1773 - $friends_plugin = false;
1774 - $friend_user = false;
1775 -
1776 - $protocol = wp_parse_url( $friend_url, PHP_URL_SCHEME );
1777 - if ( ! $protocol ) {
1778 - // Allow adding a friend by username.
1779 - if ( is_multisite() ) {
1780 - $friend_user = get_user_by( 'login', $friend_url );
1781 - if ( $friend_user ) {
1782 - $site = get_active_blog_for_user( $friend_user->ID );
1783 - // Ensure we're using the same URL protocol.
1784 - $friend_url = set_url_scheme( $site->siteurl );
1785 - }
1786 - }
1787 -
1788 - // If unsuccessful, then the protocol was forgotten.
1789 - if ( ! $friend_user ) {
1790 - $friend_url = apply_filters( 'friends_rewrite_incoming_url', 'https://' . $friend_url, $friend_url );
1791 - }
2548 + $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
2549 + if ( ! $hide_from_friends_page ) {
2550 + $hide_from_friends_page = array();
1792 2551 }
1793 - $friend_user_login = apply_filters( 'friends_suggest_user_login', User::get_user_login_for_url( $friend_url ), $friend_url );
1794 - $friend_display_name = apply_filters( 'friends_suggest_display_name', User::get_display_name_for_url( $friend_url ), $friend_url );
1795 2552
1796 - $friend_user = get_user_by( 'login', $friend_user_login );
2553 + $args = array(
2554 + 'friend_users' => $friend_users->get_results(),
2555 + 'friends_settings_url' => add_query_arg( '_wp_http_referer', remove_query_arg( '_wp_http_referer' ), self_admin_url( 'admin.php?page=friends-settings' ) ),
2556 + 'hide_from_friends_page' => $hide_from_friends_page,
2557 + 'keyword_override_disabled' => get_user_option( 'friends_keyword_notification_override_disabled' ),
2558 + 'no_new_post_notification' => get_user_option( 'friends_no_new_post_notification' ),
2559 + 'no_keyword_notification' => get_user_option( 'friends_no_keyword_notification' ),
2560 + 'notification_keywords' => Feed::get_all_notification_keywords(),
2561 + 'active_keywords' => Feed::get_active_notification_keywords(),
2562 + 'feed_parsers' => $this->friends->feed->get_registered_parsers(),
2563 + );
1797 2564
1798 - if ( $friend_user ) {
1799 - $args['friends_multisite_user_login'] = $friend_user_login;
1800 - $args['friends_multisite_display_name'] = $friend_display_name;
2565 + if ( class_exists( '\Activitypub\Notification' ) ) {
2566 + $args['no_friend_follower_notification'] = get_user_option( 'friends_no_friend_follower_notification' );
1801 2567 }
1802 - $rest_url = false;
1803 2568
1804 - if ( ( isset( $vars['step2'] ) && isset( $vars['feeds'] ) && is_array( $vars['feeds'] ) ) || isset( $vars['step3'] ) ) {
1805 - $friend_user_login = str_replace( ' ', '-', sanitize_user( $vars['user_login'] ) );
1806 - $friend_display_name = sanitize_text_field( $vars['display_name'] );
1807 - if ( ! $friend_user_login ) {
1808 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1809 - $errors->add( 'user_login', __( '<strong>Error</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
1810 - } elseif ( ! is_multisite() && username_exists( $friend_user_login ) ) {
1811 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1812 - $errors->add( 'user_login', __( '<strong>Error</strong>: This username is already registered. Please choose another one.' ) );
1813 - }
2569 + Friends::template_loader()->get_template_part(
2570 + 'admin/notification-manager',
2571 + null,
2572 + $args
2573 + );
1814 2574
1815 - $feeds = $vars['feeds'];
1816 - if ( ! $errors->has_errors() ) {
1817 - $friend_user = false;
1818 - if ( isset( $vars['friendship'] ) ) {
1819 - $friend_user = $this->send_friend_request( $vars['friendship'], $friend_user_login, $friend_url, $friend_display_name, $codeword, $message );
1820 - if ( $friend_user->has_errors() ) {
1821 - $vars['errors'] = $friend_user;
1822 - }
1823 - }
2575 + Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2576 + }
1824 2577
1825 - $avatar = null;
1826 - $description = null;
1827 - foreach ( $feeds as $feed_details ) {
1828 - if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
1829 - $avatar = $feed_details['avatar'];
1830 - }
1831 - if ( ! $description && ! empty( $feed_details['description'] ) ) {
1832 - $description = $feed_details['description'];
1833 - }
1834 - }
2578 + public function render_admin_import_export() {
2579 + Friends::template_loader()->get_template_part(
2580 + 'admin/settings-header',
2581 + null,
2582 + array(
2583 + 'active' => 'friends-import-export',
2584 + 'title' => __( 'Friends', 'friends' ),
2585 + )
2586 + );
2587 + $this->check_admin_settings();
1835 2588
1836 - if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1837 - $friend_user = User::create( $friend_user_login, 'subscription', $friend_url, $friend_display_name, $avatar, $description );
1838 - }
2589 + ?>
2590 + <h1><?php esc_html_e( 'Import/Export', 'friends' ); ?></h1>
2591 + <?php
1839 2592
1840 - return $this->process_admin_add_friend_response( $friend_user, $vars );
1841 - }
2593 + Friends::template_loader()->get_template_part(
2594 + 'admin/import-export',
2595 + null,
2596 + array(
2597 + 'private_rss_key' => get_option( 'friends_private_rss_key' ),
2598 + )
2599 + );
1842 2600
1843 - if ( isset( $vars['friendship'] ) ) {
1844 - $rest_url = $vars['friendship'];
1845 - } else {
1846 - $rest_url = $this->friends->rest->get_friends_rest_url( $feeds );
1847 - }
1848 - } else {
1849 - if ( home_url() === trailingslashit( $friend_url ) ) {
1850 - return new \WP_Error( 'friend-yourself', __( 'It seems like you sent a friend request to yourself.', 'friends' ) );
1851 - }
2601 + Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2602 + }
1852 2603
1853 - $friend_user = User::get_user( $friend_user_login );
1854 - if ( $friend_user && ! is_wp_error( $friend_user ) ) {
1855 - if ( $friend_user->is_valid_friend() ) {
1856 - return new \WP_Error( 'already-friend', __( 'You are already friends with this site.', 'friends' ) );
1857 - }
2604 + public function process_admin_import_export() {
2605 + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends-settings' ) ) {
2606 + return;
2607 + }
1858 2608
1859 - // translators: %s is the name of a friend / site.
1860 - return new \WP_Error( 'already-subscribed', sprintf( __( 'You are already subscribed to this site: %s', 'friends' ), '<a href="' . esc_url( $this->admin_edit_user_link( $friend_user->get_local_friends_page_url(), $friend_user ) ) . '">' . esc_html( $friend_user->display_name ) . '</a>' ) );
1861 - }
2609 + if ( ! Friends::has_required_privileges() ) {
2610 + return;
2611 + }
1862 2612
1863 - $feeds = $this->friends->feed->discover_available_feeds( $friend_url );
1864 - if ( is_wp_error( $feeds ) ) {
1865 - return $feeds;
2613 + if ( isset( $_FILES['opml']['tmp_name'] ) ) {
2614 + $opml = file_get_contents( $_FILES['opml']['tmp_name'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
2615 + $feeds = Import::opml( $opml );
2616 + $users_created = count( $feeds );
2617 + $feeds_imported = 0;
2618 + foreach ( $feeds as $user => $user_feeds ) {
2619 + $feeds_imported += count( $user_feeds );
1866 2620 }
1867 - if ( ! $feeds ) {
1868 - return new \WP_Error( 'no-feed-found', __( 'No suitable feed was found at the provided address.', 'friends' ) );
1869 - }
2621 + ?>
2622 + <div class="friends-notice notice notice-success is-dismissible">
2623 + <p>
2624 + <?php
2625 + echo esc_html(
2626 + sprintf(
2627 + // translators: %d is the number of users imported.
2628 + _n( 'Imported %d user.', 'Imported %d users.', $users_created, 'friends' ),
2629 + $users_created
2630 + )
2631 + );
2632 + ?>
2633 + <?php
2634 + echo esc_html(
2635 + sprintf(
2636 + // translators: %d is the number of feeds imported.
2637 + _n( 'They had %d feed.', 'They had %d feeds.', $feeds_imported, 'friends' ),
2638 + $feeds_imported
2639 + )
2640 + );
2641 + ?>
2642 + </p>
2643 + </div>
2644 + <?php
2645 + }
2646 + }
1870 2647
1871 - $better_display_name = User::get_display_name_from_feeds( $feeds );
1872 - if ( $better_display_name ) {
1873 - $friend_display_name = $better_display_name;
1874 - }
2648 + public function process_admin_duplicate_remover() {
2649 + $friend = $this->check_admin_duplicate_remover();
1875 2650
1876 - $rest_url = $this->friends->rest->get_friends_rest_url( $feeds );
1877 - }
2651 + // Nonce verification done in check_admin_duplicate_remover.
2652 + // phpcs:disable WordPress.Security.NonceVerification.Missing
1878 2653
1879 - if ( $rest_url ) {
1880 - $friends_plugin = $rest_url;
1881 - unset( $feeds[ $rest_url ] );
2654 + // We iterate over this array and then we sanitize _id.
2655 + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2656 + if ( empty( $_POST['deleteduplicate'] ) || ! is_array( $_POST['deleteduplicate'] ) ) {
2657 + return;
1882 2658 }
1883 2659
1884 - if ( isset( $vars['quick-subscribe'] ) ) {
1885 - $vars['feeds'] = $feeds;
1886 - $vars['subscribe'] = array();
1887 - foreach ( $feeds as $feed_url => $details ) {
1888 - if ( isset( $details['autoselect'] ) && $details['autoselect'] ) {
1889 - $vars['subscribe'][] = $feed_url;
1890 - }
2660 + $deleted = 0;
2661 + foreach ( array_keys( wp_unslash( $_POST['deleteduplicate'] ) ) as $_id ) {
2662 + if ( ! is_numeric( $_id ) ) {
2663 + continue;
1891 2664 }
1892 2665
1893 - $friend_user = false;
1894 - if ( isset( $rest_url ) ) {
1895 - $friend_user = $this->send_friend_request( $rest_url, $friend_user_login, $friend_url, $friend_display_name, $codeword, $message );
2666 + if ( wp_delete_post( intval( $_id ) ) ) {
2667 + ++$deleted;
1896 2668 }
2669 + }
2670 + // phpcs:enable WordPress.Security.NonceVerification.Missing
2671 + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1897 2672
1898 - $avatar = null;
1899 - $description = null;
1900 - foreach ( $feeds as $feed_details ) {
1901 - if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
1902 - $avatar = $feed_details['avatar'];
1903 - }
1904 - if ( ! $description && ! empty( $feed_details['description'] ) ) {
1905 - $description = $feed_details['description'];
1906 - }
1907 - }
2673 + if ( $deleted ) {
2674 + wp_safe_redirect( add_query_arg( 'deleted', $deleted ) );
2675 + exit;
2676 + }
2677 + }
2678 + public function check_admin_duplicate_remover() {
2679 + if ( ! Friends::is_main_user() ) {
2680 + wp_die( esc_html__( 'Sorry, you are not allowed to edit the rules.', 'friends' ) );
2681 + }
1908 2682
1909 - if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1910 - $friend_user = User::create( $friend_user_login, 'subscription', $friend_url, $friend_display_name, $avatar, $description );
1911 - }
2683 + if ( ! isset( $_GET['user'] ) ) {
2684 + wp_die( esc_html__( 'Invalid user.', 'friends' ) );
2685 + }
1912 2686
1913 - return $this->process_admin_add_friend_response( $friend_user, $vars );
2687 + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'duplicate-remover-' . sanitize_user( wp_unslash( $_GET['user'] ) ) ) ) {
2688 + wp_die( esc_html__( 'Invalid nonce.', 'friends' ) );
1914 2689 }
1915 2690
1916 - Friends::template_loader()->get_template_part(
1917 - 'admin/settings-header',
1918 - null,
1919 - array(
1920 - 'active' => 'add-friend-confirm',
1921 - 'title' => __( 'Add New Friend', 'friends' ),
1922 - 'menu' => array(
1923 - '1. ' . __( 'Enter Details', 'friends' ) => 'add-friend' . ( isset( $friend_url ) ? '&url=' . urlencode( $friend_url ) : '' ),
1924 - '2. ' . __( 'Confirm', 'friends' ) => 'add-friend-confirm',
1925 - ),
1926 - )
1927 - );
2691 + $friend = User::get_by_username( sanitize_user( wp_unslash( $_GET['user'] ) ) );
2692 + if ( ! $friend || is_wp_error( $friend ) ) {
2693 + wp_die( esc_html__( 'Invalid username.', 'friends' ) );
2694 + }
1928 2695
1929 - if ( $errors->has_errors() ) {
1930 - ?>
1931 - <div id="message" class="updated notice is-dismissible"><p><?php echo wp_kses( $errors->get_error_message(), array( 'strong' => array() ) ); ?></p>
1932 - </div>
1933 - <?php
2696 + if ( ! $friend->has_cap( 'subscription' ) ) {
2697 + wp_die( esc_html__( 'This is not a user related to this plugin.', 'friends' ) );
1934 2698 }
1935 2699
1936 - Friends::template_loader()->get_template_part(
1937 - 'admin/select-feeds',
1938 - null,
1939 - array_merge(
1940 - $args,
1941 - array(
1942 - 'friends_plugin' => $friends_plugin,
1943 - 'friend_url' => $friend_url,
1944 - 'friend_user_login' => $friend_user_login,
1945 - 'friend_display_name' => $friend_display_name,
1946 - 'friend_roles' => $this->get_friend_roles(),
1947 - 'default_role' => get_option( 'friends_default_friend_role', 'friend' ),
1948 - 'codeword' => $codeword,
1949 - 'message' => $message,
1950 - 'post_formats' => array_merge( array( 'autodetect' => __( 'Autodetect Post Format', 'friends' ) ), get_post_format_strings() ),
1951 - 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1952 - 'feeds' => $feeds,
1953 - )
1954 - )
1955 - );
2700 + return $friend;
1956 2701 }
1957 -
1958 2702 /**
1959 - * Render the admin form for sending a friend request.
2703 + * Render the duplicates remover
1960 2704 */
1961 - public function render_admin_add_friend() {
1962 - if ( ! friends::has_required_privileges() ) {
1963 - wp_die( esc_html__( 'Sorry, you are not allowed to add friends.', 'friends' ) );
1964 - }
2705 + public function render_admin_duplicate_remover() {
2706 + $friend = $this->check_admin_duplicate_remover();
1965 2707
1966 - if ( ! empty( $_GET['preview'] ) ) {
1967 - $url = $_GET['preview'];
1968 -
2708 + $this->header_edit_friend( $friend, 'duplicate-remover' );
2709 + // phpcs:disable WordPress.Security.NonceVerification
2710 + if ( isset( $_GET['deleted'] ) ) {
1969 2711 ?>
1970 - <h1>
2712 + <div id="message" class="updated notice is-dismissible"><p>
1971 2713 <?php
1972 - // translators: %s is a URL.
1973 - echo esc_html( sprintf( __( 'Preview for %s', 'friends' ), $url ) );
2714 + $deleted = intval( $_GET['deleted'] );
2715 + echo esc_html(
2716 + sprintf(
2717 + // translators: %d is the number of duplicates deleted.
2718 + _n( 'Deleted %d selected duplicate.', 'Deleted %d selected duplicates.', $deleted, 'friends' ),
2719 + $deleted
2720 + )
2721 + );
1974 2722 ?>
1975 - </h1>
2723 + </p></div>
1976 2724 <?php
2725 + }
2726 + // phpcs:enable WordPress.Security.NonceVerification
1977 2727
1978 - if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'preview-feed' ) ) {
1979 - ?>
1980 - <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'For security reasons, this preview is not available.', 'friends' ); ?></p>
1981 - </div>
1982 - <?php
1983 - exit;
1984 - }
2728 + $friend_posts = new \WP_Query();
1985 2729
1986 - $parser_name = $this->friends->feed->get_registered_parser( $_GET['parser'] );
1987 - if ( ! $parser_name ) {
1988 - ?>
1989 - <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'An unknown parser name was supplied.', 'friends' ); ?></p>
1990 - </div>
1991 - <?php
1992 - exit;
1993 - }
1994 - ?>
1995 - <h3><?php esc_html_e( 'Parser Details', 'friends' ); ?></h3>
1996 - <ul id="parser">
1997 - <li>
1998 - <?php
1999 - echo wp_kses(
2000 - // translators: %s is the name of a parser, e.g. simplepie.
2001 - sprintf( __( 'Parser: %s', 'friends' ), $parser_name ),
2002 - array(
2003 - 'a' => array(
2004 - 'href' => array(),
2005 - 'rel' => array(),
2006 - 'target' => array(),
2007 - ),
2008 - )
2009 - );
2010 - ?>
2011 - </li>
2012 - </ul>
2013 - <h3><?php esc_html_e( 'Items in the Feed', 'friends' ); ?></h3>
2730 + $friend_posts->set( 'post_type', Friends::CPT );
2731 + $friend_posts->set( 'post_status', array( 'publish', 'private', 'trash' ) );
2732 + $friend_posts->set( 'posts_per_page', 100 );
2733 + $friend_posts = $friend->modify_query_by_author( $friend_posts );
2014 2734
2015 - <?php
2016 -
2017 - $items = $this->friends->feed->preview( $_GET['parser'], $url, isset( $_GET['feed'] ) ? intval( $_GET['feed'] ) : null );
2018 - if ( is_wp_error( $items ) ) {
2019 - ?>
2020 - <div id="message" class="updated notice is-dismissible"><p><?php echo esc_html( $items->get_error_message() ); ?></p>
2021 - </div>
2022 - <?php
2023 - exit;
2735 + $uniques = array();
2736 + foreach ( $friend_posts->get_posts() as $_post ) {
2737 + $permalink = get_permalink( $_post );
2738 + if ( ! isset( $uniques[ $permalink ] ) ) {
2739 + $uniques[ $permalink ] = $_post->ID;
2024 2740 }
2025 - ?>
2026 -
2027 - <ul>
2028 - <?php
2029 - foreach ( $items as $item ) {
2030 - $title = $item->title;
2031 - if ( 'status' === $item->post_format ) {
2032 - $title = strip_tags( $item->content );
2033 - }
2034 - ?>
2035 - <li><a href="<?php echo esc_url( $item->permalink ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $item->date ); ?></a> (author: <?php echo esc_html( $item->author ); ?>, type: <?php echo esc_html( $item->post_format ); ?>):
2036 - <?php if ( $title ) : ?>
2037 - <a href="<?php echo esc_url( $item->permalink ); ?>" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $title ); ?></a> <?php echo esc_html( str_word_count( wp_strip_all_tags( $item->content ) ) ); ?> words
2038 - <?php else : ?>
2039 - <p>
2040 - <?php
2041 - echo wp_kses(
2042 - wp_trim_excerpt( $item->content ),
2043 - array(
2044 - 'a' => array( 'href' => array() ),
2045 - 'img' => array( 'src' => array() ),
2046 - )
2047 - );
2048 - ?>
2049 - </p>
2050 - <?php endif; ?>
2051 - </li>
2052 - <?php
2053 - }
2054 - ?>
2055 - </ul>
2056 - <?php
2057 - return;
2058 2741 }
2059 2742
2060 - if ( apply_filters( 'friends_debug', false ) && isset( $_GET['next'] ) ) {
2061 - $_POST = $_REQUEST;
2062 - $_POST['_wpnonce'] = wp_create_nonce( 'add-friend' );
2063 - if ( ! empty( $_POST['url'] ) && ! isset( $_POST['friend_url'] ) ) {
2064 - $_POST['friend_url'] = $_POST['url'];
2065 - $parsed_url = parse_url( $_POST['friend_url'] );
2066 - if ( isset( $parsed_url['host'] ) ) {
2067 - if ( ! isset( $parsed_url['scheme'] ) ) {
2068 - $_POST['friend_url'] = 'https://' . ltrim( $_POST['friend_url'], '/' );
2069 - }
2070 - }
2071 - }
2072 - }
2073 -
2074 - $response = null;
2075 - $postdata = apply_filters( 'friends_add_friend_postdata', $_POST );
2076 - if ( ! empty( $postdata ) ) {
2077 - if ( ! wp_verify_nonce( $postdata['_wpnonce'], 'add-friend' ) ) {
2078 - $response = new \WP_Error( 'invalid-nonce', __( 'For security reasons, please verify the URL and click next if you want to proceed.', 'friends' ) );
2079 - } else {
2080 - $response = $this->process_admin_add_friend( $postdata );
2081 - }
2082 - if ( is_wp_error( $response ) ) {
2083 - ?>
2084 - <div id="message" class="updated notice is-dismissible"><p>
2085 - <?php
2086 - $message = $response->get_error_message();
2087 - if ( $response->get_error_data() ) {
2088 - $message .= ' (' . $response->get_error_data() . ')';
2089 - }
2090 - echo wp_kses(
2091 - $message,
2092 - array(
2093 - 'strong' => array(),
2094 - 'a' => array(
2095 - 'href' => array(),
2096 - 'rel' => array(),
2097 - 'target' => array(),
2098 - ),
2099 - )
2100 - );
2101 - ?>
2102 - </p>
2103 - </div>
2104 - <?php
2105 - }
2106 - if ( is_null( $response ) ) {
2107 - return;
2108 - }
2109 - }
2110 -
2111 2743 $args = array(
2112 - 'friend_url' => '',
2113 - 'add-friends-placeholder' => apply_filters( 'friends_add_friends_input_placeholder', __( 'Enter URL', 'friends' ) ),
2744 + 'friend' => $friend,
2745 + 'friend_posts' => $friend_posts,
2746 + 'uniques' => array_flip( $uniques ),
2747 + 'feed' => $this->friends->feed,
2114 2748 );
2115 2749
2116 - if ( ! empty( $_GET['url'] ) || ! empty( $_POST['url'] ) ) {
2117 - $friend_url = isset( $_GET['url'] ) ? $_GET['url'] : $_POST['url'];
2118 - $parsed_url = parse_url( $friend_url );
2119 - if ( isset( $parsed_url['host'] ) ) {
2120 - if ( ! isset( $parsed_url['scheme'] ) ) {
2121 - $args['friend_url'] = apply_filters( 'friends_rewrite_incoming_url', 'https://' . ltrim( $friend_url, '/' ), $friend_url, $parsed_url );
2122 - } else {
2123 - $args['friend_url'] = $friend_url;
2124 - }
2125 - } elseif ( preg_match( '/^@?' . Feed_Parser_ActivityPub::ACTIVITYPUB_USERNAME_REGEXP . '$/i', $friend_url ) ) {
2126 - $args['friend_url'] = $friend_url;
2127 - }
2128 - }
2750 + Friends::template_loader()->get_template_part( 'admin/duplicates', null, $args );
2751 + }
2129 2752
2130 - Friends::template_loader()->get_template_part(
2131 - 'admin/settings-header',
2132 - null,
2133 - array(
2134 - 'active' => 'add-friend',
2135 - 'title' => __( 'Add New Friend', 'friends' ),
2136 - 'menu' => array(
2137 - '1. ' . __( 'Enter Details', 'friends' ) => 'add-friend' . ( isset( $friend_url ) ? '&url=' . urlencode( $friend_url ) : '' ),
2138 - '2. ' . __( 'Confirm', 'friends' ) => false,
2139 - ),
2140 - )
2141 - );
2142 2753
2143 - Friends::template_loader()->get_template_part( 'admin/add-friend', null, $args );
2754 + public static function get_browser_api_key_user( $key ) {
2755 + $key = (string) $key;
2756 + if ( ! $key ) {
2757 + return false;
2758 + }
2144 2759
2145 - Friends::template_loader()->get_template_part(
2146 - 'admin/latest-friends',
2147 - null,
2148 - array(
2149 - 'friend_requests' => User_Query::recent_friends_subscriptions( 25 )->get_results(),
2150 - )
2151 - );
2152 - Friends::template_loader()->get_template_part( 'admin/settings-footer', null, $args );
2153 - }
2760 + $parts = explode( '-', $key, 3 );
2761 + if ( 3 !== count( $parts ) ) {
2762 + return false;
2763 + }
2154 2764
2155 - /**
2156 - * Process the admin notification manager form submission.
2157 - */
2158 - public function process_admin_notification_manager() {
2765 + $user_id = (int) $parts[1];
2766 + if ( ! $user_id ) {
2767 + return false;
2768 + }
2159 2769
2160 - if ( empty( $_POST ) || empty( $_POST['friend_listed'] ) ) {
2161 - return;
2770 + $desired_key = get_user_option( 'friends_browser_api_key', $user_id );
2771 + if ( ! $desired_key || ! hash_equals( (string) $desired_key, (string) $key ) ) {
2772 + return false;
2162 2773 }
2163 2774
2164 - if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'notification-manager' ) ) {
2165 - return;
2775 + $user = get_user_by( 'ID', $user_id );
2776 + if ( ! $user ) {
2777 + return false;
2166 2778 }
2167 2779
2168 - $this->check_admin_settings();
2169 - $friend_ids = $_POST['friend_listed'];
2170 - $current_user_id = get_current_user_id();
2171 - $hide_from_friends_page = array();
2780 + return $user;
2781 + }
2172 2782
2173 - foreach ( $friend_ids as $friend_id ) {
2174 - if ( ! isset( $_POST['show_on_friends_page'][ $friend_id ] ) ) {
2175 - $hide_from_friends_page[] = $friend_id;
2176 - }
2783 + public static function check_browser_api_key( $key ) {
2784 + return false !== self::get_browser_api_key_user( $key );
2785 + }
2177 2786
2178 - $no_new_post_notification = ! isset( $_POST['new_post_notification'][ $friend_id ] );
2179 - if ( get_user_option( 'friends_no_new_post_notification_' . $friend_id ) !== $no_new_post_notification ) {
2180 - update_user_option( $current_user_id, 'friends_no_new_post_notification_' . $friend_id, $no_new_post_notification );
2181 - }
2182 -
2183 - $no_keyword_notification = ! isset( $_POST['keyword_notification'][ $friend_id ] );
2184 - if ( get_user_option( 'friends_no_keyword_notification_' . $friend_id ) !== $no_keyword_notification ) {
2185 - update_user_option( $current_user_id, 'friends_no_keyword_notification_' . $friend_id, $no_keyword_notification );
2186 - }
2787 + public static function revoke_browser_api_key( $user_id = false ) {
2788 + if ( ! $user_id ) {
2789 + $user_id = get_current_user_id();
2187 2790 }
2188 2791
2189 - update_user_option( $current_user_id, 'friends_hide_from_friends_page', $hide_from_friends_page );
2792 + delete_user_option( $user_id, 'friends_browser_api_key' );
2793 + }
2190 2794
2191 - do_action( 'friends_notification_manager_after_form_submit', $friend_ids );
2795 + public static function get_browser_api_key( $user_id = false ) {
2796 + if ( ! $user_id ) {
2797 + $user_id = get_current_user_id();
2798 + }
2192 2799
2193 - if ( isset( $_GET['_wp_http_referer'] ) ) {
2194 - wp_safe_redirect( wp_get_referer() );
2195 - } else {
2196 - wp_safe_redirect( add_query_arg( 'updated', '1', remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
2800 + $key = get_user_option( 'friends_browser_api_key', $user_id );
2801 + if ( ! $key ) {
2802 + $key = 'friends-' . $user_id . '-' . wp_generate_password( 32, false );
2803 + update_user_option( $user_id, 'friends_browser_api_key', $key );
2197 2804 }
2198 - exit;
2199 2805
2806 + return $key;
2200 2807 }
2201 2808
2202 - /**
2203 - * Render the admin notification manager.
2204 - */
2205 - public function render_admin_notification_manager() {
2809 + public function render_browser_extension() {
2810 + add_filter(
2811 + 'friends_admin_tabs',
2812 + function ( $menu ) {
2813 + $menu[ __( 'Browser Extension', 'friends' ) ] = 'friends-browser-extension';
2814 + return $menu;
2815 + }
2816 + );
2206 2817 Friends::template_loader()->get_template_part(
2207 2818 'admin/settings-header',
2208 2819 null,
2209 2820 array(
2210 - 'active' => 'friends-notification-manager',
2211 - 'title' => __( 'Friends', 'friends' ),
2821 + 'active' => 'friends-browser-extension',
2212 2822 )
2213 2823 );
2214 2824 $this->check_admin_settings();
2825 + $browser_api_key = self::get_browser_api_key();
2215 2826
2216 - ?>
2217 - <h1><?php esc_html_e( 'Notification Manager', 'friends' ); ?></h1>
2218 - <?php
2827 + if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'friends-browser-extension' ) ) {
2828 + if ( isset( $_POST['revoke-api-key'] ) ) {
2829 + self::revoke_browser_api_key();
2830 + $browser_api_key = self::get_browser_api_key();
2831 + }
2832 + }
2219 2833
2220 - $friend_users = new User_Query(
2834 + Friends::template_loader()->get_template_part(
2835 + 'admin/browser-extension',
2836 + null,
2221 2837 array(
2222 - 'role__in' => array( 'friend', 'acquaintance', 'pending_friend_request', 'friend_request', 'subscription' ),
2223 - 'orderby' => 'display_name',
2224 - 'order' => 'ASC',
2838 + 'browser-api-key' => $browser_api_key,
2225 2839 )
2226 2840 );
2227 2841
2228 - $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
2229 - if ( ! $hide_from_friends_page ) {
2230 - $hide_from_friends_page = array();
2231 - }
2842 + Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2843 + }
2232 2844
2845 + public function render_friends_logs() {
2846 + add_filter(
2847 + 'friends_admin_tabs',
2848 + function ( $menu ) {
2849 + $menu[ __( 'Logs', 'friends' ) ] = 'friends-logs';
2850 + return $menu;
2851 + }
2852 + );
2853 +
2233 2854 Friends::template_loader()->get_template_part(
2234 - 'admin/notification-manager',
2855 + 'admin/settings-header',
2235 2856 null,
2236 2857 array(
2237 - 'friend_users' => $friend_users->get_results(),
2238 - 'friends_settings_url' => add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=friends-settings' ) ),
2239 - 'hide_from_friends_page' => $hide_from_friends_page,
2240 - 'no_new_post_notification' => get_user_option( 'friends_no_new_post_notification' ),
2241 - 'no_keyword_notification' => get_user_option( 'friends_no_keyword_notification' ),
2242 - 'active_keywords' => Feed::get_active_notification_keywords(),
2858 + 'active' => 'friends-logs',
2243 2859 )
2244 2860 );
2861 + $this->check_admin_settings();
2245 2862
2863 + Friends::template_loader()->get_template_part(
2864 + 'admin/logs',
2865 + null,
2866 + array(
2867 + 'logs' => Logging::get_logs(),
2868 + )
2869 + );
2870 +
2246 2871 Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2247 2872 }
2248 2873
2249 2874 /**
2250 - * Gets the friend roles.
2251 - *
2252 - * @return array The friend roles.
2253 - */
2254 - public function get_friend_roles() {
2255 - $roles = new \WP_Roles;
2256 - $friend_roles = array();
2257 - foreach ( $roles->roles as $role => $data ) {
2258 - if ( isset( $data['capabilities']['friend'] ) ) {
2259 - $friend_roles[ $role ] = $data['name'];
2260 - }
2261 - }
2262 - return $friend_roles;
2263 - }
2264 -
2265 - /**
2266 2875 * Gets the roles associated with the Friends plugin.
2267 2876 *
2268 2877 * @return array The associated roles.
2269 2878 */
2270 2879 public static function get_associated_roles() {
2271 - $roles = new \WP_Roles;
2880 + $roles = new \WP_Roles();
2272 2881 $friend_roles = array();
2273 2882 foreach ( $roles->roles as $role => $data ) {
2274 2883 if ( isset( $data['capabilities']['friends_plugin'] ) ) {
2275 2884 $friend_roles[ $role ] = $data['name'];
@@ -2278,188 +2887,12 @@
2278 2887 return $friend_roles;
2279 2888 }
2280 2889
2281 2890 public static function get_users_url() {
2282 - return 'admin.php?page=friends-list';
2891 + return home_url( '/friends/following/' );
2283 2892 }
2284 2893
2285 2894 /**
2286 - * Add actions to the user rows
2287 - *
2288 - * @param array $actions The existing actions.
2289 - * @param \WP_User $user The user in question.
2290 - * @return array The extended actions.
2291 - */
2292 - public static function user_row_actions( array $actions, \WP_User $user ) {
2293 - if (
2294 - ! Friends::has_required_privileges() ||
2295 - (
2296 - ! $user->has_cap( 'friend_request' ) &&
2297 - ! $user->has_cap( 'pending_friend_request' ) &&
2298 - ! $user->has_cap( 'friend' ) &&
2299 - ! $user->has_cap( 'subscription' )
2300 - )
2301 - ) {
2302 - return $actions;
2303 - }
2304 -
2305 - if ( is_multisite() ) {
2306 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2307 - $actions = array_merge( array( 'edit' => '<a href="' . esc_url( self_admin_url( 'admin.php?page=edit-friend&user=' . $user->user_login ) ) . '">' . __( 'Edit' ) . '</a>' ), $actions );
2308 - }
2309 -
2310 - // Ensuire we have a friends user here.
2311 - $user = new User( $user );
2312 -
2313 - $actions['view'] = Frontend::get_link(
2314 - $user->user_url,
2315 - sprintf(
2316 - // translators: %s: Author’s display name.
2317 - __( 'Visit %s&#8217;s website' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2318 - $user->display_name
2319 - ),
2320 - array(),
2321 - $user
2322 - );
2323 - unset( $actions['resetpassword'] );
2324 -
2325 - if ( $user->has_cap( 'friend_request' ) ) {
2326 - $link = self_admin_url( wp_nonce_url( 'users.php?action=accept_friend_request&users[]=' . $user->ID ) );
2327 -
2328 - $actions['user_accept_friend_request'] = '<a href="' . esc_url( $link ) . '">' . __( 'Accept Friend Request', 'friends' ) . '</a>';
2329 - $message = get_user_option( 'friends_request_message', $user->ID );
2330 - $actions['friends friends_request_date'] = '<br/><span class="nonessential">' . esc_html(
2331 - sprintf(
2332 - // translators: %s is a date.
2333 - __( 'Requested on %s', 'friends' ),
2334 - date_i18n( __( 'F j, Y g:i a' ), strtotime( $user->user_registered ) ) // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2335 - )
2336 - ) . '</span>';
2337 - if ( $message ) {
2338 - // translators: %s is a message text.
2339 - $actions['friends friend_request_message'] = '<br/><span class="nonessential">' . esc_html( sprintf( __( 'Message: %s', 'friends' ), $message ) ) . '</span>';
2340 - }
2341 - }
2342 -
2343 - if ( $user->has_cap( 'pending_friend_request' ) || $user->has_cap( 'subscription' ) ) {
2344 - $link = wp_nonce_url( add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=edit-friend&user=' . $user->user_login ) ), 'add-friend-' . $user->user_login, 'add-friend' );
2345 - if ( $user->has_cap( 'pending_friend_request' ) ) {
2346 - $actions['user_friend_request'] = '<a href="' . esc_url( $link ) . '">' . __( 'Resend Friend Request', 'friends' ) . '</a>';
2347 - } elseif ( $user->has_cap( 'subscription' ) ) {
2348 - $actions['user_friend_request'] = '<a href="' . esc_url( $link ) . '">' . __( 'Send Friend Request', 'friends' ) . '</a>';
2349 - }
2350 - }
2351 -
2352 - return $actions;
2353 - }
2354 -
2355 - /**
2356 - * Handle bulk friend request approvals on the user page
2357 - *
2358 - * @param string $sendback The URL to send the user back to.
2359 - * @param string $action The requested action.
2360 - * @param array $users The selected users.
2361 - */
2362 - public function handle_bulk_friend_request_approval( $sendback, $action, $users ) {
2363 - if ( 'accept_friend_request' !== $action ) {
2364 - return $sendback;
2365 - }
2366 -
2367 - $accepted = 0;
2368 - foreach ( $users as $user_id ) {
2369 - $user = new User( $user_id );
2370 - if ( ! $user || is_wp_error( $user ) ) {
2371 - continue;
2372 - }
2373 -
2374 - if ( ! $user->has_cap( 'friend_request' ) ) {
2375 - continue;
2376 - }
2377 -
2378 - if ( $user->has_cap( 'friend' ) ) {
2379 - continue;
2380 - }
2381 -
2382 - $user->set_role( get_option( 'friends_default_friend_role', 'friend' ) );
2383 - $accepted++;
2384 - }
2385 -
2386 - if ( ! $sendback ) {
2387 - return array(
2388 - 'accepted' => $accepted,
2389 - );
2390 - }
2391 -
2392 - $sendback = add_query_arg( 'accepted', $accepted, $sendback );
2393 - $sendback = remove_query_arg( 'role', $sendback );
2394 - wp_safe_redirect( $sendback );
2395 - }
2396 -
2397 - /**
2398 - * Add options to the Bulk dropdown on the users page
2399 - *
2400 - * @param array $actions The existing bulk options.
2401 - * @return array The extended bulk options.
2402 - */
2403 - public function add_user_bulk_options( $actions ) {
2404 - $friends = User_Query::all_friend_requests();
2405 - $friends->get_results();
2406 -
2407 - if ( ! empty( $friends ) ) {
2408 - $actions['accept_friend_request'] = __( 'Accept Friend Request', 'friends' );
2409 - }
2410 -
2411 - $friends = User_Query::all_subscriptions();
2412 - $friends->get_results();
2413 -
2414 - if ( ! empty( $friends ) ) {
2415 - $actions['friend_request'] = __( 'Send Friend Request', 'friends' );
2416 - }
2417 -
2418 - return $actions;
2419 - }
2420 -
2421 - /**
2422 - * Add a column "Posts" (that emcompasses both user and friend posts.)
2423 - *
2424 - * @param array $columns The columns.
2425 - *
2426 - * @return array The columns extended by the friends_posts.
2427 - */
2428 - public function user_list_columns( $columns ) {
2429 - $columns['friends_posts'] = __( 'Friend Posts', 'friends' );
2430 - unset( $columns['email'] );
2431 - return $columns;
2432 - }
2433 -
2434 - /**
2435 - * Return the results for the friends_posts column.
2436 - *
2437 - * @param string $output Custom column output. Default empty.
2438 - * @param string $column_name Column name.
2439 - * @param int $user_id ID of the currently-listed user.
2440 - *
2441 - * @return string The column contents.
2442 - */
2443 - public static function user_list_custom_column( $output, $column_name, $user_id ) {
2444 - if ( 'friends_posts' !== $column_name ) {
2445 - return $output;
2446 - }
2447 - $numposts = count_user_posts( $user_id, apply_filters( 'friends_frontend_post_types', array( 'post' ) ) );
2448 - $user = User::get_user_by_id( $user_id );
2449 - return sprintf(
2450 - '<a href="%s" class="edit"><span aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
2451 - $user ? $user->get_local_friends_page_url() : "edit.php?author={$user_id}",
2452 - $numposts,
2453 - sprintf(
2454 - /* translators: %s: Number of posts. */
2455 - _n( '%s post', '%s posts', $numposts ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2456 - number_format_i18n( $numposts )
2457 - )
2458 - );
2459 - }
2460 -
2461 - /**
2462 2895 * Override the post title for specific post formats.
2463 2896 *
2464 2897 * @param string $title The title.
2465 2898 * @param int $post_id The post id.
@@ -2466,13 +2899,14 @@
2466 2899 *
2467 2900 * @return string The potentially overriden title.
2468 2901 */
2469 2902 public function override_post_format_title( $title, $post_id = null ) {
2470 - if ( empty( $title ) && is_admin() && function_exists( 'get_current_screen' ) ) {
2903 + if ( $post_id && empty( $title ) && is_admin() && function_exists( 'get_current_screen' ) ) {
2471 2904 $screen = get_current_screen();
2472 2905 if ( $screen && 'edit-post' === $screen->id ) {
2473 2906 if ( 'status' === get_post_format() ) {
2474 - return get_the_excerpt();
2907 + $post = get_post( $post_id );
2908 + return wp_trim_words( wp_strip_all_tags( $post->post_content ) );
2475 2909 }
2476 2910 }
2477 2911 }
2478 2912 return $title;
@@ -2478,41 +2912,8 @@
2478 2912 return $title;
2479 2913 }
2480 2914
2481 2915 /**
2482 - * Adds the friend requests to the unread count.
2483 - *
2484 - * @param int $unread The unread count.
2485 - *
2486 - * @return int Unread count + friend requests.
2487 - */
2488 - public function friends_unread_friend_request_count( $unread ) {
2489 - $friend_requests = User_Query::all_friend_requests();
2490 - return $unread + $friend_requests->get_total();
2491 - }
2492 -
2493 - /**
2494 - * Add open friend requests to the menu.
2495 - *
2496 - * @param \WP_Menu $wp_menu The wp menu.
2497 - * @param string $my_url My url.
2498 - */
2499 - public function friends_add_menu_open_friend_request( $wp_menu, $my_url ) {
2500 - $friend_request_count = $this->friends_unread_friend_request_count( 0 );
2501 - if ( $friend_request_count > 0 ) {
2502 - $wp_menu->add_menu(
2503 - array(
2504 - 'id' => 'open-friend-requests',
2505 - 'parent' => 'friends-menu',
2506 - // translators: %s is the number of open friend requests.
2507 - 'title' => esc_html( sprintf( _n( 'Review %s Friend Request', 'Review %s Friends Request', $friend_request_count, 'friends' ), $friend_request_count ) ),
2508 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-list-requests',
2509 - )
2510 - );
2511 - }
2512 - }
2513 -
2514 - /**
2515 2916 * Get the unread badge HTML
2516 2917 *
2517 2918 * @return string The unread badge HTML.
2518 2919 */
@@ -2537,165 +2938,54 @@
2537 2938 *
2538 2939 * @param \WP_Admin_Bar $wp_menu The admin bar to modify.
2539 2940 */
2540 2941 public function admin_bar_friends_menu( \WP_Admin_Bar $wp_menu ) {
2541 - $my_url = false;
2542 - $my_own_site = false;
2543 - $on_my_own_site = false;
2544 - $we_requested_friendship = false;
2545 - $they_requested_friendship = false;
2942 + if ( ! Friends::has_required_privileges() ) {
2943 + return;
2944 + }
2546 2945
2547 - if ( current_user_can( 'friend' ) ) {
2548 - $current_user = wp_get_current_user();
2549 - if ( ! $current_user->user_url ) {
2550 - return;
2551 - }
2946 + $my_url = home_url();
2947 + $my_admin_url = site_url();
2552 2948
2553 - $my_url = $current_user->user_url;
2554 - } elseif ( is_multisite() ) {
2555 - $site = get_active_blog_for_user( get_current_user_id() );
2556 - if ( ! $site ) {
2557 - // If we cannot find a site, we shouldn't show the admin bar entry.
2558 - return;
2559 - }
2949 + $unread = $this->get_unread_badge();
2560 2950
2561 - $my_url = set_url_scheme( $site->siteurl );
2562 - $my_own_site = $site;
2563 - $on_my_own_site = get_current_blog_id() === intval( $site->blog_id );
2564 - if ( is_user_member_of_blog( get_current_user_id(), get_current_blog_id() ) ) {
2565 - if ( current_user_can( 'pending_friend_request' ) ) {
2566 - $they_requested_friendship = true;
2567 - } elseif ( current_user_can( 'friend_request' ) ) {
2568 - $we_requested_friendship = true;
2569 - }
2570 - }
2571 - } elseif ( Friends::has_required_privileges() ) {
2572 - $my_url = home_url();
2573 - $on_my_own_site = true;
2574 - }
2575 -
2576 - if ( ! $on_my_own_site && $my_own_site ) {
2577 - switch_to_blog( $my_own_site->blog_id );
2578 - }
2579 -
2580 - $unread = '';
2581 - if ( $on_my_own_site ) {
2582 - $unread = $this->get_unread_badge();
2583 - }
2584 2951 $wp_menu->add_node(
2585 2952 array(
2586 2953 'id' => 'friends-menu',
2587 2954 'parent' => '',
2588 - 'title' => '<span class="ab-icon dashicons dashicons-groups"></span> <span class="ab-label">' . esc_html( __( 'Friends', 'friends' ) ) . $unread . '</span>',
2955 + 'title' => '<span class="ab-icon"></span> <span class="ab-label">' . esc_html( __( 'Friends', 'friends' ) ) . $unread . '</span>',
2589 2956 'href' => $my_url . '/friends/',
2590 2957 )
2591 2958 );
2592 2959
2593 - if ( $on_my_own_site ) {
2594 - do_action( 'friends_own_site_menu_top', $wp_menu, $my_url );
2595 - }
2960 + do_action( 'friends_own_site_menu_top', $wp_menu, $my_url, $my_admin_url );
2961 + do_action( 'friends_current_site_menu_top', $wp_menu, $my_url, $my_admin_url );
2596 2962
2597 - if ( ! $on_my_own_site && $my_own_site ) {
2598 - restore_current_blog();
2599 - }
2600 -
2601 - do_action( 'friends_current_site_menu_top', $wp_menu, $my_url );
2602 -
2603 2963 $wp_menu->add_menu(
2604 2964 array(
2605 2965 'id' => 'your-feed',
2606 2966 'parent' => 'friends-menu',
2607 - 'title' => esc_html__( 'My Friends Feed', 'friends' ),
2608 - 'href' => $my_url . '/friends/',
2967 + 'title' => esc_html__( 'Main Feed', 'friends' ),
2968 + 'href' => home_url( '/friends/' ),
2609 2969 )
2610 2970 );
2611 2971
2612 - if ( $they_requested_friendship ) {
2613 - $wp_menu->add_menu(
2614 - array(
2615 - 'id' => 'add-friend',
2616 - 'parent' => 'friends-menu',
2617 - 'title' => '<span style="border-left: 2px solid #d63638; padding-left: .5em">' . esc_html(
2618 - sprintf(
2619 - // translators: %s is a site title.
2620 - __( "Respond to %s's friend request", 'friends' ),
2621 - get_bloginfo( 'name' )
2622 - ) . '</span>'
2623 - ),
2624 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-list-requests',
2625 - )
2626 - );
2627 - }
2628 -
2629 - if ( $on_my_own_site ) {
2630 - $wp_menu->add_menu(
2631 - array(
2632 - 'id' => 'your-profile',
2633 - 'parent' => 'friends-menu',
2634 - 'title' => esc_html__( 'My Public Friends Profile', 'friends' ),
2635 - 'href' => $my_url . '/friends/?public',
2636 - )
2637 - );
2638 - $wp_menu->add_menu(
2639 - array(
2640 - 'id' => 'friends-requests',
2641 - 'parent' => 'friends-menu',
2642 - 'title' => esc_html__( 'My Friends & Requests', 'friends' ),
2643 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-list',
2644 - )
2645 - );
2646 - $wp_menu->add_menu(
2647 - array(
2648 - 'id' => 'friends',
2649 - 'parent' => 'friends-menu',
2650 - 'title' => esc_html__( 'Settings' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2651 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-settings',
2652 - )
2653 - );
2654 - } else {
2655 - if ( ! current_user_can( 'friend' ) ) {
2656 - if ( $we_requested_friendship ) {
2657 - $wp_menu->add_menu(
2658 - array(
2659 - 'id' => 'add-friend',
2660 - 'parent' => 'friends-menu',
2661 - 'title' => esc_html__( 'Friendship Already Requested', 'friends' ),
2662 - 'href' => $my_url . '/wp-admin/' . self::get_users_url(),
2663 - )
2664 - );
2665 - } elseif ( ! $they_requested_friendship ) {
2666 - $wp_menu->add_menu(
2667 - array(
2668 - 'id' => 'add-friend',
2669 - 'parent' => 'friends-menu',
2670 - 'title' => esc_html(
2671 - sprintf(
2672 - // translators: %s is a site title.
2673 - __( 'Add %s as a friend', 'friends' ),
2674 - get_bloginfo( 'name' )
2675 - )
2676 - ),
2677 - 'href' => $my_url . '/?add-friend=' . urlencode( home_url() ),
2678 - )
2679 - );
2680 - }
2681 - }
2682 -
2683 - $wp_menu->add_menu(
2684 - array(
2685 - 'id' => 'profile',
2686 - 'parent' => 'friends-menu',
2687 - 'title' => esc_html(
2688 - sprintf(
2689 - // translators: %s is a site title.
2690 - __( "%s's Profile", 'friends' ),
2691 - get_bloginfo( 'name' )
2692 - )
2693 - ),
2694 - 'href' => home_url( '/friends/' ),
2695 - )
2696 - );
2697 - }
2972 + $wp_menu->add_menu(
2973 + array(
2974 + 'id' => 'add-friend',
2975 + 'parent' => 'friends-menu',
2976 + 'title' => esc_html__( 'Add a friend', 'friends' ),
2977 + 'href' => home_url( '/friends/add-friend' ),
2978 + )
2979 + );
2980 + $wp_menu->add_menu(
2981 + array(
2982 + 'id' => 'friends',
2983 + 'parent' => 'friends-menu',
2984 + 'title' => esc_html__( 'Settings' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2985 + 'href' => home_url( '/friends/settings/' ),
2986 + )
2987 + );
2698 2988 }
2699 2989
2700 2990 /**
2701 2991 * Add Friend entries to the New Content admin section
@@ -2729,17 +3019,37 @@
2729 3019 public function admin_bar_mobile() {
2730 3020 if ( ! is_user_logged_in() ) {
2731 3021 return;
2732 3022 }
3023 + $logo_mask = "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-10 53 154 187'%3E%3Cpath d='M 132.29 90.93 C 119.28 54.95 70.12 63.99 38.89 88.85 -7.9 126.11 11.81 177.74 25.75 200.93 40.32 225.15 60.67 237.5 74.87 225.14 83.57 217.57 86.99 209.19 77.64 194.01 74.25 188.51 76.44 170.04 85.94 165.64 94.55 161.65 94.95 149.38 83.17 149.73 75.25 149.97 53.78 148.25 61.03 144.89 67.56 141.86 143.08 120.75 132.29 90.93 Z'/%3E%3C/svg%3E\") center/contain no-repeat";
2733 3024 ?>
2734 3025 <style type="text/css" media="screen">
3026 + #wpadminbar #wp-admin-bar-friends-menu .ab-icon:before {
3027 + content: "";
3028 + float: left;
3029 + width: 20px;
3030 + height: 20px;
3031 + margin-top: 2px;
3032 + background-color: currentColor;
3033 + -webkit-mask: <?php echo $logo_mask; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
3034 + mask: <?php echo $logo_mask; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
3035 + }
2735 3036 @media screen and (max-width: 782px) {
2736 - #wpadminbar #wp-admin-bar-friends, #wpadminbar #wp-admin-bar-friends .ab-icon {
3037 + #wpadminbar #wp-admin-bar-friends-menu, #wpadminbar #wp-admin-bar-friends-menu .ab-icon {
2737 3038 display: block !important;
2738 3039 }
2739 - #wpadminbar #wp-admin-bar-friends .ab-label {
3040 + #wpadminbar #wp-admin-bar-friends-menu .ab-label {
2740 3041 display: none !important;
2741 3042 }
3043 + #wpadminbar #wp-admin-bar-friends-menu .ab-icon:before {
3044 + width: 32px;
3045 + height: 32px;
3046 + margin-top: 6px;
3047 + margin-left: 6px;
3048 + }
3049 + body.friends-page #wpadminbar li#wp-admin-bar-comments {
3050 + display: none;
3051 + }
2742 3052 }
2743 3053 </style>
2744 3054 <?php
2745 3055 }
@@ -2754,14 +3064,9 @@
2754 3064 public function delete_user_form( $current_user, $userids ) {
2755 3065 $only_friends_affiliated = true;
2756 3066 foreach ( $userids as $user_id ) {
2757 3067 $user = new \WP_User( $user_id );
2758 - if (
2759 - ! $user->has_cap( 'friend_request' ) &&
2760 - ! $user->has_cap( 'pending_friend_request' ) &&
2761 - ! $user->has_cap( 'friend' ) &&
2762 - ! $user->has_cap( 'subscription' )
2763 - ) {
3068 + if ( ! $user->has_cap( 'subscription' ) ) {
2764 3069 $only_friends_affiliated = false;
2765 3070 break;
2766 3071 }
2767 3072 }
@@ -2813,16 +3118,18 @@
2813 3118 <h3><?php esc_html_e( 'Bookmarklets', 'friends' ); ?></h3>
2814 3119
2815 3120 <p><?php esc_html_e( "Drag one of these bookmarklets to your bookmarks bar and click it when you're on a site around the web for the appropriate action.", 'friends' ); ?></p>
2816 3121 <p>
2817 - <a href="javascript:void(location.href='<?php echo esc_attr( self_admin_url( 'admin.php?page=add-friend&url=' ) ); ?>'+encodeURIComponent(location.href))" style="display: inline-block; padding: .5em; border: 1px solid #999; border-radius: 4px; background-color: #ddd;text-decoration: none; margin-right: 3em"><?php echo esc_html_e( 'Add friend', 'friends' ); ?></a>
2818 - <a href="javascript:void(location.href='<?php echo esc_attr( self_admin_url( 'admin.php?page=add-friend&url=' ) ); ?>'+encodeURIComponent(location.href))" style="display: inline-block; padding: .5em; border: 1px solid #999; border-radius: 4px; background-color: #ddd; text-decoration: none; margin-right: 3em"><?php echo esc_html_e( 'Subscribe', 'friends' ); ?></a>
3122 + <a href="javascript:void(location.href='<?php echo esc_attr( self_admin_url( 'admin.php?page=add-friend&url=' ) ); ?>'+encodeURIComponent(location.href))" style="display: inline-block; padding: .5em; border: 1px solid #999; border-radius: 4px; background-color: #ddd;text-decoration: none; margin-right: 3em"><?php esc_html_e( 'Add friend', 'friends' ); ?></a>
3123 + <a href="javascript:void(location.href='<?php echo esc_attr( self_admin_url( 'admin.php?page=add-friend&url=' ) ); ?>'+encodeURIComponent(location.href))" style="display: inline-block; padding: .5em; border: 1px solid #999; border-radius: 4px; background-color: #ddd; text-decoration: none; margin-right: 3em"><?php esc_html_e( 'Subscribe', 'friends' ); ?></a>
2819 3124 </p>
2820 3125 <h3><?php esc_html_e( 'Browser Extension', 'friends' ); ?></h3>
2821 3126
2822 - <p><?php esc_html_e( 'There is also the option to use a browser extension.', 'friends' ); ?></p>
3127 + <p><?php esc_html_e( 'For a smoother experience, install the Friends browser extension. It adds a toolbar button to subscribe to the current site with one click, plus quick actions provided by other Friends-aware plugins.', 'friends' ); ?></p>
2823 3128 <p>
2824 - <a href="https://addons.mozilla.org/en-US/firefox/addon/wpfriends/"><?php echo esc_html_e( 'Firefox Extension', 'friends' ); ?></a>
3129 + <a href="https://chromewebstore.google.com/detail/friends/ledbghpaplkpclndlommpbokndieflhl"><?php esc_html_e( 'Chrome Extension', 'friends' ); ?></a>
3130 + &nbsp;·&nbsp;
3131 + <a href="https://addons.mozilla.org/en-US/firefox/addon/wpfriends/"><?php esc_html_e( 'Firefox Extension', 'friends' ); ?></a>
2825 3132 </p>
2826 3133 </div>
2827 3134 <?php
2828 3135 }
@@ -2833,33 +3140,12 @@
2833 3140 * @param array $items Items inserted by another plugin.
2834 3141 * @return array Items + our items.
2835 3142 */
2836 3143 public function dashboard_glance_items( $items ) {
2837 - $count_users = count_users();
2838 - $count = array_merge(
2839 - array(
2840 - 'friend' => 0,
2841 - 'acquaintance' => 0,
2842 - 'friend_request' => 0,
2843 - 'subscription' => 0,
2844 - ),
2845 - $count_users['avail_roles']
2846 - );
2847 - $friend_count = $count['friend'] + $count['acquaintance'];
2848 - $friend_request_count = $count['friend_request'];
2849 - $subscription_count = $count['subscription'];
3144 + $subscription_count = User_Query::all_subscriptions()->get_total();
2850 3145 $friend_post_count = wp_count_posts( Friends::CPT );
2851 3146 $friend_post_count = $friend_post_count->publish + $friend_post_count->private;
2852 3147
2853 - $items[] = '<a class="friends" href="' . self_admin_url( 'users.php?role=friend' ) . '">' . sprintf(
2854 - // translators: %s is the number of your friends.
2855 - _n( '%s Friend', '%s Friends', $friend_count, 'friends' ),
2856 - $friend_count
2857 - ) . '</a>';
2858 - if ( $friend_request_count ) {
2859 - // translators: %s is the number of friend requests.
2860 - $items[] = '<a class="friend-requests" href="' . self_admin_url( 'users.php?role=friend_request' ) . '">' . sprintf( _n( '%s Friend Request', '%s Friend Requests', $friend_request_count, 'friends' ), $friend_request_count ) . '</a>';
2861 - }
2862 3148 if ( $subscription_count ) {
2863 3149 // translators: %s is the number of subscriptions.
2864 3150 $items[] = '<a class="subscriptions" href="' . self_admin_url( 'users.php?role=subscription' ) . '">' . sprintf( _n( '%s Subscription', '%s Subscriptions', $subscription_count, 'friends' ), $subscription_count ) . '</a>';
2865 3151 }
@@ -2865,82 +3151,212 @@
2865 3151 }
2866 3152
2867 3153 if ( $friend_post_count ) {
2868 3154 // translators: %s is the number of friend posts.
2869 - $items[] = '<a class="friend-posts" href="' . home_url( '/friends/' ) . '">' . sprintf( _n( '%s Post by Friends', '%s Posts by Friends', $friend_post_count, 'friends' ), $friend_post_count ) . '</a>';
3155 + $items[] = '<a class="friend-posts" href="' . home_url( '/friends/' ) . '">' . sprintf( _n( '%s Post by Friends', '%s Posts by Friends', $friend_post_count, 'friends' ), number_format_i18n( $friend_post_count ) ) . '</a>';
2870 3156 }
2871 3157 return $items;
2872 3158 }
2873 3159
2874 - public function site_status_tests( $tests ) {
2875 - $tests['direct']['friends-roles'] = array(
2876 - 'label' => __( 'Friend roles were created', 'friends' ),
2877 - 'test' => array( $this, 'friend_roles_test' ),
2878 - );
2879 - return $tests;
3160 + public function add_dashboard_widgets() {
3161 + if ( ! Friends::has_required_privileges() ) {
3162 + return;
3163 + }
3164 + $user_id = get_current_user_id();
3165 + $widgets = get_user_option( 'friends_dashboard_widgets', $user_id );
3166 + if ( ! $widgets ) {
3167 + $widgets = array( array() );
3168 + update_user_option( $user_id, 'friends_dashboard_widgets', $widgets );
3169 + }
3170 + foreach ( $widgets as $i => $widget ) {
3171 + if ( ! is_array( $widget ) ) {
3172 + continue;
3173 + }
3174 + $title = __( 'Latest Posts', 'friends' );
3175 + if ( isset( $widget['format'] ) ) {
3176 + $title = get_post_format_string( sanitize_key( $widget['format'] ) );
3177 + }
3178 +
3179 + if ( ! empty( $widget['friend'] ) ) {
3180 + $user = User::get_by_username( $widget['friend'] );
3181 + $title = ' by ' . $user->display_name;
3182 + }
3183 + $title = sprintf(
3184 + // translators: %s is an author name or "Latest Posts".
3185 + __( 'Friends: %s', 'friends' ),
3186 + $title
3187 + );
3188 + wp_add_dashboard_widget( 'friends_dashboard_widget' . $i, $title, array( $this, 'render_dashboard_widget' ), array( $this, 'render_dashboard_widget_controls' ), $widget, 'side', 'high' );
3189 + }
2880 3190 }
2881 3191
2882 - public function get_missing_friends_plugin_roles() {
2883 - $missing = Friends::get_friends_plugin_roles();
2884 - $roles = new \WP_Roles;
2885 - foreach ( $roles->roles as $role => $data ) {
2886 - if ( isset( $data['capabilities']['friends_plugin'] ) ) {
2887 - foreach ( $missing as $k => $cap ) {
2888 - if ( isset( $data['capabilities'][ $cap ] ) ) {
2889 - unset( $missing[ $k ] );
2890 - break;
2891 - }
2892 - }
3192 + public function add_new_dashboard_widget( $friend = null, $format = null ) {
3193 + $user_id = get_current_user_id();
3194 + $widgets = get_user_option( 'friends_dashboard_widgets', $user_id );
3195 + if ( ! $widgets ) {
3196 + $widgets = array();
3197 + }
3198 + $widget = array();
3199 + if ( $friend ) {
3200 + $widget['friend'] = $friend;
3201 + }
3202 + if ( $format ) {
3203 + $widget['format'] = $format;
3204 + }
3205 + $widgets[] = $widget;
3206 + update_user_option( $user_id, 'friends_dashboard_widgets', $widgets );
3207 + }
3208 +
3209 + public function render_dashboard_widget_controls( $id, $widget = false ) {
3210 + if ( empty( $id ) && $widget ) {
3211 + $id = intval( str_replace( 'friends_dashboard_widget', '', $widget['id'] ) );
3212 + }
3213 + $user_id = get_current_user_id();
3214 + $widgets = get_user_option( 'friends_dashboard_widgets', $user_id );
3215 + if ( ! $widgets ) {
3216 + $widgets = array( array() );
3217 + }
3218 +
3219 + // phpcs:disable WordPress.Security.NonceVerification
3220 + if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget_id'] ) ) {
3221 +
3222 + $id = intval( str_replace( 'friends_dashboard_widget', '', sanitize_text_field( wp_unslash( $_POST['widget_id'] ) ) ) );
3223 + if ( isset( $_POST['add-new'] ) ) {
3224 + $id = count( $widgets );
3225 + $widgets[ $id ] = array();
2893 3226 }
3227 + if ( ! empty( $_POST['friend'] ) ) {
3228 + $widgets[ $id ]['friend'] = sanitize_text_field( wp_unslash( $_POST['friend'] ) );
3229 + } else {
3230 + unset( $widgets[ $id ]['friend'] );
3231 + }
3232 + if ( ! empty( $_POST['format'] ) ) {
3233 + $widgets[ $id ]['format'] = sanitize_text_field( wp_unslash( $_POST['format'] ) );
3234 + } else {
3235 + unset( $widgets[ $id ]['format'] );
3236 + }
3237 + if ( isset( $_POST['delete'] ) ) {
3238 + unset( $widgets[ $id ] );
3239 + }
3240 +
3241 + update_user_option( $user_id, 'friends_dashboard_widgets', $widgets );
2894 3242 }
3243 + // phpcs:enable WordPress.Security.NonceVerification
3244 + $args = array();
3245 + if ( isset( $widgets[ $id ] ) ) {
3246 + $args = $widgets[ $id ];
3247 + }
3248 + echo '<p>';
3249 + echo '<label>';
3250 + esc_html_e( 'Friend:', 'friends' );
3251 + echo '<select name="friend">';
3252 + echo '<option value="">' . esc_html__( 'Any Friend', 'friends' ) . '</option>';
3253 + $users = User_Query::all_associated_users();
3254 + foreach ( $users->get_results() as $user ) {
3255 + echo '<option value="' . esc_attr( $user->user_login ) . '"';
3256 + if ( isset( $args['friend'] ) && $args['friend'] === $user->user_login ) {
3257 + echo ' selected="selected"';
3258 + }
3259 + echo '>' . esc_html( $user->display_name ) . ' (' . esc_html( $user->user_login ) . ')</option>';
3260 + }
3261 + echo '</select>';
3262 + echo '</label>';
3263 + echo '</p>';
3264 + echo '<p>';
3265 + echo '<label>';
3266 + esc_html_e( 'Post Format:', 'friends' );
3267 + echo '<select name="format">';
3268 + echo '<option value="">' . esc_html__( 'Any Post Format', 'friends' ) . '</option>';
3269 + foreach ( get_post_format_strings() as $format => $label ) {
3270 + echo '<option value="' . esc_attr( $format ) . '"';
3271 + if ( isset( $args['format'] ) && $args['format'] === $format ) {
3272 + echo ' selected="selected"';
3273 + }
3274 + echo '>' . esc_html( $label ) . '</option>';
3275 + }
3276 + echo '</select>';
3277 + echo '</label>';
3278 + echo '</p>';
3279 + echo '<p>';
3280 + echo ' <button name="add-new" class="button button-secondary">' . esc_html__( 'Save as a new widget', 'friends' ) . '</button>';
3281 + echo ' <button name="delete" class="button">' . esc_html__( 'Delete this widget', 'friends' ) . '</button>';
3282 + echo '</p>';
3283 + }
2895 3284
2896 - return array_values( $missing );
3285 + public function render_dashboard_widget( $args, $widget ) {
3286 + $args = $widget['args'];
3287 + echo '<div class="friends-dashboard-widget" data-nonce="';
3288 + echo esc_attr( wp_create_nonce( 'friends-dashboard' ) );
3289 + echo '"';
3290 + if ( ! empty( $args['friend'] ) ) {
3291 + echo ' data-friend="' . esc_attr( $args['friend'] ) . '"';
3292 + }
3293 + if ( ! empty( $args['format'] ) ) {
3294 + echo ' data-format="' . esc_attr( $args['format'] ) . '"';
3295 + }
3296 + echo '></div>';
2897 3297 }
2898 3298
2899 - public function friend_roles_test() {
2900 - $result = array(
2901 - 'label' => __( 'The friend roles have been installed', 'friends' ),
2902 - 'status' => 'good',
2903 - 'badge' => array(
2904 - 'label' => __( 'Friends', 'friends' ),
2905 - 'color' => 'green',
2906 - ),
2907 - 'description' =>
2908 - '<p>' .
2909 - __( 'The Friends Plugin uses users and user roles to determine friendship status between sites.', 'friends' ) .
2910 - '</p>' .
2911 - '<p>' .
2912 - sprintf(
2913 - // translators: %s is a list of roles.
2914 - __( 'These are the roles required for the friends plugin: %s', 'friends' ),
2915 - implode( ', ', Friends::get_friends_plugin_roles() )
2916 - ) .
2917 - '</p>',
2918 - 'test' => 'friends-roles',
2919 - );
3299 + public function ajax_friends_dashboard() {
3300 + check_ajax_referer( 'friends-dashboard' );
2920 3301
2921 - $missing_friend_roles = $this->get_missing_friends_plugin_roles();
2922 - if ( ! empty( $missing_friend_roles ) ) {
3302 + $query_args = array();
3303 + $args = array();
2923 3304
2924 - $result['label'] = sprintf(
2925 - // translators: %s is a list of missing roles.
2926 - __( 'Not all friend roles have been installed. Missing: %s', 'friends' ),
2927 - implode( ', ', $missing_friend_roles )
3305 + if ( isset( $_POST['friend'] ) ) {
3306 + $friend = User::get_by_username( sanitize_text_field( wp_unslash( $_POST['friend'] ) ) );
3307 + if ( $friend ) {
3308 + $args['friend_user'] = $friend;
3309 + $query_args = $friend->modify_get_posts_args_by_author( $query_args );
3310 + }
3311 + }
3312 +
3313 + if ( isset( $_POST['format'] ) ) {
3314 + $post_formats = get_post_format_slugs();
3315 + $format = sanitize_text_field( wp_unslash( $_POST['format'] ) );
3316 +
3317 + if ( isset( $post_formats[ $format ] ) ) {
3318 + $args['post_format'] = $format;
3319 + if ( 'standard' !== $format ) {
3320 + $query_args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
3321 + array(
3322 + 'taxonomy' => 'post_format',
3323 + 'field' => 'slug',
3324 + 'terms' => array( 'post-format-' . $format ),
3325 + ),
3326 + );
3327 + } else {
3328 + $query_args['tax_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
3329 + array(
3330 + 'taxonomy' => 'post_format',
3331 + 'operator' => 'NOT EXISTS',
3332 + ),
3333 + );
3334 + }
3335 + }
3336 + }
3337 +
3338 + $any_friends = User_Query::all_associated_users();
3339 +
3340 + ob_start();
3341 + if ( 0 === $any_friends->get_total() && empty( $query_args ) ) {
3342 + Friends::template_loader()->get_template_part(
3343 + 'admin/dashboard-widget-welcome',
3344 + null,
3345 + array()
2928 3346 );
2929 - $result['badge']['color'] = 'red';
2930 - $result['status'] = 'critical';
2931 - $result['description'] .= '<p>';
2932 - $result['description'] .= wp_kses_post(
2933 - sprintf(
2934 - // translators: %s is a URL.
2935 - __( '<strong>To fix this:</strong> <a href="%s">Re-run activation of the Friends plugin</a>.', 'friends' ),
2936 - esc_url( wp_nonce_url( add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=friends-settings&rerun-activate' ) ), 'friends-settings' ) )
2937 - )
2938 - );
2939 - $result['description'] .= '</p>';
3347 +
3348 + } else {
3349 + $query_args['post_type'] = apply_filters( 'friends_frontend_post_types', array( 'post' ) );
3350 + $args['posts'] = get_posts( $query_args );
3351 + Friends::template_loader()->get_template_part( 'admin/dashboard-widget', null, $args );
2940 3352 }
3353 + $data = ob_get_contents();
3354 + ob_end_clean();
2941 3355
2942 - return $result;
3356 + wp_send_json_success(
3357 + $data
3358 + );
2943 3359 }
2944 3360
2945 3361 public function site_status_test_php_modules( $modules ) {
2946 3362 $modules['mbstring']['required'] = true;
@@ -2946,62 +3362,70 @@
2946 3362 $modules['mbstring']['required'] = true;
2947 3363 return $modules;
2948 3364 }
2949 3365
2950 - public function site_health_debug( $debug_info ) {
2951 - $missing_friend_roles = $this->get_missing_friends_plugin_roles();
2952 - $debug_info['friends'] = array(
2953 - 'label' => __( 'Friends', 'friends' ),
2954 - 'fields' => array(
2955 - 'version' => array(
2956 - 'label' => __( 'Friends Version', 'friends' ),
2957 - 'value' => Friends::VERSION,
2958 - ),
2959 - 'mbstring' => array(
2960 - 'label' => __( 'mbstring is available', 'friends' ),
2961 - 'value' => function_exists( 'mb_check_encoding' ) ? __( 'Yes' ) : __( 'No' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2962 - ),
2963 - 'roles' => array(
2964 - 'label' => __( 'Friend roles missing', 'friends' ),
2965 - 'value' => empty( $missing_friend_roles ) ? sprintf(
2966 - // translators: %s is a list of roles.
2967 - __( 'All roles found: %s', 'friends' ),
2968 - implode( ', ', Friends::get_friends_plugin_roles() )
2969 - ) : implode( ', ', $missing_friend_roles ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2970 - ),
2971 - 'main_user' => array(
2972 - 'label' => __( 'Main Friend User', 'friends' ),
2973 - 'value' => self::human_readable_main_user(),
2974 - ),
2975 - 'parsers' => array(
2976 - 'label' => __( 'Registered Parsers', 'friends' ),
2977 - 'value' => strip_tags( implode( ', ', $this->friends->feed->get_registered_parsers() ) ),
2978 - ),
2979 - ),
2980 - );
3366 + public function admin_friend_posts_query( $query ) {
3367 + global $wp_query, $wp, $authordata;
3368 + if ( $wp_query !== $query || ! is_admin() ) {
3369 + return $query;
3370 + }
3371 + if ( ! isset( $query->query['post_type'] ) || ! in_array( $query->query['post_type'], apply_filters( 'friends_frontend_post_types', array( 'post' ) ), true ) ) {
3372 + return $query;
3373 + }
2981 3374
2982 - return $debug_info;
3375 + if ( empty( $query->query['author'] ) ) {
3376 + return $query;
3377 + }
3378 +
3379 + $author = User::get_user_by_id( $query->query['author'] );
3380 + if ( ! $author ) {
3381 + return $query;
3382 + }
3383 + $query->query_vars['author'] = '';
3384 + $query = $author->modify_query_by_author( $query );
3385 +
3386 + return $query;
2983 3387 }
2984 3388
2985 3389 /**
2986 - * Returns a human readable string for which user is the main user.
3390 + * Render an "ActivityPub plugin not active" notice for activitypub-parser feeds
3391 + * when the ActivityPub plugin is not loaded (so Feed_Parser_ActivityPub never fires).
2987 3392 *
2988 - * @return string
3393 + * @param User_Feed $feed The feed.
3394 + * @param int $term_id The term ID.
3395 + * @param string $parser The parser slug.
2989 3396 */
2990 - private static function human_readable_main_user() {
2991 - $main_user = Friends::get_main_friend_user_id();
2992 -
2993 - if ( ! $main_user ) {
2994 - // translators: %d is the number of users.
2995 - return esc_html( sprintf( __( 'No main user set. Admin users: %d', 'friends' ), User_Query::all_admin_users()->get_total() ) );
3397 + public function maybe_render_activitypub_inactive_notice( $feed, $term_id, $parser ) {
3398 + if ( 'activitypub' !== $parser ) {
3399 + return;
2996 3400 }
2997 3401
2998 - $user = new \WP_User( $main_user );
2999 -
3000 - if ( ! $user ) {
3001 - return sprintf( '#%1$d %2$s', $main_user, '???' );
3402 + if ( class_exists( '\Activitypub\Activitypub' ) ) {
3403 + return;
3002 3404 }
3003 -
3004 - return sprintf( '#%1$d %2$s', $user->ID, $user->user_login );
3405 + ?>
3406 + <div class="activitypub-subscription-check">
3407 + <div class="ap-section-header"><?php esc_html_e( 'ActivityPub Plugin', 'friends' ); ?></div>
3408 + <div class="ap-data-grid">
3409 + <span class="ap-data-label"><?php esc_html_e( 'Status', 'friends' ); ?></span>
3410 + <span class="ap-data-value"><em style="color: orange;"><?php esc_html_e( 'not active', 'friends' ); ?></em></span>
3411 + </div>
3412 + <div class="ap-section-footer">
3413 + <?php
3414 + if ( current_user_can( 'activate_plugins' ) ) {
3415 + echo wp_kses(
3416 + sprintf(
3417 + /* translators: %s is a link to the plugin search page */
3418 + __( 'The <a href="%s">ActivityPub plugin</a> is required to receive posts from this feed.', 'friends' ),
3419 + esc_url( admin_url( 'plugin-install.php?s=activitypub&tab=search&type=term' ) )
3420 + ),
3421 + array( 'a' => array( 'href' => array() ) )
3422 + );
3423 + } else {
3424 + esc_html_e( 'The ActivityPub plugin is required to receive posts from this feed.', 'friends' );
3425 + }
3426 + ?>
3427 + </div>
3428 + </div>
3429 + <?php
3005 3430 }
3006 -
3007 3431 }