__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

[email protected]: ~ $
<?php

/**
 * Templately AI Content Importer
 *
 * @package Templately
 * @since 1.0.0
 */

namespace Templately\API;

use Error;
use Exception;
use Templately\Utils\Helper;
use WP_REST_Request;
use WP_Error;
use Templately\Core\Importer\Utils\Utils;
use Templately\Core\Importer\Utils\AIUtils;
use Templately\Core\Importer\Parsers\WXR_Parser;

class AIContent extends API {
	private $endpoint  = 'ai-content';
	private $dev_mode  = false;


	/**
	 * AIContent constructor.
	 *
	 * @param string $file    File path.
	 * @param array  $settings Settings.
	 */
	public function __construct() {

		parent::__construct();

	}

	public function _permission_check(WP_REST_Request $request) {
		$this->request = $request;
		$this->api_key = $this->utils('options')->get( 'api_key' );
		$process_id    = $this->get_param('process_id');

		$_route = $request->get_route();
		if ('/templately/v1/ai-content/ai-update' === $_route || '/templately/v1/ai-content/ai-update-preview' === $_route) {
			if (empty($process_id)) {
				return $this->error('invalid_id', __('Invalid ID.', 'templately'), 'calculate_credit', 400);
			}

			// Check AI process data using API key-based storage
			$ai_process_data = AIUtils::get_ai_process_data();
			if (is_array($ai_process_data) && !empty($ai_process_data[$process_id])) {
				return true;
			}

			return (bool) AIUtils::get_matched_session_data($process_id);
		}

		// // Allow access to attachments endpoint
		// if ('/templately/v1/ai-content/attachments' === $_route) {
		// 	return true;
		// }

		return parent::permission_check($request);
	}


	public function register_routes() {
		// $this->get( $this->endpoint . '/calculate-credit', [ $this, 'calculate_credit' ] );
		$this->post($this->endpoint . '/modify-content', [$this, 'modify_content']);
		$this->post($this->endpoint . '/ai-update', [$this, 'ai_update']);
		$this->post($this->endpoint . '/ai-update-preview', [$this, 'ai_update_preview']);
		$this->get($this->endpoint . '/attachments', [$this, 'get_attachments'], [
			'type' => [
				'default' => 'pack',
				'required' => false,
				'sanitize_callback' => 'sanitize_text_field',
			],
			'id' => [
				'required' => false,
				'sanitize_callback' => 'sanitize_text_field',
			],
			'pack_id' => [
				'required' => false,
				'sanitize_callback' => 'sanitize_text_field',
			],
		]);
		$this->get($this->endpoint . '/images', [$this, 'search_images'], [
			'query' => [
				'required' => false,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function($param, $request, $key) {
					return is_string($param) && strlen($param) <= 255;
				},
			],
			'orientation' => [
				'required' => false,
				'default' => 'all',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function($param, $request, $key) {
					$allowed_orientations = ['all', 'landscape', 'portrait', 'square'];
					return in_array($param, $allowed_orientations, true);
				},
			],
			'size' => [
				'required' => false,
				'default' => 'medium',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function($param, $request, $key) {
					$allowed_sizes = ['small', 'medium', 'large'];
					return in_array($param, $allowed_sizes, true);
				},
			],
			'color' => [
				'required' => false,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function($param, $request, $key) {
					return is_string($param) && strlen($param) <= 50;
				},
			],
			'page' => [
				'required' => false,
				'default' => 1,
				'sanitize_callback' => 'absint',
				'validate_callback' => function($param, $request, $key) {
					return is_numeric($param) && $param > 0 && $param <= 1000;
				},
			],
			'per_page' => [
				'required' => false,
				'default' => 20,
				'sanitize_callback' => 'absint',
				'validate_callback' => function($param, $request, $key) {
					return is_numeric($param) && $param > 0 && $param <= 100;
				},
			],
		]);
		// die(rest_url( 'templately/v1/ai-content/ai-update' ));
	}

	public function calculate_credit() {
		$pack_id = $this->get_param('pack_id');

		return [
			'status' => 'success',
			'data'   => [
				'availableCredit' => 100,
			],
		];

		if (empty($pack_id)) {
			return $this->error('invalid_id', __('Invalid ID.', 'templately'), 'calculate_credit', 400);
		}

		$extra_headers = [
			'Accept' => 'application/json',
		];
		$response = Helper::make_api_get_request("v2/ai/calculate-credit/pack/$pack_id", [], $extra_headers, 30);


		// return $response;
		if (is_wp_error($response)) {
			return $this->error('request_failed', __('Request failed.', 'templately'), 'calculate_credit', 500, $response);
		}

		$body = wp_remote_retrieve_body($response);
		$data = json_decode($body, true);
		// error status is ok
		if (! is_array($data) || ! isset($data['status'])) {
			return $this->error('invalid_response', __('Invalid response.', 'templately'), 'calculate_credit', 500);
		}


		return $data;
	}

	public function modify_content() {
		add_filter('wp_redirect', '__return_false', 999);
		set_time_limit(3 * MINUTE_IN_SECONDS);
		ini_set('max_execution_time', 3 * MINUTE_IN_SECONDS);

		$pack_id             = $this->get_param('pack_id');
		$isBusinessNichesNew = $this->get_param('isBusinessNichesNew', false);
		$ai_page_ids         = $this->get_param('ai_page_ids', [], null);
		$content_ids         = $this->get_param('content_ids', [], null);
		$session_id          = $this->get_param('session_id');                  // Add session_id parameter
		$preview_pages       = $this->get_param('preview_pages', [], null);
		$image_replace       = $this->get_param('imageReplace', [], null);
		$platform            = $this->get_param('platform');

		// ai content fields
		$name            = $this->get_param('name');
		$category        = $this->get_param('category');
		$description     = $this->get_param('description');
		$email           = $this->get_param('email');
		$contactNumber   = $this->get_param('contactNumber');
		$businessAddress = $this->get_param('businessAddress');
		$openingHour     = $this->get_param('openingHour');

		if (empty($pack_id)) {
			return $this->error('invalid_id', __('Invalid ID.', 'templately'), 'modify_content', 400);
		}
		if (empty($category)) {
			return $this->error('invalid_prompt', __('Invalid prompt.', 'templately'), 'modify_content', 400);
		}
		if (empty($content_ids) && empty($preview_pages)) {
			return $this->error('invalid_content_ids', __('Invalid content ids.', 'templately'), 'modify_content', 400);
		}
		if (empty($platform)) {
			return $this->error('invalid_platform', __('Invalid platform.', 'templately'), 'modify_content', 400);
		}


		// $response    = get_transient( '__templately_ai_process_id' );

		// if(empty($response)) {
		$extra_headers = [
			'Accept'                  => 'application/json',
			'x-templately-session-id' => $session_id,
		];
		$body_data = [
			'business_name'   => $name,
			'business_niches' => $category,
			'prompt'          => $description,
			'email'           => $email,
			'phone'           => $contactNumber,
			'address'         => $businessAddress,
			'openingHour'     => $openingHour,
			'pack_id'         => $pack_id,
			'content_ids'     => $content_ids,
			'platform'        => $platform,
			'preview_pages'   => $preview_pages,
			'callback'        => defined('TEMPLATELY_CALLBACK') ? TEMPLATELY_CALLBACK . '/wp-json/templately/v1/ai-content/ai-update' : rest_url('templately/v1/ai-content/ai-update'),
		];
		$response = Helper::make_api_post_request('v2/ai/modify-content/pack', $body_data, $extra_headers, 15 * MINUTE_IN_SECONDS);

		// 	set_transient( '__templately_ai_process_id', $response, 60 * 60 * 24 * 30 );
		// }

		$bk_ai_business_niches = get_option('templately_ai_business_niches', []);
		if (!empty($business_niches) && $isBusinessNichesNew && ! in_array($business_niches, $bk_ai_business_niches)) {
			$bk_ai_business_niches[] = $business_niches;
			update_option('templately_ai_business_niches', $bk_ai_business_niches, false);
		}

		// return $response;
		if (is_wp_error($response)) {
			error_log(print_r($response, true));
			return $this->error('request_failed', __('Request failed.', 'templately'), 'modify_content', 500, $response->additional_data);
		}

		$body = wp_remote_retrieve_body($response);
		$data = json_decode($body, true);
		// error status is ok, if status is error then return as is
		if (! is_array($data) || ! isset($data['status'])) {
			return $this->error('invalid_response', __('Invalid response.', 'templately'), 'modify_content', 500, $data);
		}

		// "{"status":"success","message":"The content is being generated in the queue","process_id":"01JRQQD39GNWTNF18EWF8YH0BG-271838-pack-408"}"
		if (isset($data['status']) && $data['status'] === 'success' && isset($data['process_id'])) {
			$process_id = $data['process_id'];

			// // Save templates to files if available using the common function
			// if (!empty($data['templates']) && is_array($data['templates'])) {
			// 	foreach ($data['templates'] as $content_id => $template_data) {
			// 		// Decode template if it's base64 encoded
			// 		if (! empty($template_data) && base64_decode($template_data, true) !== false) {
			// 			$data['templates'][$content_id] = base64_decode($template_data);
			// 		}

			// 		if (!empty($template_data)) {
			// 			AIUtils::save_template_to_file(
			// 				$process_id,
			// 				$content_id,
			// 				$template_data,
			// 				$ai_page_ids,
			// 				true, // Always use preview mode for AI content workflow
			// 				isset($template_data['isSkipped']) ? $template_data['isSkipped'] : false
			// 			);
			// 		}
			// 	}
			// }

			$user = $this->utils('options')->get('user');

			$ai_process_data[$process_id] = [
				'name'            => $name,
				'category'        => $category,
				'description'     => $description,
				'email'           => $email,
				'contactNumber'   => $contactNumber,
				'businessAddress' => $businessAddress,
				'openingHour'     => $openingHour,
				'process_id'      => $process_id,
				'pack_id'         => $pack_id,
				'ai_page_ids'     => $ai_page_ids,
				'ai_preview_ids'  => $preview_pages,
				'content_ids'     => $content_ids,
				'platform'        => $platform,
				'api_key'         => $this->api_key,
				'user_id'         => isset($user['id']) ? $user['id'] : null,
				'session_id'      => $session_id,        // Store session_id for coordination
				'imageReplace'    => $image_replace,    // Store session_id for coordination
			];

			// Update using API key-based storage with automatic count-based cleanup
			AIUtils::update_ai_process_data($ai_process_data);

			return [
				'status'     => 'success',
				'message'    => __('The content is being generated in the queue', 'templately'),
				'process_id' => $process_id,
				'templates'  => !empty($data['templates']) ? $data['templates'] : null,
				'is_local_site'  => !empty($data['is_local_site']) ? $data['is_local_site'] : null,
			];
		}

		return $data;
	}

	public function ai_update() {
		add_filter('wp_redirect', '__return_false', 999);

		$template    = $this->get_param('template');
		$process_id  = $this->get_param('process_id');
		$template_id = $this->get_param('template_id');
		$content_id  = $this->get_param('content_id');
		$type        = $this->get_param('type');
		$isSkipped   = $this->get_param('isSkipped', false);
		$credit_cost = $this->request->get_param('credit_cost');

		error_log('process_id: ' . $process_id);

		// Handle credit cost updates separately
		if ($this->request->has_param('credit_cost')) {
			$processed_pages = get_option("templately_ai_processed_pages", []);
			$processed_pages[$process_id] = $processed_pages[$process_id] ?? [];
			$processed_pages[$process_id]['credit_cost'] = $credit_cost;
			update_option("templately_ai_processed_pages", $processed_pages, false);

			return [
				'status' => 'success',
				'data'   => [
					'process_id' => $process_id,
					'credit_cost' => $credit_cost,
				],
			];
		}

		// Always use preview mode for AI content workflow
		// Validate and get process data using centralized method
		$process_data = AIUtils::validate_and_get_process_data($process_id);
		if (is_wp_error($process_data)) {
			return $process_data;
		}

		$session_id = $process_data['session_id'];
		$ai_page_ids = $process_data['ai_page_ids'];

		// Use the common helper function to save the template
		$result = AIUtils::save_template_to_file(
			$process_id,
			$session_id,
			$content_id,
			$template,
			$ai_page_ids,
			$isSkipped
		);

		if(is_wp_error($result)){
			return $result;
		}

		// Return the result from the helper function
		if (isset($result['status']) && $result['status'] === 'success') {
			return $result;
		}

		// Return error if the helper function failed
		return $result;
	}

	public function ai_update_preview() {
		add_filter('wp_redirect', '__return_false', 999);

		$template   = $this->get_param('templates');         // Now expects an array with content_id as keys
		$process_id = $this->get_param('process_id');
		$isSkipped  = $this->get_param('isSkipped', false);
		$error      = $this->get_param('error', null);

		error_log('process_id: ' . $process_id);

		if (!empty($isSkipped) || !empty($error)) {
			// Update AI process data with error using API key-based storage
			$ai_process_data = AIUtils::get_ai_process_data();
			if (isset($ai_process_data[$process_id])) {
				$ai_process_data[$process_id]['preview_error'] = $error;
				AIUtils::update_ai_process_data($ai_process_data);
			}
			wp_send_json_error([
				'status' => 'error',
				'message' => $error,
			]);
		}

		// Validate template parameter is an array
		if (!is_array($template) || empty($template)) {
			return $this->error('invalid_template', __('Template must be a non-empty array with content_id as keys.', 'templately'), 'ai-content/ai-update-preview', 400);
		}

		// Always use preview mode for AI content workflow
		// Validate and get process data using centralized method
		$process_data = AIUtils::validate_and_get_process_data($process_id);
		if (is_wp_error($process_data)) {
			return $process_data;
		}

		$session_id = $process_data['session_id'];
		$ai_page_ids = $process_data['ai_page_ids'];
		$results = [];
		$success_count = 0;
		$error_count = 0;

		// Process each content_id/template pair
		foreach ($template as $content_id => $template_data) {
			// Use the common helper function to save the template (always preview mode)
			$result = AIUtils::save_template_to_file(
				$process_id,
				$session_id,
				$content_id,
				$template_data,
				$ai_page_ids,
				$isSkipped
			);

			$results[$content_id] = $result;

			// Track success/error counts
			if (isset($result['status']) && $result['status'] === 'success') {
				$success_count++;
			} else {
				$error_count++;
			}
		}

		// Return consolidated response
		$overall_status = $error_count === 0 ? 'success' : ($success_count === 0 ? 'error' : 'partial_success');

		// Note: No cleanup needed with API key-based storage and count-based management

		return [
			'status' => $overall_status,
			'message' => sprintf(
				__('Processed %d templates: %d successful, %d failed.', 'templately'),
				count($template),
				$success_count,
				$error_count
			),
		];
	}

	/**
	 * Get attachments from API endpoint
	 *
	 * @return array
	 */
	public function get_attachments() {
		// Get parameters from request
		$type = $this->get_param('type', 'pack');
		$id = $this->get_param('pack_id');

		// Require ID parameter - return error if not provided
		if (empty($id)) {
			return $this->error('missing_id', __('Pack ID or ID parameter is required.', 'templately'), 'get_attachments', 400);
		}

		try {
			// Construct API endpoint URL
			$api_endpoint = "get-xml-attachment/{$type}/{$id}";

			// Make API call
			$extra_headers = [
				'Accept' => 'application/xml, text/xml',
			];
			$response = Helper::make_api_get_request("v2/$api_endpoint", [], $extra_headers, 30);

			// Check for HTTP errors
			if (is_wp_error($response)) {
				return $this->error('api_request_failed', __('Failed to fetch attachments from API.', 'templately'), 'get_attachments', 500, $response->get_error_message());
			}

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

			if ($response_code !== 200) {
				// check if $xml_content contains valid json
				// ex. '{"status":"error","message":"Attachment XML file not found in pack archive."}'
				$error_data = @json_decode($xml_content, true);
				if(is_array($error_data) && isset($error_data['status']) && $error_data['status'] === 'error' && !empty($error_data['message'])){
					return $this->error('api_http_error', $error_data['message'], 'get_attachments', $response_code);
				}
				return $this->error('api_http_error', sprintf(__('API returned HTTP %d error.', 'templately'), $response_code), 'get_attachments', $response_code);
			}

			// Validate we have XML content
			if (empty($xml_content)) {
				return $this->error('no_xml_content', __('No XML content found in API response.', 'templately'), 'get_attachments', 404);
			}

			// Parse the XML content from API response
			$parsed_data = $this->parse_xml_content($xml_content);

			if (is_wp_error($parsed_data)) {
				return $this->error('xml_parse_error', __('Failed to parse XML content.', 'templately'), 'get_attachments', 500, $parsed_data->get_error_message());
			}

			// Extract attachments from parsed data
			$attachments = $this->extract_attachments_from_parsed_data($parsed_data);

			return [
				'status' => 'success',
				'data' => $attachments,
				'message' => sprintf(__('Found %d attachments.', 'templately'), count($attachments)),
			];

		} catch (Exception $e) {
			return $this->error('exception', __('An unexpected error occurred while fetching attachments.', 'templately'), 'get_attachments', 500, $e->getMessage());
		}
	}



	/**
	 * Parse XML content string using WXR Parser
	 *
	 * @param string $xml_content XML content string
	 * @return array|WP_Error Parsed data or error
	 */
	private function parse_xml_content($xml_content) {
		// Ensure WordPress filesystem functions are available
		if (!function_exists('wp_tempnam')) {
			require_once(ABSPATH . 'wp-admin/includes/file.php');
		}

		// Create a temporary file to store XML content
		$temp_file = wp_tempnam('templately_attachments');
		if (!$temp_file) {
			return new WP_Error('temp_file_failed', __('Failed to create temporary file.', 'templately'));
		}

		// Write XML content to temporary file
		$bytes_written = file_put_contents($temp_file, $xml_content);
		if ($bytes_written === false) {
			unlink($temp_file);
			return new WP_Error('write_failed', __('Failed to write XML content to temporary file.', 'templately'));
		}

		try {
			// Initialize WXR Parser
			$parser = new WXR_Parser();

			// Parse the temporary XML file
			$parsed_data = $parser->parse($temp_file);

			// Clean up temporary file
			unlink($temp_file);

			return $parsed_data;

		} catch (Exception $e) {
			// Clean up temporary file on exception
			if (file_exists($temp_file)) {
				unlink($temp_file);
			}
			return new WP_Error('parse_exception', $e->getMessage());
		}
	}

	/**
	 * Extract attachments from parsed WXR data
	 *
	 * @param array $parsed_data Parsed WXR data
	 * @return array Array of attachment data
	 */
	private function extract_attachments_from_parsed_data($parsed_data) {
		$attachments = [];

		if (isset($parsed_data['posts']) && is_array($parsed_data['posts'])) {
			foreach ($parsed_data['posts'] as $post) {
				// Check if this is an attachment
				if (isset($post['post_type']) && $post['post_type'] === 'attachment') {
					$attachment = [
						'id' => isset($post['post_id']) ? (int) $post['post_id'] : 0,
						'url' => isset($post['attachment_url']) ? (string) $post['attachment_url'] : '',
						'title' => isset($post['post_title']) ? (string) $post['post_title'] : '',
						'type' => isset($post['attachment_type']) ? (string) $post['attachment_type'] : '',
					];

					// Extract metadata including dimensions and medium URL
					$metadata = $this->extract_medium_size_url($post, $attachment['url']);

					// Filter out small images (width or height <= 150px) to ignore small icons
					if ($metadata && isset($metadata['width']) && isset($metadata['height'])) {
						if ($metadata['width'] < 150 || $metadata['height'] < 150) {
							continue; // Skip small images/icons
						}

						// Add dimensions to attachment data
						$attachment['width'] = $metadata['width'];
						$attachment['height'] = $metadata['height'];

						// Add medium URL if available
						if (isset($metadata['medium_url'])) {
							$attachment['medium_url'] = $metadata['medium_url'];
						}
					} else {
						// Skip attachments without metadata or dimensions
						continue;
					}

					// Only add if we have the required data
					if ($attachment['id'] && $attachment['url'] && $attachment['title']) {
						$attachments[] = $attachment;
					}
				}
			}
		}

		return $attachments;
	}

	/**
	 * Extract medium size URL from attachment metadata and get image dimensions
	 *
	 * @param array $post Post data from WXR parser
	 * @param string $original_url Original attachment URL
	 * @return array|null Array with medium_url and dimensions if found, null otherwise
	 */
	private function extract_medium_size_url($post, $original_url) {
		if (!isset($post['postmeta']) || !is_array($post['postmeta'])) {
			return null;
		}

		foreach ($post['postmeta'] as $meta) {
			if (!isset($meta['key']) || !isset($meta['value'])) {
				continue;
			}

			// Only check _wp_attachment_metadata
			if ($meta['key'] === '_wp_attachment_metadata') {
				$attachment_metadata = @unserialize($meta['value']);
				if (is_array($attachment_metadata)) {
					$result = [];

					// Get original image dimensions
					$width = isset($attachment_metadata['width']) ? (int) $attachment_metadata['width'] : 0;
					$height = isset($attachment_metadata['height']) ? (int) $attachment_metadata['height'] : 0;

					$result['width'] = $width;
					$result['height'] = $height;

					// Check if medium size exists
					if (isset($attachment_metadata['sizes']['medium']['file'])) {
						// Construct medium URL from original URL and medium filename
						$medium_filename = $attachment_metadata['sizes']['medium']['file'];
						$original_path = dirname(parse_url($original_url, PHP_URL_PATH));
						$base_url = str_replace(parse_url($original_url, PHP_URL_PATH), '', $original_url);
						$result['medium_url'] = $base_url . $original_path . '/' . $medium_filename;
					}

					return $result;
				}
			}
		}

		return null;
	}



	/**
	 * Search images endpoint
	 *
	 * @param WP_REST_Request $request
	 * @return WP_REST_Response|WP_Error
	 */
	public function search_images(WP_REST_Request $request) {
		// Get and sanitize parameters
		$query = $this->get_param('query', '');
		$orientation = $this->get_param('orientation', 'all');
		$size = $this->get_param('size', 'medium');
		$color = $this->get_param('color', '');
		$page = $this->get_param('page', 1, 'absint');
		$per_page = $this->get_param('per_page', 20, 'absint');

		// Validate required query parameter
		if (empty($query)) {
			return $this->error(
				'missing_query',
				__('Search query is required.', 'templately'),
				'search_images',
				400
			);
		}

		// Prepare API request parameters
		$api_params = [
			'query' => urlencode($query),
			'page' => $page,
			'per_page' => $per_page,
		];

		// Add optional parameters if provided
		if ($orientation !== 'all') {
			$api_params['orientation'] = $orientation;
		}

		if (!empty($size)) {
			$api_params['size'] = $size;
		}

		if (!empty($color)) {
			$api_params['color'] = $color;
		}

		// Make API request to external image service
		$extra_headers = [
			'Content-Type' => 'application/json',
		];

		$response = Helper::make_api_get_request('v2/images', $api_params, $extra_headers, 30);

		// Handle API response errors
		if (is_wp_error($response)) {
			return $this->error(
				'api_request_failed',
				__('Failed to fetch images from external service.', 'templately'),
				'search_images',
				500
			);
		}

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

		if ($response_code !== 200) {
			return $this->error(
				'api_response_error',
				sprintf(__('External API returned error code: %d', 'templately'), $response_code),
				'search_images',
				$response_code
			);
		}

		// Parse and validate response
		$data = json_decode($response_body, true);
		if (json_last_error() !== JSON_ERROR_NONE) {
			return $this->error(
				'invalid_response',
				__('Invalid response from external service.', 'templately'),
				'search_images',
				500
			);
		}

		// Check if the response has the expected structure and success status
		if (!isset($data['status']) || $data['status'] !== 'success') {
			return $this->error(
				'api_response_error',
				__('External API returned an error status.', 'templately'),
				'search_images',
				500
			);
		}

		// Extract nested data from the response
		$response_data = $data['data'] ?? [];
		$images = $response_data['images'] ?? [];
		$total_results = $response_data['total_results'] ?? 0;
		$current_page = $response_data['page'] ?? $page;
		$per_page_count = $response_data['per_page'] ?? $per_page;

		// Return successful response with properly mapped data
		return $this->success([
			'images' => $images,
			'total' => $total_results,
			'page' => $current_page,
			'per_page' => $per_page_count,
			'total_pages' => $total_results > 0 ? ceil($total_results / $per_page_count) : 0,
		]);
	}
}

Filemanager

Name Type Size Permission Actions
AIContent.php File 25.11 KB 0640
API.php File 6.33 KB 0640
Categories.php File 1.06 KB 0640
Conditions.php File 3.87 KB 0640
Dependencies.php File 12.41 KB 0640
FullSiteImport.php File 252 B 0640
Import.php File 6.81 KB 0640
Items.php File 14.25 KB 0640
Login.php File 8.08 KB 0640
MyClouds.php File 7.71 KB 0640
Profile.php File 5.42 KB 0640
SavedTemplates.php File 1.73 KB 0640
SignUp.php File 2.64 KB 0640
Tags.php File 866 B 0640
TemplateTypes.php File 1.85 KB 0640
ThemeBuilderApi.php File 1.82 KB 0640
WorkSpaces.php File 9.79 KB 0640
Filemanager