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 +1977 -1556 2.7.84.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' ) );
@@ -227,12 +263,12 @@
227 263 __( 'Welcome to the Friends Settings! You can configure the Friends plugin here to your liking.', 'friends' ) .
228 264 '</p>' .
229 265 '<p>' .
230 266 sprintf(
231 - // translators: %1$s is a URL, %2$s is the name of a wp-admin screen.
232 - __( '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' ),
233 - '"' . esc_attr( self_admin_url( self::get_users_url() ) ) . '"',
234 - __( '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' )
235 271 ) .
236 272 '</p>',
237 273 )
238 274 );
@@ -255,19 +291,17 @@
255 291 public function admin_enqueue_scripts() {
256 292 $handle = 'friends-admin';
257 293 $file = 'friends-admin.js';
258 294 $version = Friends::VERSION;
259 - 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 );
260 296
261 297 $variables = array(
262 298 'ajax_url' => admin_url( 'admin-ajax.php' ),
263 299 'add_friend_url' => self_admin_url( 'admin.php?page=add-friend' ),
264 300 'add_friend_text' => __( 'Add a Friend', 'friends' ),
301 + 'copy_text' => __( 'Copy', 'friends' ),
302 + 'copied_text' => __( 'Copied!', 'friends' ),
265 303 'delete_feed_question' => __( 'Delete the feed? You need to click "Save Changes" to really delete it.', 'friends' ),
266 - 'role_friend' => __( 'Friend', 'friends' ),
267 - 'role_acquaintance' => __( 'Acquaintance', 'friends' ),
268 - 'role_friend_request' => __( 'Friend Request', 'friends' ),
269 - 'role_pending_friend_request' => __( 'Pending Friend Request', 'friends' ),
270 304 'role_subscription' => __( 'Following', 'friends' ),
271 305 'role_connection' => __( 'Connection', 'friends' ),
272 306 'role_contact' => __( 'Contact', 'friends' ),
273 307 'role_connection_request' => __( 'Connection Request', 'friends' ),
@@ -294,10 +328,17 @@
294 328
295 329 add_filter(
296 330 'friends_friend_private_feed_url',
297 331 function ( $feed_url, $friend_user ) {
298 - // translators: %1s is the name of the friend, %2$s is the feed URL.
299 - 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 + );
300 341 return $feed_url;
301 342 },
302 343 10,
303 344 2
@@ -338,10 +379,10 @@
338 379 10,
339 380 2
340 381 );
341 382
342 - if ( isset( $_GET['user'] ) ) {
343 - $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
344 385 if ( ! $friend_user || is_wp_error( $friend_user ) || ! $friend_user->can_refresh_feeds() ) {
345 386 wp_die( esc_html__( 'Invalid user ID.' ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
346 387 }
347 388 $friend_user->retrieve_posts_from_active_feeds();
@@ -350,85 +391,8 @@
350 391 }
351 392 }
352 393
353 394 /**
354 - * Admin page for installing plugins.
355 - */
356 - public function admin_plugin_installer() {
357 - Friends::template_loader()->get_template_part( 'admin/plugin-installer-header' );
358 - Plugin_Installer::init();
359 - Friends::template_loader()->get_template_part( 'admin/plugin-installer-footer' );
360 - }
361 -
362 - /**
363 - * Send a friend request to another WordPress with the Friends plugin
364 - *
365 - * @param string $rest_url The site URL of the friend's
366 - * WordPress.
367 - * @param string $user_login The user login.
368 - * @param string $user_url The user url.
369 - * @param string $display_name The display name.
370 - * @param string $codeword A codeword to send along.
371 - * @param string $message A message to send along.
372 - *
373 - * @return \WP_User|\WP_error $user The new associated user or an error object.
374 - */
375 - public function send_friend_request( $rest_url, $user_login, $user_url, $display_name, $codeword = 'friends', $message = '' ) {
376 - if ( ! is_string( $rest_url ) || ! Friends::check_url( $rest_url ) ) {
377 - return new \WP_Error( 'invalid-url', __( 'You entered an invalid URL.', 'friends' ) );
378 - }
379 -
380 - $future_in_token = wp_generate_password( 128, false );
381 -
382 - $current_user = wp_get_current_user();
383 - $response = wp_safe_remote_post(
384 - $rest_url . '/friend-request',
385 - array(
386 - 'body' => array(
387 - 'version' => 2,
388 - 'codeword' => $codeword,
389 - 'name' => $current_user->display_name,
390 - 'url' => home_url(),
391 - 'icon_url' => get_avatar_url( $current_user->ID ),
392 - 'message' => mb_substr( trim( $message ), 0, 2000 ),
393 - 'key' => $future_in_token,
394 - ),
395 - 'timeout' => 20,
396 - 'redirection' => 5,
397 - )
398 - );
399 - if ( is_wp_error( $response ) ) {
400 - return $response;
401 - }
402 -
403 - $json = json_decode( wp_remote_retrieve_body( $response ) );
404 - if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
405 - if ( $json && isset( $json->code ) && isset( $json->message ) ) {
406 - // translators: %s is the message from the other server.
407 - return new \WP_Error( $json->code, sprintf( __( 'The other side responded: %s', 'friends' ), $json->message ), $json->data );
408 - }
409 - }
410 -
411 - if ( ! $json || ! is_object( $json ) ) {
412 - return new \WP_Error( 'unexpected-rest-response', 'Unexpected remote response: ' . substr( wp_remote_retrieve_body( $response ), 0, 30 ), $response );
413 - }
414 -
415 - $friend_user = User::create( $user_login, 'pending_friend_request', $user_url, $display_name );
416 - if ( is_wp_error( $friend_user ) ) {
417 - return $friend_user;
418 - }
419 - $friend_user->update_user_option( 'friends_rest_url', $rest_url );
420 -
421 - if ( isset( $json->request ) ) {
422 - update_option( 'friends_request_' . sha1( $json->request ), $friend_user->ID );
423 - $friend_user->update_user_option( 'friends_future_in_token_' . sha1( $json->request ), $future_in_token );
424 - $friend_user->set_role( 'pending_friend_request' );
425 - }
426 -
427 - return $friend_user;
428 - }
429 -
430 - /**
431 395 * Don't show the edit link for friend posts
432 396 *
433 397 * @param string $link The edit link.
434 398 * @param int|User $user The user.
@@ -434,8 +398,21 @@
434 398 * @param int|User $user The user.
435 399 * @return string|bool The edit link or false.
436 400 */
437 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 + }
438 415 if ( ! $user instanceof \WP_User ) {
439 416 if ( is_string( $user ) ) {
440 417 $user = User::get_by_username( $user );
441 418 } else {
@@ -442,22 +419,37 @@
442 419 $user = new \WP_User( $user );
443 420 }
444 421 }
445 422
423 + if ( ! $user || is_wp_error( $user ) ) {
424 + $cache[ $cache_key ] = false;
425 + return $link;
426 + }
427 +
446 428 if ( is_multisite() && is_super_admin( $user->ID ) ) {
429 + $cache[ $cache_key ] = false;
447 430 return $link;
448 431 }
449 432 if ( ! $user->has_cap( 'friends_plugin' ) ) {
433 + $cache[ $cache_key ] = false;
450 434 return $link;
451 435 }
452 436
453 - 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 ];
454 439 }
455 440
456 441 public static function get_edit_friend_link( $user ) {
457 - if ( ! $user instanceof \WP_User ) {
458 - $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 );
459 446 }
447 +
448 + if ( ! $user || is_wp_error( $user ) ) {
449 + return '';
450 + }
451 +
460 452 return apply_filters( 'get_edit_user_link', $user->user_url, $user->user_login );
461 453 }
462 454
463 455 public static function get_unfriend_link( $user ) {
@@ -492,15 +484,15 @@
492 484 if ( empty( $_REQUEST ) || ! isset( $_REQUEST['_wpnonce'] ) ) {
493 485 return;
494 486 }
495 487
496 - if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'friends-settings' ) ) {
488 + if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends-settings' ) ) {
497 489 return;
498 490 }
499 491
500 492 $this->check_admin_settings();
501 - foreach ( array( 'ignore_incoming_friend_requests' ) as $checkbox ) {
502 - 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 ] ) ) {
503 495 update_option( 'friends_' . $checkbox, true );
504 496 } else {
505 497 delete_option( 'friends_' . $checkbox );
506 498 }
@@ -505,19 +497,18 @@
505 497 delete_option( 'friends_' . $checkbox );
506 498 }
507 499 }
508 500
509 - foreach ( array( 'friend_request_notification' ) as $negative_user_checkbox ) {
510 - if ( isset( $_POST[ $negative_user_checkbox ] ) && $_POST[ $negative_user_checkbox ] ) {
511 - delete_user_option( get_current_user_id(), 'friends_no_' . $negative_user_checkbox );
512 - } else {
513 - 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 + }
514 507 }
515 - }
516 508
517 - if ( current_user_can( 'manage_options' ) ) {
518 - foreach ( array( 'force_enable_post_formats', 'expose_post_format_feeds' ) as $checkbox ) {
519 - 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 ] ) ) {
520 511 update_option( 'friends_' . $checkbox, true );
521 512 } else {
522 513 delete_option( 'friends_' . $checkbox );
523 514 }
@@ -522,73 +513,20 @@
522 513 delete_option( 'friends_' . $checkbox );
523 514 }
524 515 }
525 516
526 - if ( isset( $_POST['limit_homepage_post_format'] ) && $_POST['limit_homepage_post_format'] && in_array( $_POST['limit_homepage_post_format'], get_post_format_slugs() ) ) {
527 - 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'] ) );
528 520 } else {
529 - delete_option( 'friends_limit_homepage_post_format' );
521 + delete_option( 'friends_compose_post_format' );
530 522 }
531 -
532 - if ( isset( $_POST['main_user_id'] ) && is_numeric( $_POST['main_user_id'] ) ) {
533 - update_option( 'friends_main_user_id', intval( $_POST['main_user_id'] ) );
534 - } else {
535 - $main_user_id = Friends::get_main_friend_user_id();
536 - $main_user_id_exists = false;
537 - $users = User_Query::all_admin_users();
538 - foreach ( $users->get_results() as $user ) {
539 - if ( $user->ID === $main_user_id ) {
540 - $main_user_id_exists = true;
541 - break;
542 - }
543 - }
544 - if ( ! $main_user_id_exists ) {
545 - // Reset the main user id.
546 - delete_option( 'friends_main_user_id' );
547 - Friends::get_main_friend_user_id();
548 - }
549 - }
550 -
551 - if ( isset( $_POST['comment_registration'] ) && $_POST['comment_registration'] ) {
552 - update_option( 'comment_registration', true );
553 - } else {
554 - delete_option( 'comment_registration' );
555 - }
556 -
557 - if ( isset( $_POST['blocks_everywhere'] ) && $_POST['blocks_everywhere'] ) {
558 - update_user_option( get_current_user_id(), 'friends_blocks_everywhere', 1 );
559 - } else {
560 - delete_user_option( get_current_user_id(), 'friends_blocks_everywhere' );
561 - }
562 -
563 - if ( isset( $_POST['comment_registration_message'] ) && $_POST['comment_registration_message'] ) {
564 - update_option( 'friends_comment_registration_message', $_POST['comment_registration_message'] );
565 - } else {
566 - delete_option( 'friends_comment_registration_message' );
567 - }
568 523 }
569 524
570 - if ( isset( $_POST['require_codeword'] ) && $_POST['require_codeword'] ) {
571 - update_option( 'friends_require_codeword', true );
572 - } else {
573 - delete_option( 'friends_require_codeword' );
574 - }
575 -
576 - if ( isset( $_POST['codeword'] ) && $_POST['codeword'] ) {
577 - update_option( 'friends_codeword', $_POST['codeword'] );
578 - } else {
579 - delete_option( 'friends_codeword' );
580 - }
581 -
582 - if ( isset( $_POST['wrong_codeword_message'] ) && $_POST['wrong_codeword_message'] ) {
583 - update_option( 'friends_wrong_codeword_message', $_POST['wrong_codeword_message'] );
584 - } else {
585 - delete_option( 'friends_wrong_codeword_message' );
586 - }
587 -
588 - if ( isset( $_POST['available_emojis'] ) && $_POST['available_emojis'] ) {
525 + if ( isset( $_POST['available_emojis'] ) && is_array( $_POST['available_emojis'] ) ) {
589 526 $available_emojis = array();
590 - 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 );
591 529 $data = Reactions::get_emoji_data( $id );
592 530 if ( $data ) {
593 531 $available_emojis[ $id ] = $data;
594 532 }
@@ -597,58 +535,59 @@
597 535 } else {
598 536 delete_option( 'friends_selected_emojis' );
599 537 }
600 538
601 - if ( isset( $_POST['notification_keywords'] ) && $_POST['notification_keywords'] ) {
602 - $keywords = array();
603 - foreach ( $_POST['notification_keywords'] as $i => $keyword ) {
604 - if ( trim( $keyword ) ) {
605 - $keywords[] = array(
606 - 'enabled' => isset( $_POST['notification_keywords_enabled'][ $i ] ) && $_POST['notification_keywords_enabled'][ $i ],
607 - 'keyword' => $keyword,
608 - );
609 - }
610 - }
611 - update_option( 'friends_notification_keywords', $keywords );
612 - }
613 -
614 - if ( isset( $_POST['default_role'] ) && in_array( $_POST['default_role'], array( 'friend', 'acquaintance' ), true ) ) {
615 - update_option( 'friends_default_friend_role', $_POST['default_role'] );
616 - }
617 -
618 - if ( isset( $_POST['new_post_notification'] ) && $_POST['new_post_notification'] ) {
619 - delete_user_option( get_current_user_id(), 'friends_no_new_post_notification' );
620 - } else {
621 - update_user_option( get_current_user_id(), 'friends_no_new_post_notification', 1 );
622 - }
623 -
624 539 // Global retention.
625 540 $retention_number_enabled = boolval( isset( $_POST['friends_enable_retention_number'] ) && $_POST['friends_enable_retention_number'] );
626 541 update_option( 'friends_enable_retention_number', $retention_number_enabled );
627 - if ( $retention_number_enabled ) {
542 + if ( $retention_number_enabled && isset( $_POST['friends_retention_number'] ) ) {
628 543 update_option( 'friends_retention_number', max( 1, intval( $_POST['friends_retention_number'] ) ) );
629 544 }
630 545 $retention_days_enabled = boolval( isset( $_POST['friends_enable_retention_days'] ) && $_POST['friends_enable_retention_days'] );
631 546 update_option( 'friends_enable_retention_days', $retention_days_enabled );
632 - if ( $retention_days_enabled ) {
547 + if ( $retention_days_enabled && isset( $_POST['friends_retention_days'] ) ) {
633 548 update_option( 'friends_retention_days', max( 1, intval( $_POST['friends_retention_days'] ) ) );
634 549 }
635 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 +
636 557 if ( isset( $_POST['frontend_default_view'] ) && in_array(
637 - $_POST['frontend_default_view'],
558 + wp_unslash( $_POST['frontend_default_view'] ),
638 559 array(
639 560 'collapsed',
640 561 )
641 562 ) ) {
642 - 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
643 564 } else {
644 - delete_option( 'friends_frontend_default_view' );
565 + delete_user_option( get_current_user_id(), 'friends_frontend_default_view' );
645 566 }
646 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 +
647 586 if ( isset( $_GET['_wp_http_referer'] ) ) {
648 587 wp_safe_redirect( wp_get_referer() );
649 588 } else {
650 - 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' ) ) ) );
651 590 }
652 591 exit;
653 592 }
654 593
@@ -662,13 +601,121 @@
662 601 return empty( $locale ) ? 'en_US' : $locale;
663 602 }
664 603
665 604 /**
666 - * 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
667 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 + */
668 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 +
669 715 $friends_subscriptions = User_Query::all_associated_users();
670 - $has_friend_users = $friends_subscriptions->get_total() > 0;
716 + $is_new_user = 0 === $friends_subscriptions->get_total();
717 +
671 718 wp_enqueue_script( 'plugin-install' );
672 719 add_thickbox();
673 720 wp_enqueue_script( 'updates' );
674 721
@@ -676,18 +723,548 @@
676 723 'admin/settings-header',
677 724 null,
678 725 array(
679 726 'active' => 'friends',
680 - 'title' => __( 'Friends', 'friends' ),
681 727 )
682 728 );
683 729
684 - Friends::template_loader()->get_template_part( 'admin/welcome', null, array( 'installed_plugins' => get_plugins() ) );
730 + $news_entries = self::get_news_entries();
685 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 +
686 745 Friends::template_loader()->get_template_part( 'admin/settings-footer' );
687 746 }
688 747
689 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 + /**
690 1267 * Render the Friends Admin settings page
691 1268 */
692 1269 public function render_admin_settings() {
693 1270 Friends::template_loader()->get_template_part(
@@ -694,14 +1271,13 @@
694 1271 'admin/settings-header',
695 1272 null,
696 1273 array(
697 1274 'active' => 'friends-settings',
698 - 'title' => __( 'Friends', 'friends' ),
699 1275 )
700 1276 );
701 1277 $this->check_admin_settings();
702 1278
703 - if ( isset( $_GET['updated'] ) ) {
1279 + if ( isset( $_GET['updated'] ) && boolval( $_GET['updated'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
704 1280 ?>
705 1281 <div id="message" class="updated notice is-dismissible"><p>
706 1282 <?php
707 1283 esc_html_e( 'Your settings were updated.', 'friends' );
@@ -709,27 +1285,13 @@
709 1285 </p></div>
710 1286 <?php
711 1287 }
712 1288
713 - // In order to switch to the frontend locale, we need to first pretend that nothing was loaded yet.
714 - global $l10n;
715 - $l10n = array();
716 -
717 - switch_to_locale( $this->get_frontend_locale() );
718 - // Now while loading the next translations we need to ensure that determine_locale() doesn't return the admin language but the frontend language.
719 - add_filter( 'pre_determine_locale', array( $this, 'get_frontend_locale' ) );
720 -
721 - $wrong_codeword_message = __( 'An invalid codeword was provided.', 'friends' );
722 - $comment_registration_message = __( 'Only people in my network can comment.', 'friends' );
723 - $my_network = __( 'my network', 'friends' );
724 - $comment_registration_default = strip_tags(
725 - /* translators: %s: Login URL. */
726 - __( 'You must be <a href="%s">logged in</a> to post a comment.' ) // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
727 - );
728 - // Now let's switch back to the admin language.
729 - remove_filter( 'pre_determine_locale', array( $this, 'get_frontend_locale' ) );
730 - restore_previous_locale();
731 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 + }
732 1294
733 1295 Friends::template_loader()->get_template_part(
734 1296 'admin/settings',
735 1297 null,
@@ -734,35 +1296,27 @@
734 1296 'admin/settings',
735 1297 null,
736 1298 array_merge(
737 1299 Friends::get_post_stats(),
1300 + $post_type_themes,
738 1301 array(
739 - 'potential_main_users' => User_Query::all_admin_users(),
740 - 'main_user_id' => Friends::get_main_friend_user_id(),
741 - 'friend_roles' => $this->get_friend_roles(),
742 - 'default_role' => get_option( 'friends_default_friend_role', 'friend' ),
743 - 'force_enable_post_formats' => get_option( 'friends_force_enable_post_formats' ),
744 - 'post_format_strings' => get_post_format_strings(),
745 - 'limit_homepage_post_format' => get_option( 'friends_limit_homepage_post_format', false ),
746 - 'expose_post_format_feeds' => get_option( 'friends_expose_post_format_feeds' ),
747 - 'private_rss_key' => get_option( 'friends_private_rss_key' ),
748 - 'comment_registration' => get_option( 'comment_registration' ), // WordPress option.
749 - 'comment_registration_message' => get_option( 'friends_comment_registration_message', $comment_registration_message ),
750 - 'comment_registration_default' => $comment_registration_default,
751 - 'my_network' => $my_network,
752 - 'public_profile_link' => home_url( '/friends/' ),
753 - 'codeword' => get_option( 'friends_codeword', 'friends' ),
754 - 'require_codeword' => get_option( 'friends_require_codeword' ),
755 - 'wrong_codeword_message' => get_option( 'friends_wrong_codeword_message', $wrong_codeword_message ),
756 - 'no_friend_request_notification' => get_user_option( 'friends_no_friend_request_notification' ),
757 - 'no_new_post_notification' => get_user_option( 'friends_no_new_post_notification' ),
758 - 'notification_keywords' => Feed::get_all_notification_keywords(),
759 - 'retention_days' => Friends::get_retention_days(),
760 - 'retention_number' => Friends::get_retention_number(),
761 - 'retention_days_enabled' => get_option( 'friends_enable_retention_days' ),
762 - 'retention_number_enabled' => get_option( 'friends_enable_retention_number' ),
763 - 'frontend_default_view' => get_option( 'friends_frontend_default_view', 'expanded' ),
764 - 'blocks_everywhere' => get_user_option( 'friends_blocks_everywhere' ),
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' ),
765 1319 )
766 1320 )
767 1321 );
768 1322
@@ -780,19 +1334,18 @@
780 1334 if ( ! isset( $_GET['user'] ) ) {
781 1335 wp_die( esc_html__( 'Invalid user.', 'friends' ) );
782 1336 }
783 1337
784 - $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'] ) ) );
785 1343 if ( ! $friend || is_wp_error( $friend ) ) {
786 1344 wp_die( esc_html__( 'Invalid username.', 'friends' ) );
787 1345 }
788 1346
789 - if (
790 - ! $friend->has_cap( 'friend_request' ) &&
791 - ! $friend->has_cap( 'pending_friend_request' ) &&
792 - ! $friend->has_cap( 'friend' ) &&
793 - ! $friend->has_cap( 'subscription' )
794 - ) {
1347 + if ( ! $friend->has_cap( 'subscription' ) ) {
795 1348 wp_die( esc_html__( 'This is not a user related to this plugin.', 'friends' ) );
796 1349 }
797 1350
798 1351 return $friend;
@@ -804,18 +1357,24 @@
804 1357 public function process_admin_edit_friend_rules() {
805 1358 $friend = $this->check_admin_edit_friend_rules();
806 1359 $arg = 'updated';
807 1360 $arg_value = 1;
808 - if ( isset( $_POST['friend-rules-raw'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'friend-rules-raw-' . $friend->user_login ) ) {
809 - $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'] ) );
810 1363 if ( false === $rules ) {
811 1364 $arg = 'error';
812 1365 } else {
813 1366 $friend->update_user_option( 'friends_feed_rules', $rules );
814 1367 }
815 - } elseif ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-rules-' . $friend->user_login ) ) {
816 - $friend->update_user_option( 'friends_feed_catch_all', $this->friends->feed->validate_feed_catch_all( $_POST['catch_all'] ) );
817 - $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 + );
818 1377 } else {
819 1378 return;
820 1379 }
821 1380
@@ -821,9 +1380,9 @@
821 1380
822 1381 if ( isset( $_GET['_wp_http_referer'] ) ) {
823 1382 wp_safe_redirect( wp_get_referer() );
824 1383 } else {
825 - 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' ) ) );
826 1385 }
827 1386 exit;
828 1387 }
829 1388
@@ -854,12 +1413,14 @@
854 1413 'action' => in_array( $catch_all, array( 'trash', 'delete' ), true ) ? 'accept' : 'trash',
855 1414 'replace' => '',
856 1415 );
857 1416
858 - if ( isset( $_GET['post'] ) && intval( $_GET['post'] ) ) {
859 - $post = get_post( intval( $_GET['post'] ) );
860 - } else {
861 - $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 + }
862 1423 }
863 1424
864 1425 $args = array(
865 1426 'rules' => $rules,
@@ -883,16 +1444,28 @@
883 1444 public function ajax_preview_friend_rules() {
884 1445 if ( ! Friends::has_required_privileges() ) {
885 1446 wp_die( -1 );
886 1447 }
1448 + if ( ! isset( $_GET['user'] ) ) {
1449 + wp_die( esc_html__( 'Invalid user.', 'friends' ) );
1450 + }
887 1451
1452 + check_ajax_referer( 'edit-friend-rules-' . sanitize_user( wp_unslash( $_GET['user'] ) ) );
1453 +
888 1454 if ( isset( $_GET['post'] ) && intval( $_GET['post'] ) ) {
889 1455 $post = get_post( intval( $_GET['post'] ) );
890 1456 } else {
891 1457 $post = null;
892 1458 }
893 -
894 - $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 );
895 1468 wp_die( 1 );
896 1469 }
897 1470
898 1471 /**
@@ -901,123 +1474,24 @@
901 1474 public function ajax_fetch_feeds() {
902 1475 if ( ! isset( $_POST['friend'] ) ) {
903 1476 wp_send_json_error( 'missing-parameters' );
904 1477 }
905 - check_ajax_referer( 'fetch-feeds-' . $_POST['friend'] );
906 1478
907 - $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'] ) ) );
908 1482 if ( ! $friend_user ) {
909 1483 wp_send_json_error( 'unknown-user' );
910 1484 }
911 1485
1486 + add_filter( 'notify_about_new_friend_post', '__return_false', 999 );
1487 +
912 1488 $friend_user->retrieve_posts_from_active_feeds();
913 1489
914 1490 wp_send_json_success();
915 1491 }
916 1492
917 -
918 1493 /**
919 - * Respond to the Ajax request to refresh the link token
920 - */
921 - public function ajax_refresh_link_token() {
922 - if ( ! isset( $_POST['url'] ) || ! isset( $_POST['friend'] ) ) {
923 - wp_send_json_error( 'missing-parameters' );
924 - }
925 - $url = $_POST['url'];
926 - check_ajax_referer( 'auth-link-' . $url );
927 -
928 - if ( ! friends::has_required_privileges() ) {
929 - wp_send_json_error( 'missing-priviledges' );
930 - }
931 -
932 - $friend_user = User::get_user( $_POST['friend'] );
933 - if ( ! $friend_user ) {
934 - wp_send_json_error( 'unknown-user' );
935 - }
936 -
937 - wp_send_json_success(
938 - array(
939 - 'success' => true,
940 - 'data' => array(
941 - 'token' => $friend_user->get_friend_auth(),
942 - ),
943 - )
944 - );
945 - }
946 -
947 - public function render_friends_list() {
948 - Friends::template_loader()->get_template_part(
949 - 'admin/settings-header',
950 - null,
951 - array(
952 - 'menu' => array(
953 - __( 'Your Friends & Subscriptions', 'friends' ) => 'friends-list',
954 - __( 'Your Friend Requests', 'friends' ) => 'friends-list-requests',
955 - ),
956 - 'active' => $_GET['page'],
957 - 'title' => __( 'Friends', 'friends' ),
958 - )
959 - );
960 -
961 - if ( isset( $_GET['page'] ) && 'friends-list-requests' === $_GET['page'] ) {
962 - echo '<p>';
963 - echo wp_kses(
964 - sprintf(
965 - // translators: %1$s is a URL, %2$s is the translated text "Your Friends & Subscriptions".
966 - __( 'These are your current friend requests. To see all your friends and subscriptions, go to <a href="%1$s">%2$s</a>.', 'friends' ),
967 - self_admin_url( 'admin.php?page=friends-list' ),
968 - __( 'Your Friends & Subscriptions', 'friends' )
969 - ),
970 - array(
971 - 'a' => array(
972 - 'href' => array(),
973 - ),
974 - )
975 - );
976 - echo '</p>';
977 - $query = User_Query::all_friend_requests();
978 - } else {
979 - $query = User_Query::all_associated_users();
980 - }
981 -
982 - if ( isset( $_GET['deleted'] ) ) {
983 - ?>
984 - <div id="message" class="updated notice is-dismissible"><p>
985 - <?php
986 - echo esc_html(
987 - sprintf(
988 - // translators: % s is a username.
989 - __( '%s was deleted.', 'friends' ),
990 - $_GET['deleted']
991 - )
992 - );
993 - ?>
994 - </p></div>
995 - <?php
996 - } elseif ( isset( $_GET['error'] ) ) {
997 - ?>
998 - <div id="message" class="updated error is-dismissible"><p>
999 - <?php
1000 - esc_html_e( 'An error occurred.', 'friends' );
1001 - echo ' ';
1002 - echo esc_html( $_GET['error'] );
1003 - ?>
1004 - </p></div>
1005 - <?php
1006 - }
1007 -
1008 - Friends::template_loader()->get_template_part(
1009 - 'admin/friends-list',
1010 - null,
1011 - array(
1012 - 'friends' => $query->get_results(),
1013 - )
1014 - );
1015 -
1016 - Friends::template_loader()->get_template_part( 'admin/settings-footer' );
1017 - }
1018 -
1019 - /**
1020 1494 * Render the Friend rules preview
1021 1495 *
1022 1496 * @param array $rules The rules to apply.
1023 1497 * @param string $catch_all The catch all behavior.
@@ -1022,9 +1496,9 @@
1022 1496 * @param array $rules The rules to apply.
1023 1497 * @param string $catch_all The catch all behavior.
1024 1498 * @param \WP_Post $post The post.
1025 1499 */
1026 - 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 ) {
1027 1501 $friend = $this->check_admin_edit_friend_rules();
1028 1502 $friend_posts = new \WP_Query();
1029 1503
1030 1504 $friend_posts->set( 'post_type', Friends::CPT );
@@ -1052,13 +1526,13 @@
1052 1526 if ( ! friends::has_required_privileges() ) {
1053 1527 wp_die( esc_html__( 'Sorry, you are not allowed to edit this user.' ) ); // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1054 1528 }
1055 1529
1056 - if ( ! isset( $_GET['user'] ) ) {
1530 + if ( ! isset( $_GET['user'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
1057 1531 wp_die( esc_html__( 'Invalid user.', 'friends' ) );
1058 1532 }
1059 1533
1060 - $friend = User::get_by_username( $_GET['user'] );
1534 + $friend = User::get_by_username( sanitize_user( wp_unslash( $_GET['user'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification
1061 1535 if ( ! $friend || is_wp_error( $friend ) ) {
1062 1536 wp_die( esc_html__( 'Invalid username.', 'friends' ) );
1063 1537 }
1064 1538
@@ -1076,95 +1550,40 @@
1076 1550 $friend = $this->check_admin_edit_friend();
1077 1551 $arg = 'updated';
1078 1552 $arg_value = 1;
1079 1553
1080 - if ( isset( $_GET['convert-to-user'] ) && wp_verify_nonce( $_GET['convert-to-user'], 'convert-to-user-' . $friend->user_login ) ) {
1081 - if ( $friend instanceof Subscription ) {
1082 - Subscription::convert_to_user( $friend );
1083 - }
1084 - } elseif ( isset( $_GET['convert-from-user'] ) && wp_verify_nonce( $_GET['convert-from-user'], 'convert-from-user-' . $friend->user_login ) ) {
1085 - if ( $friend instanceof User && ! $friend instanceof Subscription ) {
1086 - if ( $friend->has_cap( 'friends_plugin' ) && ! $friend->has_cap( 'friend' ) && ! $friend->has_cap( 'pending_friend_request' ) && ! $friend->has_cap( 'friend_request' ) ) {
1087 - Subscription::convert_from_user( $friend );
1088 - } else {
1089 - $arg = 'error';
1090 - $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;
1091 1560 }
1092 1561 }
1093 - } elseif ( isset( $_GET['accept-friend-request'] ) && wp_verify_nonce( $_GET['accept-friend-request'], 'accept-friend-request-' . $friend->user_login ) ) {
1094 - if ( $friend->has_cap( 'friend_request' ) ) {
1095 - $friend->set_role( get_option( 'friends_default_friend_role', 'friend' ) );
1096 - $arg = 'friend';
1562 + if ( isset( $_POST['friends_description'] ) ) {
1563 + $friend->description = trim( sanitize_text_field( wp_unslash( $_POST['friends_description'] ) ) );
1097 1564 }
1098 - } elseif ( isset( $_GET['add-friend'] ) && wp_verify_nonce( $_GET['add-friend'], 'add-friend-' . $friend->user_login ) ) {
1099 - if ( $friend->has_cap( 'pending_friend_request' ) || $friend->has_cap( 'subscription' ) ) {
1100 - $rest_url = $this->friends->rest->discover_rest_url( $friend->user_url );
1101 - if ( ! is_wp_error( $rest_url ) ) {
1102 - $response = $this->send_friend_request( $rest_url, $friend->user_login, $friend->user_url, $friend->display_name );
1103 - } else {
1104 - $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;
1105 1569 }
1106 -
1107 - if ( is_wp_error( $response ) ) {
1108 - $arg = 'error';
1109 - } elseif ( $response instanceof User ) {
1110 - if ( $response->has_cap( 'pending_friend_request' ) ) {
1111 - $arg = 'sent-request';
1112 - // translators: %s is a Site URL.
1113 - $arg_value = wp_kses( sprintf( __( 'Friendship requested for site %s.', 'friends' ), $response->get_local_friends_page_url() ), array( 'a' => array( 'href' => array() ) ) );
1114 - } elseif ( $response->has_cap( 'friend' ) ) {
1115 - $arg = 'friend';
1116 - $arg_value = 1;
1117 - } elseif ( $response->has_cap( 'subscription' ) ) {
1118 - $arg = 'subscribed';
1119 - $arg_value = 1;
1120 - }
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 );
1121 1575 }
1122 1576 }
1123 - } elseif ( isset( $_GET['change-to-restricted-friend'] ) && wp_verify_nonce( $_GET['change-to-restricted-friend'], 'change-to-restricted-friend-' . $friend->user_login ) ) {
1124 - if ( $friend->has_cap( 'friend' ) ) {
1125 - $friend->set_role( 'acquaintance' );
1126 - }
1127 - } elseif ( isset( $_GET['change-to-friend'] ) && wp_verify_nonce( $_GET['change-to-friend'], 'change-to-friend-' . $friend->user_login ) ) {
1128 - if ( $friend->has_cap( 'acquaintance' ) ) {
1129 - $friend->set_role( 'friend' );
1130 - }
1131 - } elseif ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'edit-friend-' . $friend->user_login ) ) {
1132 - if ( trim( $_POST['friends_display_name'] ) ) {
1133 - $friend->first_name = trim( $_POST['friends_display_name'] );
1134 - $friend->display_name = trim( $_POST['friends_display_name'] );
1135 - }
1136 -
1137 - $friend->description = trim( $_POST['friends_description'] );
1138 - if ( trim( $_POST['user_url'] ) && filter_var( $_POST['user_url'], FILTER_VALIDATE_URL ) ) {
1139 - $friend->user_url = $_POST['user_url'];
1140 - }
1141 1577 $friend->save();
1142 -
1143 - $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
1144 - if ( ! $hide_from_friends_page ) {
1145 - $hide_from_friends_page = array();
1146 - }
1147 - if ( ! isset( $_POST['show_on_friends_page'] ) || ! $_POST['show_on_friends_page'] ) {
1148 - if ( ! in_array( $friend->user_login, $hide_from_friends_page ) ) {
1149 - $hide_from_friends_page[] = $friend->user_login;
1150 - update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1151 - }
1152 - } else {
1153 - if ( in_array( $friend->user_login, $hide_from_friends_page ) ) {
1154 - $hide_from_friends_page = array_values( array_diff( $hide_from_friends_page, array( $friend->user_login ) ) );
1155 - update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1156 - }
1157 - }
1158 1578 } else {
1159 1579 return;
1160 1580 }
1161 1581
1162 - if ( isset( $_GET['_wp_http_referer'] ) ) {
1163 - wp_safe_redirect( add_query_arg( $arg, $arg_value, wp_get_referer() ) );
1164 - } else {
1165 - wp_safe_redirect( add_query_arg( $arg, $arg_value, remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) );
1166 - }
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 ) );
1167 1586 exit;
1168 1587 }
1169 1588
1170 1589 /**
@@ -1173,9 +1592,9 @@
1173 1592 * @param User $friend The friend.
1174 1593 * @param string $active The active menu entry.
1175 1594 */
1176 1595 public function header_edit_friend( User $friend, $active ) {
1177 - $append = '&user=' . $friend->user_login;
1596 + $append = '&user=' . sanitize_user( $friend->user_login );
1178 1597 Friends::template_loader()->get_template_part(
1179 1598 'admin/settings-header',
1180 1599 null,
1181 1600 array(
@@ -1181,12 +1600,13 @@
1181 1600 array(
1182 1601 'active' => $active . $append,
1183 1602 'title' => $friend->user_login,
1184 1603 'menu' => array(
1185 - 'Friend Settings' => 'edit-friend' . $append,
1186 - 'Feeds' => 'edit-friend-feeds' . $append,
1187 - 'Notifications' => 'edit-friend-notifications' . $append,
1188 - '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 ),
1189 1609 ),
1190 1610 )
1191 1611 );
1192 1612 }
@@ -1199,20 +1619,16 @@
1199 1619
1200 1620 $args = array_merge(
1201 1621 $friend->get_post_stats(),
1202 1622 array(
1203 - 'friend' => $friend,
1204 - 'friends_settings_url' => add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=friends-settings' ) ),
1205 - 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1206 - '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(),
1207 1626 )
1208 1627 );
1209 - if ( ! $args['hide_from_friends_page'] ) {
1210 - $args['hide_from_friends_page'] = array();
1211 - }
1212 1628
1213 1629 $this->header_edit_friend( $friend, 'edit-friend' );
1214 -
1630 + // phpcs:disable WordPress.Security.NonceVerification
1215 1631 if ( isset( $_GET['updated'] ) ) {
1216 1632 ?>
1217 1633 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'User was updated.', 'friends' ); ?></p></div>
1218 1634 <?php
@@ -1221,10 +1637,18 @@
1221 1637 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'You are now friends.', 'friends' ); ?></p></div>
1222 1638 <?php
1223 1639 } elseif ( isset( $_GET['error'] ) ) {
1224 1640 ?>
1225 - <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>
1226 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
1227 1651 } elseif ( isset( $_GET['sent-request'] ) ) {
1228 1652 ?>
1229 1653 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Your request was sent.', 'friends' ); ?></p></div>
1230 1654 <?php
@@ -1232,33 +1656,363 @@
1232 1656 ?>
1233 1657 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Subscription activated.', 'friends' ); ?></p></div>
1234 1658 <?php
1235 1659 }
1660 + // phpcs:enable WordPress.Security.NonceVerification
1236 1661
1237 1662 Friends::template_loader()->get_template_part( 'admin/edit-friend', null, $args );
1238 1663 }
1239 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 +
1240 1986 public function ajax_set_avatar() {
1241 - $user_id = isset( $_POST['user'] ) ? $_POST['user'] : 0;
1987 + if ( ! isset( $_POST['user'] ) ) {
1988 + wp_send_json_error( __( 'No user specified.', 'friends' ) );
1989 + }
1242 1990
1243 - check_ajax_referer( "set-avatar-$user_id" );
1991 + check_ajax_referer( 'set-avatar-' . sanitize_user( wp_unslash( $_POST['user'] ) ) );
1244 1992
1245 1993 if ( ! current_user_can( Friends::REQUIRED_ROLE ) ) {
1246 1994 wp_send_json_error();
1247 1995 exit;
1248 1996 }
1249 -
1250 - if ( empty( $_POST['avatar'] ) || ! Friends::check_url( $_POST['avatar'] ) ) {
1997 + if ( empty( $_POST['avatar'] ) ) {
1251 1998 wp_send_json_error();
1252 1999 exit;
1253 2000 }
2001 + $avatar = check_url( wp_unslash( $_POST['avatar'] ) );
2002 + if ( empty( $avatar ) ) {
2003 + wp_send_json_error();
2004 + exit;
2005 + }
1254 2006
1255 - $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 + }
1256 2012
1257 - $image = file_get_contents( $_POST['avatar'] );
1258 -
1259 2013 // Use WordPress functions to check the image dimensions.
1260 - $size = \wp_getimagesize( $_POST['avatar'] );
2014 + $size = \wp_getimagesize( $avatar );
1261 2015 if ( ! $size ) {
1262 2016 wp_send_json_error( __( 'Image is in an unknown format.', 'friends' ) );
1263 2017 exit;
1264 2018 }
@@ -1267,9 +2021,9 @@
1267 2021 wp_send_json_error( __( 'Image must be square and not larger than 512x512.', 'friends' ) );
1268 2022 exit;
1269 2023 }
1270 2024
1271 - $url = $friend->update_user_icon_url( $_POST['avatar'] );
2025 + $url = $friend->update_user_icon_url( $avatar );
1272 2026
1273 2027 if ( ! $url || is_wp_error( $url ) ) {
1274 2028 wp_send_json_error( $url );
1275 2029 exit;
@@ -1289,11 +2043,11 @@
1289 2043 $friend = $this->check_admin_edit_friend();
1290 2044 $arg = 'updated';
1291 2045 $arg_value = 1;
1292 2046
1293 - 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 ) ) {
1294 2048 if ( ! get_user_option( 'friends_no_new_post_notification' ) ) {
1295 - 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'] ) ) {
1296 2050 delete_user_option( get_current_user_id(), 'friends_no_new_post_notification_' . $friend->user_login );
1297 2051 } else {
1298 2052 update_user_option( get_current_user_id(), 'friends_no_new_post_notification_' . $friend->user_login, 1 );
1299 2053 }
@@ -1299,9 +2053,9 @@
1299 2053 }
1300 2054 }
1301 2055
1302 2056 if ( ! get_user_option( 'friends_no_keyword_notification' ) ) {
1303 - if ( isset( $_POST['friends_keyword_notification'] ) && $_POST['friends_keyword_notification'] ) {
2057 + if ( isset( $_POST['friends_keyword_notification'] ) && boolval( $_POST['friends_keyword_notification'] ) ) {
1304 2058 delete_user_option( get_current_user_id(), 'friends_no_keyword_notification_' . $friend->user_login );
1305 2059 } else {
1306 2060 update_user_option( get_current_user_id(), 'friends_no_keyword_notification_' . $friend->user_login, 1 );
1307 2061 }
@@ -1314,9 +2068,9 @@
1314 2068
1315 2069 if ( isset( $_GET['_wp_http_referer'] ) ) {
1316 2070 wp_safe_redirect( wp_get_referer() );
1317 2071 } else {
1318 - 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' ) ) ) );
1319 2073 }
1320 2074 exit;
1321 2075 }
1322 2076
@@ -1328,8 +2082,9 @@
1328 2082 $post_stats = $friend->get_post_stats();
1329 2083
1330 2084 $this->header_edit_friend( $friend, 'edit-friend-notifications' );
1331 2085
2086 + // phpcs:disable WordPress.Security.NonceVerification
1332 2087 if ( isset( $_GET['updated'] ) ) {
1333 2088 ?>
1334 2089 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Notification Settings were updated.', 'friends' ); ?></p></div>
1335 2090 <?php
@@ -1337,8 +2092,9 @@
1337 2092 ?>
1338 2093 <div id="message" class="updated error is-dismissible"><p><?php esc_html_e( 'An error occurred.', 'friends' ); ?></p></div>
1339 2094 <?php
1340 2095 }
2096 + // phpcs:enable WordPress.Security.NonceVerification
1341 2097
1342 2098 Friends::template_loader()->get_template_part(
1343 2099 'admin/edit-notifications',
1344 2100 null,
@@ -1355,30 +2111,28 @@
1355 2111 $friend = $this->check_admin_edit_friend();
1356 2112 $arg = 'updated';
1357 2113 $arg_value = 1;
1358 2114
1359 - 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 ) ) {
1360 2116 $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
1361 2117 if ( ! $hide_from_friends_page ) {
1362 2118 $hide_from_friends_page = array();
1363 2119 }
1364 - 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'] ) ) {
1365 2121 if ( ! in_array( $friend->user_login, $hide_from_friends_page ) ) {
1366 2122 $hide_from_friends_page[] = $friend->user_login;
1367 2123 update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1368 2124 }
1369 - } else {
1370 - if ( in_array( $friend->user_login, $hide_from_friends_page ) ) {
2125 + } elseif ( in_array( $friend->user_login, $hide_from_friends_page ) ) {
1371 2126 $hide_from_friends_page = array_values( array_diff( $hide_from_friends_page, array( $friend->user_login ) ) );
1372 2127 update_user_option( get_current_user_id(), 'friends_hide_from_friends_page', $hide_from_friends_page );
1373 - }
1374 2128 }
1375 2129
1376 - if ( $friend->set_retention_number_enabled( isset( $_POST['friends_enable_retention_number'] ) && $_POST['friends_enable_retention_number'] ) ) {
1377 - $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 ) );
1378 2132 }
1379 - if ( $friend->set_retention_days_enabled( isset( $_POST['friends_enable_retention_days'] ) && $_POST['friends_enable_retention_days'] ) ) {
1380 - $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 ) );
1381 2135 }
1382 2136
1383 2137 $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
1384 2138 if ( ! $hide_from_friends_page ) {
@@ -1384,32 +2138,61 @@
1384 2138 if ( ! $hide_from_friends_page ) {
1385 2139 $hide_from_friends_page = array();
1386 2140 }
1387 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 +
1388 2163 if ( isset( $_POST['feeds'] ) ) {
2164 + // Sanitized below.
2165 + $feeds = wp_unslash( $_POST['feeds'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1389 2166 $existing_feeds = $friend->get_feeds();
1390 - if ( '' === trim( $_POST['feeds']['new']['url'] ) ) {
1391 - unset( $_POST['feeds']['new'] );
1392 - } else {
1393 - foreach ( $existing_feeds as $term_id => $user_feed ) {
1394 - if ( $user_feed->get_url() === trim( $_POST['feeds']['new']['url'] ) ) {
1395 - if ( isset( $_POST['feeds'][ $term_id ] ) ) {
1396 - // Let a newly entered feed overrule an existing one.
1397 - $_POST['feeds'][ $term_id ] = array_merge( $_POST['feeds'][ $term_id ], $_POST['feeds']['new'] );
1398 - $_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;
1399 2180 }
1400 - unset( $_POST['feeds']['new'] );
1401 - break;
1402 2181 }
1403 2182 }
1404 2183 }
1405 - foreach ( $_POST['feeds'] as $term_id => $feed ) {
2184 + foreach ( $feeds as $term_id => $feed ) {
1406 2185 if ( 'new' === $term_id ) {
1407 - if ( '' === trim( $feed['url'] ) ) {
2186 + if ( ! isset( $feed['url'] ) || '' === trim( $feed['url'] ) ) {
1408 2187 continue;
1409 2188 }
1410 2189
1411 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 + }
1412 2195 $new_feed = $friend->subscribe( $feed['url'], $feed );
1413 2196 if ( is_wp_error( $new_feed ) ) {
1414 2197 do_action( 'friends_process_feed_item_submit_error', $new_feed, $feed );
1415 2198 continue;
@@ -1425,8 +2208,13 @@
1425 2208 }
1426 2209 $user_feed = $existing_feeds[ $term_id ];
1427 2210 unset( $existing_feeds[ $term_id ] );
1428 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 +
1429 2217 if ( $user_feed->get_url() !== $feed['url'] ) {
1430 2218 do_action( 'friends_user_feed_deactivated', $user_feed );
1431 2219
1432 2220 if ( ! isset( $feed['mime-type'] ) ) {
@@ -1434,9 +2222,11 @@
1434 2222 }
1435 2223
1436 2224 if ( $feed['active'] ) {
1437 2225 $new_feed = $friend->subscribe( $feed['url'], $feed );
1438 - 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 + }
1439 2229 } else {
1440 2230 $new_feed = $friend->save_feed( $feed['url'], $feed );
1441 2231 }
1442 2232
@@ -1486,11 +2276,10 @@
1486 2276 foreach ( $existing_feeds as $term_id => $user_feed ) {
1487 2277 do_action( 'friends_user_feed_deactivated', $user_feed );
1488 2278 $user_feed->delete();
1489 2279 }
1490 -
1491 - do_action( 'friends_edit_friend_after_form_submit', $friend );
1492 2280 }
2281 + do_action( 'friends_edit_feeds_after_form_submit', $friend );
1493 2282 } else {
1494 2283 return;
1495 2284 }
1496 2285
@@ -1496,9 +2285,9 @@
1496 2285
1497 2286 if ( isset( $_GET['_wp_http_referer'] ) ) {
1498 2287 wp_safe_redirect( wp_get_referer() );
1499 2288 } else {
1500 - 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' ) ) ) );
1501 2290 }
1502 2291 exit;
1503 2292 }
1504 2293
@@ -1507,8 +2296,21 @@
1507 2296 */
1508 2297 public function render_admin_edit_friend_feeds() {
1509 2298 $friend = $this->check_admin_edit_friend();
1510 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 +
1511 2313 $args = array_merge(
1512 2314 $friend->get_post_stats(),
1513 2315 array(
1514 2316 'friend' => $friend,
@@ -1514,14 +2316,15 @@
1514 2316 'friend' => $friend,
1515 2317 'rules' => $friend->get_feed_rules(),
1516 2318 'hide_from_friends_page' => get_user_option( 'friends_hide_from_friends_page' ),
1517 2319 'post_formats' => array_merge( array( 'autodetect' => __( 'Autodetect Post Format', 'friends' ) ), get_post_format_strings() ),
1518 - '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' ) ),
1519 2321 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1520 2322 'global_retention_days' => Friends::get_retention_days(),
1521 2323 'global_retention_number' => Friends::get_retention_number(),
1522 2324 'global_retention_days_enabled' => get_option( 'friends_enable_retention_days' ),
1523 2325 'global_retention_number_enabled' => get_option( 'friends_enable_retention_number' ),
2326 + 'show_on_dashboard' => $already_on_dashboard,
1524 2327 )
1525 2328 );
1526 2329 if ( ! $args['hide_from_friends_page'] ) {
1527 2330 $args['hide_from_friends_page'] = array();
@@ -1527,8 +2330,9 @@
1527 2330 $args['hide_from_friends_page'] = array();
1528 2331 }
1529 2332 $this->header_edit_friend( $friend, 'edit-friend-feeds' );
1530 2333
2334 + // phpcs:disable WordPress.Security.NonceVerification
1531 2335 if ( isset( $_GET['updated'] ) ) {
1532 2336 ?>
1533 2337 <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Feeds were updated.', 'friends' ); ?></p></div>
1534 2338 <?php
@@ -1536,8 +2340,9 @@
1536 2340 ?>
1537 2341 <div id="message" class="updated error is-dismissible"><p><?php esc_html_e( 'An error occurred.', 'friends' ); ?></p></div>
1538 2342 <?php
1539 2343 }
2344 + // phpcs:enable WordPress.Security.NonceVerification
1540 2345
1541 2346 Friends::template_loader()->get_template_part( 'admin/edit-feeds', null, $args );
1542 2347 }
1543 2348
@@ -1548,9 +2353,9 @@
1548 2353 $friend = $this->check_admin_edit_friend();
1549 2354 $arg = 'deleted';
1550 2355 $arg_value = $friend->user_login;
1551 2356
1552 - 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 ) ) {
1553 2358 $friend->delete();
1554 2359 } else {
1555 2360 return;
1556 2361 }
@@ -1557,9 +2362,9 @@
1557 2362
1558 2363 if ( isset( $_GET['_wp_http_referer'] ) ) {
1559 2364 wp_safe_redirect( wp_get_referer() );
1560 2365 } else {
1561 - 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/' ) ) );
1562 2367 }
1563 2368 exit;
1564 2369 }
1565 2370
@@ -1593,14 +2398,14 @@
1593 2398 <div id="message" class="updated error is-dismissible"><p><?php echo esc_html( $errors->get_error_message() ); ?></p>
1594 2399 <?php
1595 2400 $error_data = $errors->get_error_data();
1596 2401 if ( isset( $error_data->error ) ) {
1597 - $error = unserialize( $error_data->error );
2402 + $error = unserialize( $error_data->error ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
1598 2403 if ( is_wp_error( $error ) ) {
1599 2404 ?>
1600 2405 <pre>
1601 2406 <?php
1602 - print_r( $error );
2407 + print_r( $error ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1603 2408 ?>
1604 2409 </pre>
1605 2410 <?php
1606 2411 } elseif ( is_array( $error ) && isset( $error['body'] ) ) {
@@ -1617,657 +2422,457 @@
1617 2422 </div>
1618 2423 <?php
1619 2424 }
1620 2425
1621 - public function create_and_follow( $user_id, $url, $type, $vars = array() ) {
1622 - $vars['friend_url'] = $url;
2426 + public function create_and_follow( $user_id, $url ) {
2427 + // TODO: replace with frontend functionality.
2428 + }
1623 2429
1624 - $vars['user_login'] = apply_filters( 'friends_suggest_user_login', User::get_user_login_for_url( $url ), $url );
1625 - if ( empty( $vars['display_name'] ) ) {
1626 - $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;
1627 2436 }
1628 2437
1629 - $vars['step2'] = true;
2438 + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_wpnonce'] ), 'notification-manager' ) ) {
2439 + return;
2440 + }
1630 2441
1631 - $vars['subscribe'] = array( $url );
1632 - $vars['feeds'] = $this->friends->feed->discover_available_feeds( $url );
2442 + $this->check_admin_settings();
1633 2443
1634 - ob_start();
1635 - $ret = $this->process_admin_add_friend( $vars );
1636 - 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 + }
1637 2456
1638 - if ( is_wp_error( $ret ) ) {
1639 - 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 );
1640 2461 }
1641 2462
1642 - $friend_user = User::get_by_username( $vars['user_login'] );
1643 - if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1644 - 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 );
1645 2467 }
1646 2468
1647 - return $friend_user->ID;
1648 - }
1649 -
1650 - /**
1651 - * Previous process the Add Friend form. Todo: re-integrate.
1652 - *
1653 - * @param User $friend_user The Friend user.
1654 - * @param array $vars The variables from the admin
1655 - * submission.
1656 - *
1657 - * @return boolean true when there was no error.
1658 - */
1659 - public function process_admin_add_friend_response( $friend_user, $vars ) {
1660 - if ( is_wp_error( $friend_user ) ) {
1661 - $this->display_errors( $friend_user );
1662 - 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 );
1663 2473 }
1664 2474
1665 - if ( ! $friend_user instanceof User ) {
1666 - ?>
1667 - <div id="message" class="updated notice is-dismissible"><p>
1668 - <?php
1669 - // translators: %s is a username.
1670 - esc_html_e( 'Unknown error', 'friends' );
1671 - ?>
1672 - </p></div>
1673 - <?php
1674 - 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 + }
1675 2481 }
1676 2482
1677 - $feed_options = array();
1678 - if ( ! isset( $vars['feeds'] ) ) {
1679 - $vars['feeds'] = array();
1680 - }
1681 - foreach ( $vars['feeds'] as $feed ) {
1682 - if ( isset( $feed['type'] ) ) {
1683 - $feed['mime-type'] = $feed['type'];
1684 - 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 );
1685 2488 }
1686 - $feed_options[ $feed['url'] ] = $feed;
1687 2489 }
1688 2490
1689 - // Save the all feeds for possible later activation.
1690 - $friend_user->save_feeds( $feed_options );
1691 -
1692 - if ( ! isset( $vars['subscribe'] ) ) {
1693 - $vars['subscribe'] = array();
2491 + if ( empty( $_POST['friend_listed'] ) ) {
2492 + return;
1694 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();
1695 2498
1696 - $count = 0;
1697 - foreach ( $vars['subscribe'] as $feed_url ) {
1698 - 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 ) {
1699 2502 continue;
1700 2503 }
1701 - $new_feed = $friend_user->subscribe( $feed_url, $feed_options[ $feed_url ] );
1702 - if ( ! is_wp_error( $new_feed ) ) {
1703 - do_action( 'friends_user_feed_activated', $new_feed );
1704 - $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;
1705 2507 }
1706 - }
1707 2508
1708 - 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 + }
1709 2513
1710 - wp_schedule_single_event( time(), 'friends_retrieve_user_feeds', array( $friend_user->ID ) );
1711 -
1712 - if ( isset( $vars['errors'] ) ) {
1713 - $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 + }
1714 2518 }
1715 2519
1716 - $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>';
1717 - $message = false;
2520 + update_user_option( $current_user_id, 'friends_hide_from_friends_page', $hide_from_friends_page );
1718 2521
1719 - if ( $friend_user->has_cap( 'pending_friend_request' ) ) {
1720 - // translators: %s is a Site URL.
1721 - $message = sprintf( __( 'Friendship requested for site %s.', 'friends' ), $friend_link );
1722 - $message .= ' ' . sprintf( __( 'Until they respond, we have already subscribed you to their updates.', 'friends' ), $friend_link );
1723 - } elseif ( $friend_user->has_cap( 'friend' ) ) {
1724 - // translators: %s is a Site URL.
1725 - $message = sprintf( __( "You're now a friend of site %s.", 'friends' ), $friend_link );
1726 - // translators: %s is the friends page URL.
1727 - } elseif ( $friend_user->has_cap( 'subscription' ) ) {
1728 - if ( isset( $vars['friendship'] ) ) {
1729 - // translators: %s is a Site URL.
1730 - $message = sprintf( __( 'No friends plugin installed at %s.', 'friends' ), $friend_link );
1731 - $message .= ' ' . esc_html__( 'We subscribed you to their updates.', 'friends' );
1732 - } else {
1733 - // translators: %s is a Site URL.
1734 - $message = sprintf( __( "You're now subscribed to %s.", 'friends' ), $friend_link );
1735 - }
1736 - }
2522 + do_action( 'friends_notification_manager_after_form_submit', $friend_usernames );
1737 2523
1738 - if ( $message ) {
1739 - ?>
1740 - <div id="message" class="updated notice is-dismissible"><p>
1741 - <?php
1742 - echo wp_kses( $message, array( 'a' => array( 'href' => array() ) ) );
1743 - // translators: %s is the friends page URL.
1744 - 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() ) ) );
1745 - 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>';
1746 - ?>
1747 - </p></div>
1748 - <?php
1749 - 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' ) ) ) );
1750 2528 }
1751 -
1752 - ?>
1753 - <div id="message" class="updated notice is-dismissible"><p>
1754 - <?php
1755 - // translators: %s is a username.
1756 - echo esc_html( sprintf( __( 'User %s could not be assigned the appropriate role.', 'friends' ), $friend_user->display_name ) );
1757 - ?>
1758 - </p></div>
1759 - <?php
1760 - return false;
2529 + exit;
1761 2530 }
1762 2531
1763 2532 /**
1764 - * Process the Add Friend form.
1765 - *
1766 - * @param array $vars The POST or GET variables.
1767 - *
1768 - * @return boolean A \WP_Error or void.
2533 + * Render the admin notification manager.
1769 2534 */
1770 - public function process_admin_add_friend( $vars ) {
1771 - $errors = new \WP_Error();
1772 - $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();
1773 2545
1774 - $friend_url = isset( $vars['friend_url'] ) ? trim( $vars['friend_url'] ) : '';
1775 - $codeword = isset( $vars['codeword'] ) ? trim( $vars['codeword'] ) : '';
1776 - $message = isset( $vars['message'] ) ? trim( $vars['message'] ) : '';
2546 + $friend_users = User_Query::all_subscriptions();
1777 2547
1778 - $friends_plugin = false;
1779 - $friend_user = false;
1780 -
1781 - $protocol = wp_parse_url( $friend_url, PHP_URL_SCHEME );
1782 - if ( ! $protocol ) {
1783 - // Allow adding a friend by username.
1784 - if ( is_multisite() ) {
1785 - $friend_user = get_user_by( 'login', $friend_url );
1786 - if ( $friend_user ) {
1787 - $site = get_active_blog_for_user( $friend_user->ID );
1788 - // Ensure we're using the same URL protocol.
1789 - $friend_url = set_url_scheme( $site->siteurl );
1790 - }
1791 - }
1792 -
1793 - // If unsuccessful, then the protocol was forgotten.
1794 - if ( ! $friend_user ) {
1795 - $friend_url = apply_filters( 'friends_rewrite_incoming_url', 'https://' . $friend_url, $friend_url );
1796 - }
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();
1797 2551 }
1798 - $friend_user_login = apply_filters( 'friends_suggest_user_login', User::get_user_login_for_url( $friend_url ), $friend_url );
1799 - $friend_display_name = apply_filters( 'friends_suggest_display_name', User::get_display_name_for_url( $friend_url ), $friend_url );
1800 2552
1801 - $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 + );
1802 2564
1803 - if ( $friend_user ) {
1804 - $args['friends_multisite_user_login'] = $friend_user_login;
1805 - $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' );
1806 2567 }
1807 - $rest_url = false;
1808 2568
1809 - if ( ( isset( $vars['step2'] ) && isset( $vars['feeds'] ) && is_array( $vars['feeds'] ) ) || isset( $vars['step3'] ) ) {
1810 - $friend_user_login = str_replace( ' ', '-', sanitize_user( $vars['user_login'] ) );
1811 - $friend_display_name = sanitize_text_field( $vars['display_name'] );
1812 - if ( ! $friend_user_login ) {
1813 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1814 - $errors->add( 'user_login', __( '<strong>Error</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
1815 - } elseif ( ! is_multisite() && username_exists( $friend_user_login ) ) {
1816 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
1817 - $errors->add( 'user_login', __( '<strong>Error</strong>: This username is already registered. Please choose another one.' ) );
1818 - }
2569 + Friends::template_loader()->get_template_part(
2570 + 'admin/notification-manager',
2571 + null,
2572 + $args
2573 + );
1819 2574
1820 - $feeds = $vars['feeds'];
1821 - if ( ! $errors->has_errors() ) {
1822 - $friend_user = false;
1823 - if ( isset( $vars['friendship'] ) ) {
1824 - $friend_user = $this->send_friend_request( $vars['friendship'], $friend_user_login, $friend_url, $friend_display_name, $codeword, $message );
1825 - if ( $friend_user->has_errors() ) {
1826 - $vars['errors'] = $friend_user;
1827 - }
1828 - }
2575 + Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2576 + }
1829 2577
1830 - $avatar = null;
1831 - $description = null;
1832 - foreach ( $feeds as $feed_details ) {
1833 - if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
1834 - $avatar = $feed_details['avatar'];
1835 - }
1836 - if ( ! $description && ! empty( $feed_details['description'] ) ) {
1837 - $description = $feed_details['description'];
1838 - }
1839 - }
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();
1840 2588
1841 - if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1842 - $friend_user = User::create( $friend_user_login, 'subscription', $friend_url, $friend_display_name, $avatar, $description );
1843 - }
2589 + ?>
2590 + <h1><?php esc_html_e( 'Import/Export', 'friends' ); ?></h1>
2591 + <?php
1844 2592
1845 - return $this->process_admin_add_friend_response( $friend_user, $vars );
1846 - }
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 + );
1847 2600
1848 - if ( isset( $vars['friendship'] ) ) {
1849 - $rest_url = $vars['friendship'];
1850 - } else {
1851 - $rest_url = $this->friends->rest->get_friends_rest_url( $feeds );
1852 - }
1853 - } else {
1854 - if ( home_url() === trailingslashit( $friend_url ) ) {
1855 - return new \WP_Error( 'friend-yourself', __( 'It seems like you sent a friend request to yourself.', 'friends' ) );
1856 - }
2601 + Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2602 + }
1857 2603
1858 - $friend_user = User::get_user( $friend_user_login );
1859 - if ( $friend_user && ! is_wp_error( $friend_user ) ) {
1860 - if ( $friend_user->is_valid_friend() ) {
1861 - return new \WP_Error( 'already-friend', __( 'You are already friends with this site.', 'friends' ) );
1862 - }
2604 + public function process_admin_import_export() {
2605 + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'friends-settings' ) ) {
2606 + return;
2607 + }
1863 2608
1864 - // translators: %s is the name of a friend / site.
1865 - 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>' ) );
1866 - }
2609 + if ( ! Friends::has_required_privileges() ) {
2610 + return;
2611 + }
1867 2612
1868 - $feeds = $this->friends->feed->discover_available_feeds( $friend_url );
1869 - if ( is_wp_error( $feeds ) ) {
1870 - 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 );
1871 2620 }
1872 - if ( ! $feeds ) {
1873 - return new \WP_Error( 'no-feed-found', __( 'No suitable feed was found at the provided address.', 'friends' ) );
1874 - }
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 + }
1875 2647
1876 - $better_display_name = User::get_display_name_from_feeds( $feeds );
1877 - if ( $better_display_name ) {
1878 - $friend_display_name = $better_display_name;
1879 - }
2648 + public function process_admin_duplicate_remover() {
2649 + $friend = $this->check_admin_duplicate_remover();
1880 2650
1881 - $rest_url = $this->friends->rest->get_friends_rest_url( $feeds );
1882 - }
2651 + // Nonce verification done in check_admin_duplicate_remover.
2652 + // phpcs:disable WordPress.Security.NonceVerification.Missing
1883 2653
1884 - if ( $rest_url ) {
1885 - $friends_plugin = $rest_url;
1886 - 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;
1887 2658 }
1888 2659
1889 - if ( isset( $vars['quick-subscribe'] ) ) {
1890 - $vars['feeds'] = $feeds;
1891 - $vars['subscribe'] = array();
1892 - foreach ( $feeds as $feed_url => $details ) {
1893 - if ( isset( $details['autoselect'] ) && $details['autoselect'] ) {
1894 - $vars['subscribe'][] = $feed_url;
1895 - }
2660 + $deleted = 0;
2661 + foreach ( array_keys( wp_unslash( $_POST['deleteduplicate'] ) ) as $_id ) {
2662 + if ( ! is_numeric( $_id ) ) {
2663 + continue;
1896 2664 }
1897 2665
1898 - $friend_user = false;
1899 - if ( isset( $rest_url ) ) {
1900 - $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;
1901 2668 }
2669 + }
2670 + // phpcs:enable WordPress.Security.NonceVerification.Missing
2671 + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1902 2672
1903 - $avatar = null;
1904 - $description = null;
1905 - foreach ( $feeds as $feed_details ) {
1906 - if ( ! $avatar && ! empty( $feed_details['avatar'] ) ) {
1907 - $avatar = $feed_details['avatar'];
1908 - }
1909 - if ( ! $description && ! empty( $feed_details['description'] ) ) {
1910 - $description = $feed_details['description'];
1911 - }
1912 - }
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 + }
1913 2682
1914 - if ( ! $friend_user || is_wp_error( $friend_user ) ) {
1915 - $friend_user = User::create( $friend_user_login, 'subscription', $friend_url, $friend_display_name, $avatar, $description );
1916 - }
2683 + if ( ! isset( $_GET['user'] ) ) {
2684 + wp_die( esc_html__( 'Invalid user.', 'friends' ) );
2685 + }
1917 2686
1918 - 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' ) );
1919 2689 }
1920 2690
1921 - Friends::template_loader()->get_template_part(
1922 - 'admin/settings-header',
1923 - null,
1924 - array(
1925 - 'active' => 'add-friend-confirm',
1926 - 'title' => __( 'Add New Friend', 'friends' ),
1927 - 'menu' => array(
1928 - '1. ' . __( 'Enter Details', 'friends' ) => 'add-friend' . ( isset( $friend_url ) ? '&url=' . urlencode( $friend_url ) : '' ),
1929 - '2. ' . __( 'Confirm', 'friends' ) => 'add-friend-confirm',
1930 - ),
1931 - )
1932 - );
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 + }
1933 2695
1934 - if ( $errors->has_errors() ) {
1935 - ?>
1936 - <div id="message" class="updated notice is-dismissible"><p><?php echo wp_kses( $errors->get_error_message(), array( 'strong' => array() ) ); ?></p>
1937 - </div>
1938 - <?php
2696 + if ( ! $friend->has_cap( 'subscription' ) ) {
2697 + wp_die( esc_html__( 'This is not a user related to this plugin.', 'friends' ) );
1939 2698 }
1940 2699
1941 - Friends::template_loader()->get_template_part(
1942 - 'admin/select-feeds',
1943 - null,
1944 - array_merge(
1945 - $args,
1946 - array(
1947 - 'friends_plugin' => $friends_plugin,
1948 - 'friend_url' => $friend_url,
1949 - 'friend_user_login' => $friend_user_login,
1950 - 'friend_display_name' => $friend_display_name,
1951 - 'friend_roles' => $this->get_friend_roles(),
1952 - 'default_role' => get_option( 'friends_default_friend_role', 'friend' ),
1953 - 'codeword' => $codeword,
1954 - 'message' => $message,
1955 - 'post_formats' => array_merge( array( 'autodetect' => __( 'Autodetect Post Format', 'friends' ) ), get_post_format_strings() ),
1956 - 'registered_parsers' => $this->friends->feed->get_registered_parsers(),
1957 - 'feeds' => $feeds,
1958 - )
1959 - )
1960 - );
2700 + return $friend;
1961 2701 }
1962 -
1963 2702 /**
1964 - * Render the admin form for sending a friend request.
2703 + * Render the duplicates remover
1965 2704 */
1966 - public function render_admin_add_friend() {
1967 - if ( ! friends::has_required_privileges() ) {
1968 - wp_die( esc_html__( 'Sorry, you are not allowed to add friends.', 'friends' ) );
1969 - }
2705 + public function render_admin_duplicate_remover() {
2706 + $friend = $this->check_admin_duplicate_remover();
1970 2707
1971 - if ( ! empty( $_GET['preview'] ) ) {
1972 - $url = $_GET['preview'];
1973 -
2708 + $this->header_edit_friend( $friend, 'duplicate-remover' );
2709 + // phpcs:disable WordPress.Security.NonceVerification
2710 + if ( isset( $_GET['deleted'] ) ) {
1974 2711 ?>
1975 - <h1>
2712 + <div id="message" class="updated notice is-dismissible"><p>
1976 2713 <?php
1977 - // translators: %s is a URL.
1978 - 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 + );
1979 2722 ?>
1980 - </h1>
2723 + </p></div>
1981 2724 <?php
2725 + }
2726 + // phpcs:enable WordPress.Security.NonceVerification
1982 2727
1983 - if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'preview-feed' ) ) {
1984 - ?>
1985 - <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'For security reasons, this preview is not available.', 'friends' ); ?></p>
1986 - </div>
1987 - <?php
1988 - exit;
1989 - }
2728 + $friend_posts = new \WP_Query();
1990 2729
1991 - $parser_name = $this->friends->feed->get_registered_parser( $_GET['parser'] );
1992 - if ( ! $parser_name ) {
1993 - ?>
1994 - <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'An unknown parser name was supplied.', 'friends' ); ?></p>
1995 - </div>
1996 - <?php
1997 - exit;
1998 - }
1999 - ?>
2000 - <h3><?php esc_html_e( 'Parser Details', 'friends' ); ?></h3>
2001 - <ul id="parser">
2002 - <li>
2003 - <?php
2004 - echo wp_kses(
2005 - // translators: %s is the name of a parser, e.g. simplepie.
2006 - sprintf( __( 'Parser: %s', 'friends' ), $parser_name ),
2007 - array(
2008 - 'a' => array(
2009 - 'href' => array(),
2010 - 'rel' => array(),
2011 - 'target' => array(),
2012 - ),
2013 - )
2014 - );
2015 - ?>
2016 - </li>
2017 - </ul>
2018 - <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 );
2019 2734
2020 - <?php
2021 -
2022 - $items = $this->friends->feed->preview( $_GET['parser'], $url, isset( $_GET['feed'] ) ? intval( $_GET['feed'] ) : null );
2023 - if ( is_wp_error( $items ) ) {
2024 - ?>
2025 - <div id="message" class="updated notice is-dismissible"><p><?php echo esc_html( $items->get_error_message() ); ?></p>
2026 - </div>
2027 - <?php
2028 - 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;
2029 2740 }
2030 - ?>
2031 -
2032 - <ul>
2033 - <?php
2034 - foreach ( $items as $item ) {
2035 - $title = $item->title;
2036 - if ( 'status' === $item->post_format ) {
2037 - $title = strip_tags( $item->content );
2038 - }
2039 - ?>
2040 - <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 ); ?>):
2041 - <?php if ( $title ) : ?>
2042 - <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
2043 - <?php else : ?>
2044 - <p>
2045 - <?php
2046 - echo wp_kses(
2047 - wp_trim_excerpt( $item->content ),
2048 - array(
2049 - 'a' => array( 'href' => array() ),
2050 - 'img' => array( 'src' => array() ),
2051 - )
2052 - );
2053 - ?>
2054 - </p>
2055 - <?php endif; ?>
2056 - </li>
2057 - <?php
2058 - }
2059 - ?>
2060 - </ul>
2061 - <?php
2062 - return;
2063 2741 }
2064 2742
2065 - if ( apply_filters( 'friends_debug', false ) && isset( $_GET['next'] ) ) {
2066 - $_POST = $_REQUEST;
2067 - $_POST['_wpnonce'] = wp_create_nonce( 'add-friend' );
2068 - if ( ! empty( $_POST['url'] ) && ! isset( $_POST['friend_url'] ) ) {
2069 - $_POST['friend_url'] = $_POST['url'];
2070 - $parsed_url = parse_url( $_POST['friend_url'] );
2071 - if ( isset( $parsed_url['host'] ) ) {
2072 - if ( ! isset( $parsed_url['scheme'] ) ) {
2073 - $_POST['friend_url'] = 'https://' . ltrim( $_POST['friend_url'], '/' );
2074 - }
2075 - }
2076 - }
2077 - }
2078 -
2079 - $response = null;
2080 - $postdata = apply_filters( 'friends_add_friend_postdata', $_POST );
2081 - if ( ! empty( $postdata ) ) {
2082 - if ( ! wp_verify_nonce( $postdata['_wpnonce'], 'add-friend' ) ) {
2083 - $response = new \WP_Error( 'invalid-nonce', __( 'For security reasons, please verify the URL and click next if you want to proceed.', 'friends' ) );
2084 - } else {
2085 - $response = $this->process_admin_add_friend( $postdata );
2086 - }
2087 - if ( is_wp_error( $response ) ) {
2088 - ?>
2089 - <div id="message" class="updated notice is-dismissible"><p>
2090 - <?php
2091 - $message = $response->get_error_message();
2092 - if ( $response->get_error_data() ) {
2093 - $message .= ' (' . $response->get_error_data() . ')';
2094 - }
2095 - echo wp_kses(
2096 - $message,
2097 - array(
2098 - 'strong' => array(),
2099 - 'a' => array(
2100 - 'href' => array(),
2101 - 'rel' => array(),
2102 - 'target' => array(),
2103 - ),
2104 - )
2105 - );
2106 - ?>
2107 - </p>
2108 - </div>
2109 - <?php
2110 - }
2111 - if ( is_null( $response ) ) {
2112 - return;
2113 - }
2114 - }
2115 -
2116 2743 $args = array(
2117 - 'friend_url' => '',
2118 - '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,
2119 2748 );
2120 2749
2121 - if ( ! empty( $_GET['url'] ) || ! empty( $_POST['url'] ) ) {
2122 - $friend_url = isset( $_GET['url'] ) ? $_GET['url'] : $_POST['url'];
2123 - $parsed_url = parse_url( $friend_url );
2124 - if ( isset( $parsed_url['host'] ) ) {
2125 - if ( ! isset( $parsed_url['scheme'] ) ) {
2126 - $args['friend_url'] = apply_filters( 'friends_rewrite_incoming_url', 'https://' . ltrim( $friend_url, '/' ), $friend_url, $parsed_url );
2127 - } else {
2128 - $args['friend_url'] = $friend_url;
2129 - }
2130 - } elseif ( preg_match( '/^@?' . Feed_Parser_ActivityPub::ACTIVITYPUB_USERNAME_REGEXP . '$/i', $friend_url ) ) {
2131 - $args['friend_url'] = $friend_url;
2132 - }
2133 - }
2750 + Friends::template_loader()->get_template_part( 'admin/duplicates', null, $args );
2751 + }
2134 2752
2135 - Friends::template_loader()->get_template_part(
2136 - 'admin/settings-header',
2137 - null,
2138 - array(
2139 - 'active' => 'add-friend',
2140 - 'title' => __( 'Add New Friend', 'friends' ),
2141 - 'menu' => array(
2142 - '1. ' . __( 'Enter Details', 'friends' ) => 'add-friend' . ( isset( $friend_url ) ? '&url=' . urlencode( $friend_url ) : '' ),
2143 - '2. ' . __( 'Confirm', 'friends' ) => false,
2144 - ),
2145 - )
2146 - );
2147 2753
2148 - 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 + }
2149 2759
2150 - Friends::template_loader()->get_template_part(
2151 - 'admin/latest-friends',
2152 - null,
2153 - array(
2154 - 'friend_requests' => User_Query::recent_friends_subscriptions( 25 )->get_results(),
2155 - )
2156 - );
2157 - Friends::template_loader()->get_template_part( 'admin/settings-footer', null, $args );
2158 - }
2760 + $parts = explode( '-', $key, 3 );
2761 + if ( 3 !== count( $parts ) ) {
2762 + return false;
2763 + }
2159 2764
2160 - /**
2161 - * Process the admin notification manager form submission.
2162 - */
2163 - public function process_admin_notification_manager() {
2765 + $user_id = (int) $parts[1];
2766 + if ( ! $user_id ) {
2767 + return false;
2768 + }
2164 2769
2165 - if ( empty( $_POST ) || empty( $_POST['friend_listed'] ) ) {
2166 - 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;
2167 2773 }
2168 2774
2169 - if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'notification-manager' ) ) {
2170 - return;
2775 + $user = get_user_by( 'ID', $user_id );
2776 + if ( ! $user ) {
2777 + return false;
2171 2778 }
2172 2779
2173 - $this->check_admin_settings();
2174 - $friend_ids = $_POST['friend_listed'];
2175 - $current_user_id = get_current_user_id();
2176 - $hide_from_friends_page = array();
2780 + return $user;
2781 + }
2177 2782
2178 - foreach ( $friend_ids as $friend_id ) {
2179 - if ( ! isset( $_POST['show_on_friends_page'][ $friend_id ] ) ) {
2180 - $hide_from_friends_page[] = $friend_id;
2181 - }
2783 + public static function check_browser_api_key( $key ) {
2784 + return false !== self::get_browser_api_key_user( $key );
2785 + }
2182 2786
2183 - $no_new_post_notification = ! isset( $_POST['new_post_notification'][ $friend_id ] );
2184 - if ( get_user_option( 'friends_no_new_post_notification_' . $friend_id ) !== $no_new_post_notification ) {
2185 - update_user_option( $current_user_id, 'friends_no_new_post_notification_' . $friend_id, $no_new_post_notification );
2186 - }
2187 -
2188 - $no_keyword_notification = ! isset( $_POST['keyword_notification'][ $friend_id ] );
2189 - if ( get_user_option( 'friends_no_keyword_notification_' . $friend_id ) !== $no_keyword_notification ) {
2190 - update_user_option( $current_user_id, 'friends_no_keyword_notification_' . $friend_id, $no_keyword_notification );
2191 - }
2787 + public static function revoke_browser_api_key( $user_id = false ) {
2788 + if ( ! $user_id ) {
2789 + $user_id = get_current_user_id();
2192 2790 }
2193 2791
2194 - 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 + }
2195 2794
2196 - 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 + }
2197 2799
2198 - if ( isset( $_GET['_wp_http_referer'] ) ) {
2199 - wp_safe_redirect( wp_get_referer() );
2200 - } else {
2201 - 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 );
2202 2804 }
2203 - exit;
2805 +
2806 + return $key;
2204 2807 }
2205 2808
2206 - /**
2207 - * Render the admin notification manager.
2208 - */
2209 - 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 + );
2210 2817 Friends::template_loader()->get_template_part(
2211 2818 'admin/settings-header',
2212 2819 null,
2213 2820 array(
2214 - 'active' => 'friends-notification-manager',
2215 - 'title' => __( 'Friends', 'friends' ),
2821 + 'active' => 'friends-browser-extension',
2216 2822 )
2217 2823 );
2218 2824 $this->check_admin_settings();
2825 + $browser_api_key = self::get_browser_api_key();
2219 2826
2220 - ?>
2221 - <h1><?php esc_html_e( 'Notification Manager', 'friends' ); ?></h1>
2222 - <?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 + }
2223 2833
2224 - $friend_users = new User_Query(
2834 + Friends::template_loader()->get_template_part(
2835 + 'admin/browser-extension',
2836 + null,
2225 2837 array(
2226 - 'role__in' => array( 'friend', 'acquaintance', 'pending_friend_request', 'friend_request', 'subscription' ),
2227 - 'orderby' => 'display_name',
2228 - 'order' => 'ASC',
2838 + 'browser-api-key' => $browser_api_key,
2229 2839 )
2230 2840 );
2231 2841
2232 - $hide_from_friends_page = get_user_option( 'friends_hide_from_friends_page' );
2233 - if ( ! $hide_from_friends_page ) {
2234 - $hide_from_friends_page = array();
2235 - }
2842 + Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2843 + }
2236 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 +
2237 2854 Friends::template_loader()->get_template_part(
2238 - 'admin/notification-manager',
2855 + 'admin/settings-header',
2239 2856 null,
2240 2857 array(
2241 - 'friend_users' => $friend_users->get_results(),
2242 - 'friends_settings_url' => add_query_arg( '_wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), self_admin_url( 'admin.php?page=friends-settings' ) ),
2243 - 'hide_from_friends_page' => $hide_from_friends_page,
2244 - 'no_new_post_notification' => get_user_option( 'friends_no_new_post_notification' ),
2245 - 'no_keyword_notification' => get_user_option( 'friends_no_keyword_notification' ),
2246 - 'active_keywords' => Feed::get_active_notification_keywords(),
2858 + 'active' => 'friends-logs',
2247 2859 )
2248 2860 );
2861 + $this->check_admin_settings();
2249 2862
2863 + Friends::template_loader()->get_template_part(
2864 + 'admin/logs',
2865 + null,
2866 + array(
2867 + 'logs' => Logging::get_logs(),
2868 + )
2869 + );
2870 +
2250 2871 Friends::template_loader()->get_template_part( 'admin/settings-footer' );
2251 2872 }
2252 2873
2253 2874 /**
2254 - * Gets the friend roles.
2255 - *
2256 - * @return array The friend roles.
2257 - */
2258 - public function get_friend_roles() {
2259 - $roles = new \WP_Roles();
2260 - $friend_roles = array();
2261 - foreach ( $roles->roles as $role => $data ) {
2262 - if ( isset( $data['capabilities']['friend'] ) ) {
2263 - $friend_roles[ $role ] = $data['name'];
2264 - }
2265 - }
2266 - return $friend_roles;
2267 - }
2268 -
2269 - /**
2270 2875 * Gets the roles associated with the Friends plugin.
2271 2876 *
2272 2877 * @return array The associated roles.
2273 2878 */
@@ -2282,188 +2887,12 @@
2282 2887 return $friend_roles;
2283 2888 }
2284 2889
2285 2890 public static function get_users_url() {
2286 - return 'admin.php?page=friends-list';
2891 + return home_url( '/friends/following/' );
2287 2892 }
2288 2893
2289 2894 /**
2290 - * Add actions to the user rows
2291 - *
2292 - * @param array $actions The existing actions.
2293 - * @param \WP_User $user The user in question.
2294 - * @return array The extended actions.
2295 - */
2296 - public static function user_row_actions( array $actions, \WP_User $user ) {
2297 - if (
2298 - ! Friends::has_required_privileges() ||
2299 - (
2300 - ! $user->has_cap( 'friend_request' ) &&
2301 - ! $user->has_cap( 'pending_friend_request' ) &&
2302 - ! $user->has_cap( 'friend' ) &&
2303 - ! $user->has_cap( 'subscription' )
2304 - )
2305 - ) {
2306 - return $actions;
2307 - }
2308 -
2309 - if ( is_multisite() ) {
2310 - // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2311 - $actions = array_merge( array( 'edit' => '<a href="' . esc_url( self_admin_url( 'admin.php?page=edit-friend&user=' . $user->user_login ) ) . '">' . __( 'Edit' ) . '</a>' ), $actions );
2312 - }
2313 -
2314 - // Ensuire we have a friends user here.
2315 - $user = new User( $user );
2316 -
2317 - $actions['view'] = Frontend::get_link(
2318 - $user->user_url,
2319 - sprintf(
2320 - // translators: %s: Author’s display name.
2321 - __( 'Visit %s&#8217;s website' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2322 - $user->display_name
2323 - ),
2324 - array(),
2325 - $user
2326 - );
2327 - unset( $actions['resetpassword'] );
2328 -
2329 - if ( $user->has_cap( 'friend_request' ) ) {
2330 - $link = self_admin_url( wp_nonce_url( 'users.php?action=accept_friend_request&users[]=' . $user->ID ) );
2331 -
2332 - $actions['user_accept_friend_request'] = '<a href="' . esc_url( $link ) . '">' . __( 'Accept Friend Request', 'friends' ) . '</a>';
2333 - $message = get_user_option( 'friends_request_message', $user->ID );
2334 - $actions['friends friends_request_date'] = '<br/><span class="nonessential">' . esc_html(
2335 - sprintf(
2336 - // translators: %s is a date.
2337 - __( 'Requested on %s', 'friends' ),
2338 - date_i18n( __( 'F j, Y g:i a' ), strtotime( $user->user_registered ) ) // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2339 - )
2340 - ) . '</span>';
2341 - if ( $message ) {
2342 - // translators: %s is a message text.
2343 - $actions['friends friend_request_message'] = '<br/><span class="nonessential">' . esc_html( sprintf( __( 'Message: %s', 'friends' ), $message ) ) . '</span>';
2344 - }
2345 - }
2346 -
2347 - if ( $user->has_cap( 'pending_friend_request' ) || $user->has_cap( 'subscription' ) ) {
2348 - $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' );
2349 - if ( $user->has_cap( 'pending_friend_request' ) ) {
2350 - $actions['user_friend_request'] = '<a href="' . esc_url( $link ) . '">' . __( 'Resend Friend Request', 'friends' ) . '</a>';
2351 - } elseif ( $user->has_cap( 'subscription' ) ) {
2352 - $actions['user_friend_request'] = '<a href="' . esc_url( $link ) . '">' . __( 'Send Friend Request', 'friends' ) . '</a>';
2353 - }
2354 - }
2355 -
2356 - return $actions;
2357 - }
2358 -
2359 - /**
2360 - * Handle bulk friend request approvals on the user page
2361 - *
2362 - * @param string $sendback The URL to send the user back to.
2363 - * @param string $action The requested action.
2364 - * @param array $users The selected users.
2365 - */
2366 - public function handle_bulk_friend_request_approval( $sendback, $action, $users ) {
2367 - if ( 'accept_friend_request' !== $action ) {
2368 - return $sendback;
2369 - }
2370 -
2371 - $accepted = 0;
2372 - foreach ( $users as $user_id ) {
2373 - $user = new User( $user_id );
2374 - if ( ! $user || is_wp_error( $user ) ) {
2375 - continue;
2376 - }
2377 -
2378 - if ( ! $user->has_cap( 'friend_request' ) ) {
2379 - continue;
2380 - }
2381 -
2382 - if ( $user->has_cap( 'friend' ) ) {
2383 - continue;
2384 - }
2385 -
2386 - $user->set_role( get_option( 'friends_default_friend_role', 'friend' ) );
2387 - ++$accepted;
2388 - }
2389 -
2390 - if ( ! $sendback ) {
2391 - return array(
2392 - 'accepted' => $accepted,
2393 - );
2394 - }
2395 -
2396 - $sendback = add_query_arg( 'accepted', $accepted, $sendback );
2397 - $sendback = remove_query_arg( 'role', $sendback );
2398 - wp_safe_redirect( $sendback );
2399 - }
2400 -
2401 - /**
2402 - * Add options to the Bulk dropdown on the users page
2403 - *
2404 - * @param array $actions The existing bulk options.
2405 - * @return array The extended bulk options.
2406 - */
2407 - public function add_user_bulk_options( $actions ) {
2408 - $friends = User_Query::all_friend_requests();
2409 - $friends->get_results();
2410 -
2411 - if ( ! empty( $friends ) ) {
2412 - $actions['accept_friend_request'] = __( 'Accept Friend Request', 'friends' );
2413 - }
2414 -
2415 - $friends = User_Query::all_subscriptions();
2416 - $friends->get_results();
2417 -
2418 - if ( ! empty( $friends ) ) {
2419 - $actions['friend_request'] = __( 'Send Friend Request', 'friends' );
2420 - }
2421 -
2422 - return $actions;
2423 - }
2424 -
2425 - /**
2426 - * Add a column "Posts" (that emcompasses both user and friend posts.)
2427 - *
2428 - * @param array $columns The columns.
2429 - *
2430 - * @return array The columns extended by the friends_posts.
2431 - */
2432 - public function user_list_columns( $columns ) {
2433 - $columns['friends_posts'] = __( 'Friend Posts', 'friends' );
2434 - unset( $columns['email'] );
2435 - return $columns;
2436 - }
2437 -
2438 - /**
2439 - * Return the results for the friends_posts column.
2440 - *
2441 - * @param string $output Custom column output. Default empty.
2442 - * @param string $column_name Column name.
2443 - * @param int $user_id ID of the currently-listed user.
2444 - *
2445 - * @return string The column contents.
2446 - */
2447 - public static function user_list_custom_column( $output, $column_name, $user_id ) {
2448 - if ( 'friends_posts' !== $column_name ) {
2449 - return $output;
2450 - }
2451 - $numposts = count_user_posts( $user_id, apply_filters( 'friends_frontend_post_types', array( 'post' ) ) );
2452 - $user = User::get_user_by_id( $user_id );
2453 - return sprintf(
2454 - '<a href="%s" class="edit"><span aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
2455 - $user ? $user->get_local_friends_page_url() : "edit.php?author={$user_id}",
2456 - $numposts,
2457 - sprintf(
2458 - /* translators: %s: Number of posts. */
2459 - _n( '%s post', '%s posts', $numposts ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2460 - number_format_i18n( $numposts )
2461 - )
2462 - );
2463 - }
2464 -
2465 - /**
2466 2895 * Override the post title for specific post formats.
2467 2896 *
2468 2897 * @param string $title The title.
2469 2898 * @param int $post_id The post id.
@@ -2470,13 +2899,14 @@
2470 2899 *
2471 2900 * @return string The potentially overriden title.
2472 2901 */
2473 2902 public function override_post_format_title( $title, $post_id = null ) {
2474 - if ( empty( $title ) && is_admin() && function_exists( 'get_current_screen' ) ) {
2903 + if ( $post_id && empty( $title ) && is_admin() && function_exists( 'get_current_screen' ) ) {
2475 2904 $screen = get_current_screen();
2476 2905 if ( $screen && 'edit-post' === $screen->id ) {
2477 2906 if ( 'status' === get_post_format() ) {
2478 - return get_the_excerpt();
2907 + $post = get_post( $post_id );
2908 + return wp_trim_words( wp_strip_all_tags( $post->post_content ) );
2479 2909 }
2480 2910 }
2481 2911 }
2482 2912 return $title;
@@ -2482,41 +2912,8 @@
2482 2912 return $title;
2483 2913 }
2484 2914
2485 2915 /**
2486 - * Adds the friend requests to the unread count.
2487 - *
2488 - * @param int $unread The unread count.
2489 - *
2490 - * @return int Unread count + friend requests.
2491 - */
2492 - public function friends_unread_friend_request_count( $unread ) {
2493 - $friend_requests = User_Query::all_friend_requests();
2494 - return $unread + $friend_requests->get_total();
2495 - }
2496 -
2497 - /**
2498 - * Add open friend requests to the menu.
2499 - *
2500 - * @param \WP_Menu $wp_menu The wp menu.
2501 - * @param string $my_url My url.
2502 - */
2503 - public function friends_add_menu_open_friend_request( $wp_menu, $my_url ) {
2504 - $friend_request_count = $this->friends_unread_friend_request_count( 0 );
2505 - if ( $friend_request_count > 0 ) {
2506 - $wp_menu->add_menu(
2507 - array(
2508 - 'id' => 'open-friend-requests',
2509 - 'parent' => 'friends-menu',
2510 - // translators: %s is the number of open friend requests.
2511 - 'title' => esc_html( sprintf( _n( 'Review %s Friend Request', 'Review %s Friends Request', $friend_request_count, 'friends' ), $friend_request_count ) ),
2512 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-list-requests',
2513 - )
2514 - );
2515 - }
2516 - }
2517 -
2518 - /**
2519 2916 * Get the unread badge HTML
2520 2917 *
2521 2918 * @return string The unread badge HTML.
2522 2919 */
@@ -2541,165 +2938,54 @@
2541 2938 *
2542 2939 * @param \WP_Admin_Bar $wp_menu The admin bar to modify.
2543 2940 */
2544 2941 public function admin_bar_friends_menu( \WP_Admin_Bar $wp_menu ) {
2545 - $my_url = false;
2546 - $my_own_site = false;
2547 - $on_my_own_site = false;
2548 - $we_requested_friendship = false;
2549 - $they_requested_friendship = false;
2942 + if ( ! Friends::has_required_privileges() ) {
2943 + return;
2944 + }
2550 2945
2551 - if ( current_user_can( 'friend' ) ) {
2552 - $current_user = wp_get_current_user();
2553 - if ( ! $current_user->user_url ) {
2554 - return;
2555 - }
2946 + $my_url = home_url();
2947 + $my_admin_url = site_url();
2556 2948
2557 - $my_url = $current_user->user_url;
2558 - } elseif ( is_multisite() ) {
2559 - $site = get_active_blog_for_user( get_current_user_id() );
2560 - if ( ! $site ) {
2561 - // If we cannot find a site, we shouldn't show the admin bar entry.
2562 - return;
2563 - }
2949 + $unread = $this->get_unread_badge();
2564 2950
2565 - $my_url = set_url_scheme( $site->siteurl );
2566 - $my_own_site = $site;
2567 - $on_my_own_site = get_current_blog_id() === intval( $site->blog_id );
2568 - if ( is_user_member_of_blog( get_current_user_id(), get_current_blog_id() ) ) {
2569 - if ( current_user_can( 'pending_friend_request' ) ) {
2570 - $they_requested_friendship = true;
2571 - } elseif ( current_user_can( 'friend_request' ) ) {
2572 - $we_requested_friendship = true;
2573 - }
2574 - }
2575 - } elseif ( Friends::has_required_privileges() ) {
2576 - $my_url = home_url();
2577 - $on_my_own_site = true;
2578 - }
2579 -
2580 - if ( ! $on_my_own_site && $my_own_site ) {
2581 - switch_to_blog( $my_own_site->blog_id );
2582 - }
2583 -
2584 - $unread = '';
2585 - if ( $on_my_own_site ) {
2586 - $unread = $this->get_unread_badge();
2587 - }
2588 2951 $wp_menu->add_node(
2589 2952 array(
2590 2953 'id' => 'friends-menu',
2591 2954 'parent' => '',
2592 - '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>',
2593 2956 'href' => $my_url . '/friends/',
2594 2957 )
2595 2958 );
2596 2959
2597 - if ( $on_my_own_site ) {
2598 - do_action( 'friends_own_site_menu_top', $wp_menu, $my_url );
2599 - }
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 );
2600 2962
2601 - if ( ! $on_my_own_site && $my_own_site ) {
2602 - restore_current_blog();
2603 - }
2604 -
2605 - do_action( 'friends_current_site_menu_top', $wp_menu, $my_url );
2606 -
2607 2963 $wp_menu->add_menu(
2608 2964 array(
2609 2965 'id' => 'your-feed',
2610 2966 'parent' => 'friends-menu',
2611 - 'title' => esc_html__( 'My Friends Feed', 'friends' ),
2612 - 'href' => $my_url . '/friends/',
2967 + 'title' => esc_html__( 'Main Feed', 'friends' ),
2968 + 'href' => home_url( '/friends/' ),
2613 2969 )
2614 2970 );
2615 2971
2616 - if ( $they_requested_friendship ) {
2617 - $wp_menu->add_menu(
2618 - array(
2619 - 'id' => 'add-friend',
2620 - 'parent' => 'friends-menu',
2621 - 'title' => '<span style="border-left: 2px solid #d63638; padding-left: .5em">' . esc_html(
2622 - sprintf(
2623 - // translators: %s is a site title.
2624 - __( "Respond to %s's friend request", 'friends' ),
2625 - get_bloginfo( 'name' )
2626 - ) . '</span>'
2627 - ),
2628 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-list-requests',
2629 - )
2630 - );
2631 - }
2632 -
2633 - if ( $on_my_own_site ) {
2634 - $wp_menu->add_menu(
2635 - array(
2636 - 'id' => 'your-profile',
2637 - 'parent' => 'friends-menu',
2638 - 'title' => esc_html__( 'My Public Friends Profile', 'friends' ),
2639 - 'href' => $my_url . '/friends/?public',
2640 - )
2641 - );
2642 - $wp_menu->add_menu(
2643 - array(
2644 - 'id' => 'friends-requests',
2645 - 'parent' => 'friends-menu',
2646 - 'title' => esc_html__( 'My Friends & Requests', 'friends' ),
2647 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-list',
2648 - )
2649 - );
2650 - $wp_menu->add_menu(
2651 - array(
2652 - 'id' => 'friends',
2653 - 'parent' => 'friends-menu',
2654 - 'title' => esc_html__( 'Settings' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2655 - 'href' => $my_url . '/wp-admin/admin.php?page=friends-settings',
2656 - )
2657 - );
2658 - } else {
2659 - if ( ! current_user_can( 'friend' ) ) {
2660 - if ( $we_requested_friendship ) {
2661 - $wp_menu->add_menu(
2662 - array(
2663 - 'id' => 'add-friend',
2664 - 'parent' => 'friends-menu',
2665 - 'title' => esc_html__( 'Friendship Already Requested', 'friends' ),
2666 - 'href' => $my_url . '/wp-admin/' . self::get_users_url(),
2667 - )
2668 - );
2669 - } elseif ( ! $they_requested_friendship ) {
2670 - $wp_menu->add_menu(
2671 - array(
2672 - 'id' => 'add-friend',
2673 - 'parent' => 'friends-menu',
2674 - 'title' => esc_html(
2675 - sprintf(
2676 - // translators: %s is a site title.
2677 - __( 'Add %s as a friend', 'friends' ),
2678 - get_bloginfo( 'name' )
2679 - )
2680 - ),
2681 - 'href' => $my_url . '/?add-friend=' . urlencode( home_url() ),
2682 - )
2683 - );
2684 - }
2685 - }
2686 -
2687 - $wp_menu->add_menu(
2688 - array(
2689 - 'id' => 'profile',
2690 - 'parent' => 'friends-menu',
2691 - 'title' => esc_html(
2692 - sprintf(
2693 - // translators: %s is a site title.
2694 - __( "%s's Profile", 'friends' ),
2695 - get_bloginfo( 'name' )
2696 - )
2697 - ),
2698 - 'href' => home_url( '/friends/' ),
2699 - )
2700 - );
2701 - }
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 + );
2702 2988 }
2703 2989
2704 2990 /**
2705 2991 * Add Friend entries to the New Content admin section
@@ -2733,17 +3019,37 @@
2733 3019 public function admin_bar_mobile() {
2734 3020 if ( ! is_user_logged_in() ) {
2735 3021 return;
2736 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";
2737 3024 ?>
2738 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 + }
2739 3036 @media screen and (max-width: 782px) {
2740 - #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 {
2741 3038 display: block !important;
2742 3039 }
2743 - #wpadminbar #wp-admin-bar-friends .ab-label {
3040 + #wpadminbar #wp-admin-bar-friends-menu .ab-label {
2744 3041 display: none !important;
2745 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 + }
2746 3052 }
2747 3053 </style>
2748 3054 <?php
2749 3055 }
@@ -2758,14 +3064,9 @@
2758 3064 public function delete_user_form( $current_user, $userids ) {
2759 3065 $only_friends_affiliated = true;
2760 3066 foreach ( $userids as $user_id ) {
2761 3067 $user = new \WP_User( $user_id );
2762 - if (
2763 - ! $user->has_cap( 'friend_request' ) &&
2764 - ! $user->has_cap( 'pending_friend_request' ) &&
2765 - ! $user->has_cap( 'friend' ) &&
2766 - ! $user->has_cap( 'subscription' )
2767 - ) {
3068 + if ( ! $user->has_cap( 'subscription' ) ) {
2768 3069 $only_friends_affiliated = false;
2769 3070 break;
2770 3071 }
2771 3072 }
@@ -2817,16 +3118,18 @@
2817 3118 <h3><?php esc_html_e( 'Bookmarklets', 'friends' ); ?></h3>
2818 3119
2819 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>
2820 3121 <p>
2821 - <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>
2822 - <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>
2823 3124 </p>
2824 3125 <h3><?php esc_html_e( 'Browser Extension', 'friends' ); ?></h3>
2825 3126
2826 - <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>
2827 3128 <p>
2828 - <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>
2829 3132 </p>
2830 3133 </div>
2831 3134 <?php
2832 3135 }
@@ -2837,33 +3140,12 @@
2837 3140 * @param array $items Items inserted by another plugin.
2838 3141 * @return array Items + our items.
2839 3142 */
2840 3143 public function dashboard_glance_items( $items ) {
2841 - $count_users = count_users();
2842 - $count = array_merge(
2843 - array(
2844 - 'friend' => 0,
2845 - 'acquaintance' => 0,
2846 - 'friend_request' => 0,
2847 - 'subscription' => 0,
2848 - ),
2849 - $count_users['avail_roles']
2850 - );
2851 - $friend_count = $count['friend'] + $count['acquaintance'];
2852 - $friend_request_count = $count['friend_request'];
2853 - $subscription_count = $count['subscription'];
3144 + $subscription_count = User_Query::all_subscriptions()->get_total();
2854 3145 $friend_post_count = wp_count_posts( Friends::CPT );
2855 3146 $friend_post_count = $friend_post_count->publish + $friend_post_count->private;
2856 3147
2857 - $items[] = '<a class="friends" href="' . self_admin_url( 'users.php?role=friend' ) . '">' . sprintf(
2858 - // translators: %s is the number of your friends.
2859 - _n( '%s Friend', '%s Friends', $friend_count, 'friends' ),
2860 - $friend_count
2861 - ) . '</a>';
2862 - if ( $friend_request_count ) {
2863 - // translators: %s is the number of friend requests.
2864 - $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>';
2865 - }
2866 3148 if ( $subscription_count ) {
2867 3149 // translators: %s is the number of subscriptions.
2868 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>';
2869 3151 }
@@ -2869,82 +3151,212 @@
2869 3151 }
2870 3152
2871 3153 if ( $friend_post_count ) {
2872 3154 // translators: %s is the number of friend posts.
2873 - $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>';
2874 3156 }
2875 3157 return $items;
2876 3158 }
2877 3159
2878 - public function site_status_tests( $tests ) {
2879 - $tests['direct']['friends-roles'] = array(
2880 - 'label' => __( 'Friend roles were created', 'friends' ),
2881 - 'test' => array( $this, 'friend_roles_test' ),
2882 - );
2883 - 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 + }
2884 3190 }
2885 3191
2886 - public function get_missing_friends_plugin_roles() {
2887 - $missing = Friends::get_friends_plugin_roles();
2888 - $roles = new \WP_Roles();
2889 - foreach ( $roles->roles as $role => $data ) {
2890 - if ( isset( $data['capabilities']['friends_plugin'] ) ) {
2891 - foreach ( $missing as $k => $cap ) {
2892 - if ( isset( $data['capabilities'][ $cap ] ) ) {
2893 - unset( $missing[ $k ] );
2894 - break;
2895 - }
2896 - }
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();
2897 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 );
2898 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 + }
2899 3284
2900 - 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>';
2901 3297 }
2902 3298
2903 - public function friend_roles_test() {
2904 - $result = array(
2905 - 'label' => __( 'The friend roles have been installed', 'friends' ),
2906 - 'status' => 'good',
2907 - 'badge' => array(
2908 - 'label' => __( 'Friends', 'friends' ),
2909 - 'color' => 'green',
2910 - ),
2911 - 'description' =>
2912 - '<p>' .
2913 - __( 'The Friends Plugin uses users and user roles to determine friendship status between sites.', 'friends' ) .
2914 - '</p>' .
2915 - '<p>' .
2916 - sprintf(
2917 - // translators: %s is a list of roles.
2918 - __( 'These are the roles required for the friends plugin: %s', 'friends' ),
2919 - implode( ', ', Friends::get_friends_plugin_roles() )
2920 - ) .
2921 - '</p>',
2922 - 'test' => 'friends-roles',
2923 - );
3299 + public function ajax_friends_dashboard() {
3300 + check_ajax_referer( 'friends-dashboard' );
2924 3301
2925 - $missing_friend_roles = $this->get_missing_friends_plugin_roles();
2926 - if ( ! empty( $missing_friend_roles ) ) {
3302 + $query_args = array();
3303 + $args = array();
2927 3304
2928 - $result['label'] = sprintf(
2929 - // translators: %s is a list of missing roles.
2930 - __( 'Not all friend roles have been installed. Missing: %s', 'friends' ),
2931 - 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()
2932 3346 );
2933 - $result['badge']['color'] = 'red';
2934 - $result['status'] = 'critical';
2935 - $result['description'] .= '<p>';
2936 - $result['description'] .= wp_kses_post(
2937 - sprintf(
2938 - // translators: %s is a URL.
2939 - __( '<strong>To fix this:</strong> <a href="%s">Re-run activation of the Friends plugin</a>.', 'friends' ),
2940 - 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' ) )
2941 - )
2942 - );
2943 - $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 );
2944 3352 }
3353 + $data = ob_get_contents();
3354 + ob_end_clean();
2945 3355
2946 - return $result;
3356 + wp_send_json_success(
3357 + $data
3358 + );
2947 3359 }
2948 3360
2949 3361 public function site_status_test_php_modules( $modules ) {
2950 3362 $modules['mbstring']['required'] = true;
@@ -2950,61 +3362,70 @@
2950 3362 $modules['mbstring']['required'] = true;
2951 3363 return $modules;
2952 3364 }
2953 3365
2954 - public function site_health_debug( $debug_info ) {
2955 - $missing_friend_roles = $this->get_missing_friends_plugin_roles();
2956 - $debug_info['friends'] = array(
2957 - 'label' => __( 'Friends', 'friends' ),
2958 - 'fields' => array(
2959 - 'version' => array(
2960 - 'label' => __( 'Friends Version', 'friends' ),
2961 - 'value' => Friends::VERSION,
2962 - ),
2963 - 'mbstring' => array(
2964 - 'label' => __( 'mbstring is available', 'friends' ),
2965 - 'value' => function_exists( 'mb_check_encoding' ) ? __( 'Yes' ) : __( 'No' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2966 - ),
2967 - 'roles' => array(
2968 - 'label' => __( 'Friend roles missing', 'friends' ),
2969 - 'value' => empty( $missing_friend_roles ) ? sprintf(
2970 - // translators: %s is a list of roles.
2971 - __( 'All roles found: %s', 'friends' ),
2972 - implode( ', ', Friends::get_friends_plugin_roles() )
2973 - ) : implode( ', ', $missing_friend_roles ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
2974 - ),
2975 - 'main_user' => array(
2976 - 'label' => __( 'Main Friend User', 'friends' ),
2977 - 'value' => self::human_readable_main_user(),
2978 - ),
2979 - 'parsers' => array(
2980 - 'label' => __( 'Registered Parsers', 'friends' ),
2981 - 'value' => strip_tags( implode( ', ', $this->friends->feed->get_registered_parsers() ) ),
2982 - ),
2983 - ),
2984 - );
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 + }
2985 3374
2986 - 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;
2987 3387 }
2988 3388
2989 3389 /**
2990 - * 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).
2991 3392 *
2992 - * @return string
3393 + * @param User_Feed $feed The feed.
3394 + * @param int $term_id The term ID.
3395 + * @param string $parser The parser slug.
2993 3396 */
2994 - private static function human_readable_main_user() {
2995 - $main_user = Friends::get_main_friend_user_id();
2996 -
2997 - if ( ! $main_user ) {
2998 - // translators: %d is the number of users.
2999 - 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;
3000 3400 }
3001 3401
3002 - $user = new \WP_User( $main_user );
3003 -
3004 - if ( ! $user ) {
3005 - return sprintf( '#%1$d %2$s', $main_user, '???' );
3402 + if ( class_exists( '\Activitypub\Activitypub' ) ) {
3403 + return;
3006 3404 }
3007 -
3008 - 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
3009 3430 }
3010 3431 }