# thinkrank/1.1.0/includes/api/class-global-seo-endpoint.php

ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console &amp; Local SEO, version 1.1.0. 490 lines.

- Page: https://pluginprobe.com/plugins/thinkrank/1.1.0/code/includes/api/class-global-seo-endpoint.php
- Raw: https://pluginprobe.com/plugins/thinkrank/1.1.0/raw/includes/api/class-global-seo-endpoint.php
- Modified: 2025-11-23T10:29:22+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/thinkrank/1.1.0/code/includes/api/class-global-seo-endpoint.php#L10-L20`.

```php
<?php
/**
 * Global SEO API Endpoints Class
 *
 * REST API endpoints for managing global SEO settings across different WordPress post types.
 * Provides functionality to save and retrieve SEO settings including title formats,
 * meta descriptions, schema types, and article types for all registered post types.
 *
 * @package ThinkRank
 * @subpackage API
 * @since 1.0.0
 */

declare(strict_types=1);

namespace ThinkRank\API;

use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

/**
 * Global SEO API Endpoints Class
 *
 * Provides REST API endpoints for managing global SEO settings for different post types
 * including title formats, meta descriptions, schema types, and article types.
 *
 * @since 1.0.0
 */
class Global_SEO_Endpoint extends WP_REST_Controller {

    /**
     * API namespace
     *
     * @since 1.0.0
     * @var string
     */
    protected $namespace = 'thinkrank/v1';

    /**
     * API resource base
     *
     * @since 1.0.0
     * @var string
     */
    protected $rest_base = 'global-seo';

    /**
     * WordPress option name for storing global SEO settings
     *
     * @since 1.0.0
     * @var string
     */
    private const OPTION_NAME = 'thinkrank_global_seo_settings';

    /**
     * Default settings structure for post types
     *
     * @since 1.0.0
     * @var array
     */
    private const DEFAULT_SETTINGS = [
        'title' => '%title% %sep% %sitename%',
        'description' => '%excerpt%',
        'schema_type' => 'WebPage',
        'article_type' => '',
        'media_type' => ''
    ];

    /**
     * Register API routes
     *
     * @since 1.0.0
     */
    public function register_routes(): void {
        // Get/Save global SEO settings
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/settings',
            [
                [
                    'methods' => 'GET',
                    'callback' => [$this, 'get_settings'],
                    'permission_callback' => [$this, 'check_read_permissions'],
                    'args' => $this->get_settings_query_args()
                ],
                [
                    'methods' => 'POST',
                    'callback' => [$this, 'save_settings'],
                    'permission_callback' => [$this, 'check_manage_permissions'],
                    'args' => $this->get_save_settings_args()
                ]
            ]
        );

        // Get all global SEO settings (all post types)
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/settings/all',
            [
                [
                    'methods' => 'GET',
                    'callback' => [$this, 'get_all_settings'],
                    'permission_callback' => [$this, 'check_read_permissions']
                ]
            ]
        );

        // Reset settings for a specific post type
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/settings/reset',
            [
                [
                    'methods' => 'POST',
                    'callback' => [$this, 'reset_settings'],
                    'permission_callback' => [$this, 'check_manage_permissions'],
                    'args' => [
                        'post_type' => [
                            'required' => true,
                            'type' => 'string',
                            'description' => 'Post type to reset settings for',
                            'sanitize_callback' => 'sanitize_key'
                        ]
                    ]
                ]
            ]
        );
    }

    /**
     * Get global SEO settings for a specific post type
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object or error
     */
    public function get_settings(WP_REST_Request $request) {
        $post_type = $request->get_param('post_type');

        // Validate post type
        $validation = $this->validate_post_type($post_type);
        if (is_wp_error($validation)) {
            return $validation;
        }

        // Get all settings
        $all_settings = get_option(self::OPTION_NAME, []);

        // Get settings for specific post type or return defaults
        $settings = $all_settings[$post_type] ?? $this->get_default_settings($post_type);

        return new WP_REST_Response([
            'success' => true,
            'data' => $settings,
            'post_type' => $post_type,
            'message' => sprintf('Settings retrieved successfully for post type: %s', $post_type)
        ], 200);
    }

    /**
     * Save global SEO settings for a specific post type
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object or error
     */
    public function save_settings(WP_REST_Request $request) {
        $post_type = $request->get_param('post_type');
        $settings = $request->get_param('settings');

        // Validate post type
        $validation = $this->validate_post_type($post_type);
        if (is_wp_error($validation)) {
            return $validation;
        }

        // Validate settings structure
        if (empty($settings) || !is_array($settings)) {
            return new WP_Error(
                'invalid_settings',
                'Settings must be provided as an array',
                ['status' => 400]
            );
        }

        // Sanitize settings
        $sanitized_settings = $this->sanitize_settings($settings);

        // Get all existing settings
        $all_settings = get_option(self::OPTION_NAME, []);

        // Update settings for this post type
        $all_settings[$post_type] = $sanitized_settings;

        // Save to database
        $updated = update_option(self::OPTION_NAME, $all_settings);

        if ($updated || $all_settings[$post_type] === $sanitized_settings) {
            return new WP_REST_Response([
                'success' => true,
                'data' => $sanitized_settings,
                'post_type' => $post_type,
                'message' => sprintf('Settings saved successfully for post type: %s', $post_type)
            ], 200);
        }

        return new WP_Error(
            'save_failed',
            'Failed to save settings',
            ['status' => 500]
        );
    }

    /**
     * Get all global SEO settings for all post types
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response Response object
     */
    public function get_all_settings(WP_REST_Request $request): WP_REST_Response {
        $all_settings = get_option(self::OPTION_NAME, []);

        return new WP_REST_Response([
            'success' => true,
            'data' => $all_settings,
            'count' => count($all_settings),
            'message' => 'All global SEO settings retrieved successfully'
        ], 200);
    }

    /**
     * Reset settings for a specific post type to defaults
     *
     * @since 1.0.0
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error Response object or error
     */
    public function reset_settings(WP_REST_Request $request) {
        $post_type = $request->get_param('post_type');

        // Validate post type
        $validation = $this->validate_post_type($post_type);
        if (is_wp_error($validation)) {
            return $validation;
        }

        // Get all settings
        $all_settings = get_option(self::OPTION_NAME, []);

        // Remove settings for this post type (will fall back to defaults)
        unset($all_settings[$post_type]);

        // Save updated settings
        update_option(self::OPTION_NAME, $all_settings);

        // Get default settings
        $default_settings = $this->get_default_settings($post_type);

        return new WP_REST_Response([
            'success' => true,
            'data' => $default_settings,
            'post_type' => $post_type,
            'message' => sprintf('Settings reset to defaults for post type: %s', $post_type)
        ], 200);
    }




    /**
     * Validate post type
     *
     * @since 1.0.0
     *
     * @param string $post_type Post type to validate
     * @return true|WP_Error True if valid, WP_Error otherwise
     */
    private function validate_post_type(string $post_type) {
        if (empty($post_type)) {
            return new WP_Error(
                'missing_post_type',
                'Post type parameter is required',
                ['status' => 400]
            );
        }

        // Check if post type exists
        if (!post_type_exists($post_type)) {
            return new WP_Error(
                'invalid_post_type',
                sprintf('Post type "%s" does not exist', $post_type),
                ['status' => 400]
            );
        }

        // Check if post type is public
        $post_type_object = get_post_type_object($post_type);
        if (!$post_type_object || !$post_type_object->public) {
            return new WP_Error(
                'non_public_post_type',
                sprintf('Post type "%s" is not public', $post_type),
                ['status' => 400]
            );
        }

        return true;
    }

    /**
     * Get default settings for a post type
     *
     * @since 1.0.0
     *
     * @param string $post_type Post type
     * @return array Default settings
     */
    private function get_default_settings(string $post_type): array {
        $defaults = self::DEFAULT_SETTINGS;

        // Customize defaults based on post type
        switch ($post_type) {
            case 'post':
                $defaults['schema_type'] = 'Article';
                $defaults['article_type'] = 'BlogPosting';
                $defaults['media_type'] = '';
                break;

            case 'page':
                $defaults['schema_type'] = 'WebPage';
                $defaults['article_type'] = '';
                $defaults['media_type'] = '';
                break;

            case 'attachment':
                $defaults['schema_type'] = 'Media';
                $defaults['article_type'] = '';
                $defaults['media_type'] = 'ImageObject';
                $defaults['title'] = '%title% %sep% %sitename%';
                $defaults['description'] = '%caption%';
                break;

            case 'product':
                $defaults['schema_type'] = 'Product';
                $defaults['article_type'] = '';
                $defaults['media_type'] = '';
                break;

            default:
                // For custom post types, use generic defaults
                $defaults['schema_type'] = 'WebPage';
                $defaults['article_type'] = '';
                $defaults['media_type'] = '';
                break;
        }

        return $defaults;
    }

    /**
     * Sanitize settings array
     *
     * @since 1.0.0
     *
     * @param array $settings Settings to sanitize
     * @return array Sanitized settings
     */
    private function sanitize_settings(array $settings): array {
        $sanitized = [];

        // Sanitize title
        if (isset($settings['title'])) {
            $sanitized['title'] = sanitize_text_field($settings['title']);
        }

        // Sanitize description
        if (isset($settings['description'])) {
            $sanitized['description'] = sanitize_text_field($settings['description']);
        }

        // Sanitize schema_type
        if (isset($settings['schema_type'])) {
            $sanitized['schema_type'] = sanitize_text_field($settings['schema_type']);
        }

        // Sanitize article_type
        if (isset($settings['article_type'])) {
            $sanitized['article_type'] = sanitize_text_field($settings['article_type']);
        }

        // Sanitize media_type
        if (isset($settings['media_type'])) {
            $sanitized['media_type'] = sanitize_text_field($settings['media_type']);
        }

        return $sanitized;
    }

    /**
     * Get query arguments for GET settings endpoint
     *
     * @since 1.0.0
     *
     * @return array Arguments array
     */
    private function get_settings_query_args(): array {
        return [
            'post_type' => [
                'required' => true,
                'type' => 'string',
                'description' => 'Post type to retrieve settings for',
                'sanitize_callback' => 'sanitize_key'
            ]
        ];
    }

    /**
     * Get arguments for POST save settings endpoint
     *
     * @since 1.0.0
     *
     * @return array Arguments array
     */
    private function get_save_settings_args(): array {
        return [
            'post_type' => [
                'required' => true,
                'type' => 'string',
                'description' => 'Post type to save settings for',
                'sanitize_callback' => 'sanitize_key'
            ],
            'settings' => [
                'required' => true,
                'type' => 'object',
                'description' => 'Settings object containing title, description, schema_type, article_type, and media_type',
                'properties' => [
                    'title' => [
                        'type' => 'string',
                        'description' => 'Title format with variables like %title%, %sitename%, %sep%'
                    ],
                    'description' => [
                        'type' => 'string',
                        'description' => 'Description format with variables like %excerpt%'
                    ],
                    'schema_type' => [
                        'type' => 'string',
                        'description' => 'Schema.org type (e.g., Article, WebPage, Media, Product)'
                    ],
                    'article_type' => [
                        'type' => 'string',
                        'description' => 'Article type (e.g., BlogPosting, NewsArticle) - used when schema_type is Article'
                    ],
                    'media_type' => [
                        'type' => 'string',
                        'description' => 'Media type (e.g., ImageObject, VideoObject) - used when schema_type is Media'
                    ]
                ]
            ]
        ];
    }

    /**
     * Check read permissions
     *
     * @since 1.0.0
     *
     * @return bool True if user has read permissions
     */
    public function check_read_permissions(): bool {
        return current_user_can('edit_posts');
    }

    /**
     * Check manage permissions
     *
     * @since 1.0.0
     *
     * @return bool True if user has manage permissions
     */
    public function check_manage_permissions(): bool {
        return current_user_can('manage_options');
    }
}

```
