| 1 |
<?php |
| 2 |
|
| 3 |
namespace Templately\Modules\FullSiteImport\Ajax; |
| 4 |
|
| 5 |
use Elementor\Plugin; |
| 6 |
use Exception; |
| 7 |
use Templately\Modules\FullSiteImport\Utils\Utils; |
| 8 |
use Templately\Modules\FullSiteImport\Utils\SessionData; |
| 9 |
use Templately\Modules\FullSiteImport\Utils\AIUtils; |
| 10 |
use Templately\Modules\ElementorKitSettings\Utils\ElementorSettingsMerger; |
| 11 |
use Templately\Utils\Helper; |
| 12 |
use Templately\Utils\Response\ResponseNormalizer; |
| 13 |
use Templately\Utils\Response\AjaxResponder; |
| 14 |
use Templately\Utils\Response\ErrorCode; |
| 15 |
|
| 16 |
/** |
| 17 |
* AssetController — independent AJAX endpoint handler extracted from FullSiteImport. |
| 18 |
* Registered through FullSiteImport's nonce/capability wrapper (action names unchanged). |
| 19 |
*/ |
| 20 |
class AssetController { |
| 21 |
|
| 22 |
public function google_font() { |
| 23 |
$result = get_transient('templately-google-fonts'); |
| 24 |
|
| 25 |
if (false == $result) { |
| 26 |
$response = Helper::make_api_get_request('v2/google-font', [], [], 30); |
| 27 |
|
| 28 |
// 043 FR-003 — one normalizer classifies transport failures and error |
| 29 |
// bodies alike, replacing `extract_error_from_response()`. That helper |
| 30 |
// returned `mixed` (a string, or the whole decoded upstream body), so a |
| 31 |
// framework stack trace could end up rendered as the user's error text. |
| 32 |
// The wire shape here is unchanged; only the message source moved. |
| 33 |
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) != 200) { |
| 34 |
$error = ResponseNormalizer::normalize($response)->error(); |
| 35 |
AjaxResponder::error($error); |
| 36 |
return; |
| 37 |
} |
| 38 |
|
| 39 |
$body = wp_remote_retrieve_body($response); |
| 40 |
$data = json_decode($body, true); |
| 41 |
|
| 42 |
if (!isset($data['status']) || $data['status'] !== 'success') { |
| 43 |
AjaxResponder::error(ErrorCode::INVALID_REQUEST, 'API response indicates failure.'); |
| 44 |
return; |
| 45 |
} |
| 46 |
|
| 47 |
if (!isset($data['data'])) { |
| 48 |
AjaxResponder::error(ErrorCode::EMPTY_RESPONSE, 'API response missing data.'); |
| 49 |
return; |
| 50 |
} |
| 51 |
|
| 52 |
$result = $data['data']; |
| 53 |
// The Google Fonts catalog is near-static (additive, a handful of new fonts a |
| 54 |
// month), and a stale list is harmless — a newly published font just appears |
| 55 |
// late. A month-long cache spares the upstream round-trip for every settings |
| 56 |
// Typography / FSI customizer / fsi-kit mount that shares this transient. |
| 57 |
set_transient('templately-google-fonts', $result, MONTH_IN_SECONDS); |
| 58 |
} |
| 59 |
|
| 60 |
AjaxResponder::success($result); |
| 61 |
} |
| 62 |
/** |
| 63 |
* AJAX handler: merge global kit settings into template content. |
| 64 |
* |
| 65 |
* Accepts (via POST JSON body): |
| 66 |
* platform - 'elementor' | 'gutenberg' |
| 67 |
* template - the template data array returned by /v1/insert |
| 68 |
* settings - the kit settings array from import_info |
| 69 |
* |
| 70 |
* Returns the template data with globals resolved, or an error. |
| 71 |
* The JS side should fall back to the unmerged template on any error. |
| 72 |
*/ |
| 73 |
public function merge_settings_with_template() { |
| 74 |
$body = file_get_contents( 'php://input' ); |
| 75 |
$data = json_decode( $body, true ); |
| 76 |
|
| 77 |
$platform = isset( $data['platform'] ) ? sanitize_text_field( $data['platform'] ) : 'elementor'; |
| 78 |
$template = $data['template'] ?? null; |
| 79 |
$settings = $data['settings'] ?? null; |
| 80 |
|
| 81 |
if ( empty( $template ) || ! is_array( $template ) ) { |
| 82 |
AjaxResponder::error( ErrorCode::INVALID_REQUEST, 'Invalid template data.' ); |
| 83 |
return; |
| 84 |
return; |
| 85 |
} |
| 86 |
|
| 87 |
if ( empty( $settings ) || ! is_array( $settings ) ) { |
| 88 |
// No settings to merge – return template unchanged. |
| 89 |
AjaxResponder::success( $template ); |
| 90 |
return; |
| 91 |
} |
| 92 |
|
| 93 |
try { |
| 94 |
if ( $platform === 'elementor' ) { |
| 95 |
if ( ! empty( $template['content'] ) && is_array( $template['content'] ) ) { |
| 96 |
$template['content'] = ElementorSettingsMerger::merge( $template['content'], $settings ); |
| 97 |
} |
| 98 |
} elseif ( $platform === 'gutenberg' ) { |
| 99 |
if ( ! empty( $template['content'] ) && is_string( $template['content'] ) ) { |
| 100 |
// Platform filter seam (spec 025/033): gutenberg-integration hooks |
| 101 |
// this to run GutenbergSettingsMerger::merge without FSI importing it. |
| 102 |
$template['content'] = apply_filters( 'templately_fsi_merge_settings', $template['content'], $settings ); |
| 103 |
} |
| 104 |
} |
| 105 |
|
| 106 |
AjaxResponder::success( $template ); |
| 107 |
} catch ( \Exception $e ) { |
| 108 |
Helper::log( 'merge_settings_with_template error: ' . $e->getMessage() ); |
| 109 |
// Return error so JS can fall back to unmerged template. |
| 110 |
AjaxResponder::error( ErrorCode::SERVER_ERROR, 'Settings merge failed.' ); |
| 111 |
return; |
| 112 |
} |
| 113 |
} |
| 114 |
} |
| 115 |
|