You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
546 lines
14 KiB
PHTML
546 lines
14 KiB
PHTML
8 months ago
|
<?php
|
||
|
/**
|
||
|
* The Global functionality of the plugin.
|
||
|
*
|
||
|
* Defines the functionality loaded on admin.
|
||
|
*
|
||
|
* @since 1.0.71
|
||
|
* @package RankMath
|
||
|
* @subpackage RankMath\Rest
|
||
|
* @author Rank Math <support@rankmath.com>
|
||
|
*/
|
||
|
|
||
|
namespace RankMath\ContentAI;
|
||
|
|
||
|
use WP_Error;
|
||
|
use WP_REST_Server;
|
||
|
use WP_REST_Request;
|
||
|
use WP_REST_Controller;
|
||
|
use RankMath\Helper;
|
||
|
use RankMath\Admin\Admin_Helper;
|
||
|
|
||
|
defined( 'ABSPATH' ) || exit;
|
||
|
|
||
|
/**
|
||
|
* Rest class.
|
||
|
*/
|
||
|
class Rest extends WP_REST_Controller {
|
||
|
|
||
|
/**
|
||
|
* Registered data.
|
||
|
*
|
||
|
* @var array|false
|
||
|
*/
|
||
|
private $registered;
|
||
|
|
||
|
/**
|
||
|
* Constructor.
|
||
|
*/
|
||
|
public function __construct() {
|
||
|
$this->namespace = \RankMath\Rest\Rest_Helper::BASE . '/ca';
|
||
|
$this->registered = Admin_Helper::get_registration_data();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers the routes for the objects of the controller.
|
||
|
*/
|
||
|
public function register_routes() {
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/researchKeyword',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'research_keyword' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/getCredits',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'get_credits' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/createPost',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'create_post' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/saveOutput',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'save_output' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/deleteOutput',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'delete_output' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/updateRecentPrompt',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'update_recent_prompt' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/updatePrompt',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'update_prompt' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/savePrompts',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::CREATABLE,
|
||
|
'callback' => [ $this, 'save_prompts' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/pingContentAI',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::READABLE,
|
||
|
'callback' => [ $this, 'ping_content_ai' ],
|
||
|
'permission_callback' => [ $this, 'has_ping_permission' ],
|
||
|
]
|
||
|
);
|
||
|
|
||
|
register_rest_route(
|
||
|
$this->namespace,
|
||
|
'/migrateuser',
|
||
|
[
|
||
|
'methods' => WP_REST_Server::READABLE,
|
||
|
'callback' => [ $this, 'migrate_user' ],
|
||
|
'permission_callback' => [ $this, 'has_permission' ],
|
||
|
]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check API key in request.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
* @return bool Whether the API key matches or not.
|
||
|
*/
|
||
|
public function has_ping_permission( WP_REST_Request $request ) {
|
||
|
if ( empty( $this->registered ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return $request->get_param( 'apiKey' ) === $this->registered['api_key'] &&
|
||
|
$request->get_param( 'username' ) === $this->registered['username'];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determines if the current user can manage analytics.
|
||
|
*
|
||
|
* @return true
|
||
|
*/
|
||
|
public function has_permission() {
|
||
|
if ( ! Helper::has_cap( 'content_ai' ) || empty( $this->registered ) ) {
|
||
|
return new WP_Error(
|
||
|
'rest_cannot_access',
|
||
|
__( 'Sorry, only authenticated users can research the keyword.', 'rank-math' ),
|
||
|
[ 'status' => rest_authorization_required_code() ]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Content AI Credits.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return int Credits.
|
||
|
*/
|
||
|
public function get_credits( WP_REST_Request $request ) {
|
||
|
$credits = Helper::get_content_ai_credits( true, true );
|
||
|
if ( ! empty( $credits['error'] ) ) {
|
||
|
$error = $credits['error'];
|
||
|
$error_texts = Helper::get_content_ai_errors();
|
||
|
return [
|
||
|
'error' => ! empty( $error_texts[ $error ] ) ? wp_specialchars_decode( $error_texts[ $error ], ENT_QUOTES ) : $error,
|
||
|
'credits' => isset( $credits['credits'] ) ? $credits['credits'] : '',
|
||
|
];
|
||
|
}
|
||
|
|
||
|
return $credits;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Research a keyword.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function research_keyword( WP_REST_Request $request ) {
|
||
|
$object_id = $request->get_param( 'objectID' );
|
||
|
$country = $request->get_param( 'country' );
|
||
|
$keyword = mb_strtolower( $request->get_param( 'keyword' ) );
|
||
|
$force_update = $request->get_param( 'force_update' );
|
||
|
$keyword_data = get_option( 'rank_math_ca_data' );
|
||
|
|
||
|
if ( ! in_array( get_post_type( $object_id ), (array) Helper::get_settings( 'general.content_ai_post_types' ), true ) ) {
|
||
|
return [
|
||
|
'data' => esc_html__( 'Content AI is not enabled on this Post type.', 'rank-math' ),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if ( ! apply_filters( 'rank_math/content_ai/call_api', true ) ) {
|
||
|
return [
|
||
|
'data' => 'show_dummy_data',
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
! $force_update &&
|
||
|
! empty( $keyword_data ) &&
|
||
|
! empty( $keyword_data[ $country ] ) &&
|
||
|
! empty( $keyword_data[ $country ][ $keyword ] )
|
||
|
) {
|
||
|
update_post_meta(
|
||
|
$object_id,
|
||
|
'rank_math_ca_keyword',
|
||
|
[
|
||
|
'keyword' => $keyword,
|
||
|
'country' => $country,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
return [
|
||
|
'data' => $keyword_data[ $country ][ $keyword ],
|
||
|
'keyword' => $keyword,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
$data = $this->get_researched_data( $keyword, $country, $force_update );
|
||
|
if ( ! empty( $data['error'] ) ) {
|
||
|
return $this->get_errored_data( $data['error'] );
|
||
|
}
|
||
|
|
||
|
$credits = ! empty( $data['credits'] ) ? $data['credits'] : 0;
|
||
|
if ( ! empty( $credits ) ) {
|
||
|
$credits = $credits['available'] - $credits['taken'];
|
||
|
}
|
||
|
|
||
|
$data = $data['data']['details'];
|
||
|
$this->get_recommendations( $data );
|
||
|
|
||
|
update_post_meta(
|
||
|
$object_id,
|
||
|
'rank_math_ca_keyword',
|
||
|
[
|
||
|
'keyword' => $keyword,
|
||
|
'country' => $country,
|
||
|
]
|
||
|
);
|
||
|
$keyword_data[ $country ][ $keyword ] = $data;
|
||
|
update_option( 'rank_math_ca_data', $keyword_data, false );
|
||
|
Helper::update_credits( $credits );
|
||
|
|
||
|
return [
|
||
|
'data' => $keyword_data[ $country ][ $keyword ],
|
||
|
'credits' => $credits,
|
||
|
'keyword' => $keyword,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new Post from Content AI Page.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function create_post( WP_REST_Request $request ) {
|
||
|
$content = $request->get_param( 'content' );
|
||
|
$title = 'Content AI Post';
|
||
|
$blocks = parse_blocks( $content );
|
||
|
$current_block = ! empty( $blocks ) ? current( $blocks ) : '';
|
||
|
if (
|
||
|
! empty( $current_block ) &&
|
||
|
$current_block['blockName'] === 'core/heading' &&
|
||
|
$current_block['attrs']['level'] === 1
|
||
|
) {
|
||
|
$title = wp_strip_all_tags( $current_block['innerHTML'] );
|
||
|
}
|
||
|
|
||
|
$post_id = wp_insert_post(
|
||
|
[
|
||
|
'post_title' => $title,
|
||
|
'post_content' => $content,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
return wp_specialchars_decode( add_query_arg( 'tab', 'content-ai', get_edit_post_link( $post_id ) ) );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save the API output.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function save_output( WP_REST_Request $request ) {
|
||
|
$outputs = $request->get_param( 'outputs' );
|
||
|
$endpoint = $request->get_param( 'endpoint' );
|
||
|
$is_chat = $request->get_param( 'isChat' );
|
||
|
$attributes = $request->get_param( 'attributes' );
|
||
|
$credits_data = $request->get_param( 'credits' );
|
||
|
|
||
|
if ( ! empty( $credits_data ) ) {
|
||
|
$credits = ! empty( $credits_data['credits'] ) ? $credits_data['credits'] : [];
|
||
|
$data = [
|
||
|
'credits' => ! empty( $credits['available'] ) ? $credits['available'] - $credits['taken'] : 0,
|
||
|
'plan' => ! empty( $credits_data['plan'] ) ? $credits_data['plan'] : '',
|
||
|
'refresh_date' => ! empty( $credits_data['refreshDate'] ) ? $credits_data['refreshDate'] : '',
|
||
|
];
|
||
|
|
||
|
Helper::update_credits( $data );
|
||
|
}
|
||
|
|
||
|
if ( $is_chat ) {
|
||
|
Helper::update_chats( current( $outputs ), end( $attributes['messages'] ), $attributes['session'], $attributes['isNew'], $attributes['regenerate'] );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return Helper::update_outputs( $endpoint, $outputs );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete the API output.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function delete_output( WP_REST_Request $request ) {
|
||
|
$is_chat = $request->get_param( 'isChat' );
|
||
|
if ( $is_chat ) {
|
||
|
return Helper::delete_chats( $request->get_param( 'index' ) );
|
||
|
}
|
||
|
|
||
|
return Helper::delete_outputs();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the Prompts.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function update_prompt( WP_REST_Request $request ) {
|
||
|
$prompt = $request->get_param( 'prompt' );
|
||
|
|
||
|
if ( is_string( $prompt ) ) {
|
||
|
return Helper::delete_prompt( $prompt );
|
||
|
}
|
||
|
|
||
|
return Helper::update_prompts( $prompt );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Save the Prompts.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function save_prompts( WP_REST_Request $request ) {
|
||
|
$prompts = $request->get_param( 'prompts' );
|
||
|
if ( empty( $prompts ) ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return Helper::save_default_prompts( $prompts );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the Recent Prompts.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function update_recent_prompt( WP_REST_Request $request ) {
|
||
|
$prompt = $request->get_param( 'prompt' );
|
||
|
return Helper::update_recent_prompts( $prompt );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Endpoing to update the AI plan and credits.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function ping_content_ai( WP_REST_Request $request ) {
|
||
|
$credits = ! empty( $data['credits'] ) ? json_decode( $data['credits'], true ) : [];
|
||
|
$data = [
|
||
|
'credits' => ! empty( $credits['available'] ) ? $credits['available'] - $credits['taken'] : 0,
|
||
|
'plan' => $request->get_param( 'plan' ),
|
||
|
'refresh_date' => $request->get_param( 'refreshDate' ),
|
||
|
];
|
||
|
|
||
|
Helper::update_credits( $data );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Migrate user to nest js server.
|
||
|
*
|
||
|
* @param WP_REST_Request $request Full details about the request.
|
||
|
*
|
||
|
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
|
||
|
*/
|
||
|
public function migrate_user( WP_REST_Request $request ) {
|
||
|
return Helper::migrate_user_to_nest_js();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get data from the API.
|
||
|
*
|
||
|
* @param string $keyword Researched keyword.
|
||
|
* @param string $country Researched country.
|
||
|
* @param bool $force_update Whether to force update the researched data.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private function get_researched_data( $keyword, $country, $force_update = false ) {
|
||
|
$args = [
|
||
|
'username' => rawurlencode( $this->registered['username'] ),
|
||
|
'api_key' => rawurlencode( $this->registered['api_key'] ),
|
||
|
'keyword' => rawurlencode( $keyword ),
|
||
|
'site_url' => rawurlencode( Helper::get_home_url() ),
|
||
|
'new_api' => 1,
|
||
|
];
|
||
|
|
||
|
if ( 'all' !== $country ) {
|
||
|
$args['locale'] = rawurlencode( $country );
|
||
|
}
|
||
|
|
||
|
if ( $force_update ) {
|
||
|
$args['force_refresh'] = 1;
|
||
|
}
|
||
|
|
||
|
$url = add_query_arg(
|
||
|
$args,
|
||
|
CONTENT_AI_URL . '/ai/research'
|
||
|
);
|
||
|
|
||
|
$data = wp_remote_get(
|
||
|
$url,
|
||
|
[
|
||
|
'timeout' => 60,
|
||
|
]
|
||
|
);
|
||
|
|
||
|
$response_code = wp_remote_retrieve_response_code( $data );
|
||
|
if ( 200 !== $response_code ) {
|
||
|
return [
|
||
|
'error' => 410 !== $response_code ? $data['response']['message'] : wp_kses_post(
|
||
|
sprintf(
|
||
|
// Translators: link to the update page.
|
||
|
__( 'There is a new version of Content AI available! %s the Rank Math SEO plugin to use this feature.', 'rank-math' ),
|
||
|
'<a href="' . esc_url( self_admin_url( 'update-core.php' ) ) . '">' . __( 'Please update', 'rank-math' ) . '</a>'
|
||
|
)
|
||
|
),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
$data = wp_remote_retrieve_body( $data );
|
||
|
$data = json_decode( $data, true );
|
||
|
|
||
|
if ( empty( $data['error'] ) && empty( $data['data']['details'] ) ) {
|
||
|
return [
|
||
|
'error' => esc_html__( 'No data found for the researched keyword.', 'rank-math' ),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
return $data;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get errored data.
|
||
|
*
|
||
|
* @param array $error Error data received from the API.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private function get_errored_data( $error ) {
|
||
|
if ( empty( $error['code'] ) ) {
|
||
|
return [
|
||
|
'data' => $error,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if ( 'invalid_domain' === $error['code'] ) {
|
||
|
return [
|
||
|
'data' => esc_html__( 'This feature is not available on the localhost.', 'rank-math' ),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if ( 'domain_limit_reached' === $error['code'] ) {
|
||
|
return [
|
||
|
'data' => esc_html__( 'You have used all the free credits which are allowed to this domain.', 'rank-math' ),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
return [
|
||
|
'data' => '',
|
||
|
'credits' => $error['code'],
|
||
|
];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the Recommendations data.
|
||
|
*
|
||
|
* @param array $data Researched data.
|
||
|
*/
|
||
|
private function get_recommendations( &$data ) {
|
||
|
foreach ( $data['recommendations'] as $key => $value ) {
|
||
|
if ( ! is_array( $value ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$data['recommendations'][ $key ]['total'] = array_sum( $value );
|
||
|
}
|
||
|
}
|
||
|
}
|