HEX
Server: LiteSpeed
System: Linux server902.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: deshuvsd (2181)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //proc/thread-self/root/home/deshuvsd/www/wp-content/plugins/surerank/inc/api/onboarding.php
<?php
/**
 * Onboarding class
 *
 * Handles onboarding related REST API endpoints for the SureRank plugin.
 *
 * @package SureRank\Inc\API
 */

namespace SureRank\Inc\API;

use SureRank\Inc\Admin\Helper;
use SureRank\Inc\Admin\Update_Timestamp;
use SureRank\Inc\Functions\Send_Json;
use SureRank\Inc\Functions\Settings;
use SureRank\Inc\Functions\Update;
use SureRank\Inc\Traits\Get_Instance;
use WP_REST_Request;
use WP_REST_Server;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Class Onboarding
 *
 * Handles onboarding related REST API endpoints.
 */
class Onboarding extends Api_Base {
	use Get_Instance;

	/**
	 * Route Onboarding
	 */
	protected const ONBOARDING = '/onboarding';

	/**
	 * Route Improve Description
	 */
	protected const IMPROVE_DESCRIPTION = '/onboarding/improve-description';

	/**
	 * Register API routes.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			$this->get_api_namespace(),
			self::ONBOARDING,
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'save_website_details' ],
				'permission_callback' => [ $this, 'validate_permission' ],
				'args'                => [
					'website_type'         => [
						'type'              => 'string',
						'required'          => false,
						'description'       => __( 'Type of the website.', 'surerank' ),
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => static function( $value ) {
							return is_string( $value );
						},
					],
					'website_name'         => [
						'type'              => 'string',
						'required'          => false,
						'description'       => __( 'Name of the website.', 'surerank' ),
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => static function( $value ) {
							return is_string( $value );
						},
					],
					'business_description' => [
						'type'              => 'string',
						'required'          => false,
						'description'       => __( 'Business description of the website.', 'surerank' ),
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => static function( $value ) {
							return is_string( $value );
						},
					],
					'website_owner_name'   => [
						'type'              => 'string',
						'required'          => false,
						'description'       => __( 'Name of the website owner.', 'surerank' ),
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => static function( $value ) {
							return is_string( $value );
						},
					],
					'website_owner_phone'  => [
						'type'              => 'string',
						'required'          => false,
						'description'       => __( 'Phone number of the website owner.', 'surerank' ),
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => static function( $value ) {
							return is_string( $value );
						},
					],
					'organization_type'    => [
						'type'              => 'string',
						'required'          => false,
						'description'       => __( 'Type of the organization.', 'surerank' ),
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => static function( $value ) {
							return is_string( $value );
						},
					],
					'about_page'           => [
						'type'              => 'integer',
						'required'          => false,
						'sanitize_callback' => 'absint',

					],
					'contact_page'         => [
						'type'              => 'integer',
						'required'          => false,
						'sanitize_callback' => 'absint',
					],
					'social_profiles'      => [
						'type'        => 'object',
						'required'    => false,
						'description' => __( 'Social profiles URLs.', 'surerank' ),
						'properties'  => array_fill_keys(
							self::get_social_profile_keys(),
							[
								'type'              => 'string',
								'sanitize_callback' => 'esc_url_raw',
								'validate_callback' => static function( $value ) {
									return filter_var( $value, FILTER_VALIDATE_URL ) !== false;
								},
							]
						),
					],
					'website_logo'         => [
						'type'     => 'string',
						'required' => false,
					],
				],
			]
		);

		register_rest_route(
			$this->get_api_namespace(),
			self::IMPROVE_DESCRIPTION,
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'improve_description' ],
				'permission_callback' => [ $this, 'validate_permission' ],
				'args'                => [
					'business_name'     => [
						'type'              => 'string',
						'required'          => false,
						'sanitize_callback' => 'sanitize_text_field',
					],
					'business_desc'     => [
						'type'              => 'string',
						'required'          => true,
						'sanitize_callback' => 'sanitize_textarea_field',
					],
					'business_category' => [
						'type'              => 'string',
						'required'          => false,
						'sanitize_callback' => 'sanitize_text_field',
					],
					'language'          => [
						'type'              => 'string',
						'required'          => false,
						'default'           => 'en',
						'sanitize_callback' => 'sanitize_text_field',
					],
				],
			]
		);
	}

	/**
	 * Update common onboarding data.
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Onboarding data.
	 * @return bool
	 */
	public static function update_common_onboarding_data( $data ) {
		if ( empty( $data ) ) {
			return false;
		}

		$instance = self::get_instance();

		$auth_data = get_option( 'surerank_auth', [] );

		$defaults = [
			'website_type'         => '',
			'website_name'         => '',
			'business_description' => Helper::get_saved_business_details( 'business_description' ),
			'website_owner_name'   => '',
			'organization_type'    => 'Organization',
			'website_owner_phone'  => '',
			'website_logo'         => '',
			'about_page'           => 0,
			'contact_page'         => 0,
			'social_profiles'      => [],
			'email'                => $auth_data['user_email'] ?? '',
		];

		$data = wp_parse_args(
			$data,
			$defaults
		);

		$settings = Settings::get();

		$instance->set_onboarding_data( $data, $settings );
		$updated_onboarding = $instance->process_onboarding_data( $data, $settings );
		$updated_settings   = Update::option( SURERANK_SETTINGS, $settings );

		if ( $updated_onboarding && $updated_settings ) {
			$instance->set_schemas_pages( $data, $settings );
			Update_Timestamp::timestamp_option();
			return true;
		}

		return false;
	}

	/**
	 * Save Website Details
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request<array<string, mixed>> $request Request object.
	 * @return void
	 */
	public function save_website_details( $request ) {
		$data = $request->get_params();

		if ( empty( $data ) ) {
			Send_Json::error( [ 'message' => __( 'Invalid data provided', 'surerank' ) ] );
		}

		if ( self::update_common_onboarding_data( $data ) ) {
			Send_Json::success( [ 'message' => __( 'Settings updated successfully', 'surerank' ) ] );
		}

		Update::option( 'surerank_onboarding_completed', true );

		Send_Json::error( [ 'message' => __( 'Failed to update settings', 'surerank' ) ] );
	}

	/**
	 * Improve business description using AI
	 *
	 * @since 1.6.2
	 * @param WP_REST_Request<array<string, mixed>> $request Request object.
	 * @return void
	 */
	public function improve_description( $request ) {
		$params = $request->get_params();

		$body = [
			'business_name'     => $params['business_name'] ?? '',
			'business_desc'     => $params['business_desc'] ?? '',
			'business_category' => $params['business_category'] ?? '',
			'language'          => $params['language'] ?? 'en',
		];

		$json_body = wp_json_encode( $body );

		if ( false === $json_body ) {
			Send_Json::error( [ 'message' => __( 'Failed to encode request data', 'surerank' ) ] );
		}

		$response = wp_safe_remote_post(
			'https://api.zipwp.com/api/v1/sites/suggest-description',
			[
				'headers' => [
					'Content-Type'     => 'application/json',
					'Accept'           => 'application/json, text/plain, */*',
					'X-Requested-With' => 'XMLHttpRequest',
				],
				'body'    => (string) $json_body,
				'timeout' => 30, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout
			]
		);

		if ( is_wp_error( $response ) ) {
			Send_Json::error(
				[
					'message' => __( 'Failed to improve description', 'surerank' ),
					'error'   => $response->get_error_message(),
				]
			);
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );

		if ( $response_code !== 200 ) {
			Send_Json::error(
				[
					'message' => __( 'Failed to improve description', 'surerank' ),
					'code'    => $response_code,
				]
			);
		}

		$data = json_decode( $response_body, true );

		if ( isset( $data['description'] ) ) {
			Send_Json::success(
				[
					'message'     => __( 'Description improved successfully', 'surerank' ),
					'description' => sanitize_textarea_field( $data['description'] ),
				]
			);
		}

		Send_Json::error( [ 'message' => __( 'Invalid response from AI service', 'surerank' ) ] );
	}

	/**
	 * Set Schemas Pages
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return void
	 */
	public function set_schemas_pages( $data, $settings ) {
		$this->set_about_page( $data, $settings );
		$this->set_contact_page( $data, $settings );
	}

	/**
	 * Set About Page
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return bool|int
	 */
	public function set_about_page( $data, &$settings ) {
		$about_page = $data['about_page'] ?? 0;

		if ( ! $about_page ) {
			return false;
		}

		$default_schemas                               = Settings::prepare_schemas( $settings, 'page', $about_page, true );
		$schema                                        = $this->find_schema_by_type( $settings, 'WebPage' );
		$default_schemas[ $schema ]['fields']['@type'] = 'AboutPage';
		$default_schemas[ $schema ]['type']            = 'AboutPage';
		$schemas                                       = [
			'schemas' => $default_schemas,
		];

		return Update::post_meta( $about_page, 'surerank_settings_schemas', $schemas );
	}

	/**
	 * Set Contact Page
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return bool|int
	 */
	public function set_contact_page( $data, &$settings ) {
		$contact_page = $data['contact_page'] ?? 0;

		if ( ! $contact_page ) {
			return false;
		}

		$default_schemas                               = Settings::prepare_schemas( $settings, 'page', $contact_page, true );
		$schema                                        = $this->find_schema_by_type( $settings, 'WebPage' );
		$default_schemas[ $schema ]['fields']['@type'] = 'ContactPage';
		$default_schemas[ $schema ]['type']            = 'ContactPage';
		$schemas                                       = [
			'schemas' => $default_schemas,
		];

		return Update::post_meta( $contact_page, 'surerank_settings_schemas', $schemas );
	}

	/**
	 * Save Onboarding Data
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @return bool
	 */
	public function save_onboarding_data( $data ) {
		return Update::option( 'surerank_settings_onboarding', $data );
	}

	/**
	 * Set Website Details
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return void
	 */
	public function set_website_details( $data, &$settings ) {
		$this->update_schema_field( $settings, 'Organization', 'name', $data['website_name'] );
		$this->update_schema_field( $settings, 'Organization', '@type', $data['organization_type'] );
		$this->update_schema_field( $settings, 'Organization', 'logo', $data['website_logo'] );
		$this->update_schema_field( $settings, 'Organization', 'telephone', $data['website_owner_phone'] );

		if ( ! empty( $data['email'] ) ) {
			$this->update_schema_field( $settings, 'Organization', 'email', $data['email'] );
		}

		$this->update_schema_field( $settings, 'Person', 'name', $data['website_owner_name'] );
		$this->update_schema_field( $settings, 'Person', 'image', $data['website_logo'] );
		$sanitized_name = sanitize_text_field( $data['website_name'] );
		update_option( 'blogname', $sanitized_name );
	}

	/**
	 * Update Schema Field
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $settings Settings.
	 * @param string               $schema_type Schema type.
	 * @param string               $field Field name.
	 * @param mixed                $value Field value.
	 * @return void
	 */
	public function update_schema_field( &$settings, $schema_type, $field, $value ) {

		$schema_id = $this->find_schema_by_type( $settings, $schema_type );
		if ( is_null( $schema_id ) ) {
			return; // Bail early if schema is not found.
		}

		if ( ! isset( $settings['schemas'][ $schema_id ]['fields'] ) ) {
			$settings['schemas'][ $schema_id ]['fields'] = [];
		}

		$settings['schemas'][ $schema_id ]['fields'][ $field ] = $value;
	}

	/**
	 * Find Schema by Type
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $settings Settings array.
	 * @param string               $type Schema type to find.
	 * @return string|null Schema ID if found, null otherwise.
	 */
	public function find_schema_by_type( &$settings, $type ) {
		if ( isset( $settings['schemas'] ) && is_array( $settings['schemas'] ) ) {
			foreach ( $settings['schemas'] as $schema_id => $schema ) {
				if ( isset( $schema['type'] ) && $schema['type'] === $type ) {
					return $schema_id;
				}
			}
		}
		return null;
	}

	/**
	 * Person or Organization
	 *
	 * @since 1.0.0
	 * @param string $website_type Website type.
	 * @return string
	 */
	public function person_or_organization( $website_type ) {
		return in_array( $website_type, [ 'blog', 'personal' ] ) ? 'person' : 'organization';
	}

	/**
	 * Get Social Profile Keys
	 *
	 * @since 1.0.0
	 * @return array<string, mixed>|array<int, array<string, mixed>>
	 */
	public static function get_social_profile_keys() {
		return array_column( self::social_profiles(), 'id' );
	}

	/**
	 * Set Social Profiles
	 *
	 * @since 1.0.0
	 * @return array<string, mixed>
	 */
	public static function social_profiles() {
		return apply_filters(
			'surerank_social_profiles',
			[
				[
					'label'             => 'Facebook',
					'id'                => 'facebook',
					'placeholder'       => 'https://www.facebook.com/my-page-url',
					'show_in_dashboard' => false,
				],
				[
					'label'             => 'X',
					'id'                => 'twitter',
					'placeholder'       => 'https://x.com/myaccount',
					'show_in_dashboard' => false,
				],
				[
					'label'       => 'Instagram',
					'id'          => 'instagram',
					'placeholder' => 'https://www.instagram.com/my-page-url',
				],
				[
					'label'       => 'YouTube',
					'id'          => 'youtube',
					'placeholder' => 'https://www.youtube.com/my-channel-url',
				],
				[
					'label'       => 'LinkedIn',
					'id'          => 'linkedin',
					'placeholder' => 'https://www.linkedin.com/company/my-company',
				],
				[
					'label'       => 'TikTok',
					'id'          => 'tiktok',
					'placeholder' => 'https://www.tiktok.com/@username',
				],
				[
					'label'       => 'Pinterest',
					'id'          => 'pinterest',
					'placeholder' => 'https://www.pinterest.com/my-page-url',
				],
				[
					'label'       => 'WhatsApp',
					'id'          => 'whatsapp',
					'placeholder' => 'https://wa.me/number',
				],
				[
					'label'       => 'Telegram',
					'id'          => 'telegram',
					'placeholder' => 'https://t.me/username',
				],
				[
					'label'       => 'Yelp',
					'id'          => 'yelp',
					'placeholder' => 'https://www.yelp.com/biz/business-name-location',
				],
				[
					'label'       => 'BlueSky',
					'id'          => 'bluesky',
					'placeholder' => 'https://bsky.app/profile/username',
				],
			]
		);
	}

	/**
	 * Set Social Schema
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return void
	 */
	public function set_social_schema( $data, &$settings ) {
		$same_as_urls = array_filter(
			$data['social_profiles'] ?? [],
			static function( $url ) {
				return ! empty( $url );
			}
		);
		$this->update_schema_field( $settings, 'Organization', 'sameAs', $same_as_urls );
	}

	/**
	 * Set Onboarding Data
	 *
	 * We need to store the facebook page url and twitter profile username in the settings array as per the new requirement.
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return void
	 */
	private function set_onboarding_data( $data, &$settings ) {
		$settings['facebook_page_url']        = $data['social_profiles']['facebook'] ?? '';
		$settings['twitter_profile_username'] = $data['social_profiles']['twitter'] ?? '';
	}

	/**
	 * Process Onboarding Data
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return bool
	 */
	private function process_onboarding_data( $data, &$settings ) {
		$this->set_website_details( $data, $settings );
		$this->set_social_schema( $data, $settings );
		$this->set_social_profiles( $data, $settings );
		$this->set_person_or_organization( $data, $settings );

		return $this->save_onboarding_data( $data );
	}

	/**
	 * Set Person or Organization.
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return void
	 */
	private function set_person_or_organization( &$data, &$settings ) {
		$type                           = $this->person_or_organization( $data['website_type'] );
		$data['person_or_organization'] = $type;

		/**
		 * Depending on the type, we need to set the publisher to the correct schema.
		 * We need to set the publisher for WebPage and Article.
		 */
		$schema = $type === 'organization' ? '%schemas.organization%' : '%schemas.person%';

		foreach ( [ 'WebPage', 'Article' ] as $context ) {
			$this->update_schema_field( $settings, $context, 'publisher', $schema );
		}
	}

	/**
	 * Set Social Profiles
	 *
	 * @since 1.0.0
	 * @param array<string, mixed> $data Data.
	 * @param array<string, mixed> $settings Settings.
	 * @return void
	 */
	private function set_social_profiles( &$data, &$settings ) {
		$settings['social_profiles'] = $data['social_profiles'];
		unset( $data['social_profiles'] );
	}

}