Allow Contributor to change the author of his own post?

I’m adding a second answer because my first approach got voted down and this one didn’t get proper attention.

The idea is to create custom meta box that lists all the users and changes the author in save_post hook. This way you don’t mess with users capabilities and the author change happens when the posts is already saved. Also the additional profit is that you can control the list of users that are available in author dropdown. Steps to follow:

Register meta box:

function wpse313020_add_custom_box() {

    // Bail out for users other than contributors
    if ( ! user_can( get_current_user_id(), 'contributor' ) ) {
        return;
    } 

    // Register custom meta box
    add_meta_box(
        'wpse313020_author_override',
        'Change Author', // metabox title
        'wpse313020_author_ovveride_box_html', // callbac function
        'post' // a post type you want to show the metabox on
    );
}
add_action('add_meta_boxes', 'wpse313020_add_custom_box');

Build markup for your meta box:

/**
 * HTML for custom meta box
 */
 function wpse313020_author_ovveride_box_html() {
    // you can modify the list of users by passing additional args to get_users()
    $users = get_users();
    ?>
    <label for="wpse313020_author_override_id">Select post author</label><br />
    <select name="wpse313020_author_override_id" id="wpse313020_author_override_id" class="postbox">
        <option value="">Select user...</option>
        <?php
        // get post ID on admin edit screen and retrieve saved post meta
        $post_id     = is_admin() && isset( $_GET['post'] ) ? absint( wp_unslash( $_GET['post'] ) ) : '';
        $saved_value = ! empty( $post_id ) ? get_post_meta( $post_id, 'wpse313020_author_override', true ) : '';

        foreach ( $users as $user ) {
            echo sprintf( '<option value="%1$d" %2$s>%3$s</option>', absint( $user->ID ), selected( $saved_value, absint($user->ID, false ) ), esc_html( $user->display_name ) );
        }
        ?>
    </select>
    <?php
 }

Hook into save_post to save data and override the author:

/**
 * Save custom post meta and override the post author
 */
function wpse313020_save_postdata( $post_id ) {

    if ( array_key_exists('wpse313020_author_override_id', $_POST ) ) {
        // save post meta with author ID
        update_post_meta( $post_id, 'wpse313020_author_override', absint( $_POST['wpse313020_author_override_id'] ) );

        // now modify the post author, we need to unhook the current function to prevent infinite loop
        // you could add additional check here to see if the current author is not the same as chosen author

        remove_action( 'save_post', 'wpse313020_save_postdata' );

        $updated_data = [
            'ID'          => $post_id,
            'post_author' => absint( $_POST['wpse313020_author_override_id'] ),
        ];

        wp_update_post( $updated_data );

        add_action( 'save_post', 'wpse313020_save_postdata' );
    }
}
add_action('save_post', 'wpse313020_save_postdata');

NOTE
Remember to add nonce field and check it on post save.
Also you might consider using other hooks instead of save_post, i.e. pre_post_update or wp_insert_post_data, to process data on initial post save.

Hope that helps!

Leave a Comment