diff --git a/includes/class-admin.php b/includes/class-admin.php index 353b75fbc..4ed8babe5 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -1,6 +1,7 @@ true, 'default' => Blog::get_default_username(), 'sanitize_callback' => function ( $value ) { - // hack to allow dots in the username - $parts = explode( '.', $value ); - $sanitized = array(); + $sanitized = self::sanitize_identifier( $value ); - foreach ( $parts as $part ) { - $sanitized[] = \sanitize_title( $part ); + if ( ! $sanitized ) { + return Blog::get_default_username(); } - $sanitized = implode( '.', $sanitized ); - - // check for login or nicename. - $user = new WP_User_Query( - array( - 'search' => $sanitized, - 'search_columns' => array( 'user_login', 'user_nicename' ), - 'number' => 1, - 'hide_empty' => true, - 'fields' => 'ID', - ) - ); - - if ( $user->results ) { + if ( \is_wp_error( $sanitized ) ) { add_settings_error( 'activitypub_blog_identifier', 'activitypub_blog_identifier', @@ -367,6 +353,7 @@ public static function add_followers_list_help_tab() { public static function add_profile( $user ) { $description = \get_user_option( 'activitypub_description', $user->ID ); + $identifier = \get_user_option( 'activitypub_identifier', $user->ID ); wp_enqueue_media(); wp_enqueue_script( 'activitypub-header-image' ); @@ -376,6 +363,7 @@ public static function add_profile( $user ) { true, array( 'description' => $description, + 'identifier' => $identifier, ) ); } @@ -413,6 +401,26 @@ public static function save_user_settings( $user_id ) { } else { \delete_user_option( $user_id, 'activitypub_header_image' ); } + + $identifier = ! empty( $_POST['activitypub_identifier'] ) ? sanitize_text_field( wp_unslash( $_POST['activitypub_identifier'] ) ) : false; + $identifier = self::sanitize_identifier( $identifier ); + + if ( ! \is_wp_error( $identifier ) ) { + \update_user_option( $user_id, 'activitypub_identifier', $identifier ); + } else { + if ( \is_wp_error( $identifier ) ) { + // show error message on user settings page + add_action( + 'user_profile_update_errors', + function ( $errors ) use ( $identifier ) { + $errors->add( 'activitypub_identifier', $identifier->get_error_message() ); + }, + 10, + 3 + ); + } + \delete_user_option( $user_id, 'activitypub_identifier' ); + } } public static function enqueue_scripts( $hook_suffix ) { @@ -758,4 +766,47 @@ public static function dashboard_glance_items( $items ) { return $items; } + + /** + * Sanatize the identifier + * + * @param string $id The identifier. + * + * @return false|string The sanitized identifier or false if it is already in use. + */ + private static function sanitize_identifier( $id ) { + if ( empty( $id ) ) { + return false; + } + + // hack to allow dots in the username + $parts = explode( '.', $id ); + $sanitized = array(); + + foreach ( $parts as $part ) { + $sanitized[] = \sanitize_title( $part ); + } + + $sanitized = implode( '.', $sanitized ); + + // check for login or nicename. + $user = new WP_User_Query( + array( + 'search' => $sanitized, + 'search_columns' => array( 'user_login', 'user_nicename' ), + 'number' => 1, + 'hide_empty' => true, + 'fields' => 'ID', + ) + ); + + if ( $user->get_results() ) { + return new WP_Error( + 'identifier_exists', + \__( 'This identifier is already in use.', 'activitypub' ) + ); + } + + return $sanitized; + } } diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index a87c381ae..1389ceac7 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -303,14 +303,24 @@ public static function user_meta_update( $meta_id, $user_id, $meta_key ) { if ( ! \user_can( $user_id, 'activitypub' ) ) { return; } + + global $wpdb; + + $prefix = $wpdb->get_blog_prefix(); + // the user meta fields that affect a profile. $fields = array( 'activitypub_description', 'activitypub_header_image', + 'activitypub_identifier', + $prefix . 'activitypub_description', + $prefix . 'activitypub_header_image', + $prefix . 'activitypub_identifier', 'description', 'user_url', 'display_name', ); + if ( in_array( $meta_key, $fields, true ) ) { self::schedule_profile_update( $user_id ); } diff --git a/includes/class-signature.php b/includes/class-signature.php index 07831583e..dd1abca56 100644 --- a/includes/class-signature.php +++ b/includes/class-signature.php @@ -135,7 +135,7 @@ protected static function get_signature_options_key_for( $user_id ) { if ( $user_id > 0 ) { $user = \get_userdata( $user_id ); - // sanatize username because it could include spaces and special chars + // sanitize username because it could include spaces and special chars $id = sanitize_title( $user->user_login ); } diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php index 06fe6e8eb..baff6eaf5 100644 --- a/includes/collection/class-users.php +++ b/includes/collection/class-users.php @@ -85,6 +85,8 @@ public static function get_by_username( $username ) { return new Application(); } + global $wpdb; + // check for 'activitypub_username' meta $user = new WP_User_Query( array( @@ -96,9 +98,14 @@ public static function get_by_username( $username ) { 'meta_query' => array( 'relation' => 'OR', array( - 'key' => 'activitypub_user_identifier', + 'key' => 'activitypub_identifier', + 'value' => $username, + 'compare' => '=', + ), + array( + 'key' => $wpdb->get_blog_prefix() . 'activitypub_identifier', 'value' => $username, - 'compare' => 'LIKE', + 'compare' => '=', ), ), ) @@ -248,7 +255,7 @@ public static function get_by_various( $id ) { $user = self::get_by_resource( $id ); } - if ( $user && ! is_wp_error( $user ) ) { + if ( $user && ! \is_wp_error( $user ) ) { return $user; } diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 7849b9ab7..4935caabc 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -106,9 +106,9 @@ public function get_name() { * @return string The User-Description. */ public function get_summary() { - $description = get_user_option( 'activitypub_description', $this->_id ); + $description = \get_user_option( 'activitypub_description', $this->_id ); if ( empty( $description ) ) { - $description = get_user_meta( $this->_id, 'description', true ); + $description = \get_user_meta( $this->_id, 'description', true ); } return \wpautop( \wp_kses( $description, 'default' ) ); } @@ -132,6 +132,11 @@ public function get_alternate_url() { } public function get_preferred_username() { + $custom_user_identifier = get_user_option( 'activitypub_identifier', $this->_id ); + if ( $custom_user_identifier ) { + return \esc_attr( $custom_user_identifier ); + } + return \esc_attr( \get_the_author_meta( 'login', $this->_id ) ); } diff --git a/templates/user-settings.php b/templates/user-settings.php index da85be3f8..80f3e702d 100644 --- a/templates/user-settings.php +++ b/templates/user-settings.php @@ -11,10 +11,15 @@ - +

+ +

+

get_webfinger() ); ?> or get_url() ); ?>

diff --git a/tests/test-class-activitypub-users-collection.php b/tests/test-class-activitypub-users-collection.php index 1304735a6..fa8db36e4 100644 --- a/tests/test-class-activitypub-users-collection.php +++ b/tests/test-class-activitypub-users-collection.php @@ -5,7 +5,7 @@ public function set_up() { parent::set_up(); add_option( 'activitypub_blog_identifier', 'blog' ); - add_user_meta( 1, 'activitypub_user_identifier', 'admin' ); + add_user_meta( 1, 'activitypub_identifier', 'admin' ); } /** * @dataProvider the_resource_provider