Open robbiet480 opened 6 years ago
We wrote our own syndication application and used the author's email address to query the receiving site for the ID. This assumes authors use the same email address on all sites in the network.
Thanks for the tip @dleeward, I just finished hacking together some code that can live in your theme functions.php to sync authors during a push. It will attempt to find the author by the passed email address. If an existing user can not be found, a user is created with the properties from the pushing WP. This hasn't been well tested yet and should not yet be used in production.
if ( defined( 'DT_VERSION' ) ) {
function setup_distributor_replacements() {
$post_types = get_post_types(array('show_in_rest' => true));
foreach ( $post_types as $post_type ) {
add_action( "rest_insert_{$post_type}", 'process_original_author', 10, 3 );
}
}
function process_original_author( $post, $request, $update ) {
if ( empty( $post ) || is_wp_error( $post ) ) {
return;
}
if ( ! empty( $request['distributor_original_author'] ) ) {
$author_obj = $request['distributor_original_author'];
$new_author = get_user_by( 'email', $author_obj['email'] );
$author_id = 0;
if ( ! empty( $new_author ) && ! is_wp_error( $new_author ) ) {
$author_id = $new_author->ID;
} else {
$create_user = wp_insert_user(array(
'display_name' => $author_obj['display_name'],
'first_name' => $author_obj['first_name'],
'last_name' => $author_obj['last_name'],
'nickname' => $author_obj['nickname'],
'user_displayname' => $author_obj['user_displayname'],
'user_email' => $author_obj['user_email'],
'user_login' => $author_obj['user_login'],
'user_nicename' => $author_obj['user_nicename'],
'user_pass' => NULL
));
if ( is_wp_error( $create_user ) ) {
error_log("Error creating user during Distributor push ".$create_user->get_error_message());
return;
}
$author_id = $create_user;
}
wp_update_post( array( 'ID' => $post->ID, 'post_author' => $author_id ) );
}
}
add_action('init', 'setup_distributor_replacements');
function add_original_author($post_body, $post, $that) {
$post_author = get_user_by( 'id', $post->post_author );
$author_obj = array(
'display_name' => $post_author->display_name,
'first_name' => get_user_meta($post->post_author, 'first_name', true),
'first_name' => get_user_meta($post->post_author, 'first_name', true),
'last_name' => get_user_meta($post->post_author, 'last_name', true),
'user_email' => $post_author->user_email,
'user_login' => $post_author->user_login,
'user_nicename' => $post_author->user_nicename,
);
$post_body['distributor_original_author'] = $author_obj;
return $post_body;
}
add_filter('dt_push_post_args', 'add_original_author', 1, 3);
}
@robbiet480 You shouldn't assume that the original author's user_login and user_nicename aren't already being used. We create an arbitrary username of the current unix time prefixed with a short text string such as ABC1519562843. This does assume you aren't creating more than one user per second. You could use microtime or add a random suffix instead .
We'd discussed this one a lot.
Generally, we believe posting as the authenticated user is the safest most compatible solution for everyone. We definitely see different publishers may have different requirements. Creating a new user on the remote site opens up a ton of hard questions. What is the new users role? Should that account be accessible by the original author?
Of course this is open for discussion, and we'd love to hear other opinions, but for now the best we can do is to add additional hooks/filters to make what @robbiet480 accomplished easier.
One thing to note, is that many publications have the need to support multiple authors for content, and in those cases, "Users" are often not even used for the author <-> content relationship.
When I was working on syndicating content at DFM, we used Co-Authors Plus which was a hybrid post-type/taxonomy solution for adding many authors to posts, so in our case we actually wanted the authenticated user to be connected to the post as the author of the syndicated content, so we had a trail of what user triggered the syndication, and we syndicated additional data to handle the authors<->content relationship (somewhat similar to how relationships to media is handled)
Appreciate the feedback, Jason!
So i've taken this a step further now and actually added support for Co-Authors Plus to Distributor via hooks. Here's my code for anyone else interested in the integration. I've inquired in #131 about inclusion in Distributor.
<?php
function handle_dt_process_distributor_attributes( $post, $request, $update ) {
if ( empty( $post ) || is_wp_error( $post ) ) {
return;
}
if ( is_plugin_active( 'co-authors-plus/co-authors-plus.php' ) && ! empty( $request['distributor_coauthors'] ) ) {
global $coauthors_plus;
$author_logins = array();
foreach ( $request['distributor_coauthors'] as $coauthor ) {
$existing_guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'user_login', $coauthor['user_login'] );
if ( ! $existing_guest_author ) {
$guest_author_id = $coauthors_plus->guest_authors->create( array(
'display_name' => $coauthor['display_name'],
'first_name' => $coauthor['first_name'],
'last_name' => $coauthor['last_name'],
'user_login' => $coauthor['user_login'],
'user_nicename' => $coauthor['user_nicename']
) );
if ( is_wp_error( $guest_author_id ) ) {
error_log("Error creating co-author during Distributor push ".$guest_author_id->get_error_message());
return;
}
if ( !$guest_author_id ) {
error_log( '-- Failed to create guest author.' );
}
if ( ! empty($coauthor['avatar_url']) ) {
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
$thumbnail_id = media_sideload_image($coauthor['avatar_url'], $guest_author_id, null, 'id');
set_post_thumbnail($guest_author_id, $thumbnail_id);
}
$guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'ID', $guest_author_id );
array_push($author_logins, $guest_author->user_login);
} else {
array_push($author_logins, $existing_guest_author->user_login);
}
}
clean_post_cache($post->ID);
$coauthors_plus->guest_authors->delete_guest_author_cache($author_logins[0]);
}
}
add_action( 'dt_process_distributor_attributes', 'handle_dt_process_distributor_attributes', 10, 3 );
if ( is_plugin_active( 'co-authors-plus/co-authors-plus.php' ) ) {
function distributor_coauthors_get_avatar_url( $coauthor ) {
global $coauthors_plus;
if ( ! is_object( $coauthor ) ) {
return '';
}
if ( isset( $coauthor->type ) && 'guest-author' == $coauthor->type ) {
if ( ! has_post_thumbnail( $coauthor->ID ) ) {
return '';
}
$large_image_url = wp_get_attachment_image_src( get_post_thumbnail_id( $coauthor->ID ), 'large' );
return $large_image_url[0];
}
// Make sure we're dealing with an object for which we can retrieve an email
if ( isset( $coauthor->user_email ) ) {
return get_avatar( $coauthor->user_email, $size, $default, $alt );
}
// Nothing matched, an invalid object was passed.
return '';
}
}
function add_dt_push_post_args($post_body, $post, $that) {
if ( is_plugin_active( 'co-authors-plus/co-authors-plus.php' ) ) {
$post_body['distributor_coauthors'] = array();
foreach ( $coauthors as $coauthor ) {
if( array_key_exists("data", $coauthor) ) {
// Don't include real WP_Users
continue;
}
$coauthor_arr = (array) $coauthor;
$coauthor_arr['avatar_url'] = distributor_coauthors_get_avatar_url($coauthor);
array_push($post_body['distributor_coauthors'], $coauthor_arr);
}
}
return $post_body;
}
add_filter('dt_push_post_args', 'add_dt_push_post_args', 1, 3);
add_filter('dt_push_post_timeout', function($default_timeout, $post) {
return 30;
}, 1, 2);
Re: the original query about distributing with authors, I'd say that CAP could be a good stop gap solution to allow for Distributor to ensure the proper author is credited while not having to worry about things that @tlovett1 mentioned like what role should new users have and such. Furthermore, I think it would make a lot of sense to add more template tags to expose information like original author to the frontend just like how we are now able to expose original site name.
I'd like to block this issue by the decision-to-be-made in #131. If that decision turns out to be that Distributor will support external plugins more than via hooks/filters, then we can continue investigating this specific issue with Co-Authors Plus and how it can best be gracefully included with/alongside Distributor. If Distributor takes the hooks/filters-only approach, then its likely we'll close this issue (and hopefully link out to an example repo for the Co-Authors Plus extension code).
We have a pretty big network of sites. All of them need to show proper author information. I see that as of right now Distributor will set the distributed posts author to the authenticated users ID. I'd like to change this (and am willing to do the work myself). Here's two possible solutions:
dt_item_mapping
for pull ordt_push_post_args
for push filter) to allow passing the "correct" author ID based on the existing post author ID and what site we are posting to. This would require end users to have a map or database of current site author ID to external site author ID. Not the hardest thing in the world for a lot of admins to build, but still a pretty high bar. If people think this is the best route, I would most likely write my own solution and document it inREADME.md
. Eventually, a UI could possible be created in Distributor to manage this.Anyway, looking for feedback on which path to go down or a different path entirely. I'm looking to get this done ASAP and would of course submit any changes to the plugin back.