Skip to content

Commit

Permalink
Merge branch 'trunk' of github.com:Yoast/wordpress-seo into feature/l…
Browse files Browse the repository at this point in the history
…ingo-fixes
  • Loading branch information
FAMarfuaty committed Oct 31, 2023
2 parents f60ea93 + 2b55ed2 commit 6902f5b
Show file tree
Hide file tree
Showing 33 changed files with 1,049 additions and 175 deletions.
1 change: 0 additions & 1 deletion admin/metabox/class-metabox.php
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,6 @@ public function enqueue() {
'isJetpackBoostNotPremium' => ( $is_block_editor ) ? YoastSEO()->classes->get( Jetpack_Boost_Not_Premium_Conditional::class )->is_met() : false,
'isWooCommerceActive' => $woocommerce_active,
'woocommerceUpsell' => get_post_type( $post_id ) === 'product' && ! $woocommerce_seo_active && $woocommerce_active,
'isWooCommerceActive' => $woocommerce_active,
'linkParams' => WPSEO_Shortlinker::get_query_params(),
'pluginUrl' => \plugins_url( '', \WPSEO_FILE ),
'wistiaEmbedPermission' => YoastSEO()->classes->get( Wistia_Embed_Permission_Repository::class )->get_value_for_user( \get_current_user_id() ),
Expand Down
23 changes: 12 additions & 11 deletions admin/watchers/class-slug-change-watcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ public function detect_post_trash( $post_id ) {
$post_label = $this->get_post_type_label( get_post_type( $post_id ) );

/* translators: %1$s expands to the translated name of the post type. */
$first_sentence = sprintf( __( 'You just trashed a %1$s.', 'wordpress-seo' ), $post_label );
$message = $this->get_message( $first_sentence, 'trashed', $post_label );
$first_sentence = sprintf( __( 'You just trashed a %1$s.', 'wordpress-seo' ), $post_label );
$second_sentence = __( 'Search engines and other websites can still send traffic to your trashed content.', 'wordpress-seo' );
$message = $this->get_message( $first_sentence, $second_sentence );

$this->add_notification( $message );
}
Expand All @@ -85,8 +86,9 @@ public function detect_post_delete( $post_id ) {
$post_label = $this->get_post_type_label( get_post_type( $post_id ) );

/* translators: %1$s expands to the translated name of the post type. */
$first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $post_label );
$message = $this->get_message( $first_sentence, 'deleted', $post_label );
$first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $post_label );
$second_sentence = __( 'Search engines and other websites can still send traffic to your deleted content.', 'wordpress-seo' );
$message = $this->get_message( $first_sentence, $second_sentence );

$this->add_notification( $message );
}
Expand All @@ -107,8 +109,9 @@ public function detect_term_delete( $term_taxonomy_id ) {
$term_label = $this->get_taxonomy_label_for_term( $term->term_id );

/* translators: %1$s expands to the translated name of the term. */
$first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $term_label );
$message = $this->get_message( $first_sentence, 'deleted', $term_label );
$first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $term_label );
$second_sentence = __( 'Search engines and other websites can still send traffic to your deleted content.', 'wordpress-seo' );
$message = $this->get_message( $first_sentence, $second_sentence );

$this->add_notification( $message );
}
Expand Down Expand Up @@ -209,17 +212,15 @@ protected function check_visible_post_status( $post_status ) {
* Returns the message around changed URLs.
*
* @param string $first_sentence The first sentence of the notification.
* @param string $action The action performed, either "deleted" or "trashed".
* @param string $object_label The label of the object that was deleted or trashed.
* @param string $second_sentence The second sentence of the notification.
*
* @return string The full notification.
*/
protected function get_message( $first_sentence, $action, $object_label ) {
protected function get_message( $first_sentence, $second_sentence ) {
return '<h2>' . __( 'Make sure you don\'t miss out on traffic!', 'wordpress-seo' ) . '</h2>'
. '<p>'
. $first_sentence
/* translators: %1$s expands to either "deleted" or "trashed". %2$s expands to the name of the post or term. */
. ' ' . sprintf( __( 'Search engines and other websites can still send traffic to your %1$s %2$s.', 'wordpress-seo' ), $action, $object_label )
. ' ' . $second_sentence
. ' ' . __( 'You should create a redirect to ensure your visitors do not get a 404 error when they click on the no longer working URL.', 'wordpress-seo' )
/* translators: %s expands to Yoast SEO Premium */
. ' ' . sprintf( __( 'With %s, you can easily create such redirects.', 'wordpress-seo' ), 'Yoast SEO Premium' )
Expand Down
3 changes: 2 additions & 1 deletion inc/options/class-wpseo-option-wpseo.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class WPSEO_Option_Wpseo extends WPSEO_Option {
'enable_xml_sitemap' => true,
'enable_text_link_counter' => true,
'enable_index_now' => true,
'enable_ai_generator' => false,
'enable_ai_generator' => true,
'ai_enabled_pre_default' => false,
'show_onboarding_notice' => false,
'first_activated_on' => false,
'myyoast-oauth' => [
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"typescript": "^4.2.4"
},
"yoast": {
"pluginVersion": "21.5-RC4"
"pluginVersion": "21.5"
},
"version": "0.0.0"
}
4 changes: 2 additions & 2 deletions packages/js/src/ai-generator/components/modal-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const STORE = "yoast-seo/editor";
*/
export const ModalContent = () => {
const learnMoreLink = useSelect( select => select( STORE ).selectLink( "https://www.yoa.st/ai-generator-learn-more" ), [] );
const upsellLink = useSelect( select => select( STORE ).selectLink( "https://yoa.st/ai-generator-upsell" ), [] );


const imageLink = useSelect( select => select( STORE ).selectImageLink( "ai-generator-preview.png" ), [] );
const thumbnail = useMemo( () => ( {
Expand All @@ -23,10 +23,10 @@ export const ModalContent = () => {
const { setWistiaEmbedPermission: set } = useDispatch( STORE );
const wistiaEmbedPermission = useMemo( () => ( { value, status, set } ), [ value, status, set ] );


return (
<AiGenerateTitlesAndDescriptionsUpsell
learnMoreLink={ learnMoreLink }
upsellLink={ upsellLink }
thumbnail={ thumbnail }
wistiaEmbedPermission={ wistiaEmbedPermission }
/>
Expand Down
10 changes: 8 additions & 2 deletions packages/js/src/ai-generator/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,25 @@ AiGeneratorUpsell.propTypes = {
fieldId: PropTypes.string.isRequired,
};

const STORE = "yoast-seo/editor";

/**
* Initializes the AI Generator upsell.
*
* @returns {void}
*/
const initializeAiGenerator = () => {
const isPremium = select( "yoast-seo/editor" ).getIsPremium();
const isPremium = select( STORE ).getIsPremium();
const isWooSeoUpsell = select( STORE ).getIsWooSeoUpsell();
const isProduct = select( STORE ).getIsProduct();

const shouldShowAiGeneratorUpsell = ( isProduct ) ? ! isPremium || isWooSeoUpsell : ! isPremium;

addFilter(
"yoast.replacementVariableEditor.additionalButtons",
"yoast/yoast-seo-premium/AiGenerator",
( buttons, { fieldId } ) => {
if ( ! isPremium ) {
if ( shouldShowAiGeneratorUpsell ) {
buttons.push(
<Fill name={ `yoast.replacementVariableEditor.additionalButtons.${ fieldId }` }>
<AiGeneratorUpsell fieldId={ fieldId } />
Expand Down
2 changes: 1 addition & 1 deletion packages/js/src/introductions/components/introduction.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const Introduction = () => {
const introduction = useSelect( select => select( STORE_NAME_INTRODUCTIONS ).selectCurrentIntroduction(), [] );
const components = useIntroductionsContext();

const Component = useMemo( () => components?.[ introduction?.name ], [ introduction, components ] );
const Component = useMemo( () => components?.[ introduction?.id ], [ introduction, components ] );

if ( ! Component ) {
return null;
Expand Down
8 changes: 4 additions & 4 deletions packages/js/src/introductions/components/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ export const IntroductionProvider = ( { children, initialComponents } ) => {
const [ components, setComponents ] = useState( initialComponents );
const introductions = useSelect( select => select( STORE_NAME_INTRODUCTIONS ).selectIntroductions(), [] );

const registerComponent = useCallback( ( name, Component ) => {
const introduction = find( introductions, { name } );
const registerComponent = useCallback( ( id, Component ) => {
const introduction = find( introductions, { id } );
if ( ! introduction ) {
// Bail when unknown.
console.error( "Warning: Introductions received a registration for an unknown key:", name );
console.error( "Warning: Introductions received a registration for an unknown key:", id );
return;
}
setComponents( currentComponents => ( { ...currentComponents, [ name ]: Component } ) );
setComponents( currentComponents => ( { ...currentComponents, [ id ]: Component } ) );
}, [ introductions, setComponents ] );

useEffect( () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/js/src/introductions/store/introductions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { get, map } from "lodash";
export const INTRODUCTIONS_NAME = "introductions";

const adapter = createEntityAdapter( {
selectId: introduction => introduction.name,
selectId: introduction => introduction.id,
sortComparer: ( a, b ) => {
if ( a.priority === b.priority ) {
return 0;
Expand All @@ -15,10 +15,10 @@ const adapter = createEntityAdapter( {

/**
* @param {Object} introduction The introduction.
* @returns {{name: string, priority: number}} The prepared introduction.
* @returns {{id: string, priority: number}} The prepared introduction.
*/
const prepareIntroduction = introduction => ( {
name: introduction.name || nanoid(),
id: introduction.id || nanoid(),
priority: introduction.priority || 0,
} );

Expand Down
2 changes: 1 addition & 1 deletion packages/js/src/post-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ domReady( () => {
initializeInsights();


const AI_IGNORED_POST_TYPES = [ "attachment", "product" ];
const AI_IGNORED_POST_TYPES = [ "attachment" ];

if ( window.wpseoScriptData.postType && ! AI_IGNORED_POST_TYPES.includes( window.wpseoScriptData.postType ) ) {
// Initialize the AI Generator upsell.
Expand Down
1 change: 1 addition & 0 deletions packages/js/src/redux/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ export * from "./WincherModal";
export * from "./WincherRequest";
export * from "./WincherSEOPerformance";
export * from "./isPremium";
export * from "./isWooSEO";
export * from "./postId";
8 changes: 8 additions & 0 deletions packages/js/src/redux/selectors/isWooSEO.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { get } from "lodash";

/**
* Determines whether the WooCommerce SEO addon is not active in a product page.
*
* @returns {Boolean} Whether the plugin is WooCommerce SEO or not.
*/
export const getIsWooSeoUpsell = () => get( window, "wpseoScriptData.woocommerceUpsell", false );
Original file line number Diff line number Diff line change
@@ -1,20 +1,90 @@
/* eslint-disable complexity */
import { LockOpenIcon } from "@heroicons/react/outline";
import { ArrowNarrowRightIcon } from "@heroicons/react/solid";
import { createInterpolateElement } from "@wordpress/element";
import { useSelect } from "@wordpress/data";
import { __, sprintf } from "@wordpress/i18n";
import { Badge, Button, useModalContext } from "@yoast/ui-library";
import PropTypes from "prop-types";
import { OutboundLink, VideoFlow } from ".";

const STORE = "yoast-seo/editor";

/**
* @param {string} learnMoreLink The learn more link.
* @param {string} upsellLink The upsell link.
* @param {Object} thumbnail The thumbnail: img props.
* @param {Object} wistiaEmbedPermission The value, status and set for the Wistia embed permission.
* @returns {JSX.Element} The element.
*/
export const AiGenerateTitlesAndDescriptionsUpsell = ( { learnMoreLink, upsellLink, thumbnail, wistiaEmbedPermission } ) => {
export const AiGenerateTitlesAndDescriptionsUpsell = ( { learnMoreLink, thumbnail, wistiaEmbedPermission } ) => {
const { onClose, initialFocus } = useModalContext();
const upsellLinkPremium = useSelect( select => select( STORE ).selectLink( "https://yoa.st/ai-generator-upsell" ), [] );
const upsellLinkWooPremiumBundle = useSelect( select => select( STORE ).selectLink( "https://yoa.st/ai-generator-upsell-woo-seo-premium-bundle" ), [] );
const upsellLinkWoo = useSelect( select => select( STORE ).selectLink( "https://yoa.st/ai-generator-upsell-woo-seo" ), [] );
const isPremium = useSelect( select => select( STORE ).getIsPremium(), [] );
const isWooSeoUpsell = useSelect( select => select( STORE ).getIsWooSeoUpsell(), [] );
const isProduct = useSelect( select => select( STORE ).getIsProduct(), [] );

const wooSeoNoPremium = isProduct && ! isWooSeoUpsell && ! isPremium;
const isProductCopy = isWooSeoUpsell || wooSeoNoPremium;
let upsellLink = upsellLinkPremium;
let newToText = sprintf(
/* translators: %1$s expands to Yoast SEO Premium. */
__( "New to %1$s", "wordpress-seo" ),
"Yoast SEO Premium"
);
const learnMoreLinkStructure = {
// eslint-disable-next-line jsx-a11y/anchor-has-content
a: <OutboundLink
href={ learnMoreLink }
className="yst-inline-flex yst-items-center yst-gap-1 yst-no-underline yst-font-medium"
variant="primary"
/>,
ArrowNarrowRightIcon: <ArrowNarrowRightIcon className="yst-w-4 yst-h-4 rtl:yst-rotate-180" />,
};

let upsellLabel = sprintf(
/* translators: %1$s expands to Yoast SEO Premium. */
__( "Unlock with %1$s", "wordpress-seo" ),
"Yoast SEO Premium"
);
let bundleNote = "";
let title = __( "Generate titles & descriptions with Yoast AI!", "wordpress-seo" );


if ( isProductCopy ) {
const upsellPremiumWooLabel = sprintf(
/* translators: %1$s expands to Yoast SEO Premium, %2$s expands to Yoast WooCommerce SEO. */
__( "%1$s + %2$s", "wordpress-seo" ),
"Yoast SEO Premium",
"Yoast WooCommerce SEO"
);
newToText = sprintf(
/* translators: %1$s expands to Yoast SEO Premium and Yoast WooCommerce SEO. */
__( "New to %1$s", "wordpress-seo" ),
upsellPremiumWooLabel
);
title = __( "Generate product titles & descriptions with AI!", "wordpress-seo" );
if ( ! isPremium && isWooSeoUpsell ) {
upsellLabel = `${sprintf(
/* translators: %1$s expands to Woo Premium bundle. */
__( "Unlock with the %1$s", "wordpress-seo" ),
"Woo Premium bundle"
)}*`;
bundleNote = <div className="yst-text-xs yst-text-slate-500 yst-mt-2">
{ `*${upsellPremiumWooLabel}` }
</div>;
upsellLink = upsellLinkWooPremiumBundle;
}
if ( isPremium ) {
upsellLabel = sprintf(
/* translators: %1$s expands to Yoast WooCommerce SEO. */
__( "Unlock with %1$s", "wordpress-seo" ),
"Yoast WooCommerce SEO"
);
upsellLink = upsellLinkWoo;
}
}

return (
<div className="yst-flex yst-flex-col yst-items-center yst-p-10">
Expand All @@ -28,41 +98,42 @@ export const AiGenerateTitlesAndDescriptionsUpsell = ( { learnMoreLink, upsellLi
</div>
<div className="yst-mt-6 yst-text-xs yst-font-medium">
<span className="yst-introduction-modal-uppercase">
{ sprintf(
/* translators: %1$s expands to Yoast SEO Premium. */
__( "New to %1$s", "wordpress-seo" ),
"Yoast SEO Premium"
) }
{ newToText }
</span>
&nbsp;
<span className="yst-uppercase yst-text-slate-700">21.0</span>

{ ! isProductCopy && <span className="yst-uppercase yst-text-slate-700"> 21.0</span> }
</div>
<div className="yst-mt-4 yst-mx-1.5 yst-text-center">
<h3 className="yst-text-slate-900 yst-text-lg yst-font-medium">
{ __( "Generate titles & descriptions with Yoast AI!", "wordpress-seo" ) }
{ title }
</h3>
<div className="yst-mt-2 yst-text-slate-600 yst-text-sm">
{ createInterpolateElement(
{ isProductCopy ? createInterpolateElement(
sprintf(
/* translators: %1$s and %2$s are anchor tag; %3$s is the arrow icon. */
__(
"Speed up your workflow with generative AI. Get high-quality title and description suggestions for your search and social appearance. %1$sLearn more%2$s%3$s",
"Speed up your workflow with generative AI. Get high-quality product title and description suggestions for your search and social appearance. %1$sLearn more%2$s%3$s",
"wordpress-seo"
),
"<a>",
"<ArrowNarrowRightIcon />",
"</a>"
),
{
// eslint-disable-next-line jsx-a11y/anchor-has-content
a: <OutboundLink
href={ learnMoreLink }
className="yst-inline-flex yst-items-center yst-gap-1 yst-no-underline yst-font-medium"
variant="primary"
/>,
ArrowNarrowRightIcon: <ArrowNarrowRightIcon className="yst-w-4 yst-h-4 rtl:yst-rotate-180" />,
}
) }
learnMoreLinkStructure
)
: createInterpolateElement(
sprintf(
/* translators: %1$s and %2$s are anchor tag; %3$s is the arrow icon. */
__(
"Speed up your workflow with generative AI. Get high-quality title and description suggestions for your search and social appearance. %1$sLearn more%2$s%3$s",
"wordpress-seo"
),
"<a>",
"<ArrowNarrowRightIcon />",
"</a>"
),
learnMoreLinkStructure
) }
</div>
</div>
<div className="yst-w-full yst-flex yst-mt-10">
Expand All @@ -76,11 +147,7 @@ export const AiGenerateTitlesAndDescriptionsUpsell = ( { learnMoreLink, upsellLi
ref={ initialFocus }
>
<LockOpenIcon className="yst--ml-1 yst-mr-2 yst-h-5 yst-w-5" />
{ sprintf(
/* translators: %1$s expands to Yoast SEO Premium. */
__( "Unlock with %1$s", "wordpress-seo" ),
"Yoast SEO Premium"
) }
{ upsellLabel }
<span className="yst-sr-only">
{
/* translators: Hidden accessibility text. */
Expand All @@ -89,6 +156,7 @@ export const AiGenerateTitlesAndDescriptionsUpsell = ( { learnMoreLink, upsellLi
</span>
</Button>
</div>
{ bundleNote }
<Button
as="a"
className="yst-mt-4"
Expand All @@ -102,7 +170,6 @@ export const AiGenerateTitlesAndDescriptionsUpsell = ( { learnMoreLink, upsellLi
};
AiGenerateTitlesAndDescriptionsUpsell.propTypes = {
learnMoreLink: PropTypes.string.isRequired,
upsellLink: PropTypes.string.isRequired,
thumbnail: PropTypes.shape( {
src: PropTypes.string.isRequired,
width: PropTypes.string,
Expand Down
Loading

0 comments on commit 6902f5b

Please sign in to comment.