Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor URL validation CLI script to make parts reusable #5306

Merged
merged 49 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
717ce36
Refactor URL validation CLI script
johnwatkins0 Aug 31, 2020
6c3a318
URL validation: return WP_Error if validity is an error
johnwatkins0 Sep 1, 2020
4a069f4
Use with_lock where method is called instead of inside method
johnwatkins0 Sep 1, 2020
47f22c8
Minor updates primarily in formatting and comments
johnwatkins0 Oct 6, 2020
640d6b6
Use get_flag_value in CLI script to handle assoc args
johnwatkins0 Oct 7, 2020
9661bec
PHPCS fixes
johnwatkins0 Oct 7, 2020
be19b24
Merge branch 'feature/refactor-url-validation' of https://github.com/…
johnwatkins0 Oct 7, 2020
1cf083d
Fix CLI script imports
johnwatkins0 Oct 7, 2020
8b6099c
Use option instead of transient for locking
johnwatkins0 Oct 7, 2020
fdcc5f0
Rename ValidationURLProvider
johnwatkins0 Oct 7, 2020
4ff12fd
Make class properties private
johnwatkins0 Oct 7, 2020
39c1665
Trim outdated comment content
johnwatkins0 Oct 7, 2020
cdbad7a
Remove unneeded use
johnwatkins0 Oct 7, 2020
14c4d62
Remove test code
johnwatkins0 Oct 7, 2020
98bd4b2
Merge remote-tracking branch 'origin/develop' into feature/refactor-u…
johnwatkins0 Oct 7, 2020
60db6fa
Try importing WP_CLI\Utils
schlessera Oct 7, 2020
2608783
Merge remote-tracking branch 'origin/develop' into feature/refactor-u…
johnwatkins0 Oct 13, 2020
2ccffb3
Include cli utils in autoload-dev
johnwatkins0 Oct 19, 2020
ebdc0e7
Merge remote-tracking branch 'origin/develop' into feature/refactor-u…
johnwatkins0 Oct 21, 2020
b569341
Remove cli utils from autoload-dev
johnwatkins0 Oct 21, 2020
e7c12f2
Fix with_lock unit tests
johnwatkins0 Oct 21, 2020
5ee7d42
Apply suggestions from code review
johnwatkins0 Oct 28, 2020
58bacd0
Replace obsolete class property with get_flag_value call
johnwatkins0 Oct 28, 2020
050b1e7
Make validity_by_type class property private and add getter method
johnwatkins0 Oct 28, 2020
457d145
ScannableURLProvider: return limit_per_type # of taxonomy links by de…
johnwatkins0 Oct 28, 2020
5893cfb
Add omitted property
johnwatkins0 Oct 28, 2020
9559058
Undo use of assertIsString
johnwatkins0 Oct 28, 2020
d1e3bde
PHPStan ignore a line
johnwatkins0 Oct 29, 2020
fbc39a6
Update lock file
johnwatkins0 Oct 29, 2020
3071784
Merge remote-tracking branch 'origin/develop' into feature/refactor-u…
johnwatkins0 Oct 29, 2020
1e8ec58
Merge remote-tracking branch 'origin/develop' into feature/refactor-u…
johnwatkins0 Nov 10, 2020
3c7d7d7
Simplify test assertion
johnwatkins0 Nov 10, 2020
e70f816
Improve coverage with more @covers comments
johnwatkins0 Nov 10, 2020
c4899a7
Update covers comment style
johnwatkins0 Nov 10, 2020
6c449c0
PHPCS fix
johnwatkins0 Nov 10, 2020
4c0238d
Rename class properties to match the classes they're instances of
johnwatkins0 Nov 11, 2020
6f42fd3
Add stub for wp cli function to avoid phpstan ignore comment
johnwatkins0 Nov 11, 2020
bf19d97
Add parentheses to function in covers tag
johnwatkins0 Nov 11, 2020
90eb92c
Merge branch 'develop' of github.com:ampproject/amp-wp into feature/r…
westonruter Nov 21, 2020
de33111
Combine redundant calls to get_urls method
johnwatkins0 Nov 23, 2020
f185379
Fix for line inadvertently placed in conditional
johnwatkins0 Nov 23, 2020
cc3c949
Formatting fixes
johnwatkins0 Nov 23, 2020
d87ad9d
Eliminate the use of a flag in validate URL function
johnwatkins0 Nov 23, 2020
e34d6f5
Add leading slash to unnamespaced class used within namespace
johnwatkins0 Nov 23, 2020
2e48427
Convert ValidationRequestMocking into trait
johnwatkins0 Nov 23, 2020
7fcf1ac
Merge branch 'feature/refactor-url-validation' of https://github.com/…
johnwatkins0 Nov 23, 2020
8ea22d6
Reset lock timeout from inside CLI script
johnwatkins0 Nov 23, 2020
13917b3
Eliminate lock timeout filter
johnwatkins0 Nov 23, 2020
9c627b3
PHPCS fix
johnwatkins0 Nov 23, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 0 additions & 177 deletions composer.lock

Large diffs are not rendered by default.

587 changes: 135 additions & 452 deletions includes/cli/class-amp-cli-validation-command.php

Large diffs are not rendered by default.

309 changes: 309 additions & 0 deletions src/Validation/ScannableURLProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
<?php
/**
* Provides URLs to scan.
*
* @package AMP
* @since 2.1
*/

namespace AmpProject\AmpWP\Validation;

use AMP_Theme_Support;
use WP_Query;

/**
* ScannableURLProvider class.
*
* @since 2.1
*/
final class ScannableURLProvider {

/**
* Whether to include URLs that don't support AMP.
*
* @var bool
*/
private $include_unsupported;

/**
* An allowlist of conditionals to use for querying URLs.
*
* Usually, this class will query all of the templates that don't have AMP disabled. This allows inclusion based on only these conditionals.
*
* @var string[]
*/
private $include_conditionals;

/**
* The maximum number of URLs to provide for each content type.
*
* Templates are each a separate type, like those for is_category() and is_tag(), and each post type is a type.
*
* @var int
*/
private $limit_per_type;

/**
* Class constructor.
*
* @param integer $limit_per_type The maximum number of URLs to validate for each type.
* @param array $include_conditionals An allowlist of conditionals to use for validation.
* @param boolean $include_unsupported Whether to include URLs that don't support AMP.
*/
public function __construct(
$limit_per_type = 20,
$include_conditionals = [],
$include_unsupported = false
) {
$this->limit_per_type = $limit_per_type;
$this->include_conditionals = $include_conditionals;
$this->include_unsupported = $include_unsupported;
}

/**
* Provides the array of URLs to check.
*
* Each URL is an array with two elements, with the URL at index 0 and the type at index 1.
*
* @param int|null $offset Optional. The number of URLs to offset by, where applicable. Defaults to 0.
* @return array Array of URLs and types.
*/
public function get_urls( $offset = 0 ) {
$urls = [];

/*
* If 'Your homepage displays' is set to 'Your latest posts', include the homepage.
*/
if ( 'posts' === get_option( 'show_on_front' ) && $this->is_template_supported( 'is_home' ) ) {
$urls[] = [
'url' => home_url( '/' ),
'type' => 'home',
];
}

$amp_enabled_taxonomies = array_filter(
get_taxonomies( [ 'public' => true ] ),
[ $this, 'does_taxonomy_support_amp' ]
);
$public_post_types = get_post_types( [ 'public' => true ] );

// Include one URL of each template/content type, then another URL of each type on the next iteration.
for ( $i = $offset; $i < $this->limit_per_type + $offset; $i++ ) {
// Include all public, published posts.
foreach ( $public_post_types as $post_type ) {
$post_ids = $this->get_posts_that_support_amp( $this->get_posts_by_type( $post_type, $i, 1 ) );
if ( ! empty( $post_ids[0] ) ) {
$urls[] = [
'url' => get_permalink( $post_ids[0] ),
'type' => $post_type,
];
}
}

foreach ( $amp_enabled_taxonomies as $taxonomy ) {
$taxonomy_links = $this->get_taxonomy_links( $taxonomy, $i, 1 );
$link = reset( $taxonomy_links );
if ( ! empty( $link ) ) {
$urls[] = [
'url' => $link,
'type' => $taxonomy,
];
}
}

$author_page_urls = $this->get_author_page_urls( $i, 1 );
if ( ! empty( $author_page_urls[0] ) ) {
$urls[] = [
'url' => $author_page_urls[0],
'type' => 'author',
];
}
}

// Only validate 1 date and 1 search page.
$url = $this->get_date_page();
if ( $url ) {
$urls[] = [
'url' => $url,
'type' => 'date',
];
}
$url = $this->get_search_page();
if ( $url ) {
$urls[] = [
'url' => $url,
'type' => 'search',
];
}

return $urls;
}

/**
* Gets whether the template is supported.
*
* @param string $template The template to check.
* @return bool Whether the template is supported.
*/
private function is_template_supported( $template ) {
// If we received an allowlist of conditionals, this template conditional must be present in it.
if ( ! empty( $this->include_conditionals ) ) {
return in_array( $template, $this->include_conditionals, true );
}
if ( $this->include_unsupported ) {
return true;
}

$supportable_templates = AMP_Theme_Support::get_supportable_templates();

// Check whether this taxonomy's template is supported, including in the 'AMP Settings' > 'Supported Templates' UI.
return ! empty( $supportable_templates[ $template ]['supported'] );
}

/**
* Gets the post IDs that support AMP.
*
* By default, this only gets the post IDs if they support AMP.
* This means that 'Posts' isn't deselected in 'AMP Settings' > 'Supported Templates'
* and 'Enable AMP' isn't unchecked in the post's editor.
*
* @param int[] $ids The post IDs to check for AMP support.
* @return array The post IDs that support AMP, or an empty array.
*/
private function get_posts_that_support_amp( $ids ) {
if ( ! $this->is_template_supported( 'is_singular' ) ) {
return [];
}

if ( $this->include_unsupported ) {
return $ids;
}

return array_filter(
$ids,
'post_supports_amp'
);
}

/**
* Gets the IDs of public, published posts.
*
* @param string $post_type The post type.
* @param int|null $offset The offset of the query (optional).
* @param int|null $number The number of posts to query for (optional).
* @return int[] $post_ids The post IDs in an array.
*/
private function get_posts_by_type( $post_type, $offset = null, $number = null ) {
johnwatkins0 marked this conversation as resolved.
Show resolved Hide resolved
$args = [
'post_type' => $post_type,
'posts_per_page' => is_int( $number ) ? $number : $this->limit_per_type,
'post_status' => 'publish',
'orderby' => 'ID',
'order' => 'DESC',
'fields' => 'ids',
];
if ( is_int( $offset ) ) {
$args['offset'] = $offset;
}

// Attachment posts usually have the post_status of 'inherit,' so they can use the status of the post they're attached to.
if ( 'attachment' === $post_type ) {
$args['post_status'] = 'inherit';
}
$query = new WP_Query( $args );

return $query->posts;
}

/**
* Gets the author page URLs, like https://example.com/author/admin/.
*
* Accepts an $offset parameter, for the query of authors.
* 0 is the first author in the query, and 1 is the second.
*
* @param int|string $offset The offset for the URL to query for, should be an int if passing an argument.
* @param int|string $number The total number to query for, should be an int if passing an argument.
* @return string[] The author page URLs, or an empty array.
*/
private function get_author_page_urls( $offset = '', $number = '' ) {
$author_page_urls = [];
if ( ! $this->is_template_supported( 'is_author' ) ) {
return $author_page_urls;
}

$number = ! empty( $number ) ? $number : $this->limit_per_type;
foreach ( get_users( compact( 'offset', 'number' ) ) as $author ) {
$author_page_urls[] = get_author_posts_url( $author->ID, $author->user_nicename );
}

return $author_page_urls;
}

/**
* Gets a single search page URL, like https://example.com/?s=example.
*
* @return string|null An example search page, or null.
*/
private function get_search_page() {
if ( ! $this->is_template_supported( 'is_search' ) ) {
return null;
}

return add_query_arg( 's', 'example', home_url( '/' ) );
}

/**
* Gets a single date page URL, like https://example.com/?year=2018.
*
* @return string|null An example search page, or null.
*/
private function get_date_page() {
if ( ! $this->is_template_supported( 'is_date' ) ) {
return null;
}

return add_query_arg( 'year', gmdate( 'Y' ), home_url( '/' ) );
}

/**
* Gets whether the taxonomy supports AMP.
*
* @param string $taxonomy The taxonomy.
* @return boolean Whether the taxonomy supports AMP.
*/
private function does_taxonomy_support_amp( $taxonomy ) {
if ( 'post_tag' === $taxonomy ) {
$taxonomy = 'tag';
}
$taxonomy_key = 'is_' . $taxonomy;
$custom_taxonomy_key = sprintf( 'is_tax[%s]', $taxonomy );
return $this->is_template_supported( $taxonomy_key ) || $this->is_template_supported( $custom_taxonomy_key );
}

/**
* Gets the front-end links for taxonomy terms.
* For example, https://example.org/?cat=2
*
* @param string $taxonomy The name of the taxonomy, like 'category' or 'post_tag'.
* @param int|string $offset The number at which to offset the query (optional).
* @param int $number The maximum amount of links to get (optional).
* @return string[] The term links, as an array of strings.
*/
private function get_taxonomy_links( $taxonomy, $offset = '', $number = null ) {
if ( is_null( $number ) ) {
$number = $this->limit_per_type;
}

return array_map(
'get_term_link',
get_terms(
array_merge(
compact( 'taxonomy', 'offset', 'number' ),
[
'orderby' => 'id',
]
)
)
);
}
}
Loading