/**
 * Sets an author on a post, with a specified role.
 *
 * This can be used to edit an existing coauthor - for example, changing the role
 * of an author already attached to a post - but it is not guaranteed to
 * preserve order. If the sorted order of authors needs to be preserved, call
 * `update_coauthors_on_post()` and pass the full list of coauthors to update,
 * sorted.
 *
 * @param int|object $post_id Post to set author as "coauthor" on
 * @param object|string $author user_nicename of Author to add on post (or WP_User object)
 * @param object|string $author_role Term or slug of contributor role to set. Defaults to "byline" if empty
 * @return bool True on success, false on failure (if any of the inputs are not acceptable).
 */
function set_author_on_post($post_id, $author, $author_role = false)
{
    global $coauthors_plus;
    if (is_object($post_id) && isset($post_id->ID)) {
        $post_id = $post_id->ID;
    }
    $post_id = intval($post_id);
    if (is_string($author)) {
        $author = $coauthors_plus->get_coauthor_by('user_nicename', $author);
    }
    if (!isset($author->user_nicename)) {
        return false;
    }
    // Only create the byline term if the contributor role is:
    //  - one of the byline roles, as set in register_author_role(), or
    //  - unset, meaning they should default to primary author role.
    if (!$author_role || in_array($author_role, byline_roles())) {
        $coauthors_plus->add_coauthors($post_id, array($author->user_nicename), true);
    }
    if (!$post_id || !$author) {
        return false;
    }
    foreach (get_post_meta($post_id) as $key => $values) {
        if (strpos($key, 'cap-') === 0 && in_array($author->user_nicename, $values)) {
            delete_post_meta($post_id, $key, $author->user_nicename);
        }
    }
    if (!is_object($author_role)) {
        $author_role = get_author_role($author_role);
    }
    if (!$author_role) {
        return false;
    }
    add_post_meta($post_id, 'cap-' . $author_role->slug, $author->user_nicename);
}
/**
 * Update the co-authors on a post on saving.
 *
 * @param int $post_ID
 * @param array $new_coauthors Array of strings in the format "{nicename}|||{role}"
 *                              as posted by the sortables in the meta box.
 */
function update_coauthors_on_post($post_id, $new_coauthors)
{
    global $coauthors_plus;
    $post = get_post($post_id);
    if (!$coauthors_plus->is_post_type_enabled($post->post_type)) {
        return;
    }
    if ($new_coauthors && is_array($new_coauthors)) {
        // Convert the new coauthors array from the string format used on inputs from the post edit
        // screen into proper objects which can be compared with existing coauthors.
        $new_coauthors = array_filter(array_map(function ($coauthor_string) {
            global $coauthors_plus;
            list($author_name, $role) = explode('|||', $coauthor_string);
            // Empty roles can come in either as the string "byline",
            // or as an empty string. Handle them both the same.
            if ($role === 'byline') {
                $role = '';
            }
            $new_coauthor = $coauthors_plus->get_coauthor_by('user_nicename', sanitize_text_field($author_name));
            if ($new_coauthor) {
                $new_coauthor->author_role = sanitize_text_field($role);
                return $new_coauthor;
            }
        }, $new_coauthors));
        // Diff new data against the already added coauthors. If they aren't identical, wipe the old postmeta and re-add.
        $existing_coauthors = get_coauthors($post_id, array('author_role' => 'any'));
        $difference = false;
        for ($i = 0; $i <= max(count($new_coauthors), count($existing_coauthors)); $i++) {
            if (!isset($new_coauthors[$i]) || !isset($existing_coauthors[$i])) {
                $difference = true;
                break;
            }
            if ($new_coauthors[$i]->user_nicename !== $existing_coauthors[$i]->user_nicename) {
                $difference = true;
                break;
            }
            if ($new_coauthors[$i]->author_role !== $existing_coauthors[$i]->author_role) {
                $difference = true;
                break;
            }
        }
        if ($difference) {
            remove_all_coauthor_meta($post_id);
            foreach ($new_coauthors as $new_coauthor) {
                set_author_on_post($post_id, $new_coauthor, $new_coauthor->author_role);
            }
        }
        // Use $coauthors_plus->set_coauthors to update byline roles. This clears the existing terms and
        // re-adds them in the correct order.
        $new_byline_coauthors = array_filter($new_coauthors, function ($author) {
            return empty($author->author_role) || in_array($author->author_role, byline_roles());
        });
        $byline_coauthors_slugs = wp_list_pluck($new_byline_coauthors, 'user_nicename');
        $coauthors_plus->add_coauthors($post_id, $byline_coauthors_slugs, false);
    }
}