Merged in feature/from-pantheon (pull request #16)

code from pantheon

* code from pantheon
This commit is contained in:
Tony Volpe
2024-01-10 17:03:02 +00:00
parent 054b4fffc9
commit 4eb982d7a8
16492 changed files with 3475854 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Response;
/**
* Abstract_Action_Route class.
*
* Abstract class for action routes.
*/
abstract class Abstract_Action_Route implements Route_Interface {
/**
* Responds to an indexing request.
*
* @param array $objects The objects that have been indexed.
* @param string $next_url The url that should be called to continue reindexing. False if done.
*
* @return WP_REST_Response The response.
*/
protected function respond_with( $objects, $next_url ) {
return new WP_REST_Response(
[
'objects' => $objects,
'next_url' => $next_url,
]
);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
/**
* Abstract_Indexation_Route class.
*
* Reindexing route for indexables.
*/
abstract class Abstract_Indexation_Route extends Abstract_Action_Route {
/**
* Runs an indexing action and returns the response.
*
* @param Indexation_Action_Interface $indexation_action The indexing action.
* @param string $url The url of the indexing route.
*
* @return WP_REST_Response The response.
*/
protected function run_indexation_action( Indexation_Action_Interface $indexation_action, $url ) {
$indexables = $indexation_action->index();
$next_url = false;
if ( \count( $indexables ) >= $indexation_action->get_limit() ) {
$next_url = \rest_url( $url );
}
return $this->respond_with( $indexables, $next_url );
}
}

View File

@@ -0,0 +1,104 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Alert_Dismissal_Action;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Main;
/**
* Class Alert_Dismissal_Route.
*/
class Alert_Dismissal_Route implements Route_Interface {
use No_Conditionals;
/**
* Represents the alerts route prefix.
*
* @var string
*/
const ROUTE_PREFIX = 'alerts';
/**
* Represents the dismiss route.
*
* @var string
*/
const DISMISS_ROUTE = self::ROUTE_PREFIX . '/dismiss';
/**
* Represents the full dismiss route.
*
* @var string
*/
const FULL_DISMISS_ROUTE = Main::API_V1_NAMESPACE . '/' . self::DISMISS_ROUTE;
/**
* Represents the alert dismissal action.
*
* @var Alert_Dismissal_Action
*/
protected $alert_dismissal_action;
/**
* Constructs Alert_Dismissal_Route.
*
* @param Alert_Dismissal_Action $alert_dismissal_action The alert dismissal action.
*/
public function __construct( Alert_Dismissal_Action $alert_dismissal_action ) {
$this->alert_dismissal_action = $alert_dismissal_action;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$dismiss_route_args = [
'methods' => 'POST',
'callback' => [ $this, 'dismiss' ],
'permission_callback' => [ $this, 'can_dismiss' ],
'args' => [
'key' => [
'validate_callback' => [ $this->alert_dismissal_action, 'is_allowed' ],
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::DISMISS_ROUTE, $dismiss_route_args );
}
/**
* Dismisses an alert.
*
* @param WP_REST_Request $request The request. This request should have a key param set.
*
* @return WP_REST_Response The response.
*/
public function dismiss( WP_REST_Request $request ) {
$success = $this->alert_dismissal_action->dismiss( $request['key'] );
$status = $success === ( true ) ? 200 : 400;
return new WP_REST_Response(
(object) [
'success' => $success,
'status' => $status,
],
$status
);
}
/**
* Whether or not the current user is allowed to dismiss alerts.
*
* @return bool Whether or not the current user is allowed to dismiss alerts.
*/
public function can_dismiss() {
return \current_user_can( 'edit_posts' );
}
}

View File

@@ -0,0 +1,366 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Configuration\First_Time_Configuration_Action;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Main;
/**
* First_Time_Configuration_Route class.
*/
class First_Time_Configuration_Route implements Route_Interface {
use No_Conditionals;
/**
* Represents the first time configuration route.
*
* @var string
*/
const CONFIGURATION_ROUTE = '/configuration';
/**
* Represents a site representation route.
*
* @var string
*/
const SITE_REPRESENTATION_ROUTE = '/site_representation';
/**
* Represents a social profiles route.
*
* @var string
*/
const SOCIAL_PROFILES_ROUTE = '/social_profiles';
/**
* Represents a person's social profiles route.
*
* @deprecated 20.2
* @var string
*/
const PERSON_SOCIAL_PROFILES_ROUTE = '/person_social_profiles';
/**
* Represents a route to enable/disable tracking.
*
* @var string
*/
const ENABLE_TRACKING_ROUTE = '/enable_tracking';
/**
* Represents a route to check if current user has the correct capabilities to edit another user's profile.
*
* @var string
*/
const CHECK_CAPABILITY_ROUTE = '/check_capability';
/**
* Represents a route to save the first time configuration state.
*
* @var string
*/
const SAVE_CONFIGURATION_STATE_ROUTE = '/save_configuration_state';
/**
* Represents a route to save the first time configuration state.
*
* @var string
*/
const GET_CONFIGURATION_STATE_ROUTE = '/get_configuration_state';
/**
* The first tinme configuration action.
*
* @var First_Time_Configuration_Action
*/
private $first_time_configuration_action;
/**
* First_Time_Configuration_Route constructor.
*
* @param First_Time_Configuration_Action $first_time_configuration_action The first-time configuration action.
*/
public function __construct(
First_Time_Configuration_Action $first_time_configuration_action
) {
$this->first_time_configuration_action = $first_time_configuration_action;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$site_representation_route = [
'methods' => 'POST',
'callback' => [ $this, 'set_site_representation' ],
'permission_callback' => [ $this, 'can_manage_options' ],
'args' => [
'company_or_person' => [
'type' => 'string',
'enum' => [
'company',
'person',
],
'required' => true,
],
'company_name' => [
'type' => 'string',
],
'company_logo' => [
'type' => 'string',
],
'company_logo_id' => [
'type' => 'integer',
],
'person_logo' => [
'type' => 'string',
],
'person_logo_id' => [
'type' => 'integer',
],
'company_or_person_user_id' => [
'type' => 'integer',
],
'description' => [
'type' => 'string',
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::SITE_REPRESENTATION_ROUTE, $site_representation_route );
$social_profiles_route = [
'methods' => 'POST',
'callback' => [ $this, 'set_social_profiles' ],
'permission_callback' => [ $this, 'can_manage_options' ],
'args' => [
'facebook_site' => [
'type' => 'string',
],
'twitter_site' => [
'type' => 'string',
],
'other_social_urls' => [
'type' => 'array',
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::SOCIAL_PROFILES_ROUTE, $social_profiles_route );
$check_capability_route = [
'methods' => 'GET',
'callback' => [ $this, 'check_capability' ],
'permission_callback' => [ $this, 'can_manage_options' ],
'args' => [
'user_id' => [
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::CHECK_CAPABILITY_ROUTE, $check_capability_route );
$enable_tracking_route = [
'methods' => 'POST',
'callback' => [ $this, 'set_enable_tracking' ],
'permission_callback' => [ $this, 'can_manage_options' ],
'args' => [
'tracking' => [
'type' => 'boolean',
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::ENABLE_TRACKING_ROUTE, $enable_tracking_route );
$save_configuration_state_route = [
'methods' => 'POST',
'callback' => [ $this, 'save_configuration_state' ],
'permission_callback' => [ $this, 'can_manage_options' ],
'args' => [
'finishedSteps' => [
'type' => 'array',
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::SAVE_CONFIGURATION_STATE_ROUTE, $save_configuration_state_route );
$get_configuration_state_route = [
[
'methods' => 'GET',
'callback' => [ $this, 'get_configuration_state' ],
'permission_callback' => [ $this, 'can_manage_options' ],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::GET_CONFIGURATION_STATE_ROUTE, $get_configuration_state_route );
}
/**
* Sets the site representation values.
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function set_site_representation( WP_REST_Request $request ) {
$data = $this
->first_time_configuration_action
->set_site_representation( $request->get_json_params() );
return new WP_REST_Response( $data, $data->status );
}
/**
* Sets the social profiles values.
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function set_social_profiles( WP_REST_Request $request ) {
$data = $this
->first_time_configuration_action
->set_social_profiles( $request->get_json_params() );
return new WP_REST_Response(
[ 'json' => $data ]
);
}
/**
* Checks if the current user has the correct capability to edit a specific user.
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function check_capability( WP_REST_Request $request ) {
$data = $this
->first_time_configuration_action
->check_capability( $request->get_param( 'user_id' ) );
return new WP_REST_Response( $data );
}
/**
* Enables or disables tracking.
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function set_enable_tracking( WP_REST_Request $request ) {
$data = $this
->first_time_configuration_action
->set_enable_tracking( $request->get_json_params() );
return new WP_REST_Response( $data, $data->status );
}
/**
* Checks if the current user has the right capability.
*
* @return bool
*/
public function can_manage_options() {
return \current_user_can( 'wpseo_manage_options' );
}
/**
* Checks if the current user has the capability to edit a specific user.
*
* @param WP_REST_Request $request The request.
*
* @return bool
*/
public function can_edit_user( WP_REST_Request $request ) {
$response = $this->first_time_configuration_action->check_capability( $request->get_param( 'user_id' ) );
return $response->success;
}
/**
* Checks if the current user has the capability to edit posts of other users.
*
* @return bool
*/
public function can_edit_other_posts() {
return \current_user_can( 'edit_others_posts' );
}
/**
* Saves the first time configuration state.
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function save_configuration_state( WP_REST_Request $request ) {
$data = $this
->first_time_configuration_action
->save_configuration_state( $request->get_json_params() );
return new WP_REST_Response( $data, $data->status );
}
/**
* Returns the first time configuration state.
*
* @return WP_REST_Response the state of the configuration.
*/
public function get_configuration_state() {
$data = $this
->first_time_configuration_action
->get_configuration_state();
return new WP_REST_Response( $data, $data->status );
}
/* DEPRECATED METHODS */
/**
* Gets a person's social profiles values.
*
* @deprecated 20.2
* @codeCoverageIgnore
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function get_person_social_profiles( WP_REST_Request $request ) {
\_deprecated_function( __METHOD__, 'Yoast SEO 20.2' );
$data = $this
->first_time_configuration_action
->get_person_social_profiles( $request->get_param( 'user_id' ) );
return new WP_REST_Response( $data, $data->status );
}
/**
* Sets a person's social profiles values.
*
* @deprecated 20.2
* @codeCoverageIgnore
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function set_person_social_profiles( WP_REST_Request $request ) {
\_deprecated_function( __METHOD__, 'Yoast SEO 20.2' );
$data = $this
->first_time_configuration_action
->set_person_social_profiles( $request->get_json_params() );
return new WP_REST_Response(
[ 'json' => $data ]
);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_Error;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Importing\Importing_Action_Interface;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Exceptions\Importing\Aioseo_Validation_Exception;
use Yoast\WP\SEO\Main;
use Yoast\WP\SEO\Services\Importing\Importable_Detector_Service;
/**
* Importing_Route class.
*
* Importing route for importing from other SEO plugins.
*/
class Importing_Route extends Abstract_Action_Route {
use No_Conditionals;
/**
* The import route constant.
*
* @var string
*/
const ROUTE = '/import/(?P<plugin>[\w-]+)/(?P<type>[\w-]+)';
/**
* List of available importers.
*
* @var Importing_Action_Interface[]
*/
protected $importers = [];
/**
* The importable detector service.
*
* @var Importable_Detector_Service
*/
protected $importable_detector;
/**
* Importing_Route constructor.
*
* @param Importable_Detector_Service $importable_detector The importable detector service.
* @param Importing_Action_Interface ...$importers All available importers.
*/
public function __construct(
Importable_Detector_Service $importable_detector,
Importing_Action_Interface ...$importers
) {
$this->importable_detector = $importable_detector;
$this->importers = $importers;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
\register_rest_route(
Main::API_V1_NAMESPACE,
self::ROUTE,
[
'callback' => [ $this, 'execute' ],
'permission_callback' => [ $this, 'is_user_permitted_to_import' ],
'methods' => [ 'POST' ],
]
);
}
/**
* Executes the rest request, but only if the respective action is enabled.
*
* @param mixed $data The request parameters.
*
* @return WP_REST_Response|false Response or false on non-existent route.
*/
public function execute( $data ) {
$plugin = (string) $data['plugin'];
$type = (string) $data['type'];
$next_url = $this->get_endpoint( $plugin, $type );
try {
$importer = $this->get_importer( $plugin, $type );
if ( $importer === false || ! $importer->is_enabled() ) {
return new WP_Error(
'rest_no_route',
'Requested importer not found',
[
'status' => 404,
]
);
}
$result = $importer->index();
if ( $result === false || \count( $result ) === 0 ) {
$next_url = false;
}
return $this->respond_with(
$result,
$next_url
);
} catch ( \Exception $exception ) {
if ( $exception instanceof Aioseo_Validation_Exception ) {
return new WP_Error(
'wpseo_error_validation',
$exception->getMessage(),
[ 'stackTrace' => $exception->getTraceAsString() ]
);
}
return new WP_Error(
'wpseo_error_indexing',
$exception->getMessage(),
[ 'stackTrace' => $exception->getTraceAsString() ]
);
}
}
/**
* Gets the right importer for the given arguments.
*
* @param string $plugin The plugin to import from.
* @param string $type The type of entity to import.
*
* @return Importing_Action_Interface|false The importer, or false if no importer was found.
*/
protected function get_importer( $plugin, $type ) {
$importers = $this->importable_detector->filter_actions( $this->importers, $plugin, $type );
if ( \count( $importers ) !== 1 ) {
return false;
}
return \current( $importers );
}
/**
* Gets the right endpoint for the given arguments.
*
* @param string $plugin The plugin to import from.
* @param string $type The type of entity to import.
*
* @return string|false The endpoint for the given action or false on failure of finding the one.
*/
public function get_endpoint( $plugin, $type ) {
if ( empty( $plugin ) || empty( $type ) ) {
return false;
}
return Main::API_V1_NAMESPACE . "/import/{$plugin}/{$type}";
}
/**
* Whether or not the current user is allowed to import.
*
* @return bool Whether or not the current user is allowed to import.
*/
public function is_user_permitted_to_import() {
return \current_user_can( 'activate_plugins' );
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Indexables\Indexable_Head_Action;
use Yoast\WP\SEO\Conditionals\Headless_Rest_Endpoints_Enabled_Conditional;
use Yoast\WP\SEO\Main;
/**
* Head route for indexables.
*/
class Indexables_Head_Route implements Route_Interface {
/**
* The posts route constant.
*
* @var string
*/
const HEAD_FOR_URL_ROUTE = 'get_head';
/**
* The full posts route constant.
*
* @var string
*/
const FULL_HEAD_FOR_URL_ROUTE = Main::API_V1_NAMESPACE . '/' . self::HEAD_FOR_URL_ROUTE;
/**
* The head action.
*
* @var Indexable_Head_Action
*/
private $head_action;
/**
* Indexable_Indexation_Route constructor.
*
* @param Indexable_Head_Action $head_action The head action.
*/
public function __construct( Indexable_Head_Action $head_action ) {
$this->head_action = $head_action;
}
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Headless_Rest_Endpoints_Enabled_Conditional::class ];
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$route_args = [
'methods' => 'GET',
'callback' => [ $this, 'get_head' ],
'permission_callback' => '__return_true',
'args' => [
'url' => [
'validate_callback' => [ $this, 'is_valid_url' ],
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::HEAD_FOR_URL_ROUTE, $route_args );
}
/**
* Gets the head of a page for a given URL.
*
* @param WP_REST_Request $request The request. This request should have a url param set.
*
* @return WP_REST_Response The response.
*/
public function get_head( WP_REST_Request $request ) {
$url = \esc_url_raw( \utf8_uri_encode( $request['url'] ) );
$data = $this->head_action->for_url( $url );
return new WP_REST_Response( $data, $data->status );
}
/**
* Checks if a url is a valid url.
*
* @param string $url The url to check.
*
* @return bool Whether or not the url is valid.
*/
public function is_valid_url( $url ) {
if ( \filter_var( \utf8_uri_encode( $url ), \FILTER_VALIDATE_URL ) === false ) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,429 @@
<?php
namespace Yoast\WP\SEO\Routes;
use Exception;
use WP_Error;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Indexing\Indexable_General_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Indexing_Complete_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Post_Type_Archive_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexable_Term_Indexation_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexation_Action_Interface;
use Yoast\WP\SEO\Actions\Indexing\Indexing_Complete_Action;
use Yoast\WP\SEO\Actions\Indexing\Indexing_Prepare_Action;
use Yoast\WP\SEO\Actions\Indexing\Post_Link_Indexing_Action;
use Yoast\WP\SEO\Actions\Indexing\Term_Link_Indexing_Action;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Helpers\Indexing_Helper;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Main;
/**
* Indexing_Route class.
*
* Indexing route for indexables.
*/
class Indexing_Route extends Abstract_Indexation_Route {
use No_Conditionals;
/**
* The indexing complete route constant.
*
* @var string
*/
const COMPLETE_ROUTE = 'indexing/complete';
/**
* The full indexing complete route constant.
*
* @var string
*/
const FULL_COMPLETE_ROUTE = Main::API_V1_NAMESPACE . '/' . self::COMPLETE_ROUTE;
/**
* The indexables complete route constant.
*
* @var string
*/
const INDEXABLES_COMPLETE_ROUTE = 'indexing/indexables-complete';
/**
* The full indexing complete route constant.
*
* @var string
*/
const FULL_INDEXABLES_COMPLETE_ROUTE = Main::API_V1_NAMESPACE . '/' . self::INDEXABLES_COMPLETE_ROUTE;
/**
* The indexing prepare route constant.
*
* @var string
*/
const PREPARE_ROUTE = 'indexing/prepare';
/**
* The full indexing prepare route constant.
*
* @var string
*/
const FULL_PREPARE_ROUTE = Main::API_V1_NAMESPACE . '/' . self::PREPARE_ROUTE;
/**
* The posts route constant.
*
* @var string
*/
const POSTS_ROUTE = 'indexing/posts';
/**
* The full posts route constant.
*
* @var string
*/
const FULL_POSTS_ROUTE = Main::API_V1_NAMESPACE . '/' . self::POSTS_ROUTE;
/**
* The terms route constant.
*
* @var string
*/
const TERMS_ROUTE = 'indexing/terms';
/**
* The full terms route constant.
*
* @var string
*/
const FULL_TERMS_ROUTE = Main::API_V1_NAMESPACE . '/' . self::TERMS_ROUTE;
/**
* The terms route constant.
*
* @var string
*/
const POST_TYPE_ARCHIVES_ROUTE = 'indexing/post-type-archives';
/**
* The full terms route constant.
*
* @var string
*/
const FULL_POST_TYPE_ARCHIVES_ROUTE = Main::API_V1_NAMESPACE . '/' . self::POST_TYPE_ARCHIVES_ROUTE;
/**
* The general route constant.
*
* @var string
*/
const GENERAL_ROUTE = 'indexing/general';
/**
* The full general route constant.
*
* @var string
*/
const FULL_GENERAL_ROUTE = Main::API_V1_NAMESPACE . '/' . self::GENERAL_ROUTE;
/**
* The posts route constant.
*
* @var string
*/
const POST_LINKS_INDEXING_ROUTE = 'link-indexing/posts';
/**
* The full posts route constant.
*
* @var string
*/
const FULL_POST_LINKS_INDEXING_ROUTE = Main::API_V1_NAMESPACE . '/' . self::POST_LINKS_INDEXING_ROUTE;
/**
* The terms route constant.
*
* @var string
*/
const TERM_LINKS_INDEXING_ROUTE = 'link-indexing/terms';
/**
* The full terms route constant.
*
* @var string
*/
const FULL_TERM_LINKS_INDEXING_ROUTE = Main::API_V1_NAMESPACE . '/' . self::TERM_LINKS_INDEXING_ROUTE;
/**
* The post indexing action.
*
* @var Indexable_Post_Indexation_Action
*/
protected $post_indexation_action;
/**
* The term indexing action.
*
* @var Indexable_Term_Indexation_Action
*/
protected $term_indexation_action;
/**
* The post type archive indexing action.
*
* @var Indexable_Post_Type_Archive_Indexation_Action
*/
protected $post_type_archive_indexation_action;
/**
* Represents the general indexing action.
*
* @var Indexable_General_Indexation_Action
*/
protected $general_indexation_action;
/**
* The prepare indexing action.
*
* @var Indexing_Prepare_Action
*/
protected $prepare_indexing_action;
/**
* The indexable indexing complete action.
*
* @var Indexable_Indexing_Complete_Action
*/
protected $indexable_indexing_complete_action;
/**
* The indexing complete action.
*
* @var Indexing_Complete_Action
*/
protected $indexing_complete_action;
/**
* The post link indexing action.
*
* @var Post_Link_Indexing_Action
*/
protected $post_link_indexing_action;
/**
* The term link indexing action.
*
* @var Term_Link_Indexing_Action
*/
protected $term_link_indexing_action;
/**
* The options helper.
*
* @var Options_Helper
*/
protected $options_helper;
/**
* The indexing helper.
*
* @var Indexing_Helper
*/
protected $indexing_helper;
/**
* Indexing_Route constructor.
*
* @param Indexable_Post_Indexation_Action $post_indexation_action The post indexing action.
* @param Indexable_Term_Indexation_Action $term_indexation_action The term indexing action.
* @param Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation_action The post type archive indexing action.
* @param Indexable_General_Indexation_Action $general_indexation_action The general indexing action.
* @param Indexable_Indexing_Complete_Action $indexable_indexing_complete_action The complete indexing action.
* @param Indexing_Complete_Action $indexing_complete_action The complete indexing action.
* @param Indexing_Prepare_Action $prepare_indexing_action The prepare indexing action.
* @param Post_Link_Indexing_Action $post_link_indexing_action The post link indexing action.
* @param Term_Link_Indexing_Action $term_link_indexing_action The term link indexing action.
* @param Options_Helper $options_helper The options helper.
* @param Indexing_Helper $indexing_helper The indexing helper.
*/
public function __construct(
Indexable_Post_Indexation_Action $post_indexation_action,
Indexable_Term_Indexation_Action $term_indexation_action,
Indexable_Post_Type_Archive_Indexation_Action $post_type_archive_indexation_action,
Indexable_General_Indexation_Action $general_indexation_action,
Indexable_Indexing_Complete_Action $indexable_indexing_complete_action,
Indexing_Complete_Action $indexing_complete_action,
Indexing_Prepare_Action $prepare_indexing_action,
Post_Link_Indexing_Action $post_link_indexing_action,
Term_Link_Indexing_Action $term_link_indexing_action,
Options_Helper $options_helper,
Indexing_Helper $indexing_helper
) {
$this->post_indexation_action = $post_indexation_action;
$this->term_indexation_action = $term_indexation_action;
$this->post_type_archive_indexation_action = $post_type_archive_indexation_action;
$this->general_indexation_action = $general_indexation_action;
$this->indexable_indexing_complete_action = $indexable_indexing_complete_action;
$this->indexing_complete_action = $indexing_complete_action;
$this->prepare_indexing_action = $prepare_indexing_action;
$this->options_helper = $options_helper;
$this->post_link_indexing_action = $post_link_indexing_action;
$this->term_link_indexing_action = $term_link_indexing_action;
$this->indexing_helper = $indexing_helper;
}
/**
* Registers the routes used to index indexables.
*/
public function register_routes() {
$route_args = [
'methods' => 'POST',
'callback' => [ $this, 'index_posts' ],
'permission_callback' => [ $this, 'can_index' ],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::POSTS_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'index_terms' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::TERMS_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'index_post_type_archives' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::POST_TYPE_ARCHIVES_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'index_general' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::GENERAL_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'prepare' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::PREPARE_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'indexables_complete' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::INDEXABLES_COMPLETE_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'complete' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::COMPLETE_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'index_post_links' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::POST_LINKS_INDEXING_ROUTE, $route_args );
$route_args['callback'] = [ $this, 'index_term_links' ];
\register_rest_route( Main::API_V1_NAMESPACE, self::TERM_LINKS_INDEXING_ROUTE, $route_args );
}
/**
* Indexes a number of unindexed posts.
*
* @return WP_REST_Response The response.
*/
public function index_posts() {
return $this->run_indexation_action( $this->post_indexation_action, self::FULL_POSTS_ROUTE );
}
/**
* Indexes a number of unindexed terms.
*
* @return WP_REST_Response The response.
*/
public function index_terms() {
return $this->run_indexation_action( $this->term_indexation_action, self::FULL_TERMS_ROUTE );
}
/**
* Indexes a number of unindexed post type archive pages.
*
* @return WP_REST_Response The response.
*/
public function index_post_type_archives() {
return $this->run_indexation_action( $this->post_type_archive_indexation_action, self::FULL_POST_TYPE_ARCHIVES_ROUTE );
}
/**
* Indexes a number of unindexed general items.
*
* @return WP_REST_Response The response.
*/
public function index_general() {
return $this->run_indexation_action( $this->general_indexation_action, self::FULL_GENERAL_ROUTE );
}
/**
* Indexes a number of posts for post links.
*
* @return WP_REST_Response The response.
*/
public function index_post_links() {
return $this->run_indexation_action( $this->post_link_indexing_action, self::FULL_POST_LINKS_INDEXING_ROUTE );
}
/**
* Indexes a number of terms for term links.
*
* @return WP_REST_Response The response.
*/
public function index_term_links() {
return $this->run_indexation_action( $this->term_link_indexing_action, self::FULL_TERM_LINKS_INDEXING_ROUTE );
}
/**
* Prepares the indexation.
*
* @return WP_REST_Response The response.
*/
public function prepare() {
$this->prepare_indexing_action->prepare();
return $this->respond_with( [], false );
}
/**
* Completes the indexable indexation.
*
* @return WP_REST_Response The response.
*/
public function indexables_complete() {
$this->indexable_indexing_complete_action->complete();
return $this->respond_with( [], false );
}
/**
* Completes the indexation.
*
* @return WP_REST_Response The response.
*/
public function complete() {
$this->indexing_complete_action->complete();
return $this->respond_with( [], false );
}
/**
* Whether or not the current user is allowed to index.
*
* @return bool Whether or not the current user is allowed to index.
*/
public function can_index() {
return \current_user_can( 'edit_posts' );
}
/**
* Runs an indexing action and returns the response.
*
* @param Indexation_Action_Interface $indexation_action The indexing action.
* @param string $url The url of the indexing route.
*
* @return WP_REST_Response|WP_Error The response, or an error when running the indexing action failed.
*/
protected function run_indexation_action( Indexation_Action_Interface $indexation_action, $url ) {
try {
return parent::run_indexation_action( $indexation_action, $url );
} catch ( Exception $exception ) {
$this->indexing_helper->indexing_failed();
return new WP_Error(
'wpseo_error_indexing',
$exception->getMessage(),
[ 'stackTrace' => $exception->getTraceAsString() ]
);
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Integrations_Action;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Main;
/**
* Integrations_Route class.
*/
class Integrations_Route implements Route_Interface {
use No_Conditionals;
/**
* Represents the integrations route.
*
* @var string
*/
const INTEGRATIONS_ROUTE = '/integrations';
/**
* Represents a route to set the state of an integration.
*
* @var string
*/
const SET_ACTIVE_ROUTE = '/set_active';
/**
* The integrations action.
*
* @var Integrations_Action
*/
private $integrations_action;
/**
* Integrations_Route constructor.
*
* @param Integrations_Action $integrations_action The integrations action.
*/
public function __construct(
Integrations_Action $integrations_action
) {
$this->integrations_action = $integrations_action;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$set_active_route = [
'methods' => 'POST',
'callback' => [ $this, 'set_integration_active' ],
'permission_callback' => [ $this, 'can_manage_options' ],
'args' => [
'active' => [
'type' => 'boolean',
'required' => true,
],
'integration' => [
'type' => 'string',
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_ACTIVE_ROUTE, $set_active_route );
}
/**
* Checks if the current user has the right capability.
*
* @return bool
*/
public function can_manage_options() {
return \current_user_can( 'wpseo_manage_options' );
}
/**
* Sets integration state.
*
* @param WP_REST_Request $request The request.
*
* @return WP_REST_Response
*/
public function set_integration_active( WP_REST_Request $request ) {
$params = $request->get_json_params();
$integration_name = $params['integration'];
$value = $params['active'];
$data = $this
->integrations_action
->set_integration_active( $integration_name, $value );
return new WP_REST_Response(
[ 'json' => $data ]
);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Main;
/**
* Meta_Search_Route class
*/
class Meta_Search_Route implements Route_Interface {
use No_Conditionals;
/**
* Represents meta search route.
*
* @var string
*/
const META_SEARCH_ROUTE = '/meta/search';
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$route = [
[
'methods' => 'GET',
'callback' => [ $this, 'search_meta' ],
'permission_callback' => [ $this, 'permission_check' ],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::META_SEARCH_ROUTE, $route );
}
/**
* Performs the permission check.
*
* @param WP_REST_Request $request The request.
*
* @return bool
*/
public function permission_check( $request ) {
if ( ! isset( $request['post_id'] ) ) {
return false;
}
$post_type = \get_post_type( $request['post_id'] );
$post_type_object = \get_post_type_object( $post_type );
return \current_user_can( $post_type_object->cap->edit_posts );
}
/**
* Searches meta fields of a given post.
*
* @param WP_REST_Request $request The REST request.
*
* @return WP_REST_Response
*/
public function search_meta( $request ) {
$post_id = $request['post_id'];
$query = $request['query'];
$meta = \get_post_custom( $post_id );
$matches = [];
foreach ( $meta as $key => $values ) {
if ( \substr( $key, 0, \strlen( $query ) ) !== $query ) {
continue;
}
if ( empty( $query ) && \substr( $key, 0, 1 ) === '_' ) {
continue;
}
// Skip custom field values that are serialized.
if ( \is_serialized( $values[0] ) ) {
continue;
}
$matches[] = [
'key' => $key,
'value' => $values[0],
];
if ( \count( $matches ) >= 25 ) {
break;
}
}
return \rest_ensure_response( [ 'meta' => $matches ] );
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Yoast\WP\SEO\Routes;
use Yoast\WP\SEO\Loadable_Interface;
/**
* Route interface.
*/
interface Route_Interface extends Loadable_Interface {
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes();
}

View File

@@ -0,0 +1,248 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\SEMrush\SEMrush_Login_Action;
use Yoast\WP\SEO\Actions\SEMrush\SEMrush_Options_Action;
use Yoast\WP\SEO\Actions\SEMrush\SEMrush_Phrases_Action;
use Yoast\WP\SEO\Conditionals\SEMrush_Enabled_Conditional;
use Yoast\WP\SEO\Main;
/**
* SEMrush_Route class.
*/
class SEMrush_Route implements Route_Interface {
/**
* The SEMrush route prefix.
*
* @var string
*/
const ROUTE_PREFIX = 'semrush';
/**
* The authenticate route constant.
*
* @var string
*/
const AUTHENTICATION_ROUTE = self::ROUTE_PREFIX . '/authenticate';
/**
* The country code option route constant.
*
* @var string
*/
const COUNTRY_CODE_OPTION_ROUTE = self::ROUTE_PREFIX . '/country_code';
/**
* The request related keyphrases route constant.
*
* @var string
*/
const RELATED_KEYPHRASES_ROUTE = self::ROUTE_PREFIX . '/related_keyphrases';
/**
* The full login route constant.
*
* @var string
*/
const FULL_AUTHENTICATION_ROUTE = Main::API_V1_NAMESPACE . '/' . self::AUTHENTICATION_ROUTE;
/**
* The full country code option route constant.
*
* @var string
*/
const FULL_COUNTRY_CODE_OPTION_ROUTE = Main::API_V1_NAMESPACE . '/' . self::COUNTRY_CODE_OPTION_ROUTE;
/**
* The login action.
*
* @var SEMrush_Login_Action
*/
private $login_action;
/**
* The options action.
*
* @var SEMrush_Options_Action
*/
private $options_action;
/**
* The phrases action.
*
* @var SEMrush_Phrases_Action
*/
private $phrases_action;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ SEMrush_Enabled_Conditional::class ];
}
/**
* SEMrush_Route constructor.
*
* @param SEMrush_Login_Action $login_action The login action.
* @param SEMrush_Options_Action $options_action The options action.
* @param SEMrush_Phrases_Action $phrases_action The phrases action.
*/
public function __construct(
SEMrush_Login_Action $login_action,
SEMrush_Options_Action $options_action,
SEMrush_Phrases_Action $phrases_action
) {
$this->login_action = $login_action;
$this->options_action = $options_action;
$this->phrases_action = $phrases_action;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$authentication_route_args = [
'methods' => 'POST',
'callback' => [ $this, 'authenticate' ],
'permission_callback' => [ $this, 'can_use_semrush' ],
'args' => [
'code' => [
'validate_callback' => [ $this, 'has_valid_code' ],
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::AUTHENTICATION_ROUTE, $authentication_route_args );
$set_country_code_option_route_args = [
'methods' => 'POST',
'callback' => [ $this, 'set_country_code_option' ],
'permission_callback' => [ $this, 'can_use_semrush' ],
'args' => [
'country_code' => [
'validate_callback' => [ $this, 'has_valid_country_code' ],
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::COUNTRY_CODE_OPTION_ROUTE, $set_country_code_option_route_args );
$related_keyphrases_route_args = [
'methods' => 'GET',
'callback' => [ $this, 'get_related_keyphrases' ],
'permission_callback' => [ $this, 'can_use_semrush' ],
'args' => [
'keyphrase' => [
'validate_callback' => [ $this, 'has_valid_keyphrase' ],
'required' => true,
],
'country_code' => [
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::RELATED_KEYPHRASES_ROUTE, $related_keyphrases_route_args );
}
/**
* Authenticates with SEMrush.
*
* @param WP_REST_Request $request The request. This request should have a code param set.
*
* @return WP_REST_Response The response.
*/
public function authenticate( WP_REST_Request $request ) {
$data = $this
->login_action
->authenticate( $request['code'] );
return new WP_REST_Response( $data, $data->status );
}
/**
* Sets the SEMrush country code option.
*
* @param WP_REST_Request $request The request. This request should have a country code param set.
*
* @return WP_REST_Response The response.
*/
public function set_country_code_option( WP_REST_Request $request ) {
$data = $this
->options_action
->set_country_code( $request['country_code'] );
return new WP_REST_Response( $data, $data->status );
}
/**
* Checks if a valid code was returned.
*
* @param string $code The code to check.
*
* @return bool Whether or not the code is valid.
*/
public function has_valid_code( $code ) {
return $code !== '';
}
/**
* Checks if a valid keyphrase is provided.
*
* @param string $keyphrase The keyphrase to check.
*
* @return bool Whether or not the keyphrase is valid.
*/
public function has_valid_keyphrase( $keyphrase ) {
return \trim( $keyphrase ) !== '';
}
/**
* Gets the related keyphrases based on the passed keyphrase and database code.
*
* @param WP_REST_Request $request The request. This request should have a keyphrase and country_code param set.
*
* @return WP_REST_Response The response.
*/
public function get_related_keyphrases( WP_REST_Request $request ) {
$data = $this
->phrases_action
->get_related_keyphrases(
$request['keyphrase'],
$request['country_code']
);
return new WP_REST_Response( $data, $data->status );
}
/**
* Checks if a valid country code was submitted.
*
* @param string $country_code The country code to check.
*
* @return bool Whether or not the country code is valid.
*/
public function has_valid_country_code( $country_code ) {
return ( $country_code !== '' && \preg_match( '/^[a-z]{2}$/', $country_code ) === 1 );
}
/**
* Whether or not the current user is allowed to edit post/pages and thus use the SEMrush integration.
*
* @return bool Whether or not the current user is allowed to use SEMrush.
*/
public function can_use_semrush() {
return \current_user_can( 'edit_posts' ) || \current_user_can( 'edit_pages' );
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Response;
use Yoast\WP\SEO\Conditionals\Addon_Installation_Conditional;
use Yoast\WP\SEO\Main;
/**
* Supported_Features_Route class.
*/
class Supported_Features_Route implements Route_Interface {
/**
* Represents the supported features route.
*
* @var string
*/
const SUPPORTED_FEATURES_ROUTE = '/supported-features';
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [
Addon_Installation_Conditional::class,
];
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$supported_features_route = [
'methods' => 'GET',
'callback' => [ $this, 'get_supported_features' ],
'permission_callback' => '__return_true',
];
\register_rest_route( Main::API_V1_NAMESPACE, self::SUPPORTED_FEATURES_ROUTE, $supported_features_route );
}
/**
* Returns a list of features supported by this yoast seo installation.
*
* @return WP_REST_Response a list of features supported by this yoast seo installation.
*/
public function get_supported_features() {
return new WP_REST_Response(
[
'addon-installation' => 1,
]
);
}
}

View File

@@ -0,0 +1,331 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Actions\Wincher\Wincher_Account_Action;
use Yoast\WP\SEO\Actions\Wincher\Wincher_Keyphrases_Action;
use Yoast\WP\SEO\Actions\Wincher\Wincher_Login_Action;
use Yoast\WP\SEO\Conditionals\Wincher_Enabled_Conditional;
use Yoast\WP\SEO\Main;
/**
* Wincher_Route class.
*/
class Wincher_Route implements Route_Interface {
/**
* The Wincher route prefix.
*
* @var string
*/
const ROUTE_PREFIX = 'wincher';
/**
* The authorize route constant.
*
* @var string
*/
const AUTHORIZATION_URL_ROUTE = self::ROUTE_PREFIX . '/authorization-url';
/**
* The authenticate route constant.
*
* @var string
*/
const AUTHENTICATION_ROUTE = self::ROUTE_PREFIX . '/authenticate';
/**
* The track bulk keyphrases route constant.
*
* @var string
*/
const KEYPHRASES_TRACK_ROUTE = self::ROUTE_PREFIX . '/keyphrases/track';
/**
* The keyphrases route constant.
*
* @var string
*/
const TRACKED_KEYPHRASES_ROUTE = self::ROUTE_PREFIX . '/keyphrases';
/**
* The untrack keyphrase route constant.
*
* @var string
*/
const UNTRACK_KEYPHRASE_ROUTE = self::ROUTE_PREFIX . '/keyphrases/untrack';
/**
* The check limit route constant.
*
* @var string
*/
const CHECK_LIMIT_ROUTE = self::ROUTE_PREFIX . '/account/limit';
/**
* The upgrade campaign route constant.
*
* @var string
*/
const UPGRADE_CAMPAIGN_ROUTE = self::ROUTE_PREFIX . '/account/upgrade-campaign';
/**
* The login action.
*
* @var Wincher_Login_Action
*/
private $login_action;
/**
* The account action.
*
* @var Wincher_Account_Action
*/
private $account_action;
/**
* The keyphrases action.
*
* @var Wincher_Keyphrases_Action
*/
private $keyphrases_action;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Wincher_Enabled_Conditional::class ];
}
/**
* Wincher_Route constructor.
*
* @param Wincher_Login_Action $login_action The login action.
* @param Wincher_Account_Action $account_action The account action.
* @param Wincher_Keyphrases_Action $keyphrases_action The keyphrases action.
*/
public function __construct(
Wincher_Login_Action $login_action,
Wincher_Account_Action $account_action,
Wincher_Keyphrases_Action $keyphrases_action
) {
$this->login_action = $login_action;
$this->account_action = $account_action;
$this->keyphrases_action = $keyphrases_action;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$authorize_route_args = [
'methods' => 'GET',
'callback' => [ $this, 'get_authorization_url' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::AUTHORIZATION_URL_ROUTE, $authorize_route_args );
$authentication_route_args = [
'methods' => 'POST',
'callback' => [ $this, 'authenticate' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
'args' => [
'code' => [
'validate_callback' => [ $this, 'has_valid_code' ],
'required' => true,
],
'websiteId' => [
'validate_callback' => [ $this, 'has_valid_website_id' ],
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::AUTHENTICATION_ROUTE, $authentication_route_args );
$track_keyphrases_route_args = [
'methods' => 'POST',
'callback' => [ $this, 'track_keyphrases' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
'args' => [
'keyphrases' => [
'required' => true,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::KEYPHRASES_TRACK_ROUTE, $track_keyphrases_route_args );
$get_keyphrases_route_args = [
'methods' => 'POST',
'callback' => [ $this, 'get_tracked_keyphrases' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
'args' => [
'keyphrases' => [
'required' => false,
],
'permalink' => [
'required' => false,
],
'startAt' => [
'required' => false,
],
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::TRACKED_KEYPHRASES_ROUTE, $get_keyphrases_route_args );
$delete_keyphrase_route_args = [
'methods' => 'DELETE',
'callback' => [ $this, 'untrack_keyphrase' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::UNTRACK_KEYPHRASE_ROUTE, $delete_keyphrase_route_args );
$check_limit_route_args = [
'methods' => 'GET',
'callback' => [ $this, 'check_limit' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::CHECK_LIMIT_ROUTE, $check_limit_route_args );
$get_upgrade_campaign_route_args = [
'methods' => 'GET',
'callback' => [ $this, 'get_upgrade_campaign' ],
'permission_callback' => [ $this, 'can_use_wincher' ],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::UPGRADE_CAMPAIGN_ROUTE, $get_upgrade_campaign_route_args );
}
/**
* Returns the authorization URL.
*
* @return WP_REST_Response The response.
*/
public function get_authorization_url() {
$data = $this->login_action->get_authorization_url();
return new WP_REST_Response( $data, $data->status );
}
/**
* Authenticates with Wincher.
*
* @param WP_REST_Request $request The request. This request should have a code param set.
*
* @return WP_REST_Response The response.
*/
public function authenticate( WP_REST_Request $request ) {
$data = $this
->login_action
->authenticate( $request['code'], (string) $request['websiteId'] );
return new WP_REST_Response( $data, $data->status );
}
/**
* Posts keyphrases to track.
*
* @param WP_REST_Request $request The request. This request should have a code param set.
*
* @return WP_REST_Response The response.
*/
public function track_keyphrases( WP_REST_Request $request ) {
$limits = $this->account_action->check_limit();
if ( $limits->status !== 200 ) {
return new WP_REST_Response( $limits, $limits->status );
}
$data = $this->keyphrases_action->track_keyphrases( $request['keyphrases'], $limits );
return new WP_REST_Response( $data, $data->status );
}
/**
* Gets the tracked keyphrases via POST.
* This is done via POST, so we don't potentially run into URL limit issues when a lot of long keyphrases are tracked.
*
* @param WP_REST_Request $request The request. This request should have a code param set.
*
* @return WP_REST_Response The response.
*/
public function get_tracked_keyphrases( WP_REST_Request $request ) {
$data = $this->keyphrases_action->get_tracked_keyphrases( $request['keyphrases'], $request['permalink'], $request['startAt'] );
return new WP_REST_Response( $data, $data->status );
}
/**
* Untracks the tracked keyphrase.
*
* @param WP_REST_Request $request The request. This request should have a code param set.
*
* @return WP_REST_Response The response.
*/
public function untrack_keyphrase( WP_REST_Request $request ) {
$data = $this->keyphrases_action->untrack_keyphrase( $request['keyphraseID'] );
return new WP_REST_Response( $data, $data->status );
}
/**
* Checks the account limit.
*
* @return WP_REST_Response The response.
*/
public function check_limit() {
$data = $this->account_action->check_limit();
return new WP_REST_Response( $data, $data->status );
}
/**
* Gets the upgrade campaign.
* If it's not a free user, no campaign is returned.
*
* @return WP_REST_Response The response.
*/
public function get_upgrade_campaign() {
$data = $this->account_action->get_upgrade_campaign();
return new WP_REST_Response( $data, $data->status );
}
/**
* Checks if a valid code was returned.
*
* @param string $code The code to check.
*
* @return bool Whether the code is valid.
*/
public function has_valid_code( $code ) {
return $code !== '';
}
/**
* Checks if a valid website_id was returned.
*
* @param int $website_id The website_id to check.
*
* @return bool Whether the website_id is valid.
*/
public function has_valid_website_id( $website_id ) {
return ! empty( $website_id ) && \is_int( $website_id );
}
/**
* Whether the current user is allowed to publish post/pages and thus use the Wincher integration.
*
* @return bool Whether the current user is allowed to use Wincher.
*/
public function can_use_wincher() {
return \current_user_can( 'publish_posts' ) || \current_user_can( 'publish_pages' );
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace Yoast\WP\SEO\Routes;
use WP_REST_Request;
use WP_REST_Response;
use Yoast\WP\SEO\Conditionals\No_Conditionals;
use Yoast\WP\SEO\Helpers\Options_Helper;
use Yoast\WP\SEO\Main;
/**
* Workouts_Route class.
*/
class Workouts_Route implements Route_Interface {
use No_Conditionals;
/**
* Represents workouts route.
*
* @var string
*/
const WORKOUTS_ROUTE = '/workouts';
/**
* The Options helper.
*
* @var Options_Helper
*/
private $options_helper;
/**
* Workouts_Route constructor.
*
* @param Options_Helper $options_helper The options helper.
*/
public function __construct(
Options_Helper $options_helper
) {
$this->options_helper = $options_helper;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$edit_others_posts = static function() {
return \current_user_can( 'edit_others_posts' );
};
$workouts_route = [
[
'methods' => 'GET',
'callback' => [ $this, 'get_workouts' ],
'permission_callback' => $edit_others_posts,
],
[
'methods' => 'POST',
'callback' => [ $this, 'set_workouts' ],
'permission_callback' => $edit_others_posts,
'args' => $this->get_workouts_routes_args(),
],
];
\register_rest_route( Main::API_V1_NAMESPACE, self::WORKOUTS_ROUTE, $workouts_route );
}
/**
* Returns the workouts as configured for the site.
*
* @return WP_REST_Response the configuration of the workouts.
*/
public function get_workouts() {
$workouts_option = $this->options_helper->get( 'workouts_data' );
/**
* Filter: 'Yoast\WP\SEO\workouts_options' - Allows adding workouts options by the add-ons.
*
* @api array $workouts_option The content of the `workouts_data` option in Free.
*/
$workouts_option = \apply_filters( 'Yoast\WP\SEO\workouts_options', $workouts_option );
return new WP_REST_Response(
[ 'json' => $workouts_option ]
);
}
/**
* Sets the workout configuration.
*
* @param WP_REST_Request $request The request object.
*
* @return WP_REST_Response the configuration of the workouts.
*/
public function set_workouts( $request ) {
$workouts_data = $request->get_json_params();
/**
* Filter: 'Yoast\WP\SEO\workouts_route_save' - Allows the add-ons to save the options data in their own options.
*
* @api mixed|null $result The result of the previous saving operation.
*
* @param array $workouts_data The full set of workouts option data to save.
*/
$result = \apply_filters( 'Yoast\WP\SEO\workouts_route_save', null, $workouts_data );
return new WP_REST_Response(
[ 'json' => $result ]
);
}
/**
* Gets the args for all the registered workouts.
*
* @return array
*/
private function get_workouts_routes_args() {
$args_array = [];
/**
* Filter: 'Yoast\WP\SEO\workouts_route_args' - Allows the add-ons add their own arguments to the route registration.
*
* @api array $args_array The array of arguments for the route registration.
*/
return \apply_filters( 'Yoast\WP\SEO\workouts_route_args', $args_array );
}
}

View File

@@ -0,0 +1,223 @@
<?php // phpcs:ignore Yoast.Files.FileName.InvalidClassFileName -- Reason: this explicitly concerns the Yoast head fields.
namespace Yoast\WP\SEO\Routes;
use Yoast\WP\SEO\Actions\Indexables\Indexable_Head_Action;
use Yoast\WP\SEO\Conditionals\Headless_Rest_Endpoints_Enabled_Conditional;
use Yoast\WP\SEO\Helpers\Post_Helper;
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
/**
* Yoast_Head_REST_Field class.
*
* Registers the yoast head REST field.
* Not technically a route but behaves the same so is included here.
*/
class Yoast_Head_REST_Field implements Route_Interface {
/**
* The name of the Yoast head field.
*
* @var string
*/
const YOAST_HEAD_ATTRIBUTE_NAME = 'yoast_head';
/**
* The name of the Yoast head JSON field.
*
* @var string
*/
const YOAST_JSON_HEAD_ATTRIBUTE_NAME = 'yoast_head_json';
/**
* The post type helper.
*
* @var Post_Type_Helper
*/
protected $post_type_helper;
/**
* The taxonomy helper.
*
* @var Taxonomy_Helper
*/
protected $taxonomy_helper;
/**
* The post helper.
*
* @var Post_Helper
*/
protected $post_helper;
/**
* The head action.
*
* @var Indexable_Head_Action
*/
protected $head_action;
/**
* Returns the conditionals based in which this loadable should be active.
*
* @return array
*/
public static function get_conditionals() {
return [ Headless_Rest_Endpoints_Enabled_Conditional::class ];
}
/**
* Yoast_Head_REST_Field constructor.
*
* @param Post_Type_Helper $post_type_helper The post type helper.
* @param Taxonomy_Helper $taxonomy_helper The taxonomy helper.
* @param Post_Helper $post_helper The post helper.
* @param Indexable_Head_Action $head_action The head action.
*/
public function __construct(
Post_Type_Helper $post_type_helper,
Taxonomy_Helper $taxonomy_helper,
Post_Helper $post_helper,
Indexable_Head_Action $head_action
) {
$this->post_type_helper = $post_type_helper;
$this->taxonomy_helper = $taxonomy_helper;
$this->post_helper = $post_helper;
$this->head_action = $head_action;
}
/**
* Registers routes with WordPress.
*
* @return void
*/
public function register_routes() {
$public_post_types = $this->post_type_helper->get_indexable_post_types();
foreach ( $public_post_types as $post_type ) {
$this->register_rest_fields( $post_type, 'for_post' );
}
$public_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies();
foreach ( $public_taxonomies as $taxonomy ) {
if ( $taxonomy === 'post_tag' ) {
$taxonomy = 'tag';
}
$this->register_rest_fields( $taxonomy, 'for_term' );
}
$this->register_rest_fields( 'user', 'for_author' );
$this->register_rest_fields( 'type', 'for_post_type_archive' );
}
/**
* Returns the head for a post.
*
* @param array $params The rest request params.
* @param string $format The desired output format.
*
* @return string|null The head.
*/
public function for_post( $params, $format = self::YOAST_HEAD_ATTRIBUTE_NAME ) {
if ( ! isset( $params['id'] ) ) {
return null;
}
if ( ! $this->post_helper->is_post_indexable( $params['id'] ) ) {
return null;
}
$obj = $this->head_action->for_post( $params['id'] );
return $this->render_object( $obj, $format );
}
/**
* Returns the head for a term.
*
* @param array $params The rest request params.
* @param string $format The desired output format.
*
* @return string|null The head.
*/
public function for_term( $params, $format = self::YOAST_HEAD_ATTRIBUTE_NAME ) {
$obj = $this->head_action->for_term( $params['id'] );
return $this->render_object( $obj, $format );
}
/**
* Returns the head for an author.
*
* @param array $params The rest request params.
* @param string $format The desired output format.
*
* @return string|null The head.
*/
public function for_author( $params, $format = self::YOAST_HEAD_ATTRIBUTE_NAME ) {
$obj = $this->head_action->for_author( $params['id'] );
return $this->render_object( $obj, $format );
}
/**
* Returns the head for a post type archive.
*
* @param array $params The rest request params.
* @param string $format The desired output format.
*
* @return string|null The head.
*/
public function for_post_type_archive( $params, $format = self::YOAST_HEAD_ATTRIBUTE_NAME ) {
if ( $params['slug'] === 'post' ) {
$obj = $this->head_action->for_posts_page();
}
elseif ( ! $this->post_type_helper->has_archive( $params['slug'] ) ) {
return null;
}
else {
$obj = $this->head_action->for_post_type_archive( $params['slug'] );
}
return $this->render_object( $obj, $format );
}
/**
* Registers the Yoast rest fields.
*
* @param string $object_type The object type.
* @param string $callback The function name of the callback.
*
* @return void
*/
protected function register_rest_fields( $object_type, $callback ) {
// Output metadata in page head meta tags.
\register_rest_field( $object_type, self::YOAST_HEAD_ATTRIBUTE_NAME, [ 'get_callback' => [ $this, $callback ] ] );
// Output metadata in a json object in a head meta tag.
\register_rest_field( $object_type, self::YOAST_JSON_HEAD_ATTRIBUTE_NAME, [ 'get_callback' => [ $this, $callback ] ] );
}
/**
* Returns the correct property for the Yoast head.
*
* @param stdObject $head The Yoast head.
* @param string $format The format to return.
*
* @return string|array|null The output value. String if HTML was requested, array otherwise.
*/
protected function render_object( $head, $format = self::YOAST_HEAD_ATTRIBUTE_NAME ) {
if ( $head->status === 404 ) {
return null;
}
switch ( $format ) {
case self::YOAST_HEAD_ATTRIBUTE_NAME:
return $head->html;
case self::YOAST_JSON_HEAD_ATTRIBUTE_NAME:
return $head->json;
}
return null;
}
}