From b22220c44deeef78eb15ce4ce807835db40db611 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Mon, 17 Feb 2020 21:54:48 +0100 Subject: [PATCH 01/12] Fix bug where link to network settings from the site settings page would be displayed to network admins where ALP is active on the site only, not network-wide --- ssl-alp/alp.php | 3 ++- ssl-alp/includes/class-ssl-alp.php | 12 +++++++++++- ssl-alp/partials/admin/settings/display-site.php | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ssl-alp/alp.php b/ssl-alp/alp.php index 55efa24..3a1f446 100644 --- a/ssl-alp/alp.php +++ b/ssl-alp/alp.php @@ -24,10 +24,11 @@ define( 'SSL_ALP_VERSION', '0.18.0' ); /** - * Plugin name + * Plugin name and path */ define( 'SSL_ALP_PLUGIN_NAME', 'Academic Labbook' ); +define( 'SSL_ALP_PLUGIN_PATH', 'ssl-alp/alp.php' ); /** * Base plugin directory diff --git a/ssl-alp/includes/class-ssl-alp.php b/ssl-alp/includes/class-ssl-alp.php index 30bc5e9..ed467d0 100644 --- a/ssl-alp/includes/class-ssl-alp.php +++ b/ssl-alp/includes/class-ssl-alp.php @@ -232,9 +232,14 @@ public function load_plugin_textdomain() { } /** - * Register the network settings page, if network is enabled. + * Register the network settings page, if plugin is enabled network-wide. */ public function add_network_admin_menu() { + if ( ! is_plugin_active_for_network( SSL_ALP_PLUGIN_PATH ) ) { + // Network settings only available when plugin is network active. + return; + } + add_submenu_page( 'settings.php', // Network settings page. __( 'Academic Labbook Settings', 'ssl-alp' ), @@ -294,6 +299,11 @@ public function output_site_settings_page() { * Handle settings data posted from the network settings page. */ public function update_network_options() { + if ( ! is_plugin_active_for_network( SSL_ALP_PLUGIN_PATH ) ) { + // Network settings only available when plugin is network active. + return; + } + // Check nonce. check_admin_referer( 'ssl-alp-network-admin-options' ); diff --git a/ssl-alp/partials/admin/settings/display-site.php b/ssl-alp/partials/admin/settings/display-site.php index 51dc30a..f24e73b 100644 --- a/ssl-alp/partials/admin/settings/display-site.php +++ b/ssl-alp/partials/admin/settings/display-site.php @@ -6,7 +6,7 @@ ?>

- +

Date: Thu, 27 Feb 2020 14:44:11 +0100 Subject: [PATCH 02/12] Remove manual WP_Query SQL modifications for including coauthors in search results The system copied from the CoAuthors Plus plugin modified the SQL query in WP_Query directly. This is discouraged and unnecessary since WP_Query can include taxonomy terms in searches directly. The down-side is that WP_Query cannot match against authors OR taxonomy terms, only AND. That means that the default WP_Query object parsed from a request for an author page must be modified to remove the author clause and then have only the taxonomy term for the author added. This breaks WordPress functionality elsewhere, such as the handling of 404 pages when there are no author posts found, so the author clause has to be restored once the query has been executed. --- ssl-alp/includes/class-ssl-alp-coauthors.php | 304 +++++++------------ 1 file changed, 105 insertions(+), 199 deletions(-) diff --git a/ssl-alp/includes/class-ssl-alp-coauthors.php b/ssl-alp/includes/class-ssl-alp-coauthors.php index c1d1188..0c731c9 100644 --- a/ssl-alp/includes/class-ssl-alp-coauthors.php +++ b/ssl-alp/includes/class-ssl-alp-coauthors.php @@ -31,11 +31,11 @@ class SSL_ALP_Coauthors extends SSL_ALP_Module { protected $coauthor_term_slug_prefix = 'ssl-alp-coauthor-'; /** - * HAVING SQL clause. + * Whether we're doing an author query or not. * - * @var string + * @var bool */ - protected $having_terms = ''; + protected $is_author_query = false; /** * Register settings. @@ -98,9 +98,13 @@ public function register_hooks() { $loader->add_action( 'set_object_terms', $this, 'check_post_author', 10, 4 ); // Modify SQL queries to include coauthors where appropriate. - $loader->add_filter( 'posts_where', $this, 'posts_where_filter', 10, 2 ); - $loader->add_filter( 'posts_join', $this, 'posts_join_filter', 10, 2 ); - $loader->add_filter( 'posts_groupby', $this, 'posts_groupby_filter', 10, 2 ); + $loader->add_filter( 'pre_get_posts', $this, 'add_coauthor_posts_to_query' ); + + // Make sure we've correctly set author query flag on author pages. + $loader->add_action( 'posts_selection', $this, 'fix_author_query', 10, 0 ); + + // Make sure the author data is correctly set after an author query. + $loader->add_action( 'wp', $this, 'fix_author_query_data', 10, 0 ); // Allow public coauthor query vars. $loader->add_filter( 'query_vars', $this, 'whitelist_search_query_vars' ); @@ -154,11 +158,6 @@ public function register_hooks() { // every count event. $loader->add_filter( 'get_usernumposts', $this, 'filter_count_user_posts', 10, 2 ); - // Make sure we've correctly set author data on author pages. - // Use posts_selection since it's after WP_Query has built the request and before it's queried any posts. - $loader->add_action( 'posts_selection', $this, 'fix_author_page', 10, 0 ); - $loader->add_filter( 'the_author', $this, 'fix_author_page_filter', 10, 1 ); - // Filter the display of coauthor terms in the admin post list. $loader->add_filter( 'ssl-alp-coauthor_name', $this, 'filter_coauthor_term_display', 10, 3 ); @@ -393,7 +392,7 @@ private function get_coauthor_term_slug( $user ) { * * @param WP_Term $term Coauthor term. * - * @return WP_User + * @return WP_User|false */ private function get_user_from_coauthor_term( $term ) { if ( substr( $term->slug, 0, strlen( $this->coauthor_term_slug_prefix ) ) !== $this->coauthor_term_slug_prefix ) { @@ -819,154 +818,143 @@ public function filter_count_user_posts( $count, $user_id ) { } /** - * Modify the author query posts SQL to include posts co-authored. + * Modify the WP_Query to match coauthored posts when an author query is being made. * - * @param string $join JOIN SQL clause. - * @param string $query SQL query. - * @return string + * This modifies the query by removing the match against post author and replacing it with a + * coauthor taxonomy term search. This allows the post query to match posts that are coauthored + * by the author as well as those they wrote as primary author. + * + * This function removes the 'author' and 'author_name' query variables. The function + * `fix_author_query` restores 'author' to avoid WP::handle_404 throwing a 404 when an author + * is a member of a blog but has no posts. + * + * @param WP_Query $query The WP_Query instance (passed by reference). + * @return null */ - public function posts_join_filter( $join, $query ) { + public function add_coauthor_posts_to_query( $query ) { if ( ! get_option( 'ssl_alp_allow_multiple_authors' ) ) { // Coauthors disabled. - return $join; + return; } - global $wpdb; - - if ( ! $query->is_author() ) { - // Not an author query, so return unmodified. - return $join; + if ( ! empty( $query->query_vars['post_type'] ) && ! is_object_in_taxonomy( $query->query_vars['post_type'], 'ssl-alp-coauthor' ) ) { + // The query is for a particular post type and it's not one that we add coauthors to. + return; } - if ( ! empty( $query->query_vars['post_type'] ) && ! is_object_in_taxonomy( $query->query_vars['post_type'], 'ssl-alp-coauthor' ) ) { - // Not a valid post type, so return unmodified. - return $join; + $queried_author_name = $query->query_vars['author_name']; + $queried_author_id = $query->query_vars['author']; + + if ( ! empty( $queried_author_name ) ) { + // author_name is the user nicename which is also the slug. + $coauthor = get_user_by( 'slug', $queried_author_name ); + } elseif ( ! empty( $queried_author_id ) ) { + // author is the user ID. + $coauthor = get_user_by( 'id', $queried_author_id ); + } else { + // This is not an author query. + return; } - if ( empty( $this->having_terms ) ) { - return $join; + if ( ! $coauthor ) { + // Coauthor not found. + return; } - $term_relationship_join = " LEFT JOIN {$wpdb->term_relationships} AS tr1 ON ({$wpdb->posts}.ID = tr1.object_id)"; + $term = $this->get_coauthor_term( $coauthor ); + + if ( ! $term ) { + // No coauthor term available for the specified user. + return; + } - $term_taxonomy_join = " LEFT JOIN {$wpdb->term_taxonomy} ON ( tr1.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id )"; + // Set that we're doing an author query. + $this->is_author_query = true; - $join .= $term_relationship_join; - $join .= $term_taxonomy_join; + // Unset author query (we replace it with taxonomy query). This is necessary otherwise + // WP_Query will AND the post primary author into the query, which will only return + // primary authored posts for this author. + unset( $query->query_vars['author_name'] ); + unset( $query->query_vars['author'] ); - return $join; + $query->query_vars['taxonomy'] = 'ssl-alp-coauthor'; + $query->query_vars['term'] = $term->slug; } /** - * Modify the author query posts SQL to include posts co-authored. + * Fix internal WP_Query fields due to changes to the author query in + * `add_coauthor_posts_to_query`. * - * @param string $where WHERE SQL clause. - * @param string $query SQL query. - * @return string + * @global WP_Query $wp_query */ - public function posts_where_filter( $where, $query ) { + public function fix_author_query() { + global $wp_query; + if ( ! get_option( 'ssl_alp_allow_multiple_authors' ) ) { // Coauthors disabled. - return $where; - } - - global $wpdb; - - if ( ! $query->is_author() ) { - // Not an author query, so return unmodified. - return $where; + return; } - if ( ! empty( $query->query_vars['post_type'] ) && ! is_object_in_taxonomy( $query->query_vars['post_type'], 'ssl-alp-coauthor' ) ) { - // Not a valid post type, so return unmodified. - return $where; + if ( ! $this->is_author_query ) { + // Not an author post query. + return; } - if ( $query->get( 'author_name' ) ) { - // author_name is actually user_nicename. - $author_nicename = $query->get( 'author_name' ); + if ( ! empty( $wp_query->tax_query->queried_terms ) ) { + $queried_taxonomies = array_keys( $wp_query->tax_query->queried_terms ); + $matched_taxonomy = reset( $queried_taxonomies ); + $query = $wp_query->tax_query->queried_terms[ $matched_taxonomy ]; - if ( is_null( $author_nicename ) ) { - // No author defined. - return $where; + if ( ! empty( $query['terms'] ) ) { + if ( 'term_id' == $query['field'] ) { + $term = get_term( reset( $query['terms'] ), $matched_taxonomy ); + } else { + $term = get_term_by( $query['field'], reset( $query['terms'] ), $matched_taxonomy ); + } } - // user_nicename == slug. - $coauthor = get_user_by( 'slug', $author_nicename ); - } else { - $author_data = get_userdata( $query->get( 'author' ) ); - - if ( ! is_object( $author_data ) ) { - // No author defined. - return $where; - } + if ( ! empty( $term ) && ! is_wp_error( $term ) ) { + // Get author from term. + $author = $this->get_user_from_coauthor_term( $term ); - $coauthor = get_user_by( 'login', $author_data->user_login ); - } + if ( $author ) { + // Set queried object. + $wp_query->queried_object = $author; + $wp_query->queried_object_id = (int) $author->ID; - $terms = array(); - $author_term = $this->get_coauthor_term( $coauthor ); + // Set flags as if we had loaded an author page. + $wp_query->is_author = true; + $wp_query->is_archive = true; - if ( $author_term ) { - $terms[] = $author_term; - } - - if ( ! empty( $terms ) ) { - $terms_implode = ''; - $this->having_terms = ''; - - foreach ( $terms as $term ) { - $terms_implode .= "({$wpdb->term_taxonomy}.taxonomy = 'ssl-alp-coauthor' AND {$wpdb->term_taxonomy}.term_id = '{$term->term_id}') OR "; - $this->having_terms .= " {$wpdb->term_taxonomy}.term_id = '{$term->term_id}' OR "; + // To avoid the WP::handle_404 throwing a 404 when there is no author query var, + // set it here. + set_query_var( 'author', $author->ID ); + } } - - $terms_implode = rtrim( $terms_implode, ' OR' ); - $this->having_terms = rtrim( $this->having_terms, ' OR' ); - - // Match "wp_posts.post_author = [number]" or "wp_posts.post_author IN ([list of numbers])" - // and append "OR (wp_term_taxonomy.taxonomy = 'ssl-alp-coauthor' AND wp_term_taxonomy.term_id = '6')". - $where = preg_replace( - '/(\b(?:' . $wpdb->posts . '\.)?post_author\s*(?:=|IN)\s*\(?(\d+)\)?)/', - '($1 OR ' . $terms_implode . ')', - $where, - 1 - ); } - - return $where; } /** - * Modify the author query posts SQL to include posts co-authored. + * Fix author data shown on author archive page. * - * @param string $groupby GROUP BY SQL clause. - * @param string $query SQL query. - * @return string + * We override the author query to include coauthored posts, so there is a chance the first + * retrieved post has another author as its primary author. WordPress uses the display name of + * the primary author of the first retrieved post on the author archive page, so we have to + * manually set this to the real author. + * + * @global WP_Query $wp_query + * @global WP_User $authordata */ - public function posts_groupby_filter( $groupby, $query ) { + public function fix_author_query_data() { + global $wp_query, $authordata; + if ( ! get_option( 'ssl_alp_allow_multiple_authors' ) ) { // Coauthors disabled. - return $groupby; - } - - global $wpdb; - - if ( ! $query->is_author() ) { - // Not an author query, so return unmodified. - return $groupby; - } - - if ( ! empty( $query->query_vars['post_type'] ) && ! is_object_in_taxonomy( $query->query_vars['post_type'], 'ssl-alp-coauthor' ) ) { - // Not a valid post type, so return unmodified. - return $groupby; - } - - if ( $this->having_terms ) { - $having = "MAX( IF ( {$wpdb->term_taxonomy}.taxonomy = 'ssl-alp-coauthor', IF ( {$this->having_terms}, 2, 1 ), 0 ) ) <> 1 "; - $groupby = "{$wpdb->posts}.ID HAVING {$having}"; + return; } - return $groupby; + // Use the queried author set by `fix_author_query`. + $authordata = $wp_query->queried_object; } /** @@ -1563,88 +1551,6 @@ public function get_user_post_count( $user ) { return $user_term->count; } - /** - * Fix for author pages 404ing or not properly displaying on author pages - * - * If an author has no posts, we only want to force the queried object to be - * the author if they're a member of the blog. - * - * If the author does have posts, it doesn't matter that they're not an author. - * - * Alternatively, on an author page, if the first story has coauthors and - * the first author is NOT the same as the author for the archive, - * the query_var is changed. - */ - public function fix_author_page() { - global $wp_query, $authordata; - - if ( ! get_option( 'ssl_alp_allow_multiple_authors' ) ) { - // Coauthors disabled. - return; - } - - if ( ! is_author() ) { - // Page is not an author page. - return; - } - - $author_id = absint( get_query_var( 'author' ) ); - $author_name = sanitize_title( get_query_var( 'author_name' ) ); - - if ( isset( $author_id ) ) { - // Get author by ID. - $author = get_user_by( 'id', $author_id ); - } elseif ( isset( $author_name ) ) { - // Get author by specified name. - $author = get_user_by( 'slug', $author_name ); - } else { - // No query variable was specified; not much we can do. - return; - } - - if ( is_object( $author ) ) { - // Override the authordata global with the requested author, in case the first post's - // primary author is not the requested author. - $authordata = $author; - $term = $this->get_coauthor_term( $authordata ); - } - - if ( ( is_object( $authordata ) ) || ( ! empty( $term ) ) ) { - // Update the query to the requested author. - $wp_query->queried_object = $authordata; - $wp_query->queried_object_id = $authordata->ID; - } else { - $wp_query->queried_object = null; - $wp_query->queried_object_id = null; - $wp_query->is_author = false; - $wp_query->is_archive = false; - $wp_query->is_404 = false; - } - } - - /** - * Fix author page filter to show author name instead of their coauthor term name. - * - * @param string $author_name Author name. - * @return string - */ - public function fix_author_page_filter( $author_name ) { - if ( ! get_option( 'ssl_alp_allow_multiple_authors' ) ) { - // Coauthors disabled. - return $author_name; - } - - if ( ! is_author() ) { - // Page is not an author page. - return $author_name; - } - - global $wp_query; - - // Set author from query. - return $wp_query->queried_object->display_name; - } - /** * Add coauthor emails to comment email recipients. * From c2671e0d37f720175d6e69c990851047228ba288 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Thu, 27 Feb 2020 15:07:52 +0100 Subject: [PATCH 03/12] Fix tests --- ssl-alp/tests/unit/test-coauthors.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ssl-alp/tests/unit/test-coauthors.php b/ssl-alp/tests/unit/test-coauthors.php index a42e208..ade4ecf 100644 --- a/ssl-alp/tests/unit/test-coauthors.php +++ b/ssl-alp/tests/unit/test-coauthors.php @@ -299,7 +299,7 @@ function test_add_same_coauthor_to_post__author_name_arg() { $query = new WP_Query( array( - 'author_name' => $user->user_login + 'author_name' => $user->user_nicename, ) ); @@ -328,7 +328,7 @@ public function test__author_name_arg_plus_tax_query__user_is_post_author() { $query = new WP_Query( array( - 'author_name' => $user->user_login, + 'author_name' => $user->user_nicename, 'tag' => 'test', ) ); @@ -337,7 +337,7 @@ public function test__author_name_arg_plus_tax_query__user_is_post_author() { $this->assertEquals( $post->ID, $query->posts[ 0 ]->ID ); } - public function tests__author_name_arg_plus_tax_query__is_coauthor() { + public function test__author_name_arg_plus_tax_query__is_coauthor() { global $ssl_alp; $user_1 = $this->factory->user->create_and_get(); @@ -356,7 +356,7 @@ public function tests__author_name_arg_plus_tax_query__is_coauthor() { $query = new WP_Query( array( - 'author_name' => $user_2->user_login, + 'author_name' => $user_2->user_nicename, 'tag' => 'test', ) ); From e1a49b3d1bdbb034de2c037ec476755beee5f0ce Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Thu, 27 Feb 2020 15:08:08 +0100 Subject: [PATCH 04/12] Change to array-based taxonomy query vars --- ssl-alp/includes/class-ssl-alp-coauthors.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ssl-alp/includes/class-ssl-alp-coauthors.php b/ssl-alp/includes/class-ssl-alp-coauthors.php index 0c731c9..652c8fd 100644 --- a/ssl-alp/includes/class-ssl-alp-coauthors.php +++ b/ssl-alp/includes/class-ssl-alp-coauthors.php @@ -877,8 +877,13 @@ public function add_coauthor_posts_to_query( $query ) { unset( $query->query_vars['author_name'] ); unset( $query->query_vars['author'] ); - $query->query_vars['taxonomy'] = 'ssl-alp-coauthor'; - $query->query_vars['term'] = $term->slug; + $query->query_vars['tax_query'] = array( + array( + 'taxonomy' => 'ssl-alp-coauthor', + 'terms' => array( $term->slug ), + 'field' => 'slug', + ), + ); } /** From dc2f5096f9bfd0f159f19d810f7f11f9bcf2b04e Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Thu, 27 Feb 2020 15:55:22 +0100 Subject: [PATCH 05/12] Update Travis config --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4576b9a..eaa9091 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ -sudo: false +os: linux + dist: trusty language: php @@ -13,10 +14,12 @@ cache: - vendor - $HOME/.composer/cache -matrix: +jobs: include: - php: nightly env: WP_VERSION=latest + - php: 7.4 + env: WP_VERSION=latest - php: 7.3 env: WP_VERSION=latest - php: 7.2 From d558d5f1841cfb9afae50789eddbbe6f424bc066 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Thu, 27 Feb 2020 15:58:33 +0100 Subject: [PATCH 06/12] Update Travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eaa9091..ab0f262 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,7 +43,7 @@ before_script: if [[ ! -z "$WP_VERSION" ]] ; then bash ssl-alp/bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION case "$TRAVIS_PHP_VERSION" in - 7.3|7.2|7.1|7.0|nightly) + 7.4|7.3|7.2|7.1|7.0|nightly) echo "Using PHPUnit 6.x" composer global require "phpunit/phpunit:^6" ;; From c6c2e54052cbd44d0ee8b9c8efcbe4ebe5d733a2 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Mon, 2 Mar 2020 23:01:03 +0100 Subject: [PATCH 07/12] Update bugs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dfed2d0..e8b78c6 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ been pushed back to these plugins and WordPress itself: [17](https://github.com/adamsilverstein/mathml-block/issues/12), [18](https://github.com/WordPress/gutenberg/issues/13749), [19](https://core.trac.wordpress.org/ticket/46459) +[20](https://github.com/WordPress/gutenberg/issues/20600) ### Co-Authors Plus Authors: Mohammad Jangda, Daniel Bachhuber, Automattic, Shepherd Interactive, Mark Jaquith From 4c24e41b62cdc8e08f419bf8f95e1baaec28d869 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Tue, 10 Mar 2020 23:15:00 +0100 Subject: [PATCH 08/12] Add ability to disable the display of cross-references on supported posts --- ssl-alp/includes/class-ssl-alp-inventory.php | 1 + ssl-alp/includes/class-ssl-alp-references.php | 105 +++++++++++++++++- .../includes/class-ssl-alp-uninstaller.php | 1 + 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/ssl-alp/includes/class-ssl-alp-inventory.php b/ssl-alp/includes/class-ssl-alp-inventory.php index 5d8442b..f235e8b 100644 --- a/ssl-alp/includes/class-ssl-alp-inventory.php +++ b/ssl-alp/includes/class-ssl-alp-inventory.php @@ -190,6 +190,7 @@ public function register_post_type() { 'revisions', 'page-attributes', 'thumbnail', + 'custom-fields', ), 'rewrite' => array( 'slug' => 'inventory', diff --git a/ssl-alp/includes/class-ssl-alp-references.php b/ssl-alp/includes/class-ssl-alp-references.php index ce2c2ab..149e1c1 100644 --- a/ssl-alp/includes/class-ssl-alp-references.php +++ b/ssl-alp/includes/class-ssl-alp-references.php @@ -26,6 +26,26 @@ class SSL_ALP_References extends SSL_ALP_Module { 'ssl-alp-inventory' => false, ); + /** + * Register scripts. + */ + public function register_scripts() { + // Hide cross-references editor plugin. + wp_register_script( + 'ssl-alp-hide-crossreferences-block-editor-js', + esc_url( SSL_ALP_BASE_URL . 'js/references/index.js' ), + array( + 'wp-edit-post', + 'wp-plugins', + 'wp-i18n', + 'wp-element', + 'wp-compose', + ), + $this->get_version(), + true + ); + } + /** * Register settings. */ @@ -65,10 +85,26 @@ public function register_hooks() { // Create cross-reference taxonomy. Priority makes it get called after settings are registered. $loader->add_action( 'init', $this, 'register_crossreference_taxonomy', 20 ); + // Register post meta flag to hide cross-references from the post page. + $loader->add_action( 'init', $this, 'register_hide_crossreferences_post_meta', 20 ); + // Extract references from saved posts. $loader->add_action( 'save_post', $this, 'extract_crossreferences', 10, 2 ); } + /** + * Enqueue block editor scripts. + */ + public function enqueue_block_editor_scripts() { + // Get post being edited. + $post = get_post(); + + if ( $this->is_supported( $post ) ) { + // Enqueue block editor plugin script. + wp_enqueue_script( 'ssl-alp-hide-crossreferences-block-editor-js' ); + } + } + /** * Reference settings partial. */ @@ -101,6 +137,33 @@ public function register_crossreference_taxonomy() { ); } + /** + * Register the hide cross-references post meta. + * + * This flag is used to avoid displaying cross-references to posts on their + * post page. This can be used for example on posts which get regularly + * linked to by other posts and where you don't want the clutter of having + * these pages linked in the theme. + */ + public function register_hide_crossreferences_post_meta() { + if ( ! get_option( 'ssl_alp_enable_crossreferences' ) ) { + // Cross-references are disabled. + return; + } + + foreach ( array_keys( $this->supported_reference_post_types ) as $post_type ) { + register_post_meta( + $post_type, + 'ssl_alp_hide_crossreferences_to', + array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'boolean', + ) + ); + } + } + /** * Extract references from updated/created posts and insert them into the term database for * display under the post. @@ -221,11 +284,40 @@ public function rebuild_references() { } } + /** + * Check if the specified post has cross-references hidden. + * + * A post allows cross-references to and from other posts to be shown on the + * theme if it is supported and the post has not had cross-references hidden + * by setting the appropriate meta flag. + * + * @param WP_Post $post Post ID or post object. Defaults to global $post. + * @return boolean|null Whether cross-references are enabled, or null if the + * cross-references system is disabled or the post type + * is not found or not supported. + */ + public function crossreferences_hidden( $post ) { + if ( ! get_option( 'ssl_alp_enable_crossreferences' ) ) { + // Cross-references are disabled. + return; + } + + $post = get_post( $post ); + + if ( is_null( $post ) || ! $this->is_supported( $post ) ) { + // Post is invalid or post type is not supported. + return; + } + + return (bool) get_post_meta( $post->ID, 'ssl_alp_hide_crossreferences_to', true ); + } + /** * Get posts that are referenced by the specified post. * * @param int|WP_Post|null $post Post ID or post object. Defaults to global $post. - * @return array|null Referenced posts, or null if invalid post specified. + * @return array|null Referenced posts, or null if invalid post specified or + * if the post has been set to hide cross-references. * * @global $ssl_alp */ @@ -238,6 +330,10 @@ public function get_reference_to_posts( $post = null ) { return; } + if ( $this->crossreferences_hidden( $post ) ) { + return; + } + $terms = get_the_terms( $post, 'ssl-alp-crossreference' ); $posts = array(); @@ -287,7 +383,8 @@ public function get_reference_to_posts( $post = null ) { * Get posts that reference the specified post. * * @param int|WP_Post|null $post Post ID or post object. Defaults to global $post. - * @return array|null Referencing posts, or null if invalid post specified. + * @return array|null Referencing posts, or null if invalid post specified or + * if the post has been set to hide cross-references. * @global $wpdb * @global $ssl_alp; */ @@ -300,6 +397,10 @@ public function get_reference_from_posts( $post = null ) { return; } + if ( $this->crossreferences_hidden( $post ) ) { + return; + } + // Reference posts cache key. $cache_key = 'ssl-alp-reference-from_posts-' . $post->ID; diff --git a/ssl-alp/includes/class-ssl-alp-uninstaller.php b/ssl-alp/includes/class-ssl-alp-uninstaller.php index 3bd4a1e..ad897a0 100644 --- a/ssl-alp/includes/class-ssl-alp-uninstaller.php +++ b/ssl-alp/includes/class-ssl-alp-uninstaller.php @@ -238,6 +238,7 @@ private static function delete_taxonomy( $taxonomy ) { private static function delete_post_metas() { self::delete_post_meta( 'ssl_alp_edit_summary' ); self::delete_post_meta( 'ssl_alp_edit_summary_revert_id' ); + self::delete_post_meta( 'ssl_alp_hide_crossreferences_to' ); } /** From 02a0f585418f8e0b588bcd59009891eb1d8e5bb9 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Tue, 10 Mar 2020 23:15:25 +0100 Subject: [PATCH 09/12] Add required JavaScript for last commit --- ssl-alp/js/references/index.js | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 ssl-alp/js/references/index.js diff --git a/ssl-alp/js/references/index.js b/ssl-alp/js/references/index.js new file mode 100644 index 0000000..0a9271e --- /dev/null +++ b/ssl-alp/js/references/index.js @@ -0,0 +1,76 @@ +/** + * Cross-reference editor tools. + * + * This adds a checkbox to the sidebar when composing or editing a post which + * avoids cross-references from appearing under the post. + * + * @package ssl-alp + */ + +( function( wp ) { + const el = wp.element.createElement; + const __ = wp.i18n.__; + const Component = wp.element.Component; + const PluginPostStatusInfo = wp.editPost.PluginPostStatusInfo; + const CheckboxControl = wp.components.CheckboxControl; + const registerPlugin = wp.plugins.registerPlugin; + const compose = wp.compose.compose; + const withSelect = wp.data.withSelect; + const withDispatch = wp.data.withDispatch; + + class CrossReferencePlugin extends Component { + render() { + const { hideCrossReferences, setHideCrossReferences } = this.props; + + return el( + PluginPostStatusInfo, + { + className: 'ssl-alp-hide-crossreferences-panel' + }, + el( + CheckboxControl, + { + name: 'ssl_alp_hide_crossreferences', + label: __( 'Hide cross-references', 'ssl-alp' ), + help: __( 'Do not display posts linked to/from this one on the post page', 'ssl-alp' ), + checked: hideCrossReferences, + onChange: ( value ) => { + setHideCrossReferences( value ); + } + } + ) + ); + } + } + + const CrossReferencePluginHOC = compose( [ + withSelect( ( select ) => { + const { getEditedPostAttribute } = select( 'core/editor' ); + + const editedPostAttributes = getEditedPostAttribute( 'meta' ); + const hideCrossReferences = editedPostAttributes[ 'ssl_alp_hide_crossreferences_to' ]; + + return { hideCrossReferences }; + } ), + withDispatch( ( dispatch ) => { + const { editPost } = dispatch( 'core/editor' ); + + return { + setHideCrossReferences: function( value ) { + editPost( { + meta: { ssl_alp_hide_crossreferences_to: value }, + } ); + } + } + } ) + ])( CrossReferencePlugin ); + + /** + * Register sidebar plugin with block editor. + */ + registerPlugin( 'ssl-alp-hide-crossreferences-plugin', { + render: CrossReferencePluginHOC + } ); +} )( + window.wp +); From faaf1e5e62d1581b40d97e27bc9e0a55c4fa4e68 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Tue, 10 Mar 2020 23:26:46 +0100 Subject: [PATCH 10/12] Refactor edit summary JS --- ssl-alp/js/edit-summary/index.js | 436 ++++++++++++++++--------------- 1 file changed, 231 insertions(+), 205 deletions(-) diff --git a/ssl-alp/js/edit-summary/index.js b/ssl-alp/js/edit-summary/index.js index 14b1808..60720fd 100644 --- a/ssl-alp/js/edit-summary/index.js +++ b/ssl-alp/js/edit-summary/index.js @@ -1,231 +1,257 @@ -const el = wp.element.createElement; -const __ = wp.i18n.__; -const Component = wp.element.Component; -const PluginPostStatusInfo = wp.editPost.PluginPostStatusInfo; -const TextControl = wp.components.TextControl; -const registerPlugin = wp.plugins.registerPlugin; -const registerStore = wp.data.registerStore; -const compose = wp.compose.compose; -const withSelect = wp.data.withSelect; -const withDispatch = wp.data.withDispatch; - -const DEFAULT_STATE = { - // edit summary initially empty - editSummary: '' -}; - -const actions = { - setEditSummary( editSummary ) { - return { - type: 'SET_EDIT_SUMMARY', - editSummary: editSummary - }; - }, - - resetEditSummary() { - return { - type: 'SET_EDIT_SUMMARY', - editSummary: DEFAULT_STATE.editSummary - }; - } -}; - -const editSummaryStore = registerStore( 'ssl-alp/edit-summary', { - reducer( state = DEFAULT_STATE, action ) { - switch ( action.type ) { - case 'SET_EDIT_SUMMARY': - return { - ...state, - editSummary: action.editSummary - }; - } - - return state; - }, - - actions, - - selectors: { - getEditSummary( state ) { - return state.editSummary; - }, - }, -} ); - -class EditSummaryPlugin extends Component { - constructor() { - super( ...arguments ); - - this.state = { - // create textbox value that is initially empty - editSummary: '' +/** + * Edit summary editor tools. + * + * This adds a textbox to the sidebar when editing an existing post which allows + * the user to specify an edit summary. + * + * @package ssl-alp + */ + +( function( wp ) { + const el = wp.element.createElement; + const __ = wp.i18n.__; + const Component = wp.element.Component; + const PluginPostStatusInfo = wp.editPost.PluginPostStatusInfo; + const TextControl = wp.components.TextControl; + const registerPlugin = wp.plugins.registerPlugin; + const registerStore = wp.data.registerStore; + const compose = wp.compose.compose; + const withSelect = wp.data.withSelect; + const withDispatch = wp.data.withDispatch; + + const DEFAULT_STATE = { + // Edit summary initially empty. + editSummary: '' + }; + + const actions = { + setEditSummary( editSummary ) { + return { + type: 'SET_EDIT_SUMMARY', + editSummary: editSummary + }; + }, + + resetEditSummary() { + return { + type: 'SET_EDIT_SUMMARY', + editSummary: DEFAULT_STATE.editSummary + }; } + }; + + registerStore( 'ssl-alp/edit-summary', { + reducer( state = DEFAULT_STATE, action ) { + switch ( action.type ) { + case 'SET_EDIT_SUMMARY': + return { + ...state, + editSummary: action.editSummary + }; + } - /** - * Send the edit summary to WordPress when a new revision is created. - */ + return state; + }, + + actions, + + selectors: { + getEditSummary( state ) { + return state.editSummary; + }, + }, + } ); + + class EditSummaryPlugin extends Component { + constructor() { + super( ...arguments ); - // previous (initial) revision ID - let lastRevisionId = this.props.lastRevisionId; + this.state = { + // Create textbox value that is initially empty. + editSummary: '', + } - wp.data.subscribe(() => { - if ( this.props.isPublished && this.props.isSaving && !this.props.isAutosaving && !this.props.isPublishing ) { - // User is saving update to published post. + /** + * Send the edit summary to WordPress when a new revision is created. + */ - // get latest revision ID - let newRevisionId = this.props.lastRevisionId; + // Previous (initial) revision ID. + let { lastRevisionId } = this.props; - if ( newRevisionId !== null && newRevisionId !== lastRevisionId ) { - // a new revision has been created + wp.data.subscribe( () => { + const { + isPublished, + isPublishing, + isSaving, + isAutosaving, + getEditSummary, + resetEditSummary, + } = this.props; - // get edit message - let editSummary = this.props.getEditSummary(); + if ( isPublished && isSaving && ! isAutosaving && ! isPublishing ) { + // User is saving update to published post. - // set this message in the revision - this.setRevisionEditSummary( newRevisionId, editSummary ); + // Get latest revision ID. + let newRevisionId = this.props.lastRevisionId; - // update revision - lastRevisionId = newRevisionId; + if ( newRevisionId !== null && newRevisionId !== lastRevisionId ) { + // A new revision has been created. - if ( this.props.isSaving && !this.props.isAutosaving ) { - // clear the edit summary - this.props.resetEditSummary(); + // Get edit message. + const editSummary = getEditSummary(); + + // Set this message in the revision. + this.setRevisionEditSummary( newRevisionId, editSummary ); + + // Update revision. + lastRevisionId = newRevisionId; + + if ( isSaving && ! isAutosaving ) { + // Clear the edit summary. + resetEditSummary(); + } } + } else if ( isPublishing ) { + // User is publishing a new post. + + // Set last revision id. + lastRevisionId = this.props.lastRevisionId; } - } else if ( this.props.isPublishing ) { - // User is publishing a new post. + } ); - // set last revision id - lastRevisionId = this.props.lastRevisionId; - } - }); + /** + * Set edit summary when it changes elsewhere. + * + * This is used to allow the edit summary textbox to be cleared when its contents is sent + * to WordPress. + */ - /** - * Set edit summary when it changes elsewhere. - * - * This is used to allow the edit summary textbox to be cleared when its contents is sent - * to WordPress. - */ - - // previous (initial) edit summary value - let lastEditSummary = ''; + // Previous (initial) edit summary value. + let lastEditSummary = ''; - wp.data.subscribe(() => { - // latest edit summary - let newEditSummary = this.props.getEditSummary(); + wp.data.subscribe( () => { + const { getEditSummary } = this.props; - if ( newEditSummary !== null && lastEditSummary !== null && newEditSummary !== lastEditSummary ) { - // an external change has been made to the edit summary - this.setState( { - editSummary: newEditSummary - }); - } + // Latest edit summary. + const newEditSummary = getEditSummary(); - lastEditSummary = newEditSummary; - }); - } + if ( newEditSummary !== null && lastEditSummary !== null && newEditSummary !== lastEditSummary ) { + // An external change has been made to the edit summary. + this.setState( { + editSummary: newEditSummary, + } ); + } - /** - * Set revision edit summary via REST. - */ - setRevisionEditSummary( revisionId, editSummary ) { - let payload = { - post_id: revisionId, - key: 'ssl_alp_edit_summary', - value: editSummary - }; - - wp.apiRequest( { - path: '/ssl-alp/v1/update-revision-meta', - method: 'POST', - data: payload - } ).then( - ( data ) => { - return data; - }, - ( err ) => { - return err; - } - ); - } + lastEditSummary = newEditSummary; + }); + } - render() { - if ( !this.props.isPublished ) { - // don't render edit summary box on new posts - return null; + /** + * Set revision edit summary via REST. + */ + setRevisionEditSummary( revisionId, editSummary ) { + const payload = { + post_id: revisionId, + key: 'ssl_alp_edit_summary', + value: editSummary, + }; + + wp.apiRequest( { + path: '/ssl-alp/v1/update-revision-meta', + method: 'POST', + data: payload, + } ).then( + ( data ) => { + return data; + }, + ( err ) => { + return err; + } + ); } - return el( - PluginPostStatusInfo, - { - className: 'ssl-alp-edit-summary-panel' - }, - el( - TextControl, + render() { + const { isPublished, setEditSummary } = this.props; + + if ( ! isPublished ) { + // Don't render edit summary box on new posts. + return null; + } + + return el( + PluginPostStatusInfo, { - name: 'ssl_alp_edit_summary', - label: __( 'Edit summary', 'ssl-alp' ), - help: __( 'Briefly summarise your changes', 'ssl-alp' ), - spellCheck: true, - maxLength: 100, - value: this.state.editSummary, - onChange: ( value ) => { - // update text - this.setState( { - editSummary: value - }); - - // set message in datastore - this.props.setEditSummary( value ); + className: 'ssl-alp-edit-summary-panel' + }, + el( + TextControl, + { + name: 'ssl_alp_edit_summary', + label: __( 'Edit summary', 'ssl-alp' ), + help: __( 'Briefly summarise your changes', 'ssl-alp' ), + spellCheck: true, + maxLength: 100, + value: this.state.editSummary, + onChange: ( value ) => { + // Update text. + this.setState( { + editSummary: value + }); + + // Set message in datastore. + setEditSummary( value ); + } } - } - ) - ); + ) + ); + } } -} -/** - * Wrap a higher-order component around plugin to catch post update events. - */ -const EditSummaryPluginHOC = compose( [ - withSelect( ( select, { forceIsSaving } ) => { - const { - isCurrentPostPublished, - isPublishingPost, - isSavingPost, - isAutosavingPost, - getCurrentPostLastRevisionId, - } = select( 'core/editor' ); - - const { - getEditSummary - } = select( 'ssl-alp/edit-summary' ); - - return { - isPublished: isCurrentPostPublished(), - isPublishing: isPublishingPost(), - isSaving: forceIsSaving || isSavingPost(), - isAutosaving: isAutosavingPost(), - lastRevisionId: getCurrentPostLastRevisionId(), - getEditSummary: getEditSummary, - }; - } ), - withDispatch( ( dispatch ) => { - const { - setEditSummary, - resetEditSummary, - } = dispatch( 'ssl-alp/edit-summary' ); - - return { - setEditSummary: setEditSummary, - resetEditSummary: resetEditSummary, - }; - } ) -])( EditSummaryPlugin ); + /** + * Wrap a higher-order component around plugin to catch post update events. + */ + const EditSummaryPluginHOC = compose( [ + withSelect( ( select, { forceIsSaving } ) => { + const { + isCurrentPostPublished, + isPublishingPost, + isSavingPost, + isAutosavingPost, + getCurrentPostLastRevisionId, + } = select( 'core/editor' ); + + const { + getEditSummary + } = select( 'ssl-alp/edit-summary' ); + + return { + isPublished: isCurrentPostPublished(), + isPublishing: isPublishingPost(), + isSaving: forceIsSaving || isSavingPost(), + isAutosaving: isAutosavingPost(), + lastRevisionId: getCurrentPostLastRevisionId(), + getEditSummary: getEditSummary, + }; + } ), + withDispatch( ( dispatch ) => { + const { + setEditSummary, + resetEditSummary, + } = dispatch( 'ssl-alp/edit-summary' ); + + return { + setEditSummary, + resetEditSummary, + }; + } ) + ])( EditSummaryPlugin ); -/** - * Register sidebar plugin with block editor. - */ -registerPlugin( 'ssl-alp-edit-summary-plugin', { - render: EditSummaryPluginHOC -} ); + /** + * Register sidebar plugin with block editor. + */ + registerPlugin( 'ssl-alp-edit-summary-plugin', { + render: EditSummaryPluginHOC + } ); +} )( + window.wp +); From 5dc686b1d1ed0129245d94bf9a1dec11db1af281 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Wed, 11 Mar 2020 20:32:49 +0100 Subject: [PATCH 11/12] Remove check for hidden cross-references in functions that return references (it is now up to the theme to check if they are hidden) --- ssl-alp/includes/class-ssl-alp-references.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/ssl-alp/includes/class-ssl-alp-references.php b/ssl-alp/includes/class-ssl-alp-references.php index 149e1c1..1f64992 100644 --- a/ssl-alp/includes/class-ssl-alp-references.php +++ b/ssl-alp/includes/class-ssl-alp-references.php @@ -316,8 +316,7 @@ public function crossreferences_hidden( $post ) { * Get posts that are referenced by the specified post. * * @param int|WP_Post|null $post Post ID or post object. Defaults to global $post. - * @return array|null Referenced posts, or null if invalid post specified or - * if the post has been set to hide cross-references. + * @return array|null Referenced posts, or null if invalid post specified. * * @global $ssl_alp */ @@ -330,10 +329,6 @@ public function get_reference_to_posts( $post = null ) { return; } - if ( $this->crossreferences_hidden( $post ) ) { - return; - } - $terms = get_the_terms( $post, 'ssl-alp-crossreference' ); $posts = array(); @@ -383,8 +378,7 @@ public function get_reference_to_posts( $post = null ) { * Get posts that reference the specified post. * * @param int|WP_Post|null $post Post ID or post object. Defaults to global $post. - * @return array|null Referencing posts, or null if invalid post specified or - * if the post has been set to hide cross-references. + * @return array|null Referencing posts, or null if invalid post specified. * @global $wpdb * @global $ssl_alp; */ @@ -397,10 +391,6 @@ public function get_reference_from_posts( $post = null ) { return; } - if ( $this->crossreferences_hidden( $post ) ) { - return; - } - // Reference posts cache key. $cache_key = 'ssl-alp-reference-from_posts-' . $post->ID; From 47fe885fa5bca9a829cd80b7a3ddc7cc50c087f0 Mon Sep 17 00:00:00 2001 From: Sean Leavey Date: Wed, 11 Mar 2020 21:23:20 +0100 Subject: [PATCH 12/12] Bump version --- ssl-alp/README.txt | 14 +++++++++++++- ssl-alp/alp.php | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ssl-alp/README.txt b/ssl-alp/README.txt index 472d7a7..f62bf84 100644 --- a/ssl-alp/README.txt +++ b/ssl-alp/README.txt @@ -4,7 +4,7 @@ Tags: logbook, coauthor, revisions, references, latex, tex, mathematics, wiki Requires at least: 5.1.0 Tested up to: 5.3.2 Requires PHP: 7.0.0 -Stable tag: 0.18.0 +Stable tag: 0.19.0 License: GNU General Public License v3 or later License URI: LICENCE @@ -79,6 +79,18 @@ on the ALP website. == Changelog == += 0.19.0 = + - Removed the manual WP_Query SQL modifications for including coauthors in + search results, and replaced with a parameter based search. Due to this + functionality not being identical, other changes were made to restore + original functionality. This is quite a major change and needs field testing. + - Added ability to disable the display of cross-references on supported posts. + - Updated edit summary JavaScript (just refactoring, no functionality changes). + - Fixed bug where a link to network settings from the site settings page would + be displayed to network admins where ALP is active on the site only, not + network-wide. + - Updated automatic test configuration. + = 0.18.0 = - Fix bug with child block which appeared from 5.3. - Fix bug whereby media types detected by WordPress/PHP to have different media diff --git a/ssl-alp/alp.php b/ssl-alp/alp.php index 3a1f446..cc5ef97 100644 --- a/ssl-alp/alp.php +++ b/ssl-alp/alp.php @@ -3,7 +3,7 @@ * Plugin Name: Academic Labbook * Plugin URI: https://alp.attackllama.com/ * Description: Turn WordPress into a collaborative academic labbook. - * Version: 0.18.0 + * Version: 0.19.0 * Author: Sean Leavey * Author URI: https://attackllama.com/ * License: GPL3 @@ -21,7 +21,7 @@ * Current plugin version. */ -define( 'SSL_ALP_VERSION', '0.18.0' ); +define( 'SSL_ALP_VERSION', '0.19.0' ); /** * Plugin name and path