rebase from live enviornment

This commit is contained in:
Rachit Bhargava
2024-01-09 22:14:20 -05:00
parent ff0b49a046
commit 3a22fcaa4a
15968 changed files with 2344674 additions and 45234 deletions

View File

@@ -10,8 +10,6 @@ use Yoast\WP\SEO\Introductions\Domain\Introduction_Interface;
* Represents the introduction for the AI generate titles and introduction upsell.
*
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
*
* @makePublic
*/
class Ai_Generate_Titles_And_Descriptions_Introduction_Upsell implements Introduction_Interface {
@@ -47,13 +45,27 @@ class Ai_Generate_Titles_And_Descriptions_Introduction_Upsell implements Introdu
$this->options_helper = $options_helper;
}
/**
* Returns the ID.
*
* @return string
*/
public function get_id() {
return 'ai-generate-titles-and-descriptions-upsell';
}
/**
* Returns the unique name.
*
* @deprecated 21.6
* @codeCoverageIgnore
*
* @return string
*/
public function get_name() {
return 'ai-generate-titles-and-descriptions-upsell';
_deprecated_function( __METHOD__, 'Yoast SEO 21.6', 'Please use get_id() instead' );
return $this->get_id();
}
/**
@@ -80,10 +92,6 @@ class Ai_Generate_Titles_And_Descriptions_Introduction_Upsell implements Introdu
return false;
}
if ( ! $this->is_version_between( $this->product_helper->get_version(), '20.11-RC4', '21.1-RC0' ) ) {
return false;
}
if ( ! $this->is_user_allowed( [ 'edit_posts' ] ) ) {
return false;
}

View File

@@ -5,11 +5,10 @@ namespace Yoast\WP\SEO\Introductions\Application;
use Yoast\WP\SEO\Introductions\Domain\Introduction_Interface;
use Yoast\WP\SEO\Introductions\Domain\Introduction_Item;
use Yoast\WP\SEO\Introductions\Domain\Introductions_Bucket;
use Yoast\WP\SEO\Introductions\Infrastructure\Introductions_Seen_Repository;
/**
* Manages the collection of introductions.
*
* @makePublic
*/
class Introductions_Collector {
@@ -44,11 +43,11 @@ class Introductions_Collector {
if ( ! $introduction->should_show() ) {
continue;
}
if ( $this->is_seen( $introduction->get_name(), $metadata ) ) {
if ( $this->is_seen( $introduction->get_id(), $metadata ) ) {
continue;
}
$bucket->add_introduction(
new Introduction_Item( $introduction->get_name(), $introduction->get_priority() )
new Introduction_Item( $introduction->get_id(), $introduction->get_priority() )
);
}
@@ -87,7 +86,7 @@ class Introductions_Collector {
* @return array The introductions' metadata.
*/
private function get_metadata( $user_id ) {
$metadata = \get_user_meta( $user_id, '_yoast_wpseo_introductions', true );
$metadata = \get_user_meta( $user_id, Introductions_Seen_Repository::USER_META_KEY, true );
if ( \is_array( $metadata ) ) {
return $metadata;
}
@@ -110,4 +109,21 @@ class Introductions_Collector {
return false;
}
/**
* Checks if the given introduction ID is a known ID to the system.
*
* @param string $introduction_id The introduction ID to check.
*
* @return bool
*/
public function is_available_introduction( string $introduction_id ): bool {
foreach ( $this->introductions as $introduction ) {
if ( $introduction->get_id() === $introduction_id ) {
return true;
}
}
return false;
}
}

View File

@@ -7,9 +7,19 @@ namespace Yoast\WP\SEO\Introductions\Domain;
*/
interface Introduction_Interface {
/**
* Returns the ID.
*
* @return string
*/
public function get_id();
/**
* Returns the unique name.
*
* @deprecated 21.6
* @codeCoverageIgnore
*
* @return string
*/
public function get_name();

View File

@@ -8,11 +8,11 @@ namespace Yoast\WP\SEO\Introductions\Domain;
class Introduction_Item {
/**
* The unique name.
* The ID.
*
* @var string
*/
private $name;
private $id;
/**
* The priority.
@@ -24,11 +24,11 @@ class Introduction_Item {
/**
* Constructs the instance.
*
* @param string $name The unique name.
* @param string $id The ID.
* @param int $priority The priority.
*/
public function __construct( $name, $priority ) {
$this->name = $name;
public function __construct( $id, $priority ) {
$this->id = $id;
$this->priority = $priority;
}
@@ -39,18 +39,18 @@ class Introduction_Item {
*/
public function to_array() {
return [
'name' => $this->get_name(),
'id' => $this->get_id(),
'priority' => $this->get_priority(),
];
}
/**
* Returns the unique name.
* Returns the ID.
*
* @return string
*/
public function get_name() {
return $this->name;
public function get_id() {
return $this->id;
}
/**

View File

@@ -0,0 +1,29 @@
<?php
namespace Yoast\WP\SEO\Introductions\Domain;
use InvalidArgumentException;
use Throwable;
/**
* Invalid user ID.
*/
class Invalid_User_Id_Exception extends InvalidArgumentException {
/**
* Constructs the exception.
*
* @link https://php.net/manual/en/exception.construct.php
*
* @param string $message [optional] The Exception message to throw.
* @param int $code [optional] The Exception code.
* @param null|Throwable $previous [optional] The previous throwable used for the exception chaining.
*/
public function __construct( // phpcs:ignore Generic.CodeAnalysis.UselessOverridingMethod.Found -- Reason: A default message is used.
$message = 'Invalid User ID',
$code = 0,
$previous = null
) {
parent::__construct( $message, $code, $previous );
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace Yoast\WP\SEO\Introductions\Infrastructure;
use Yoast\WP\SEO\Helpers\User_Helper;
use Yoast\WP\SEO\Introductions\Domain\Invalid_User_Id_Exception;
/**
* Stores and retrieves whether the user has seen certain introductions.
*/
class Introductions_Seen_Repository {
const USER_META_KEY = '_yoast_wpseo_introductions';
const DEFAULT_VALUE = [];
/**
* Holds the User_Helper instance.
*
* @var User_Helper
*/
private $user_helper;
/**
* Constructs the class.
*
* @param User_Helper $user_helper The User_Helper.
*/
public function __construct( User_Helper $user_helper ) {
$this->user_helper = $user_helper;
}
/**
* Retrieves the introductions.
*
* @param int $user_id User ID.
*
* @throws Invalid_User_Id_Exception If an invalid user ID is supplied.
*
* @return array The introductions.
*/
public function get_all_introductions( $user_id ): array {
$seen_introductions = $this->user_helper->get_meta( $user_id, self::USER_META_KEY, true );
if ( $seen_introductions === false ) {
throw new Invalid_User_Id_Exception();
}
if ( \is_array( $seen_introductions ) ) {
return $seen_introductions;
}
/**
* Why could $value be invalid?
* - When the database row does not exist yet, $value can be an empty string.
* - Faulty data was stored?
*/
return self::DEFAULT_VALUE;
}
/**
* Sets the introductions.
*
* @param int $user_id The user ID.
* @param array $introductions The introductions.
*
* @return bool True on successful update, false on failure or if the value passed to the function is the same as
* the one that is already in the database.
*/
public function set_all_introductions( $user_id, array $introductions ): bool {
return $this->user_helper->update_meta( $user_id, self::USER_META_KEY, $introductions ) !== false;
}
/**
* Retrieves whether an introduction is seen.
*
* @param int $user_id User ID.
* @param string $introduction_id The introduction ID.
*
* @throws Invalid_User_Id_Exception If an invalid user ID is supplied.
*
* @return bool Whether the introduction is seen.
*/
public function is_introduction_seen( $user_id, string $introduction_id ): bool {
$introductions = $this->get_all_introductions( $user_id );
if ( \array_key_exists( $introduction_id, $introductions ) ) {
return (bool) $introductions[ $introduction_id ];
}
return false;
}
/**
* Sets the introduction as seen.
*
* @param int $user_id The user ID.
* @param string $introduction_id The introduction ID.
* @param bool $is_seen Whether the introduction is seen. Defaults to true.
*
* @throws Invalid_User_Id_Exception If an invalid user ID is supplied.
*
* @return bool False on failure. Not having to update is a success.
*/
public function set_introduction( $user_id, string $introduction_id, bool $is_seen = true ): bool {
$introductions = $this->get_all_introductions( $user_id );
// Check if the wanted value is already set.
if ( \array_key_exists( $introduction_id, $introductions ) && $introductions[ $introduction_id ] === $is_seen ) {
return true;
}
$introductions[ $introduction_id ] = $is_seen;
return $this->set_all_introductions( $user_id, $introductions );
}
}

View File

@@ -4,13 +4,15 @@ Is for showing introductions to a user, on Yoast admin pages.
Based on plugin version, page, user capabilities and whether the user has seen it already.
- `Introduction_Interface` defines what data is needed
- `name` as unique identifier
- `id` as unique identifier
- `plugin` and `version` to determine if the introduction is new (version > plugin version)
- `pages` to be able to only show on certain Yoast admin pages
- `capabilities` to be able to only show for certain users
- `Introductions_Collector` uses that data to determine whether an introduction should be "shown" to a user
- uses the `wpseo_introductions` filter to be extendable from our other plugins
- uses `_yoast_introductions` user metadata to determine if the user saw an introduction already
- uses `Introductions_Seen_Repository` to get the data to determine if the user saw an introduction already
- `Introductions_Seen_Repository` is the doorway whether a user has seen an introduction or not
- uses the `_yoast_introductions` user metadata
- `Introduction_Bucket` and `Introduction_Item` are used by the collector to get an array
- `Introductions_Integration` runs on the Yoast Admin pages and loads the assets
- only loads on our Yoast admin pages, but never on our installation success pages as to not disturb onboarding
@@ -20,6 +22,6 @@ Based on plugin version, page, user capabilities and whether the user has seen i
- `css/src/ai-generator.css` holds the CSS
Inside JS, register the modal content via `window.YoastSEO._registerIntroductionComponent`, which takes a
`name` and a `Component`. The name needs to be the same as the name in the `Introduction_Interface`.
`id` and a `Component`. The id needs to be the same as the id in the `Introduction_Interface`.
The action `yoast.introductions.ready` can be used to know whether the registration function is available and ready for
use.

View File

@@ -153,7 +153,7 @@ class Introductions_Integration implements Integration_Interface {
$metadata = [];
}
foreach ( $introductions as $introduction ) {
$metadata[ $introduction['name'] ] = true;
$metadata[ $introduction['id'] ] = true;
}
$this->user_helper->update_meta( $user_id, '_yoast_wpseo_introductions', $metadata );
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Yoast\WP\SEO\Introductions\User_Interface;
use Exception;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Helpers\User_Helper;
use Yoast\WP\SEO\Introductions\Application\Introductions_Collector;
use Yoast\WP\SEO\Introductions\Infrastructure\Introductions_Seen_Repository;
use Yoast\WP\SEO\Main;
use Yoast\WP\SEO\Routes\Route_Interface;
/**
* Registers a route to set whether the user has seen an introduction.
*
* @makePublic
*/
class Introductions_Seen_Route implements Route_Interface {
use No_Conditionals;
/**
* Represents the prefix.
*
* @var string
*/
const ROUTE_PREFIX = '/introductions/(?P<introduction_id>[\w-]+)/seen';
/**
* Holds the introductions collector instance.
*
* @var Introductions_Collector
*/
private $introductions_collector;
/**
* Holds the repository.
*
* @var Introductions_Seen_Repository
*/
private $introductions_seen_repository;
/**
* Holds the user helper.
*
* @var User_Helper
*/
private $user_helper;
/**
* Constructs the class.
*
* @param Introductions_Seen_Repository $introductions_seen_repository The repository.
* @param User_Helper $user_helper The user helper.
* @param Introductions_Collector $introductions_collector The introduction collector.
*/
public function __construct(
Introductions_Seen_Repository $introductions_seen_repository,
User_Helper $user_helper,
Introductions_Collector $introductions_collector
) {
$this->introductions_seen_repository = $introductions_seen_repository;
$this->user_helper = $user_helper;
$this->introductions_collector = $introductions_collector;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
\register_rest_route(
Main::API_V1_NAMESPACE,
self::ROUTE_PREFIX,
[
[
'methods' => 'POST',
'callback' => [ $this, 'set_introduction_seen' ],
'permission_callback' => [ $this, 'permission_edit_posts' ],
'args' => [
'introduction_id' => [
'required' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
],
'is_seen' => [
'required' => false,
'type' => 'bool',
'default' => true,
'sanitize_callback' => 'rest_sanitize_boolean',
],
],
],
]
);
}
/**
* Sets whether the introduction is seen.
*
* @param WP_REST_Request $request The request object.
*
* @return WP_REST_Response|WP_Error The success or failure response.
*/
public function set_introduction_seen( WP_REST_Request $request ) {
$params = $request->get_params();
$introduction_id = $params['introduction_id'];
$is_seen = $params['is_seen'];
if ( $this->introductions_collector->is_available_introduction( $introduction_id ) ) {
try {
$user_id = $this->user_helper->get_current_user_id();
$result = $this->introductions_seen_repository->set_introduction( $user_id, $introduction_id, $is_seen );
} catch ( Exception $exception ) {
return new WP_Error(
'wpseo_introductions_seen_error',
$exception->getMessage(),
(object) []
);
}
return new WP_REST_Response(
[
'json' => (object) [
'success' => $result,
],
],
( $result ) ? 200 : 400
);
}
return new WP_REST_Response( [], 400 );
}
/**
* Permission callback.
*
* @return bool True when user has 'edit_posts' permission.
*/
public function permission_edit_posts() {
return \current_user_can( 'edit_posts' );
}
}

View File

@@ -14,8 +14,6 @@ use Yoast\WP\SEO\Routes\Route_Interface;
/**
* Registers a route to offer get/set of the wistia embed permission for a user.
*
* @makePublic
*/
class Wistia_Embed_Permission_Route implements Route_Interface {