WordPress,用户前端编辑,无法更新电子邮件和个人信息

WordPress, User front end editing, can't update email and personal information

我想创建一个表单,注册用户可以在其中编辑自己的信息, 一切正常,除了电子邮件,它没有更新。

我发现了一些关于此的问题,但没有答案。

我缩短了一个代码,只留下了用户名和用户电子邮件,以表明用户名有效,但电子邮件无效。这是:

add_action('init', '_themename_edit_user_data');
function _themename_edit_user_data(){
    $current_user = wp_get_current_user();
    $user_id = $current_user->ID;
    
    if ( 'POST' == $_SERVER['REQUEST_METHOD'] && !empty( $_POST['action'] ) && $_POST['action'] == 'update-user' ) {

        /* Update user information. */
        if ( !empty( $_POST['email'] ) ){
            wp_update_user( array ('ID' => $user_id, 'user_email' => sanitize_email( $_POST['email'] )));
        }

        if ( !empty( $_POST['first-name'] ) ) {
            update_user_meta( $user_id, 'first_name', esc_attr( $_POST['first-name'] ) );
        }

    }
}

这是我的表格:

<form method="post" id="adduser" action="<?php the_permalink(); ?>">
    <p class="form-username">
        <label for="first-name"><?php _e('First Name', 'profile'); ?></label>
        <input class="text-input" name="first-name" type="text" id="first-name" value="<?php the_author_meta( 'first_name', $current_user->ID ); ?>" />
    </p><!-- .form-username -->
    <p class="form-email">
        <label for="email"><?php _e('E-mail *', 'profile'); ?></label>
        <input class="text-input" name="email" type="text" id="email" value="<?php the_author_meta( 'user_email', $current_user->ID ); ?>" />
    </p><!-- .form-email -->

    <p class="form-submit">
        <input name="updateuser" type="submit" id="updateuser" class="submit button" value="<?php _e('Update', 'profile'); ?>" />
        <?php wp_nonce_field( 'update-user' ) ?>
        <input name="action" type="hidden" id="action" value="update-user" />
    </p><!-- .form-submit -->
</form><!-- #adduser -->

我做错了什么?

试试我的回答:

$args = array(
 'ID'         => $user_id,
 'user_email' => esc_attr( $_POST['email'] )
);

wp_update_user( $args );

对不起,伙计们!我发现我没有正确的条件检查,这是一个问题! 当然,每个人都使用相同的代码:

if ( !empty( $_POST['email'] ) ){
    if (!is_email(sanitize_email( $_POST['email'] ))) {
        $error[] = __('The Email you entered is not valid.  please try again.', 'profile');
    } elseif( email_exists(sanitize_email( $_POST['email'] )) != $user_id ) {
        $error[] = __('This email is already used by another user.  try a different one.', 'profile');
    } else {
        wp_update_user( array ('ID' => $user_id, 'user_email' => sanitize_email( $_POST['email'] )));
    }
}

问题出在这里:

elseif( email_exists(sanitize_email( $_POST['email'] )) != $user_id )

必须是:

elseif( email_exists(sanitize_email( $_POST['email'] )) == $user_id )

对不起!

我必须对此做出反应 post。不指出您当前面临的所有安全问题将是犯罪行为。警告阅读时间长但理解时间短。

我远算不上专家,但大多数在 Whosebug 上查看 post 的关于 front-end 用户数据输入和更新的人(通常)大多是 Wordpress 的新手,并且在此处学习。很容易不知所措。让我们开始吧。


首先,在检查权限时,您应该始终使用三重运算符。 === 而不是 ==。看起来不多,但一个表示 EQUAL,另一个表示 IDENTICAL。 You can learn more about Comparison Operators here.

检查用户身份是防止恶意行为的唯一防线。授予前端编辑能力只是用户在被授予数据库访问权限之前必须通过的一系列条件语句。咱别闹了。


我的假设是您让用户从 author.php 模板提供的用户个人资料页面更新他们的个人信息。因此,下面的想法是基于它的,但很容易适应。我必须坚持,任何模板都不受任何限制。这是我们将要使用的简单表单,目前在我们的 author.php 模板中。这是您的表单的简化版本。

<form method="POST">
    <input type="text" name="first_name" value="<?php echo esc_attr( wp_get_current_user()->first_name ); ?>">
    <input type="email" name="user_email" value="<?php echo esc_attr( wp_get_current_user()->user_email ); ?>">
    <?php wp_nonce_field( esc_attr( 'update-user-' . get_current_user_id() ), esc_attr( 'nonce-user-' . get_current_user_id() ) ); ?>
    <button type="submit">Submit</button>
</form>

为了清晰和一致,尽可能使用 Wordpress 默认功能始终是个好主意。 get_current_user_id()获取当前用户的ID,而不是wp_get_current_user()->ID.


我们来谈谈动作的钩子。 init 是一个在 Wordpress 触发序列中很早就触发的钩子。

Fires after WordPress has finished loading but before any headers are sent.

WP_Object 还没有完成设置。在寻找合适的挂钩时,您可以参考 Plugin API/Action Reference. A more suiting firing hook would be wp,这是推荐用于任何 high-level 过滤或验证的挂钩,在查询之后,但在 WordPress 进行任何路由、处理或处理之前。

Fires once the WordPress environment has been set up. After WP object is set up.

add_action( 'wp', 'wpso62938497_user_frontend_privy_editing' );
function wpso62938497_user_frontend_privy_editing() {
    //...
};

"hors d'oeuvre" of user checking in regards to front end editing is to know if the user is actually logged in. Which you are missing to do. We can use is_user_logged_in()判断当前访问者是否为登录用户。

我们还想确保 author.php 页面上的实际用户是页面所有者。 get_current_user_id() === get_queried_object()->ID 会做到这一点。我们还将检查我们所在的页面是否是作者页面并且是否设置了 WP_User object。最后我们将比较查询的用户和页面作者以确保两者相同。

add_action( 'wp', 'wpso62938497_user_frontend_privy_editing' );
function wpso62938497_user_frontend_privy_editing() {
    /**
     * Determines whether the current visitor is a logged in user.
     * Retrieves the currently queried object. Check whether an it belongs to a WP_User class.
     * Determines whether the query is for an existing author archive page. Compare it to the current logged in user ID.
     * Compare current logged in user ID with the current queried ID.
     */
    if ( is_user_logged_in() && get_queried_object() instanceof \WP_User && is_author( get_current_user_id() ) && get_current_user_id() === get_queried_object()->ID ) {
        //...
    };
};

让我们谈谈随机数。在您的表单中,您声明了一个随机数,但您没有检查它。 nonce 是一个“使用一次的数字”,有助于保护 URL 和表单免受某些类型的滥用、恶意或其他形式的滥用。就像你身份证上的序列号,那个号码就是你。一个随机数是你24小时的临时身份证。可以想象,这非常重要。 Wordpress 作为 whole page on Nonces,你应该读一读。我们还将检查 REQUEST_METHOD.

add_action( 'wp', 'wpso62938497_user_frontend_privy_editing' );
function wpso62938497_user_frontend_privy_editing() {
    /**
     * Determines whether the current visitor is a logged in user.
     * Retrieves the currently queried object. Check whether an it belongs to a WP_User class.
     * Determines whether the query is for an existing author archive page. Compare it to the current logged in user ID.
     * Compare current logged in user ID with the current queried ID.
     */
    if ( is_user_logged_in() && get_queried_object() instanceof \WP_User && is_author( get_current_user_id() ) && get_current_user_id() === get_queried_object()->ID ) {

        /**
         * Determines whether the POST request method was used to access the page.
         * Determine if our nonce variable is declared and is different than null.
         * Verifies that a correct security nonce was used with time limit.
         */
        if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['nonce-user-' . get_current_user_id()] ) && wp_verify_nonce( $_POST['nonce-user-' . get_current_user_id()], 'update-user-' . get_current_user_id() ) ) {
            //...
        };
    };
};

我们快完成了!对于任何清理要求,您可以检查 following post,其中说明哪些数据被 wp_insert_user()wp_update_user().

清理或不清理

当然你应该限制使用 javascript 的形式,但是 javascript 很容易被禁用。这就是为什么我们还需要在 php 端进行检查。

还有一种情况需要注意和处理,那就是页面刷新时一直提示提交,提交后F5,CTRL+R。可以通过将用户重定向到上一页并设置 303 header 来避免这种行为。 You can read more about that issue here.

终于大功告成了,我们的脚本可以安全地用在前端了。 And we're feeling like this!

add_action( 'wp', 'wpso62938497_user_frontend_privy_editing' );
function wpso62938497_user_frontend_privy_editing() {
    /**
     * Determines whether the current visitor is a logged in user.
     * Retrieves the currently queried object. Check whether an it belongs to a WP_User class.
     * Determines whether the query is for an existing author archive page. Compare it to the current logged in user ID.
     * Compare current logged in user ID with the current queried ID.
     */
    if ( is_user_logged_in() && get_queried_object() instanceof \WP_User && is_author( get_current_user_id() ) && get_current_user_id() === get_queried_object()->ID ) {

        /**
         * Determines whether the POST request method was used to access the page.
         * Determine if our nonce variable is declared and is different than null.
         * Verifies that a correct security nonce was used with time limit.
         */
        if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['nonce-user-' . get_current_user_id()] ) && wp_verify_nonce( $_POST['nonce-user-' . get_current_user_id()], 'update-user-' . get_current_user_id() ) ) {

            if ( isset( $_POST['first_name'] )  && ! empty( $_POST['first_name'] ) && wp_get_current_user()->first_name !== $_POST['first_name'] && strcasecmp( sanitize_text_field( $_POST['first_name'] ), $_POST['first_name'] ) ) {

                update_user_meta( get_current_user_id(), 'first_name', sanitize_text_field( $_POST['first_name'] ), esc_attr( wp_get_current_user()->first_name ) );

            };

            if ( is_email( sanitize_email( $_POST['user_email'] ) ) && strcasecmp( sanitize_email( $_POST['user_email'] ), $_POST['user_email'] ) ) {
                
                $userdata = array (
                    'ID' => get_current_user_id(), 
                    'user_email' => $_POST['user_email'],
                );

                wp_update_user( $userdata );

            };

            wp_safe_redirect( esc_url( get_author_posts_url( get_current_user_id() ) ), 303 );

            exit;

        };
    };
};

此函数缺少任何错误处理,几乎是在添加 else 语句。

最后一些明智的话:“清理输入,转义输出”。 https://developer.wordpress.org/themes/theme-security/data-sanitization-escaping/