PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.7.2
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.7.2
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 3.1.10 All 111 releases
← All changes | includes/Core/Importer/Runners/Finalizer.php +122 -14 3.6.43.7.2 View file →
@@ -4,10 +4,10 @@
4 4
5 5
6 6 use Exception;
7 7 use Templately\Core\Importer\Utils\AIContentHelper;
8 +use Templately\Core\Importer\Utils\AIContentResolver;
8 9 use Templately\Core\Importer\Utils\Utils;
9 -use Templately\Core\Importer\Utils\AIUtils;
10 10 use Templately\Utils\Helper;
11 11
12 12 class Finalizer extends BaseRunner {
13 13 use AIContentHelper;
@@ -80,9 +80,18 @@
80 80 $has_attachment = isset($imported_data[$type]['__attachments'][$id]);
81 81 }
82 82 }
83 83
84 - if ( ! isset( $template['data'] ) && !$has_attachment && !isset($template['has_logo']) && !$this->is_ai_content($id) && !isset($template["page_settings"]["fluent_cart_store_settings"]) ) {
84 + // Pick up docs the import runners flagged as carrying source-site links
85 + // (Mega Menu / nav / button URLs) so the finalize loop rewrites them to the
86 + // real imported permalinks — same opt-in mechanism as __attachments above.
87 + // $imported_data["templates"]["__link_rewrites"][52]
88 + // $imported_data["content"]["page"]["__link_rewrites"][35]
89 + $needs_link_rewrite = $sub_type
90 + ? isset( $imported_data[ $type ][ $sub_type ]['__link_rewrites'][ $id ] )
91 + : isset( $imported_data[ $type ]['__link_rewrites'][ $id ] );
92 +
93 + if ( ! isset( $template['data'] ) && !$has_attachment && !$needs_link_rewrite && !isset($template['has_logo']) && !$this->is_ai_content($id) && !isset($template["page_settings"]["fluent_cart_store_settings"]) ) {
85 94 continue;
86 95 }
87 96
88 97 // if ( ! isset( $template['data']['form'] ) && ! isset( $template['data']['nav_menus'] )) {
@@ -105,8 +114,12 @@
105 114
106 115 $this->json->imported_data = $this->imported_data;
107 116 $this->json->map_post_ids = Utils::map_old_new_post_ids( $this->imported_data );
108 117 $this->json->map_term_ids = Utils::map_old_new_term_ids( $this->imported_data );
118 + if ( $this->manifest['platform'] === 'elementor' ) {
119 + $this->json->set_source_url( $this->manifest['site'] ?? '' );
120 + $this->json->set_source_pages( $this->build_source_pages_map() );
121 + }
109 122 if ( ! empty( $imported_data['extra-content'] ) ) {
110 123 $this->extra_content = $imported_data['extra-content'];
111 124 }
112 125
@@ -133,8 +146,100 @@
133 146
134 147 return [];
135 148 }
136 149
150 + /**
151 + * Build the ORIGINAL-slug => imported-permalink map handed to ElementorHelper so
152 + * links can resolve to the page WordPress actually created (slug-change-proof).
153 + *
154 + * Keyed by the pack's original slug — which is what stored links still carry —
155 + * with the value being get_permalink() of the imported post, so a conflict rename
156 + * (about-us → about-us-2) is transparently followed. Pages win over other post
157 + * types on a slug collision. WooCommerce system pages (shop, cart, checkout,
158 + * my-account) are added as aliases since they live outside the manifest content.
159 + *
160 + * @return array
161 + */
162 + private function build_source_pages_map(): array {
163 + $map = [];
164 + $map_post_ids = $this->json->map_post_ids;
165 + $content = $this->manifest['content'] ?? [];
166 +
167 + if ( is_array( $content ) ) {
168 + foreach ( $content as $post_type => $items ) {
169 + if ( ! is_array( $items ) ) {
170 + continue;
171 + }
172 +
173 + foreach ( $items as $old_id => $entry ) {
174 + $slug = isset( $entry['slug'] ) ? strtolower( trim( (string) $entry['slug'] ) ) : '';
175 + if ( '' === $slug ) {
176 + continue;
177 + }
178 +
179 + $new_id = $map_post_ids[ $old_id ] ?? null;
180 + if ( empty( $new_id ) ) {
181 + continue;
182 + }
183 +
184 + $permalink = get_permalink( $new_id );
185 + if ( ! $permalink || is_wp_error( $permalink ) ) {
186 + continue;
187 + }
188 +
189 + // First match wins, but a real `page` always overrides another
190 + // post type that happens to share the slug.
191 + if ( ! isset( $map[ $slug ] ) || 'page' === $post_type ) {
192 + $map[ $slug ] = $permalink;
193 + }
194 + }
195 + }
196 + }
197 +
198 + $this->add_woocommerce_page_aliases( $map );
199 +
200 + return $map;
201 + }
202 +
203 + /**
204 + * Add WooCommerce system-page slugs to the resolution map. These pages are not in
205 + * the manifest content, so menu links to them (often relative, e.g. /demo/shop,
206 + * /demo/sign-in) would otherwise only get the plain domain swap.
207 + *
208 + * Heuristic: the demo's my-account page may be titled/slugged "sign-in", "login",
209 + * etc.; those common aliases are mapped to this site's WooCommerce my-account page.
210 + * Manifest pages already in the map are never overwritten.
211 + *
212 + * @param array $map slug => permalink (by reference).
213 + *
214 + * @return void
215 + */
216 + private function add_woocommerce_page_aliases( array &$map ) {
217 + if ( ! function_exists( 'wc_get_page_permalink' ) ) {
218 + return;
219 + }
220 +
221 + $aliases = [
222 + 'shop' => [ 'shop', 'store', 'products' ],
223 + 'cart' => [ 'cart' ],
224 + 'checkout' => [ 'checkout' ],
225 + 'myaccount' => [ 'my-account', 'myaccount', 'account', 'sign-in', 'signin', 'login', 'log-in', 'dashboard' ],
226 + ];
227 +
228 + foreach ( $aliases as $wc_page => $slugs ) {
229 + $permalink = wc_get_page_permalink( $wc_page );
230 + if ( ! $permalink ) {
231 + continue;
232 + }
233 +
234 + foreach ( $slugs as $slug ) {
235 + if ( ! isset( $map[ $slug ] ) ) {
236 + $map[ $slug ] = $permalink;
237 + }
238 + }
239 + }
240 + }
241 +
137 242 private function regenerate_assets() {
138 243 $upload_dir = wp_upload_dir();
139 244 if ( is_dir( $upload_dir['basedir'] . '/eb-style/' ) ) {
140 245 array_map( 'unlink', glob( $upload_dir['basedir'] . '/eb-style/*.min.css' ) );
@@ -168,22 +273,25 @@
168 273 $processed_pages = get_option("templately_ai_processed_pages", []);
169 274 $updated_ids = $processed_pages[$this->process_id] ?? [];
170 275 $ai_paths = $this->generateAiFilePaths($old_template_id);
171 276 if($this->is_ai_content($old_template_id) && !file_exists($ai_paths['ai_file_path'])){
172 - // Use the static timeout-aware wait handler from AIUtils
173 - AIUtils::handle_sse_wait_with_timeout(
174 - $this->session_id,
175 - 'ai_content_time',
176 - $updated_ids,
177 - $this->ai_page_ids,
178 - [$this, 'sse_message'],
179 - [
180 - 'name' => method_exists($this, 'get_name') ? $this->get_name() : '',
277 + // Source-agnostic seam: whichever AI-content source owns this
278 + // process (classic per-page callback, chat-id pull, or a future
279 + // source) stages the .ai.json; otherwise the shared SSE-wait runs.
280 + AIContentResolver::ensure_page_ready([
281 + 'session_id' => $this->session_id,
282 + 'process_id' => $this->process_id,
283 + 'content_id' => $old_template_id,
284 + 'ai_page_ids' => $this->ai_page_ids,
285 + 'updated_ids' => $updated_ids,
286 + 'sse_callback' => [$this, 'sse_message'],
287 + 'progress_id' => 'ai_content_time',
288 + 'additional_sse_data' => [
289 + 'name' => method_exists($this, 'get_name') ? $this->get_name() : '',
181 290 'post_type' => $post_type,
182 - 'id' => $old_template_id,
291 + 'id' => $old_template_id,
183 292 ],
184 - $old_template_id // Pass template ID for local site polling
185 - );
293 + ]);
186 294 }
187 295 // Check if this is AI content before processing
188 296 if ($this->isAiContent($old_template_id)) {
189 297 // Process AI content using AIContentHelper trait