From ac7125aa78cf28b49e6f3cfda5eac9dd910bf24c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 30 Sep 2024 10:44:16 +0200 Subject: [PATCH 1/6] make username configurable at least for the WebFinger ID --- includes/class-admin.php | 86 ++++++++++++++----- includes/class-scheduler.php | 1 + includes/collection/class-users.php | 11 ++- includes/model/class-user.php | 5 ++ templates/user-settings.php | 7 +- ...est-class-activitypub-users-collection.php | 2 +- 6 files changed, 87 insertions(+), 25 deletions(-) diff --git a/includes/class-admin.php b/includes/class-admin.php index 353b75fbc..78daa3649 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -310,28 +310,9 @@ public static function register_settings() { 'show_in_rest' => true, 'default' => Blog::get_default_username(), 'sanitize_callback' => function ( $value ) { - // hack to allow dots in the username - $parts = explode( '.', $value ); - $sanitized = array(); + $sanatized = self::sanatize_identifier( $value ); - 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->results ) { + if ( \is_wp_error( $sanatized ) ) { add_settings_error( 'activitypub_blog_identifier', 'activitypub_blog_identifier', @@ -413,6 +394,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::sanatize_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 +759,47 @@ public static function dashboard_glance_items( $items ) { return $items; } + + /** + * Sanatize the identifier + * + * @param string $id The identifier. + * + * @return false|string The sanatized identifier or false if it is already in use. + */ + private static function sanatize_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 631da8d77..bec5af7c1 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -307,6 +307,7 @@ public static function user_meta_update( $meta_id, $user_id, $meta_key ) { $fields = array( 'activitypub_description', 'activitypub_header_image', + 'activitypub_identifier', 'description', 'user_url', 'display_name', diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php index d6e70cec5..eda665bbf 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->prefix . 'activitypub_identifier', 'value' => $username, - 'compare' => 'LIKE', + 'compare' => '=', ), ), ) diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 7849b9ab7..69a8d37b1 100644 --- a/includes/model/class-user.php +++ b/includes/model/class-user.php @@ -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..8c5bc6967 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 From 035dc9f7588d7b4b514fb107c41c47f827e1289b Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 30 Sep 2024 10:49:38 +0200 Subject: [PATCH 2/6] escaping --- includes/collection/class-users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php index eda665bbf..2b0b8c23c 100644 --- a/includes/collection/class-users.php +++ b/includes/collection/class-users.php @@ -255,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; } From b8046154e9a9f1087982f03badf8efccaaaf3f9f Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Mon, 30 Sep 2024 10:56:49 +0200 Subject: [PATCH 3/6] fix phpcs issue and improved code --- includes/class-admin.php | 2 ++ includes/model/class-user.php | 6 +++--- templates/user-settings.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/includes/class-admin.php b/includes/class-admin.php index 78daa3649..130b57674 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -348,6 +348,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' ); @@ -357,6 +358,7 @@ public static function add_profile( $user ) { true, array( 'description' => $description, + 'identifier' => $identifier, ) ); } diff --git a/includes/model/class-user.php b/includes/model/class-user.php index 69a8d37b1..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,7 +132,7 @@ public function get_alternate_url() { } public function get_preferred_username() { - $custom_user_identifier = get_user_option ( 'activitypub_identifier', $this->_id ); + $custom_user_identifier = get_user_option( 'activitypub_identifier', $this->_id ); if ( $custom_user_identifier ) { return \esc_attr( $custom_user_identifier ); } diff --git a/templates/user-settings.php b/templates/user-settings.php index 8c5bc6967..80f3e702d 100644 --- a/templates/user-settings.php +++ b/templates/user-settings.php @@ -16,7 +16,7 @@

From a768fb80ee8416f681acbae61d4f37a6a5874d3c Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 2 Oct 2024 17:35:22 +0200 Subject: [PATCH 4/6] check also the usernames --- includes/class-admin.php | 36 ++++++++++++++++++++++++++++- includes/collection/class-users.php | 2 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/includes/class-admin.php b/includes/class-admin.php index 130b57674..c44a6aa8c 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -1,6 +1,7 @@ get_results() ) { - return new \WP_Error( + return new WP_Error( + 'identifier_exists', + \__( 'This identifier is already in use.', 'activitypub' ) + ); + } + + global $wpdb; + + // check for 'activitypub_username' meta + $user = new WP_User_Query( + array( + 'count_total' => false, + 'number' => 1, + 'hide_empty' => true, + 'fields' => 'ID', + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + 'meta_query' => array( + 'relation' => 'OR', + array( + 'key' => 'activitypub_identifier', + 'value' => $username, + 'compare' => '=', + ), + array( + 'key' => $wpdb->get_blog_prefix() . 'activitypub_identifier', + 'value' => $username, + 'compare' => '=', + ), + ), + ) + ); + + if ( $user->results ) { + return new WP_Error( 'identifier_exists', \__( 'This identifier is already in use.', 'activitypub' ) ); diff --git a/includes/collection/class-users.php b/includes/collection/class-users.php index 2b0b8c23c..0f305f33d 100644 --- a/includes/collection/class-users.php +++ b/includes/collection/class-users.php @@ -103,7 +103,7 @@ public static function get_by_username( $username ) { 'compare' => '=', ), array( - 'key' => $wpdb->prefix . 'activitypub_identifier', + 'key' => $wpdb->get_blog_prefix() . 'activitypub_identifier', 'value' => $username, 'compare' => '=', ), From a85ad9742ad33f3919c90e4b9914bf096d0115b6 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 2 Oct 2024 17:52:48 +0200 Subject: [PATCH 5/6] fix typo --- includes/class-admin.php | 47 +++++++----------------------------- includes/class-signature.php | 2 +- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/includes/class-admin.php b/includes/class-admin.php index c44a6aa8c..4ed8babe5 100644 --- a/includes/class-admin.php +++ b/includes/class-admin.php @@ -311,9 +311,13 @@ public static function register_settings() { 'show_in_rest' => true, 'default' => Blog::get_default_username(), 'sanitize_callback' => function ( $value ) { - $sanatized = self::sanatize_identifier( $value ); + $sanitized = self::sanitize_identifier( $value ); - if ( \is_wp_error( $sanatized ) ) { + if ( ! $sanitized ) { + return Blog::get_default_username(); + } + + if ( \is_wp_error( $sanitized ) ) { add_settings_error( 'activitypub_blog_identifier', 'activitypub_blog_identifier', @@ -399,7 +403,7 @@ public static function save_user_settings( $user_id ) { } $identifier = ! empty( $_POST['activitypub_identifier'] ) ? sanitize_text_field( wp_unslash( $_POST['activitypub_identifier'] ) ) : false; - $identifier = self::sanatize_identifier( $identifier ); + $identifier = self::sanitize_identifier( $identifier ); if ( ! \is_wp_error( $identifier ) ) { \update_user_option( $user_id, 'activitypub_identifier', $identifier ); @@ -768,9 +772,9 @@ public static function dashboard_glance_items( $items ) { * * @param string $id The identifier. * - * @return false|string The sanatized identifier or false if it is already in use. + * @return false|string The sanitized identifier or false if it is already in use. */ - private static function sanatize_identifier( $id ) { + private static function sanitize_identifier( $id ) { if ( empty( $id ) ) { return false; } @@ -803,39 +807,6 @@ private static function sanatize_identifier( $id ) { ); } - global $wpdb; - - // check for 'activitypub_username' meta - $user = new WP_User_Query( - array( - 'count_total' => false, - 'number' => 1, - 'hide_empty' => true, - 'fields' => 'ID', - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query - 'meta_query' => array( - 'relation' => 'OR', - array( - 'key' => 'activitypub_identifier', - 'value' => $username, - 'compare' => '=', - ), - array( - 'key' => $wpdb->get_blog_prefix() . 'activitypub_identifier', - 'value' => $username, - 'compare' => '=', - ), - ), - ) - ); - - if ( $user->results ) { - return new WP_Error( - 'identifier_exists', - \__( 'This identifier is already in use.', 'activitypub' ) - ); - } - return $sanitized; } } 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 ); } From 177cf785d9e801f90deefe716a47155d62a51b91 Mon Sep 17 00:00:00 2001 From: Matthias Pfefferle Date: Wed, 2 Oct 2024 17:53:04 +0200 Subject: [PATCH 6/6] listen also to user_option changes --- includes/class-scheduler.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/includes/class-scheduler.php b/includes/class-scheduler.php index 7dc8a82eb..1389ceac7 100644 --- a/includes/class-scheduler.php +++ b/includes/class-scheduler.php @@ -303,15 +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 ); }