PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.8.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.8.0
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / full-site-import / Abilities / StartFullSiteImportAbility.php

StartFullSiteImportAbility.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.8.0, at modules/full-site-import/Abilities/StartFullSiteImportAbility.php

164 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * `templately/start-full-site-import` — begin a Full Site Import and return a
4 * durable handle immediately (spec 042 US1, FR-001/007/008/013/015).
5 *
6 * Fast by design: this only creates the FSI session (loopback
7 * `templately_pack_import_settings`) and returns the `session_id` as the
8 * handle. The bounded-but-slower pack download and the lengthy content import
9 * happen across subsequent `full-site-import-status` slices (poll-driven,
10 * research.md §3) so `start` never blocks on a download.
11 *
12 * Reuses 041 infra verbatim: `ImportRateLimiter`, `DependencyGateResolver`
13 * (its `resolve_item` already covers the `packs` collection). Entitlement is
14 * enforced by the backend during the download slice and surfaced by the status
15 * ability with a purchase URL (ProGateResolver) — no site content is imported
16 * for an unentitled pack.
17 *
18 * @package Templately\Modules\FullSiteImport\Abilities
19 */
20
21 namespace Templately\Modules\FullSiteImport\Abilities;
22
23 use Templately\Modules\McpCore\Registry\ToolDescriptor;
24 use Templately\Modules\McpCore\Support\AjaxLoopbackDispatcher;
25 use Templately\Modules\McpCore\Support\DependencyGateResolver;
26 use Templately\Modules\FullSiteImport\Abilities\Support\FsiActiveImportGuard;
27 use Templately\Modules\McpCore\Support\ImportRateLimiter;
28 use Templately\Modules\McpCore\Support\Permissions;
29 use Templately\Utils\Options;
30 use WP_Error;
31
32 class StartFullSiteImportAbility {
33
34 const ID = 'templately/start-full-site-import';
35
36 public static function descriptor(): array {
37 return [
38 'id' => self::ID,
39 'label' => __( 'Start Templately Full Site Import', 'templately' ),
40 'description' => __( 'Begin importing a full-site Templately pack and return a tracking handle immediately — the import does NOT run on its own. Drive it to completion by repeatedly calling templately/full-site-import-status with advance=true until its status is "complete" or "failed"; the import only progresses while you poll. This poll loop can be automated by a background/monitor process that watches for the terminal status.', 'templately' ),
41 'input_schema' => [
42 'type' => 'object',
43 'properties' => [
44 'id' => [ 'type' => 'integer', 'description' => __( 'Full-site pack id (from discover-templates type=pack).', 'templately' ) ],
45 'platform' => [ 'type' => 'string', 'enum' => [ 'elementor', 'gutenberg' ], 'default' => 'elementor' ],
46 ],
47 'required' => [ 'id' ],
48 'additionalProperties' => false,
49 ],
50 'output_schema' => [
51 'type' => 'object',
52 'properties' => [
53 'handle' => [ 'type' => 'string' ],
54 'status' => [ 'type' => 'string' ],
55 'already_running' => [ 'type' => 'boolean' ],
56 'next' => [ 'type' => 'string' ],
57 ],
58 ],
59 'execute_callback' => [ self::class, 'execute' ],
60 'permission_callback' => [ Permissions::class, 'can_use_abilities' ],
61 'access_level' => ToolDescriptor::ACCESS_FULL,
62 'annotations' => [ 'readonly' => false, 'destructive' => true, 'idempotent' => false ],
63 ];
64 }
65
66 /**
67 * @param array $input
68 * @return array|WP_Error
69 */
70 public static function execute( array $input ) {
71 $id = (int) ( $input['id'] ?? 0 );
72 $platform = (string) ( $input['platform'] ?? 'elementor' );
73
74 if ( empty( $id ) ) {
75 return new WP_Error( 'pack_not_found', __( 'A full-site pack id is required.', 'templately' ) );
76 }
77 if ( ! in_array( $platform, [ 'elementor', 'gutenberg' ], true ) ) {
78 $platform = 'elementor';
79 }
80
81 $not_connected = self::require_connection();
82 if ( is_wp_error( $not_connected ) ) {
83 return $not_connected;
84 }
85
86 $rate_limit_error = ImportRateLimiter::check();
87 if ( $rate_limit_error ) {
88 return $rate_limit_error;
89 }
90
91 // FR-015: one active import per user — return the in-flight handle.
92 $user_id = get_current_user_id();
93 $active = FsiActiveImportGuard::get_active( $user_id );
94 if ( $active ) {
95 return [
96 'handle' => $active['session_id'],
97 'status' => 'running',
98 'already_running' => true,
99 'next' => __( 'An import is already in progress. Poll templately/full-site-import-status with this handle.', 'templately' ),
100 ];
101 }
102
103 // FR-008: unmet plugin dependencies (reuses 041's packs-aware resolver).
104 $dependency_check = DependencyGateResolver::check( $id, $platform );
105 if ( is_wp_error( $dependency_check ) ) {
106 return $dependency_check;
107 }
108
109 // Create the session (no download here) via the frozen AJAX contract.
110 $response = AjaxLoopbackDispatcher::dispatch_json( 'import_settings', [
111 'id' => $id,
112 'platform' => $platform,
113 ] );
114 if ( is_wp_error( $response ) ) {
115 return $response;
116 }
117
118 $failure = AjaxLoopbackDispatcher::envelope_error(
119 $response,
120 __( 'The import backend could not create a session.', 'templately' )
121 );
122 if ( null !== $failure ) {
123 return $failure;
124 }
125
126 if ( empty( $response['data']['session_id'] ) ) {
127 return new WP_Error(
128 'start_failed',
129 __( 'The import backend could not create a session.', 'templately' )
130 );
131 }
132
133 $handle = (string) $response['data']['session_id'];
134 FsiActiveImportGuard::set_active( $user_id, $handle );
135
136 return [
137 'handle' => $handle,
138 'status' => 'running',
139 'already_running' => false,
140 'next' => __( 'Call templately/full-site-import-status with this handle to advance and track the import.', 'templately' ),
141 ];
142 }
143
144 /**
145 * A connected, non-disconnected Templately account is a precondition
146 * (spec 042 Assumptions / Edge Cases); connecting is 041's job.
147 *
148 * @return true|WP_Error
149 */
150 private static function require_connection() {
151 $options = Options::get_instance();
152 $user = $options->get( 'user' );
153
154 if ( empty( $options->get( 'api_key' ) ) || ( is_array( $user ) && ! empty( $user['is_disconnected'] ) ) ) {
155 return new WP_Error(
156 'not_connected',
157 __( 'No connected Templately account. Connect first using the auth abilities, then retry.', 'templately' )
158 );
159 }
160
161 return true;
162 }
163 }
164