diff --git a/.travis.yml b/.travis.yml
index da7b0e08..28765d3b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,17 +1,70 @@
language: php
php:
- - 5.3
- - 5.5
+ - 7.1
env:
- WP_VERSION=latest
- - WP_VERSION=4.1
- - WP_VERSION=4.0
+
+matrix:
+matrix:
+ include:
+ - php: "5.2"
+ env: WP_VERSION=latest
+ - php: "5.2"
+ env: WP_VERSION=4.6
+ - php: "5.6"
+ env:
+ - WP_VERSION=latest
+ - SNIFF=1
+ - php: "5.6"
+ env: WP_VERSION=4.6
+ - php: "7.0"
+ env: WP_VERSION=latest
+ - php: "7.0"
+ env: WP_VERSION=4.6
+ # 7.1 / latest already included above as first build.
+ - php: "7.1"
+ env: WP_VERSION=4.6
before_script:
- - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
+ - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
+ # PHPCS
+ - export PHPCS_DIR=/tmp/phpcs
+ - export SNIFFS_DIR=/tmp/sniffs
+ # Install CodeSniffer for WordPress Coding Standards checks.
+ - if [[ "$SNIFF" == "1" ]]; then git clone -b master --depth 1 https://github.com/squizlabs/PHP_CodeSniffer.git $PHPCS_DIR; fi
+ # Install WordPress Coding Standards.
+ - if [[ "$SNIFF" == "1" ]]; then git clone -b master --depth 1 https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards.git $SNIFFS_DIR; fi
+ # Install PHP Compatibility sniffs.
+ - if [[ "$SNIFF" == "1" ]]; then git clone -b master --depth 1 https://github.com/wimg/PHPCompatibility.git $SNIFFS_DIR/PHPCompatibility; fi
+ # Set install path for PHPCS sniffs.
+ # @link https://github.com/squizlabs/PHP_CodeSniffer/blob/4237c2fc98cc838730b76ee9cee316f99286a2a7/CodeSniffer.php#L1941
+ - if [[ "$SNIFF" == "1" ]]; then $PHPCS_DIR/scripts/phpcs --config-set installed_paths $SNIFFS_DIR; fi
+ # After CodeSniffer install you should refresh your path.
+ - if [[ "$SNIFF" == "1" ]]; then phpenv rehash; fi
+ # Properly handle PHPunit versions
+ - export PATH="$HOME/.composer/vendor/bin:$PATH"
+ - |
+ if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then
+ composer global require "phpunit/phpunit=5.7.*"
+ elif [[ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]]; then
+ composer global require "phpunit/phpunit=4.8.*"
+ fi
+ - phpunit --version
script:
- - make lint
- - make phpunit
+ # Search for PHP syntax errors.
+ - find -L . -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l
+ # WordPress Coding Standards.
+ # @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards
+ # @link http://pear.php.net/package/PHP_CodeSniffer/
+ # -p flag: Show progress of the run.
+ # -s flag: Show sniff codes in all reports.
+ # -v flag: Print verbose output.
+ # -n flag: Do not print warnings. (shortcut for --warning-severity=0)
+ # --standard: Use WordPress as the standard.
+ # --extensions: Only sniff PHP files.
+ - if [[ "$SNIFF" == "1" ]]; then $PHPCS_DIR/scripts/phpcs -p -s -v -n . --standard="WordPress-VIP" --extensions=php; fi
+ # Unit tests
+ - phpunit
diff --git a/co-authors-plus.php b/co-authors-plus.php
index 339184a9..bceed1ab 100644
--- a/co-authors-plus.php
+++ b/co-authors-plus.php
@@ -3,7 +3,7 @@
Plugin Name: Co-Authors Plus
Plugin URI: http://wordpress.org/extend/plugins/co-authors-plus/
Description: Allows multiple authors to be assigned to a post. This plugin is an extended version of the Co-Authors plugin developed by Weston Ruter.
-Version: 3.1.2
+Version: 3.2.2
Author: Mohammad Jangda, Daniel Bachhuber, Automattic
Copyright: 2008-2015 Shared and distributed between Mohammad Jangda, Daniel Bachhuber, Weston Ruter
@@ -24,7 +24,7 @@
*/
-define( 'COAUTHORS_PLUS_VERSION', '3.1.2' );
+define( 'COAUTHORS_PLUS_VERSION', '3.2.2' );
require_once( dirname( __FILE__ ) . '/template-tags.php' );
require_once( dirname( __FILE__ ) . '/deprecated.php' );
@@ -86,6 +86,9 @@ function __construct() {
// Action to set up author auto-suggest
add_action( 'wp_ajax_coauthors_ajax_suggest', array( $this, 'ajax_suggest' ) );
+ // Action to create user
+ add_action( 'wp_ajax_coauthors_ajax_create_user', array( $this, 'ajax_create_user' ) );
+
// Filter to allow coauthors to edit posts
add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 3 );
@@ -116,6 +119,10 @@ function __construct() {
// Support infinite scroll for Guest Authors on author pages
add_filter( 'infinite_scroll_js_settings', array( $this, 'filter_infinite_scroll_js_settings' ), 10, 2 );
+ // Delete CoAuthor Cache on Post Save & Post Delete
+ add_action( 'save_post', array( $this, 'clear_cache') );
+ add_action( 'delete_post', array( $this, 'clear_cache') );
+ add_action( 'set_object_terms', array( $this, 'clear_cache_on_terms_set' ), 10, 6 );
}
/**
@@ -375,14 +382,14 @@ public function coauthors_meta_box( $post ) {
?>
- Note: To edit post authors, please enable javascript or use a javascript-capable browser', 'co-authors-plus' ), array( 'strong' => array() ) ); ?>
+ Note: To edit post authors, please enable javascript or use a javascript-capable browser', 'co-authors-plus' ), array( 'strong' => array() ) ); ?>
-
Remove to remove them.', 'co-authors-plus' ), array( 'strong' => array() ) ); ?>
+
Remove to remove them.', 'co-authors-plus' ), array( 'strong' => array() ) ); ?>
@@ -506,7 +513,7 @@ function _action_quick_edit_custom_box( $column_name, $post_type ) {
@@ -611,12 +618,18 @@ function posts_join_filter( $join, $query ) {
}
// Check to see that JOIN hasn't already been added. Props michaelingp and nbaxley
- $term_relationship_join = " INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
- $term_taxonomy_join = " INNER JOIN {$wpdb->term_taxonomy} ON ( {$wpdb->term_relationships}.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id )";
+ $term_relationship_inner_join = " INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
+ $term_relationship_left_join = " LEFT JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
+
+ $term_taxonomy_join = " INNER JOIN {$wpdb->term_relationships} AS tr1 ON ({$wpdb->posts}.ID = tr1.object_id)";
+ $term_taxonomy_join .= " INNER JOIN {$wpdb->term_taxonomy} ON ( tr1.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id )";
- if ( false === strpos( $join, trim( $term_relationship_join ) ) ) {
- $join .= str_replace( 'INNER JOIN', 'LEFT JOIN', $term_relationship_join );
+ // 4.6+ uses a LEFT JOIN for tax queries so we need to check for both
+ if ( false === strpos( $join, trim( $term_relationship_inner_join ) )
+ && false === strpos( $join, trim( $term_relationship_left_join ) ) ) {
+ $join .= $term_relationship_left_join;
}
+
if ( false === strpos( $join, trim( $term_taxonomy_join ) ) ) {
$join .= str_replace( 'INNER JOIN', 'LEFT JOIN', $term_taxonomy_join );
}
@@ -1065,6 +1078,14 @@ public function ajax_suggest() {
$authors = $this->search_authors( $search, $ignore );
+ if ( empty( $authors ) ) {
+ $author_display_name = $search;
+ $author_login = str_replace( " ", "-", $search );
+ echo __( 'New', 'co-authors-plus' ) . ' | ' . $author_login . ' | ' . $author_display_name . "\n";
+
+ die();
+ }
+
foreach ( $authors as $author ) {
echo esc_html( $author->ID . ' | ' . $author->user_login . ' | ' . $author->display_name . ' | ' . $author->user_email . ' | ' . $author->user_nicename ) . "\n";
}
@@ -1073,6 +1094,40 @@ public function ajax_suggest() {
}
+ /**
+ * Create new guest author if not exists.
+ */
+ public function ajax_create_user() {
+ global $coauthors_plus;
+
+ if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'coauthors-create' ) ) {
+ die();
+ }
+
+ // Get author by user_login.
+ $guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'user_email', $_POST['email'], true );
+
+ if ( ! $guest_author ){
+ // Get author by user_login.
+ $guest_author = $coauthors_plus->guest_authors->get_guest_author_by( 'user_login', $_POST['login'], true );
+ }
+
+ // If user isn't exists.
+ if ( ! $guest_author ) {
+
+ $guest_author_id = $coauthors_plus->guest_authors->create( array(
+ 'display_name' => sanitize_text_field( $_POST['name'] ),
+ 'user_login' => sanitize_text_field( $_POST['login'] ),
+ 'user_email' => sanitize_email( $_POST['email'] ) ,
+ ) );
+
+ if ( $guest_author_id ) {
+ echo wp_json_encode( array( 'success' => true ) );
+ }
+ }
+ die();
+ }
+
/**
* Get matching authors based on a search value
*/
@@ -1253,6 +1308,21 @@ public function js_vars() {
wp_nonce_url( 'admin-ajax.php', 'coauthors-search' )
)
); ?>;
+ // AJAX link used for the create new guest user.
+ var coAuthorsPlus_ajax_create_new_user_link = 'coauthors_ajax_create_user',
+ 'post_type' => rawurlencode( get_post_type() ),
+ ),
+ wp_nonce_url( 'admin-ajax.php', 'coauthors-create' )
+ )
+ ); ?>;
+ var coAuthorsPlus_vars = {
+ 'email_prompt': "",
+ 'email_invalid': ""
+ };
coauthor_taxonomy, array(
+ 'orderby' => 'term_order',
+ 'order' => 'ASC',
+ ) );
+
+ // This usually happens if the taxonomy doesn't exist, which should never happen, but you never know.
+ if ( is_wp_error( $coauthor_terms ) ) {
+ return array();
+ }
+
+ wp_cache_set( $cache_key, $coauthor_terms, 'co-authors-plus' );
+ }
+
+ return $coauthor_terms;
+
+ }
+
+ /**
+ * Callback to clear the cache on post save and post delete.
+ *
+ * @param $post_id The Post ID.
+ */
+ public function clear_cache( $post_id ) {
+ wp_cache_delete( 'coauthors_post_' . $post_id, 'co-authors-plus' );
+ }
+
+ /**
+ * Callback to clear the cache when an object's terms are changed.
+ *
+ * @param $post_id The Post ID.
+ */
+ public function clear_cache_on_terms_set( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
+
+ // We only care about the coauthors taxonomy
+ if ( $this->coauthor_taxonomy !== $taxonomy ) {
+ return;
+ }
+
+ wp_cache_delete( 'coauthors_post_' . $object_id, 'co-authors-plus' );
+
+ }
+
}
global $coauthors_plus;
@@ -1595,6 +1727,7 @@ function cap_filter_comment_moderation_email_recipients( $recipients, $comment_i
if ( isset( $post_id ) ) {
$coauthors = get_coauthors( $post_id );
+ $extra_recipients = array();
foreach ( $coauthors as $user ) {
if ( ! empty( $user->user_email ) ) {
$extra_recipients[] = $user->user_email;
@@ -1605,3 +1738,17 @@ function cap_filter_comment_moderation_email_recipients( $recipients, $comment_i
}
return $recipients;
}
+
+/**
+ * Retrieve a list of coauthor terms for a single post.
+ *
+ * Grabs a correctly ordered list of authors for a single post, appropriately
+ * cached because it requires `wp_get_object_terms()` to succeed.
+ *
+ * @param int $post_id ID of the post for which to retrieve authors.
+ * @return array Array of coauthor WP_Term objects
+ */
+function cap_get_coauthor_terms_for_post( $post_id ) {
+ global $coauthors_plus;
+ return $coauthors_plus->get_coauthor_terms_for_post( $post_id );
+}
diff --git a/js/co-authors-plus.js b/js/co-authors-plus.js
index 7691f91b..e3d27af5 100755
--- a/js/co-authors-plus.js
+++ b/js/co-authors-plus.js
@@ -107,6 +107,28 @@ jQuery( document ).ready(function () {
return true;
}
+ /*
+ * Create new guest author if not exists
+ * @param string Author Data
+ * @param object The autosuggest input box
+ */
+ function coauthors_new_author_display( authordata, co ){
+ // Reset placeholder.
+ co.attr( 'value', coAuthorsPlusStrings.search_box_text )
+ .focus( function(){ co.val( '' ) } )
+ .blur( function(){ co.val( coAuthorsPlusStrings.search_box_text ) } );
+
+ // Ajax Request to create a new guest author.
+ jQuery.post(coAuthorsPlus_ajax_create_new_user_link, authordata ,function(res){
+ // If guest user created successfully.
+ if( JSON.parse(res).success == true ){
+ coauthors_add_coauthor( authordata, co );
+ }
+
+ });
+
+ return true;
+ }
/*
* Add the autosuggest box and text tag to the Co-Authors table
@@ -202,7 +224,25 @@ jQuery( document ).ready(function () {
author.nicename = jQuery.trim( vals[4] );
if ( author.id=='New' ) {
- coauthors_new_author_display( name );
+
+ // Allow user to enter email address of user.
+ author.email = prompt( coAuthorsPlus_vars.email_prompt );
+
+ var email_filter = /^[\w\-\.\+]+\@[a-zA-Z0-9\.\-]+\.[a-zA-z0-9]{2,4}$/;
+
+ if ( ! author.email || ! email_filter.test( author.email ) ) {
+
+ alert( coAuthorsPlus_vars.email_invalid );
+ $this.attr( 'value', coAuthorsPlusStrings.search_box_text )
+ .focus( function(){ $this.val( '' ) } )
+ .blur( function(){ $this.val( coAuthorsPlusStrings.search_box_text ) } )
+ ;
+ return false;
+ }
+
+ // Create new guest author if not exists.
+ coauthors_new_author_display( author, $this );
+
} else {
coauthors_add_coauthor( author, $this );
// Show the delete button if we now have more than one co-author
diff --git a/php/class-coauthors-guest-authors.php b/php/class-coauthors-guest-authors.php
index 90537899..5c322ed0 100644
--- a/php/class-coauthors-guest-authors.php
+++ b/php/class-coauthors-guest-authors.php
@@ -675,11 +675,21 @@ function metabox_manage_guest_author_bio() {
foreach ( $fields as $field ) {
$pm_key = $this->get_post_meta_key( $field['key'] );
$value = get_post_meta( $post->ID, $pm_key, true );
- echo '';
- echo '';
- echo ' | ';
- echo '';
- echo ' |
';
+ printf( '
+
+
+
+ |
+
+
+ |
+
+ ',
+ esc_attr( $pm_key ),
+ esc_html( $field['label'] ),
+ esc_attr( $pm_key ),
+ esc_textarea( $value )
+ );
}
echo '';
diff --git a/php/class-coauthors-wp-list-table.php b/php/class-coauthors-wp-list-table.php
index dacbf203..552f6db0 100644
--- a/php/class-coauthors-wp-list-table.php
+++ b/php/class-coauthors-wp-list-table.php
@@ -64,6 +64,8 @@ function prepare_items() {
'order' => 'ASC',
);
+ $args = apply_filters( 'coauthors_guest_author_query_args', $args );
+
if ( isset( $_REQUEST['orderby'] ) ) {
switch ( $_REQUEST['orderby'] ) {
case 'display_name':
diff --git a/php/class-wp-cli.php b/php/class-wp-cli.php
index 8dcf1ee2..568f988e 100644
--- a/php/class-wp-cli.php
+++ b/php/class-wp-cli.php
@@ -76,9 +76,9 @@ public function create_terms_for_posts() {
$count++;
- $terms = wp_get_post_terms( $single_post->ID, $coauthors_plus->coauthor_taxonomy );
- if ( is_wp_error( $terms ) ) {
- WP_CLI::error( $terms->get_error_message() );
+ $terms = cap_get_coauthor_terms_for_post( $single_post->ID );
+ if ( empty( $terms ) ) {
+ WP_CLI::error( sprintf( 'No co-authors found for post #%d.', $single_post->ID ) );
}
if ( ! empty( $terms ) ) {
@@ -235,8 +235,13 @@ public function assign_user_to_coauthor( $args, $assoc_args ) {
$posts = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author=%d AND post_type IN ('$post_types')", $user->ID ) );
$affected = 0;
foreach ( $posts as $post_id ) {
- if ( $coauthors = wp_get_post_terms( $post_id, $coauthors_plus->coauthor_taxonomy ) ) {
- WP_CLI::line( sprintf( __( 'Skipping - Post #%d already has co-authors assigned: %s', 'co-authors-plus' ), $post_id, implode( ', ', wp_list_pluck( $coauthors, 'slug' ) ) ) );
+ $coauthors = cap_get_coauthor_terms_for_post( $post_id );
+ if ( ! empty( $coauthors ) ) {
+ WP_CLI::line( sprintf(
+ __( 'Skipping - Post #%d already has co-authors assigned: %s', 'co-authors-plus' ),
+ $post_id,
+ implode( ', ', wp_list_pluck( $coauthors, 'slug' ) )
+ ) );
continue;
}
@@ -545,7 +550,7 @@ public function list_posts_without_terms( $args, $assoc_args ) {
foreach ( $posts->posts as $single_post ) {
- $terms = wp_get_post_terms( $single_post->ID, $coauthors_plus->coauthor_taxonomy );
+ $terms = cap_get_coauthor_terms_for_post( $single_post->ID );
if ( empty( $terms ) ) {
$saved = array(
$single_post->ID,
@@ -698,8 +703,8 @@ public function remove_terms_from_revisions() {
$affected = 0;
foreach ( $ids as $post_id ) {
- $terms = wp_get_post_terms( $post_id, 'author' );
- if ( ! $terms ) {
+ $terms = cap_get_coauthor_terms_for_post( $post_id );
+ if ( empty( $terms ) ) {
continue;
}
diff --git a/readme.txt b/readme.txt
index 608d7e83..ba030853 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,9 +1,9 @@
=== Co-Authors Plus ===
Contributors: batmoo, danielbachhuber, automattic
Tags: authors, users, multiple authors, coauthors, multi-author, publishing
-Tested up to: 4.5
+Tested up to: 4.7.3
Requires at least: 4.1
-Stable tag: 3.1.1
+Stable tag: 3.2.2
Assign multiple bylines to posts, pages, and custom post types via a search-as-you-type input box
@@ -57,11 +57,22 @@ Bug fixes and minor enhancements
== Changelog ==
+= 3.2.2 =
+* Fix broken author ordering in 4.7+ (props mslinnea)
+* Fix no moderation e-mail bug (props RobjS)
+* Cached functions in CLI commands (props jasonbahl)
+* Fix missing echos (props trepmal)
+* Add `coauthors_guest_author_query_args` filter (props trepmal)
+
+= 3.2.1 (May 16, 2016) =
+* Hotfix for broken Guest Author bio metabox (props JS Morisset)
+
+= 3.2 (May 12, 2016) =
+Various minor bug and security fixes
+
= 3.1.2 (Aug. 31, 2015) =
* Minor bug fixes and coding standards changes.
* The author's display name is now filtered through the_author in coauthors_posts_links_single()
-
-= ??? (??? ?? ????) =
* New Russian and Ukrainian translations, courtesy of [Jurko Chervony](http://skinik.name/).
= 3.1.1 (Mar. 20, 2014) =
diff --git a/template-tags.php b/template-tags.php
index 725fc47f..ffc398ae 100644
--- a/template-tags.php
+++ b/template-tags.php
@@ -14,8 +14,7 @@ function get_coauthors( $post_id = 0 ) {
}
if ( $post_id ) {
- $coauthor_terms = get_the_terms( $post_id, $coauthors_plus->coauthor_taxonomy );
-
+ $coauthor_terms = cap_get_coauthor_terms_for_post( $post_id );
if ( is_array( $coauthor_terms ) && ! empty( $coauthor_terms ) ) {
foreach ( $coauthor_terms as $coauthor ) {
$coauthor_slug = preg_replace( '#^cap\-#', '', $coauthor->slug );
diff --git a/tests/coauthorsplus-testcase.php b/tests/coauthorsplus-testcase.php
index eb56cd81..99116c10 100644
--- a/tests/coauthorsplus-testcase.php
+++ b/tests/coauthorsplus-testcase.php
@@ -3,65 +3,11 @@
/**
* Base unit test class for Co-Authors Plus
*/
-
class CoAuthorsPlus_TestCase extends WP_UnitTestCase {
-
- protected $suppress = false;
-
public function setUp() {
- global $wpdb;
parent::setUp();
- $this->suppress = $wpdb->suppress_errors();
-
- $_SERVER['REMOTE_ADDR'] = '';
-
- $this->author1 = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'author1' ) );
- $this->editor1 = $this->factory->user->create( array( 'role' => 'editor', 'user_login' => 'editor2' ) );
-
- $post = array(
- 'post_author' => $this->author1,
- 'post_status' => 'publish',
- 'post_content' => rand_str(),
- 'post_title' => rand_str(),
- 'post_type' => 'post',
- );
-
- $this->author1_post1 = wp_insert_post( $post );
-
- $post = array(
- 'post_author' => $this->author1,
- 'post_status' => 'publish',
- 'post_content' => rand_str(),
- 'post_title' => rand_str(),
- 'post_type' => 'post',
- );
-
- $this->author1_post2 = wp_insert_post( $post );
-
- $page = array(
- 'post_author' => $this->author1,
- 'post_status' => 'publish',
- 'post_content' => rand_str(),
- 'post_title' => rand_str(),
- 'post_type' => 'page',
- );
-
- $this->author1_page1 = wp_insert_post( $page );
-
- $page = array(
- 'post_author' => $this->author1,
- 'post_status' => 'publish',
- 'post_content' => rand_str(),
- 'post_title' => rand_str(),
- 'post_type' => 'page',
- );
-
- $this->author1_page2 = wp_insert_post( $page );
- }
- public function tearDown() {
- global $wpdb;
- parent::tearDown();
- $wpdb->suppress_errors( $this->suppress );
+ global $coauthors_plus;
+ $this->_cap = $coauthors_plus;
}
}
diff --git a/tests/test-author-queried-object.php b/tests/test-author-queried-object.php
new file mode 100644
index 00000000..25689cf5
--- /dev/null
+++ b/tests/test-author-queried-object.php
@@ -0,0 +1,102 @@
+factory->user->create( array( 'user_login' => 'msauthor1' ) );
+ $author2 = $this->factory->user->create( array( 'user_login' => 'msauthor2' ) );
+ $blog2 = $this->factory->blog->create( array( 'user_id' => $author1 ) );
+
+ switch_to_blog( $blog2 );
+ $wp_rewrite->init();
+
+ $blog2_post1 = $this->factory->post->create( array(
+ 'post_status' => 'publish',
+ 'post_content' => rand_str(),
+ 'post_title' => rand_str(),
+ 'post_author' => $author1,
+ ) );
+
+ /**
+ * Author 1 is an author on the blog
+ */
+ $this->go_to( get_author_posts_url( $author1 ) );
+ $this->assertQueryTrue( 'is_author', 'is_archive' );
+
+ /**
+ * Author 2 is not yet an author on the blog
+ */
+ $this->go_to( get_author_posts_url( $author2 ) );
+ $this->assertQueryTrue( 'is_404' );
+
+ // Add the user to the blog
+ add_user_to_blog( $blog2, $author2, 'author' );
+
+ /**
+ * Author 2 is now on the blog, but not yet published
+ */
+ $this->go_to( get_author_posts_url( $author2 ) );
+ $this->assertQueryTrue( 'is_author', 'is_archive' );
+
+ // Add the user as an author on the original post
+ $author2_obj = get_user_by( 'id', $author2 );
+ $coauthors_plus->add_coauthors( $blog2_post1, array( $author2_obj->user_login ), true );
+
+ /**
+ * Author 2 is now on the blog, and published
+ */
+ $this->go_to( get_author_posts_url( $author2 ) );
+ $this->assertQueryTrue( 'is_author', 'is_archive' );
+
+ // Remove the user from the blog
+ remove_user_from_blog( $author2, $blog2 );
+
+ /**
+ * Author 2 was removed from the blog, but still a published author
+ */
+ $this->go_to( get_author_posts_url( $author2 ) );
+ $this->assertQueryTrue( 'is_author', 'is_archive' );
+
+ // Delete the user from the network
+ wpmu_delete_user( $author2 );
+
+ /**
+ * Author 2 is no more
+ */
+ $this->go_to( get_author_posts_url( $author2 ) );
+ $this->assertQueryTrue( 'is_404' );
+ $this->assertEquals( false, get_user_by( 'id', $author2 ) );
+
+ restore_current_blog();
+
+ }
+}
diff --git a/tests/test-author-queries.php b/tests/test-author-queries.php
index 2f6c1524..d6d58c7a 100644
--- a/tests/test-author-queries.php
+++ b/tests/test-author-queries.php
@@ -1,88 +1,129 @@
factory->user->create( array( 'user_login' => 'msauthor1' ) );
- $author2 = $this->factory->user->create( array( 'user_login' => 'msauthor2' ) );
- $blog2 = $this->factory->blog->create( array( 'user_id' => $author1 ) );
-
- switch_to_blog( $blog2 );
- $wp_rewrite->init();
-
- $blog2_post1 = $this->factory->post->create( array(
+ public function test__author_arg__user_is_post_author() {
+ $author_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'batman' ) );
+ $author = get_userdata( $author_id );
+ $post_id = $this->factory->post->create( array(
+ 'post_author' => $author_id,
'post_status' => 'publish',
- 'post_content' => rand_str(),
- 'post_title' => rand_str(),
- 'post_author' => $author1,
+ 'post_type' => 'post',
) );
+ $this->_cap->add_coauthors( $post_id, array( $author->user_login ) );
- /**
- * Author 1 is an author on the blog
- */
- $this->go_to( get_author_posts_url( $author1 ) );
- $this->assertQueryTrue( 'is_author', 'is_archive' );
-
- /**
- * Author 2 is not yet an author on the blog
- */
- $this->go_to( get_author_posts_url( $author2 ) );
- $this->assertQueryTrue( 'is_404' );
-
- // Add the user to the blog
- add_user_to_blog( $blog2, $author2, 'author' );
-
- /**
- * Author 2 is now on the blog, but not yet published
- */
- $this->go_to( get_author_posts_url( $author2 ) );
- $this->assertQueryTrue( 'is_author', 'is_archive' );
-
- // Add the user as an author on the original post
- $author2_obj = get_user_by( 'id', $author2 );
- $coauthors_plus->add_coauthors( $blog2_post1, array( $author2_obj->user_login ), true );
-
- /**
- * Author 2 is now on the blog, and published
- */
- $this->go_to( get_author_posts_url( $author2 ) );
- $this->assertQueryTrue( 'is_author', 'is_archive' );
-
- // Remove the user from the blog
- remove_user_from_blog( $author2, $blog2 );
-
- /**
- * Author 2 was removed from the blog, but still a published author
- */
- $this->go_to( get_author_posts_url( $author2 ) );
- $this->assertQueryTrue( 'is_author', 'is_archive' );
-
- // Delete the user from the network
- wpmu_delete_user( $author2 );
-
- /**
- * Author 2 is no more
- */
- $this->go_to( get_author_posts_url( $author2 ) );
- $this->assertQueryTrue( 'is_404' );
- $this->assertEquals( false, get_user_by( 'id', $author2 ) );
-
- restore_current_blog();
+ $query = new WP_Query( array(
+ 'author' => $author_id,
+ ) );
+
+ $this->assertEquals( 1, count( $query->posts ) );
+ $this->assertEquals( $post_id, $query->posts[ 0 ]->ID );
+ }
+
+ public function test__author_name_arg__user_is_post_author() {
+ $author_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'batman' ) );
+ $author = get_userdata( $author_id );
+ $post_id = $this->factory->post->create( array(
+ 'post_author' => $author_id,
+ 'post_status' => 'publish',
+ 'post_type' => 'post',
+ ) );
+ $this->_cap->add_coauthors( $post_id, array( $author->user_login ) );
+
+ $query = new WP_Query( array(
+ 'author_name' => $author->user_login,
+ ) );
+
+ $this->assertEquals( 1, count( $query->posts ) );
+ $this->assertEquals( $post_id, $query->posts[ 0 ]->ID );
+ }
+
+ public function test__author_name_arg__user_is_coauthor() {
+ $author1_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'batman' ) );
+ $author1 = get_userdata( $author1_id );
+ $author2_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'superman' ) );
+ $author2 = get_userdata( $author2_id );
+
+ $post_id = $this->factory->post->create( array(
+ 'post_author' => $author1_id,
+ 'post_status' => 'publish',
+ 'post_type' => 'post',
+ ) );
+ $this->_cap->add_coauthors( $post_id, array( $author1->user_login, $author2->user_login ) );
+
+ $query = new WP_Query( array(
+ 'author_name' => $author2->user_login,
+ ) );
+
+ $this->assertEquals( 1, count( $query->posts ) );
+ $this->assertEquals( $post_id, $query->posts[ 0 ]->ID );
+ }
+
+ public function test__author_arg__user_is_coauthor__author_arg() {
+ return; // TODO: re-enable; fails currently because WordPress generates query as `post_author IN (id)` which doesn't match our regex in the posts_where filter.
+
+ $author1_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'batman' ) );
+ $author1 = get_userdata( $author1_id );
+ $author2_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'superman' ) );
+ $author2 = get_userdata( $author2_id );
+
+ $post_id = $this->factory->post->create( array(
+ 'post_author' => $author1_id,
+ 'post_status' => 'publish',
+ 'post_type' => 'post',
+ ) );
+ $this->_cap->add_coauthors( $post_id, array( $author1->user_login, $author2->user_login ) );
+
+ $query = new WP_Query( array(
+ 'author' => $author2_id,
+ ) );
+
+ $this->assertEquals( 1, count( $query->posts ) );
+ $this->assertEquals( $post_id, $query->posts[ 0 ]->ID );
+ }
+
+ public function test__author_name_arg_plus_tax_query__user_is_post_author() {
+ $author_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'batman' ) );
+ $author = get_userdata( $author_id );
+ $post_id = $this->factory->post->create( array(
+ 'post_author' => $author_id,
+ 'post_status' => 'publish',
+ 'post_type' => 'post',
+ ) );
+ $this->_cap->add_coauthors( $post_id, array( $author->user_login ) );
+ wp_set_post_terms( $post_id, 'test', 'post_tag' );
+
+ $query = new WP_Query( array(
+ 'author_name' => $author->user_login,
+ 'tag' => 'test',
+ ) );
+
+ $this->assertEquals( 1, count( $query->posts ) );
+ $this->assertEquals( $post_id, $query->posts[ 0 ]->ID );
+ }
+
+ public function tests__author_name_arg_plus_tax_query__is_coauthor() {
+ return; // TODO: re-enable; fails currently because our posts_join_filter doesn't add an exclusive JOIN on relationships + taxonomy to match the query mods we make. We'd need aliased JOINs on relationships + taxonomy on top of the JOIN that the tax query already adds.
+
+ $author1_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'batman' ) );
+ $author1 = get_userdata( $author1_id );
+ $author2_id = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'superman' ) );
+ $author2 = get_userdata( $author2_id );
+
+ $post_id = $this->factory->post->create( array(
+ 'post_author' => $author1_id,
+ 'post_status' => 'publish',
+ 'post_type' => 'post',
+ ) );
+ $this->_cap->add_coauthors( $post_id, array( $author1->user_login, $author2->user_login ) );
+ wp_set_post_terms( $post_id, 'test', 'post_tag' );
+
+ $query = new WP_Query( array(
+ 'author_name' => $author2->user_login,
+ 'tag' => 'test',
+ ) );
+ $this->assertEquals( 1, count( $query->posts ) );
+ $this->assertEquals( $post_id, $query->posts[ 0 ]->ID );
}
}
diff --git a/tests/test-manage-coauthors.php b/tests/test-manage-coauthors.php
index 1f5b1945..fad2f9ca 100644
--- a/tests/test-manage-coauthors.php
+++ b/tests/test-manage-coauthors.php
@@ -2,6 +2,57 @@
class Test_Manage_CoAuthors extends CoAuthorsPlus_TestCase {
+ public function setUp() {
+ parent::setUp();
+
+ $this->author1 = $this->factory->user->create( array( 'role' => 'author', 'user_login' => 'author1' ) );
+ $this->editor1 = $this->factory->user->create( array( 'role' => 'editor', 'user_login' => 'editor2' ) );
+
+ $post = array(
+ 'post_author' => $this->author1,
+ 'post_status' => 'publish',
+ 'post_content' => rand_str(),
+ 'post_title' => rand_str(),
+ 'post_type' => 'post',
+ );
+
+ $this->author1_post1 = wp_insert_post( $post );
+
+ $post = array(
+ 'post_author' => $this->author1,
+ 'post_status' => 'publish',
+ 'post_content' => rand_str(),
+ 'post_title' => rand_str(),
+ 'post_type' => 'post',
+ );
+
+ $this->author1_post2 = wp_insert_post( $post );
+
+ $page = array(
+ 'post_author' => $this->author1,
+ 'post_status' => 'publish',
+ 'post_content' => rand_str(),
+ 'post_title' => rand_str(),
+ 'post_type' => 'page',
+ );
+
+ $this->author1_page1 = wp_insert_post( $page );
+
+ $page = array(
+ 'post_author' => $this->author1,
+ 'post_status' => 'publish',
+ 'post_content' => rand_str(),
+ 'post_title' => rand_str(),
+ 'post_type' => 'page',
+ );
+
+ $this->author1_page2 = wp_insert_post( $page );
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+ }
+
/**
* Test assigning a Co-Author to a post
*/
diff --git a/wpcom-helper.php b/wpcom-helper.php
new file mode 100644
index 00000000..1c9bb1b1
--- /dev/null
+++ b/wpcom-helper.php
@@ -0,0 +1,238 @@
+is_enabled() && ! in_array( get_option( 'template' ), wpcom_vip_get_coauthors_plus_auto_apply_themes() ) )
+ add_action( 'admin_notices', function() {
+
+ // Allow this to be short-circuted in mu-plugins
+ if ( ! apply_filters( 'wpcom_coauthors_show_enterprise_notice', true ) )
+ return;
+
+ echo '' . __( "Co-Authors Plus isn't yet integrated with your theme. Please contact support to make it happen." ) . '
';
+ } );
+}
+
+/**
+ * We want to let Elasticsearch know that it should search the author taxonomy's name as a search field
+ * See: https://elasticsearchp2.wordpress.com/2015/01/08/in-36757-z-vanguard-says-they/
+ *
+ * @param $es_wp_query_args The ElasticSearch Query Parameters
+ * @param $query
+ *
+ * @return mixed
+ */
+function co_author_plus_es_support( $es_wp_query_args, $query ){
+ if ( empty( $es_wp_query_args['query_fields'] ) ) {
+ $es_wp_query_args['query_fields'] = array( 'title', 'content', 'author', 'tag', 'category' );
+ }
+
+ // Search CAP author names
+ $es_wp_query_args['query_fields'][] = 'taxonomy.author.name';
+
+ // Filter based on CAP names
+ if ( !empty( $query->query['author'] ) ) {
+ $es_wp_query_args['terms']['author'] = 'cap-' . $query->query['author'];
+ }
+
+ return $es_wp_query_args;
+}
+add_filter('wpcom_elasticsearch_wp_query_args', 'co_author_plus_es_support', 10, 2 );
+
+
+/**
+ * Change the post authors in the subscription email.
+ *
+ * Creates an array of authors, that will be used later.
+ *
+ * @param $author WP_User the original author
+ * @param $post_id
+ *
+ * @return array of coauthors
+ */
+add_filter( 'wpcom_subscriber_email_author', function( $author, $post_id ) {
+
+ $authors = get_coauthors( $post_id );
+ return $authors;
+
+}, 10, 2 );
+
+/**
+ * Change the author avatar url. If there are multiple authors, link the avatar to the post.
+ *
+ * @param $author_url
+ * @param $post_id
+ * @param $authors
+ *
+ * @return string with new author url.
+ */
+add_filter( 'wpcom_subscriber_email_author_url', function( $author_url, $post_id, $authors ) {
+ if( is_array( $authors ) ) {
+ if ( count( $authors ) > 1 ) {
+ return get_permalink( $post_id );
+ }
+
+ return get_author_posts_url( $authors[0]->ID, $authors[0]->user_nicename );
+ }
+
+ return get_author_posts_url( $authors->ID, $authors->user_nicename );
+}, 10, 3);
+
+/**
+ * Change the avatar to be the avatar of the first author
+ *
+ * @param $author_avatar
+ * @param $post_id
+ * @param $authors
+ *
+ * @return string with the html for the avatar
+ */
+add_filter( 'wpcom_subscriber_email_author_avatar', function( $author_avatar, $post_id, $authors ) {
+ if( is_array( $authors ) )
+ return coauthors_get_avatar( $authors[0], 50 );
+
+ return coauthors_get_avatar( $authors, 50 );
+}, 10, 3);
+
+/**
+ * Changes the author byline in the subscription email to include all the authors of the post
+ *
+ * @param $author_byline
+ * @param $post_id
+ * @param $authors
+ *
+ * @return string with the byline html
+ */
+add_filter( 'wpcom_subscriber_email_author_byline_html', function( $author_byline, $post_id, $authors ) {
+ // Check if $authors is a valid array
+ if( ! is_array( $authors ) ) {
+ $authors = array( $authors );
+ }
+
+ $byline = 'by ';
+ foreach( $authors as $author ) {
+ $byline .= '' . esc_html( $author->display_name ) . '';
+ if ( $author != end( $authors ) ) {
+ $byline .= ', ';
+ }
+ }
+
+ return $byline;
+}, 10, 3);
+
+/**
+ * Change the meta information to include all the authors
+ *
+ * @param $meta
+ * @param $post_id
+ * @param $authors
+ *
+ * @return array with new meta information
+ */
+add_filter( 'wpcom_subscriber_email_meta', function( $meta, $post_id, $authors ) {
+ // Check if $authors is a valid array
+ if( ! is_array( $authors ) ) {
+ $authors = array( $authors );
+ }
+
+ $author_meta = '';
+ foreach( $authors as $author ) {
+ $author_meta .= '' . esc_html( $author->display_name ) . '';
+
+ if ( $author != end( $authors ) ) {
+ $author_meta .= ', ';
+ }
+ }
+
+ // Only the first entry of meta includes the author listing
+ $meta[0] = $author_meta;
+
+ return $meta;
+}, 10, 3);
+
+/**
+ * Change the author information in the text-only subscription email.
+ *
+ * @param $author
+ * @param $post_id
+ *
+ * @returns string with the authors
+ */
+add_filter( 'wpcom_subscriber_text_email_author', function( $author, $post_id ) {
+ // Check if $authors is a valid array
+ $authors = get_coauthors( $post_id );
+
+ $author_text = '';
+ foreach( $authors as $author ) {
+ $author_text .= esc_html( $author->display_name );
+ if ( $author != end( $authors ) ) {
+ $author_text .= ', ';
+ }
+ }
+
+ return $author_text;
+}, 10, 2);
+
+/**
+ * Replace author_url in oembed endpoint response
+ * Since the oembed specification does not allow multiple urls, we'll go with the first coauthor
+ *
+ * The function is meant as a filter for `get_author_posts_url` function, which it is using as well
+ * Recursion is prevented by a simple check agains original attributes passed to the funciton. That
+ * also prevents execution in case the only coauthor is real author.
+ *
+ * This function is hooked only to oembed endpoint and it should stay that way
+ */
+
+function wpcom_vip_cap_replace_author_link( $link, $author_id, $author_nicename ) {
+
+ //get coauthors and iterate to the first one
+ //in case there are no coauthors, the Iterator returns current author
+ $i = new CoAuthorsIterator();
+ $i->iterate();
+
+ //check if the current $author_id and $author_nicename is not the same as the first coauthor
+ if ( $i->current_author->ID !== $author_id || $i->current_author->user_nicename !== $author_nicename ) {
+
+ //alter the author_url
+ $link = get_author_posts_url( $i->current_author->ID, $i->current_author->user_nicename );
+
+ }
+
+ return $link;
+}
+
+add_action( 'init', function() {
+ //Hook the above callback only on oembed endpoint reply
+ if ( true === defined( 'WPCOM_VIP_IS_OEMBED' ) && true === constant( 'WPCOM_VIP_IS_OEMBED' ) && true === apply_filters( 'wpcom_vip_coauthors_replace_oembed', false, 'author_url' ) ) {
+ add_filter( 'author_link', 'wpcom_vip_cap_replace_author_link', 99, 3 );
+ }
+}, 9 );