Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/routes.tar

integrations-route.php000066600000015403151120025510011116 0ustar00<?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 Semrush integration.
	 *
	 * @var string
	 */
	const SET_SEMRUSH_ACTIVE_ROUTE = '/set_semrush_active';

	/**
	 * Represents a route to set the state of Wincher integration.
	 *
	 * @var string
	 */
	const SET_WINCHER_ACTIVE_ROUTE = '/set_wincher_active';

	/**
	 * Represents a route to set the state of Ryte integration.
	 *
	 * @var string
	 */
	const SET_RYTE_ACTIVE_ROUTE = '/set_ryte_active';

	/**
	 * Represents a route to set the state of WordProof integration.
	 *
	 * @var string
	 */
	const SET_WORDPROOF_ACTIVE_ROUTE = '/set_wordproof_active';

	/**
	 * Represents a route to set the state of Zapier integration.
	 *
	 * @var string
	 */
	const SET_ZAPIER_ACTIVE_ROUTE = '/set_zapier_active';

	/**
	 * Represents a route to set the state of Algolia integration.
	 *
	 * @var string
	 */
	const SET_ALGOLIA_ACTIVE_ROUTE = '/set_algolia_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_semrush_active_route = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'set_semrush_active' ],
			'permission_callback' => [ $this, 'can_manage_options' ],
			'args'                => [
				'active' => [
					'type'     => 'boolean',
					'required' => true,
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_SEMRUSH_ACTIVE_ROUTE, $set_semrush_active_route );

		$set_wincher_active_route = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'set_wincher_active' ],
			'permission_callback' => [ $this, 'can_manage_options' ],
			'args'                => [
				'active' => [
					'type'     => 'boolean',
					'required' => true,
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_WINCHER_ACTIVE_ROUTE, $set_wincher_active_route );

		$set_ryte_active_route = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'set_ryte_active' ],
			'permission_callback' => [ $this, 'can_manage_options' ],
			'args'                => [
				'active' => [
					'type'     => 'boolean',
					'required' => true,
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_RYTE_ACTIVE_ROUTE, $set_ryte_active_route );

		$set_wordproof_active_route = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'set_wordproof_active' ],
			'permission_callback' => [ $this, 'can_manage_options' ],
			'args'                => [
				'active' => [
					'type'     => 'boolean',
					'required' => true,
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_WORDPROOF_ACTIVE_ROUTE, $set_wordproof_active_route );

		$set_zapier_active_route = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'set_zapier_active' ],
			'permission_callback' => [ $this, 'can_manage_options' ],
			'args'                => [
				'active' => [
					'type'     => 'boolean',
					'required' => true,
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_ZAPIER_ACTIVE_ROUTE, $set_zapier_active_route );

		$set_algolia_active_route = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'set_algolia_active' ],
			'permission_callback' => [ $this, 'can_manage_options' ],
			'args'                => [
				'active' => [
					'type'     => 'boolean',
					'required' => true,
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::INTEGRATIONS_ROUTE . self::SET_ALGOLIA_ACTIVE_ROUTE, $set_algolia_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 Semrush integration state.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_semrush_active( WP_REST_Request $request ) {
		$data = $this
			->integrations_action
			->set_integration_active( 'semrush', $request->get_json_params() );

		return new WP_REST_Response(
			[ 'json' => $data ]
		);
	}

	/**
	 * Sets Wincher integration state.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_wincher_active( WP_REST_Request $request ) {
		$data = $this
			->integrations_action
			->set_integration_active( 'wincher', $request->get_json_params() );

		return new WP_REST_Response(
			[ 'json' => $data ]
		);
	}

	/**
	 * Sets Ryte integration state.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_ryte_active( WP_REST_Request $request ) {
		$data = $this
			->integrations_action
			->set_integration_active( 'ryte', $request->get_json_params() );

		return new WP_REST_Response(
			[ 'json' => $data ]
		);
	}

	/**
	 * Sets WordProof integration state.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_wordproof_active( WP_REST_Request $request ) {
		$data = $this
			->integrations_action
			->set_integration_active( 'wordproof', $request->get_json_params() );

		return new WP_REST_Response(
			[ 'json' => $data ]
		);
	}

	/**
	 * Sets Zapier integration state.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_zapier_active( WP_REST_Request $request ) {
		$data = $this
			->integrations_action
			->set_integration_active( 'zapier', $request->get_json_params() );

		return new WP_REST_Response(
			[ 'json' => $data ]
		);
	}

	/**
	 * Sets Algolia integration state.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_algolia_active( WP_REST_Request $request ) {
		$data = $this
			->integrations_action
			->set_integration_active( 'algolia', $request->get_json_params() );

		return new WP_REST_Response(
			[ 'json' => $data ]
		);
	}
}
meta-search-route.php000066600000003707151120025510010605 0ustar00<?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 ] );
	}
}
abstract-indexation-route.php000066600000001604151120025510012351 0ustar00<?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 );
	}
}
indexing-route.php000066600000030277151120025510010223 0ustar00<?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() ]
			);
		}
	}
}
alert-dismissal-route.php000066600000004474151120025510011513 0ustar00<?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' );
	}
}
workouts-route.php000066600000006132151120025510010304 0ustar00<?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 );
	}
}
first-time-configuration-route.php000066600000024340151120025510013340 0ustar00<?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.
	 *
	 * @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 );

		$person_social_profiles_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_person_social_profiles' ],
				'permission_callback' => [ $this, 'can_manage_options' ],
				'args'                => [
					'user_id' => [
						'required' => true,
					],
				],
			],
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'set_person_social_profiles' ],
				'permission_callback' => [ $this, 'can_edit_user' ],
				'args'                => [
					'user_id' => [
						'type'     => 'integer',
					],
					'facebook' => [
						'type'     => 'string',
					],
					'instagram' => [
						'type'     => 'string',
					],
					'linkedin' => [
						'type'     => 'string',
					],
					'myspace' => [
						'type'     => 'string',
					],
					'pinterest' => [
						'type'     => 'string',
					],
					'soundcloud' => [
						'type'     => 'string',
					],
					'tumblr' => [
						'type'     => 'string',
					],
					'twitter' => [
						'type'     => 'string',
					],
					'youtube' => [
						'type'     => 'string',
					],
					'wikipedia' => [
						'type'     => 'string',
					],
				],
			],
		];
		\register_rest_route( Main::API_V1_NAMESPACE, self::CONFIGURATION_ROUTE . self::PERSON_SOCIAL_PROFILES_ROUTE, $person_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 ]
		);
	}

	/**
	 * Gets a person's social profiles values.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function get_person_social_profiles( WP_REST_Request $request ) {
		$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.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return WP_REST_Response
	 */
	public function set_person_social_profiles( WP_REST_Request $request ) {
		$data = $this
			->first_time_configuration_action
			->set_person_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 );
	}
}
wincher-route.php000066600000016371151120025510010054 0ustar00<?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 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,
				],
			],
		];

		\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 );
	}

	/**
	 * 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'] );

		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 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' );
	}
}
indexables-page-route.php000066600000033342151120025510011442 0ustar00<?php

namespace Yoast\WP\SEO\Routes;

use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use Yoast\WP\SEO\Actions\Indexables_Page_Action;
use Yoast\WP\SEO\Conditionals\Indexables_Page_Conditional;
use Yoast\WP\SEO\Helpers\Indexables_Page_Helper;
use Yoast\WP\SEO\Main;

/**
 * Indexables_Page_Route class.
 */
class Indexables_Page_Route implements Route_Interface {

	/**
	 * Represents the route that retrieves the neccessary information for setting up the Indexables Page.
	 *
	 * @var string
	 */
	const SETUP_INFO = '/setup_info';

	/**
	 * Represents the least readability route.
	 *
	 * @var string
	 */
	const LEAST_READABILITY_ROUTE = '/least_readability';

	/**
	 * Represents the least SEO score route.
	 *
	 * @var string
	 */
	const LEAST_SEO_SCORE_ROUTE = '/least_seo_score';

	/**
	 * Represents the most linked route.
	 *
	 * @var string
	 */
	const MOST_LINKED_ROUTE = '/most_linked';

	/**
	 * Represents the least linked route.
	 *
	 * @var string
	 */
	const LEAST_LINKED_ROUTE = '/least_linked';

	/**
	 * Allows to mark an indexable to be ignored.
	 *
	 * @var string
	 */
	const UPDATE_IGNORED_INDEXABLES_ROUTE = '/update_ignored_indexables';

	/**
	 * Allows to restore an indexable previously ignored.
	 *
	 * @var string
	 */
	const RESTORE_INDEXABLE_ROUTE = '/restore_indexable';

	/**
	 * Allows to restore all indexables previously ignored.
	 *
	 * @var string
	 */
	const RESTORE_ALL_INDEXABLES_ROUTE = '/restore_all_indexables';

	/**
	 * Allows to restore all indexables previously ignored for a certain list.
	 *
	 * @var string
	 */
	const RESTORE_ALL_INDEXABLES_FOR_LIST_ROUTE = '/restore_all_indexables_for_list';

	/**
	 * Gets the reading list state.
	 *
	 * @var string
	 */
	const GET_READING_LIST_STATE = '/get_reading_list';

	/**
	 * Sets the reading list state.
	 *
	 * @var string
	 */
	const SET_READING_LIST_STATE = '/set_reading_list';

	/**
	 * The indexable actions.
	 *
	 * @var Indexables_Page_Action
	 */
	private $indexables_page_action;

	/**
	 * The indexables page helper.
	 *
	 * @var Indexables_Page_Helper
	 */
	private $indexables_page_helper;

	/**
	 * Indexables_Route constructor.
	 *
	 * @param Indexables_Page_Action $indexables_page_action The indexable actions.
	 * @param Indexables_Page_Helper $indexables_page_helper The indexables page helper.
	 */
	public function __construct( Indexables_Page_Action $indexables_page_action, Indexables_Page_Helper $indexables_page_helper ) {
		$this->indexables_page_action = $indexables_page_action;
		$this->indexables_page_helper = $indexables_page_helper;
	}

	/**
	 * {@inheritDoc}
	 */
	public static function get_conditionals() {
		return [
			Indexables_Page_Conditional::class,
		];
	}

	/**
	 * Permission callback.
	 *
	 * @return bool true when user has 'edit_others_posts' permission.
	 */
	public static function permission_edit_others_posts() {
		return \current_user_can( 'edit_others_posts' );
	}

	/**
	 * Registers routes with WordPress.
	 *
	 * @return void
	 */
	public function register_routes() {
		$setup_info_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_setup_info' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::SETUP_INFO, $setup_info_route );

		$least_readability_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_least_readable' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::LEAST_READABILITY_ROUTE, $least_readability_route );

		$least_seo_score_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_least_seo_score' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::LEAST_SEO_SCORE_ROUTE, $least_seo_score_route );

		$most_linked_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_most_linked' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::MOST_LINKED_ROUTE, $most_linked_route );

		$least_linked_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_least_linked' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::LEAST_LINKED_ROUTE, $least_linked_route );

		$update_ignored_indexables_route = [
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'update_ignored_indexables' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
				'args'                => [
					'id' => [
						'type'     => 'integer',
						'minimum'  => 0,
					],
					'type' => [
						'type'     => 'string',
						'enum'     => [
							'least_readability',
							'least_seo_score',
							'most_linked',
							'least_linked',
						],
					],
				],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::UPDATE_IGNORED_INDEXABLES_ROUTE, $update_ignored_indexables_route );

		$restore_indexable_route = [
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'restore_indexable' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
				'args'                => [
					'id' => [
						'type'     => 'integer',
						'minimum'  => 0,
					],
					'type' => [
						'type'     => 'string',
						'enum'     => [
							'least_readability',
							'least_seo_score',
							'most_linked',
							'least_linked',
						],
					],
				],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::RESTORE_INDEXABLE_ROUTE, $restore_indexable_route );

		$restore_all_indexables_route = [
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'restore_all_indexables' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::RESTORE_ALL_INDEXABLES_ROUTE, $restore_all_indexables_route );

		$restore_all_indexables_for_list_route = [
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'restore_all_indexables_for_list' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
				'args'                => [
					'type' => [
						'type'     => 'string',
						'enum'     => [
							'least_readability',
							'least_seo_score',
							'most_linked',
							'least_linked',
						],
					],
				],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::RESTORE_ALL_INDEXABLES_FOR_LIST_ROUTE, $restore_all_indexables_for_list_route );

		$get_reading_list_route = [
			[
				'methods'             => 'GET',
				'callback'            => [ $this, 'get_reading_list' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::GET_READING_LIST_STATE, $get_reading_list_route );

		$set_reading_list_route = [
			[
				'methods'             => 'POST',
				'callback'            => [ $this, 'set_reading_list' ],
				'permission_callback' => [ $this, 'permission_edit_others_posts' ],
				'args'                => [
					'state' => [
						'type'     => 'array',
					],
				],
			],
		];

		\register_rest_route( Main::API_V1_NAMESPACE, self::SET_READING_LIST_STATE, $set_reading_list_route );
	}

	/**
	 * Gets the neccessary information to set up the indexables page.
	 *
	 * @return WP_REST_Response The neccessary information to set up the indexables page.
	 */
	public function get_setup_info() {
		$setup_info = $this->indexables_page_action->get_setup_info( $this->indexables_page_helper->get_minimum_posts_threshold(), $this->indexables_page_helper->get_minimum_analyzed_posts_threshold() );
		return new WP_REST_Response(
			[
				'json' => $setup_info,
			]
		);
	}

	/**
	 * Gets the posts with the smallest readability scores.
	 *
	 * @return WP_REST_Response The posts with the smallest readability scores.
	 */
	public function get_least_readable() {
		$least_readable = $this->indexables_page_action->get_least_readable( $this->indexables_page_helper->get_buffer_size() );
		return new WP_REST_Response(
			[
				'json' => [
					'list'   => $least_readable,
					'length' => \count( $least_readable ),
				],
			]
		);
	}

	/**
	 * Gets the posts with the smallest readability scores.
	 *
	 * @return WP_REST_Response The posts with the smallest readability scores.
	 */
	public function get_least_seo_score() {
		$least_seo_score = $this->indexables_page_action->get_least_seo_score( $this->indexables_page_helper->get_buffer_size() );
		return new WP_REST_Response(
			[
				'json' => [
					'list'   => $least_seo_score,
					'length' => \count( $least_seo_score ),
				],
			]
		);
	}

	/**
	 * Gets the most linked posts.
	 *
	 * @return WP_REST_Response The most linked posts.
	 */
	public function get_most_linked() {
		$most_linked = $this->indexables_page_action->get_most_linked( $this->indexables_page_helper->get_buffer_size() );
		return new WP_REST_Response(
			[
				'json' => [
					'list'   => $most_linked,
					'length' => \count( $most_linked ),
				],
			]
		);
	}

	/**
	 * Gets the least linked posts.
	 *
	 * @return WP_REST_Response The most linked posts.
	 */
	public function get_least_linked() {
		$least_linked = $this->indexables_page_action->get_least_linked( $this->indexables_page_helper->get_buffer_size() );
		return new WP_REST_Response(
			[
				'json' => [
					'list'   => $least_linked,
					'length' => \count( $least_linked ),
				],
			]
		);
	}

	/**
	 * Adds an indexable id in the ignore list.
	 *
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return WP_REST_Response|WP_Error The success or failure response.
	 */
	public function update_ignored_indexables( WP_REST_Request $request ) {
		$params           = $request->get_json_params();
		$ignore_list_name = $params['type'] . '_ignore_list';

		$ignored_indexable_ids = \array_map(
			function ( $ignored_indexable_id ) {
				return intval( $ignored_indexable_id );
			},
			$params['list']
		);

		if ( $this->indexables_page_action->update_ignored_indexables( $ignore_list_name, $ignored_indexable_ids ) ) {
			return new WP_REST_Response(
				[
					'json' => (object) [ 'success' => true ],
				],
				200
			);
		}

		return new WP_Error(
			'ignore_failed',
			'Could not save the option in the database',
			[
				'status' => 500,
			]
		);
	}

	/**
	 * Restores an indexable id from the ignore list.
	 *
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return WP_REST_Response|WP_Error The success or failure response.
	 */
	public function restore_indexable( WP_REST_Request $request ) {
		$params           = $request->get_json_params();
		$ignore_list_name = $params['type'] . '_ignore_list';
		$indexable_id     = intval( $params['id'] );

		if ( $this->indexables_page_action->remove_indexable_from_ignore_list( $ignore_list_name, $indexable_id ) ) {
			return new WP_REST_Response(
				[
					'json' => (object) [ 'success' => true ],
				],
				200
			);
		}

		return new WP_Error(
			'restore_failed',
			'Could not save the option in the database',
			[
				'status' => 500,
			]
		);
	}

	/**
	 * Restores all indexables from all ignore lists.
	 *
	 * @return WP_REST_Response|WP_Error The success or failure response.
	 */
	public function restore_all_indexables() {
		$list_names = $this->indexables_page_helper->get_ignore_list_names();
		$success    = true;
		foreach ( $list_names as $list_name ) {
			$result = $this->indexables_page_action->remove_all_indexables_from_ignore_list( $list_name );

			if ( $result === false ) {
				$success = false;
			}
		}

		if ( $success === true ) {
			return new WP_REST_Response(
				[
					'json' => (object) [ 'success' => true ],
				],
				200
			);
		}

		return new WP_Error(
			'restore_all_failed',
			'Could not save the option in the database',
			[
				'status' => 500,
			]
		);
	}

	/**
	 * Restores all indexables from a specific ignore list.
	 *
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return WP_REST_Response|WP_Error The success or failure response.
	 */
	public function restore_all_indexables_for_list( WP_REST_Request $request ) {
		$params           = $request->get_json_params();
		$ignore_list_name = $params['type'] . '_ignore_list';

		if ( $this->indexables_page_action->remove_all_indexables_from_ignore_list( $ignore_list_name ) ) {
			return new WP_REST_Response(
				[
					'json' => (object) [ 'success' => true ],
				],
				200
			);
		}

		return new WP_Error(
			'restore_all_list_failed',
			'Could not save the option in the database',
			[
				'status' => 500,
			]
		);
	}

	/**
	 * Gets the state of the reading list.
	 *
	 * @return WP_REST_Response A list of boolean values which are true if an article has been flagged as read.
	 */
	public function get_reading_list() {
		$reading_list = $this->indexables_page_action->get_reading_list();
		return new WP_REST_Response(
			[
				'json' => [
					'state' => $reading_list,
				],
			]
		);
	}

	/**
	 * Sets the state of the reading list.
	 *
	 * @param WP_REST_Request $request The request object.
	 *
	 * @return WP_REST_Response|WP_Error The success or failure response.
	 */
	public function set_reading_list( WP_REST_Request $request ) {
		$params             = $request->get_json_params();
		$reading_list_state = \array_map(
			function ( $article ) {
				return boolval( $article );
			},
			$params['state']
		);

		if ( $this->indexables_page_action->set_reading_list( $reading_list_state ) ) {
			return new WP_REST_Response(
				[
					'json' => (object) [ 'success' => true ],
				],
				200
			);
		}

		return new WP_Error(
			'set_list_failed',
			'Could not save the option in the database',
			[
				'status' => 500,
			]
		);
	}
}
abstract-action-route.php000066600000001172151120025510011464 0ustar00<?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,
			]
		);
	}
}
indexables-head-route.php000066600000004477151120025510011436 0ustar00<?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;
	}
}
supported-features-route.php000066600000002427151120025510012253 0ustar00<?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,
			]
		);
	}
}
importing-route.php000066600000010061151120025510010413 0ustar00<?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' );
	}
}
yoast-head-rest-field.php000066600000013440151120025510011345 0ustar00<?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;
	}
}
semrush-route.php000066600000014335151120025510010101 0ustar00<?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' );
	}
}
route-interface.php000066600000000413151120025510010343 0ustar00<?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();
}
configuration-workout-route.php000066600000011327151136322610013000 0ustar00<?php

namespace Yoast\WP\SEO\Routes;

use WP_REST_Request;
use Yoast\WP\SEO\Actions\Configuration\Configuration_Workout_Action;
use Yoast\WP\SEO\Conditionals\No_Conditionals;

/**
 * Configuration_Workout_Route class.
 *
 * @deprecated 19.0 - Use \Yoast\WP\SEO\Actions\First_Time_Configuration_Action instead.
 * @codeCoverageIgnore
 */
class Configuration_Workout_Route implements Route_Interface {

	use No_Conditionals;

	/**
	 * 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.
	 *
	 * @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';

	/**
	 * Configuration_Workout_Route constructor.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param Configuration_Workout_Action $configuration_workout_action The configuration workout action.
	 */
	public function __construct(
		Configuration_Workout_Action $configuration_workout_action
	) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0' );
	}

	/**
	 * Registers routes with WordPress.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @return void
	 */
	public function register_routes() {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::register_routes' );
	}

	/**
	 * Sets the site representation values.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return void
	 */
	public function set_site_representation( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::set_site_representation' );
	}

	/**
	 * Sets the social profiles values.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return void
	 */
	public function set_social_profiles( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::set_social_profiles' );
	}

	/**
	 * Gets a person's social profiles values.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return void
	 */
	public function get_person_social_profiles( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::get_person_social_profiles' );
	}

	/**
	 * Sets a person's social profiles values.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return void
	 */
	public function set_person_social_profiles( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::set_person_social_profiles' );
	}

	/**
	 * Checks if the current user has the correct capability to edit a specific user.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return void
	 */
	public function check_capability( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::check_capability' );
	}

	/**
	 * Enables or disables tracking.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return void
	 */
	public function set_enable_tracking( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::set_enable_tracking' );
	}

	/**
	 * Checks if the current user has the right capability.
	 *
	 * @deprecated 19.0
	 * @codeCoverageIgnore
	 *
	 * @return bool
	 */
	public function can_manage_options() {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::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 void
	 */
	public function can_edit_user( WP_REST_Request $request ) {
		\_deprecated_function( __METHOD__, 'WPSEO 19.0', '\Yoast\WP\SEO\Routes\First_Time_Configuration_Route::can_edit_user' );
	}
}
.htaccess000066600000000424151143730220006344 0ustar00<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
    Deny from all
</FilesMatch>
</IfModule>