1608 lines
56 KiB
PHP
1608 lines
56 KiB
PHP
<?php
|
|
/**
|
|
* Co-Authors Guest Authors
|
|
*
|
|
* Key idea: Create guest authors to assign as bylines on a post without having
|
|
* to give them access to the dashboard through a WP_User account
|
|
*/
|
|
|
|
class CoAuthors_Guest_Authors
|
|
{
|
|
|
|
var $post_type = 'guest-author';
|
|
var $parent_page = 'users.php';
|
|
var $list_guest_authors_cap = 'list_users';
|
|
|
|
public static $cache_group = 'coauthors-plus-guest-authors';
|
|
|
|
/**
|
|
* Initialize our Guest Authors class and establish common hooks
|
|
*/
|
|
function __construct() {
|
|
global $coauthors_plus;
|
|
|
|
// Add the guest author management menu
|
|
add_action( 'admin_menu', array( $this, 'action_admin_menu' ) );
|
|
|
|
// WP List Table for breaking out our Guest Authors
|
|
require_once( dirname( __FILE__ ) . '/class-coauthors-wp-list-table.php' );
|
|
|
|
// Get a co-author based on a query
|
|
add_action( 'wp_ajax_search_coauthors_to_assign', array( $this, 'handle_ajax_search_coauthors_to_assign' ) );
|
|
|
|
// Any CSS or JS
|
|
add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) );
|
|
|
|
// Extra notices
|
|
add_action( 'admin_notices', array( $this, 'action_admin_notices' ) );
|
|
|
|
// Handle actions to create or delete guest author accounts
|
|
add_action( 'admin_init', array( $this, 'handle_create_guest_author_action' ) );
|
|
add_action( 'admin_init', array( $this, 'handle_delete_guest_author_action' ) );
|
|
|
|
// Redirect if the user is mapped to a guest author
|
|
add_action( 'parse_request', array( $this, 'action_parse_request' ) );
|
|
|
|
// Filter author links and such
|
|
add_filter( 'author_link', array( $this, 'filter_author_link' ), 10, 3 );
|
|
|
|
// Over-ride the author feed
|
|
add_filter( 'author_feed_link', array( $this, 'filter_author_feed_link' ), 10, 2 );
|
|
|
|
// Validate new guest authors
|
|
add_filter( 'wp_insert_post_empty_content', array( $this, 'filter_wp_insert_post_empty_content' ), 10, 2 );
|
|
|
|
// Add metaboxes for our guest author management interface
|
|
add_action( 'add_meta_boxes', array( $this, 'action_add_meta_boxes' ), 10, 2 );
|
|
add_action( 'wp_insert_post_data', array( $this, 'manage_guest_author_filter_post_data' ), 10, 2 );
|
|
add_action( 'save_post', array( $this, 'manage_guest_author_save_meta_fields' ), 10, 2 );
|
|
|
|
// Empty associated caches when the guest author profile is updated
|
|
add_filter( 'update_post_metadata', array( $this, 'filter_update_post_metadata' ), 10, 5 );
|
|
|
|
// Modify the messages that appear when saving or creating
|
|
add_filter( 'post_updated_messages', array( $this, 'filter_post_updated_messages' ) );
|
|
|
|
// Allow admins to create or edit guest author profiles from the Manage Users listing
|
|
add_filter( 'user_row_actions', array( $this, 'filter_user_row_actions' ), 10, 2 );
|
|
|
|
// Add support for featured thumbnails that we can use for guest author avatars
|
|
add_filter( 'get_avatar', array( $this, 'filter_get_avatar' ),10 ,5 );
|
|
|
|
// Add a Personal Data Exporter to guest authors
|
|
add_filter( 'wp_privacy_personal_data_exporters', array( $this, 'filter_personal_data_exporter' ), 1 );
|
|
|
|
// Allow users to change where this is placed in the WordPress admin
|
|
$this->parent_page = apply_filters( 'coauthors_guest_author_parent_page', $this->parent_page );
|
|
|
|
// Allow users to change the required cap for modifying guest authors
|
|
$this->list_guest_authors_cap = apply_filters( 'coauthors_guest_author_manage_cap', $this->list_guest_authors_cap );
|
|
|
|
// Set up default labels, but allow themes to modify
|
|
$this->labels = apply_filters( 'coauthors_guest_author_labels', array(
|
|
'singular' => __( 'Guest Author', 'co-authors-plus' ),
|
|
'plural' => __( 'Guest Authors', 'co-authors-plus' ),
|
|
'all_items' => __( 'All Guest Authors', 'co-authors-plus' ),
|
|
'add_new_item' => __( 'Add New Guest Author', 'co-authors-plus' ),
|
|
'edit_item' => __( 'Edit Guest Author', 'co-authors-plus' ),
|
|
'new_item' => __( 'New Guest Author', 'co-authors-plus' ),
|
|
'view_item' => __( 'View Guest Author', 'co-authors-plus' ),
|
|
'search_items' => __( 'Search Guest Authors', 'co-authors-plus' ),
|
|
'not_found' => __( 'No guest authors found', 'co-authors-plus' ),
|
|
'not_found_in_trash' => __( 'No guest authors found in Trash', 'co-authors-plus' ),
|
|
'update_item' => __( 'Update Guest Author', 'co-authors-plus' ),
|
|
'metabox_about' => __( 'About the guest author', 'co-authors-plus' ),
|
|
'featured_image' => __( 'Avatar', 'co-authors-plus' ),
|
|
'set_featured_image' => __( 'Set Avatar', 'co-authors-plus' ),
|
|
'use_featured_image' => __( 'Use Avatar', 'co-authors-plus' ),
|
|
'remove_featured_image' => __( 'Remove Avatar', 'co-authors-plus' ),
|
|
) );
|
|
|
|
// Register a post type to store our guest authors
|
|
$args = array(
|
|
'label' => $this->labels['singular'],
|
|
'labels' => array(
|
|
'name' => isset( $this->labels['plural'] ) ? $this->labels['plural'] : '',
|
|
'singular_name' => isset( $this->labels['singular'] ) ? $this->labels['singular'] : '',
|
|
'add_new' => _x( 'Add New', 'guest author', 'co-authors-plus' ),
|
|
'all_items' => isset( $this->labels['all_items'] ) ? $this->labels['all_items'] : '',
|
|
'add_new_item' => isset( $this->labels['add_new_item'] ) ? $this->labels['add_new_item'] : '',
|
|
'edit_item' => isset( $this->labels['edit_item'] ) ? $this->labels['edit_item'] : '',
|
|
'new_item' => isset( $this->labels['new_item'] ) ? $this->labels['new_item'] : '',
|
|
'view_item' => isset( $this->labels['view_item'] ) ? $this->labels['view_item'] : '',
|
|
'search_items' => isset( $this->labels['search_items'] ) ? $this->labels['search_items'] : '',
|
|
'not_found' => isset( $this->labels['not_found'] ) ? $this->labels['not_found'] : '',
|
|
'not_found_in_trash' => isset( $this->labels['not_found_in_trash'] ) ? $this->labels['not_found_in_trash'] : '',
|
|
'featured_image' => isset( $this->labels['featured_image'] ) ? $this->labels['featured_image'] : '',
|
|
'set_featured_image' => isset( $this->labels['set_featured_image'] ) ? $this->labels['set_featured_image'] : '',
|
|
'use_featured_image' => isset( $this->labels['use_featured_image'] ) ? $this->labels['use_featured_image'] : '',
|
|
'remove_featured_image' => isset( $this->labels['remove_featured_image'] ) ? $this->labels['remove_featured_image'] : '',
|
|
),
|
|
'public' => true,
|
|
'publicly_queryable' => false,
|
|
'exclude_from_search' => true,
|
|
'show_in_menu' => false,
|
|
'supports' => array(
|
|
'thumbnail',
|
|
),
|
|
'taxonomies' => array(
|
|
$coauthors_plus->coauthor_taxonomy,
|
|
),
|
|
'rewrite' => false,
|
|
'query_var' => false,
|
|
);
|
|
register_post_type( $this->post_type, $args );
|
|
|
|
// Some of the common sizes used by get_avatar
|
|
$this->avatar_sizes = array();
|
|
|
|
// Hacky way to remove the title and the editor
|
|
remove_post_type_support( $this->post_type, 'title' );
|
|
remove_post_type_support( $this->post_type, 'editor' );
|
|
|
|
}
|
|
|
|
/**
|
|
* Filter the messages that appear when saving or updating a guest author
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function filter_post_updated_messages( $messages ) {
|
|
global $post;
|
|
|
|
if ( $this->post_type !== $post->post_type ) {
|
|
return $messages;
|
|
}
|
|
|
|
$guest_author = $this->get_guest_author_by( 'ID', $post->ID );
|
|
$guest_author_link = $this->filter_author_link( '', $guest_author->ID, $guest_author->user_nicename );
|
|
|
|
$messages[ $this->post_type ] = array(
|
|
0 => '', // Unused. Messages start at index 1.
|
|
1 => sprintf( __( 'Guest author updated. <a href="%s">View profile</a>', 'co-authors-plus' ), esc_url( $guest_author_link ) ),
|
|
2 => __( 'Custom field updated.', 'co-authors-plus' ),
|
|
3 => __( 'Custom field deleted.', 'co-authors-plus' ),
|
|
4 => __( 'Guest author updated.', 'co-authors-plus' ),
|
|
/* translators: %s: date and time of the revision */
|
|
5 => isset( $_GET['revision'] ) ? sprintf( __( 'Guest author restored to revision from %s', 'co-authors-plus' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
|
|
6 => sprintf( __( 'Guest author updated. <a href="%s">View profile</a>', 'co-authors-plus' ), esc_url( $guest_author_link ) ),
|
|
7 => __( 'Guest author saved.', 'co-authors-plus' ),
|
|
8 => sprintf( __( 'Guest author submitted. <a target="_blank" href="%s">Preview profile</a>', 'co-authors-plus' ), esc_url( add_query_arg( 'preview', 'true', $guest_author_link ) ) ),
|
|
9 => sprintf( __( 'Guest author scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview profile</a>', 'co-authors-plus' ),
|
|
// translators: Publish box date format, see http://php.net/date
|
|
date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( $guest_author_link ) ),
|
|
10 => sprintf( __( 'Guest author updated. <a target="_blank" href="%s">Preview profile</a>', 'co-authors-plus' ), esc_url( add_query_arg( 'preview', 'true', $guest_author_link ) ) ),
|
|
);
|
|
return $messages;
|
|
}
|
|
|
|
/**
|
|
* Handle the admin action to create a guest author based
|
|
* on an existing user
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function handle_create_guest_author_action() {
|
|
|
|
if ( ! isset( $_GET['action'], $_GET['nonce'], $_GET['user_id'] ) || 'cap-create-guest-author' !== $_GET['action'] ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! wp_verify_nonce( $_GET['nonce'], 'create-guest-author' ) ) {
|
|
wp_die( esc_html__( "Doin' something fishy, huh?", 'co-authors-plus' ) );
|
|
}
|
|
|
|
if ( ! current_user_can( $this->list_guest_authors_cap ) ) {
|
|
wp_die( esc_html__( "You don't have permission to perform this action.", 'co-authors-plus' ) );
|
|
}
|
|
|
|
$user_id = intval( $_GET['user_id'] );
|
|
|
|
// Create the guest author
|
|
$post_id = $this->create_guest_author_from_user_id( $user_id );
|
|
if ( is_wp_error( $post_id ) ) {
|
|
wp_die( esc_html( $post_id->get_error_message() ) );
|
|
}
|
|
|
|
do_action( 'cap_guest_author_create' );
|
|
|
|
// Redirect to the edit Guest Author screen
|
|
$edit_link = get_edit_post_link( $post_id, 'redirect' );
|
|
$redirect_to = add_query_arg( 'message', 'guest-author-created', $edit_link );
|
|
wp_safe_redirect( esc_url_raw( $redirect_to ) );
|
|
exit;
|
|
|
|
}
|
|
|
|
/**
|
|
* Handle the admin action to delete a guest author and possibly reassign their posts
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function handle_delete_guest_author_action() {
|
|
global $coauthors_plus;
|
|
|
|
if ( ! isset( $_POST['action'], $_POST['reassign'], $_POST['_wpnonce'], $_POST['id'] ) || 'delete-guest-author' != $_POST['action'] ) {
|
|
return;
|
|
}
|
|
|
|
// Verify the user is who they say they are
|
|
if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'delete-guest-author' ) ) {
|
|
wp_die( esc_html__( "Doin' something fishy, huh?", 'co-authors-plus' ) );
|
|
}
|
|
|
|
// Make sure they can perform the action
|
|
if ( ! current_user_can( $this->list_guest_authors_cap ) ) {
|
|
wp_die( esc_html__( "You don't have permission to perform this action.", 'co-authors-plus' ) );
|
|
}
|
|
|
|
// Make sure the guest author actually exists
|
|
$guest_author = $this->get_guest_author_by( 'ID', (int) $_POST['id'] );
|
|
if ( ! $guest_author ) {
|
|
wp_die( esc_html( sprintf( __( "%s can't be deleted because it doesn't exist.", 'co-authors-plus' ), $this->labels['singular'] ) ) );
|
|
}
|
|
|
|
// Perform the reassignment if needed
|
|
$guest_author_term = $coauthors_plus->get_author_term( $guest_author );
|
|
switch ( $_POST['reassign'] ) {
|
|
// Leave assigned to the current linked account
|
|
case 'leave-assigned':
|
|
$reassign_to = $guest_author->linked_account;
|
|
break;
|
|
// Reassign to a different user
|
|
case 'reassign-another':
|
|
$user_nicename = sanitize_title( $_POST['leave-assigned-to'] );
|
|
$reassign_to = $coauthors_plus->get_coauthor_by( 'user_nicename', $user_nicename );
|
|
if ( ! $reassign_to ) {
|
|
wp_die( esc_html__( 'Co-author does not exists. Try again?', 'co-authors-plus' ) );
|
|
}
|
|
$reassign_to = $reassign_to->user_login;
|
|
break;
|
|
// Remove the byline, but don't delete the post
|
|
case 'remove-byline':
|
|
$reassign_to = false;
|
|
break;
|
|
default:
|
|
wp_die( esc_html__( 'Please make sure to pick an option.', 'co-authors-plus' ) );
|
|
break;
|
|
}
|
|
|
|
$retval = $this->delete( $guest_author->ID, $reassign_to );
|
|
|
|
$args = array(
|
|
'page' => 'view-guest-authors',
|
|
);
|
|
if ( is_wp_error( $retval ) ) {
|
|
$args['message'] = 'delete-error';
|
|
} else {
|
|
$args['message'] = 'guest-author-deleted';
|
|
|
|
do_action( 'cap_guest_author_del' );
|
|
}
|
|
|
|
// Redirect to safety
|
|
$redirect_to = add_query_arg( array_map( 'rawurlencode', $args ), admin_url( $this->parent_page ) );
|
|
wp_safe_redirect( esc_url_raw( $redirect_to ) );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Given a search query, suggest some co-authors that might match it
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function handle_ajax_search_coauthors_to_assign() {
|
|
global $coauthors_plus;
|
|
|
|
if ( ! current_user_can( $this->list_guest_authors_cap ) ) {
|
|
die();
|
|
}
|
|
|
|
$search = sanitize_text_field( $_GET['q'] );
|
|
if ( ! empty( $_GET['guest_author'] ) ) {
|
|
$ignore = array( $this->get_guest_author_by( 'ID', (int) $_GET['guest_author'] )->user_login );
|
|
} else {
|
|
$ignore = array();
|
|
}
|
|
|
|
$results = wp_list_pluck( $coauthors_plus->search_authors( $search, $ignore ), 'user_login' );
|
|
$retval = array();
|
|
foreach ( $results as $user_login ) {
|
|
$coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $user_login );
|
|
$retval[] = (object) array(
|
|
'display_name' => $coauthor->display_name,
|
|
'user_login' => $coauthor->user_login,
|
|
'id' => $coauthor->user_nicename,
|
|
);
|
|
}
|
|
echo wp_json_encode( $retval );
|
|
die();
|
|
}
|
|
|
|
|
|
/**
|
|
* Some redirection we need to do for linked accounts
|
|
*
|
|
* @todo support author ID query vars
|
|
*/
|
|
function action_parse_request( $query ) {
|
|
|
|
if ( ! isset( $query->query_vars['author_name'] ) ) {
|
|
return $query;
|
|
}
|
|
|
|
// No redirection needed on admin requests
|
|
if ( is_admin() ) {
|
|
return $query;
|
|
}
|
|
|
|
$coauthor = $this->get_guest_author_by( 'linked_account', sanitize_title( $query->query_vars['author_name'] ) );
|
|
if ( is_object( $coauthor ) && $query->query_vars['author_name'] != $coauthor->user_login ) {
|
|
global $wp_rewrite;
|
|
$link = $wp_rewrite->get_author_permastruct();
|
|
|
|
if ( empty( $link ) ) {
|
|
$file = home_url( '/' );
|
|
$link = $file . '?author_name=' . $coauthor->user_login;
|
|
} else {
|
|
$link = str_replace( '%author%', $coauthor->user_login, $link );
|
|
$link = home_url( user_trailingslashit( $link ) );
|
|
}
|
|
wp_safe_redirect( $link );
|
|
exit;
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Add the admin menus for seeing all co-authors
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function action_admin_menu() {
|
|
|
|
add_submenu_page( $this->parent_page, $this->labels['plural'], $this->labels['plural'], $this->list_guest_authors_cap, 'view-guest-authors', array( $this, 'view_guest_authors_list' ) );
|
|
|
|
}
|
|
|
|
/**
|
|
* Enqueue any scripts or styles used for Guest Authors
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function action_admin_enqueue_scripts() {
|
|
global $pagenow;
|
|
// Enqueue our guest author CSS on the related pages
|
|
if ( $this->parent_page === $pagenow && isset( $_GET['page'] ) && 'view-guest-authors' === $_GET['page'] ) {
|
|
wp_enqueue_script( 'jquery-select2', plugins_url( 'lib/select2/select2.min.js', dirname( __FILE__ ) ), array( 'jquery' ), COAUTHORS_PLUS_VERSION );
|
|
wp_enqueue_style( 'cap-jquery-select2-css', plugins_url( 'lib/select2/select2.css', dirname( __FILE__ ) ), false, COAUTHORS_PLUS_VERSION );
|
|
|
|
wp_enqueue_style( 'guest-authors-css', plugins_url( 'css/guest-authors.css', dirname( __FILE__ ) ), false, COAUTHORS_PLUS_VERSION );
|
|
wp_enqueue_script( 'guest-authors-js', plugins_url( 'js/guest-authors.js', dirname( __FILE__ ) ), false, COAUTHORS_PLUS_VERSION );
|
|
} else if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) && $this->post_type === get_post_type() ) {
|
|
add_action( 'admin_head', array( $this, 'change_title_icon' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change the icon appearing next to the title
|
|
* Core doesn't allow us to filter screen_icon(), so changing the ID is the next best thing
|
|
*
|
|
* @since 3.0.1
|
|
*/
|
|
function change_title_icon() {
|
|
?>
|
|
<script type="text/javascript">
|
|
jQuery(document).ready(function($){
|
|
$('#icon-edit').attr('id', 'icon-users');
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Show some extra notices to the user
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function action_admin_notices() {
|
|
global $pagenow;
|
|
|
|
if ( $this->parent_page != $pagenow || ! isset( $_REQUEST['message'] ) ) {
|
|
return;
|
|
}
|
|
|
|
switch ( $_REQUEST['message'] ) {
|
|
case 'guest-author-deleted':
|
|
$message = __( 'Guest author deleted.', 'co-authors-plus' );
|
|
break;
|
|
default:
|
|
$message = false;
|
|
break;
|
|
}
|
|
|
|
if ( $message ) {
|
|
echo '<div class="updated"><p>' . esc_html( $message ) . '</p></div>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register the metaboxes used for Guest Authors
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function action_add_meta_boxes() {
|
|
global $coauthors_plus;
|
|
|
|
if ( get_post_type() == $this->post_type ) {
|
|
// Remove the submitpost metabox because we have our own
|
|
remove_meta_box( 'submitdiv', $this->post_type, 'side' );
|
|
remove_meta_box( 'slugdiv', $this->post_type, 'normal' );
|
|
add_meta_box( 'coauthors-manage-guest-author-save', __( 'Save', 'co-authors-plus' ), array( $this, 'metabox_manage_guest_author_save' ), $this->post_type, 'side', 'default' );
|
|
add_meta_box( 'coauthors-manage-guest-author-slug', __( 'Unique Slug', 'co-authors-plus' ), array( $this, 'metabox_manage_guest_author_slug' ), $this->post_type, 'side', 'default' );
|
|
// Our metaboxes with co-author details
|
|
add_meta_box( 'coauthors-manage-guest-author-name', __( 'Name', 'co-authors-plus' ), array( $this, 'metabox_manage_guest_author_name' ), $this->post_type, 'normal', 'default' );
|
|
add_meta_box( 'coauthors-manage-guest-author-contact-info', __( 'Contact Info', 'co-authors-plus' ), array( $this, 'metabox_manage_guest_author_contact_info' ), $this->post_type, 'normal', 'default' );
|
|
add_meta_box( 'coauthors-manage-guest-author-bio', $this->labels['metabox_about'], array( $this, 'metabox_manage_guest_author_bio' ), $this->post_type, 'normal', 'default' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* View a list table of all guest authors
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function view_guest_authors_list() {
|
|
|
|
// Allow guest authors to be deleted
|
|
if ( isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] ) && 'delete' == $_GET['action'] ) {
|
|
// Make sure the user is who they say they are
|
|
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'guest-author-delete' ) ) {
|
|
wp_die( esc_html__( "Doin' something fishy, huh?", 'co-authors-plus' ) );
|
|
}
|
|
|
|
// Make sure the guest author actually exists
|
|
$guest_author = $this->get_guest_author_by( 'ID', (int) $_GET['id'] );
|
|
if ( ! $guest_author ) {
|
|
wp_die( esc_html( sprintf( __( "%s can't be deleted because it doesn't exist.", 'co-authors-plus' ), $this->labels['singular'] ) ) );
|
|
}
|
|
|
|
// get post count
|
|
global $coauthors_plus;
|
|
$count = $coauthors_plus->get_guest_author_post_count( $guest_author );
|
|
|
|
echo '<div class="wrap">';
|
|
echo '<div class="icon32" id="icon-users"><br/></div>';
|
|
echo '<h2>' . esc_html( sprintf( __( 'Delete %s', 'co-authors-plus ' ), $this->labels['plural'] ) ) . '</h2>';
|
|
echo '<p>' . esc_html( sprintf( __( 'You have specified this %s for deletion:', 'co-authors-plus' ), strtolower( $this->labels['singular'] ) ) ) . '</p>';
|
|
echo '<p>#' . esc_html( $guest_author->ID . ': ' . $guest_author->display_name ) . '</p>';
|
|
// display wording differently per post count
|
|
if ( 0 === $count ) {
|
|
$post_count_message = '<p>' . sprintf( __( 'There are no posts associated with this guest author.', 'co-authors-plus' ), strtolower( $this->labels['singular'] ) ) . '</p>';
|
|
}
|
|
else {
|
|
$note = '<p class="description">' . sprintf( __( "Note: If you'd like to delete the %s and all of their posts, you should delete their posts first and then come back to delete the %s.", 'co-authors-plus' ), strtolower( $this->labels['singular'] ), strtolower( $this->labels['singular'] ) ) . '</p>';
|
|
if ( 1 === $count ) {
|
|
$post_count_message = '<p>' . sprintf( __( 'There is %d post associated with this guest author. What should be done with the post assigned to this %s?', 'co-authors-plus' ), $count, strtolower( $this->labels['singular'] ) ) . '</p>';
|
|
}
|
|
else {
|
|
$post_count_message = '<p>' . sprintf( __( 'There are %d posts associated with this guest author. What should be done with the posts assigned to this %s?', 'co-authors-plus' ), $count, strtolower( $this->labels['singular'] ) ) . '</p>';
|
|
}
|
|
$post_count_message .= $note;
|
|
}
|
|
$allowed_html = array(
|
|
'p' => array(
|
|
'class' => array(),
|
|
),
|
|
);
|
|
echo wp_kses( $post_count_message, $allowed_html );
|
|
echo '<form method="POST" action="' . esc_url( add_query_arg( 'page', 'view-guest-authors', admin_url( $this->parent_page ) ) ) . '">';
|
|
// Hidden stuffs
|
|
echo '<input type="hidden" name="action" value="delete-guest-author" />';
|
|
wp_nonce_field( 'delete-guest-author' );
|
|
echo '<input type="hidden" id="id" name="id" value="' . esc_attr( (int) $_GET['id'] ) . '" />';
|
|
echo '<fieldset><ul style="list-style-type:none;">';
|
|
// only show delete options if post count > 0
|
|
if ( $count > 0 ) {
|
|
// Reassign to another user
|
|
echo '<li class="hide-if-no-js"><label for="reassign-another">';
|
|
echo '<input type="radio" id="reassign-another" name="reassign" class="reassign-option" value="reassign-another" /> ' . esc_html__( 'Reassign to another co-author:', 'co-authors-plus' ) . ' </label>';
|
|
echo '<input type="hidden" id="leave-assigned-to" name="leave-assigned-to" style="width:200px;" />';
|
|
echo '</li>';
|
|
// Leave mapped to a linked account
|
|
if ( get_user_by( 'login', $guest_author->linked_account ) ) {
|
|
echo '<li><label for="leave-assigned">';
|
|
echo '<input type="radio" id="leave-assigned" class="reassign-option" name="reassign" value="leave-assigned" /> ' . esc_html( sprintf( __( 'Leave posts assigned to the mapped user, %s.', 'co-authors-plus' ), $guest_author->linked_account ) );
|
|
echo '</label></li>';
|
|
}
|
|
// Remove bylines from the posts
|
|
echo '<li><label for="remove-byline">';
|
|
echo '<input type="radio" id="remove-byline" class="reassign-option" name="reassign" value="remove-byline" /> ' . esc_html__( 'Remove byline from posts (but leave each post in its current status).', 'co-authors-plus' );
|
|
echo '</label></li>';
|
|
}
|
|
else {
|
|
echo '<input type="hidden" id="remove-byline" class="reassign-option" name="reassign" value="remove-byline" checked="checked" />';
|
|
}
|
|
echo '</ul></fieldset>';
|
|
// disable disabled submit button for 0 post count
|
|
if ( 0 === $count ) {
|
|
submit_button( __( 'Confirm Deletion', 'co-authors-plus' ), 'secondary', 'submit', true );
|
|
}
|
|
else {
|
|
submit_button( __( 'Confirm Deletion', 'co-authors-plus' ), 'secondary', 'submit', true, array( 'disabled' => 'disabled' ) );
|
|
}
|
|
echo '</form>';
|
|
echo '</div>';
|
|
} else {
|
|
echo '<div class="wrap">';
|
|
echo '<div class="icon32" id="icon-users"><br/></div>';
|
|
echo '<h2>' . esc_html( $this->labels['plural'] );
|
|
// @todo caps check for creating a new user
|
|
$add_new_link = admin_url( "post-new.php?post_type=$this->post_type" );
|
|
echo '<a href="' . esc_url( $add_new_link ) . '" class="add-new-h2">' . esc_html__( 'Add New', 'co-authors-plus' ) . '</a>';
|
|
echo '</h2>';
|
|
$cap_list_table = new CoAuthors_WP_List_Table();
|
|
$cap_list_table->prepare_items();
|
|
echo '<form id="guest-authors-filter" action="" method="GET">';
|
|
echo '<input type="hidden" name="page" value="view-guest-authors" />';
|
|
$cap_list_table->display();
|
|
echo '</form>';
|
|
echo '</div>';
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Metabox for saving or updating a Guest Author
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function metabox_manage_guest_author_save() {
|
|
global $post, $coauthors_plus;
|
|
|
|
if ( in_array( $post->post_status, array( 'pending', 'publish', 'draft' ) ) ) {
|
|
$button_text = $this->labels['update_item'];
|
|
} else {
|
|
$button_text = $this->labels['add_new_item'];
|
|
}
|
|
submit_button( $button_text, 'primary', 'publish', false );
|
|
|
|
// Secure all of our requests
|
|
wp_nonce_field( 'guest-author-nonce', 'guest-author-nonce' );
|
|
|
|
}
|
|
|
|
/**
|
|
* Metabox for editing this guest author's slug or changing the linked account
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function metabox_manage_guest_author_slug() {
|
|
global $post;
|
|
|
|
$pm_key = $this->get_post_meta_key( 'user_login' );
|
|
$existing_slug = get_post_meta( $post->ID, $pm_key, true );
|
|
|
|
echo '<input type="text" disabled="disabled" name="' . esc_attr( $pm_key ) . '" value="' . esc_attr( urldecode( $existing_slug ) ) . '" />';
|
|
|
|
// Taken from grist_authors.
|
|
$linked_account_key = $this->get_post_meta_key( 'linked_account' );
|
|
$linked_account = get_post_meta( $post->ID, $linked_account_key, true );
|
|
if ( $user = get_user_by( 'login', $linked_account ) ) {
|
|
$linked_account_id = $user->ID;
|
|
} else {
|
|
$linked_account_id = -1;
|
|
}
|
|
|
|
// If user_login is the same as linked account, don't let the association be removed
|
|
if ( $linked_account == $existing_slug ) {
|
|
add_filter( 'wp_dropdown_users', array( $this, 'filter_wp_dropdown_users_to_disable' ) );
|
|
}
|
|
|
|
$linked_account_user_ids = wp_list_pluck( $this->get_all_linked_accounts(), 'ID' );
|
|
if ( false !== ( $key = array_search( $linked_account_id, $linked_account_user_ids ) ) ) {
|
|
unset( $linked_account_user_ids[ $key ] );
|
|
}
|
|
|
|
echo '<p><label>' . esc_html__( 'WordPress User Mapping', 'co-authors-plus' ) . '</label> ';
|
|
wp_dropdown_users( apply_filters( 'coauthors_guest_author_linked_account_args', array(
|
|
'show_option_none' => __( '-- Not mapped --', 'co-authors-plus' ),
|
|
'name' => esc_attr( $this->get_post_meta_key( 'linked_account' ) ),
|
|
// If we're adding an author or if there is no post author (0), then use -1 (which is show_option_none).
|
|
// We then take -1 on save and convert it back to 0. (#blamenacin)
|
|
'selected' => $linked_account_id,
|
|
// Don't let user accounts to be linked to more than one guest author
|
|
'exclude' => $linked_account_user_ids,
|
|
) ) );
|
|
echo '</p>';
|
|
|
|
remove_filter( 'wp_dropdown_users', array( $this, 'filter_wp_dropdown_users_to_disable' ) );
|
|
}
|
|
|
|
/**
|
|
* Make a wp_dropdown_users disabled
|
|
* Only applied if the user_login value for the guest author matches its linked account
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
public function filter_wp_dropdown_users_to_disable( $output ) {
|
|
return str_replace( '<select ', '<select disabled="disabled" ', $output );
|
|
}
|
|
|
|
/**
|
|
* Metabox to display all of the pertient names for a Guest Author not linked to user account
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function metabox_manage_guest_author_name() {
|
|
global $post;
|
|
|
|
$fields = $this->get_guest_author_fields( 'name' );
|
|
echo '<table class="form-table"><tbody>';
|
|
foreach ( $fields as $field ) {
|
|
$pm_key = $this->get_post_meta_key( $field['key'] );
|
|
$value = get_post_meta( $post->ID, $pm_key, true );
|
|
echo '<tr><th>';
|
|
echo '<label for="' . esc_attr( $pm_key ) . '">' . esc_html( $field['label'] ) . '</label>';
|
|
echo '</th><td>';
|
|
|
|
if ( ! isset( $field['input'] ) ) {
|
|
$field['input'] = 'text';
|
|
}
|
|
$field['input'] = apply_filters( 'coauthors_name_field_type_'. $pm_key , $field['input'] );
|
|
switch ( $field['input'] ) {
|
|
case 'checkbox':
|
|
echo '<input type="checkbox" name="' . esc_attr( $pm_key ) . '"'. checked( '1', $value, false ) .' value="1"/>';
|
|
break;
|
|
default:
|
|
echo '<input type="'. esc_attr( $field['input'] ) .'" name="' . esc_attr( $pm_key ) . '" value="' . esc_attr( $value ) . '" class="regular-text" />';
|
|
break;
|
|
}
|
|
echo '</td></tr>';
|
|
}
|
|
echo '</tbody></table>';
|
|
|
|
}
|
|
|
|
/**
|
|
* Metabox to display all of the pertient contact details for a Guest Author not linked to
|
|
* user account
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function metabox_manage_guest_author_contact_info() {
|
|
global $post;
|
|
|
|
$fields = $this->get_guest_author_fields( 'contact-info' );
|
|
echo '<table class="form-table"><tbody>';
|
|
foreach ( $fields as $field ) {
|
|
$pm_key = $this->get_post_meta_key( $field['key'] );
|
|
$value = get_post_meta( $post->ID, $pm_key, true );
|
|
echo '<tr><th>';
|
|
echo '<label for="' . esc_attr( $pm_key ) . '">' . esc_html( $field['label'] ) . '</label>';
|
|
echo '</th><td>';
|
|
|
|
if ( ! isset( $field['input'] ) ) {
|
|
$field['input'] = 'text';
|
|
}
|
|
$field['input'] = apply_filters( 'coauthors_name_field_type_'. $pm_key , $field['input'] );
|
|
switch ( $field['input'] ) {
|
|
case 'checkbox':
|
|
echo '<input type="checkbox" name="' . esc_attr( $pm_key ) . '"'. checked( '1', $value, false ) .' value="1"/>';
|
|
break;
|
|
default:
|
|
echo '<input type="'. esc_attr( $field['input'] ) .'" name="' . esc_attr( $pm_key ) . '" value="' . esc_attr( $value ) . '" class="regular-text" />';
|
|
break;
|
|
}
|
|
|
|
echo '</td></tr>';
|
|
}
|
|
echo '</tbody></table>';
|
|
|
|
}
|
|
|
|
/**
|
|
* Metabox to edit the bio and other biographical details of the Guest Author
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function metabox_manage_guest_author_bio() {
|
|
global $post;
|
|
|
|
$fields = $this->get_guest_author_fields( 'about' );
|
|
echo '<table class="form-table"><tbody>';
|
|
foreach ( $fields as $field ) {
|
|
$pm_key = $this->get_post_meta_key( $field['key'] );
|
|
$value = get_post_meta( $post->ID, $pm_key, true );
|
|
printf( '
|
|
<tr>
|
|
<th>
|
|
<label for="%s">%s</label>
|
|
</th>
|
|
<td>
|
|
<textarea style="width:300px;margin-bottom:6px;" name="%s">%s</textarea>
|
|
</td>
|
|
</tr>
|
|
',
|
|
esc_attr( $pm_key ),
|
|
esc_html( $field['label'] ),
|
|
esc_attr( $pm_key ),
|
|
esc_textarea( $value )
|
|
);
|
|
}
|
|
echo '</tbody></table>';
|
|
|
|
}
|
|
|
|
/**
|
|
* When a guest author is created or updated, we need to properly create
|
|
* the post_name based on some data provided by the user
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function manage_guest_author_filter_post_data( $post_data, $original_args ) {
|
|
|
|
if ( $post_data['post_type'] != $this->post_type ) {
|
|
return $post_data;
|
|
}
|
|
|
|
// @todo caps check
|
|
if ( ! isset( $_POST['guest-author-nonce'] ) || ! wp_verify_nonce( $_POST['guest-author-nonce'], 'guest-author-nonce' ) ) {
|
|
return $post_data;
|
|
}
|
|
|
|
// Validate the display name
|
|
if ( empty( $_POST['cap-display_name'] ) ) {
|
|
wp_die( esc_html__( 'Guest authors cannot be created without display names.', 'co-authors-plus' ) );
|
|
}
|
|
$post_data['post_title'] = sanitize_text_field( $_POST['cap-display_name'] );
|
|
|
|
$slug = sanitize_title( get_post_meta( $original_args['ID'], $this->get_post_meta_key( 'user_login' ), true ) );
|
|
if ( ! $slug ) {
|
|
$slug = sanitize_title( $_POST['cap-display_name'] );
|
|
}
|
|
|
|
// Uh oh, no guest authors without slugs
|
|
if ( ! $slug ) {
|
|
wp_die( esc_html__( 'Guest authors cannot be created without display names.', 'co-authors-plus' ) );
|
|
}
|
|
$post_data['post_name'] = $this->get_post_meta_key( $slug );
|
|
|
|
// Guest authors can't be created with the same user_login as a user
|
|
$user_nicename = str_replace( 'cap-', '', $slug );
|
|
$user = get_user_by( 'slug', $user_nicename );
|
|
if ( $user
|
|
&& is_user_member_of_blog( $user->ID, get_current_blog_id() )
|
|
&& $user->user_login != get_post_meta( $original_args['ID'], $this->get_post_meta_key( 'linked_account' ), true ) ) {
|
|
wp_die( esc_html__( 'Guest authors cannot be created with the same user_login value as a user. Try creating a profile from the user on the Manage Users listing instead.', 'co-authors-plus' ) );
|
|
}
|
|
|
|
// Guest authors can't have the same post_name value
|
|
$guest_author = $this->get_guest_author_by( 'post_name', $post_data['post_name'] );
|
|
if ( $guest_author && $guest_author->ID != $original_args['ID'] ) {
|
|
wp_die( esc_html__( 'Display name conflicts with another guest author display name.', 'co-authors-plus' ) );
|
|
}
|
|
|
|
return $post_data;
|
|
}
|
|
|
|
/**
|
|
* Save the various meta fields associated with our guest author model
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function manage_guest_author_save_meta_fields( $post_id, $post ) {
|
|
global $coauthors_plus;
|
|
|
|
if ( $post->post_type != $this->post_type ) {
|
|
return;
|
|
}
|
|
|
|
// @todo caps check
|
|
if ( ! isset( $_POST['guest-author-nonce'] ) || ! wp_verify_nonce( $_POST['guest-author-nonce'], 'guest-author-nonce' ) ) {
|
|
return;
|
|
}
|
|
|
|
// Save our data to post meta
|
|
$author_fields = $this->get_guest_author_fields();
|
|
foreach ( $author_fields as $author_field ) {
|
|
|
|
$key = $this->get_post_meta_key( $author_field['key'] );
|
|
// 'user_login' should only be saved on post update if it doesn't exist
|
|
if ( 'user_login' == $author_field['key'] && ! get_post_meta( $post_id, $key, true ) ) {
|
|
$display_name_key = $this->get_post_meta_key( 'display_name' );
|
|
$temp_slug = sanitize_title( $_POST[ $display_name_key ] );
|
|
update_post_meta( $post_id, $key, $temp_slug );
|
|
continue;
|
|
}
|
|
if ( 'linked_account' == $author_field['key'] ) {
|
|
$linked_account_key = $this->get_post_meta_key( 'linked_account' );
|
|
if ( ! empty( $_POST[ $linked_account_key ] ) ) {
|
|
$user_id = intval( $_POST[ $linked_account_key ] );
|
|
} else {
|
|
continue;
|
|
}
|
|
$user = get_user_by( 'id', $user_id );
|
|
if ( $user_id > 0 && is_object( $user ) ) {
|
|
$user_login = $user->user_login;
|
|
} else {
|
|
$user_login = '';
|
|
}
|
|
update_post_meta( $post_id, $key, $user_login );
|
|
continue;
|
|
}
|
|
|
|
if ( isset( $author_field['input'] ) && 'checkbox' === $author_field['input'] && ! isset( $_POST[ $key ] ) ) {
|
|
delete_post_meta( $post_id, $key );
|
|
}
|
|
|
|
if ( ! isset( $_POST[ $key ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( isset( $author_field['sanitize_function'] ) && is_callable( $author_field['sanitize_function'] ) ) {
|
|
$value = call_user_func( $author_field['sanitize_function'], $_POST[ $key ] );
|
|
} else {
|
|
$value = sanitize_text_field( $_POST[ $key ] );
|
|
}
|
|
update_post_meta( $post_id, $key, $value );
|
|
}
|
|
|
|
$author = $this->get_guest_author_by( 'ID', $post_id );
|
|
$author_term = $coauthors_plus->update_author_term( $author );
|
|
// Add the author as a post term
|
|
wp_set_post_terms( $post_id, array( $author_term->slug ), $coauthors_plus->coauthor_taxonomy, false );
|
|
|
|
// Explicitly clear all caches, to remove negative caches that may have existed prior to this
|
|
// Guest Author's creation / update
|
|
$this->delete_guest_author_cache( $post_id );
|
|
}
|
|
|
|
/**
|
|
* Return a simulated WP_User object based on the post ID
|
|
* of a guest author
|
|
*
|
|
* @since 3.0
|
|
*
|
|
* @param string $key Key to search by (login,email)
|
|
* @param string $value Value to search for
|
|
* @param object|false $coauthor The guest author on success, false on failure
|
|
*/
|
|
function get_guest_author_by( $key, $value, $force = false ) {
|
|
global $wpdb;
|
|
|
|
$cache_key = $this->get_cache_key( $key, $value );
|
|
|
|
if ( false == $force && false !== ( $retval = wp_cache_get( $cache_key, self::$cache_group ) ) ) {
|
|
// Properly catch our false condition cache
|
|
if ( is_object( $retval ) ) {
|
|
return $retval;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
switch ( $key ) {
|
|
case 'ID':
|
|
case 'id':
|
|
$query = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID=%d AND post_type = %s", $value, $this->post_type );
|
|
$post_id = $wpdb->get_var( $query );
|
|
if ( empty( $post_id ) ) {
|
|
$post_id = '0';
|
|
}
|
|
break;
|
|
case 'user_nicename':
|
|
case 'post_name':
|
|
$value = $this->get_post_meta_key( $value );
|
|
$query = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_name=%s AND post_type = %s", $value, $this->post_type );
|
|
$post_id = $wpdb->get_var( $query );
|
|
if ( empty( $post_id ) ) {
|
|
$post_id = '0';
|
|
}
|
|
break;
|
|
case 'login':
|
|
case 'user_login':
|
|
case 'linked_account':
|
|
case 'user_email':
|
|
if ( 'login' == $key ) {
|
|
$key = 'user_login';
|
|
}
|
|
// Ensure we aren't doing the lookup by the prefixed value
|
|
if ( 'user_login' == $key ) {
|
|
$value = preg_replace( '#^cap\-#', '', $value );
|
|
}
|
|
$query = $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value=%s;", $this->get_post_meta_key( $key ), $value );
|
|
$post_id = $wpdb->get_var( $query );
|
|
if ( empty( $post_id ) ) {
|
|
if ( 'user_login' == $key ) {
|
|
return $this->get_guest_author_by( 'post_name', $value ); // fallback to post_name in case the guest author isn't a linked account
|
|
}
|
|
$post_id = '0';
|
|
}
|
|
break;
|
|
default:
|
|
$post_id = '0';
|
|
break;
|
|
}
|
|
|
|
if ( ! $post_id ) {
|
|
// Best hacky way to cache the false condition
|
|
wp_cache_set( $cache_key, '0', self::$cache_group );
|
|
return false;
|
|
}
|
|
|
|
$guest_author = array(
|
|
'ID' => $post_id,
|
|
);
|
|
|
|
// Load the guest author fields
|
|
$fields = $this->get_guest_author_fields();
|
|
foreach ( $fields as $field ) {
|
|
$key = $field['key'];
|
|
$pm_key = $this->get_post_meta_key( $field['key'] );
|
|
$guest_author[ $key ] = get_post_meta( $post_id, $pm_key, true );
|
|
}
|
|
// Support for non-Latin characters. They're stored as urlencoded slugs
|
|
$guest_author['user_login'] = urldecode( $guest_author['user_login'] );
|
|
|
|
// Hack to model the WP_User object
|
|
$guest_author['user_nicename'] = sanitize_title( $guest_author['user_login'] );
|
|
$guest_author['type'] = 'guest-author';
|
|
|
|
wp_cache_set( $cache_key, (object) $guest_author, self::$cache_group );
|
|
|
|
return (object) $guest_author;
|
|
}
|
|
|
|
/**
|
|
* Get an thumbnail for a Guest Author object
|
|
*
|
|
* @param object The Guest Author object for which to retrieve the thumbnail.
|
|
* @param int The desired image size.
|
|
* @param array|string Optional. An array or string of additional classes. Default null.
|
|
* @return string The thumbnail image tag, or null if one doesn't exist.
|
|
*/
|
|
function get_guest_author_thumbnail( $guest_author, $size, $class = null ) {
|
|
// See if the guest author has an avatar
|
|
if ( ! has_post_thumbnail( $guest_author->ID ) ) {
|
|
return null;
|
|
}
|
|
|
|
$args = array(
|
|
'class' => "avatar avatar-{$size} photo",
|
|
);
|
|
if ( ! empty( $class ) ) {
|
|
if ( is_array( $class ) ) {
|
|
$class = implode( ' ', $class );
|
|
}
|
|
$args['class'] .= " $class";
|
|
}
|
|
|
|
$size = array( $size, $size );
|
|
|
|
$thumbnail = get_the_post_thumbnail( $guest_author->ID, $size, $args );
|
|
|
|
return $thumbnail;
|
|
}
|
|
|
|
/**
|
|
* Get all of the meta fields that can be associated with a guest author
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function get_guest_author_fields( $groups = 'all' ) {
|
|
|
|
$groups = (array) $groups;
|
|
$global_fields = array(
|
|
// Hidden (included in object, no UI elements)
|
|
array(
|
|
'key' => 'ID',
|
|
'label' => __( 'ID', 'co-authors-plus' ),
|
|
'group' => 'hidden',
|
|
'input' => 'hidden',
|
|
),
|
|
// Name
|
|
array(
|
|
'key' => 'display_name',
|
|
'label' => __( 'Display Name', 'co-authors-plus' ),
|
|
'group' => 'name',
|
|
'required' => true,
|
|
),
|
|
array(
|
|
'key' => 'first_name',
|
|
'label' => __( 'First Name', 'co-authors-plus' ),
|
|
'group' => 'name',
|
|
),
|
|
array(
|
|
'key' => 'last_name',
|
|
'label' => __( 'Last Name', 'co-authors-plus' ),
|
|
'group' => 'name',
|
|
),
|
|
array(
|
|
'key' => 'user_login',
|
|
'label' => __( 'Slug', 'co-authors-plus' ),
|
|
'group' => 'slug',
|
|
'required' => true,
|
|
),
|
|
// Contact info
|
|
array(
|
|
'key' => 'user_email',
|
|
'label' => __( 'E-mail', 'co-authors-plus' ),
|
|
'group' => 'contact-info',
|
|
'input' => 'email',
|
|
),
|
|
array(
|
|
'key' => 'linked_account',
|
|
'label' => __( 'Linked Account', 'co-authors-plus' ),
|
|
'group' => 'slug',
|
|
),
|
|
array(
|
|
'key' => 'website',
|
|
'label' => __( 'Website', 'co-authors-plus' ),
|
|
'group' => 'contact-info',
|
|
'input' => 'url',
|
|
),
|
|
array(
|
|
'key' => 'aim',
|
|
'label' => __( 'AIM', 'co-authors-plus' ),
|
|
'group' => 'contact-info',
|
|
),
|
|
array(
|
|
'key' => 'yahooim',
|
|
'label' => __( 'Yahoo IM', 'co-authors-plus' ),
|
|
'group' => 'contact-info',
|
|
),
|
|
array(
|
|
'key' => 'jabber',
|
|
'label' => __( 'Jabber / Google Talk', 'co-authors-plus' ),
|
|
'group' => 'contact-info',
|
|
),
|
|
array(
|
|
'key' => 'description',
|
|
'label' => __( 'Biographical Info', 'co-authors-plus' ),
|
|
'group' => 'about',
|
|
'sanitize_function' => 'wp_filter_post_kses',
|
|
),
|
|
);
|
|
$fields_to_return = array();
|
|
foreach ( $global_fields as $single_field ) {
|
|
if ( in_array( $single_field['group'], $groups ) || 'all' === $groups[0] && 'hidden' !== $single_field['group'] ) {
|
|
$fields_to_return[] = $single_field;
|
|
}
|
|
}
|
|
|
|
return apply_filters( 'coauthors_guest_author_fields', $fields_to_return, $groups );
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets a postmeta key by prefixing it with 'cap-'
|
|
* if not yet prefixed
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function get_post_meta_key( $key ) {
|
|
|
|
if ( 0 !== stripos( $key, 'cap-' ) ) {
|
|
$key = 'cap-' . $key;
|
|
}
|
|
|
|
return $key;
|
|
}
|
|
|
|
/**
|
|
* Build a cache key for a given key/value
|
|
*
|
|
* @param string $key A guest author field
|
|
* @param string $value The guest author field value
|
|
*
|
|
* @return string The generated cache key
|
|
*/
|
|
function get_cache_key( $key, $value ) {
|
|
// Normalize $key and $value
|
|
switch ( $key ) {
|
|
case 'post_name':
|
|
$key = 'user_nicename';
|
|
|
|
if ( 0 === strpos( $value, 'cap-' ) ) {
|
|
$value = substr( $value, 4 );
|
|
}
|
|
|
|
break;
|
|
|
|
case 'login':
|
|
$key = 'user_login';
|
|
|
|
break;
|
|
}
|
|
|
|
$cache_key = md5( 'guest-author-' . $key . '-' . $value );
|
|
|
|
return $cache_key;
|
|
}
|
|
|
|
/**
|
|
* Get all of the user accounts that have been linked
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function get_all_linked_accounts( $force = false ) {
|
|
global $wpdb;
|
|
|
|
$cache_key = 'all-linked-accounts';
|
|
$retval = wp_cache_get( $cache_key, self::$cache_group );
|
|
|
|
if ( true === $force || false === $retval ) {
|
|
$user_logins = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key=%s AND meta_value !=''", $this->get_post_meta_key( 'linked_account' ) ) );
|
|
$users = array();
|
|
foreach ( $user_logins as $user_login ) {
|
|
$user = get_user_by( 'login', $user_login );
|
|
if ( ! $user ) {
|
|
continue;
|
|
}
|
|
$users[] = array(
|
|
'ID' => $user->ID,
|
|
'user_login' => $user->user_login,
|
|
);
|
|
}
|
|
$retval = $users;
|
|
wp_cache_set( $cache_key, $retval, self::$cache_group );
|
|
}
|
|
return ( $retval ) ? $retval : array();
|
|
}
|
|
|
|
/**
|
|
* Filter update post metadata
|
|
* Clean caches when any of the values have been changed
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function filter_update_post_metadata( $retnull, $object_id, $meta_key, $meta_value, $prev_value ) {
|
|
|
|
if ( $this->post_type != get_post_type( $object_id ) ) {
|
|
return $retnull;
|
|
}
|
|
|
|
// If the linked_account is changing, invalidate the cache of all linked accounts
|
|
// Don't regenerate though, as we haven't saved the new value
|
|
$linked_account_key = $this->get_post_meta_key( 'linked_account' );
|
|
if ( $linked_account_key == $meta_key && get_post_meta( $object_id, $linked_account_key, true ) !== $meta_value ) {
|
|
$this->delete_guest_author_cache( $object_id );
|
|
}
|
|
|
|
// If one of the guest author meta values has changed, we'll need to invalidate all keys
|
|
if ( false !== strpos( $meta_key, 'cap-' ) && get_post_meta( $object_id, $meta_key, true ) !== $meta_value ) {
|
|
$this->delete_guest_author_cache( $object_id );
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Delete all of the cache values associated with a guest author
|
|
*
|
|
* @since 3.0
|
|
*
|
|
* @param int|object $guest_author The guest author ID or object
|
|
*/
|
|
public function delete_guest_author_cache( $id_or_object ) {
|
|
|
|
if ( is_object( $id_or_object ) ) {
|
|
$guest_author = $id_or_object;
|
|
} else {
|
|
$guest_author = $this->get_guest_author_by( 'ID', $id_or_object, true );
|
|
}
|
|
|
|
// Delete the lookup cache associated with each old co-author value
|
|
$keys = wp_list_pluck( $this->get_guest_author_fields(), 'key' );
|
|
$keys = array_merge( $keys, array( 'login', 'post_name', 'user_nicename', 'ID' ) );
|
|
foreach ( $keys as $key ) {
|
|
$value_key = $key;
|
|
|
|
if ( 'post_name' == $key ) {
|
|
$value_key = 'user_nicename';
|
|
} else if ( 'login' == $key ) {
|
|
$value_key = 'user_login';
|
|
}
|
|
|
|
$cache_key = $this->get_cache_key( $key, $guest_author->$value_key );
|
|
|
|
wp_cache_delete( $cache_key, self::$cache_group );
|
|
}
|
|
|
|
// Delete the 'all-linked-accounts' cache
|
|
wp_cache_delete( 'all-linked-accounts', self::$cache_group );
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a guest author
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function create( $args ) {
|
|
global $coauthors_plus;
|
|
|
|
// Validate the arguments that have been passed
|
|
$fields = $this->get_guest_author_fields();
|
|
foreach ( $fields as $field ) {
|
|
|
|
// Make sure required fields are there
|
|
if ( isset( $field['required'] ) && $field['required'] && empty( $args[ $field['key'] ] ) ) {
|
|
return new WP_Error( 'field-required', sprintf( __( '%s is a required field', 'co-authors-plus' ), $field['key'] ) );
|
|
}
|
|
|
|
// The user login field shouldn't collide with any existing users
|
|
if ( 'user_login' == $field['key'] && $existing_coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $args['user_login'], true ) ) {
|
|
if ( 'guest-author' == $existing_coauthor->type ) {
|
|
return new WP_Error( 'duplicate-field', __( 'user_login cannot duplicate existing guest author or mapped user', 'co-authors-plus' ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the primary post object
|
|
$new_post = array(
|
|
'post_title' => $args['display_name'],
|
|
'post_name' => sanitize_title( $this->get_post_meta_key( $args['user_login'] ) ),
|
|
'post_type' => $this->post_type,
|
|
);
|
|
$post_id = wp_insert_post( $new_post, true );
|
|
if ( is_wp_error( $post_id ) ) {
|
|
return $post_id;
|
|
}
|
|
|
|
// Add all of the fields for the new guest author
|
|
foreach ( $fields as $field ) {
|
|
$key = $field['key'];
|
|
if ( empty( $args[ $key ] ) ) {
|
|
continue;
|
|
}
|
|
$pm_key = $this->get_post_meta_key( $key );
|
|
update_post_meta( $post_id, $pm_key, $args[ $key ] );
|
|
}
|
|
|
|
// Attach the avatar / featured image.
|
|
if ( ! empty( $args[ 'avatar' ] ) ) {
|
|
set_post_thumbnail( $post_id, $args[ 'avatar' ] );
|
|
}
|
|
|
|
// Make sure the author term exists and that we're assigning it to this post type
|
|
$author_term = $coauthors_plus->update_author_term( $this->get_guest_author_by( 'ID', $post_id ) );
|
|
wp_set_post_terms( $post_id, array( $author_term->slug ), $coauthors_plus->coauthor_taxonomy, false );
|
|
|
|
// Explicitly clear all caches, to remove negative caches that may have existed prior to this
|
|
// Guest Author's creation
|
|
$this->delete_guest_author_cache( $post_id );
|
|
|
|
return $post_id;
|
|
}
|
|
|
|
/**
|
|
* Delete a guest author
|
|
*
|
|
* @since 3.0
|
|
*
|
|
* @param int $post_id The ID for the guest author profile
|
|
* @param string $reassign_to User login value for the co-author to reassign posts to
|
|
* @return bool|WP_Error $success True on success, WP_Error on a failure
|
|
*/
|
|
public function delete( $id, $reassign_to = false ) {
|
|
global $coauthors_plus;
|
|
|
|
$guest_author = $this->get_guest_author_by( 'ID', $id );
|
|
if ( ! $guest_author ) {
|
|
return new WP_Error( 'guest-author-missing', __( 'Guest author does not exist', 'co-authors-plus' ) );
|
|
}
|
|
|
|
$guest_author_term = $coauthors_plus->get_author_term( $guest_author );
|
|
|
|
if ( $reassign_to ) {
|
|
|
|
// We're reassigning the guest author's posts user to its linked account
|
|
if ( $guest_author->linked_account == $reassign_to ) {
|
|
$reassign_to_author = get_user_by( 'login', $reassign_to );
|
|
} else {
|
|
$reassign_to_author = $coauthors_plus->get_coauthor_by( 'user_login', $reassign_to );
|
|
}
|
|
|
|
if ( ! $reassign_to_author ) {
|
|
return new WP_Error( 'reassign-to-missing', __( 'Reassignment co-author does not exist', 'co-authors-plus' ) );
|
|
}
|
|
|
|
$reassign_to_term = $coauthors_plus->get_author_term( $reassign_to_author );
|
|
// In the case where the guest author and its linked account shared the same term, we don't want to reassign
|
|
if ( $guest_author_term->term_id != $reassign_to_term->term_id ) {
|
|
wp_delete_term( $guest_author_term->term_id, $coauthors_plus->coauthor_taxonomy, array( 'default' => $reassign_to_term->term_id, 'force_default' => true ) );
|
|
}
|
|
} else {
|
|
wp_delete_term( $guest_author_term->term_id, $coauthors_plus->coauthor_taxonomy );
|
|
}
|
|
|
|
// Delete the guest author profile
|
|
wp_delete_post( $guest_author->ID, true );
|
|
|
|
// Make sure all of the caches are reset
|
|
$this->delete_guest_author_cache( $guest_author );
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a guest author from an existing WordPress user
|
|
*
|
|
* @since 3.0
|
|
*
|
|
* @param int $user_id ID for a WordPress user
|
|
* @return int|WP_Error $retval ID for the new guest author on success, WP_Error on failure
|
|
*/
|
|
function create_guest_author_from_user_id( $user_id ) {
|
|
|
|
$user = get_user_by( 'id', $user_id );
|
|
if ( ! $user ) {
|
|
return new WP_Error( 'invalid-user', __( 'No user exists with that ID', 'co-authors-plus' ) );
|
|
}
|
|
|
|
$guest_author = array();
|
|
foreach ( $this->get_guest_author_fields() as $field ) {
|
|
$key = $field['key'];
|
|
if ( ! empty( $user->$key ) ) {
|
|
$guest_author[ $key ] = $user->$key;
|
|
} else {
|
|
$guest_author[ $key ] = '';
|
|
}
|
|
}
|
|
// Don't need the old user ID
|
|
unset( $guest_author['ID'] );
|
|
// Retain the user mapping and try to produce an unique user_login based on the name.
|
|
$guest_author['linked_account'] = $guest_author['user_login'];
|
|
if ( ! empty( $guest_author['display_name'] ) && $guest_author['display_name'] != $guest_author['user_login'] ) {
|
|
$guest_author['user_login'] = sanitize_title( $guest_author['display_name'] );
|
|
} else if ( ! empty( $guest_author['first_name'] ) && ! empty( $guest_author['last_name'] ) ) {
|
|
$guest_author['user_login'] = sanitize_title( $guest_author['first_name'] . ' ' . $guest_author['last_name'] );
|
|
}
|
|
|
|
$retval = $this->create( $guest_author );
|
|
return $retval;
|
|
}
|
|
|
|
/**
|
|
* Guest authors must have Display Names
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function filter_wp_insert_post_empty_content( $maybe_empty, $postarr ) {
|
|
|
|
if ( $this->post_type != $postarr['post_type'] ) {
|
|
return $maybe_empty;
|
|
}
|
|
|
|
if ( empty( $postarr['post_title'] ) ) {
|
|
return true;
|
|
}
|
|
|
|
return $maybe_empty;
|
|
}
|
|
|
|
/**
|
|
* On the User Management view, add action links to create or edit
|
|
* guest author profiles
|
|
*
|
|
* @since 3.0
|
|
*
|
|
* @param array $actions The existing actions to perform on a user
|
|
* @param object $user_object A WP_User object
|
|
* @return array $actions Modified actions
|
|
*/
|
|
function filter_user_row_actions( $actions, $user_object ) {
|
|
|
|
if ( ! current_user_can( $this->list_guest_authors_cap ) || is_network_admin() ) {
|
|
return $actions;
|
|
}
|
|
|
|
$new_actions = array();
|
|
if ( $guest_author = $this->get_guest_author_by( 'linked_account', $user_object->user_login ) ) {
|
|
$edit_guest_author_link = get_edit_post_link( $guest_author->ID );
|
|
$new_actions['edit-guest-author'] = '<a href="' . esc_url( $edit_guest_author_link ) . '">' . __( 'Edit Profile', 'co-authors-plus' ) . '</a>';
|
|
} else {
|
|
$query_args = array(
|
|
'action' => 'cap-create-guest-author',
|
|
'user_id' => $user_object->ID,
|
|
'nonce' => wp_create_nonce( 'create-guest-author' ),
|
|
);
|
|
$create_guest_author_link = add_query_arg( array_map( 'rawurlencode', $query_args ), admin_url( $this->parent_page ) );
|
|
if ( apply_filters( 'coauthors_show_create_profile_user_link', false ) ) {
|
|
$new_actions['create-guest-author'] = '<a href="' . esc_url( $create_guest_author_link ) . '">' . __( 'Create Profile', 'co-authors-plus' ) . '</a>';
|
|
}
|
|
}
|
|
|
|
return $new_actions + $actions;
|
|
}
|
|
|
|
/**
|
|
* Filter 'get_avatar' to replace with our own avatar if one exists
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function filter_get_avatar( $avatar, $id_or_email, $size, $default ) {
|
|
if ( is_object( $id_or_email ) || ! is_email( $id_or_email ) ) {
|
|
return $avatar;
|
|
}
|
|
|
|
// See if this matches a guest author
|
|
$guest_author = $this->get_guest_author_by( 'user_email', $id_or_email );
|
|
if ( ! $guest_author ) {
|
|
return $avatar;
|
|
}
|
|
|
|
$thumbnail = $this->get_guest_author_thumbnail( $guest_author, $size );
|
|
|
|
if ( $thumbnail ) {
|
|
return $thumbnail;
|
|
}
|
|
|
|
return $avatar;
|
|
}
|
|
|
|
/**
|
|
* Filter the URL used in functions like the_author_posts_link()
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
function filter_author_link( $link, $author_id, $author_nicename ) {
|
|
|
|
// If we're using this at the top of the loop on author.php,
|
|
// our queried object should be set correctly
|
|
if ( ! $author_nicename && is_author() && get_queried_object() ) {
|
|
$author_nicename = get_queried_object()->user_nicename;
|
|
}
|
|
|
|
if ( empty( $link ) ) {
|
|
$link = add_query_arg( 'author_name', rawurlencode( $author_nicename ), home_url() );
|
|
} else {
|
|
global $wp_rewrite;
|
|
$link = $wp_rewrite->get_author_permastruct();
|
|
if ( $link ) {
|
|
$link = str_replace( '%author%', $author_nicename, $link );
|
|
$link = home_url( user_trailingslashit( $link ) );
|
|
} else {
|
|
$link = add_query_arg( 'author_name', rawurlencode( $author_nicename ), home_url() );
|
|
}
|
|
}
|
|
return $link;
|
|
|
|
}
|
|
|
|
/**
|
|
* Filter Author Feed Link for non native authors
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $feed_link Required. Original feed link for the author.
|
|
* @param string $feed Required. Type of feed being generated.
|
|
* @return string Feed link for the author updated, if needs to be
|
|
*/
|
|
public function filter_author_feed_link( $feed_link, $feed ) {
|
|
if ( ! is_author() ) {
|
|
return $feed_link;
|
|
}
|
|
|
|
// Get author, then check if author is guest-author because
|
|
// that's the only type that will need to be adjusted
|
|
$author = get_queried_object();
|
|
if ( empty( $author ) || 'guest-author' != $author->type ) {
|
|
return $feed_link;
|
|
}
|
|
|
|
// The next section is similar to
|
|
// get_author_feed_link() in wp-includes/link-template.php
|
|
$permalink_structure = get_option( 'permalink_structure' );
|
|
|
|
if ( empty( $feed ) ) {
|
|
$feed = get_default_feed();
|
|
}
|
|
|
|
if ( '' == $permalink_structure ) {
|
|
$link = home_url( "?feed=$feed&author=" . $author->ID );
|
|
} else {
|
|
$link = get_author_posts_url( $author->ID );
|
|
$feed_link = ( get_default_feed() === $feed ) ? 'feed' : "feed/$feed";
|
|
$link = trailingslashit( $link ) . user_trailingslashit( $feed_link, 'feed' );
|
|
}
|
|
|
|
return $link;
|
|
}
|
|
|
|
/**
|
|
* Filter Personal Data Exporters to add Guest Author exporter
|
|
*
|
|
* @since 3.3.1
|
|
*/
|
|
public function filter_personal_data_exporter( $exporters ) {
|
|
$exporters['cap-guest-author'] = array(
|
|
'exporter_friendly_name' => __( 'Guest Author', 'co-authors-plus' ),
|
|
'callback' => array( $this, 'personal_data_exporter' ),
|
|
);
|
|
|
|
return $exporters;
|
|
}
|
|
|
|
/**
|
|
* Finds and exports personal data associated with an email address for guest authors
|
|
*
|
|
* @since 3.3.1
|
|
*
|
|
* @param string $email_address The guest author email address.
|
|
* @return array An array of personal data.
|
|
*/
|
|
public function personal_data_exporter( $email_address ) {
|
|
$email_address = trim( $email_address );
|
|
|
|
$data_to_export = array();
|
|
|
|
$author = $this->get_guest_author_by( 'user_email', $email_address );
|
|
|
|
if ( ! $author ) {
|
|
return array(
|
|
'data' => array(),
|
|
'done' => true,
|
|
);
|
|
}
|
|
|
|
$author_data = array(
|
|
'ID' => __( 'ID', 'co-authors-plus' ),
|
|
'user_login' => __( 'Login Name', 'co-authors-plus' ),
|
|
'display_name' => __( 'Display Name', 'co-authors-plus' ),
|
|
'user_email' => __( 'Email', 'co-authors-plus' ),
|
|
'first_name' => __( 'First Name', 'co-authors-plus' ),
|
|
'last_name' => __( 'Last Name', 'co-authors-plus' ),
|
|
'website' => __( 'Website', 'co-authors-plus' ),
|
|
'aim' => __( 'AIM', 'co-authors-plus' ),
|
|
'yahooim' => __( 'Yahoo IM', 'co-authors-plus' ),
|
|
'jabber' => __( 'Jabber / Google Talk', 'co-authors-plus' ),
|
|
'description' => __( 'Biographical Info', 'co-authors-plus' ),
|
|
);
|
|
|
|
$author_data_to_export = array();
|
|
|
|
foreach ( $author_data as $key => $name ) {
|
|
if ( empty( $author->$key ) ) {
|
|
continue;
|
|
}
|
|
|
|
$author_data_to_export[] = array(
|
|
'name' => $name,
|
|
'value' => $author->$key,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Filters extra data to allow plugins add data related to guest author
|
|
*
|
|
* @since 3.3.1
|
|
*
|
|
* @param array $extra_data A empty array to be populated with extra data
|
|
* @param int $author->ID The guest author ID
|
|
* @param string $email_address The guest author email address
|
|
*/
|
|
$extra_data = apply_filters( 'coauthors_guest_author_personal_export_extra_data', [], $author->ID, $email_address );
|
|
|
|
if ( is_array( $extra_data ) && ! empty( $extra_data ) ) {
|
|
$author_data_to_export = array_merge( $author_data_to_export, $extra_data );
|
|
}
|
|
|
|
$data_to_export[] = array(
|
|
'group_id' => 'cap-guest-author',
|
|
'group_label' => __( 'Guest Author', 'co-authors-plus' ),
|
|
'item_id' => "cap-guest-author-{$author->ID}",
|
|
'data' => $author_data_to_export,
|
|
);
|
|
|
|
return array(
|
|
'data' => $data_to_export,
|
|
'done' => true,
|
|
);
|
|
}
|
|
}
|