From b3fd2cd266504945441c4233e7e821a35d007963 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 12 Nov 2021 13:38:28 +0100 Subject: [PATCH 01/53] Ensure array return type This caused issues with the rename_column, as column_info expects an array --- lib/migrations/adapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/migrations/adapter.php b/lib/migrations/adapter.php index 0edb740c236..4c8e456710f 100644 --- a/lib/migrations/adapter.php +++ b/lib/migrations/adapter.php @@ -331,7 +331,7 @@ public function select_one( $query ) { return false; } - return $wpdb->last_result[0]; + return (array) $wpdb->last_result[0]; } return false; From 87c11c3f24ebbcce570a8a2fae0c838c266fd083 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 12 Nov 2021 13:40:44 +0100 Subject: [PATCH 02/53] Allow for deprecating columns If a replacement is given the ORM uses the replacement automatically. --- lib/model.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/lib/model.php b/lib/model.php index aa15ae2343c..1dfab75f98c 100644 --- a/lib/model.php +++ b/lib/model.php @@ -94,6 +94,13 @@ class Model implements JsonSerializable { */ protected $float_columns = []; + /** + * Which columns are deprecated and optionally: what are their replacements. + * + * @var array + */ + protected $deprecated_columns = []; + /** * Hacks around the Model to provide WordPress prefix to tables. * @@ -504,6 +511,7 @@ public function set_orm( $orm ) { * @return mixed The value of the property */ public function __get( $property ) { + $property = $this->handleDeprecation( $property ); $value = $this->orm->get( $property ); if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { @@ -528,6 +536,7 @@ public function __get( $property ) { * @return void */ public function __set( $property, $value ) { + $property = $this->handleDeprecation( $property ); if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { $value = ( $value ) ? '1' : '0'; } @@ -589,6 +598,7 @@ public function __isset( $property ) { * @return string The value of a property. */ public function get( $property ) { + $property = $this->handleDeprecation( $property ); return $this->orm->get( $property ); } @@ -601,6 +611,7 @@ public function get( $property ) { * @return static Current object. */ public function set( $property, $value = null ) { + $property = $this->handleDeprecation( $property ); $this->orm->set( $property, $value ); return $this; @@ -615,6 +626,7 @@ public function set( $property, $value = null ) { * @return static Current object. */ public function set_expr( $property, $value = null ) { + $property = $this->handleDeprecation( $property ); $this->orm->set_expr( $property, $value ); return $this; @@ -628,6 +640,7 @@ public function set_expr( $property, $value = null ) { * @return bool True when field is changed. */ public function is_dirty( $property ) { + $property = $this->handleDeprecation( $property ); return $this->orm->is_dirty( $property ); } @@ -718,4 +731,29 @@ public static function __callStatic( $method, $arguments ) { return \call_user_func_array( [ $model, $method ], $arguments ); } + + /** + * Checks if a property is deprecated and handles notifications and replacement. + * + * @param string $property The property to check for deprecation. + * + * @return string The deprecated property name. Or its replacement if available. + */ + protected function handleDeprecation( $property ) { + if ( ! array_key_exists( $property, $this->deprecated_columns ) ) { + return $property; + } + $replacement = $this->deprecated_columns[ $property ]; + + if ( ! empty( $replacement ) ) { + // There is no _deprecated_property. This matches our usecase best. + _deprecated_argument( __FUNCTION__, '17.7', 'Use the \"' . $replacement . '\" property instead of \"' . $property . '\" ' ); + + return $replacement; + } + // There is no _deprecated_property. This matches our usecase best. + _deprecated_argument( __FUNCTION__, '17.7', 'The \"' . $property . '\" property will be removed in a future version' ); + + return $property; + } } From 2eef6e2745c0db148caef022dbedd4cc84ba0f4c Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Mon, 15 Nov 2021 15:29:59 +0100 Subject: [PATCH 03/53] Add method for creating a noindex query for indexables --- src/builders/indexable-author-builder.php | 34 ++-- src/builders/indexable-builder.php | 44 ++++- .../indexable-date-archive-builder.php | 15 +- src/builders/indexable-home-page-builder.php | 24 ++- src/builders/indexable-post-builder.php | 72 ++------ .../indexable-post-type-archive-builder.php | 30 ++-- .../indexable-system-page-builder.php | 13 +- src/builders/indexable-term-builder.php | 26 ++- ...1254_ReplaceHasPublicPostsOnIndexables.php | 78 +++++++++ src/helpers/author-archive-helper.php | 33 ++++ src/helpers/post-helper.php | 57 ++++--- src/helpers/robots-helper.php | 33 ++++ .../watchers/indexable-post-watcher.php | 62 +++---- src/models/indexable.php | 12 +- src/repositories/indexable-repository.php | 161 +++++++++++++----- .../indexables/indexable-builder-versions.php | 12 +- .../builders/indexable-post-builder-test.php | 24 +-- .../indexable-post-builder-double.php | 4 +- .../indexable-post-watcher-double.php | 4 +- .../watchers/indexable-post-watcher-test.php | 8 +- 20 files changed, 494 insertions(+), 252 deletions(-) create mode 100644 src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index 614be73577c..6343f1e49de 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -87,22 +87,27 @@ public function build( $user_id, Indexable $indexable ) { $indexable->is_robots_noarchive = null; $indexable->is_robots_noimageindex = null; $indexable->is_robots_nosnippet = null; - $indexable->is_public = ( $indexable->is_robots_noindex ) ? false : null; - $indexable->has_public_posts = $this->author_archive->author_has_public_posts( $user_id ); $indexable->blog_id = \get_current_blog_id(); $this->reset_social_images( $indexable ); $this->handle_social_images( $indexable ); - $timestamps = $this->get_object_timestamps( $user_id ); - $indexable->object_published_at = $timestamps->published_at; - $indexable->object_last_modified = $timestamps->last_modified; + $indexable = $this->set_aggregate_values( $indexable ); $indexable->version = $this->version; return $indexable; } + public function set_aggregate_values( Indexable $indexable ) { + $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id ); + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = max($indexable->object_last_modified, $aggregates->most_recent_last_modified); + $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + + return $indexable; + } + /** * Retrieves the meta data for this indexable. * @@ -168,24 +173,31 @@ protected function find_alternative_image( Indexable $indexable ) { } /** - * Returns the timestamps for a given author. + * Returns public post aggregates for a given author. * - * @param int $author_id The author ID. + * @param int $author_id The author ID. * - * @return object An object with last_modified and published_at timestamps. + * @return object An object with the number of public posts, most recent last modified and first published at timestamps. */ - protected function get_object_timestamps( $author_id ) { + protected function get_public_post_archive_aggregates( $author_id ) { $post_statuses = $this->post_helper->get_public_post_statuses(); + $post_types = $this->author_archive->get_author_archive_post_types(); + // TODO DIEDE: Protected pages krijgen _geen_ noindex. en staan gewoon in het author archive. moeten die meegeteld worden? + // Private werkt wel zoals verwacht. $sql = " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") AND p.post_password = '' AND p.post_author = %d + AND p.post_type IN (" . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ") "; - $replacements = \array_merge( $post_statuses, [ $author_id ] ); + $replacements = \array_merge( $post_statuses, [ $author_id ], $post_types ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- We are using wpdb prepare. return $this->wpdb->get_row( $this->wpdb->prepare( $sql, $replacements ) ); diff --git a/src/builders/indexable-builder.php b/src/builders/indexable-builder.php index 9dd97e8707a..5e540dd13a5 100644 --- a/src/builders/indexable-builder.php +++ b/src/builders/indexable-builder.php @@ -219,6 +219,7 @@ public function build_for_post_type_archive( $post_type, $indexable = false ) { 'object_type' => 'post-type-archive', 'object_sub_type' => $post_type, ]; + return $this->build( $indexable, $defaults ); } @@ -235,6 +236,7 @@ public function build_for_system_page( $page_type, $indexable = false ) { 'object_type' => 'system-page', 'object_sub_type' => $page_type, ]; + return $this->build( $indexable, $defaults ); } @@ -372,8 +374,7 @@ public function build( $indexable, $defaults = null ) { } return $this->save_indexable( $indexable, $indexable_before ); - } - catch ( Source_Exception $exception ) { + } catch ( Source_Exception $exception ) { /** * The current indexable could not be indexed. Create a placeholder indexable, so we can * skip this indexable in future indexing runs. @@ -396,4 +397,43 @@ public function build( $indexable, $defaults = null ) { return $this->save_indexable( $indexable, $indexable_before ); } } + + /** + * Recalculates indexable aggregates. + * + * @param Indexable $indexable The Indexable to (re)build. + * + * @return Indexable The resulting Indexable. + */ + public function recalculate_aggregates( Indexable $indexable ) { + // Backup the previous Indexable, if there was one. + $indexable_before = $this->deep_copy_indexable( $indexable ); + + switch ( $indexable->object_type ) { + case 'system-page': + case 'date-archive': + case 'post': + // Nothing to recalculate. + break; + + case 'user': + $indexable = $this->author_builder->set_aggregate_values( $indexable ); + break; + + case 'term': + $indexable = $this->term_builder->set_aggregate_values( $indexable ); + break; + + case 'home-page': + $indexable = $this->home_page_builder->set_aggregate_values( $indexable ); + break; + + case 'post-type-archive': + $indexable = $this->post_type_archive_builder->set_aggregate_values( $indexable ); + break; + } + + return $this->save_indexable( $indexable, $indexable_before ); + } } + diff --git a/src/builders/indexable-date-archive-builder.php b/src/builders/indexable-date-archive-builder.php index 944a03e263e..95418096a95 100644 --- a/src/builders/indexable-date-archive-builder.php +++ b/src/builders/indexable-date-archive-builder.php @@ -51,14 +51,13 @@ public function __construct( * @return Indexable The extended indexable. */ public function build( $indexable ) { - $indexable->object_type = 'date-archive'; - $indexable->title = $this->options->get( 'title-archive-wpseo' ); - $indexable->description = $this->options->get( 'metadesc-archive-wpseo' ); - $indexable->is_robots_noindex = $this->options->get( 'noindex-archive-wpseo' ); - $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); - $indexable->blog_id = \get_current_blog_id(); - $indexable->permalink = null; - $indexable->version = $this->version; + $indexable->object_type = 'date-archive'; + $indexable->title = $this->options->get( 'title-archive-wpseo' ); + $indexable->description = $this->options->get( 'metadesc-archive-wpseo' ); + $indexable->is_robots_noindex = $this->options->get( 'noindex-archive-wpseo' ); + $indexable->blog_id = \get_current_blog_id(); + $indexable->permalink = null; + $indexable->version = $this->version; return $indexable; } diff --git a/src/builders/indexable-home-page-builder.php b/src/builders/indexable-home-page-builder.php index 8cc5d49afe2..97768c2cd5b 100644 --- a/src/builders/indexable-home-page-builder.php +++ b/src/builders/indexable-home-page-builder.php @@ -114,25 +114,35 @@ public function build( $indexable ) { $this->set_open_graph_image_meta_data( $indexable ); } - $timestamps = $this->get_object_timestamps(); - $indexable->object_published_at = $timestamps->published_at; - $indexable->object_last_modified = $timestamps->last_modified; + $indexable = $this->set_aggregate_values( $indexable ); $indexable->version = $this->version; return $indexable; } + public function set_aggregate_values( Indexable $indexable ) { + $aggregates = $this->get_public_post_archive_aggregates(); + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + + return $indexable; + } + /** - * Returns the timestamps for the homepage. + * Returns public post aggregates for the homepage. * - * @return object An object with last_modified and published_at timestamps. + * @return object An object with the number of posts, most recent last modified and first published at timestamps. */ - protected function get_object_timestamps() { + protected function get_public_post_archive_aggregates() { $post_statuses = $this->post_helper->get_public_post_statuses(); $sql = " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") AND p.post_password = '' diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 71417b8ed06..4548177b3ee 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -142,12 +142,11 @@ public function build( $post_id, $indexable ) { $indexable->author_id = $post->post_author; $indexable->post_parent = $post->post_parent; - $indexable->number_of_pages = $this->get_number_of_pages_for_post( $post ); - $indexable->post_status = $post->post_status; - $indexable->is_protected = $post->post_password !== ''; - $indexable->is_public = $this->is_public( $indexable ); - $indexable->has_public_posts = $this->has_public_posts( $indexable ); - $indexable->blog_id = \get_current_blog_id(); + $indexable->number_of_pages = $this->get_number_of_pages_for_post( $post ); + $indexable->post_status = $post->post_status; + $indexable->is_protected = $post->post_password !== ''; + + $indexable->blog_id = \get_current_blog_id(); $indexable->schema_page_type = $this->get_meta_value( $post_id, 'schema_page_type' ); $indexable->schema_article_type = $this->get_meta_value( $post_id, 'schema_article_type' ); @@ -155,6 +154,8 @@ public function build( $post_id, $indexable ) { $indexable->object_last_modified = $post->post_modified_gmt; $indexable->object_published_at = $post->post_date_gmt; + $indexable->number_of_public_posts = 0; + $indexable->version = $this->version; return $indexable; @@ -181,31 +182,10 @@ protected function get_permalink( $post_type, $post_id ) { * * @param Indexable $indexable The indexable. * - * @return bool|null Whether or not the post type is public. Null if no override is set. + * @return bool|null Whether the post type is public. Null if no override is set. */ - protected function is_public( $indexable ) { - if ( $indexable->is_protected === true ) { - return false; - } - - if ( $indexable->is_robots_noindex === true ) { - return false; - } - - // Attachments behave differently than the other post types, since they inherit from their parent. - if ( $indexable->object_sub_type === 'attachment' ) { - return $this->is_public_attachment( $indexable ); - } - - if ( ! \in_array( $indexable->post_status, $this->post_helper->get_public_post_statuses(), true ) ) { - return false; - } - - if ( $indexable->is_robots_noindex === false ) { - return true; - } - - return null; + protected function is_accessible_post( $indexable ) { + return is_post_type_viewable($indexable->object_sub_type) && is_post_status_viewable($indexable->post_status); } /** @@ -225,38 +205,6 @@ protected function is_public_attachment( $indexable ) { return null; } - /** - * Determines the value of has_public_posts. - * - * @param Indexable $indexable The indexable. - * - * @return bool|null Whether the attachment has a public parent, can be true, false and null. Null when it is not an attachment. - */ - protected function has_public_posts( $indexable ) { - // Only attachments (and authors) have this value. - if ( $indexable->object_sub_type !== 'attachment' ) { - return null; - } - - // The attachment should have a post parent. - if ( empty( $indexable->post_parent ) ) { - return false; - } - - // The attachment should inherit the post status. - if ( $indexable->post_status !== 'inherit' ) { - return false; - } - - // The post parent should be public. - $post_parent_indexable = $this->indexable_repository->find_by_id_and_type( $indexable->post_parent, 'post' ); - if ( $post_parent_indexable !== false ) { - return $post_parent_indexable->is_public; - } - - return false; - } - /** * Converts the meta robots noindex value to the indexable value. * diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 3c0f09059d8..2523405ae55 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -80,14 +80,21 @@ public function build( $post_type, Indexable $indexable ) { $indexable->description = $this->options->get( 'metadesc-ptarchive-' . $post_type ); $indexable->breadcrumb_title = $this->get_breadcrumb_title( $post_type ); $indexable->permalink = \get_post_type_archive_link( $post_type ); - $indexable->is_robots_noindex = $this->options->get( 'noindex-ptarchive-' . $post_type ); - $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); + $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); $indexable->blog_id = \get_current_blog_id(); - $indexable->version = $this->version; - $timestamps = $this->get_object_timestamps( $post_type ); - $indexable->object_published_at = $timestamps->published_at; - $indexable->object_last_modified = $timestamps->last_modified; + $indexable = $this->set_aggregate_values( $indexable ); + + $indexable->version = $this->version; + + return $indexable; + } + + public function set_aggregate_values( Indexable $indexable ) { + $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_sub_type ); + $indexable->object_published_at = $aggregates->published_at; + $indexable->object_last_modified = $aggregates->last_modified; + $indexable->number_of_public_posts = $aggregates->number_of_public_posts; return $indexable; } @@ -124,17 +131,20 @@ private function get_breadcrumb_title( $post_type ) { } /** - * Returns the timestamps for a given post type. + * Returns public post aggregates for a given post type. * * @param string $post_type The post type. * - * @return object An object with last_modified and published_at timestamps. + * @return object An object with the number of posts, most recent last modified and first published at timestamps. */ - protected function get_object_timestamps( $post_type ) { + protected function get_public_post_archive_aggregates( $post_type ) { $post_statuses = $this->post_helper->get_public_post_statuses(); $sql = " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") AND p.post_password = '' diff --git a/src/builders/indexable-system-page-builder.php b/src/builders/indexable-system-page-builder.php index a592ed414c0..c38d6e94fc3 100644 --- a/src/builders/indexable-system-page-builder.php +++ b/src/builders/indexable-system-page-builder.php @@ -20,7 +20,7 @@ class Indexable_System_Page_Builder { */ const OPTION_MAPPING = [ 'search-result' => [ - 'title' => 'title-search-wpseo', + 'title' => 'title-search-wpseo', ], '404' => [ 'title' => 'title-404-wpseo', @@ -65,11 +65,12 @@ public function __construct( * @return Indexable The extended indexable. */ public function build( $object_sub_type, Indexable $indexable ) { - $indexable->object_type = 'system-page'; - $indexable->object_sub_type = $object_sub_type; - $indexable->title = $this->options->get( static::OPTION_MAPPING[ $object_sub_type ]['title'] ); - $indexable->is_robots_noindex = true; - $indexable->blog_id = \get_current_blog_id(); + $indexable->object_type = 'system-page'; + $indexable->object_sub_type = $object_sub_type; + $indexable->title = $this->options->get( static::OPTION_MAPPING[ $object_sub_type ]['title'] ); + $indexable->is_robots_noindex = true; + $indexable->number_of_public_posts = 0; + $indexable->blog_id = \get_current_blog_id(); if ( \array_key_exists( 'breadcrumb_title', static::OPTION_MAPPING[ $object_sub_type ] ) ) { $indexable->breadcrumb_title = $this->options->get( static::OPTION_MAPPING[ $object_sub_type ]['breadcrumb_title'] ); diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 4f2ec43c217..59e5fa79e1c 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -8,7 +8,6 @@ use Yoast\WP\SEO\Helpers\Post_Helper; use Yoast\WP\SEO\Helpers\Taxonomy_Helper; use Yoast\WP\SEO\Models\Indexable; -use Yoast\WP\SEO\Repositories\Indexable_Repository; use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions; /** @@ -110,7 +109,6 @@ public function build( $term_id, $indexable ) { ); $indexable->is_robots_noindex = $this->get_noindex_value( $this->get_meta_value( 'wpseo_noindex', $term_meta ) ); - $indexable->is_public = ( $indexable->is_robots_noindex === null ) ? null : ! $indexable->is_robots_noindex; $this->reset_social_images( $indexable ); @@ -132,15 +130,22 @@ public function build( $term_id, $indexable ) { $indexable->is_robots_noimageindex = null; $indexable->is_robots_nosnippet = null; - $timestamps = $this->get_object_timestamps( $term_id, $term->taxonomy ); - $indexable->object_published_at = $timestamps->published_at; - $indexable->object_last_modified = $timestamps->last_modified; + $indexable = $this->set_aggregate_values( $indexable ); $indexable->version = $this->version; return $indexable; } + public function set_aggregate_values( Indexable $indexable ) { + $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id, $indexable->object_sub_type ); + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + + return $indexable; + } + /** * Converts the meta noindex value to the indexable value. * @@ -241,18 +246,21 @@ protected function find_alternative_image( Indexable $indexable ) { } /** - * Returns the timestamps for a given term. + * Returns public post aggregates for a given term. * * @param int $term_id The term ID. * @param string $taxonomy The taxonomy. * - * @return object An object with last_modified and published_at timestamps. + * @return object An object with the number of posts, most recent last modified and first published at timestamps. */ - protected function get_object_timestamps( $term_id, $taxonomy ) { + protected function get_public_post_archive_aggregates( $term_id, $taxonomy ) { $post_statuses = $this->post_helper->get_public_post_statuses(); $sql = " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p INNER JOIN {$this->wpdb->term_relationships} AS term_rel ON term_rel.object_id = p.ID diff --git a/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php b/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php new file mode 100644 index 00000000000..182c9f05368 --- /dev/null +++ b/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php @@ -0,0 +1,78 @@ +get_table_name(); + $this->rename_column( + $table_name, + 'has_public_posts', + 'number_of_public_posts' + ); + + $this->change_column( + $table_name, + 'number_of_public_posts', + 'integer' + ); + + } + + /** + * Migration down. + * + * @return void + */ + public function down() { + $table_name = $this->get_table_name(); + $this->change_column( + $table_name, + 'number_of_public_posts', + 'boolean', + [ + 'null' => true, + 'default' => null, + ] + ); + + $this->rename_column( + $table_name, + 'number_of_public_posts', + 'has_public_posts' + ); + } + + /** + * Retrieves the table name to use. + * + * @return string The table name to use. + */ + protected function get_table_name() { + return Model::get_table_name( 'Indexable' ); + } +} diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index 1f6bffed1f8..fe0f3e04b0f 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -3,12 +3,31 @@ namespace Yoast\WP\SEO\Helpers; use Yoast\WP\Lib\Model; +use Yoast\WP\SEO\Repositories\Indexable_Repository; /** * A helper object for author archives. */ class Author_Archive_Helper { + /** + * @var Indexable_Repository + */ + private $indexable_repository; + + /** + * Sets the indexable repository. Done to avoid circular dependencies. + * + * @required + * + * @param Indexable_Repository $indexable_repository + * + * @return void + */ + public function setIndexableRepository( Indexable_Repository $indexable_repository ) { + $this->indexable_repository = $indexable_repository; + } + /** * Gets the array of post types that are shown on an author's archive. * @@ -23,6 +42,20 @@ public function get_author_archive_post_types() { return \apply_filters( 'wpseo_author_archive_post_types', [ 'post' ] ); } + /** + * Gets the number of public posts an author has. + * Depends on the post indexables to be fully indexed. + * + * @param int $author_id The id of the author to get the number of public posts for. + * + * @return int The number of public posts an author has. + */ + public function get_number_of_public_posts( $author_id ) { + $author_indexable = $this->indexable_repository->find_by_id_and_type( $author_id, 'user' ); + + return $author_indexable->number_of_public_posts; + } + /** * Returns whether the author has at least one public post. * diff --git a/src/helpers/post-helper.php b/src/helpers/post-helper.php index b3f3e7bb2e3..a9731f0c308 100644 --- a/src/helpers/post-helper.php +++ b/src/helpers/post-helper.php @@ -3,6 +3,7 @@ namespace Yoast\WP\SEO\Helpers; use WP_Post; +use Yoast\WP\SEO\Builders\Indexable_Builder; use Yoast\WP\SEO\Repositories\Indexable_Repository; /** @@ -24,6 +25,13 @@ class Post_Helper { */ private $repository; + /** + * A builder that creates and updates indexables. + * + * @var Indexable_Builder + */ + private $indexable_builder; + /** * Post_Helper constructor. * @@ -35,6 +43,19 @@ public function __construct( String_Helper $string ) { $this->string = $string; } + /** + * Sets the indexable builder. Done to avoid circular dependencies. + * + * @param Indexable_Builder $indexable_builder A builder that creates and updates indexables. + * + * @required + * + * @return void + */ + public function setIndexableBuilder( Indexable_Builder $indexable_builder ) { + $this->indexable_builder = $indexable_builder; + } + /** * Sets the indexable repository. Done to avoid circular dependencies. * @@ -115,7 +136,7 @@ public function get_post( $post_id ) { } /** - * Updates the has_public_posts field on attachments for a post_parent. + * Updates the number_of_public_posts field on attachments for a post_parent. * * An attachment is represented by their post parent when: * - The attachment has a post parent. @@ -123,37 +144,19 @@ public function get_post( $post_id ) { * * @codeCoverageIgnore It relies too much on dependencies. * - * @param int $post_parent Post ID. - * @param int $has_public_posts Whether the parent is public. + * @param int $post_parent Post ID. + * @param int $number_of_public_posts The number of public posts * * @return bool Whether the update was successful. */ - public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) { - $query = $this->repository->query() - ->select( 'id' ) - ->where( 'object_type', 'post' ) - ->where( 'object_sub_type', 'attachment' ) - ->where( 'post_status', 'inherit' ) - ->where( 'post_parent', $post_parent ); - - if ( $has_public_posts !== null ) { - $query->where_raw( '( has_public_posts IS NULL OR has_public_posts <> %s )', [ $has_public_posts ] ); - } - else { - $query->where_not_null( 'has_public_posts' ); - } - $results = $query->find_array(); - - if ( empty( $results ) ) { - return true; - } + public function update_number_of_public_posts_on_attachments( $post_parent, $number_of_public_posts ) { + // TODO DIEDE no alternative? builders zijn geen publieke API. + _deprecated_function( __METHOD__, '17.7' ); + $indexable = $this->repository->find_by_id_and_type( $post_parent, 'post' ); - $updated = $this->repository->query() - ->set( 'has_public_posts', $has_public_posts ) - ->where_id_in( \wp_list_pluck( $results, 'id' ) ) - ->update_many(); + $this->indexable_builder->recalculate_aggregates( $indexable ); - return $updated !== false; + return true; } /** diff --git a/src/helpers/robots-helper.php b/src/helpers/robots-helper.php index 94a8bd3e578..a23151e31fb 100644 --- a/src/helpers/robots-helper.php +++ b/src/helpers/robots-helper.php @@ -7,6 +7,18 @@ */ class Robots_Helper { + /** + * @var Options_Helper + */ + private $options_helper; + + /** + * @param Options_Helper $options_helper The indexable version manager. + */ + public function __construct( Options_Helper $options_helper ) { + $this->options_helper = $options_helper; + } + /** * Sets the robots index to noindex. * @@ -24,4 +36,25 @@ public function set_robots_no_index( $robots ) { return $robots; } + + /** + * Gets the site default noindex value for an object type. + * + * @param string $object_type The object type. + * @param string $object_sub_type The object subtype. Used for post_types. + * + * @return bool Whether the site default is set to noindex for the requested object type. + */ + public function get_default_noindex_for_object( $object_type, $object_sub_type = "" ) { + switch ( $object_type ) { + case 'post': + return (bool) $this->options_helper->get( 'noindex-' . $object_sub_type ); + case 'user': + return (bool) $this->options_helper->get( 'noindex-author-wpseo' ); + case 'term': + return (bool) $this->options_helper->get( 'noindex-tax-' . $object_sub_type ); + default: + return false; + } + } } diff --git a/src/integrations/watchers/indexable-post-watcher.php b/src/integrations/watchers/indexable-post-watcher.php index ca3847b243e..487592f675a 100644 --- a/src/integrations/watchers/indexable-post-watcher.php +++ b/src/integrations/watchers/indexable-post-watcher.php @@ -142,13 +142,10 @@ public function delete_indexable( $post_id ) { return; } - $this->update_relations( $this->post->get_post( $post_id ) ); - - $this->update_has_public_posts( $indexable ); - $this->hierarchy_repository->clear_ancestors( $indexable->id ); $this->link_builder->delete( $indexable ); $indexable->delete(); + $this->update_relations( $this->post->get_post( $post_id ) ); } /** @@ -165,10 +162,8 @@ public function updated_indexable( $updated_indexable, $old_indexable ) { $post = $this->post->get_post( $updated_indexable->object_id ); - $this->update_relations( $post ); - $this->update_has_public_posts( $updated_indexable ); - $updated_indexable->save(); + $this->update_relations( $post ); } /** @@ -201,25 +196,6 @@ public function build_indexable( $post_id ) { } } - /** - * Updates the has_public_posts when the post indexable is built. - * - * @param Indexable $indexable The indexable to check. - */ - protected function update_has_public_posts( $indexable ) { - // Update the author indexable's has public posts value. - try { - $author_indexable = $this->repository->find_by_id_and_type( $indexable->author_id, 'user' ); - $author_indexable->has_public_posts = $this->author_archive->author_has_public_posts( $author_indexable->object_id ); - $author_indexable->save(); - } catch ( Exception $exception ) { - $this->logger->log( LogLevel::ERROR, $exception->getMessage() ); - } - - // Update possible attachment's has public posts value. - $this->post->update_has_public_posts_on_attachments( $indexable->object_id, $indexable->is_public ); - } - /** * Updates the relations on post save or post status change. * @@ -227,10 +203,10 @@ protected function update_has_public_posts( $indexable ) { */ protected function update_relations( $post ) { $related_indexables = $this->get_related_indexables( $post ); - - foreach ( $related_indexables as $indexable ) { - $indexable->object_last_modified = max( $indexable->object_last_modified, $post->post_modified_gmt ); - $indexable->save(); + $now = current_time( 'mysql' ); + foreach ( $related_indexables as $related_indexable ) { + $related_indexable->object_last_modified = $now; + $this->builder->recalculate_aggregates( $related_indexable ); } } @@ -251,12 +227,27 @@ protected function get_related_indexables( $post ) { $related_indexables[] = $this->repository->find_by_id_and_type( $post->post_author, 'user', false ); $related_indexables[] = $this->repository->find_for_post_type_archive( $post->post_type, false ); $related_indexables[] = $this->repository->find_for_home_page( false ); + $related_indexables = \array_merge( + $related_indexables, + $this->get_related_term_indexables( $post->ID ) + ); - $taxonomies = \get_post_taxonomies( $post->ID ); + return \array_filter( $related_indexables ); + } + + /** + * Retrieves the related term indexables for a given post. + * + * @param int $post_id The id of the post to get the related term indexables for. + * + * @return Indexable[] The term indexables related to the post. + */ + protected function get_related_term_indexables( $post_id ) { + $taxonomies = \get_post_taxonomies( $post_id ); $taxonomies = \array_filter( $taxonomies, 'is_taxonomy_viewable' ); $term_ids = []; foreach ( $taxonomies as $taxonomy ) { - $terms = \get_the_terms( $post->ID, $taxonomy ); + $terms = \get_the_terms( $post_id, $taxonomy ); if ( empty( $terms ) || \is_wp_error( $terms ) ) { continue; @@ -264,14 +255,11 @@ protected function get_related_indexables( $post ) { $term_ids = \array_merge( $term_ids, \wp_list_pluck( $terms, 'term_id' ) ); } - $related_indexables = \array_merge( - $related_indexables, - $this->repository->find_by_multiple_ids_and_type( $term_ids, 'term', false ) - ); - return \array_filter( $related_indexables ); + return $this->repository->find_by_multiple_ids_and_type( $term_ids, 'term', false ); } + /** * Tests if the site is multisite and switched. * diff --git a/src/models/indexable.php b/src/models/indexable.php index 9f807e34379..865c99c3374 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -59,10 +59,9 @@ * * @property int $prominent_words_version * - * @property bool $is_public * @property bool $is_protected * @property string $post_status - * @property bool $has_public_posts + * @property int $number_of_public_posts * * @property int $blog_id * @@ -109,9 +108,8 @@ class Indexable extends Model { 'is_robots_noimageindex', 'is_robots_nosnippet', 'is_cornerstone', - 'is_public', 'is_protected', - 'has_public_posts', + 'calculated_no_index', ]; /** @@ -133,6 +131,12 @@ class Indexable extends Model { 'blog_id', 'estimated_reading_time_minutes', 'version', + 'number_of_public_posts', + ]; + + protected $deprecated_columns = [ + 'has_public_posts' => 'number_of_public_posts', + 'is_public', ]; /** diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 83221f1db6b..c5694fd7f38 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -9,6 +9,7 @@ use Yoast\WP\SEO\Builders\Indexable_Builder; use Yoast\WP\SEO\Helpers\Current_Page_Helper; use Yoast\WP\SEO\Helpers\Indexable_Helper; +use Yoast\WP\SEO\Helpers\Robots_Helper; use Yoast\WP\SEO\Loggers\Logger; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Services\Indexables\Indexable_Version_Manager; @@ -37,7 +38,7 @@ class Indexable_Repository { * * @var Current_Page_Helper */ - protected $current_page; + protected $current_page_helper; /** * The logger object. @@ -67,30 +68,41 @@ class Indexable_Repository { */ protected $version_manager; + /** + * A helper class for robots values. + * + * @var Robots_Helper + */ + private $robots_helper; + + /** * Returns the instance of this class constructed through the ORM Wrapper. * * @param Indexable_Builder $builder The indexable builder. - * @param Current_Page_Helper $current_page The current post helper. + * @param Current_Page_Helper $current_page_helper The current post helper. * @param Logger $logger The logger. * @param Indexable_Hierarchy_Repository $hierarchy_repository The hierarchy repository. * @param wpdb $wpdb The WordPress database instance. * @param Indexable_Version_Manager $version_manager The indexable version manager. + * @param Robots_Helper $robots_helper A helper class for robots values. */ public function __construct( Indexable_Builder $builder, - Current_Page_Helper $current_page, + Current_Page_Helper $current_page_helper, Logger $logger, Indexable_Hierarchy_Repository $hierarchy_repository, wpdb $wpdb, - Indexable_Version_Manager $version_manager + Indexable_Version_Manager $version_manager, + Robots_Helper $robots_helper, ) { $this->builder = $builder; - $this->current_page = $current_page; + $this->current_page_helper = $current_page_helper; $this->logger = $logger; $this->hierarchy_repository = $hierarchy_repository; $this->wpdb = $wpdb; $this->version_manager = $version_manager; + $this->robots_helper = $robots_helper; } /** @@ -113,31 +125,31 @@ public function for_current_page() { $indexable = false; switch ( true ) { - case $this->current_page->is_simple_page(): - $indexable = $this->find_by_id_and_type( $this->current_page->get_simple_page_id(), 'post' ); + case $this->current_page_helper->is_simple_page(): + $indexable = $this->find_by_id_and_type( $this->current_page_helper->get_simple_page_id(), 'post' ); break; - case $this->current_page->is_home_static_page(): - $indexable = $this->find_by_id_and_type( $this->current_page->get_front_page_id(), 'post' ); + case $this->current_page_helper->is_home_static_page(): + $indexable = $this->find_by_id_and_type( $this->current_page_helper->get_front_page_id(), 'post' ); break; - case $this->current_page->is_home_posts_page(): + case $this->current_page_helper->is_home_posts_page(): $indexable = $this->find_for_home_page(); break; - case $this->current_page->is_term_archive(): - $indexable = $this->find_by_id_and_type( $this->current_page->get_term_id(), 'term' ); + case $this->current_page_helper->is_term_archive(): + $indexable = $this->find_by_id_and_type( $this->current_page_helper->get_term_id(), 'term' ); break; - case $this->current_page->is_date_archive(): + case $this->current_page_helper->is_date_archive(): $indexable = $this->find_for_date_archive(); break; - case $this->current_page->is_search_result(): + case $this->current_page_helper->is_search_result(): $indexable = $this->find_for_system_page( 'search-result' ); break; - case $this->current_page->is_post_type_archive(): - $indexable = $this->find_for_post_type_archive( $this->current_page->get_queried_post_type() ); + case $this->current_page_helper->is_post_type_archive(): + $indexable = $this->find_for_post_type_archive( $this->current_page_helper->get_queried_post_type() ); break; - case $this->current_page->is_author_archive(): - $indexable = $this->find_by_id_and_type( $this->current_page->get_author_id(), 'user' ); + case $this->current_page_helper->is_author_archive(): + $indexable = $this->find_by_id_and_type( $this->current_page_helper->get_author_id(), 'user' ); break; - case $this->current_page->is_404(): + case $this->current_page_helper->is_404(): $indexable = $this->find_for_system_page( '404' ); break; } @@ -155,6 +167,43 @@ public function for_current_page() { return $indexable; } + /** + * Gets a query that finds all indexables of a type+subtype that match the noindex value + * while taking site defaults into account. + * + * @param string $object_type The indexable object type. + * @param string|null $object_sub_type The indexable object subtype. + * @param bool $noindex The noindex value of the posts to find. + * + * @return ORM The query object. + */ + public function noindex_query( $object_type, $object_sub_type = null, $noindex = true ) { + $query = $this->query() + ->where( 'object_type', $object_type ) + ->where( 'object_sub_type', $object_sub_type ); + + $default_noindex = $this->robots_helper->get_default_noindex_for_object( $object_type, $object_sub_type ); + + + $condition = 'is_robots_noindex = %d '; + // If the requested noindex value matches the default, include NULL values in the result. + if ( $default_noindex === $noindex ) { + $condition .= 'OR is_robot_noindex IS NULL'; + } + + // Let the number of posts in an archive determine the noindex value. + if ( in_array( $object_type, [ 'post-type-archive', 'term', 'user', 'home-page' ], true ) ) { + if ( $noindex === true ) { + $condition .= ' OR number_of_public_posts = 0'; + } + else { + $condition = '(' . $condition . ') AND number_public_posts > 0'; + } + } + + return $query->where_raw( $condition, (int) $noindex ); + } + /** * Retrieves an indexable by its permalink. * @@ -167,9 +216,9 @@ public function find_by_permalink( $permalink ) { // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() - ->where( 'permalink_hash', $permalink_hash ) - ->where( 'permalink', $permalink ) - ->find_one(); + ->where( 'permalink_hash', $permalink_hash ) + ->where( 'permalink', $permalink ) + ->find_one(); } /** @@ -282,9 +331,9 @@ public function find_for_post_type_archive( $post_type, $auto_create = true ) { * @var Indexable $indexable */ $indexable = $this->query() - ->where( 'object_type', 'post-type-archive' ) - ->where( 'object_sub_type', $post_type ) - ->find_one(); + ->where( 'object_type', 'post-type-archive' ) + ->where( 'object_sub_type', $post_type ) + ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); @@ -308,9 +357,9 @@ public function find_for_system_page( $object_sub_type, $auto_create = true ) { * @var Indexable $indexable */ $indexable = $this->query() - ->where( 'object_type', 'system-page' ) - ->where( 'object_sub_type', $object_sub_type ) - ->find_one(); + ->where( 'object_type', 'system-page' ) + ->where( 'object_sub_type', $object_sub_type ) + ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); @@ -319,6 +368,30 @@ public function find_for_system_page( $object_sub_type, $auto_create = true ) { return $this->upgrade_indexable( $indexable ); } + + /** + * Retrieves a list of attachment indexables that belong to a parent post. + * + * @param int $parent_post_id The id of the parent post. + * + * @return Indexable[] Instances of the attachment indexables. + */ + public function find_for_post_attachments( $parent_post_id ) { + /** + * Indexable instance. + * + * @var Indexable[] $indexables + */ + $indexables = $this->query() + ->where( 'object_type', 'post' ) + ->where( 'object_sub_type', 'attachment' ) + ->where( 'post_status', 'inherit' ) + ->where( 'post_parent', $parent_post_id ) + ->find_many(); + + return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); + } + /** * Retrieves an indexable by its ID and type. * @@ -330,9 +403,9 @@ public function find_for_system_page( $object_sub_type, $auto_create = true ) { */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() - ->where( 'object_id', $object_id ) - ->where( 'object_type', $object_type ) - ->find_one(); + ->where( 'object_id', $object_id ) + ->where( 'object_type', $object_type ) + ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); @@ -364,9 +437,9 @@ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_ * @var Indexable[] $indexables */ $indexables = $this->query() - ->where_in( 'object_id', $object_ids ) - ->where( 'object_type', $object_type ) - ->find_many(); + ->where_in( 'object_id', $object_ids ) + ->where( 'object_type', $object_type ) + ->find_many(); if ( $auto_create ) { $indexables_available = []; @@ -433,9 +506,9 @@ public function get_ancestors( Indexable $indexable ) { } $indexables = $this->query() - ->where_in( 'id', $indexable_ids ) - ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) - ->find_many(); + ->where_in( 'id', $indexable_ids ) + ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) + ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } @@ -450,13 +523,14 @@ public function get_ancestors( Indexable $indexable ) { */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() - ->where( 'post_parent', $post_parent ) - ->where( 'object_type', 'post' ) - ->where( 'post_status', 'publish' ); + ->where( 'post_parent', $post_parent ) + ->where( 'object_type', 'post' ) + ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } + return $query->find_many(); } @@ -470,9 +544,9 @@ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() - ->set( 'incoming_link_count', $count ) - ->where( 'id', $indexable_id ) - ->update_many(); + ->set( 'incoming_link_count', $count ) + ->where( 'id', $indexable_id ) + ->update_many(); } /** @@ -505,6 +579,7 @@ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } + return $indexable; } diff --git a/src/values/indexables/indexable-builder-versions.php b/src/values/indexables/indexable-builder-versions.php index 4f327dd88da..03cce961f59 100644 --- a/src/values/indexables/indexable-builder-versions.php +++ b/src/values/indexables/indexable-builder-versions.php @@ -18,13 +18,13 @@ class Indexable_Builder_Versions { */ protected $indexable_builder_versions_by_type = [ 'date-archive' => self::DEFAULT_INDEXABLE_BUILDER_VERSION, - 'general' => self::DEFAULT_INDEXABLE_BUILDER_VERSION, + 'general' => 2, 'home-page' => 2, - 'post' => 2, - 'post-type-archive' => 2, - 'term' => 2, - 'user' => 2, - 'system-page' => self::DEFAULT_INDEXABLE_BUILDER_VERSION, + 'post' => 3, + 'post-type-archive' => 3, + 'term' => 3, + 'user' => 3, + 'system-page' => 2, ]; /** diff --git a/tests/unit/builders/indexable-post-builder-test.php b/tests/unit/builders/indexable-post-builder-test.php index 0a49c4f317b..8d07a7e6597 100644 --- a/tests/unit/builders/indexable-post-builder-test.php +++ b/tests/unit/builders/indexable-post-builder-test.php @@ -672,43 +672,43 @@ public function test_get_keyword_score_no_keyword() { /** * Tests is_public for when the post is protected. * - * @covers ::is_public + * @covers ::is_accessible_post */ public function test_is_public_post_protected() { $this->indexable->is_protected = true; - $this->assertFalse( $this->instance->is_public( $this->indexable ) ); + $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); } /** * Tests is_public for when the post is noindex. * - * @covers ::is_public + * @covers ::is_accessible_post */ public function test_is_public_post_noindex() { $this->indexable->is_protected = false; $this->indexable->is_robots_noindex = true; - $this->assertFalse( $this->instance->is_public( $this->indexable ) ); + $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); } /** * Tests is_public for when the post is an attachment. * - * @covers ::is_public + * @covers ::is_accessible_post */ public function test_is_public_post_is_attachment() { $this->indexable->is_protected = false; $this->indexable->is_robots_noindex = false; $this->indexable->object_sub_type = 'attachment'; - $this->assertFalse( $this->instance->is_public( $this->indexable ) ); + $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); } /** * Tests is_public for when the post status is not public. * - * @covers ::is_public + * @covers ::is_accessible_post */ public function test_is_public_post_status_is_not_public() { $this->indexable->is_protected = false; @@ -718,13 +718,13 @@ public function test_is_public_post_status_is_not_public() { $this->post->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - $this->assertFalse( $this->instance->is_public( $this->indexable ) ); + $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); } /** * Tests is_public for when the post noindex is false. * - * @covers ::is_public + * @covers ::is_accessible_post */ public function test_is_public_post_noindex_false() { $this->indexable->is_protected = false; @@ -734,13 +734,13 @@ public function test_is_public_post_noindex_false() { $this->post->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - $this->assertTrue( $this->instance->is_public( $this->indexable ) ); + $this->assertTrue( $this->instance->is_accessible_post( $this->indexable ) ); } /** * Tests is_public for when the post noindex is null. * - * @covers ::is_public + * @covers ::is_accessible_post */ public function test_is_public_post_noindex_null() { $this->indexable->is_protected = false; @@ -750,7 +750,7 @@ public function test_is_public_post_noindex_null() { $this->post->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - $this->assertNull( $this->instance->is_public( $this->indexable ) ); + $this->assertNull( $this->instance->is_accessible_post( $this->indexable ) ); } /** diff --git a/tests/unit/doubles/builders/indexable-post-builder-double.php b/tests/unit/doubles/builders/indexable-post-builder-double.php index 752b6161d75..09f8debf842 100644 --- a/tests/unit/doubles/builders/indexable-post-builder-double.php +++ b/tests/unit/doubles/builders/indexable-post-builder-double.php @@ -18,8 +18,8 @@ class Indexable_Post_Builder_Double extends Indexable_Post_Builder { * * @return bool|null Whether or not the post type is public. Null if no override is set. */ - public function is_public( $indexable ) { - return parent::is_public( $indexable ); + public function is_accessible_post( $indexable ) { + return parent::is_accessible_post( $indexable ); } /** diff --git a/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php b/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php index 8b957d989bf..404a9036cb2 100644 --- a/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php +++ b/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php @@ -16,8 +16,8 @@ class Indexable_Post_Watcher_Double extends Indexable_Post_Watcher { * * @param Indexable $indexable The indexable to check. */ - public function update_has_public_posts( $indexable ) { - parent::update_has_public_posts( $indexable ); + public function update_number_of_public_posts( $indexable ) { + parent::update_number_of_public_posts( $indexable ); } /** diff --git a/tests/unit/integrations/watchers/indexable-post-watcher-test.php b/tests/unit/integrations/watchers/indexable-post-watcher-test.php index ffba4019fd4..93165e8f73f 100644 --- a/tests/unit/integrations/watchers/indexable-post-watcher-test.php +++ b/tests/unit/integrations/watchers/indexable-post-watcher-test.php @@ -349,7 +349,7 @@ public function test_updated_indexable_non_post() { /** * Tests that update_has_public_posts updates the author archive too. * - * @covers ::update_has_public_posts + * @covers ::update_number_of_public_posts */ public function test_update_has_public_posts_with_post() { $post_indexable = Mockery::mock(); @@ -376,7 +376,7 @@ public function test_update_has_public_posts_with_post() { $this->post->expects( 'update_has_public_posts_on_attachments' )->once()->with( 33, null )->andReturnTrue(); - $this->instance->update_has_public_posts( $post_indexable ); + $this->instance->update_number_of_public_posts( $post_indexable ); $this->assertTrue( $author_indexable->has_public_posts ); } @@ -384,7 +384,7 @@ public function test_update_has_public_posts_with_post() { /** * Tests that update_has_public_posts updates the author archive . * - * @covers ::update_has_public_posts + * @covers ::update_number_of_public_posts */ public function test_update_has_public_posts_with_post_throwing_exceptions() { $post_indexable = Mockery::mock(); @@ -404,7 +404,7 @@ public function test_update_has_public_posts_with_post_throwing_exceptions() { ->andReturnTrue(); $this->logger->expects( 'log' )->once()->with( 'error', 'an error' ); - $this->instance->update_has_public_posts( $post_indexable ); + $this->instance->update_number_of_public_posts( $post_indexable ); } /** From b5bc8db940f20761bb791f780597f0ecb16d02b0 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Mon, 15 Nov 2021 17:01:33 +0100 Subject: [PATCH 04/53] Rename is_public to is_publicly_viewable and number_of_public_posts to number_of_publicly_viewable_posts --- src/builders/indexable-author-builder.php | 10 +++--- src/builders/indexable-home-page-builder.php | 11 ++++--- src/builders/indexable-post-builder.php | 31 ++----------------- .../indexable-post-type-archive-builder.php | 13 +++++--- .../indexable-system-page-builder.php | 13 ++++---- src/builders/indexable-term-builder.php | 19 ++++++------ src/models/indexable.php | 11 ++++--- 7 files changed, 46 insertions(+), 62 deletions(-) diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index 6343f1e49de..72bed3654ce 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -100,10 +100,10 @@ public function build( $user_id, Indexable $indexable ) { } public function set_aggregate_values( Indexable $indexable ) { - $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id ); - $indexable->object_published_at = $aggregates->first_published_at; - $indexable->object_last_modified = max($indexable->object_last_modified, $aggregates->most_recent_last_modified); - $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id ); + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = max( $indexable->object_last_modified, $aggregates->most_recent_last_modified ); + $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; } @@ -181,7 +181,7 @@ protected function find_alternative_image( Indexable $indexable ) { */ protected function get_public_post_archive_aggregates( $author_id ) { $post_statuses = $this->post_helper->get_public_post_statuses(); - $post_types = $this->author_archive->get_author_archive_post_types(); + $post_types = $this->author_archive->get_author_archive_post_types(); // TODO DIEDE: Protected pages krijgen _geen_ noindex. en staan gewoon in het author archive. moeten die meegeteld worden? // Private werkt wel zoals verwacht. diff --git a/src/builders/indexable-home-page-builder.php b/src/builders/indexable-home-page-builder.php index 97768c2cd5b..e74878ca5a4 100644 --- a/src/builders/indexable-home-page-builder.php +++ b/src/builders/indexable-home-page-builder.php @@ -96,7 +96,8 @@ public function build( $indexable ) { $indexable->description = \get_bloginfo( 'description' ); } - $indexable->is_robots_noindex = \get_option( 'blog_public' ) === '0'; + $indexable->is_robots_noindex = \get_option( 'blog_public' ) === '0'; + $indexable->is_publicly_viewable = true; $indexable->open_graph_title = $this->options->get( 'open_graph_frontpage_title' ); $indexable->open_graph_image = $this->options->get( 'open_graph_frontpage_image' ); @@ -122,10 +123,10 @@ public function build( $indexable ) { } public function set_aggregate_values( Indexable $indexable ) { - $aggregates = $this->get_public_post_archive_aggregates(); - $indexable->object_published_at = $aggregates->first_published_at; - $indexable->object_last_modified = $aggregates->most_recent_last_modified; - $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + $aggregates = $this->get_public_post_archive_aggregates(); + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; } diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 4548177b3ee..dc56458d7f5 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -154,7 +154,8 @@ public function build( $post_id, $indexable ) { $indexable->object_last_modified = $post->post_modified_gmt; $indexable->object_published_at = $post->post_date_gmt; - $indexable->number_of_public_posts = 0; + $indexable->number_of_publicly_viewable_posts = 0; + $indexable->is_publicly_viewable = \is_post_publicly_viewable( $post ); $indexable->version = $this->version; @@ -177,34 +178,6 @@ protected function get_permalink( $post_type, $post_id ) { return \wp_get_attachment_url( $post_id ); } - /** - * Determines the value of is_public. - * - * @param Indexable $indexable The indexable. - * - * @return bool|null Whether the post type is public. Null if no override is set. - */ - protected function is_accessible_post( $indexable ) { - return is_post_type_viewable($indexable->object_sub_type) && is_post_status_viewable($indexable->post_status); - } - - /** - * Determines the value of is_public for attachments. - * - * @param Indexable $indexable The indexable. - * - * @return bool|null False when it has no parent. Null when it has a parent. - */ - protected function is_public_attachment( $indexable ) { - // If the attachment has no parent, it should not be public. - if ( empty( $indexable->post_parent ) ) { - return false; - } - - // If the attachment has a parent, the is_public should be NULL. - return null; - } - /** * Converts the meta robots noindex value to the indexable value. * diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 2523405ae55..689b17d2f69 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -83,6 +83,11 @@ public function build( $post_type, Indexable $indexable ) { $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); $indexable->blog_id = \get_current_blog_id(); + $post_type_object = get_post_type_object( $post_type ); + if ( $post_type_object !== null ) { + $indexable->is_publicly_viewable = $post_type_object->has_archive && $post_type_object->rewrite !== false; + } + $indexable = $this->set_aggregate_values( $indexable ); $indexable->version = $this->version; @@ -91,10 +96,10 @@ public function build( $post_type, Indexable $indexable ) { } public function set_aggregate_values( Indexable $indexable ) { - $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_sub_type ); - $indexable->object_published_at = $aggregates->published_at; - $indexable->object_last_modified = $aggregates->last_modified; - $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_sub_type ); + $indexable->object_published_at = $aggregates->published_at; + $indexable->object_last_modified = $aggregates->last_modified; + $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; } diff --git a/src/builders/indexable-system-page-builder.php b/src/builders/indexable-system-page-builder.php index c38d6e94fc3..c14e1a94d1e 100644 --- a/src/builders/indexable-system-page-builder.php +++ b/src/builders/indexable-system-page-builder.php @@ -65,12 +65,13 @@ public function __construct( * @return Indexable The extended indexable. */ public function build( $object_sub_type, Indexable $indexable ) { - $indexable->object_type = 'system-page'; - $indexable->object_sub_type = $object_sub_type; - $indexable->title = $this->options->get( static::OPTION_MAPPING[ $object_sub_type ]['title'] ); - $indexable->is_robots_noindex = true; - $indexable->number_of_public_posts = 0; - $indexable->blog_id = \get_current_blog_id(); + $indexable->object_type = 'system-page'; + $indexable->object_sub_type = $object_sub_type; + $indexable->title = $this->options->get( static::OPTION_MAPPING[ $object_sub_type ]['title'] ); + $indexable->is_robots_noindex = true; + $indexable->number_of_publicly_viewable_posts = 0; + $indexable->is_publicly_viewable = true; + $indexable->blog_id = \get_current_blog_id(); if ( \array_key_exists( 'breadcrumb_title', static::OPTION_MAPPING[ $object_sub_type ] ) ) { $indexable->breadcrumb_title = $this->options->get( static::OPTION_MAPPING[ $object_sub_type ]['breadcrumb_title'] ); diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 59e5fa79e1c..fc295d35c41 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -97,11 +97,12 @@ public function build( $term_id, $indexable ) { $term_meta = $this->taxonomy_helper->get_term_meta( $term ); - $indexable->object_id = $term_id; - $indexable->object_type = 'term'; - $indexable->object_sub_type = $term->taxonomy; - $indexable->permalink = $term_link; - $indexable->blog_id = \get_current_blog_id(); + $indexable->object_id = $term_id; + $indexable->object_type = 'term'; + $indexable->object_sub_type = $term->taxonomy; + $indexable->permalink = $term_link; + $indexable->blog_id = \get_current_blog_id(); + $indexable->is_publicly_viewable = is_taxonomy_viewable( $term->taxonomy ); $indexable->primary_focus_keyword_score = $this->get_keyword_score( $this->get_meta_value( 'wpseo_focuskw', $term_meta ), @@ -138,10 +139,10 @@ public function build( $term_id, $indexable ) { } public function set_aggregate_values( Indexable $indexable ) { - $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id, $indexable->object_sub_type ); - $indexable->object_published_at = $aggregates->first_published_at; - $indexable->object_last_modified = $aggregates->most_recent_last_modified; - $indexable->number_of_public_posts = $aggregates->number_of_public_posts; + $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id, $indexable->object_sub_type ); + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; } diff --git a/src/models/indexable.php b/src/models/indexable.php index 865c99c3374..1db8d3faea6 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -61,7 +61,9 @@ * * @property bool $is_protected * @property string $post_status - * @property int $number_of_public_posts + * + * @property int $number_of_publicly_viewable_posts + * @property bool $is_publicly_viewable * * @property int $blog_id * @@ -108,6 +110,7 @@ class Indexable extends Model { 'is_robots_noimageindex', 'is_robots_nosnippet', 'is_cornerstone', + 'is_publicly_viewable', 'is_protected', 'calculated_no_index', ]; @@ -131,12 +134,12 @@ class Indexable extends Model { 'blog_id', 'estimated_reading_time_minutes', 'version', - 'number_of_public_posts', + 'number_of_publicly_viewable_posts', ]; protected $deprecated_columns = [ - 'has_public_posts' => 'number_of_public_posts', - 'is_public', + 'has_public_posts' => 'number_of_publicly_viewable_posts', + 'is_public' => 'is_publicly_viewable', ]; /** From 6c015bae822e392d3f93fe8ac12d5d071d23a061 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 08:45:35 +0100 Subject: [PATCH 05/53] Use PHP 5.6 syntax --- src/repositories/indexable-repository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index c5694fd7f38..61e212d6aa9 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -94,7 +94,7 @@ public function __construct( Indexable_Hierarchy_Repository $hierarchy_repository, wpdb $wpdb, Indexable_Version_Manager $version_manager, - Robots_Helper $robots_helper, + Robots_Helper $robots_helper ) { $this->builder = $builder; $this->current_page_helper = $current_page_helper; From 3d969684e439aacf659693f02b1ba9f2330a95d0 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 09:43:20 +0100 Subject: [PATCH 06/53] Add migrations --- ...1254_ReplaceHasPublicPostsOnIndexables.php | 8 +-- ...1108133106_ReplaceIsPublicOnIndexables.php | 70 +++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php diff --git a/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php b/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php index 182c9f05368..e7b85bc865b 100644 --- a/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php +++ b/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php @@ -32,12 +32,12 @@ public function up() { $this->rename_column( $table_name, 'has_public_posts', - 'number_of_public_posts' + 'number_of_publicly_viewable_posts' ); $this->change_column( $table_name, - 'number_of_public_posts', + 'number_of_publicly_viewable_posts', 'integer' ); @@ -52,7 +52,7 @@ public function down() { $table_name = $this->get_table_name(); $this->change_column( $table_name, - 'number_of_public_posts', + 'number_of_publicly_viewable_posts', 'boolean', [ 'null' => true, @@ -62,7 +62,7 @@ public function down() { $this->rename_column( $table_name, - 'number_of_public_posts', + 'number_of_publicly_viewable_posts', 'has_public_posts' ); } diff --git a/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php b/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php new file mode 100644 index 00000000000..5aa7edc0ba0 --- /dev/null +++ b/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php @@ -0,0 +1,70 @@ +get_table_name(); + + $this->rename_column( + $table_name, + 'is_public', + 'is_publicly_viewable' + ); + + } + + /** + * Migration down. + * Requires a reindex of indexables. + * + * @return void + */ + public function down() { + $table_name = $this->get_table_name(); + + $this->rename_column( + $table_name, + 'calculated_no_index', + 'is_public' + ); + } + + + /** + * Retrieves the table name to use. + * + * @return string The table name to use. + */ + protected function get_table_name() { + return Model::get_table_name( 'Indexable' ); + } +} + + + From acdf97a6ad3b99ece006bf09122435e19ba24d28 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 10:39:24 +0100 Subject: [PATCH 07/53] Deprecate is_public instead of immediately replacing it is_public is replaced by a different approach in querying data and by is_publicly_viewable. If we would immediately replace is_public with is_publicly_viewable, that would be a BC break. --- lib/model.php | 15 +++-- src/builders/indexable-author-builder.php | 1 + .../indexable-date-archive-builder.php | 16 +++-- src/builders/indexable-post-builder.php | 67 +++++++++++++++++-- .../indexable-post-type-archive-builder.php | 1 + src/builders/indexable-term-builder.php | 1 + ...1108133106_ReplaceIsPublicOnIndexables.php | 18 ++--- .../watchers/indexable-post-watcher.php | 11 +-- src/models/indexable.php | 14 +++- 9 files changed, 108 insertions(+), 36 deletions(-) diff --git a/lib/model.php b/lib/model.php index 1dfab75f98c..e7490906cd7 100644 --- a/lib/model.php +++ b/lib/model.php @@ -95,7 +95,7 @@ class Model implements JsonSerializable { protected $float_columns = []; /** - * Which columns are deprecated and optionally: what are their replacements. + * Which columns are deprecated. * * @var array */ @@ -599,6 +599,7 @@ public function __isset( $property ) { */ public function get( $property ) { $property = $this->handleDeprecation( $property ); + return $this->orm->get( $property ); } @@ -743,13 +744,17 @@ protected function handleDeprecation( $property ) { if ( ! array_key_exists( $property, $this->deprecated_columns ) ) { return $property; } - $replacement = $this->deprecated_columns[ $property ]; + $deprecation = $this->deprecated_columns[ $property ]; - if ( ! empty( $replacement ) ) { + if ( ! empty( $deprecation['replacement'] ) ) { // There is no _deprecated_property. This matches our usecase best. - _deprecated_argument( __FUNCTION__, '17.7', 'Use the \"' . $replacement . '\" property instead of \"' . $property . '\" ' ); + _deprecated_argument( __FUNCTION__, '17.7', 'Use the \"' . $deprecation['replacement'] . '\" property instead of \"' . $property . '\" ' ); + + if ( $deprecation['automatically_use_replacement'] === true ) { + return $deprecation['replacement']; + } - return $replacement; + return $property; } // There is no _deprecated_property. This matches our usecase best. _deprecated_argument( __FUNCTION__, '17.7', 'The \"' . $property . '\" property will be removed in a future version' ); diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index 72bed3654ce..4df16107863 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -87,6 +87,7 @@ public function build( $user_id, Indexable $indexable ) { $indexable->is_robots_noarchive = null; $indexable->is_robots_noimageindex = null; $indexable->is_robots_nosnippet = null; + $indexable->is_public = ( $indexable->is_robots_noindex ) ? false : null; $indexable->blog_id = \get_current_blog_id(); $this->reset_social_images( $indexable ); diff --git a/src/builders/indexable-date-archive-builder.php b/src/builders/indexable-date-archive-builder.php index 95418096a95..57c7193d68a 100644 --- a/src/builders/indexable-date-archive-builder.php +++ b/src/builders/indexable-date-archive-builder.php @@ -51,13 +51,15 @@ public function __construct( * @return Indexable The extended indexable. */ public function build( $indexable ) { - $indexable->object_type = 'date-archive'; - $indexable->title = $this->options->get( 'title-archive-wpseo' ); - $indexable->description = $this->options->get( 'metadesc-archive-wpseo' ); - $indexable->is_robots_noindex = $this->options->get( 'noindex-archive-wpseo' ); - $indexable->blog_id = \get_current_blog_id(); - $indexable->permalink = null; - $indexable->version = $this->version; + $indexable->object_type = 'date-archive'; + $indexable->title = $this->options->get( 'title-archive-wpseo' ); + $indexable->description = $this->options->get( 'metadesc-archive-wpseo' ); + $indexable->is_robots_noindex = $this->options->get( 'noindex-archive-wpseo' ); + $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); + $indexable->is_publicly_viewable = (bool) $this->options->get( 'disable-date' ); + $indexable->blog_id = \get_current_blog_id(); + $indexable->permalink = null; + $indexable->version = $this->version; return $indexable; } diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index dc56458d7f5..61c926d4b75 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -142,11 +142,13 @@ public function build( $post_id, $indexable ) { $indexable->author_id = $post->post_author; $indexable->post_parent = $post->post_parent; - $indexable->number_of_pages = $this->get_number_of_pages_for_post( $post ); - $indexable->post_status = $post->post_status; - $indexable->is_protected = $post->post_password !== ''; - - $indexable->blog_id = \get_current_blog_id(); + $indexable->number_of_pages = $this->get_number_of_pages_for_post( $post ); + $indexable->post_status = $post->post_status; + $indexable->is_protected = $post->post_password !== ''; + $indexable->is_public = $this->is_public( $indexable ); + $indexable->is_publicly_viewable = \is_post_publicly_viewable( $post ); + $indexable->number_of_publicly_viewable_posts = 0; + $indexable->blog_id = \get_current_blog_id(); $indexable->schema_page_type = $this->get_meta_value( $post_id, 'schema_page_type' ); $indexable->schema_article_type = $this->get_meta_value( $post_id, 'schema_article_type' ); @@ -154,8 +156,6 @@ public function build( $post_id, $indexable ) { $indexable->object_last_modified = $post->post_modified_gmt; $indexable->object_published_at = $post->post_date_gmt; - $indexable->number_of_publicly_viewable_posts = 0; - $indexable->is_publicly_viewable = \is_post_publicly_viewable( $post ); $indexable->version = $this->version; @@ -178,6 +178,59 @@ protected function get_permalink( $post_type, $post_id ) { return \wp_get_attachment_url( $post_id ); } + /** + * Determines the value of is_public. + * + * @param Indexable $indexable The indexable. + * + * @return bool|null Whether the post type is public. Null if no override is set. + * + * @deprecated 17.7 + */ + protected function is_public( $indexable ) { + if ( $indexable->is_protected === true ) { + return false; + } + + if ( $indexable->is_robots_noindex === true ) { + return false; + } + + // Attachments behave differently than the other post types, since they inherit from their parent. + if ( $indexable->object_sub_type === 'attachment' ) { + return $this->is_public_attachment( $indexable ); + } + + if ( ! \in_array( $indexable->post_status, $this->post_helper->get_public_post_statuses(), true ) ) { + return false; + } + + if ( $indexable->is_robots_noindex === false ) { + return true; + } + + return null; + } + + /** + * Determines the value of is_public for attachments. + * + * @param Indexable $indexable The indexable. + * + * @return bool|null False when it has no parent. Null when it has a parent. + * + * @deprecated 17.7 + */ + protected function is_public_attachment( $indexable ) { + // If the attachment has no parent, it should not be public. + if ( empty( $indexable->post_parent ) ) { + return false; + } + + // If the attachment has a parent, the is_public should be NULL. + return null; + } + /** * Converts the meta robots noindex value to the indexable value. * diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 689b17d2f69..a11f0ab7218 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -81,6 +81,7 @@ public function build( $post_type, Indexable $indexable ) { $indexable->breadcrumb_title = $this->get_breadcrumb_title( $post_type ); $indexable->permalink = \get_post_type_archive_link( $post_type ); $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); + $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); $indexable->blog_id = \get_current_blog_id(); $post_type_object = get_post_type_object( $post_type ); diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index fc295d35c41..8bab2e25c00 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -110,6 +110,7 @@ public function build( $term_id, $indexable ) { ); $indexable->is_robots_noindex = $this->get_noindex_value( $this->get_meta_value( 'wpseo_noindex', $term_meta ) ); + $indexable->is_public = ( $indexable->is_robots_noindex === null ) ? null : ! $indexable->is_robots_noindex; $this->reset_social_images( $indexable ); diff --git a/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php b/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php index 5aa7edc0ba0..ee358d3d063 100644 --- a/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php +++ b/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php @@ -30,13 +30,14 @@ class ReplaceIsPublicOnIndexables extends Migration { */ public function up() { $table_name = $this->get_table_name(); - - $this->rename_column( + $this->add_column( $table_name, - 'is_public', - 'is_publicly_viewable' - ); - + 'is_publicly_viewable', + 'boolean', + [ + 'null' => true, + 'default' => null, + ] ); } /** @@ -48,10 +49,9 @@ public function up() { public function down() { $table_name = $this->get_table_name(); - $this->rename_column( + $this->remove_column( $table_name, - 'calculated_no_index', - 'is_public' + 'is_publicly_viewable', ); } diff --git a/src/integrations/watchers/indexable-post-watcher.php b/src/integrations/watchers/indexable-post-watcher.php index 487592f675a..cb098f1bcb4 100644 --- a/src/integrations/watchers/indexable-post-watcher.php +++ b/src/integrations/watchers/indexable-post-watcher.php @@ -119,7 +119,7 @@ public function __construct( */ public function register_hooks() { \add_action( 'wp_insert_post', [ $this, 'build_indexable' ], \PHP_INT_MAX ); - \add_action( 'delete_post', [ $this, 'delete_indexable' ] ); + \add_action( 'delete_post', [ $this, 'delete_indexable' ], 10, 2 ); \add_action( 'wpseo_save_indexable', [ $this, 'updated_indexable' ], \PHP_INT_MAX, 2 ); \add_action( 'edit_attachment', [ $this, 'build_indexable' ], \PHP_INT_MAX ); @@ -130,11 +130,12 @@ public function register_hooks() { /** * Deletes the meta when a post is deleted. * - * @param int $post_id Post ID. + * @param int $post_id Post ID. + * @param \WP_Post $post The to be deleted post. * * @return void */ - public function delete_indexable( $post_id ) { + public function delete_indexable( $post_id, $post ) { $indexable = $this->repository->find_by_id_and_type( $post_id, 'post', false ); // Only interested in post indexables. @@ -145,7 +146,7 @@ public function delete_indexable( $post_id ) { $this->hierarchy_repository->clear_ancestors( $indexable->id ); $this->link_builder->delete( $indexable ); $indexable->delete(); - $this->update_relations( $this->post->get_post( $post_id ) ); + $this->update_relations( $post ); } /** @@ -203,7 +204,7 @@ public function build_indexable( $post_id ) { */ protected function update_relations( $post ) { $related_indexables = $this->get_related_indexables( $post ); - $now = current_time( 'mysql' ); + $now = current_time( 'mysql' ); foreach ( $related_indexables as $related_indexable ) { $related_indexable->object_last_modified = $now; $this->builder->recalculate_aggregates( $related_indexable ); diff --git a/src/models/indexable.php b/src/models/indexable.php index 1db8d3faea6..fee687da9f2 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -110,9 +110,9 @@ class Indexable extends Model { 'is_robots_noimageindex', 'is_robots_nosnippet', 'is_cornerstone', + 'is_public', 'is_publicly_viewable', 'is_protected', - 'calculated_no_index', ]; /** @@ -138,8 +138,16 @@ class Indexable extends Model { ]; protected $deprecated_columns = [ - 'has_public_posts' => 'number_of_publicly_viewable_posts', - 'is_public' => 'is_publicly_viewable', + 'has_public_posts' => [ + 'since' => '17.7', + 'replacement' => 'number_of_publicly_viewable_posts', + 'automatically_use_replacement' => true, + ], + 'is_public' => [ + 'since' => '17.7', + 'replacement' => 'is_publicly_viewable', + 'automatically_use_replacement' => false, + ], ]; /** From e18c9ac7439dbf4e112e5770c8c3c08a11694544 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 10:42:20 +0100 Subject: [PATCH 08/53] Keep the method name the same and deprecate --- src/helpers/post-helper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpers/post-helper.php b/src/helpers/post-helper.php index a9731f0c308..daf5c997be1 100644 --- a/src/helpers/post-helper.php +++ b/src/helpers/post-helper.php @@ -148,14 +148,14 @@ public function get_post( $post_id ) { * @param int $number_of_public_posts The number of public posts * * @return bool Whether the update was successful. + * + * @deprecated 17.7 */ - public function update_number_of_public_posts_on_attachments( $post_parent, $number_of_public_posts ) { - // TODO DIEDE no alternative? builders zijn geen publieke API. + public function update_has_public_posts_on_attachments( $post_parent, $number_of_public_posts ) { _deprecated_function( __METHOD__, '17.7' ); $indexable = $this->repository->find_by_id_and_type( $post_parent, 'post' ); $this->indexable_builder->recalculate_aggregates( $indexable ); - return true; } From 58d6a71aa5cb87f3b93a06b6db0c3d2a79877db2 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 11:01:02 +0100 Subject: [PATCH 09/53] Access properties that were renamed earlier --- src/builders/indexable-post-type-archive-builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index a11f0ab7218..1ca8bd87bcf 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -98,8 +98,8 @@ public function build( $post_type, Indexable $indexable ) { public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_sub_type ); - $indexable->object_published_at = $aggregates->published_at; - $indexable->object_last_modified = $aggregates->last_modified; + $indexable->object_published_at = $aggregates->first_published_at; + $indexable->object_last_modified = $aggregates->most_recent_last_modified; $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; From b7cfa0438bba14080f692fc6cddd9d2750cd8569 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 11:01:47 +0100 Subject: [PATCH 10/53] Make the homepage get a number_of_publicly_viewable_posts and is_publicly_viewable value --- src/values/indexables/indexable-builder-versions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/values/indexables/indexable-builder-versions.php b/src/values/indexables/indexable-builder-versions.php index 03cce961f59..45c5d40dffb 100644 --- a/src/values/indexables/indexable-builder-versions.php +++ b/src/values/indexables/indexable-builder-versions.php @@ -19,7 +19,7 @@ class Indexable_Builder_Versions { protected $indexable_builder_versions_by_type = [ 'date-archive' => self::DEFAULT_INDEXABLE_BUILDER_VERSION, 'general' => 2, - 'home-page' => 2, + 'home-page' => 3, 'post' => 3, 'post-type-archive' => 3, 'term' => 3, From c2d121e5f8c7e5eff881315ae40aa53b80fa1e05 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 11:17:23 +0100 Subject: [PATCH 11/53] Use new column name --- src/repositories/indexable-repository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 61e212d6aa9..6137462050a 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -194,10 +194,10 @@ public function noindex_query( $object_type, $object_sub_type = null, $noindex = // Let the number of posts in an archive determine the noindex value. if ( in_array( $object_type, [ 'post-type-archive', 'term', 'user', 'home-page' ], true ) ) { if ( $noindex === true ) { - $condition .= ' OR number_of_public_posts = 0'; + $condition .= ' OR number_of_publicly_viewable_posts = 0'; } else { - $condition = '(' . $condition . ') AND number_public_posts > 0'; + $condition = '(' . $condition . ') AND number_of_publicly_viewable_posts > 0'; } } From 38b552db0c80d39c7e123ea124cc8f9517ef48f2 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 11:18:00 +0100 Subject: [PATCH 12/53] Add deprecation tags --- src/helpers/author-archive-helper.php | 21 +++++++-------------- src/helpers/post-helper.php | 6 +++--- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index fe0f3e04b0f..3a5f4276098 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -42,28 +42,17 @@ public function get_author_archive_post_types() { return \apply_filters( 'wpseo_author_archive_post_types', [ 'post' ] ); } - /** - * Gets the number of public posts an author has. - * Depends on the post indexables to be fully indexed. - * - * @param int $author_id The id of the author to get the number of public posts for. - * - * @return int The number of public posts an author has. - */ - public function get_number_of_public_posts( $author_id ) { - $author_indexable = $this->indexable_repository->find_by_id_and_type( $author_id, 'user' ); - - return $author_indexable->number_of_public_posts; - } - /** * Returns whether the author has at least one public post. * * @param int $author_id The author ID. * * @return bool|null Whether the author has at least one public post. + * + * @deprecated 17.7 */ public function author_has_public_posts( $author_id ) { + _deprecated_function( __METHOD__, '17.7',Indexable_Repository::class.'::noindex_query'); // First check if the author has at least one public post. $has_public_post = $this->author_has_a_public_post( $author_id ); if ( $has_public_post ) { @@ -87,6 +76,8 @@ public function author_has_public_posts( $author_id ) { * @param int $author_id The author ID. * * @return bool Whether the author has at least one public post. + * + * @deprecated 17.7 */ protected function author_has_a_public_post( $author_id ) { $cache_key = 'author_has_a_public_post_' . $author_id; @@ -118,6 +109,8 @@ protected function author_has_a_public_post( $author_id ) { * @param int $author_id The author ID. * * @return bool Whether the author has at least one post with the is public null. + * + * @deprecated 17.7 */ protected function author_has_a_post_with_is_public_null( $author_id ) { $cache_key = 'author_has_a_post_with_is_public_null_' . $author_id; diff --git a/src/helpers/post-helper.php b/src/helpers/post-helper.php index daf5c997be1..8419851c38b 100644 --- a/src/helpers/post-helper.php +++ b/src/helpers/post-helper.php @@ -136,7 +136,7 @@ public function get_post( $post_id ) { } /** - * Updates the number_of_public_posts field on attachments for a post_parent. + * Updates the number_of_publicly_viewable_posts field on attachments for a post_parent. * * An attachment is represented by their post parent when: * - The attachment has a post parent. @@ -145,13 +145,13 @@ public function get_post( $post_id ) { * @codeCoverageIgnore It relies too much on dependencies. * * @param int $post_parent Post ID. - * @param int $number_of_public_posts The number of public posts + * @param int $has_public_posts Unused. * * @return bool Whether the update was successful. * * @deprecated 17.7 */ - public function update_has_public_posts_on_attachments( $post_parent, $number_of_public_posts ) { + public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) { _deprecated_function( __METHOD__, '17.7' ); $indexable = $this->repository->find_by_id_and_type( $post_parent, 'post' ); From 2fdce2fa88d397a99e673078426ae75400b8829f Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 16 Nov 2021 11:47:41 +0100 Subject: [PATCH 13/53] Fix cs --- lib/model.php | 10 +- src/builders/indexable-author-builder.php | 17 ++-- src/builders/indexable-home-page-builder.php | 7 ++ src/builders/indexable-post-builder.php | 2 + .../indexable-post-type-archive-builder.php | 7 ++ src/builders/indexable-term-builder.php | 7 ++ ...1254_ReplaceHasPublicPostsOnIndexables.php | 6 -- ...1108133106_ReplaceIsPublicOnIndexables.php | 11 +-- src/helpers/author-archive-helper.php | 23 +---- src/helpers/post-helper.php | 6 +- src/helpers/robots-helper.php | 8 +- .../watchers/indexable-post-watcher.php | 1 - src/models/indexable.php | 5 + src/repositories/indexable-repository.php | 94 ++++++++++--------- 14 files changed, 114 insertions(+), 90 deletions(-) diff --git a/lib/model.php b/lib/model.php index e7490906cd7..5163a3b7ef7 100644 --- a/lib/model.php +++ b/lib/model.php @@ -512,7 +512,7 @@ public function set_orm( $orm ) { */ public function __get( $property ) { $property = $this->handleDeprecation( $property ); - $value = $this->orm->get( $property ); + $value = $this->orm->get( $property ); if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { return (bool) $value; @@ -748,7 +748,11 @@ protected function handleDeprecation( $property ) { if ( ! empty( $deprecation['replacement'] ) ) { // There is no _deprecated_property. This matches our usecase best. - _deprecated_argument( __FUNCTION__, '17.7', 'Use the \"' . $deprecation['replacement'] . '\" property instead of \"' . $property . '\" ' ); + _deprecated_argument( + __FUNCTION__, + '17.7', + 'Use the \"' . esc_html( $deprecation['replacement'] ) . '\" property instead of \"' . esc_html( $property ) . '\" ' + ); if ( $deprecation['automatically_use_replacement'] === true ) { return $deprecation['replacement']; @@ -757,7 +761,7 @@ protected function handleDeprecation( $property ) { return $property; } // There is no _deprecated_property. This matches our usecase best. - _deprecated_argument( __FUNCTION__, '17.7', 'The \"' . $property . '\" property will be removed in a future version' ); + _deprecated_argument( __FUNCTION__, '17.7', 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); return $property; } diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index 4df16107863..26a34b41142 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -100,6 +100,13 @@ public function build( $user_id, Indexable $indexable ) { return $indexable; } + /** + * Sets the aggregate values for an author indexable. + * + * @param Indexable $indexable The indexable to set the aggregates for. + * + * @return Indexable The indexable with set aggregates. + */ public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id ); $indexable->object_published_at = $aggregates->first_published_at; @@ -183,8 +190,6 @@ protected function find_alternative_image( Indexable $indexable ) { protected function get_public_post_archive_aggregates( $author_id ) { $post_statuses = $this->post_helper->get_public_post_statuses(); $post_types = $this->author_archive->get_author_archive_post_types(); - // TODO DIEDE: Protected pages krijgen _geen_ noindex. en staan gewoon in het author archive. moeten die meegeteld worden? - // Private werkt wel zoals verwacht. $sql = " SELECT @@ -192,11 +197,11 @@ protected function get_public_post_archive_aggregates( $author_id ) { MAX(p.post_modified_gmt) AS most_recent_last_modified, MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p - WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") - AND p.post_password = '' + WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') + AND p.post_password = \'\' AND p.post_author = %d - AND p.post_type IN (" . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ") - "; + AND p.post_type IN (' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ') + '; $replacements = \array_merge( $post_statuses, [ $author_id ], $post_types ); diff --git a/src/builders/indexable-home-page-builder.php b/src/builders/indexable-home-page-builder.php index e74878ca5a4..8db6bb2c65a 100644 --- a/src/builders/indexable-home-page-builder.php +++ b/src/builders/indexable-home-page-builder.php @@ -122,6 +122,13 @@ public function build( $indexable ) { return $indexable; } + /** + * Sets the aggregate values for a home page indexable. + * + * @param Indexable $indexable The indexable to set the aggregates for. + * + * @return Indexable The indexable with set aggregates. + */ public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates(); $indexable->object_published_at = $aggregates->first_published_at; diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 61c926d4b75..090d4fa2cb2 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -185,6 +185,7 @@ protected function get_permalink( $post_type, $post_id ) { * * @return bool|null Whether the post type is public. Null if no override is set. * + * @codeCoverageIgnore * @deprecated 17.7 */ protected function is_public( $indexable ) { @@ -219,6 +220,7 @@ protected function is_public( $indexable ) { * * @return bool|null False when it has no parent. Null when it has a parent. * + * @codeCoverageIgnore * @deprecated 17.7 */ protected function is_public_attachment( $indexable ) { diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 1ca8bd87bcf..dc63a0608db 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -96,6 +96,13 @@ public function build( $post_type, Indexable $indexable ) { return $indexable; } + /** + * Sets the aggregate values for a post type archive indexable. + * + * @param Indexable $indexable The indexable to set the aggregates for. + * + * @return Indexable The indexable with set aggregates. + */ public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_sub_type ); $indexable->object_published_at = $aggregates->first_published_at; diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 8bab2e25c00..177a3f15017 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -139,6 +139,13 @@ public function build( $term_id, $indexable ) { return $indexable; } + /** + * Sets the aggregate values for a term indexable. + * + * @param Indexable $indexable The indexable to set the aggregates for. + * + * @return Indexable The indexable with set aggregates. + */ public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id, $indexable->object_sub_type ); $indexable->object_published_at = $aggregates->first_published_at; diff --git a/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php b/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php index e7b85bc865b..cecbf168c86 100644 --- a/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php +++ b/src/config/migrations/20211105121254_ReplaceHasPublicPostsOnIndexables.php @@ -1,9 +1,4 @@ true, 'default' => null, - ] ); + ] + ); } /** @@ -51,11 +47,10 @@ public function down() { $this->remove_column( $table_name, - 'is_publicly_viewable', + 'is_publicly_viewable' ); } - /** * Retrieves the table name to use. * diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index 3a5f4276098..981fc4a5cb9 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -10,24 +10,6 @@ */ class Author_Archive_Helper { - /** - * @var Indexable_Repository - */ - private $indexable_repository; - - /** - * Sets the indexable repository. Done to avoid circular dependencies. - * - * @required - * - * @param Indexable_Repository $indexable_repository - * - * @return void - */ - public function setIndexableRepository( Indexable_Repository $indexable_repository ) { - $this->indexable_repository = $indexable_repository; - } - /** * Gets the array of post types that are shown on an author's archive. * @@ -49,10 +31,11 @@ public function get_author_archive_post_types() { * * @return bool|null Whether the author has at least one public post. * + * @codeCoverageIgnore * @deprecated 17.7 */ public function author_has_public_posts( $author_id ) { - _deprecated_function( __METHOD__, '17.7',Indexable_Repository::class.'::noindex_query'); + _deprecated_function( __METHOD__, '17.7', esc_html( Indexable_Repository::class ) . '::noindex_query' ); // First check if the author has at least one public post. $has_public_post = $this->author_has_a_public_post( $author_id ); if ( $has_public_post ) { @@ -110,7 +93,7 @@ protected function author_has_a_public_post( $author_id ) { * * @return bool Whether the author has at least one post with the is public null. * - * @deprecated 17.7 + * @deprecated 17.7 */ protected function author_has_a_post_with_is_public_null( $author_id ) { $cache_key = 'author_has_a_post_with_is_public_null_' . $author_id; diff --git a/src/helpers/post-helper.php b/src/helpers/post-helper.php index 8419851c38b..903cc773623 100644 --- a/src/helpers/post-helper.php +++ b/src/helpers/post-helper.php @@ -52,7 +52,7 @@ public function __construct( String_Helper $string ) { * * @return void */ - public function setIndexableBuilder( Indexable_Builder $indexable_builder ) { + public function set_indexable_builder( Indexable_Builder $indexable_builder ) { $this->indexable_builder = $indexable_builder; } @@ -135,6 +135,8 @@ public function get_post( $post_id ) { return \get_post( $post_id ); } + // phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- Signature kept the same after deprecation. + /** * Updates the number_of_publicly_viewable_posts field on attachments for a post_parent. * @@ -159,6 +161,8 @@ public function update_has_public_posts_on_attachments( $post_parent, $has_publi return true; } + // phpcs:enable + /** * Determines if the post can be indexed. * diff --git a/src/helpers/robots-helper.php b/src/helpers/robots-helper.php index a23151e31fb..d8e2da3493b 100644 --- a/src/helpers/robots-helper.php +++ b/src/helpers/robots-helper.php @@ -8,12 +8,16 @@ class Robots_Helper { /** + * A helper to get and set plugin options. + * * @var Options_Helper */ private $options_helper; /** - * @param Options_Helper $options_helper The indexable version manager. + * Robots_Helper constructor. + * + * @param Options_Helper $options_helper A helper to get and set plugin options. */ public function __construct( Options_Helper $options_helper ) { $this->options_helper = $options_helper; @@ -45,7 +49,7 @@ public function set_robots_no_index( $robots ) { * * @return bool Whether the site default is set to noindex for the requested object type. */ - public function get_default_noindex_for_object( $object_type, $object_sub_type = "" ) { + public function get_default_noindex_for_object( $object_type, $object_sub_type = '' ) { switch ( $object_type ) { case 'post': return (bool) $this->options_helper->get( 'noindex-' . $object_sub_type ); diff --git a/src/integrations/watchers/indexable-post-watcher.php b/src/integrations/watchers/indexable-post-watcher.php index cb098f1bcb4..ca45de7e9a7 100644 --- a/src/integrations/watchers/indexable-post-watcher.php +++ b/src/integrations/watchers/indexable-post-watcher.php @@ -260,7 +260,6 @@ protected function get_related_term_indexables( $post_id ) { return $this->repository->find_by_multiple_ids_and_type( $term_ids, 'term', false ); } - /** * Tests if the site is multisite and switched. * diff --git a/src/models/indexable.php b/src/models/indexable.php index fee687da9f2..ca31c6ebbf7 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -137,6 +137,11 @@ class Indexable extends Model { 'number_of_publicly_viewable_posts', ]; + /** + * Which columns are deprecated. + * + * @var array + */ protected $deprecated_columns = [ 'has_public_posts' => [ 'since' => '17.7', diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 6137462050a..742ead5981c 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -75,7 +75,6 @@ class Indexable_Repository { */ private $robots_helper; - /** * Returns the instance of this class constructed through the ORM Wrapper. * @@ -178,9 +177,10 @@ public function for_current_page() { * @return ORM The query object. */ public function noindex_query( $object_type, $object_sub_type = null, $noindex = true ) { - $query = $this->query() - ->where( 'object_type', $object_type ) - ->where( 'object_sub_type', $object_sub_type ); + $query = $this + ->query() + ->where( 'object_type', $object_type ) + ->where( 'object_sub_type', $object_sub_type ); $default_noindex = $this->robots_helper->get_default_noindex_for_object( $object_type, $object_sub_type ); @@ -215,10 +215,11 @@ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. - return $this->query() - ->where( 'permalink_hash', $permalink_hash ) - ->where( 'permalink', $permalink ) - ->find_one(); + return $this + ->query() + ->where( 'permalink_hash', $permalink_hash ) + ->where( 'permalink', $permalink ) + ->find_one(); } /** @@ -330,10 +331,11 @@ public function find_for_post_type_archive( $post_type, $auto_create = true ) { * * @var Indexable $indexable */ - $indexable = $this->query() - ->where( 'object_type', 'post-type-archive' ) - ->where( 'object_sub_type', $post_type ) - ->find_one(); + $indexable = $this + ->query() + ->where( 'object_type', 'post-type-archive' ) + ->where( 'object_sub_type', $post_type ) + ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); @@ -356,10 +358,11 @@ public function find_for_system_page( $object_sub_type, $auto_create = true ) { * * @var Indexable $indexable */ - $indexable = $this->query() - ->where( 'object_type', 'system-page' ) - ->where( 'object_sub_type', $object_sub_type ) - ->find_one(); + $indexable = $this + ->query() + ->where( 'object_type', 'system-page' ) + ->where( 'object_sub_type', $object_sub_type ) + ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); @@ -368,7 +371,6 @@ public function find_for_system_page( $object_sub_type, $auto_create = true ) { return $this->upgrade_indexable( $indexable ); } - /** * Retrieves a list of attachment indexables that belong to a parent post. * @@ -382,12 +384,13 @@ public function find_for_post_attachments( $parent_post_id ) { * * @var Indexable[] $indexables */ - $indexables = $this->query() - ->where( 'object_type', 'post' ) - ->where( 'object_sub_type', 'attachment' ) - ->where( 'post_status', 'inherit' ) - ->where( 'post_parent', $parent_post_id ) - ->find_many(); + $indexables = $this + ->query() + ->where( 'object_type', 'post' ) + ->where( 'object_sub_type', 'attachment' ) + ->where( 'post_status', 'inherit' ) + ->where( 'post_parent', $parent_post_id ) + ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } @@ -402,10 +405,11 @@ public function find_for_post_attachments( $parent_post_id ) { * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { - $indexable = $this->query() - ->where( 'object_id', $object_id ) - ->where( 'object_type', $object_type ) - ->find_one(); + $indexable = $this + ->query() + ->where( 'object_id', $object_id ) + ->where( 'object_type', $object_type ) + ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); @@ -436,10 +440,11 @@ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_ * * @var Indexable[] $indexables */ - $indexables = $this->query() - ->where_in( 'object_id', $object_ids ) - ->where( 'object_type', $object_type ) - ->find_many(); + $indexables = $this + ->query() + ->where_in( 'object_id', $object_ids ) + ->where( 'object_type', $object_type ) + ->find_many(); if ( $auto_create ) { $indexables_available = []; @@ -505,10 +510,11 @@ public function get_ancestors( Indexable $indexable ) { return []; } - $indexables = $this->query() - ->where_in( 'id', $indexable_ids ) - ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) - ->find_many(); + $indexables = $this + ->query() + ->where_in( 'id', $indexable_ids ) + ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) + ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } @@ -522,10 +528,11 @@ public function get_ancestors( Indexable $indexable ) { * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { - $query = $this->query() - ->where( 'post_parent', $post_parent ) - ->where( 'object_type', 'post' ) - ->where( 'post_status', 'publish' ); + $query = $this + ->query() + ->where( 'post_parent', $post_parent ) + ->where( 'object_type', 'post' ) + ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); @@ -543,10 +550,11 @@ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { - return (bool) $this->query() - ->set( 'incoming_link_count', $count ) - ->where( 'id', $indexable_id ) - ->update_many(); + return (bool) $this + ->query() + ->set( 'incoming_link_count', $count ) + ->where( 'id', $indexable_id ) + ->update_many(); } /** From 3a18f72008e7b2ae53c9d71b84bb2ba9fddd8f65 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:08:00 +0100 Subject: [PATCH 14/53] Mark enabled date archives as publicly viewable --- src/builders/indexable-date-archive-builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builders/indexable-date-archive-builder.php b/src/builders/indexable-date-archive-builder.php index 57c7193d68a..465cf6eeb86 100644 --- a/src/builders/indexable-date-archive-builder.php +++ b/src/builders/indexable-date-archive-builder.php @@ -56,7 +56,7 @@ public function build( $indexable ) { $indexable->description = $this->options->get( 'metadesc-archive-wpseo' ); $indexable->is_robots_noindex = $this->options->get( 'noindex-archive-wpseo' ); $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); - $indexable->is_publicly_viewable = (bool) $this->options->get( 'disable-date' ); + $indexable->is_publicly_viewable = ! (bool) $this->options->get( 'disable-date' ); $indexable->blog_id = \get_current_blog_id(); $indexable->permalink = null; $indexable->version = $this->version; From 5e89640b4c7279ccf87f883d3488aa6ec389a17d Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:08:30 +0100 Subject: [PATCH 15/53] Delegate logic to central helper --- .../indexable-post-type-archive-builder.php | 17 +++++++++---- src/helpers/post-type-helper.php | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index dc63a0608db..8dad2d15eea 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -5,6 +5,7 @@ use wpdb; use Yoast\WP\SEO\Helpers\Options_Helper; use Yoast\WP\SEO\Helpers\Post_Helper; +use Yoast\WP\SEO\Helpers\Post_Type_Helper; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions; @@ -38,6 +39,13 @@ class Indexable_Post_Type_Archive_Builder { */ protected $post_helper; + /** + * A helper for post types. + * + * @var Post_Type_Helper + */ + private $post_type_helper; + /** * The WPDB instance. * @@ -57,11 +65,13 @@ public function __construct( Options_Helper $options, Indexable_Builder_Versions $versions, Post_Helper $post_helper, + Post_Type_Helper $post_type_helper, wpdb $wpdb ) { $this->options = $options; $this->version = $versions->get_latest_version_for_type( 'post-type-archive' ); $this->post_helper = $post_helper; + $this->post_type_helper = $post_type_helper; $this->wpdb = $wpdb; } @@ -83,11 +93,8 @@ public function build( $post_type, Indexable $indexable ) { $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); $indexable->blog_id = \get_current_blog_id(); - - $post_type_object = get_post_type_object( $post_type ); - if ( $post_type_object !== null ) { - $indexable->is_publicly_viewable = $post_type_object->has_archive && $post_type_object->rewrite !== false; - } + // TODO Diede Does the watcher post type changes this? + $indexable->is_publicly_viewable = $this->post_type_helper->has_publicly_viewable_archive( $post_type ); $indexable = $this->set_aggregate_values( $indexable ); diff --git a/src/helpers/post-type-helper.php b/src/helpers/post-type-helper.php index aaf6d838093..5201b4b260b 100644 --- a/src/helpers/post-type-helper.php +++ b/src/helpers/post-type-helper.php @@ -79,6 +79,30 @@ public function get_accessible_post_types() { return $post_types; } + /** + * Checks whether a post_type has a publicly viewable archive. + * + * @param string $post_type The name of a registered post type. + * + * @return bool Whether a post_type has a publicly viewable archive. + */ + public function has_publicly_viewable_archive( $post_type ) { + $post_type_object = get_post_type_object( $post_type ); + if ( $post_type_object === null ) { + return false; + } + + if ( $post_type_object->publicly_queryable === false ) { + return false; + } + + if ( $post_type_object->rewrite === false ) { + return false; + } + + return $post_type_object->has_archive; + } + /** * Returns an array of post types that are excluded from being indexed for the * indexables. From 0a38f494a8573609f9de2e37f4cbe3ba884c715f Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:11:43 +0100 Subject: [PATCH 16/53] Make global namespace usage explicit --- lib/model.php | 4 ++-- src/helpers/author-archive-helper.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/model.php b/lib/model.php index 5163a3b7ef7..f5163daae13 100644 --- a/lib/model.php +++ b/lib/model.php @@ -748,7 +748,7 @@ protected function handleDeprecation( $property ) { if ( ! empty( $deprecation['replacement'] ) ) { // There is no _deprecated_property. This matches our usecase best. - _deprecated_argument( + \_deprecated_argument( __FUNCTION__, '17.7', 'Use the \"' . esc_html( $deprecation['replacement'] ) . '\" property instead of \"' . esc_html( $property ) . '\" ' @@ -761,7 +761,7 @@ protected function handleDeprecation( $property ) { return $property; } // There is no _deprecated_property. This matches our usecase best. - _deprecated_argument( __FUNCTION__, '17.7', 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); + \_deprecated_argument( __FUNCTION__, '17.7', 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); return $property; } diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index 981fc4a5cb9..c2a699563a3 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -35,7 +35,7 @@ public function get_author_archive_post_types() { * @deprecated 17.7 */ public function author_has_public_posts( $author_id ) { - _deprecated_function( __METHOD__, '17.7', esc_html( Indexable_Repository::class ) . '::noindex_query' ); + \_deprecated_function( __METHOD__, '17.7', esc_html( Indexable_Repository::class ) . '::noindex_query' ); // First check if the author has at least one public post. $has_public_post = $this->author_has_a_public_post( $author_id ); if ( $has_public_post ) { From 81e7421574d83de6eb961b4f9dcda7dd94b45b42 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:22:09 +0100 Subject: [PATCH 17/53] Flip argument order --- src/repositories/indexable-repository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 742ead5981c..861932fd434 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -170,13 +170,13 @@ public function for_current_page() { * Gets a query that finds all indexables of a type+subtype that match the noindex value * while taking site defaults into account. * + * @param bool $noindex The noindex value of the posts to find. * @param string $object_type The indexable object type. * @param string|null $object_sub_type The indexable object subtype. - * @param bool $noindex The noindex value of the posts to find. * * @return ORM The query object. */ - public function noindex_query( $object_type, $object_sub_type = null, $noindex = true ) { + public function query_where_noindex( $noindex, $object_type, $object_sub_type = null ) { $query = $this ->query() ->where( 'object_type', $object_type ) From 1564f53a328cd60aaa1ce0ad4a16271a100c9e52 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:22:55 +0100 Subject: [PATCH 18/53] Allow for considering empty archives as indexed --- src/repositories/indexable-repository.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 861932fd434..92b35e9fd0c 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -170,13 +170,14 @@ public function for_current_page() { * Gets a query that finds all indexables of a type+subtype that match the noindex value * while taking site defaults into account. * - * @param bool $noindex The noindex value of the posts to find. - * @param string $object_type The indexable object type. - * @param string|null $object_sub_type The indexable object subtype. + * @param bool $noindex The noindex value of the posts to find. + * @param string $object_type The indexable object type. + * @param string|null $object_sub_type The indexable object subtype. + * @param boolean $noindex_empty_archives Whether an archive should be considered as noindex if it has no public posts. * * @return ORM The query object. */ - public function query_where_noindex( $noindex, $object_type, $object_sub_type = null ) { + public function query_where_noindex( $noindex, $object_type, $object_sub_type = null, $noindex_empty_archives = true ) { $query = $this ->query() ->where( 'object_type', $object_type ) @@ -192,7 +193,8 @@ public function query_where_noindex( $noindex, $object_type, $object_sub_type = } // Let the number of posts in an archive determine the noindex value. - if ( in_array( $object_type, [ 'post-type-archive', 'term', 'user', 'home-page' ], true ) ) { + $is_archive_type = in_array( $object_type, [ 'post-type-archive', 'term', 'user', 'home-page' ], true ); + if ( $is_archive_type && $noindex_empty_archives ) { if ( $noindex === true ) { $condition .= ' OR number_of_publicly_viewable_posts = 0'; } From ecae28e381bc85d95ca986f0b80e77d786bfdde2 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:23:17 +0100 Subject: [PATCH 19/53] Update deperatation based on the expected release version --- src/models/indexable.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/indexable.php b/src/models/indexable.php index ca31c6ebbf7..6cce916936e 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -144,12 +144,12 @@ class Indexable extends Model { */ protected $deprecated_columns = [ 'has_public_posts' => [ - 'since' => '17.7', + 'since' => '17.9', 'replacement' => 'number_of_publicly_viewable_posts', 'automatically_use_replacement' => true, ], 'is_public' => [ - 'since' => '17.7', + 'since' => '17.9', 'replacement' => 'is_publicly_viewable', 'automatically_use_replacement' => false, ], From ea0a46e4e52efcf983b0151936e8f2f812910317 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:23:38 +0100 Subject: [PATCH 20/53] Prevent new integrators from accessing is_public --- src/models/indexable.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/indexable.php b/src/models/indexable.php index 6cce916936e..b8233f5654a 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -110,7 +110,6 @@ class Indexable extends Model { 'is_robots_noimageindex', 'is_robots_nosnippet', 'is_cornerstone', - 'is_public', 'is_publicly_viewable', 'is_protected', ]; From 6ccacd5ac3752ea22851fced77d48c77d8914e0e Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 14:23:56 +0100 Subject: [PATCH 21/53] Update tests --- .../indexable-author-builder-test.php | 115 ++---- .../indexable-date-archive-builder-test.php | 5 + .../indexable-home-page-builder-test.php | 56 ++- .../builders/indexable-post-builder-test.php | 379 +++++++----------- ...dexable-post-type-archive-builder-test.php | 35 +- .../indexable-system-page-builder-test.php | 4 +- .../builders/indexable-term-builder-test.php | 82 ++-- .../indexable-post-builder-double.php | 4 +- .../indexable-post-watcher-double.php | 9 - .../helpers/author-archive-helper-test.php | 35 -- tests/unit/helpers/robots-helper-test.php | 13 +- .../watchers/indexable-post-watcher-test.php | 116 ++---- .../indexable-repository-test.php | 11 + 13 files changed, 382 insertions(+), 482 deletions(-) diff --git a/tests/unit/builders/indexable-author-builder-test.php b/tests/unit/builders/indexable-author-builder-test.php index 07b2622849f..a14b7352b8e 100644 --- a/tests/unit/builders/indexable-author-builder-test.php +++ b/tests/unit/builders/indexable-author-builder-test.php @@ -15,8 +15,8 @@ /** * Class Indexable_Author_Test. * - * @group indexables - * @group builders + * @group indexables + * @group builders * * @coversDefaultClass \Yoast\WP\SEO\Builders\Indexable_Author_Builder * @covers \Yoast\WP\SEO\Builders\Indexable_Author_Builder @@ -54,7 +54,7 @@ class Indexable_Author_Builder_Test extends TestCase { /** * The wpdb instance * - * @var wpdb|Mockery\MockInterface + * @var \wpdb|Mockery\MockInterface */ protected $wpdb; @@ -95,7 +95,6 @@ protected function set_up() { $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image_source', null ); $this->indexable_mock->orm->expects( 'set' )->with( 'is_public', null ); - $this->indexable_mock->orm->expects( 'set' )->with( 'has_public_posts', true ); $this->indexable_mock->orm->expects( 'get' )->with( 'is_robots_noindex' )->andReturn( 0 ); @@ -104,9 +103,8 @@ protected function set_up() { $this->author_archive = Mockery::mock( Author_Archive_Helper::class ); $this->author_archive - ->expects( 'author_has_public_posts' ) - ->with( 1 ) - ->andReturn( true ); + ->expects( 'get_author_archive_post_types' ) + ->andReturn( [ 'post', 'my-cpt' ] ); $this->versions = Mockery::mock( Indexable_Builder_Versions::class ); $this->versions @@ -115,9 +113,35 @@ protected function set_up() { ->andReturn( 2 ); $this->post_helper = Mockery::mock( Post_Helper::class ); + $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); + $this->wpdb = Mockery::mock( 'wpdb' ); $this->wpdb->posts = 'wp_posts'; + $this->wpdb->expects( 'prepare' )->with( + " + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at + FROM {$this->wpdb->posts} AS p + WHERE p.post_status IN (%s) + AND p.post_password = '' + AND p.post_author = %d + AND p.post_type IN (%s, %s) + ", ['publish', 1, 'post', 'my-cpt'] + )->andReturn( 'PREPARED_QUERY' ); + $this->wpdb->expects( 'get_row' )->with( 'PREPARED_QUERY' )->andReturn( + (object) [ + 'number_of_public_posts' => '7', + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at'=>'1234-12-12 00:00:00', + ] + ); + + Monkey\Functions\expect( '_deprecated_argument' ); + Monkey\Functions\expect( 'esc_html' ); + $this->instance = new Indexable_Author_Builder( $this->author_archive, $this->versions, $this->post_helper, $this->wpdb ); } @@ -137,39 +161,22 @@ public function test_build() { $this->indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', true ); $this->indexable_mock->orm->expects( 'set' )->with( 'version', 2 ); + $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'object_id' )->andReturn( 1 ); $this->indexable_mock->orm->expects( 'get' )->once()->with( 'open_graph_image' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'open_graph_image_id' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'open_graph_image_source' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'twitter_image' ); $this->indexable_mock->orm->expects( 'get' )->times( 3 )->with( 'twitter_image_id' ); - $this->indexable_mock->orm->expects( 'get' )->with( 'object_id' ); + $this->indexable_mock->orm->expects( 'get' )->once()->with( 'object_last_modified' ); $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_image', 'avatar_image.jpg' ); $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_image_source', 'gravatar-image' ); $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image', 'avatar_image.jpg' ); $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image_source', 'gravatar-image' ); - $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - - $this->wpdb->expects( 'prepare' )->once()->with( - " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at - FROM {$this->wpdb->posts} AS p - WHERE p.post_status IN (%s) - AND p.post_password = '' - AND p.post_author = %d - ", - [ 'publish', 1 ] - )->andReturn( 'PREPARED_QUERY' ); - $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( - (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', - ] - ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', '7' ); Monkey\Functions\expect( 'get_avatar_url' ) ->once() @@ -193,39 +200,22 @@ public function test_build_without_alternative_image() { $this->indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', true ); $this->indexable_mock->orm->expects( 'set' )->with( 'version', 2 ); + $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'object_id' )->andReturn( 1 ); $this->indexable_mock->orm->expects( 'get' )->once()->with( 'open_graph_image' ); $this->indexable_mock->orm->expects( 'get' )->once()->with( 'open_graph_image_id' ); $this->indexable_mock->orm->expects( 'get' )->once()->with( 'open_graph_image_source' ); $this->indexable_mock->orm->expects( 'get' )->once()->with( 'twitter_image' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'twitter_image_id' ); - $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'object_id' ); - - $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - - $this->wpdb->expects( 'prepare' )->once()->with( - " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at - FROM {$this->wpdb->posts} AS p - WHERE p.post_status IN (%s) - AND p.post_password = '' - AND p.post_author = %d - ", - [ 'publish', 1 ] - )->andReturn( 'PREPARED_QUERY' ); - $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( - (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', - ] - ); + $this->indexable_mock->orm->expects( 'get' )->once()->with( 'object_last_modified' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', '7' ); Monkey\Functions\expect( 'get_avatar_url' ) ->once() ->with( - $this->indexable_mock->object_id, + 1, [ 'size' => 500, 'scheme' => 'https', @@ -252,39 +242,22 @@ public function test_build_with_undefined_author_meta() { $this->indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', false ); $this->indexable_mock->orm->expects( 'set' )->with( 'version', 2 ); + $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'object_id' )->andReturn( 1 ); $this->indexable_mock->orm->expects( 'get' )->once()->with( 'open_graph_image' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'open_graph_image_id' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'open_graph_image_source' ); $this->indexable_mock->orm->expects( 'get' )->twice()->with( 'twitter_image' ); $this->indexable_mock->orm->expects( 'get' )->times( 3 )->with( 'twitter_image_id' ); - $this->indexable_mock->orm->expects( 'get' )->with( 'object_id' ); + $this->indexable_mock->orm->expects( 'get' )->once()->with( 'object_last_modified' ); $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_image', 'avatar_image.jpg' ); $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_image_source', 'gravatar-image' ); $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image', 'avatar_image.jpg' ); $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image_source', 'gravatar-image' ); - $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - - $this->wpdb->expects( 'prepare' )->once()->with( - " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at - FROM {$this->wpdb->posts} AS p - WHERE p.post_status IN (%s) - AND p.post_password = '' - AND p.post_author = %d - ", - [ 'publish', 1 ] - )->andReturn( 'PREPARED_QUERY' ); - $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( - (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', - ] - ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', '7' ); Monkey\Functions\expect( 'get_avatar_url' ) ->once() diff --git a/tests/unit/builders/indexable-date-archive-builder-test.php b/tests/unit/builders/indexable-date-archive-builder-test.php index f3aa8b4ab7e..64066ac6772 100644 --- a/tests/unit/builders/indexable-date-archive-builder-test.php +++ b/tests/unit/builders/indexable-date-archive-builder-test.php @@ -34,6 +34,7 @@ public function test_build() { $options_mock->expects( 'get' )->with( 'title-archive-wpseo' )->andReturn( 'date_archive_title' ); $options_mock->expects( 'get' )->with( 'metadesc-archive-wpseo' )->andReturn( 'date_archive_meta_description' ); $options_mock->expects( 'get' )->with( 'noindex-archive-wpseo' )->andReturn( false ); + $options_mock->expects( 'get' )->with( 'disable-date' )->andReturn( false ); $indexable_mock = Mockery::mock( Indexable::class ); $indexable_mock->orm = Mockery::mock( ORM::class ); @@ -48,6 +49,10 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'permalink', null ); $indexable_mock->orm->expects( 'set' )->with( 'version', 1 ); + $indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', 1 ); + + Functions\expect( '_deprecated_argument' ); + Functions\expect( 'esc_html' ); $builder = new Indexable_Date_Archive_Builder( $options_mock, new Indexable_Builder_Versions() ); $builder->build( $indexable_mock ); diff --git a/tests/unit/builders/indexable-home-page-builder-test.php b/tests/unit/builders/indexable-home-page-builder-test.php index 04258ca5c01..c2837af241c 100644 --- a/tests/unit/builders/indexable-home-page-builder-test.php +++ b/tests/unit/builders/indexable-home-page-builder-test.php @@ -4,6 +4,7 @@ use Brain\Monkey; use Mockery; +use wpdb; use WPSEO_Utils; use Yoast\WP\Lib\ORM; use Yoast\WP\SEO\Builders\Indexable_Home_Page_Builder; @@ -20,13 +21,13 @@ /** * Class Indexable_Author_Test. * - * @group indexables - * @group builders + * @group indexables + * @group builders * * @coversDefaultClass \Yoast\WP\SEO\Builders\Indexable_Author_Builder * @covers \Yoast\WP\SEO\Builders\Indexable_Home_Page_Builder * - * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded -- 5 words is fine. + * @phpcs :disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded -- 5 words is fine. */ class Indexable_Home_Page_Builder_Test extends TestCase { @@ -142,13 +143,13 @@ protected function set_up() { $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_description', 'home_og_description' ); $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_image_source', null ); $this->indexable_mock->orm->expects( 'set' )->with( 'open_graph_image_meta', null ); + $this->indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', 1 ); $this->indexable_mock->orm->expects( 'set' )->with( 'version', 2 ); // Mock offsetExists. $this->indexable_mock->orm->expects( 'offsetExists' )->with( 'description' )->andReturn( true ); // Mock Indexable ORM getters. - $this->indexable_mock->orm->expects( 'get' )->with( 'description' )->andReturn( 'home_meta_description' ); $this->indexable_mock->orm->expects( 'get' )->with( 'open_graph_image' )->andReturn( 'home_og_image' ); $this->indexable_mock->orm->expects( 'get' )->with( 'open_graph_image_id' )->andReturn( 1337 )->twice(); @@ -200,6 +201,7 @@ public function test_build() { $this->options_mock->expects( 'get' )->with( 'metadesc-home-wpseo' )->andReturn( 'home_meta_description' ); $this->indexable_mock->orm->expects( 'set' )->with( 'description', 'home_meta_description' ); + $this->indexable_mock->orm->expects( 'get' )->with( 'description' )->andReturn( 'home_meta_description' ); Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $this->indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); @@ -208,7 +210,10 @@ public function test_build() { $this->wpdb->expects( 'prepare' )->once()->with( " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) AND p.post_password = '' @@ -218,13 +223,15 @@ public function test_build() { )->andReturn( 'PREPARED_QUERY' ); $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', + 'number_of_public_posts' => 20, + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at' => '1234-12-12 00:00:00', ] ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 20 ); $this->instance->build( $this->indexable_mock ); } @@ -242,8 +249,12 @@ public function test_build_with_fallback_description() { // When no meta description is stored in the WP_Options... $this->options_mock->expects( 'get' )->with( 'metadesc-home-wpseo' )->andReturn( false ); + // We expect the description to be `false` in the ORM layer. - $this->indexable_mock->orm->expects( 'set' )->with( 'description', false ); + $this->indexable_mock->orm->expects( 'set' )->once()->with( 'description', false ); + $this->indexable_mock->orm->expects( 'get' )->with( 'description' )->andReturn( false ); + // Brainmonkey makes get_bloginfo( 'description' ) return 'description'. + $this->indexable_mock->orm->expects( 'set' )->once()->with( 'description', 'description' ); Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $this->indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); @@ -252,7 +263,10 @@ public function test_build_with_fallback_description() { $this->wpdb->expects( 'prepare' )->once()->with( " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) AND p.post_password = '' @@ -262,13 +276,15 @@ public function test_build_with_fallback_description() { )->andReturn( 'PREPARED_QUERY' ); $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', + 'number_of_public_posts' => 20, + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at' => '1234-12-12 00:00:00', ] ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 20 ); $this->instance->build( $this->indexable_mock ); } @@ -280,6 +296,7 @@ public function test_build_open_graph_image_meta_data() { $this->options_mock->expects( 'get' )->with( 'metadesc-home-wpseo' )->andReturn( 'home_meta_description' ); $this->indexable_mock->orm->expects( 'set' )->with( 'description', 'home_meta_description' ); + $this->indexable_mock->orm->expects( 'get' )->with( 'description' )->andReturn( 'home_meta_description' ); // Transform the image meta mock to JSON, since we expect that to be stored in the DB. $image_meta_mock_json = WPSEO_Utils::format_json_encode( $this->image_meta_mock ); @@ -295,7 +312,10 @@ public function test_build_open_graph_image_meta_data() { $this->wpdb->expects( 'prepare' )->once()->with( " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) AND p.post_password = '' @@ -305,13 +325,15 @@ public function test_build_open_graph_image_meta_data() { )->andReturn( 'PREPARED_QUERY' ); $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', + 'number_of_public_posts' => 20, + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at' => '1234-12-12 00:00:00', ] ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 20 ); $this->instance->build( $this->indexable_mock ); } diff --git a/tests/unit/builders/indexable-post-builder-test.php b/tests/unit/builders/indexable-post-builder-test.php index 8d07a7e6597..9f6507dbf4b 100644 --- a/tests/unit/builders/indexable-post-builder-test.php +++ b/tests/unit/builders/indexable-post-builder-test.php @@ -69,7 +69,7 @@ class Indexable_Post_Builder_Test extends TestCase { * * @var Post_Helper|Mockery\MockInterface */ - protected $post; + protected $post_helper; /** * The post type helper. @@ -98,11 +98,11 @@ protected function set_up() { $this->image = Mockery::mock( Image_Helper::class ); $this->open_graph_image = Mockery::mock( Open_Graph_Image_Helper::class ); $this->twitter_image = Mockery::mock( Twitter_Image_Helper::class ); - $this->post = Mockery::mock( Post_Helper::class ); + $this->post_helper = Mockery::mock( Post_Helper::class ); $this->post_type_helper = Mockery::mock( Post_Type_Helper::class ); $this->instance = new Indexable_Post_Builder_Double( - $this->post, + $this->post_helper, $this->post_type_helper, new Indexable_Builder_Versions() ); @@ -132,20 +132,20 @@ protected function set_indexable_set_expectations( $indexable_mock, $expectation */ protected function twitter_image_set_by_user() { $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'twitter_image' ) - ->andReturn( 'twitter-image' ); + ->with( 'twitter_image' ) + ->andReturn( 'twitter-image' ); $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'twitter_image_id' ) - ->andReturn( 'twitter-image-id' ); + ->with( 'twitter_image_id' ) + ->andReturn( 'twitter-image-id' ); $this->twitter_image->shouldReceive( 'get_by_id' ) - ->with( 'twitter-image-id' ) - ->andReturn( 'twitter_image' ); + ->with( 'twitter-image-id' ) + ->andReturn( 'twitter_image' ); $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'twitter_image_source' ) - ->andReturn( 'set-by-user' ); + ->with( 'twitter_image_source' ) + ->andReturn( 'set-by-user' ); } /** @@ -155,21 +155,21 @@ protected function twitter_image_set_by_user() { */ protected function open_graph_image_set_by_user( $image_meta ) { $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'open_graph_image' ) - ->andReturn( 'open-graph-image' ); + ->with( 'open_graph_image' ) + ->andReturn( 'open-graph-image' ); $this->indexable->orm->shouldReceive( 'get' ) - ->twice() - ->with( 'open_graph_image_id' ) - ->andReturn( 'open-graph-image-id' ); + ->twice() + ->with( 'open_graph_image_id' ) + ->andReturn( 'open-graph-image-id' ); $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'open_graph_image_source' ) - ->andReturn( 'set-by-user' ); + ->with( 'open_graph_image_source' ) + ->andReturn( 'set-by-user' ); $this->open_graph_image->shouldReceive( 'get_image_by_id' ) - ->with( 'open-graph-image-id' ) - ->andReturn( $image_meta ); + ->with( 'open-graph-image-id' ) + ->andReturn( $image_meta ); } /** @@ -235,72 +235,73 @@ public function test_build() { ); Monkey\Functions\expect( 'maybe_unserialize' )->andReturnFirstArg(); - $this->post->expects( 'get_post' ) - ->once() - ->with( 1 ) - ->andReturn( - (object) [ - 'post_content' => 'The content of the post', - 'post_type' => 'post', - 'post_status' => 'publish', - 'post_password' => '', - 'post_author' => '1', - 'post_parent' => '0', - 'post_date_gmt' => '1234-12-12 00:00:00', - 'post_modified_gmt' => '1234-12-12 00:00:00', - ] - ); + $this->post_helper->expects( 'get_post' ) + ->once() + ->with( 1 ) + ->andReturn( + (object) [ + 'post_content' => 'The content of the post', + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_password' => '', + 'post_author' => '1', + 'post_parent' => '0', + 'post_date_gmt' => '1234-12-12 00:00:00', + 'post_modified_gmt' => '1234-12-12 00:00:00', + ] + ); $this->post_type_helper ->expects( 'is_excluded' ) ->with( 'post' ) ->andReturn( false ); - $this->post + $this->post_helper ->expects( 'is_post_indexable' ) ->with( 1 ) ->andReturn( true ); $indexable_expectations = [ - 'object_id' => 1, - 'object_type' => 'post', - 'object_sub_type' => 'post', - 'permalink' => 'https://permalink', - 'canonical' => 'https://canonical', - 'title' => 'title', - 'breadcrumb_title' => 'breadcrumb_title', - 'description' => 'description', - 'open_graph_title' => 'open_graph_title', - 'open_graph_image' => 'open_graph_image', - 'open_graph_image_id' => 'open_graph_image_id', - 'open_graph_description' => 'open_graph_description', - 'twitter_title' => 'twitter_title', - 'twitter_image' => 'twitter_image', - 'twitter_image_id' => null, - 'twitter_description' => 'twitter_description', - 'is_cornerstone' => true, - 'is_robots_noindex' => true, - 'is_robots_nofollow' => true, - 'is_robots_noarchive' => false, - 'is_robots_noimageindex' => false, - 'is_robots_nosnippet' => false, - 'primary_focus_keyword' => 'focuskeyword', - 'primary_focus_keyword_score' => 100, - 'readability_score' => 50, - 'number_of_pages' => null, - 'is_public' => 0, - 'post_status' => 'publish', - 'is_protected' => false, - 'author_id' => 1, - 'post_parent' => 0, - 'has_public_posts' => false, - 'blog_id' => 1, - 'schema_page_type' => 'FAQPage', - 'schema_article_type' => 'NewsArticle', - 'estimated_reading_time_minutes' => 11, - 'version' => 2, - 'object_published_at' => '1234-12-12 00:00:00', - 'object_last_modified' => '1234-12-12 00:00:00', + 'object_id' => 1, + 'object_type' => 'post', + 'object_sub_type' => 'post', + 'permalink' => 'https://permalink', + 'canonical' => 'https://canonical', + 'title' => 'title', + 'breadcrumb_title' => 'breadcrumb_title', + 'description' => 'description', + 'open_graph_title' => 'open_graph_title', + 'open_graph_image' => 'open_graph_image', + 'open_graph_image_id' => 'open_graph_image_id', + 'open_graph_description' => 'open_graph_description', + 'twitter_title' => 'twitter_title', + 'twitter_image' => 'twitter_image', + 'twitter_image_id' => null, + 'twitter_description' => 'twitter_description', + 'is_cornerstone' => true, + 'is_robots_noindex' => true, + 'is_robots_nofollow' => true, + 'is_robots_noarchive' => false, + 'is_robots_noimageindex' => false, + 'is_robots_nosnippet' => false, + 'primary_focus_keyword' => 'focuskeyword', + 'primary_focus_keyword_score' => 100, + 'readability_score' => 50, + 'number_of_pages' => null, + 'is_public' => 0, + 'is_publicly_viewable' => true, + 'number_of_publicly_viewable_posts' => 0, + 'post_status' => 'publish', + 'is_protected' => false, + 'author_id' => 1, + 'post_parent' => 0, + 'blog_id' => 1, + 'schema_page_type' => 'FAQPage', + 'schema_article_type' => 'NewsArticle', + 'estimated_reading_time_minutes' => 11, + 'version' => 3, + 'object_published_at' => '1234-12-12 00:00:00', + 'object_last_modified' => '1234-12-12 00:00:00', ]; $this->indexable = Mockery::mock( Indexable::class ); @@ -341,10 +342,10 @@ public function test_build() { // We expect the open graph image, its source and its metadata to be set. $this->indexable->orm->expects( 'set' )->with( 'open_graph_image_source', 'set-by-user' ); $this->indexable->orm->expects( 'set' ) - ->with( 'open_graph_image', 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg' ); + ->with( 'open_graph_image', 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg' ); $this->indexable->orm->expects( 'set' ) // phpcs:ignore Yoast.Yoast.AlternativeFunctions.json_encode_json_encodeWithAdditionalParams -- Test code, mocking WP. - ->with( 'open_graph_image_meta', \json_encode( $image_meta, ( \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES ) ) ); + ->with( 'open_graph_image_meta', \json_encode( $image_meta, ( \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES ) ) ); // We expect the twitter image and its source to be set. $this->indexable->orm->expects( 'set' )->with( 'twitter_image_source', 'set-by-user' ); @@ -354,9 +355,6 @@ public function test_build() { $this->indexable->orm->expects( 'get' )->with( 'is_protected' )->andReturnFalse(); $this->indexable->orm->expects( 'get' )->with( 'is_robots_noindex' )->andReturn( true ); - // Has public posts. - $this->indexable->orm->expects( 'get' )->with( 'object_sub_type' )->andReturn( 'post' ); - // Breadcrumb title. $this->indexable->orm->expects( 'set' )->with( 'breadcrumb_title', null ); $this->indexable->orm->expects( 'offsetExists' )->with( 'breadcrumb_title' )->andReturnFalse(); @@ -364,6 +362,11 @@ public function test_build() { Monkey\Functions\expect( 'get_the_title' )->with( 1 )->andReturn( 'breadcrumb_title' ); Monkey\Functions\expect( 'wp_strip_all_tags' )->with( 'breadcrumb_title', true )->andReturn( 'breadcrumb_title' ); + Monkey\Functions\expect( '_deprecated_argument' ); + Monkey\Functions\expect( 'esc_html' ); + Monkey\Functions\expect( 'is_post_publicly_viewable' )->andReturn( true ); + + // Blog ID. Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); @@ -378,7 +381,7 @@ public function test_build() { public function test_build_post_not_indexable() { $this->indexable = Mockery::mock( Indexable::class ); - $this->post + $this->post_helper ->expects( 'is_post_indexable' ) ->with( 1 ) ->andReturn( false ); @@ -396,16 +399,16 @@ public function test_find_alternative_image_from_attachment() { $this->indexable->orm = Mockery::mock( ORM::class ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'attachment' ); + ->with( 'object_sub_type' ) + ->andReturn( 'attachment' ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + ->with( 'object_id' ) + ->andReturn( 123 ); $this->image->allows( 'is_valid_attachment' ) - ->with( 123 ) - ->andReturn( true ); + ->with( 123 ) + ->andReturn( true ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -427,16 +430,16 @@ public function test_find_alternative_image_from_featured_image() { $this->indexable->orm = Mockery::mock( ORM::class ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + ->with( 'object_id' ) + ->andReturn( 123 ); $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( 456 ); + ->with( 123 ) + ->andReturn( 456 ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -459,16 +462,16 @@ public function test_find_alternative_image_from_gallery() { $this->indexable->orm = Mockery::mock( ORM::class ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + ->with( 'object_id' ) + ->andReturn( 123 ); $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( false ); + ->with( 123 ) + ->andReturn( false ); $image_meta = [ 'width' => 640, @@ -483,8 +486,8 @@ public function test_find_alternative_image_from_gallery() { ]; $this->image->allows( 'get_gallery_image' ) - ->with( 123 ) - ->andReturn( $image_meta ); + ->with( 123 ) + ->andReturn( $image_meta ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -507,20 +510,20 @@ public function test_find_alternative_image_from_post_content() { $this->indexable->orm = Mockery::mock( ORM::class ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + ->with( 'object_id' ) + ->andReturn( 123 ); $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( false ); + ->with( 123 ) + ->andReturn( false ); $this->image->allows( 'get_gallery_image' ) - ->with( 123 ) - ->andReturn( false ); + ->with( 123 ) + ->andReturn( false ); $image_meta = [ 'width' => 640, @@ -535,8 +538,8 @@ public function test_find_alternative_image_from_post_content() { ]; $this->image->allows( 'get_post_content_image' ) - ->with( 123 ) - ->andReturn( $image_meta ); + ->with( 123 ) + ->andReturn( $image_meta ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -559,24 +562,24 @@ public function test_find_alternative_image_no_image() { $this->indexable->orm = Mockery::mock( ORM::class ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + ->with( 'object_id' ) + ->andReturn( 123 ); $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( false ); + ->with( 123 ) + ->andReturn( false ); $this->image->allows( 'get_gallery_image' ) - ->with( 123 ) - ->andReturn( false ); + ->with( 123 ) + ->andReturn( false ); $this->image->allows( 'get_post_content_image' ) - ->with( 123 ) - ->andReturn( false ); + ->with( 123 ) + ->andReturn( false ); $this->assertFalse( $this->instance->find_alternative_image( $this->indexable ) ); } @@ -672,43 +675,43 @@ public function test_get_keyword_score_no_keyword() { /** * Tests is_public for when the post is protected. * - * @covers ::is_accessible_post + * @covers ::is_public */ public function test_is_public_post_protected() { $this->indexable->is_protected = true; - $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); + $this->assertFalse( $this->instance->is_public( $this->indexable ) ); } /** * Tests is_public for when the post is noindex. * - * @covers ::is_accessible_post + * @covers ::is_public */ public function test_is_public_post_noindex() { $this->indexable->is_protected = false; $this->indexable->is_robots_noindex = true; - $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); + $this->assertFalse( $this->instance->is_public( $this->indexable ) ); } /** * Tests is_public for when the post is an attachment. * - * @covers ::is_accessible_post + * @covers ::is_public */ public function test_is_public_post_is_attachment() { $this->indexable->is_protected = false; $this->indexable->is_robots_noindex = false; $this->indexable->object_sub_type = 'attachment'; - $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); + $this->assertFalse( $this->instance->is_public( $this->indexable ) ); } /** * Tests is_public for when the post status is not public. * - * @covers ::is_accessible_post + * @covers ::is_public */ public function test_is_public_post_status_is_not_public() { $this->indexable->is_protected = false; @@ -716,15 +719,15 @@ public function test_is_public_post_status_is_not_public() { $this->indexable->object_sub_type = 'post'; $this->indexable->post_status = 'private'; - $this->post->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); + $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - $this->assertFalse( $this->instance->is_accessible_post( $this->indexable ) ); + $this->assertFalse( $this->instance->is_public( $this->indexable ) ); } /** * Tests is_public for when the post noindex is false. * - * @covers ::is_accessible_post + * @covers ::is_public */ public function test_is_public_post_noindex_false() { $this->indexable->is_protected = false; @@ -732,15 +735,15 @@ public function test_is_public_post_noindex_false() { $this->indexable->object_sub_type = 'post'; $this->indexable->post_status = 'publish'; - $this->post->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); + $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - $this->assertTrue( $this->instance->is_accessible_post( $this->indexable ) ); + $this->assertTrue( $this->instance->is_public( $this->indexable ) ); } /** * Tests is_public for when the post noindex is null. * - * @covers ::is_accessible_post + * @covers ::is_public */ public function test_is_public_post_noindex_null() { $this->indexable->is_protected = false; @@ -748,9 +751,9 @@ public function test_is_public_post_noindex_null() { $this->indexable->object_sub_type = 'post'; $this->indexable->post_status = 'publish'; - $this->post->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); + $this->post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); - $this->assertNull( $this->instance->is_accessible_post( $this->indexable ) ); + $this->assertNull( $this->instance->is_public( $this->indexable ) ); } /** @@ -786,96 +789,18 @@ public function test_is_public_attachment_with_post_parent() { $this->assertNull( $this->instance->is_public_attachment( $this->indexable ) ); } - /** - * Tests has_public_posts for when the indexable does not represent an attachment. - * - * @covers ::has_public_posts - */ - public function test_has_public_posts_no_attachment() { - $this->indexable->object_sub_type = 'post'; - - $this->assertNull( $this->instance->has_public_posts( $this->indexable ) ); - } - - /** - * Tests has_public_posts for when the attachment does not have a post parent. - * - * @covers ::has_public_posts - */ - public function test_has_public_posts_attachment_no_parent() { - $this->indexable->object_sub_type = 'attachment'; - $this->indexable->post_parent = 0; - - $this->assertFalse( $this->instance->has_public_posts( $this->indexable ) ); - } - - /** - * Tests has_public_posts for when the attachment does not have the post status inherit. - * - * @covers ::has_public_posts - */ - public function test_has_public_posts_attachment_no_inherit() { - $this->indexable->object_sub_type = 'attachment'; - $this->indexable->post_parent = 1; - $this->indexable->post_status = 'private'; - - $this->assertFalse( $this->instance->has_public_posts( $this->indexable ) ); - } - - /** - * Tests has_public_posts for when the attachment has a post parent. - * - * @covers ::has_public_posts - */ - public function test_has_public_posts_attachment_with_post_parent() { - $this->indexable->object_sub_type = 'attachment'; - $this->indexable->post_parent = 1; - $this->indexable->post_status = 'inherit'; - - $post_parent_indexable = Mockery::mock(); - $post_parent_indexable->is_public = true; - - $this->indexable_repository->expects( 'find_by_id_and_type' ) - ->once() - ->with( 1, 'post' ) - ->andReturn( $post_parent_indexable ); - - $this->assertTrue( $this->instance->has_public_posts( $this->indexable ) ); - } - - /** - * Tests has_public_posts for when the attachment has a post parent but the ORM throws an false. - * - * @covers ::has_public_posts - */ - public function test_has_public_posts_attachment_with_post_parent_false() { - $this->indexable->object_sub_type = 'attachment'; - $this->indexable->post_parent = 1; - $this->indexable->post_status = 'inherit'; - - $post_parent_indexable = Mockery::mock(); - $post_parent_indexable->is_public = true; - - $this->indexable_repository->expects( 'find_by_id_and_type' ) - ->once() - ->with( 1, 'post' ) - ->andReturn( false ); - - $this->assertFalse( $this->instance->has_public_posts( $this->indexable ) ); - } - /** * Tests that build throws an exception when no post could be found. * * @covers ::build */ public function test_build_term_null() { - $this->post + $this->post_helper ->expects( 'is_post_indexable' ) ->with( 1 ) ->andReturn( true ); - $this->post->expects( 'get_post' )->once()->with( 1 )->andReturn( null ); + $this->post_helper->expects( 'get_post' )->once()->with( 1 )->andReturn( null ); $this->expectException( Post_Not_Found_Exception::class ); @@ -892,23 +817,23 @@ public function test_build_term_null() { public function test_build_post_type_excluded() { $post_id = 1; - $this->post + $this->post_helper ->expects( 'is_post_indexable' ) ->with( $post_id ) ->andReturn( true ); - $this->post->expects( 'get_post' ) - ->once() - ->with( $post_id ) - ->andReturn( - (object) [ - 'post_type' => 'excluded_post_type', - ] - ); + $this->post_helper->expects( 'get_post' ) + ->once() + ->with( $post_id ) + ->andReturn( + (object) [ + 'post_type' => 'excluded_post_type', + ] + ); $this->post_type_helper->expects( 'is_excluded' ) - ->once() - ->andReturnTrue(); + ->once() + ->andReturnTrue(); self::assertFalse( $this->instance->build( $post_id, false ) ); } diff --git a/tests/unit/builders/indexable-post-type-archive-builder-test.php b/tests/unit/builders/indexable-post-type-archive-builder-test.php index 9b85d64b121..b15918cade6 100644 --- a/tests/unit/builders/indexable-post-type-archive-builder-test.php +++ b/tests/unit/builders/indexable-post-type-archive-builder-test.php @@ -8,6 +8,7 @@ use Yoast\WP\SEO\Builders\Indexable_Post_Type_Archive_Builder; use Yoast\WP\SEO\Helpers\Options_Helper; use Yoast\WP\SEO\Helpers\Post_Helper; +use Yoast\WP\SEO\Helpers\Post_Type_Helper; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Tests\Unit\TestCase; use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions; @@ -15,13 +16,13 @@ /** * Class Indexable_Author_Test. * - * @group indexables - * @group builders + * @group indexables + * @group builders * * @coversDefaultClass \Yoast\WP\SEO\Builders\Indexable_Author_Builder * @covers \Yoast\WP\SEO\Builders\Indexable_Post_Builder * - * @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded + * @phpcs :disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded */ class Indexable_Post_Type_Archive_Builder_Test extends TestCase { @@ -47,11 +48,21 @@ public function test_build() { $post_helper = Mockery::mock( Post_Helper::class ); $post_helper->expects( 'get_public_post_statuses' )->once()->andReturn( [ 'publish' ] ); + $post_type_helper = Mockery::mock( Post_Type_Helper::class ); + $post_type_helper + ->expects( 'has_publicly_viewable_archive' ) + ->once() + ->with( 'my-post-type' ) + ->andReturn( true ); + $wpdb = Mockery::mock( 'wpdb' ); $wpdb->posts = 'wp_posts'; $wpdb->expects( 'prepare' )->once()->with( " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$wpdb->posts} AS p WHERE p.post_status IN (%s) AND p.post_password = '' @@ -61,8 +72,9 @@ public function test_build() { )->andReturn( 'PREPARED_QUERY' ); $wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', + 'number_of_public_posts' => 6, + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at' => '1234-12-12 00:00:00', ] ); @@ -76,15 +88,22 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'description', 'my_post_type_meta_description' ); $indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', false ); $indexable_mock->orm->expects( 'set' )->with( 'is_public', true ); + $indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', true ); + $indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 6 ); + $indexable_mock->orm->expects( 'get' )->with( 'is_robots_noindex' )->andReturnFalse(); + $indexable_mock->orm->expects( 'get' )->with( 'object_sub_type' )->andReturn( 'my-post-type' ); Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'version', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + + Monkey\Functions\expect( '_deprecated_argument' ); + Monkey\Functions\expect( 'esc_html' ); - $builder = new Indexable_Post_Type_Archive_Builder( $options_mock, $versions, $post_helper, $wpdb ); + $builder = new Indexable_Post_Type_Archive_Builder( $options_mock, $versions, $post_helper, $post_type_helper, $wpdb ); $builder->build( 'my-post-type', $indexable_mock ); } } diff --git a/tests/unit/builders/indexable-system-page-builder-test.php b/tests/unit/builders/indexable-system-page-builder-test.php index c1c1f717582..22e4b6e720b 100644 --- a/tests/unit/builders/indexable-system-page-builder-test.php +++ b/tests/unit/builders/indexable-system-page-builder-test.php @@ -39,10 +39,12 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'object_sub_type', 'search-result' ); $indexable_mock->orm->expects( 'set' )->with( 'title', 'search_title' ); $indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', true ); + $indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', true ); + $indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 0 ); Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); - $indexable_mock->orm->expects( 'set' )->with( 'version', 1 ); + $indexable_mock->orm->expects( 'set' )->with( 'version', 2 ); $builder = new Indexable_System_Page_Builder( $options_mock, new Indexable_Builder_Versions() ); $builder->build( 'search-result', $indexable_mock ); diff --git a/tests/unit/builders/indexable-term-builder-test.php b/tests/unit/builders/indexable-term-builder-test.php index 02dc49728b0..adb9c9d38a2 100644 --- a/tests/unit/builders/indexable-term-builder-test.php +++ b/tests/unit/builders/indexable-term-builder-test.php @@ -4,6 +4,7 @@ use Brain\Monkey; use Mockery; +use wpdb; use Yoast\WP\Lib\ORM; use Yoast\WP\SEO\Builders\Indexable_Term_Builder; use Yoast\WP\SEO\Exceptions\Indexable\Invalid_Term_Exception; @@ -21,8 +22,8 @@ /** * Class Indexable_Term_Builder_Test. * - * @group indexables - * @group builders + * @group indexables + * @group builders * * @coversDefaultClass \Yoast\WP\SEO\Builders\Indexable_Term_Builder * @covers \Yoast\WP\SEO\Builders\Indexable_Term_Builder @@ -150,19 +151,23 @@ protected function set_indexable_set_expectations( $indexable_mock, $expectation * @param Mockery\Mock|Indexable $indexable_mock The mocked indexable. */ protected function twitter_image_set_by_user( $indexable_mock ) { - $indexable_mock->orm->shouldReceive( 'get' ) + $indexable_mock->orm + ->shouldReceive( 'get' ) ->with( 'twitter_image' ) ->andReturn( 'twitter-image' ); - $indexable_mock->orm->shouldReceive( 'get' ) + $indexable_mock->orm + ->shouldReceive( 'get' ) ->with( 'twitter_image_id' ) ->andReturn( 'twitter-image-id' ); - $this->twitter_image->shouldReceive( 'get_by_id' ) + $this->twitter_image + ->shouldReceive( 'get_by_id' ) ->with( 'twitter-image-id' ) ->andReturn( 'twitter_image' ); - $indexable_mock->orm->shouldReceive( 'get' ) + $indexable_mock->orm + ->shouldReceive( 'get' ) ->with( 'twitter_image_source' ) ->andReturn( 'set-by-user' ); } @@ -174,20 +179,24 @@ protected function twitter_image_set_by_user( $indexable_mock ) { * @param array $image_meta The mocked meta data of the image. */ protected function open_graph_image_set_by_user( $indexable_mock, $image_meta ) { - $indexable_mock->orm->shouldReceive( 'get' ) + $indexable_mock->orm + ->shouldReceive( 'get' ) ->with( 'open_graph_image' ) ->andReturn( 'open-graph-image' ); - $indexable_mock->orm->shouldReceive( 'get' ) + $indexable_mock->orm + ->shouldReceive( 'get' ) ->twice() ->with( 'open_graph_image_id' ) ->andReturn( 'open-graph-image-id' ); - $indexable_mock->orm->shouldReceive( 'get' ) + $indexable_mock->orm + ->shouldReceive( 'get' ) ->with( 'open_graph_image_source' ) ->andReturn( 'set-by-user' ); - $this->open_graph_image->shouldReceive( 'get_image_by_id' ) + $this->open_graph_image + ->shouldReceive( 'get_image_by_id' ) ->with( 'open-graph-image-id' ) ->andReturn( $image_meta ); } @@ -220,8 +229,10 @@ public function test_build() { Monkey\Functions\expect( 'get_term' )->once()->with( 1 )->andReturn( $term ); Monkey\Functions\expect( 'get_term_link' )->once()->with( $term, 'category' )->andReturn( 'https://example.org/category/1' ); Monkey\Functions\expect( 'is_wp_error' )->twice()->andReturn( false ); + Monkey\Functions\expect( 'is_taxonomy_viewable' )->once()->with( 'category' )->andReturn( true ); - $this->taxonomy->expects( 'get_term_meta' ) + $this->taxonomy + ->expects( 'get_term_meta' ) ->once() ->with( $term ) ->andReturn( @@ -250,7 +261,10 @@ public function test_build() { $this->wpdb->expects( 'prepare' )->once()->with( " - SELECT MAX(p.post_modified_gmt) AS last_modified, MIN(p.post_date_gmt) AS published_at + SELECT + COUNT(p.ID) as number_of_public_posts, + MAX(p.post_modified_gmt) AS most_recent_last_modified, + MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p INNER JOIN {$this->wpdb->term_relationships} AS term_rel ON term_rel.object_id = p.ID @@ -265,8 +279,9 @@ public function test_build() { )->andReturn( 'PREPARED_QUERY' ); $this->wpdb->expects( 'get_row' )->once()->with( 'PREPARED_QUERY' )->andReturn( (object) [ - 'last_modified' => '1234-12-12 00:00:00', - 'published_at' => '1234-12-12 00:00:00', + 'number_of_public_posts' => 10, + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at' => '1234-12-12 00:00:00', ] ); @@ -296,6 +311,8 @@ public function test_build() { 'is_robots_noarchive' => null, 'is_robots_noimageindex' => null, 'is_robots_nosnippet' => null, + 'is_public' => false, + 'is_publicly_viewable' => true, 'primary_focus_keyword' => 'focuskeyword', 'primary_focus_keyword_score' => 75, 'readability_score' => 50, @@ -335,10 +352,14 @@ public function test_build() { $this->twitter_image_set_by_user( $indexable_mock ); // We expect the open graph image, its source and its metadata to be set. - $indexable_mock->orm->expects( 'set' )->with( 'open_graph_image_source', 'set-by-user' ); - $indexable_mock->orm->expects( 'set' ) + $indexable_mock->orm + ->expects( 'set' ) + ->with( 'open_graph_image_source', 'set-by-user' ); + $indexable_mock->orm + ->expects( 'set' ) ->with( 'open_graph_image', 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg' ); - $indexable_mock->orm->expects( 'set' ) + $indexable_mock->orm + ->expects( 'set' ) // phpcs:ignore Yoast.Yoast.AlternativeFunctions.json_encode_json_encodeWithAdditionalParams -- Test code, mocking WP. ->with( 'open_graph_image_meta', \json_encode( $image_meta, ( \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES ) ) ); @@ -350,12 +371,19 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->once()->with( 'breadcrumb_title', null ); $indexable_mock->orm->expects( 'get' )->twice()->with( 'is_robots_noindex' )->andReturn( true ); - $indexable_mock->orm->expects( 'set' )->once()->with( 'is_public', false ); + + $indexable_mock->orm->expects( 'get' )->with( 'object_id' )->andReturn( 1 ); + $indexable_mock->orm->expects( 'get' )->with( 'object_sub_type' )->andReturn( 'category' ); + + + Monkey\Functions\expect( '_deprecated_argument' ); + Monkey\Functions\expect( 'esc_html' ); Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); - $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 00:00:00' ); + $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); + $indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 10 ); $this->instance->build( 1, $indexable_mock ); } @@ -463,14 +491,14 @@ public function test_find_alternative_image_content_image() { $object_id = 123; $indexable_mock->orm->expects( 'get' ) - ->with( 'object_id' ) - ->andReturn( $object_id ); + ->with( 'object_id' ) + ->andReturn( $object_id ); $image = 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg'; $this->image->expects( 'get_term_content_image' ) - ->with( $object_id ) - ->andReturn( $image ); + ->with( $object_id ) + ->andReturn( $image ); $expected = [ 'image' => $image, @@ -488,12 +516,12 @@ public function test_find_alternative_image_no_content_image() { $object_id = 123; $indexable_mock->orm->expects( 'get' ) - ->with( 'object_id' ) - ->andReturn( $object_id ); + ->with( 'object_id' ) + ->andReturn( $object_id ); $this->image->expects( 'get_term_content_image' ) - ->with( $object_id ) - ->andReturn( null ); + ->with( $object_id ) + ->andReturn( null ); $this->assertFalse( $this->instance->find_alternative_image( $indexable_mock ) ); } diff --git a/tests/unit/doubles/builders/indexable-post-builder-double.php b/tests/unit/doubles/builders/indexable-post-builder-double.php index 09f8debf842..752b6161d75 100644 --- a/tests/unit/doubles/builders/indexable-post-builder-double.php +++ b/tests/unit/doubles/builders/indexable-post-builder-double.php @@ -18,8 +18,8 @@ class Indexable_Post_Builder_Double extends Indexable_Post_Builder { * * @return bool|null Whether or not the post type is public. Null if no override is set. */ - public function is_accessible_post( $indexable ) { - return parent::is_accessible_post( $indexable ); + public function is_public( $indexable ) { + return parent::is_public( $indexable ); } /** diff --git a/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php b/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php index 404a9036cb2..b138fba7e06 100644 --- a/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php +++ b/tests/unit/doubles/integrations/watchers/indexable-post-watcher-double.php @@ -11,15 +11,6 @@ */ class Indexable_Post_Watcher_Double extends Indexable_Post_Watcher { - /** - * Updates the has_public_posts when the post indexable is built. - * - * @param Indexable $indexable The indexable to check. - */ - public function update_number_of_public_posts( $indexable ) { - parent::update_number_of_public_posts( $indexable ); - } - /** * Updates the relations on post save or post status change. * diff --git a/tests/unit/helpers/author-archive-helper-test.php b/tests/unit/helpers/author-archive-helper-test.php index f11545b7b51..5fd053bbf5d 100644 --- a/tests/unit/helpers/author-archive-helper-test.php +++ b/tests/unit/helpers/author-archive-helper-test.php @@ -49,39 +49,4 @@ public function test_get_author_archive_post_types_apply_filter() { $this->assertEquals( $expected, $this->instance->get_author_archive_post_types() ); } - - /** - * Tests that true is returned when the author has a public post. - * - * @covers ::author_has_public_posts - */ - public function test_author_has_public_posts_with_public_post() { - $this->instance->expects( 'author_has_a_public_post' )->once()->with( 1 )->andReturnTrue(); - - $this->assertTrue( $this->instance->author_has_public_posts( 1 ) ); - } - - /** - * Tests that null is returned when the author has a post without noindex override. - * - * @covers ::author_has_public_posts - */ - public function test_author_has_public_posts_with_post_without_override() { - $this->instance->expects( 'author_has_a_public_post' )->once()->with( 1 )->andReturnFalse(); - $this->instance->expects( 'author_has_a_post_with_is_public_null' )->once()->with( 1 )->andReturnTrue(); - - $this->assertNull( $this->instance->author_has_public_posts( 1 ) ); - } - - /** - * Tests that false is returned when the author has no public posts and no posts without an override. - * - * @covers ::author_has_public_posts - */ - public function test_author_has_public_posts_without_public_or_override_posts() { - $this->instance->expects( 'author_has_a_public_post' )->once()->with( 1 )->andReturnFalse(); - $this->instance->expects( 'author_has_a_post_with_is_public_null' )->once()->with( 1 )->andReturnFalse(); - - $this->assertFalse( $this->instance->author_has_public_posts( 1 ) ); - } } diff --git a/tests/unit/helpers/robots-helper-test.php b/tests/unit/helpers/robots-helper-test.php index 57d71f72f3f..d96c3aa7fc0 100644 --- a/tests/unit/helpers/robots-helper-test.php +++ b/tests/unit/helpers/robots-helper-test.php @@ -3,6 +3,8 @@ namespace Yoast\WP\SEO\Tests\Unit\Helpers; use Brain\Monkey; +use Mockery\MockInterface; +use Yoast\WP\SEO\Helpers\Options_Helper; use Yoast\WP\SEO\Helpers\Robots_Helper; use Yoast\WP\SEO\Tests\Unit\TestCase; @@ -22,13 +24,20 @@ class Robots_Helper_Test extends TestCase { */ private $instance; + /** + * A helper class that manages options. + * + * @var Options_Helper|MockInterface + */ + protected $options_helper; + /** * Sets up the test class. */ protected function set_up() { parent::set_up(); - - $this->instance = new Robots_Helper(); + $this->options_helper = \Mockery::mock( Options_Helper::class ); + $this->instance = new Robots_Helper( $this->options_helper ); } /** diff --git a/tests/unit/integrations/watchers/indexable-post-watcher-test.php b/tests/unit/integrations/watchers/indexable-post-watcher-test.php index 93165e8f73f..546a6a3e21a 100644 --- a/tests/unit/integrations/watchers/indexable-post-watcher-test.php +++ b/tests/unit/integrations/watchers/indexable-post-watcher-test.php @@ -2,10 +2,8 @@ namespace Yoast\WP\SEO\Tests\Unit\Integrations\Watchers; -use Brain\Monkey; use Exception; use Mockery; -use WP_Post; use Yoast\WP\Lib\ORM; use Yoast\WP\SEO\Builders\Indexable_Builder; use Yoast\WP\SEO\Builders\Indexable_Link_Builder; @@ -19,6 +17,7 @@ use Yoast\WP\SEO\Tests\Unit\Doubles\Integrations\Watchers\Indexable_Post_Watcher_Double; use Yoast\WP\SEO\Tests\Unit\Doubles\Models\Indexable_Mock; use Yoast\WP\SEO\Tests\Unit\TestCase; +use function Brain\Monkey\Functions\expect; /** * Class Indexable_Post_Watcher_Test. @@ -55,7 +54,7 @@ class Indexable_Post_Watcher_Test extends TestCase { /** * The link builder. * - * @var Indexable_Link_Builder + * @var Mockery\MockInterface|Indexable_Link_Builder */ protected $link_builder; @@ -69,7 +68,7 @@ class Indexable_Post_Watcher_Test extends TestCase { /** * Represents the class we are testing. * - * @var Indexable_Post_Watcher_Double + * @var Mockery\MockInterface|Indexable_Post_Watcher_Double */ private $instance; @@ -164,19 +163,12 @@ public function test_delete_indexable() { $this->hierarchy_repository->expects( 'clear_ancestors' )->once()->with( $id )->andReturn( true ); $this->link_builder->expects( 'delete' )->once()->with( $indexable ); - $this->post->expects( 'get_post' )->once()->with( $id )->andReturn( $post ); - $this->instance ->expects( 'update_relations' ) ->with( $post ) ->once(); - $this->instance - ->expects( 'update_has_public_posts' ) - ->with( $indexable ) - ->once(); - - $this->instance->delete_indexable( $id ); + $this->instance->delete_indexable( $id, $post ); } /** @@ -185,11 +177,18 @@ public function test_delete_indexable() { * @covers ::delete_indexable */ public function test_delete_indexable_does_not_exist() { - $id = 1; + $id = 1; + $post = (object) []; $this->repository->expects( 'find_by_id_and_type' )->once()->with( $id, 'post', false )->andReturn( false ); - $this->instance->delete_indexable( $id ); + + $this->repository->expects( 'find_by_id_and_type' )->never(); + $this->hierarchy_repository->expects( 'clear_ancestors' )->never(); + $this->link_builder->expects( 'delete' )->never(); + $this->instance->expects( 'update_relations' )->never(); + + $this->instance->delete_indexable( $id, $post ); } /** @@ -346,67 +345,6 @@ public function test_updated_indexable_non_post() { $this->instance->updated_indexable( $updated_indexable, $old_indexable ); } - /** - * Tests that update_has_public_posts updates the author archive too. - * - * @covers ::update_number_of_public_posts - */ - public function test_update_has_public_posts_with_post() { - $post_indexable = Mockery::mock(); - $post_indexable->object_id = 33; - $post_indexable->object_sub_type = 'post'; - $post_indexable->author_id = 1; - $post_indexable->is_public = null; - - $author_indexable = Mockery::mock( Indexable_Mock::class ); - $author_indexable->object_id = 11; - - $this->repository - ->expects( 'find_by_id_and_type' ) - ->with( 1, 'user' ) - ->once() - ->andReturn( $author_indexable ); - - $this->author_archive - ->expects( 'author_has_public_posts' ) - ->with( 11 ) - ->once() - ->andReturn( true ); - $author_indexable->expects( 'save' )->once(); - - $this->post->expects( 'update_has_public_posts_on_attachments' )->once()->with( 33, null )->andReturnTrue(); - - $this->instance->update_number_of_public_posts( $post_indexable ); - - $this->assertTrue( $author_indexable->has_public_posts ); - } - - /** - * Tests that update_has_public_posts updates the author archive . - * - * @covers ::update_number_of_public_posts - */ - public function test_update_has_public_posts_with_post_throwing_exceptions() { - $post_indexable = Mockery::mock(); - $post_indexable->object_id = 33; - $post_indexable->object_sub_type = 'post'; - $post_indexable->author_id = 1; - $post_indexable->is_public = null; - - $this->repository->expects( 'find_by_id_and_type' ) - ->with( 1, 'user' ) - ->once() - ->andThrow( new Exception( 'an error' ) ); - $this->author_archive->expects( 'author_has_public_posts' )->never(); - $this->post->expects( 'update_has_public_posts_on_attachments' ) - ->once() - ->with( 33, null ) - ->andReturnTrue(); - $this->logger->expects( 'log' )->once()->with( 'error', 'an error' ); - - $this->instance->update_number_of_public_posts( $post_indexable ); - } - /** * Tests the routine for updating the relations. * @@ -420,11 +358,12 @@ public function test_update_relations() { 'post_modified_gmt' => '1234-12-12 12:12:12', ]; + expect( 'current_time' )->with( 'mysql' )->andReturn( '1234-12-12 12:12:12' ); + $indexable = Mockery::mock( Indexable_Mock::class ); $indexable->orm = Mockery::mock( ORM::class ); - $indexable->orm->expects( 'get' )->with( 'object_last_modified' )->andReturn( '1234-12-12 00:00:00' ); + $indexable->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 12:12:12' ); - $indexable->expects( 'save' )->once(); $this->instance ->expects( 'get_related_indexables' ) @@ -432,6 +371,11 @@ public function test_update_relations() { ->with( $post ) ->andReturn( [ $indexable ] ); + $this->builder + ->expects( 'recalculate_aggregates' ) + ->once() + ->with( $indexable ); + $this->instance->update_relations( $post ); } @@ -447,12 +391,18 @@ public function test_update_relations_with_no_indexables_found() { 'ID' => 1, ]; + expect( 'current_time' )->with( 'mysql' )->andReturn( '1234-12-12 12:12:12' ); + $this->instance ->expects( 'get_related_indexables' ) ->once() ->with( $post ) ->andReturn( [] ); + $this->builder + ->expects( 'recalculate_aggregates' ) + ->never(); + $this->instance->update_relations( $post ); } @@ -468,7 +418,7 @@ public function test_get_related_indexables() { 'ID' => 1, ]; - Monkey\Functions\expect( 'get_post_taxonomies' ) + expect( 'get_post_taxonomies' ) ->once() ->with( 1 ) ->andReturn( @@ -478,17 +428,17 @@ public function test_get_related_indexables() { ] ); - Monkey\Functions\expect( 'is_taxonomy_viewable' ) + expect( 'is_taxonomy_viewable' ) ->once() ->with( 'taxonomy' ) ->andReturn( true ); - Monkey\Functions\expect( 'is_taxonomy_viewable' ) + expect( 'is_taxonomy_viewable' ) ->once() ->with( 'another-taxonomy' ) ->andReturn( true ); - Monkey\Functions\expect( 'get_the_terms' ) + expect( 'get_the_terms' ) ->once() ->with( 1, 'taxonomy' ) ->andReturn( @@ -502,12 +452,12 @@ public function test_get_related_indexables() { ] ); - Monkey\Functions\expect( 'get_the_terms' ) + expect( 'get_the_terms' ) ->once() ->with( 1, 'another-taxonomy' ) ->andReturnNull(); - Monkey\Functions\expect( 'wp_list_pluck' ) + expect( 'wp_list_pluck' ) ->once() ->with( [ diff --git a/tests/unit/repositories/indexable-repository-test.php b/tests/unit/repositories/indexable-repository-test.php index a37e8d7a55e..fa92a6762e4 100644 --- a/tests/unit/repositories/indexable-repository-test.php +++ b/tests/unit/repositories/indexable-repository-test.php @@ -7,6 +7,7 @@ use Yoast\WP\Lib\ORM; use Yoast\WP\SEO\Builders\Indexable_Builder; use Yoast\WP\SEO\Helpers\Current_Page_Helper; +use Yoast\WP\SEO\Helpers\Robots_Helper; use Yoast\WP\SEO\Loggers\Logger; use Yoast\WP\SEO\Models\Indexable; use Yoast\WP\SEO\Repositories\Indexable_Hierarchy_Repository; @@ -74,6 +75,14 @@ class Indexable_Repository_Test extends TestCase { */ protected $version_manager; + + /** + * A helper class for robots meta tags. + * + * @var Mockery\MockInterface|Robots_Helper + */ + protected $robots_helper; + /** * Setup the test. */ @@ -86,6 +95,7 @@ protected function set_up() { $this->hierarchy_repository = Mockery::mock( Indexable_Hierarchy_Repository::class ); $this->wpdb = Mockery::mock( wpdb::class ); $this->version_manager = Mockery::mock( Indexable_Version_Manager::class ); + $this->robots_helper = Mockery::mock(Robots_Helper::class); $this->instance = Mockery::mock( Indexable_Repository::class, [ @@ -95,6 +105,7 @@ protected function set_up() { $this->hierarchy_repository, $this->wpdb, $this->version_manager, + $this->robots_helper ] )->makePartial(); } From ccf7f9b1547497e597fb40a52ee6b5d3df97489f Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 15:01:09 +0100 Subject: [PATCH 22/53] Remove resolved todo comment --- .../indexable-post-type-archive-builder.php | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 8dad2d15eea..f34914eb9a1 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -56,10 +56,11 @@ class Indexable_Post_Type_Archive_Builder { /** * Indexable_Post_Type_Archive_Builder constructor. * - * @param Options_Helper $options The options helper. - * @param Indexable_Builder_Versions $versions The latest version of each Indexable builder. - * @param Post_Helper $post_helper The post helper. - * @param wpdb $wpdb The WPDB instance. + * @param Options_Helper $options The options helper. + * @param Indexable_Builder_Versions $versions The latest version of each Indexable builder. + * @param Post_Helper $post_helper The post helper. + * @param Post_Type_Helper $post_type_helper The post type helper. + * @param wpdb $wpdb The WPDB instance. */ public function __construct( Options_Helper $options, @@ -68,11 +69,11 @@ public function __construct( Post_Type_Helper $post_type_helper, wpdb $wpdb ) { - $this->options = $options; - $this->version = $versions->get_latest_version_for_type( 'post-type-archive' ); - $this->post_helper = $post_helper; + $this->options = $options; + $this->version = $versions->get_latest_version_for_type( 'post-type-archive' ); + $this->post_helper = $post_helper; $this->post_type_helper = $post_type_helper; - $this->wpdb = $wpdb; + $this->wpdb = $wpdb; } /** @@ -84,16 +85,15 @@ public function __construct( * @return Indexable The extended indexable. */ public function build( $post_type, Indexable $indexable ) { - $indexable->object_type = 'post-type-archive'; - $indexable->object_sub_type = $post_type; - $indexable->title = $this->options->get( 'title-ptarchive-' . $post_type ); - $indexable->description = $this->options->get( 'metadesc-ptarchive-' . $post_type ); - $indexable->breadcrumb_title = $this->get_breadcrumb_title( $post_type ); - $indexable->permalink = \get_post_type_archive_link( $post_type ); - $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); - $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); - $indexable->blog_id = \get_current_blog_id(); - // TODO Diede Does the watcher post type changes this? + $indexable->object_type = 'post-type-archive'; + $indexable->object_sub_type = $post_type; + $indexable->title = $this->options->get( 'title-ptarchive-' . $post_type ); + $indexable->description = $this->options->get( 'metadesc-ptarchive-' . $post_type ); + $indexable->breadcrumb_title = $this->get_breadcrumb_title( $post_type ); + $indexable->permalink = \get_post_type_archive_link( $post_type ); + $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); + $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); + $indexable->blog_id = \get_current_blog_id(); $indexable->is_publicly_viewable = $this->post_type_helper->has_publicly_viewable_archive( $post_type ); $indexable = $this->set_aggregate_values( $indexable ); From af7b2198fea7973824a29b8e713bb2703cd4673c Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 15:01:32 +0100 Subject: [PATCH 23/53] Rebuild date archive indexable when disabling date archives --- .../watchers/indexable-date-archive-watcher.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/integrations/watchers/indexable-date-archive-watcher.php b/src/integrations/watchers/indexable-date-archive-watcher.php index 9b77cb1fc07..b3367e55ac8 100644 --- a/src/integrations/watchers/indexable-date-archive-watcher.php +++ b/src/integrations/watchers/indexable-date-archive-watcher.php @@ -66,7 +66,13 @@ public function register_hooks() { * @return void */ public function check_option( $old_value, $new_value ) { - $relevant_keys = [ 'title-archive-wpseo', 'breadcrumbs-archiveprefix', 'metadesc-archive-wpseo', 'noindex-archive-wpseo' ]; + $relevant_keys = [ + 'title-archive-wpseo', + 'breadcrumbs-archiveprefix', + 'metadesc-archive-wpseo', + 'noindex-archive-wpseo', + 'disable-date', + ]; foreach ( $relevant_keys as $key ) { // If both values aren't set they haven't changed. From 510f9de8bb2a9f11831bbf326f9180e5deec1dd9 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 15:01:37 +0100 Subject: [PATCH 24/53] Fix cs --- .../indexable-author-builder-test.php | 9 +- .../builders/indexable-post-builder-test.php | 286 ++++++++++-------- .../builders/indexable-term-builder-test.php | 28 +- .../indexable-repository-test.php | 5 +- 4 files changed, 184 insertions(+), 144 deletions(-) diff --git a/tests/unit/builders/indexable-author-builder-test.php b/tests/unit/builders/indexable-author-builder-test.php index a14b7352b8e..c5f3f14522d 100644 --- a/tests/unit/builders/indexable-author-builder-test.php +++ b/tests/unit/builders/indexable-author-builder-test.php @@ -129,13 +129,14 @@ protected function set_up() { AND p.post_password = '' AND p.post_author = %d AND p.post_type IN (%s, %s) - ", ['publish', 1, 'post', 'my-cpt'] + ", + [ 'publish', 1, 'post', 'my-cpt' ] )->andReturn( 'PREPARED_QUERY' ); $this->wpdb->expects( 'get_row' )->with( 'PREPARED_QUERY' )->andReturn( (object) [ - 'number_of_public_posts' => '7', - 'most_recent_last_modified' => '1234-12-12 23:59:59', - 'first_published_at'=>'1234-12-12 00:00:00', + 'number_of_public_posts' => '7', + 'most_recent_last_modified' => '1234-12-12 23:59:59', + 'first_published_at' => '1234-12-12 00:00:00', ] ); diff --git a/tests/unit/builders/indexable-post-builder-test.php b/tests/unit/builders/indexable-post-builder-test.php index 9f6507dbf4b..420f4cf7575 100644 --- a/tests/unit/builders/indexable-post-builder-test.php +++ b/tests/unit/builders/indexable-post-builder-test.php @@ -98,7 +98,7 @@ protected function set_up() { $this->image = Mockery::mock( Image_Helper::class ); $this->open_graph_image = Mockery::mock( Open_Graph_Image_Helper::class ); $this->twitter_image = Mockery::mock( Twitter_Image_Helper::class ); - $this->post_helper = Mockery::mock( Post_Helper::class ); + $this->post_helper = Mockery::mock( Post_Helper::class ); $this->post_type_helper = Mockery::mock( Post_Type_Helper::class ); $this->instance = new Indexable_Post_Builder_Double( @@ -131,21 +131,25 @@ protected function set_indexable_set_expectations( $indexable_mock, $expectation * Mocks a Twitter image that has been set by the user. */ protected function twitter_image_set_by_user() { - $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'twitter_image' ) - ->andReturn( 'twitter-image' ); - - $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'twitter_image_id' ) - ->andReturn( 'twitter-image-id' ); - - $this->twitter_image->shouldReceive( 'get_by_id' ) - ->with( 'twitter-image-id' ) - ->andReturn( 'twitter_image' ); - - $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'twitter_image_source' ) - ->andReturn( 'set-by-user' ); + $this->indexable->orm + ->shouldReceive( 'get' ) + ->with( 'twitter_image' ) + ->andReturn( 'twitter-image' ); + + $this->indexable->orm + ->shouldReceive( 'get' ) + ->with( 'twitter_image_id' ) + ->andReturn( 'twitter-image-id' ); + + $this->twitter_image + ->shouldReceive( 'get_by_id' ) + ->with( 'twitter-image-id' ) + ->andReturn( 'twitter_image' ); + + $this->indexable->orm + ->shouldReceive( 'get' ) + ->with( 'twitter_image_source' ) + ->andReturn( 'set-by-user' ); } /** @@ -154,22 +158,26 @@ protected function twitter_image_set_by_user() { * @param array $image_meta The mocked meta data of the image. */ protected function open_graph_image_set_by_user( $image_meta ) { - $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'open_graph_image' ) - ->andReturn( 'open-graph-image' ); - - $this->indexable->orm->shouldReceive( 'get' ) - ->twice() - ->with( 'open_graph_image_id' ) - ->andReturn( 'open-graph-image-id' ); - - $this->indexable->orm->shouldReceive( 'get' ) - ->with( 'open_graph_image_source' ) - ->andReturn( 'set-by-user' ); - - $this->open_graph_image->shouldReceive( 'get_image_by_id' ) - ->with( 'open-graph-image-id' ) - ->andReturn( $image_meta ); + $this->indexable->orm + ->shouldReceive( 'get' ) + ->with( 'open_graph_image' ) + ->andReturn( 'open-graph-image' ); + + $this->indexable->orm + ->shouldReceive( 'get' ) + ->twice() + ->with( 'open_graph_image_id' ) + ->andReturn( 'open-graph-image-id' ); + + $this->indexable->orm + ->shouldReceive( 'get' ) + ->with( 'open_graph_image_source' ) + ->andReturn( 'set-by-user' ); + + $this->open_graph_image + ->shouldReceive( 'get_image_by_id' ) + ->with( 'open-graph-image-id' ) + ->andReturn( $image_meta ); } /** @@ -235,21 +243,22 @@ public function test_build() { ); Monkey\Functions\expect( 'maybe_unserialize' )->andReturnFirstArg(); - $this->post_helper->expects( 'get_post' ) - ->once() - ->with( 1 ) - ->andReturn( - (object) [ - 'post_content' => 'The content of the post', - 'post_type' => 'post', - 'post_status' => 'publish', - 'post_password' => '', - 'post_author' => '1', - 'post_parent' => '0', - 'post_date_gmt' => '1234-12-12 00:00:00', - 'post_modified_gmt' => '1234-12-12 00:00:00', - ] - ); + $this->post_helper + ->expects( 'get_post' ) + ->once() + ->with( 1 ) + ->andReturn( + (object) [ + 'post_content' => 'The content of the post', + 'post_type' => 'post', + 'post_status' => 'publish', + 'post_password' => '', + 'post_author' => '1', + 'post_parent' => '0', + 'post_date_gmt' => '1234-12-12 00:00:00', + 'post_modified_gmt' => '1234-12-12 00:00:00', + ] + ); $this->post_type_helper ->expects( 'is_excluded' ) @@ -340,12 +349,18 @@ public function test_build() { $this->twitter_image_set_by_user(); // We expect the open graph image, its source and its metadata to be set. - $this->indexable->orm->expects( 'set' )->with( 'open_graph_image_source', 'set-by-user' ); - $this->indexable->orm->expects( 'set' ) - ->with( 'open_graph_image', 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg' ); - $this->indexable->orm->expects( 'set' ) + $this->indexable->orm + ->expects( 'set' ) + ->with( 'open_graph_image_source', 'set-by-user' ); + + $this->indexable->orm + ->expects( 'set' ) + ->with( 'open_graph_image', 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg' ); + + $this->indexable->orm + ->expects( 'set' ) // phpcs:ignore Yoast.Yoast.AlternativeFunctions.json_encode_json_encodeWithAdditionalParams -- Test code, mocking WP. - ->with( 'open_graph_image_meta', \json_encode( $image_meta, ( \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES ) ) ); + ->with( 'open_graph_image_meta', \json_encode( $image_meta, ( \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES ) ) ); // We expect the twitter image and its source to be set. $this->indexable->orm->expects( 'set' )->with( 'twitter_image_source', 'set-by-user' ); @@ -366,7 +381,6 @@ public function test_build() { Monkey\Functions\expect( 'esc_html' ); Monkey\Functions\expect( 'is_post_publicly_viewable' )->andReturn( true ); - // Blog ID. Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); @@ -398,17 +412,20 @@ public function test_find_alternative_image_from_attachment() { $this->indexable = Mockery::mock( Indexable::class ); $this->indexable->orm = Mockery::mock( ORM::class ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'attachment' ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_sub_type' ) + ->andReturn( 'attachment' ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_id' ) + ->andReturn( 123 ); - $this->image->allows( 'is_valid_attachment' ) - ->with( 123 ) - ->andReturn( true ); + $this->image + ->allows( 'is_valid_attachment' ) + ->with( 123 ) + ->andReturn( true ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -429,17 +446,20 @@ public function test_find_alternative_image_from_featured_image() { $this->indexable = Mockery::mock( Indexable::class ); $this->indexable->orm = Mockery::mock( ORM::class ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_id' ) + ->andReturn( 123 ); - $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( 456 ); + $this->image + ->allows( 'get_featured_image_id' ) + ->with( 123 ) + ->andReturn( 456 ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -461,17 +481,20 @@ public function test_find_alternative_image_from_gallery() { $this->indexable = Mockery::mock( Indexable::class ); $this->indexable->orm = Mockery::mock( ORM::class ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_id' ) + ->andReturn( 123 ); - $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( false ); + $this->image + ->allows( 'get_featured_image_id' ) + ->with( 123 ) + ->andReturn( false ); $image_meta = [ 'width' => 640, @@ -485,9 +508,10 @@ public function test_find_alternative_image_from_gallery() { 'type' => 'image/jpeg', ]; - $this->image->allows( 'get_gallery_image' ) - ->with( 123 ) - ->andReturn( $image_meta ); + $this->image + ->allows( 'get_gallery_image' ) + ->with( 123 ) + ->andReturn( $image_meta ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -509,21 +533,25 @@ public function test_find_alternative_image_from_post_content() { $this->indexable = Mockery::mock( Indexable::class ); $this->indexable->orm = Mockery::mock( ORM::class ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_id' ) + ->andReturn( 123 ); - $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( false ); + $this->image + ->allows( 'get_featured_image_id' ) + ->with( 123 ) + ->andReturn( false ); - $this->image->allows( 'get_gallery_image' ) - ->with( 123 ) - ->andReturn( false ); + $this->image + ->allows( 'get_gallery_image' ) + ->with( 123 ) + ->andReturn( false ); $image_meta = [ 'width' => 640, @@ -537,9 +565,10 @@ public function test_find_alternative_image_from_post_content() { 'type' => 'image/jpeg', ]; - $this->image->allows( 'get_post_content_image' ) - ->with( 123 ) - ->andReturn( $image_meta ); + $this->image + ->allows( 'get_post_content_image' ) + ->with( 123 ) + ->andReturn( $image_meta ); $actual = $this->instance->find_alternative_image( $this->indexable ); @@ -561,25 +590,30 @@ public function test_find_alternative_image_no_image() { $this->indexable = Mockery::mock( Indexable::class ); $this->indexable->orm = Mockery::mock( ORM::class ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_sub_type' ) - ->andReturn( 'post' ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_sub_type' ) + ->andReturn( 'post' ); - $this->indexable->orm->allows( 'get' ) - ->with( 'object_id' ) - ->andReturn( 123 ); + $this->indexable->orm + ->allows( 'get' ) + ->with( 'object_id' ) + ->andReturn( 123 ); - $this->image->allows( 'get_featured_image_id' ) - ->with( 123 ) - ->andReturn( false ); + $this->image + ->allows( 'get_featured_image_id' ) + ->with( 123 ) + ->andReturn( false ); - $this->image->allows( 'get_gallery_image' ) - ->with( 123 ) - ->andReturn( false ); + $this->image + ->allows( 'get_gallery_image' ) + ->with( 123 ) + ->andReturn( false ); - $this->image->allows( 'get_post_content_image' ) - ->with( 123 ) - ->andReturn( false ); + $this->image + ->allows( 'get_post_content_image' ) + ->with( 123 ) + ->andReturn( false ); $this->assertFalse( $this->instance->find_alternative_image( $this->indexable ) ); } @@ -822,18 +856,20 @@ public function test_build_post_type_excluded() { ->with( $post_id ) ->andReturn( true ); - $this->post_helper->expects( 'get_post' ) - ->once() - ->with( $post_id ) - ->andReturn( - (object) [ - 'post_type' => 'excluded_post_type', - ] - ); - - $this->post_type_helper->expects( 'is_excluded' ) - ->once() - ->andReturnTrue(); + $this->post_helper + ->expects( 'get_post' ) + ->once() + ->with( $post_id ) + ->andReturn( + (object) [ + 'post_type' => 'excluded_post_type', + ] + ); + + $this->post_type_helper + ->expects( 'is_excluded' ) + ->once() + ->andReturnTrue(); self::assertFalse( $this->instance->build( $post_id, false ) ); } diff --git a/tests/unit/builders/indexable-term-builder-test.php b/tests/unit/builders/indexable-term-builder-test.php index adb9c9d38a2..cfd0fa271f8 100644 --- a/tests/unit/builders/indexable-term-builder-test.php +++ b/tests/unit/builders/indexable-term-builder-test.php @@ -490,15 +490,17 @@ public function test_find_alternative_image_content_image() { $indexable_mock->orm = Mockery::mock( ORM::class ); $object_id = 123; - $indexable_mock->orm->expects( 'get' ) - ->with( 'object_id' ) - ->andReturn( $object_id ); + $indexable_mock->orm + ->expects( 'get' ) + ->with( 'object_id' ) + ->andReturn( $object_id ); $image = 'http://basic.wordpress.test/wp-content/uploads/2020/07/WordPress5.jpg'; - $this->image->expects( 'get_term_content_image' ) - ->with( $object_id ) - ->andReturn( $image ); + $this->image + ->expects( 'get_term_content_image' ) + ->with( $object_id ) + ->andReturn( $image ); $expected = [ 'image' => $image, @@ -515,13 +517,15 @@ public function test_find_alternative_image_no_content_image() { $indexable_mock->orm = Mockery::mock( ORM::class ); $object_id = 123; - $indexable_mock->orm->expects( 'get' ) - ->with( 'object_id' ) - ->andReturn( $object_id ); + $indexable_mock->orm + ->expects( 'get' ) + ->with( 'object_id' ) + ->andReturn( $object_id ); - $this->image->expects( 'get_term_content_image' ) - ->with( $object_id ) - ->andReturn( null ); + $this->image + ->expects( 'get_term_content_image' ) + ->with( $object_id ) + ->andReturn( null ); $this->assertFalse( $this->instance->find_alternative_image( $indexable_mock ) ); } diff --git a/tests/unit/repositories/indexable-repository-test.php b/tests/unit/repositories/indexable-repository-test.php index fa92a6762e4..a1334226c2b 100644 --- a/tests/unit/repositories/indexable-repository-test.php +++ b/tests/unit/repositories/indexable-repository-test.php @@ -75,7 +75,6 @@ class Indexable_Repository_Test extends TestCase { */ protected $version_manager; - /** * A helper class for robots meta tags. * @@ -95,7 +94,7 @@ protected function set_up() { $this->hierarchy_repository = Mockery::mock( Indexable_Hierarchy_Repository::class ); $this->wpdb = Mockery::mock( wpdb::class ); $this->version_manager = Mockery::mock( Indexable_Version_Manager::class ); - $this->robots_helper = Mockery::mock(Robots_Helper::class); + $this->robots_helper = Mockery::mock( Robots_Helper::class ); $this->instance = Mockery::mock( Indexable_Repository::class, [ @@ -105,7 +104,7 @@ protected function set_up() { $this->hierarchy_repository, $this->wpdb, $this->version_manager, - $this->robots_helper + $this->robots_helper, ] )->makePartial(); } From ed46abb03320265dc185284989fab613264dccde Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 26 Nov 2021 15:45:46 +0100 Subject: [PATCH 25/53] Prevent deprecation warnings when (re)indexing --- lib/model.php | 32 +++++++++++++++++++ src/builders/indexable-author-builder.php | 2 +- .../indexable-date-archive-builder.php | 2 +- src/builders/indexable-post-builder.php | 3 +- .../indexable-post-type-archive-builder.php | 2 +- src/builders/indexable-term-builder.php | 2 +- .../indexable-author-builder-test.php | 5 +-- .../indexable-date-archive-builder-test.php | 5 +-- .../builders/indexable-post-builder-test.php | 4 +-- ...dexable-post-type-archive-builder-test.php | 5 +-- .../builders/indexable-term-builder-test.php | 6 +--- 11 files changed, 43 insertions(+), 25 deletions(-) diff --git a/lib/model.php b/lib/model.php index f5163daae13..34526c85c9b 100644 --- a/lib/model.php +++ b/lib/model.php @@ -558,6 +558,7 @@ public function __set( $property, $value ) { * @return void */ public function __unset( $property ) { + $property = $this->handleDeprecation( $property ); $this->orm->__unset( $property ); } @@ -587,6 +588,7 @@ public function __debugInfo() { * @return bool True when value is set. */ public function __isset( $property ) { + $property = $this->handleDeprecation( $property ); return $this->orm->__isset( $property ); } @@ -618,6 +620,36 @@ public function set( $property, $value = null ) { return $this; } + /** + * Setter method for model properties that are deprecated, but still need to updated while they wait to be removed. + * + * @param string|array $property The property to set. + * @param string|null $value The value to give. + * + * @return static Current object. + * + * @internal + */ + public function set_deprecated_property( $property, $value = null ) { + if ( ! array_key_exists( $property, $this->deprecated_columns ) ) { + return $this; + } + + if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { + $value = ( $value ) ? '1' : '0'; + } + if ( $value !== null && \in_array( $property, $this->int_columns, true ) ) { + $value = (string) $value; + } + if ( $value !== null && \in_array( $property, $this->float_columns, true ) ) { + $value = (string) $value; + } + + $this->orm->set( $property, $value ); + + return $this; + } + /** * Setter method, allows $model->set_expr('property', 'value') access to data. * diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index 26a34b41142..f5dbbea809d 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -87,8 +87,8 @@ public function build( $user_id, Indexable $indexable ) { $indexable->is_robots_noarchive = null; $indexable->is_robots_noimageindex = null; $indexable->is_robots_nosnippet = null; - $indexable->is_public = ( $indexable->is_robots_noindex ) ? false : null; $indexable->blog_id = \get_current_blog_id(); + $indexable->set_deprecated_property( 'is_public', ( $indexable->is_robots_noindex ) ? false : null ); $this->reset_social_images( $indexable ); $this->handle_social_images( $indexable ); diff --git a/src/builders/indexable-date-archive-builder.php b/src/builders/indexable-date-archive-builder.php index 465cf6eeb86..2d06514f54c 100644 --- a/src/builders/indexable-date-archive-builder.php +++ b/src/builders/indexable-date-archive-builder.php @@ -55,11 +55,11 @@ public function build( $indexable ) { $indexable->title = $this->options->get( 'title-archive-wpseo' ); $indexable->description = $this->options->get( 'metadesc-archive-wpseo' ); $indexable->is_robots_noindex = $this->options->get( 'noindex-archive-wpseo' ); - $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); $indexable->is_publicly_viewable = ! (bool) $this->options->get( 'disable-date' ); $indexable->blog_id = \get_current_blog_id(); $indexable->permalink = null; $indexable->version = $this->version; + $indexable->set_deprecated_property( 'is_public', (int) $indexable->is_robots_noindex !== 1 ); return $indexable; } diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 090d4fa2cb2..00844415357 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -145,11 +145,12 @@ public function build( $post_id, $indexable ) { $indexable->number_of_pages = $this->get_number_of_pages_for_post( $post ); $indexable->post_status = $post->post_status; $indexable->is_protected = $post->post_password !== ''; - $indexable->is_public = $this->is_public( $indexable ); $indexable->is_publicly_viewable = \is_post_publicly_viewable( $post ); $indexable->number_of_publicly_viewable_posts = 0; $indexable->blog_id = \get_current_blog_id(); + $indexable->set_deprecated_property( 'is_public', $this->is_public( $indexable ) ); + $indexable->schema_page_type = $this->get_meta_value( $post_id, 'schema_page_type' ); $indexable->schema_article_type = $this->get_meta_value( $post_id, 'schema_article_type' ); diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index f34914eb9a1..62aebb92dee 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -92,9 +92,9 @@ public function build( $post_type, Indexable $indexable ) { $indexable->breadcrumb_title = $this->get_breadcrumb_title( $post_type ); $indexable->permalink = \get_post_type_archive_link( $post_type ); $indexable->is_robots_noindex = (bool) $this->options->get( 'noindex-ptarchive-' . $post_type ); - $indexable->is_public = ( (int) $indexable->is_robots_noindex !== 1 ); $indexable->blog_id = \get_current_blog_id(); $indexable->is_publicly_viewable = $this->post_type_helper->has_publicly_viewable_archive( $post_type ); + $indexable->set_deprecated_property( 'is_public', ( (int) $indexable->is_robots_noindex !== 1 ) ); $indexable = $this->set_aggregate_values( $indexable ); diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 177a3f15017..baf1d409181 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -110,7 +110,7 @@ public function build( $term_id, $indexable ) { ); $indexable->is_robots_noindex = $this->get_noindex_value( $this->get_meta_value( 'wpseo_noindex', $term_meta ) ); - $indexable->is_public = ( $indexable->is_robots_noindex === null ) ? null : ! $indexable->is_robots_noindex; + $indexable->set_deprecated_property( 'is_public', ( $indexable->is_robots_noindex === null ) ? null : ! $indexable->is_robots_noindex ); $this->reset_social_images( $indexable ); diff --git a/tests/unit/builders/indexable-author-builder-test.php b/tests/unit/builders/indexable-author-builder-test.php index c5f3f14522d..eca51b11eb6 100644 --- a/tests/unit/builders/indexable-author-builder-test.php +++ b/tests/unit/builders/indexable-author-builder-test.php @@ -94,7 +94,7 @@ protected function set_up() { $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image_id', null ); $this->indexable_mock->orm->expects( 'set' )->with( 'twitter_image_source', null ); - $this->indexable_mock->orm->expects( 'set' )->with( 'is_public', null ); + $this->indexable_mock->expects( 'set_deprecated_property' )->with( 'is_public', null ); $this->indexable_mock->orm->expects( 'get' )->with( 'is_robots_noindex' )->andReturn( 0 ); @@ -140,9 +140,6 @@ protected function set_up() { ] ); - Monkey\Functions\expect( '_deprecated_argument' ); - Monkey\Functions\expect( 'esc_html' ); - $this->instance = new Indexable_Author_Builder( $this->author_archive, $this->versions, $this->post_helper, $this->wpdb ); } diff --git a/tests/unit/builders/indexable-date-archive-builder-test.php b/tests/unit/builders/indexable-date-archive-builder-test.php index 64066ac6772..773818e71ea 100644 --- a/tests/unit/builders/indexable-date-archive-builder-test.php +++ b/tests/unit/builders/indexable-date-archive-builder-test.php @@ -41,9 +41,9 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'object_type', 'date-archive' ); $indexable_mock->orm->expects( 'set' )->with( 'title', 'date_archive_title' ); $indexable_mock->orm->expects( 'set' )->with( 'description', 'date_archive_meta_description' ); - $indexable_mock->orm->expects( 'set' )->with( 'is_public', true ); $indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', false ); $indexable_mock->orm->expects( 'get' )->with( 'is_robots_noindex' )->andReturn( 0 ); + $indexable_mock->expects( 'set_deprecated_property' )->with( 'is_public', true ); Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); @@ -51,9 +51,6 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'version', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', 1 ); - Functions\expect( '_deprecated_argument' ); - Functions\expect( 'esc_html' ); - $builder = new Indexable_Date_Archive_Builder( $options_mock, new Indexable_Builder_Versions() ); $builder->build( $indexable_mock ); } diff --git a/tests/unit/builders/indexable-post-builder-test.php b/tests/unit/builders/indexable-post-builder-test.php index 420f4cf7575..db9056060ea 100644 --- a/tests/unit/builders/indexable-post-builder-test.php +++ b/tests/unit/builders/indexable-post-builder-test.php @@ -297,7 +297,6 @@ public function test_build() { 'primary_focus_keyword_score' => 100, 'readability_score' => 50, 'number_of_pages' => null, - 'is_public' => 0, 'is_publicly_viewable' => true, 'number_of_publicly_viewable_posts' => 0, 'post_status' => 'publish', @@ -317,6 +316,7 @@ public function test_build() { $this->indexable->orm = Mockery::mock( ORM::class ); $this->set_indexable_set_expectations( $this->indexable, $indexable_expectations ); + $this->indexable->expects( 'set_deprecated_property' )->with( 'is_public', 0 ); // Reset all social images first. $this->set_indexable_set_expectations( @@ -377,8 +377,6 @@ public function test_build() { Monkey\Functions\expect( 'get_the_title' )->with( 1 )->andReturn( 'breadcrumb_title' ); Monkey\Functions\expect( 'wp_strip_all_tags' )->with( 'breadcrumb_title', true )->andReturn( 'breadcrumb_title' ); - Monkey\Functions\expect( '_deprecated_argument' ); - Monkey\Functions\expect( 'esc_html' ); Monkey\Functions\expect( 'is_post_publicly_viewable' )->andReturn( true ); // Blog ID. diff --git a/tests/unit/builders/indexable-post-type-archive-builder-test.php b/tests/unit/builders/indexable-post-type-archive-builder-test.php index b15918cade6..928bde6f69e 100644 --- a/tests/unit/builders/indexable-post-type-archive-builder-test.php +++ b/tests/unit/builders/indexable-post-type-archive-builder-test.php @@ -87,9 +87,9 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'permalink', 'https://permalink' ); $indexable_mock->orm->expects( 'set' )->with( 'description', 'my_post_type_meta_description' ); $indexable_mock->orm->expects( 'set' )->with( 'is_robots_noindex', false ); - $indexable_mock->orm->expects( 'set' )->with( 'is_public', true ); $indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', true ); $indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 6 ); + $indexable_mock->expects( 'set_deprecated_property' )->with( 'is_public', true ); $indexable_mock->orm->expects( 'get' )->with( 'is_robots_noindex' )->andReturnFalse(); $indexable_mock->orm->expects( 'get' )->with( 'object_sub_type' )->andReturn( 'my-post-type' ); @@ -100,9 +100,6 @@ public function test_build() { $indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); - Monkey\Functions\expect( '_deprecated_argument' ); - Monkey\Functions\expect( 'esc_html' ); - $builder = new Indexable_Post_Type_Archive_Builder( $options_mock, $versions, $post_helper, $post_type_helper, $wpdb ); $builder->build( 'my-post-type', $indexable_mock ); } diff --git a/tests/unit/builders/indexable-term-builder-test.php b/tests/unit/builders/indexable-term-builder-test.php index cfd0fa271f8..58e4efea64d 100644 --- a/tests/unit/builders/indexable-term-builder-test.php +++ b/tests/unit/builders/indexable-term-builder-test.php @@ -311,7 +311,6 @@ public function test_build() { 'is_robots_noarchive' => null, 'is_robots_noimageindex' => null, 'is_robots_nosnippet' => null, - 'is_public' => false, 'is_publicly_viewable' => true, 'primary_focus_keyword' => 'focuskeyword', 'primary_focus_keyword_score' => 75, @@ -320,6 +319,7 @@ public function test_build() { ]; $this->set_indexable_set_expectations( $indexable_mock, $indexable_expectations ); + $indexable_mock->expects( 'set_deprecated_property' )->with( 'is_public', false ); // Reset all social images first. $this->set_indexable_set_expectations( @@ -375,10 +375,6 @@ public function test_build() { $indexable_mock->orm->expects( 'get' )->with( 'object_id' )->andReturn( 1 ); $indexable_mock->orm->expects( 'get' )->with( 'object_sub_type' )->andReturn( 'category' ); - - Monkey\Functions\expect( '_deprecated_argument' ); - Monkey\Functions\expect( 'esc_html' ); - Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); From ff247f9850219f2824d116a7e074e4c446641d94 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 30 Nov 2021 16:40:06 +0100 Subject: [PATCH 26/53] Include password protected posts in aggegrate queries Password protected posts still end up in the post-type- and termarchives and are publicly queryable. The difference is that the content is password protected. Because if this detail, these posts should be noindexed, but the archives should still update their data because the protected posts are still shown there --- src/builders/indexable-author-builder.php | 1 - src/builders/indexable-home-page-builder.php | 1 - src/builders/indexable-post-type-archive-builder.php | 1 - src/builders/indexable-term-builder.php | 1 - 4 files changed, 4 deletions(-) diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index f5dbbea809d..5e2203e23d5 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -198,7 +198,6 @@ protected function get_public_post_archive_aggregates( $author_id ) { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') - AND p.post_password = \'\' AND p.post_author = %d AND p.post_type IN (' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ') '; diff --git a/src/builders/indexable-home-page-builder.php b/src/builders/indexable-home-page-builder.php index 8db6bb2c65a..a910bb804c0 100644 --- a/src/builders/indexable-home-page-builder.php +++ b/src/builders/indexable-home-page-builder.php @@ -153,7 +153,6 @@ protected function get_public_post_archive_aggregates() { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") - AND p.post_password = '' AND p.post_type = 'post' "; diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 62aebb92dee..deedc492d6e 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -167,7 +167,6 @@ protected function get_public_post_archive_aggregates( $post_type ) { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") - AND p.post_password = '' AND p.post_type = %s "; diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index baf1d409181..8e42a5f6272 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -278,7 +278,6 @@ protected function get_public_post_archive_aggregates( $term_id, $taxonomy ) { AND term_tax.taxonomy = %s AND term_tax.term_id = %d WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") - AND p.post_password = '' "; $replacements = \array_merge( [ $taxonomy, $term_id ], $post_statuses ); From ca85a074ab8230c4cb6bb449f43cb6dc609515b9 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 30 Nov 2021 16:42:05 +0100 Subject: [PATCH 27/53] Keep the most recent last modified date. This prevents unsetting the last modified date on archive indexables when the last post of the post-type or taxonomy is removed. Also prevents moving the date back in time when performing a reindex action. --- src/builders/indexable-home-page-builder.php | 2 +- src/builders/indexable-post-type-archive-builder.php | 2 +- src/builders/indexable-term-builder.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builders/indexable-home-page-builder.php b/src/builders/indexable-home-page-builder.php index a910bb804c0..0b0edb234dc 100644 --- a/src/builders/indexable-home-page-builder.php +++ b/src/builders/indexable-home-page-builder.php @@ -132,7 +132,7 @@ public function build( $indexable ) { public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates(); $indexable->object_published_at = $aggregates->first_published_at; - $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->object_last_modified = max( $indexable->object_last_modified, $aggregates->most_recent_last_modified ); $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index deedc492d6e..b68e54296e0 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -113,7 +113,7 @@ public function build( $post_type, Indexable $indexable ) { public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_sub_type ); $indexable->object_published_at = $aggregates->first_published_at; - $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->object_last_modified = max( $indexable->object_last_modified, $aggregates->most_recent_last_modified ); $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 8e42a5f6272..2ba2e346199 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -149,7 +149,7 @@ public function build( $term_id, $indexable ) { public function set_aggregate_values( Indexable $indexable ) { $aggregates = $this->get_public_post_archive_aggregates( $indexable->object_id, $indexable->object_sub_type ); $indexable->object_published_at = $aggregates->first_published_at; - $indexable->object_last_modified = $aggregates->most_recent_last_modified; + $indexable->object_last_modified = max( $indexable->object_last_modified, $aggregates->most_recent_last_modified ); $indexable->number_of_publicly_viewable_posts = $aggregates->number_of_public_posts; return $indexable; From f6a8adf2b4de9451f224252fb72ceb58ee371c74 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Thu, 2 Dec 2021 17:05:43 +0100 Subject: [PATCH 28/53] Adhere to naming conventions --- lib/model.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/model.php b/lib/model.php index e3813c9ad84..aae34429f27 100644 --- a/lib/model.php +++ b/lib/model.php @@ -511,7 +511,7 @@ public function set_orm( $orm ) { * @return mixed The value of the property */ public function __get( $property ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); $value = $this->orm->get( $property ); if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { @@ -536,7 +536,7 @@ public function __get( $property ) { * @return void */ public function __set( $property, $value ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { $value = ( $value ) ? '1' : '0'; } @@ -558,7 +558,7 @@ public function __set( $property, $value ) { * @return void */ public function __unset( $property ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); $this->orm->__unset( $property ); } @@ -588,7 +588,7 @@ public function __debugInfo() { * @return bool True when value is set. */ public function __isset( $property ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); return $this->orm->__isset( $property ); } @@ -600,7 +600,7 @@ public function __isset( $property ) { * @return string The value of a property. */ public function get( $property ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); return $this->orm->get( $property ); } @@ -614,7 +614,7 @@ public function get( $property ) { * @return static Current object. */ public function set( $property, $value = null ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); $this->orm->set( $property, $value ); return $this; @@ -659,7 +659,7 @@ public function set_deprecated_property( $property, $value = null ) { * @return static Current object. */ public function set_expr( $property, $value = null ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); $this->orm->set_expr( $property, $value ); return $this; @@ -673,7 +673,7 @@ public function set_expr( $property, $value = null ) { * @return bool True when field is changed. */ public function is_dirty( $property ) { - $property = $this->handleDeprecation( $property ); + $property = $this->handle_deprecation( $property ); return $this->orm->is_dirty( $property ); } @@ -772,7 +772,7 @@ public static function __callStatic( $method, $arguments ) { * * @return string The deprecated property name. Or its replacement if available. */ - protected function handleDeprecation( $property ) { + protected function handle_deprecation( $property ) { if ( ! array_key_exists( $property, $this->deprecated_columns ) ) { return $property; } From 274df539e91ec586aafce35daad168487683c0cc Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Thu, 2 Dec 2021 17:07:14 +0100 Subject: [PATCH 29/53] Don't hardcode the deprecation version number --- lib/model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/model.php b/lib/model.php index aae34429f27..122c81f1c44 100644 --- a/lib/model.php +++ b/lib/model.php @@ -782,7 +782,7 @@ protected function handle_deprecation( $property ) { // There is no _deprecated_property. This matches our usecase best. \_deprecated_argument( __FUNCTION__, - '17.7', + $deprecation['since'], 'Use the \"' . esc_html( $deprecation['replacement'] ) . '\" property instead of \"' . esc_html( $property ) . '\" ' ); @@ -793,7 +793,7 @@ protected function handle_deprecation( $property ) { return $property; } // There is no _deprecated_property. This matches our usecase best. - \_deprecated_argument( __FUNCTION__, '17.7', 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); + \_deprecated_argument( __FUNCTION__, $deprecation['since'], 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); return $property; } From d51829eb66095bc581266040af3823da6172a085 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Thu, 2 Dec 2021 17:13:48 +0100 Subject: [PATCH 30/53] Update deprecation versions and descriptions --- src/builders/indexable-post-builder.php | 4 ++-- src/helpers/author-archive-helper.php | 8 ++++---- src/helpers/post-helper.php | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 00844415357..48ceacbbd8a 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -187,7 +187,7 @@ protected function get_permalink( $post_type, $post_id ) { * @return bool|null Whether the post type is public. Null if no override is set. * * @codeCoverageIgnore - * @deprecated 17.7 + * @deprecated 17.9 */ protected function is_public( $indexable ) { if ( $indexable->is_protected === true ) { @@ -222,7 +222,7 @@ protected function is_public( $indexable ) { * @return bool|null False when it has no parent. Null when it has a parent. * * @codeCoverageIgnore - * @deprecated 17.7 + * @deprecated 17.9 */ protected function is_public_attachment( $indexable ) { // If the attachment has no parent, it should not be public. diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index c2a699563a3..aef818f6f56 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -32,10 +32,10 @@ public function get_author_archive_post_types() { * @return bool|null Whether the author has at least one public post. * * @codeCoverageIgnore - * @deprecated 17.7 + * @deprecated 17.9 */ public function author_has_public_posts( $author_id ) { - \_deprecated_function( __METHOD__, '17.7', esc_html( Indexable_Repository::class ) . '::noindex_query' ); + \_deprecated_function( __METHOD__, '17.9', esc_html( Indexable_Repository::class ) . '::query_where_noindex' ); // First check if the author has at least one public post. $has_public_post = $this->author_has_a_public_post( $author_id ); if ( $has_public_post ) { @@ -60,7 +60,7 @@ public function author_has_public_posts( $author_id ) { * * @return bool Whether the author has at least one public post. * - * @deprecated 17.7 + * @deprecated 17.9 */ protected function author_has_a_public_post( $author_id ) { $cache_key = 'author_has_a_public_post_' . $author_id; @@ -93,7 +93,7 @@ protected function author_has_a_public_post( $author_id ) { * * @return bool Whether the author has at least one post with the is public null. * - * @deprecated 17.7 + * @deprecated 17.9 */ protected function author_has_a_post_with_is_public_null( $author_id ) { $cache_key = 'author_has_a_post_with_is_public_null_' . $author_id; diff --git a/src/helpers/post-helper.php b/src/helpers/post-helper.php index 903cc773623..07e5b4a1135 100644 --- a/src/helpers/post-helper.php +++ b/src/helpers/post-helper.php @@ -151,10 +151,10 @@ public function get_post( $post_id ) { * * @return bool Whether the update was successful. * - * @deprecated 17.7 + * @deprecated 17.9 */ public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) { - _deprecated_function( __METHOD__, '17.7' ); + _deprecated_function( __METHOD__, '17.9' ); $indexable = $this->repository->find_by_id_and_type( $post_parent, 'post' ); $this->indexable_builder->recalculate_aggregates( $indexable ); From 5c1193a7f84d103966fa02f77cf835325b40acd1 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Thu, 2 Dec 2021 17:26:59 +0100 Subject: [PATCH 31/53] Rename migration --- ...p => 20211108133106_AddIsPubliclyViewableToIndexables.php} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/config/migrations/{20211108133106_ReplaceIsPublicOnIndexables.php => 20211108133106_AddIsPubliclyViewableToIndexables.php} (90%) diff --git a/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php b/src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php similarity index 90% rename from src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php rename to src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php index 4db4c9913da..901a1db4fca 100644 --- a/src/config/migrations/20211108133106_ReplaceIsPublicOnIndexables.php +++ b/src/config/migrations/20211108133106_AddIsPubliclyViewableToIndexables.php @@ -6,9 +6,9 @@ use Yoast\WP\Lib\Model; /** - * ReplaceIsPublicOnIndexables class. + * AddIsPubliclyViewableToIndexables class. */ -class ReplaceIsPublicOnIndexables extends Migration { +class AddIsPubliclyViewableToIndexables extends Migration { /** * The plugin this migration belongs to. From 82f8a78ab6123502e8ee5a07e4680b3341c75805 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 10:46:23 +0100 Subject: [PATCH 32/53] Fix cs --- src/builders/indexable-post-type-archive-builder.php | 4 ++-- src/builders/indexable-term-builder.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index b68e54296e0..09c7415601c 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -166,9 +166,9 @@ protected function get_public_post_archive_aggregates( $post_type ) { MAX(p.post_modified_gmt) AS most_recent_last_modified, MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p - WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") + WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') AND p.post_type = %s - "; + '; $replacements = \array_merge( $post_statuses, [ $post_type ] ); diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 2ba2e346199..5e755a398f8 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -277,8 +277,8 @@ protected function get_public_post_archive_aggregates( $term_id, $taxonomy ) { ON term_tax.term_taxonomy_id = term_rel.term_taxonomy_id AND term_tax.taxonomy = %s AND term_tax.term_id = %d - WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ") - "; + WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') + '; $replacements = \array_merge( [ $taxonomy, $term_id ], $post_statuses ); From 66d6e4d9cc508c85da4d54805fc83e15022ada7b Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 10:46:32 +0100 Subject: [PATCH 33/53] Update tests --- tests/unit/builders/indexable-author-builder-test.php | 1 - tests/unit/builders/indexable-home-page-builder-test.php | 6 +++--- .../builders/indexable-post-type-archive-builder-test.php | 2 +- tests/unit/builders/indexable-term-builder-test.php | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/unit/builders/indexable-author-builder-test.php b/tests/unit/builders/indexable-author-builder-test.php index eca51b11eb6..2febb5a6dc7 100644 --- a/tests/unit/builders/indexable-author-builder-test.php +++ b/tests/unit/builders/indexable-author-builder-test.php @@ -126,7 +126,6 @@ protected function set_up() { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) - AND p.post_password = '' AND p.post_author = %d AND p.post_type IN (%s, %s) ", diff --git a/tests/unit/builders/indexable-home-page-builder-test.php b/tests/unit/builders/indexable-home-page-builder-test.php index c2837af241c..876f27ed0da 100644 --- a/tests/unit/builders/indexable-home-page-builder-test.php +++ b/tests/unit/builders/indexable-home-page-builder-test.php @@ -216,7 +216,6 @@ public function test_build() { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) - AND p.post_password = '' AND p.post_type = 'post' ", [ 'publish' ] @@ -229,6 +228,7 @@ public function test_build() { ] ); + $this->indexable_mock->orm->expects( 'get' )->with( 'object_last_modified' )->andReturn( '1234-01-01 00:00:00' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 20 ); @@ -269,7 +269,6 @@ public function test_build_with_fallback_description() { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) - AND p.post_password = '' AND p.post_type = 'post' ", [ 'publish' ] @@ -282,6 +281,7 @@ public function test_build_with_fallback_description() { ] ); + $this->indexable_mock->orm->expects( 'get' )->with( 'object_last_modified' )->andReturn( '1234-01-01 00:00:00' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 20 ); @@ -318,7 +318,6 @@ public function test_build_open_graph_image_meta_data() { MIN(p.post_date_gmt) AS first_published_at FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (%s) - AND p.post_password = '' AND p.post_type = 'post' ", [ 'publish' ] @@ -331,6 +330,7 @@ public function test_build_open_graph_image_meta_data() { ] ); + $this->indexable_mock->orm->expects( 'get' )->with( 'object_last_modified' )->andReturn( '1234-01-01 00:00:00' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); $this->indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); $this->indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 20 ); diff --git a/tests/unit/builders/indexable-post-type-archive-builder-test.php b/tests/unit/builders/indexable-post-type-archive-builder-test.php index 928bde6f69e..6cee5340d22 100644 --- a/tests/unit/builders/indexable-post-type-archive-builder-test.php +++ b/tests/unit/builders/indexable-post-type-archive-builder-test.php @@ -65,7 +65,6 @@ public function test_build() { MIN(p.post_date_gmt) AS first_published_at FROM {$wpdb->posts} AS p WHERE p.post_status IN (%s) - AND p.post_password = '' AND p.post_type = %s ", [ 'publish', 'my-post-type' ] @@ -97,6 +96,7 @@ public function test_build() { Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'version', 1 ); + $indexable_mock->orm->expects( 'get' )->with( 'object_last_modified' )->andReturn( '1234-01-01 00:00:00' ); $indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); diff --git a/tests/unit/builders/indexable-term-builder-test.php b/tests/unit/builders/indexable-term-builder-test.php index 58e4efea64d..0bd0b71d2e3 100644 --- a/tests/unit/builders/indexable-term-builder-test.php +++ b/tests/unit/builders/indexable-term-builder-test.php @@ -273,7 +273,6 @@ public function test_build() { AND term_tax.taxonomy = %s AND term_tax.term_id = %d WHERE p.post_status IN (%s) - AND p.post_password = '' ", [ 'category', 1, 'publish' ] )->andReturn( 'PREPARED_QUERY' ); @@ -377,6 +376,7 @@ public function test_build() { Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); + $indexable_mock->orm->expects( 'get' )->with( 'object_last_modified' )->andReturn( '1234-01-01 00:00:00' ); $indexable_mock->orm->expects( 'set' )->with( 'object_published_at', '1234-12-12 00:00:00' ); $indexable_mock->orm->expects( 'set' )->with( 'object_last_modified', '1234-12-12 23:59:59' ); $indexable_mock->orm->expects( 'set' )->with( 'number_of_publicly_viewable_posts', 10 ); From cf56df4ee97ff2bb0b049800c2a37151dc30457f Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 11:06:51 +0100 Subject: [PATCH 34/53] Fill WP 5.7 function --- src/builders/indexable-post-builder.php | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 48ceacbbd8a..67e15a716be 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -145,7 +145,7 @@ public function build( $post_id, $indexable ) { $indexable->number_of_pages = $this->get_number_of_pages_for_post( $post ); $indexable->post_status = $post->post_status; $indexable->is_protected = $post->post_password !== ''; - $indexable->is_publicly_viewable = \is_post_publicly_viewable( $post ); + $indexable->is_publicly_viewable = $this->is_post_publicly_viewable( $post ); $indexable->number_of_publicly_viewable_posts = 0; $indexable->blog_id = \get_current_blog_id(); @@ -382,6 +382,31 @@ protected function get_number_of_pages_for_post( $post ) { return $number_of_pages; } + /** + * Determine whether a post is publicly viewable. + * + * Posts are considered publicly viewable if both the post status and post type + * are viewable. + * + * @see \is_post_publicly_viewable Polyfill for WP 5.6. This function was introduced to WP core in 5.7. + * + * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post. + * + * @return bool Whether the post is publicly viewable. + */ + protected function is_post_publicly_viewable( $post = null ) { + $post = \get_post( $post ); + + if ( ! $post ) { + return false; + } + + $post_type = get_post_type( $post ); + $post_status = get_post_status( $post ); + + return is_post_type_viewable( $post_type ) && is_post_status_viewable( $post_status ); + } + /** * Checks whether an indexable should be built for this post. * From 68aba31e140989693f19dac15a62f30865cd7d96 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 11:37:39 +0100 Subject: [PATCH 35/53] Mock calls to newly introduced functions --- src/builders/indexable-post-builder.php | 6 ++---- tests/unit/builders/indexable-post-builder-test.php | 5 ++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index 67e15a716be..bb0f3d88c20 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -390,13 +390,11 @@ protected function get_number_of_pages_for_post( $post ) { * * @see \is_post_publicly_viewable Polyfill for WP 5.6. This function was introduced to WP core in 5.7. * - * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post. + * @param WP_Post $post The post. * * @return bool Whether the post is publicly viewable. */ - protected function is_post_publicly_viewable( $post = null ) { - $post = \get_post( $post ); - + protected function is_post_publicly_viewable( $post ) { if ( ! $post ) { return false; } diff --git a/tests/unit/builders/indexable-post-builder-test.php b/tests/unit/builders/indexable-post-builder-test.php index db9056060ea..30bd36a58cb 100644 --- a/tests/unit/builders/indexable-post-builder-test.php +++ b/tests/unit/builders/indexable-post-builder-test.php @@ -377,7 +377,10 @@ public function test_build() { Monkey\Functions\expect( 'get_the_title' )->with( 1 )->andReturn( 'breadcrumb_title' ); Monkey\Functions\expect( 'wp_strip_all_tags' )->with( 'breadcrumb_title', true )->andReturn( 'breadcrumb_title' ); - Monkey\Functions\expect( 'is_post_publicly_viewable' )->andReturn( true ); + Monkey\Functions\expect( 'get_post_type' )->andReturn( 'post-type' ); + Monkey\Functions\expect( 'get_post_status' )->andReturn( 'published' ); + Monkey\Functions\expect( 'is_post_type_viewable' )->andReturn( true ); + Monkey\Functions\expect( 'is_post_status_viewable' )->andReturn( true ); // Blog ID. Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); From 3329a1bb99bef8e631c99cd34ca601a23c95e608 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 11:52:31 +0100 Subject: [PATCH 36/53] Polyfill WP 5.7 function --- src/builders/indexable-post-builder.php | 37 +++++++++++++++++-- .../builders/indexable-post-builder-test.php | 8 +++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index bb0f3d88c20..c42442363e7 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -388,11 +388,11 @@ protected function get_number_of_pages_for_post( $post ) { * Posts are considered publicly viewable if both the post status and post type * are viewable. * - * @see \is_post_publicly_viewable Polyfill for WP 5.6. This function was introduced to WP core in 5.7. - * * @param WP_Post $post The post. * * @return bool Whether the post is publicly viewable. + * + * @see \is_post_publicly_viewable Polyfill for WP 5.6. This function was introduced to WP core in 5.7. */ protected function is_post_publicly_viewable( $post ) { if ( ! $post ) { @@ -402,7 +402,38 @@ protected function is_post_publicly_viewable( $post ) { $post_type = get_post_type( $post ); $post_status = get_post_status( $post ); - return is_post_type_viewable( $post_type ) && is_post_status_viewable( $post_status ); + return is_post_type_viewable( $post_type ) && $this->is_post_status_viewable( $post_status ); + } + + /** + * Determine whether a post status is considered "viewable". + * + * For built-in post statuses such as publish and private, the 'public' value will be evaluted. + * For all others, the 'publicly_queryable' value will be used. + * + * @param string|stdClass $post_status Post status name or object. + * + * @return bool Whether the post status should be considered viewable. + * + * @see \is_post_status_viewable Polyfill for WP 5.6. This function was introduced to WP core in 5.7. + */ + protected function is_post_status_viewable( $post_status ) { + if ( is_scalar( $post_status ) ) { + $post_status = \get_post_status_object( $post_status ); + if ( ! $post_status ) { + return false; + } + } + + if ( + ! is_object( $post_status ) + || $post_status->internal + || $post_status->protected + ) { + return false; + } + + return $post_status->publicly_queryable || ( $post_status->_builtin && $post_status->public ); } /** diff --git a/tests/unit/builders/indexable-post-builder-test.php b/tests/unit/builders/indexable-post-builder-test.php index 30bd36a58cb..9cc7db6ba5b 100644 --- a/tests/unit/builders/indexable-post-builder-test.php +++ b/tests/unit/builders/indexable-post-builder-test.php @@ -380,7 +380,13 @@ public function test_build() { Monkey\Functions\expect( 'get_post_type' )->andReturn( 'post-type' ); Monkey\Functions\expect( 'get_post_status' )->andReturn( 'published' ); Monkey\Functions\expect( 'is_post_type_viewable' )->andReturn( true ); - Monkey\Functions\expect( 'is_post_status_viewable' )->andReturn( true ); + Monkey\Functions\expect( 'get_post_status_object' )->andReturn( + (object) [ + 'publicly_queryable' => true, + 'internal' => false, + 'protected' => false, + ] + ); // Blog ID. Monkey\Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); From e906aceca84663c1933c7b319e85d5edb56421c7 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 11:55:11 +0100 Subject: [PATCH 37/53] Lower error threshold --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 99c2531d7fb..6cd3bba904a 100644 --- a/composer.json +++ b/composer.json @@ -78,7 +78,7 @@ "Yoast\\WP\\SEO\\Composer\\Actions::check_coding_standards" ], "check-cs-thresholds": [ - "@putenv YOASTCS_THRESHOLD_ERRORS=251", + "@putenv YOASTCS_THRESHOLD_ERRORS=250", "@putenv YOASTCS_THRESHOLD_WARNINGS=221", "Yoast\\WP\\SEO\\Composer\\Actions::check_cs_thresholds" ], From 29739ee7bc544bf6d4dee0effdaa2d68dc999813 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 3 Dec 2021 13:07:36 +0100 Subject: [PATCH 38/53] Remove unused function that never made it into trunk --- src/repositories/indexable-repository.php | 24 ----------------------- 1 file changed, 24 deletions(-) diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 92b35e9fd0c..24a9ae71fc7 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -373,30 +373,6 @@ public function find_for_system_page( $object_sub_type, $auto_create = true ) { return $this->upgrade_indexable( $indexable ); } - /** - * Retrieves a list of attachment indexables that belong to a parent post. - * - * @param int $parent_post_id The id of the parent post. - * - * @return Indexable[] Instances of the attachment indexables. - */ - public function find_for_post_attachments( $parent_post_id ) { - /** - * Indexable instance. - * - * @var Indexable[] $indexables - */ - $indexables = $this - ->query() - ->where( 'object_type', 'post' ) - ->where( 'object_sub_type', 'attachment' ) - ->where( 'post_status', 'inherit' ) - ->where( 'post_parent', $parent_post_id ) - ->find_many(); - - return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); - } - /** * Retrieves an indexable by its ID and type. * From 66c9433417bbd77e671e39c2649dde63ce93ab73 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Mon, 6 Dec 2021 10:44:59 +0100 Subject: [PATCH 39/53] Remove test double funtion that was removed in its parent --- .../builders/indexable-post-builder-double.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/unit/doubles/builders/indexable-post-builder-double.php b/tests/unit/doubles/builders/indexable-post-builder-double.php index 752b6161d75..054304b654f 100644 --- a/tests/unit/doubles/builders/indexable-post-builder-double.php +++ b/tests/unit/doubles/builders/indexable-post-builder-double.php @@ -33,17 +33,6 @@ public function is_public_attachment( $indexable ) { return parent::is_public_attachment( $indexable ); } - /** - * Determines the value of has_public_posts. - * - * @param Indexable $indexable The indexable. - * - * @return bool|null Whether the attachment has a public parent, can be true, false and null. Null when it is not an attachment. - */ - public function has_public_posts( $indexable ) { - return parent::has_public_posts( $indexable ); - } - /** * Gets the number of pages for a post. * From 9eb92633a1e85d8e2da41c64fb7d93d593643f42 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 7 Dec 2021 11:50:11 +0100 Subject: [PATCH 40/53] Make global namespace explicit --- src/builders/indexable-post-builder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/builders/indexable-post-builder.php b/src/builders/indexable-post-builder.php index c42442363e7..2611ce98141 100644 --- a/src/builders/indexable-post-builder.php +++ b/src/builders/indexable-post-builder.php @@ -399,10 +399,10 @@ protected function is_post_publicly_viewable( $post ) { return false; } - $post_type = get_post_type( $post ); - $post_status = get_post_status( $post ); + $post_type = \get_post_type( $post ); + $post_status = \get_post_status( $post ); - return is_post_type_viewable( $post_type ) && $this->is_post_status_viewable( $post_status ); + return \is_post_type_viewable( $post_type ) && $this->is_post_status_viewable( $post_status ); } /** From 0c9ac1acd97028757f27c17e4f801b60ea2eab63 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 7 Dec 2021 11:52:28 +0100 Subject: [PATCH 41/53] Ensure that post_types is an array --- src/helpers/author-archive-helper.php | 37 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index aef818f6f56..340d5611937 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -16,12 +16,19 @@ class Author_Archive_Helper { * @return array The post types that are shown on an author's archive. */ public function get_author_archive_post_types() { + $default_post_types = [ 'post' ]; /** * Filters the array of post types that are shown on an author's archive. * * @param array $args The post types that are shown on an author archive. */ - return \apply_filters( 'wpseo_author_archive_post_types', [ 'post' ] ); + $post_types = \apply_filters( 'wpseo_author_archive_post_types', $default_post_types ); + + if ( ! is_array( $post_types ) ) { + return $default_post_types; + } + + return $post_types; } /** @@ -60,7 +67,7 @@ public function author_has_public_posts( $author_id ) { * * @return bool Whether the author has at least one public post. * - * @deprecated 17.9 + * @deprecated 17.9 */ protected function author_has_a_public_post( $author_id ) { $cache_key = 'author_has_a_public_post_' . $author_id; @@ -68,12 +75,12 @@ protected function author_has_a_public_post( $author_id ) { if ( $indexable_exists === false ) { $indexable_exists = Model::of_type( 'Indexable' ) - ->select( 'id' ) - ->where( 'object_type', 'post' ) - ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) - ->where( 'author_id', $author_id ) - ->where( 'is_public', 1 ) - ->find_one(); + ->select( 'id' ) + ->where( 'object_type', 'post' ) + ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) + ->where( 'author_id', $author_id ) + ->where( 'is_public', 1 ) + ->find_one(); if ( $indexable_exists === false ) { // Cache no results to prevent full table scanning on authors with no public posts. @@ -93,7 +100,7 @@ protected function author_has_a_public_post( $author_id ) { * * @return bool Whether the author has at least one post with the is public null. * - * @deprecated 17.9 + * @deprecated 17.9 */ protected function author_has_a_post_with_is_public_null( $author_id ) { $cache_key = 'author_has_a_post_with_is_public_null_' . $author_id; @@ -101,12 +108,12 @@ protected function author_has_a_post_with_is_public_null( $author_id ) { if ( $indexable_exists === false ) { $indexable_exists = Model::of_type( 'Indexable' ) - ->select( 'id' ) - ->where( 'object_type', 'post' ) - ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) - ->where( 'author_id', $author_id ) - ->where_null( 'is_public' ) - ->find_one(); + ->select( 'id' ) + ->where( 'object_type', 'post' ) + ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) + ->where( 'author_id', $author_id ) + ->where_null( 'is_public' ) + ->find_one(); if ( $indexable_exists === false ) { // Cache no results to prevent full table scanning on authors with no is public null posts. From 2059186d1eff94705286845db1b98dddee7a67c9 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 7 Dec 2021 11:56:22 +0100 Subject: [PATCH 42/53] Automatically update date-archive indexables --- src/values/indexables/indexable-builder-versions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/values/indexables/indexable-builder-versions.php b/src/values/indexables/indexable-builder-versions.php index 45c5d40dffb..006773bc30b 100644 --- a/src/values/indexables/indexable-builder-versions.php +++ b/src/values/indexables/indexable-builder-versions.php @@ -17,7 +17,7 @@ class Indexable_Builder_Versions { * @var array */ protected $indexable_builder_versions_by_type = [ - 'date-archive' => self::DEFAULT_INDEXABLE_BUILDER_VERSION, + 'date-archive' => 2, 'general' => 2, 'home-page' => 3, 'post' => 3, From b95492e1e19a863d51b4ba7e4adee0dee7dec6a6 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 10 Dec 2021 11:45:15 +0100 Subject: [PATCH 43/53] Use the type of the originally requested property in case of replaced properties --- lib/model.php | 8 +++++--- src/models/indexable.php | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/model.php b/lib/model.php index 122c81f1c44..13995c4137f 100644 --- a/lib/model.php +++ b/lib/model.php @@ -511,16 +511,18 @@ public function set_orm( $orm ) { * @return mixed The value of the property */ public function __get( $property ) { + $original_property = $property; $property = $this->handle_deprecation( $property ); $value = $this->orm->get( $property ); - if ( $value !== null && \in_array( $property, $this->boolean_columns, true ) ) { + // The types of a deprecated property and its replacement may differ. To prevent this kind of deprecation from being a breaking change, use the old/originally requested type. + if ( $value !== null && \in_array( $original_property, $this->boolean_columns, true ) ) { return (bool) $value; } - if ( $value !== null && \in_array( $property, $this->int_columns, true ) ) { + if ( $value !== null && \in_array( $original_property, $this->int_columns, true ) ) { return (int) $value; } - if ( $value !== null && \in_array( $property, $this->float_columns, true ) ) { + if ( $value !== null && \in_array( $original_property, $this->float_columns, true ) ) { return (float) $value; } diff --git a/src/models/indexable.php b/src/models/indexable.php index b8233f5654a..1308ed20c37 100644 --- a/src/models/indexable.php +++ b/src/models/indexable.php @@ -112,6 +112,8 @@ class Indexable extends Model { 'is_cornerstone', 'is_publicly_viewable', 'is_protected', + 'is_public', + 'has_public_posts', ]; /** From 5d095a5a468eb6aa072585111b4d80f3713cd423 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 10 Dec 2021 12:11:28 +0100 Subject: [PATCH 44/53] Fix CS warnings and errors --- composer.json | 2 +- lib/model.php | 8 ++++---- lib/orm.php | 1 - src/helpers/author-archive-helper.php | 24 ++++++++++++------------ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 453230e6479..ad782586923 100644 --- a/composer.json +++ b/composer.json @@ -78,7 +78,7 @@ "Yoast\\WP\\SEO\\Composer\\Actions::check_coding_standards" ], "check-cs-thresholds": [ - "@putenv YOASTCS_THRESHOLD_ERRORS=250", + "@putenv YOASTCS_THRESHOLD_ERRORS=252", "@putenv YOASTCS_THRESHOLD_WARNINGS=220", "Yoast\\WP\\SEO\\Composer\\Actions::check_cs_thresholds" ], diff --git a/lib/model.php b/lib/model.php index 13995c4137f..5acbc64514a 100644 --- a/lib/model.php +++ b/lib/model.php @@ -512,8 +512,8 @@ public function set_orm( $orm ) { */ public function __get( $property ) { $original_property = $property; - $property = $this->handle_deprecation( $property ); - $value = $this->orm->get( $property ); + $property = $this->handle_deprecation( $property ); + $value = $this->orm->get( $property ); // The types of a deprecated property and its replacement may differ. To prevent this kind of deprecation from being a breaking change, use the old/originally requested type. if ( $value !== null && \in_array( $original_property, $this->boolean_columns, true ) ) { @@ -784,7 +784,7 @@ protected function handle_deprecation( $property ) { // There is no _deprecated_property. This matches our usecase best. \_deprecated_argument( __FUNCTION__, - $deprecation['since'], + esc_html( $deprecation['since'] ), 'Use the \"' . esc_html( $deprecation['replacement'] ) . '\" property instead of \"' . esc_html( $property ) . '\" ' ); @@ -795,7 +795,7 @@ protected function handle_deprecation( $property ) { return $property; } // There is no _deprecated_property. This matches our usecase best. - \_deprecated_argument( __FUNCTION__, $deprecation['since'], 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); + \_deprecated_argument( __FUNCTION__, esc_html( $deprecation['since'] ), 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); return $property; } diff --git a/lib/orm.php b/lib/orm.php index 86618a1d009..acc56580145 100644 --- a/lib/orm.php +++ b/lib/orm.php @@ -48,7 +48,6 @@ * @see http://www.php-fig.org/psr/psr-1/ */ class ORM implements \ArrayAccess { - /* * --- CLASS CONSTANTS --- */ diff --git a/src/helpers/author-archive-helper.php b/src/helpers/author-archive-helper.php index 340d5611937..91c822a7a74 100644 --- a/src/helpers/author-archive-helper.php +++ b/src/helpers/author-archive-helper.php @@ -75,12 +75,12 @@ protected function author_has_a_public_post( $author_id ) { if ( $indexable_exists === false ) { $indexable_exists = Model::of_type( 'Indexable' ) - ->select( 'id' ) - ->where( 'object_type', 'post' ) - ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) - ->where( 'author_id', $author_id ) - ->where( 'is_public', 1 ) - ->find_one(); + ->select( 'id' ) + ->where( 'object_type', 'post' ) + ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) + ->where( 'author_id', $author_id ) + ->where( 'is_public', 1 ) + ->find_one(); if ( $indexable_exists === false ) { // Cache no results to prevent full table scanning on authors with no public posts. @@ -108,12 +108,12 @@ protected function author_has_a_post_with_is_public_null( $author_id ) { if ( $indexable_exists === false ) { $indexable_exists = Model::of_type( 'Indexable' ) - ->select( 'id' ) - ->where( 'object_type', 'post' ) - ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) - ->where( 'author_id', $author_id ) - ->where_null( 'is_public' ) - ->find_one(); + ->select( 'id' ) + ->where( 'object_type', 'post' ) + ->where_in( 'object_sub_type', $this->get_author_archive_post_types() ) + ->where( 'author_id', $author_id ) + ->where_null( 'is_public' ) + ->find_one(); if ( $indexable_exists === false ) { // Cache no results to prevent full table scanning on authors with no is public null posts. From d8f754f0e2e28888aee931eafd4931983acd6b50 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 10 Dec 2021 12:11:41 +0100 Subject: [PATCH 45/53] Fix tests --- tests/unit/builders/indexable-date-archive-builder-test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/builders/indexable-date-archive-builder-test.php b/tests/unit/builders/indexable-date-archive-builder-test.php index 773818e71ea..591fa7fd014 100644 --- a/tests/unit/builders/indexable-date-archive-builder-test.php +++ b/tests/unit/builders/indexable-date-archive-builder-test.php @@ -48,7 +48,7 @@ public function test_build() { Functions\expect( 'get_current_blog_id' )->once()->andReturn( 1 ); $indexable_mock->orm->expects( 'set' )->with( 'blog_id', 1 ); $indexable_mock->orm->expects( 'set' )->with( 'permalink', null ); - $indexable_mock->orm->expects( 'set' )->with( 'version', 1 ); + $indexable_mock->orm->expects( 'set' )->with( 'version', 2 ); $indexable_mock->orm->expects( 'set' )->with( 'is_publicly_viewable', 1 ); $builder = new Indexable_Date_Archive_Builder( $options_mock, new Indexable_Builder_Versions() ); From f613dfc94fe3cba5eff69967617b205b03aa21ad Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 17 Dec 2021 09:23:25 +0100 Subject: [PATCH 46/53] Clarify that the comment is about a function --- lib/model.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/model.php b/lib/model.php index 5acbc64514a..abf11dd91fa 100644 --- a/lib/model.php +++ b/lib/model.php @@ -781,7 +781,7 @@ protected function handle_deprecation( $property ) { $deprecation = $this->deprecated_columns[ $property ]; if ( ! empty( $deprecation['replacement'] ) ) { - // There is no _deprecated_property. This matches our usecase best. + // There is no _deprecated_property() function, so we use the closest match _deprecated_argument(). \_deprecated_argument( __FUNCTION__, esc_html( $deprecation['since'] ), @@ -794,7 +794,7 @@ protected function handle_deprecation( $property ) { return $property; } - // There is no _deprecated_property. This matches our usecase best. + // There is no _deprecated_property() function, so we use the closest match _deprecated_argument(). \_deprecated_argument( __FUNCTION__, esc_html( $deprecation['since'] ), 'The \"' . esc_html( $property ) . '\" property will be removed in a future version' ); return $property; From 3e7e755c7d13b252d68dec1d4c72c775ec336c58 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 17 Dec 2021 09:43:22 +0100 Subject: [PATCH 47/53] Add instructions to remove related code --- src/helpers/post-helper.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/helpers/post-helper.php b/src/helpers/post-helper.php index 07e5b4a1135..21c7fc8f56b 100644 --- a/src/helpers/post-helper.php +++ b/src/helpers/post-helper.php @@ -51,6 +51,8 @@ public function __construct( String_Helper $string ) { * @required * * @return void + * + * When the deprecated `$this->update_has_public_posts_on_attachments()` function is removed, this setter should also be removed. */ public function set_indexable_builder( Indexable_Builder $indexable_builder ) { $this->indexable_builder = $indexable_builder; @@ -152,6 +154,7 @@ public function get_post( $post_id ) { * @return bool Whether the update was successful. * * @deprecated 17.9 + * When this function is removed, also remove the indexable_builder setter. */ public function update_has_public_posts_on_attachments( $post_parent, $has_public_posts ) { _deprecated_function( __METHOD__, '17.9' ); From c24488e8e0b2ffbf306a045a29e9c40f5e97f39b Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 17 Dec 2021 09:50:48 +0100 Subject: [PATCH 48/53] Reduce risk of using the same builder version twice. If we use the same version number for two separate model changes in two separate releases, the later release won't receive automatic model updates because the version hasn't changed as of the previous release. Adding a comment of the version number, will cause a git merge conflict, which forces you to look at what the correct version number should be. --- .../indexables/indexable-builder-versions.php | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/values/indexables/indexable-builder-versions.php b/src/values/indexables/indexable-builder-versions.php index 006773bc30b..06f5f952ae2 100644 --- a/src/values/indexables/indexable-builder-versions.php +++ b/src/values/indexables/indexable-builder-versions.php @@ -14,17 +14,22 @@ class Indexable_Builder_Versions { * If the key is not in this list, the indexable type will not be managed. * These numbers should be increased if one of the builders implements a new feature. * + * When you change the version of the indexable builder, change the plugin version number of the comment too. + * This is to prevent the same builder version to be increased to the same version in multiple PRs across multiple releases. + * When 2 PRs change the same builder version for the same release, it won't cause merge conflicts - which is OK, as it is the same release + * But if the same version number is used for two separate releases, the later release should use a higher builder version number. + * * @var array */ protected $indexable_builder_versions_by_type = [ - 'date-archive' => 2, - 'general' => 2, - 'home-page' => 3, - 'post' => 3, - 'post-type-archive' => 3, - 'term' => 3, - 'user' => 3, - 'system-page' => 2, + 'date-archive' => 2, // since 17.9 + 'general' => 2, // since 17.9 + 'home-page' => 3, // since 17.9 + 'post' => 3, // since 17.9 + 'post-type-archive' => 3, // since 17.9 + 'term' => 3, // since 17.9 + 'user' => 3, // since 17.9 + 'system-page' => 2, // since 17.9 ]; /** From 4ed8b38fc5630393d5d006ce6c6a98f877d477c2 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Fri, 17 Dec 2021 17:07:03 +0100 Subject: [PATCH 49/53] Update post relation indexables when a post is set to private --- src/integrations/watchers/indexable-post-watcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrations/watchers/indexable-post-watcher.php b/src/integrations/watchers/indexable-post-watcher.php index 76a85d4b639..56513f59a6a 100644 --- a/src/integrations/watchers/indexable-post-watcher.php +++ b/src/integrations/watchers/indexable-post-watcher.php @@ -193,8 +193,8 @@ public function build_indexable( $post_id ) { $this->link_builder->build( $indexable, $post->post_content ); // Save indexable to persist the updated link count. $indexable->save(); - $this->updated_indexable( $indexable, $post ); } + $this->updated_indexable( $indexable, $post ); } catch ( Exception $exception ) { $this->logger->log( LogLevel::ERROR, $exception->getMessage() ); } From 275c4239c1e89763cc27747e4621d741c4038303 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Sat, 18 Dec 2021 15:38:23 +0100 Subject: [PATCH 50/53] Fix cs --- .../indexables/indexable-builder-versions.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/values/indexables/indexable-builder-versions.php b/src/values/indexables/indexable-builder-versions.php index 06f5f952ae2..799c23cbd70 100644 --- a/src/values/indexables/indexable-builder-versions.php +++ b/src/values/indexables/indexable-builder-versions.php @@ -22,14 +22,14 @@ class Indexable_Builder_Versions { * @var array */ protected $indexable_builder_versions_by_type = [ - 'date-archive' => 2, // since 17.9 - 'general' => 2, // since 17.9 - 'home-page' => 3, // since 17.9 - 'post' => 3, // since 17.9 - 'post-type-archive' => 3, // since 17.9 - 'term' => 3, // since 17.9 - 'user' => 3, // since 17.9 - 'system-page' => 2, // since 17.9 + 'date-archive' => 2, // Since 17.9. + 'general' => 2, // Since 17.9. + 'home-page' => 3, // Since 17.9. + 'post' => 3, // Since 17.9. + 'post-type-archive' => 3, // Since 17.9. + 'term' => 3, // Since 17.9. + 'user' => 3, // Since 17.9. + 'system-page' => 2, // Since 17.9. ]; /** From 08c1f5fad3fa93c9040ca437edf21d3381897c61 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Sun, 19 Dec 2021 16:22:36 +0100 Subject: [PATCH 51/53] Fix precedence issues in query --- src/repositories/indexable-repository.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/repositories/indexable-repository.php b/src/repositories/indexable-repository.php index 24a9ae71fc7..a54404625ee 100644 --- a/src/repositories/indexable-repository.php +++ b/src/repositories/indexable-repository.php @@ -180,23 +180,25 @@ public function for_current_page() { public function query_where_noindex( $noindex, $object_type, $object_sub_type = null, $noindex_empty_archives = true ) { $query = $this ->query() - ->where( 'object_type', $object_type ) - ->where( 'object_sub_type', $object_sub_type ); + ->where( 'object_type', $object_type ); - $default_noindex = $this->robots_helper->get_default_noindex_for_object( $object_type, $object_sub_type ); + if ( $object_sub_type !== null ) { + $query->where( 'object_sub_type', $object_sub_type ); + } + $default_noindex = $this->robots_helper->get_default_noindex_for_object( $object_type, $object_sub_type ); $condition = 'is_robots_noindex = %d '; // If the requested noindex value matches the default, include NULL values in the result. if ( $default_noindex === $noindex ) { - $condition .= 'OR is_robot_noindex IS NULL'; + $condition = '(' . $condition . 'OR is_robots_noindex IS NULL )'; } // Let the number of posts in an archive determine the noindex value. $is_archive_type = in_array( $object_type, [ 'post-type-archive', 'term', 'user', 'home-page' ], true ); if ( $is_archive_type && $noindex_empty_archives ) { if ( $noindex === true ) { - $condition .= ' OR number_of_publicly_viewable_posts = 0'; + $condition = '(' . $condition . ') OR number_of_publicly_viewable_posts = 0'; } else { $condition = '(' . $condition . ') AND number_of_publicly_viewable_posts > 0'; From 6cf15bfd32965702e1d7a51e1e9a775b24e5dba0 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Mon, 20 Dec 2021 09:34:40 +0100 Subject: [PATCH 52/53] Increase threhshold --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ad782586923..45dfa4aa58e 100644 --- a/composer.json +++ b/composer.json @@ -78,7 +78,7 @@ "Yoast\\WP\\SEO\\Composer\\Actions::check_coding_standards" ], "check-cs-thresholds": [ - "@putenv YOASTCS_THRESHOLD_ERRORS=252", + "@putenv YOASTCS_THRESHOLD_ERRORS=253", "@putenv YOASTCS_THRESHOLD_WARNINGS=220", "Yoast\\WP\\SEO\\Composer\\Actions::check_cs_thresholds" ], From bad29296393a30826a77f250e5f6f921ad4b7294 Mon Sep 17 00:00:00 2001 From: Diede Exterkate Date: Tue, 21 Dec 2021 08:53:46 +0100 Subject: [PATCH 53/53] Exclude password protected posts from the publicly_viewable_count --- src/builders/indexable-author-builder.php | 3 +++ src/builders/indexable-post-type-archive-builder.php | 3 +++ src/builders/indexable-term-builder.php | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/builders/indexable-author-builder.php b/src/builders/indexable-author-builder.php index 5e2203e23d5..984f97cc001 100644 --- a/src/builders/indexable-author-builder.php +++ b/src/builders/indexable-author-builder.php @@ -182,6 +182,8 @@ protected function find_alternative_image( Indexable $indexable ) { /** * Returns public post aggregates for a given author. + * We don't consider password protected posts to be public. This helps when building sitemaps for instance, where + * password protected posts are also excluded. * * @param int $author_id The author ID. * @@ -199,6 +201,7 @@ protected function get_public_post_archive_aggregates( $author_id ) { FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') AND p.post_author = %d + AND p.post_password = "" AND p.post_type IN (' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ') '; diff --git a/src/builders/indexable-post-type-archive-builder.php b/src/builders/indexable-post-type-archive-builder.php index 09c7415601c..e4708f6d4c5 100644 --- a/src/builders/indexable-post-type-archive-builder.php +++ b/src/builders/indexable-post-type-archive-builder.php @@ -152,6 +152,8 @@ private function get_breadcrumb_title( $post_type ) { /** * Returns public post aggregates for a given post type. + * We don't consider password protected posts to be public. This helps when building sitemaps for instance, where + * password protected posts are also excluded. * * @param string $post_type The post type. * @@ -168,6 +170,7 @@ protected function get_public_post_archive_aggregates( $post_type ) { FROM {$this->wpdb->posts} AS p WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') AND p.post_type = %s + AND p.post_password = "" '; $replacements = \array_merge( $post_statuses, [ $post_type ] ); diff --git a/src/builders/indexable-term-builder.php b/src/builders/indexable-term-builder.php index 5e755a398f8..2b8eec69467 100644 --- a/src/builders/indexable-term-builder.php +++ b/src/builders/indexable-term-builder.php @@ -256,6 +256,8 @@ protected function find_alternative_image( Indexable $indexable ) { /** * Returns public post aggregates for a given term. + * We don't consider password protected posts to be public. This helps when building sitemaps for instance, where + * password protected posts are also excluded. * * @param int $term_id The term ID. * @param string $taxonomy The taxonomy. @@ -278,6 +280,7 @@ protected function get_public_post_archive_aggregates( $term_id, $taxonomy ) { AND term_tax.taxonomy = %s AND term_tax.term_id = %d WHERE p.post_status IN (" . implode( ', ', array_fill( 0, count( $post_statuses ), '%s' ) ) . ') + AND p.post_password = "" '; $replacements = \array_merge( [ $taxonomy, $term_id ], $post_statuses );