Skip to content

Commit

Permalink
Merge branch 'release/24.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
YoastBot committed Dec 18, 2024
2 parents 1dfa2af + 8afa026 commit f727461
Show file tree
Hide file tree
Showing 134 changed files with 5,004 additions and 282 deletions.
4 changes: 2 additions & 2 deletions admin/class-gutenberg-compatibility.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ class WPSEO_Gutenberg_Compatibility {
*
* @var string
*/
public const CURRENT_RELEASE = '19.7.0';
public const CURRENT_RELEASE = '19.8.0';

/**
* The minimally supported version of Gutenberg by the plugin.
*
* @var string
*/
public const MINIMUM_SUPPORTED = '19.7.0';
public const MINIMUM_SUPPORTED = '19.8.0';

/**
* Holds the current version.
Expand Down
78 changes: 70 additions & 8 deletions admin/class-meta-columns.php
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ protected function determine_seo_filters( $seo_filter ) {
* @return array The Readability score filter.
*/
protected function determine_readability_filters( $readability_filter ) {
if ( $readability_filter === WPSEO_Rank::NO_FOCUS ) {
return $this->create_no_readability_scores_filter();
}
if ( $readability_filter === WPSEO_Rank::BAD ) {
return $this->create_bad_readability_scores_filter();
}
$rank = new WPSEO_Rank( $readability_filter );

return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
Expand Down Expand Up @@ -587,7 +593,7 @@ protected function build_filter_query( $vars, $filters ) {
$current_seo_filter = $this->get_current_seo_filter();

// This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option.
if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX, WPSEO_Rank::NO_FOCUS ], true ) ) {
if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX ], true ) ) {
$result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] );
}

Expand All @@ -600,7 +606,7 @@ protected function build_filter_query( $vars, $filters ) {
* @param number $low The lower boundary of the score.
* @param number $high The higher boundary of the score.
*
* @return array The Readability Score filter.
* @return array<array<string>> The Readability Score filter.
*/
protected function create_readability_score_filter( $low, $high ) {
return [
Expand All @@ -619,7 +625,7 @@ protected function create_readability_score_filter( $low, $high ) {
* @param number $low The lower boundary of the score.
* @param number $high The higher boundary of the score.
*
* @return array The SEO score filter.
* @return array<array<string>> The SEO score filter.
*/
protected function create_seo_score_filter( $low, $high ) {
return [
Expand All @@ -635,7 +641,7 @@ protected function create_seo_score_filter( $low, $high ) {
/**
* Creates a filter to retrieve posts that were set to no-index.
*
* @return array Array containin the no-index filter.
* @return array<array<string>> Array containin the no-index filter.
*/
protected function create_no_index_filter() {
return [
Expand All @@ -650,20 +656,76 @@ protected function create_no_index_filter() {
/**
* Creates a filter to retrieve posts that have no keyword set.
*
* @return array Array containing the no focus keyword filter.
* @return array<array<string>> Array containing the no focus keyword filter.
*/
protected function create_no_focus_keyword_filter() {
return [
[
'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
'value' => 'needs-a-value-anyway',
'compare' => 'NOT EXISTS',
],
];
}

/**
* Creates a filter to retrieve posts that have not been analyzed for readability yet.
*
* @return array<array<string>> Array containing the no readability filter.
*/
protected function create_no_readability_scores_filter() {
// We check the existence of the Estimated Reading Time, because readability scores of posts that haven't been manually saved while Yoast SEO is active, don't exist, which is also the case for posts with not enough content.
// Meanwhile, the ERT is a solid indicator of whether a post has ever been saved (aka, analyzed), so we're using that.
$rank = new WPSEO_Rank( WPSEO_Rank::BAD );
return [
[
'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
'key' => WPSEO_Meta::$meta_prefix . 'estimated-reading-time-minutes',
'value' => 'needs-a-value-anyway',
'compare' => 'NOT EXISTS',
],
[
'relation' => 'OR',
[
'key' => WPSEO_Meta::$meta_prefix . 'content_score',
'value' => $rank->get_starting_score(),
'type' => 'numeric',
'compare' => '<',
],
[
'key' => WPSEO_Meta::$meta_prefix . 'content_score',
'value' => 'needs-a-value-anyway',
'compare' => 'NOT EXISTS',
],
],
];
}

/**
* Creates a filter to retrieve posts that have bad readability scores, including those that have not enough content to have one.
*
* @return array<array<string>> Array containing the bad readability filter.
*/
protected function create_bad_readability_scores_filter() {
$rank = new WPSEO_Rank( WPSEO_Rank::BAD );
return [
'relation' => 'OR',
[
'key' => WPSEO_Meta::$meta_prefix . 'content_score',
'value' => [ $rank->get_starting_score(), $rank->get_end_score() ],
'type' => 'numeric',
'compare' => 'BETWEEN',
],
[
[
'key' => WPSEO_Meta::$meta_prefix . 'content_score',
'value' => 'needs-a-value-anyway',
'compare' => 'NOT EXISTS',
],
[
'key' => WPSEO_Meta::$meta_prefix . 'estimated-reading-time-minutes',
'compare' => 'EXISTS',
],
],
];
}

Expand Down Expand Up @@ -705,7 +767,7 @@ protected function uses_default_indexing( $post_id ) {
*
* @param string $order_by The ID of the column by which to order the posts.
*
* @return array Array containing the order filters.
* @return array<string> Array containing the order filters.
*/
private function filter_order_by( $order_by ) {
switch ( $order_by ) {
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"Yoast\\WP\\SEO\\Composer\\Actions::check_coding_standards"
],
"check-cs-thresholds": [
"@putenv YOASTCS_THRESHOLD_ERRORS=2479",
"@putenv YOASTCS_THRESHOLD_ERRORS=2474",
"@putenv YOASTCS_THRESHOLD_WARNINGS=252",
"Yoast\\WP\\SEO\\Composer\\Actions::check_cs_thresholds"
],
Expand Down
2 changes: 1 addition & 1 deletion inc/class-wpseo-admin-bar-menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ protected function add_root_menu( WP_Admin_Bar $wp_admin_bar ) {
'parent' => self::MENU_IDENTIFIER,
'id' => 'wpseo-notifications',
'title' => __( 'Notifications', 'wordpress-seo' ) . $counter,
'href' => $settings_url,
'href' => empty( $settings_url ) ? '' : $settings_url . '#/alert-center',
'meta' => [ 'tabindex' => ! empty( $settings_url ) ? false : '0' ],
];
$wp_admin_bar->add_menu( $admin_bar_menu_args );
Expand Down
7 changes: 6 additions & 1 deletion inc/class-wpseo-rank.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@ public function get_drop_down_readability_labels() {
__( 'Readability: %s', 'wordpress-seo' ),
__( 'Good', 'wordpress-seo' )
),
self::NO_FOCUS => sprintf(
/* translators: %s expands to the readability score */
__( 'Readability: %s', 'wordpress-seo' ),
__( 'Not analyzed', 'wordpress-seo' )
),
];

return $labels[ $this->rank ];
Expand Down Expand Up @@ -313,7 +318,7 @@ public static function get_all_ranks() {
* @return WPSEO_Rank[]
*/
public static function get_all_readability_ranks() {
return array_map( [ 'WPSEO_Rank', 'create_rank' ], [ self::BAD, self::OK, self::GOOD ] );
return array_map( [ 'WPSEO_Rank', 'create_rank' ], [ self::BAD, self::OK, self::GOOD, self::NO_FOCUS ] );
}

/**
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@yoast/postcss-preset": "^1.2.0",
"@yoast/related-keyphrase-suggestions": "^0.1.0",
"@yoast/tailwindcss-preset": "^2.3.0",
"@yoast/ui-library": "^4.0.0",
"@yoast/ui-library": "^4.1.0",
"colors": "1.4.0",
"copy-webpack-plugin": "^12.0.2",
"core-js": "^2.6.12",
Expand Down Expand Up @@ -77,7 +77,7 @@
"webpack-bundle-analyzer": "^4.9.1"
},
"yoast": {
"pluginVersion": "24.0"
"pluginVersion": "24.1"
},
"version": "0.0.0"
}
7 changes: 7 additions & 0 deletions packages/js/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,12 @@ module.exports = {
"react/display-name": 0,
},
},
// Ignore Proptypes in the dashboard.
{
files: [ "src/dashboard/**/*.js" ],
rules: {
"react/prop-types": 0,
},
},
],
};
2 changes: 1 addition & 1 deletion packages/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@yoast/search-metadata-previews": "^3.0.0-alpha.0",
"@yoast/social-metadata-forms": "^2.0.0-alpha.0",
"@yoast/style-guide": "^0.14.0-alpha.0",
"@yoast/ui-library": "^4.0.0",
"@yoast/ui-library": "^4.1.0",
"a11y-speak": "git+https://github.com/Yoast/a11y-speak.git#main",
"babel-polyfill": "^6.26.0",
"bowser": "^2.11.0",
Expand Down
34 changes: 34 additions & 0 deletions packages/js/src/dashboard/components/dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Scores } from "../scores/components/scores";
import { PageTitle } from "./page-title";

/**
* @type {import("../index").ContentType} ContentType
* @type {import("../index").Features} Features
* @type {import("../index").Endpoints} Endpoints
* @type {import("../index").Links} Links
*/

/**
* @param {ContentType[]} contentTypes The content types.
* @param {string} userName The user name.
* @param {Features} features Whether features are enabled.
* @param {Endpoints} endpoints The endpoints.
* @param {Object<string,string>} headers The headers for the score requests.
* @param {Links} links The links.
* @returns {JSX.Element} The element.
*/
export const Dashboard = ( { contentTypes, userName, features, endpoints, headers, links } ) => {
return (
<>
<PageTitle userName={ userName } features={ features } links={ links } />
<div className="yst-flex yst-flex-col @7xl:yst-flex-row yst-gap-6 yst-my-6">
{ features.indexables && features.seoAnalysis && (
<Scores analysisType="seo" contentTypes={ contentTypes } endpoint={ endpoints.seoScores } headers={ headers } />
) }
{ features.indexables && features.readabilityAnalysis && (
<Scores analysisType="readability" contentTypes={ contentTypes } endpoint={ endpoints.readabilityScores } headers={ headers } />
) }
</div>
</>
);
};
67 changes: 67 additions & 0 deletions packages/js/src/dashboard/components/page-title.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { createInterpolateElement } from "@wordpress/element";
import { __, sprintf } from "@wordpress/i18n";
import { Alert, Link, Paper, Title } from "@yoast/ui-library";
import { OutboundLink } from "../../shared-admin/components";

/**
* @type {import("../index").Features} Features
* @type {import("../index").Links} Links
*/

/**
* @param {string} userName The user name.
* @param {Features} features Whether features are enabled.
* @param {Links} links The links.
* @returns {JSX.Element} The element.
*/
export const PageTitle = ( { userName, features, links } ) => (
<Paper>
<Paper.Content className="yst-flex yst-flex-col yst-gap-y-4 yst-max-w-screen-sm">
<Title as="h1">
{ sprintf(
__( "Hi %s,", "wordpress-seo" ),
userName
) }
</Title>
<p className="yst-text-tiny">
{ features.indexables && ! features.seoAnalysis && ! features.readabilityAnalysis
? createInterpolateElement(
sprintf(
/**
* translators: %1$s and %2$s expand to an opening and closing anchor tag, to the site features page.
* %3$s and %4$s expand to an opening and closing anchor tag, to the user profile page.
**/
__( "It looks like the ‘SEO analysis’ and the ‘Readability analysis’ are currently disabled in your %1$sSite features%2$s or your %3$suser profile settings%4$s. Enable these features to start seeing all the insights you need right here!", "wordpress-seo" ),
"<link>",
"</link>",
"<profilelink>",
"</profilelink>"
),
{
// Added dummy space as content to prevent children prop warnings in the console.
link: <Link href="admin.php?page=wpseo_page_settings#/site-features"> </Link>,
profilelink: <Link href="profile.php"> </Link>,
}
)
: createInterpolateElement(
sprintf(
/* translators: %1$s and %2$s expand to an opening and closing anchor tag. */
__( "Welcome to your dashboard! Check your content's SEO performance, readability, and overall strengths and opportunities. %1$sLearn more about the dashboard%2$s.", "wordpress-seo" ),
"<link>",
"</link>"
),
{
// Added dummy space as content to prevent children prop warnings in the console.
link: <OutboundLink href={ links.dashboardLearnMore }> </OutboundLink>,
}
)
}
</p>
{ ! features.indexables && (
<Alert type="info">
{ __( "Oops! You can’t see the overview of your SEO scores and readability scores right now because you’re in a non-production environment.", "wordpress-seo" ) }
</Alert>
) }
</Paper.Content>
</Paper>
);
18 changes: 18 additions & 0 deletions packages/js/src/dashboard/fetch/fetch-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getResponseError } from "./get-response-error";

/**
* @param {string|URL} url The URL to fetch from.
* @param {RequestInit} options The request options.
* @returns {Promise<any|Error>} The promise of a result, or an error.
*/
export const fetchJson = async( url, options ) => {
try {
const response = await fetch( url, options );
if ( ! response.ok ) {
throw getResponseError( response );
}
return response.json();
} catch ( e ) {
return Promise.reject( e );
}
};
14 changes: 14 additions & 0 deletions packages/js/src/dashboard/fetch/get-response-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { TimeoutError } from "./timeout-error";

/**
* @param {Response} response The response.
* @returns {Error} The error that corresponds to the response.
*/
export const getResponseError = ( response ) => {
switch ( response.status ) {
case 408:
return new TimeoutError( "request timed out" );
default:
return new Error( "not ok" );
}
};
12 changes: 12 additions & 0 deletions packages/js/src/dashboard/fetch/timeout-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Represents a timeout error.
*/
export class TimeoutError extends Error {
/**
* @param {string} message The error message.
*/
constructor( message ) {
super( message );
this.name = "TimeoutError";
}
}
Loading

0 comments on commit f727461

Please sign in to comment.