calculator = $calculator; } /** * Initialize the endpoint * * @return void */ public function init(): void { add_action('rest_api_init', [$this, 'register_routes']); } /** * Register REST API routes * * @return void */ public function register_routes(): void { // Calculate SEO score endpoint register_rest_route('thinkrank/v1', '/seo-score/calculate', [ 'methods' => 'POST', 'callback' => [$this, 'calculate_score'], 'permission_callback' => [$this, 'check_permissions'], 'args' => [ 'post_id' => [ 'required' => true, 'type' => 'integer', 'validate_callback' => [$this, 'validate_post_id'], ], 'target_keyword' => [ 'required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ], 'save_score' => [ 'required' => false, 'type' => 'boolean', 'default' => true, ], 'live_content' => [ 'required' => false, 'type' => 'string', 'sanitize_callback' => 'wp_kses_post', ], ], ]); // Get existing SEO score endpoint register_rest_route('thinkrank/v1', '/seo-score/get', [ 'methods' => 'GET', 'callback' => [$this, 'get_existing_score'], 'permission_callback' => [$this, 'check_permissions'], 'args' => [ 'post_id' => [ 'required' => true, 'type' => 'integer', 'validate_callback' => [$this, 'validate_post_id'], ], ], ]); // Get score history endpoint register_rest_route('thinkrank/v1', '/seo-score/history', [ 'methods' => 'GET', 'callback' => [$this, 'get_score_history'], 'permission_callback' => [$this, 'check_permissions'], 'args' => [ 'post_id' => [ 'required' => true, 'type' => 'integer', 'validate_callback' => [$this, 'validate_post_id'], ], 'limit' => [ 'required' => false, 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 50, ], ], ]); // Get latest score endpoint register_rest_route('thinkrank/v1', '/seo-score/latest', [ 'methods' => 'GET', 'callback' => [$this, 'get_latest_score'], 'permission_callback' => [$this, 'check_permissions'], 'args' => [ 'post_id' => [ 'required' => true, 'type' => 'integer', 'validate_callback' => [$this, 'validate_post_id'], ], ], ]); } /** * Calculate SEO score for a post * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error Response object */ public function calculate_score(WP_REST_Request $request) { try { $post_id = $request->get_param('post_id'); $target_keyword = $request->get_param('target_keyword') ?? ''; $save_score = $request->get_param('save_score') ?? true; $live_content = $request->get_param('live_content') ?? ''; // Analyze content - use live content if provided, otherwise saved content if (!empty($live_content)) { $content_data = $this->calculator->analyze_live_content($live_content, $post_id); } else { $content_data = $this->calculator->analyze_post_content($post_id); } if (empty($content_data)) { return new WP_Error( 'post_not_found', 'Post not found or has no content', ['status' => 404] ); } // Get post metadata $metadata = [ 'title' => get_post_meta($post_id, '_thinkrank_seo_title', true) ?: $content_data['title'], 'description' => get_post_meta($post_id, '_thinkrank_meta_description', true) ?: '', ]; // Calculate score $score_data = $this->calculator->calculate_score( $content_data, $metadata, ['target_keyword' => $target_keyword] ); // Save score if requested if ($save_score) { $user_id = get_current_user_id(); $score_id = $this->calculator->save_score($post_id, $user_id, $score_data); if ($score_id) { $score_data['score_id'] = $score_id; } } return new WP_REST_Response([ 'success' => true, 'data' => $score_data, ], 200); } catch (\Exception $e) { return new WP_Error( 'calculation_failed', 'Failed to calculate SEO score: ' . $e->getMessage(), ['status' => 500] ); } } /** * Get existing SEO score for a post * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error Response object */ public function get_existing_score(WP_REST_Request $request) { try { $post_id = $request->get_param('post_id'); // Get existing score data from database $existing_data = $this->calculator->get_existing_score_data($post_id); if ($existing_data) { return new WP_REST_Response([ 'success' => true, 'data' => $existing_data, 'message' => __('Existing SEO score retrieved successfully', 'thinkrank') ], 200); } else { return new WP_REST_Response([ 'success' => false, 'data' => null, 'message' => __('No existing SEO analysis found', 'thinkrank') ], 200); // 200 because it's not an error, just no data } } catch (\Exception $e) { return new WP_Error('get_score_error', $e->getMessage(), ['status' => 500]); } } /** * Get score history for a post * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error Response object */ public function get_score_history(WP_REST_Request $request) { try { $post_id = $request->get_param('post_id'); $limit = $request->get_param('limit') ?? 10; $history = $this->calculator->get_score_history($post_id, $limit); return new WP_REST_Response([ 'success' => true, 'data' => $history, ], 200); } catch (\Exception $e) { return new WP_Error( 'history_failed', 'Failed to retrieve score history: ' . $e->getMessage(), ['status' => 500] ); } } /** * Get latest score for a post * * @param WP_REST_Request $request Request object * @return WP_REST_Response|WP_Error Response object */ public function get_latest_score(WP_REST_Request $request) { try { $post_id = $request->get_param('post_id'); $latest_score = $this->calculator->get_latest_score($post_id); return new WP_REST_Response([ 'success' => true, 'data' => $latest_score, ], 200); } catch (\Exception $e) { return new WP_Error( 'latest_failed', 'Failed to retrieve latest score: ' . $e->getMessage(), ['status' => 500] ); } } /** * Check permissions for API access * * @param WP_REST_Request $request Request object * @return bool True if user has permission */ public function check_permissions(WP_REST_Request $request): bool { // Check if user is logged in if (!is_user_logged_in()) { return false; } // Check if user can edit posts if (!current_user_can('edit_posts')) { return false; } // For specific post operations, check if user can edit the specific post $post_id = $request->get_param('post_id'); if ($post_id && !current_user_can('edit_post', $post_id)) { return false; } return true; } /** * Validate post ID parameter * * @param mixed $value Parameter value * @param WP_REST_Request $request Request object * @param string $param Parameter name * @return bool True if valid */ public function validate_post_id($value, WP_REST_Request $request, string $param): bool { if (!is_numeric($value) || $value <= 0) { return false; } $post = get_post((int) $value); return $post !== null; } }