PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.2.7
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.2.7
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
templately / includes / Core / Importer / FullSiteImport.php

FullSiteImport.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.2.7, at includes/Core/Importer/FullSiteImport.php

1,162 lines 35.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * remove 'action' => 'continue',
5 * way to retry
6 * way to skip if failed multiple times
7 *
8 * @todo: on runner check for timeout and retry
9 * @todo: use ErrorException on runner to skip item when error occurs: not useful
10 *
11 */
12
13
14 namespace Templately\Core\Importer;
15
16 use Elementor\Plugin;
17 use Error;
18 use Exception;
19 use Templately\Core\Importer\Exception\NonRetryableErrorException;
20 use Templately\Core\Importer\Exception\RetryableErrorException;
21 use Templately\Core\Importer\Exception\UnknownErrorException;
22 use Templately\Core\Importer\Utils\LogHandler;
23 use Templately\Core\Importer\Utils\Utils;
24 use Templately\Utils\Base;
25 use Templately\Utils\Helper;
26 use Templately\Utils\Installer;
27 use Templately\Utils\Options;
28
29 class FullSiteImport extends Base {
30 use LogHelper;
31
32 const SESSION_OPTION_KEY = 'templately_import_session';
33 public $manifest;
34 protected $export;
35
36 private $version = '1.0.0';
37
38 public $download_key;
39 protected $dev_mode = false;
40 protected $api_key = '';
41 protected $session_id = '';
42 protected $documents_data = [];
43 protected $dependency_data = [];
44 private $is_import_status_handled = false;
45
46 public $dir_path;
47 protected $filePath;
48 protected $tmp_dir = null;
49 public $request_params = [];
50
51 public function __construct() {
52 $this->dev_mode = defined('TEMPLATELY_DEV') && TEMPLATELY_DEV;
53 $this->api_key = Options::get_instance()->get('api_key');
54
55 $this->add_ajax_action('import_settings', $this);
56 $this->add_ajax_action('import_status', $this);
57 $this->add_ajax_action('import', $this);
58 $this->add_ajax_action('import_revert', $this);
59 $this->add_ajax_action('import_info', $this);
60 $this->add_ajax_action('import_close_feedback_modal', $this);
61 $this->add_ajax_action('feedback_form', $this);
62 $this->add_ajax_action('google_font', $this);
63
64 add_action('admin_init', [$this, 'admin_init']);
65 // add_action('admin_notices', [$this, 'add_revert_button']);
66
67 if(isset($_GET['action']) && ($_GET['action'] == 'templately_pack_import' || $_GET['action'] == 'templately_pack_import_status')) {
68 add_filter('wp_redirect', '__return_false', 999);
69 }
70
71 if ($this->dev_mode) {
72 add_filter('http_request_host_is_external', '__return_true');
73 add_filter('http_request_args', function ($args) {
74 $args['sslverify'] = false;
75
76 return $args;
77 });
78 }
79 }
80
81 public function add_ajax_action($action, $object) {
82 add_action("wp_ajax_templately_pack_$action", function() use ($action, $object) {
83 // Check nonce
84 $nonce = null;
85 if(isset($_POST['nonce'])){
86 $nonce = $_POST['nonce'];
87 }
88 if(isset($_GET['nonce'])){
89 $nonce = $_GET['nonce'];
90 }
91 if (!$nonce || !wp_verify_nonce($nonce, 'templately_nonce')) {
92 wp_send_json_error(['message' => __('Invalid nonce', 'templately')]);
93 wp_die();
94 }
95
96 // Check user capability
97 if (!current_user_can('install_plugins') || !current_user_can('install_themes')) {
98 wp_send_json_error(['message' => __('Insufficient permissions', 'templately')]);
99 wp_die();
100 }
101
102 // Call the actual handler method
103 call_user_func([$object, $action]);
104 });
105 }
106
107 public function admin_init() {
108 if (get_option('templately_flush_rewrite_rules', false)) {
109 flush_rewrite_rules();
110 delete_option('templately_flush_rewrite_rules');
111 }
112 }
113
114 public function import_settings() {
115 $data = wp_unslash($_POST);
116
117 $upload_dir = wp_upload_dir();
118 $session_id = uniqid();
119 $tmp_dir = trailingslashit($upload_dir['basedir']) . 'templately' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
120
121 $this->session_id = $session_id;
122 $data['session_id'] = $session_id;
123 $data['root_dir'] = $tmp_dir;
124 $data['dir_path'] = $tmp_dir . $session_id . DIRECTORY_SEPARATOR;
125 $data['zip_path'] = $tmp_dir . "{$session_id}.zip";
126
127
128 if ( is_array( $data ) && ! empty( $data ) ) {
129 foreach ( $data as $key => $value ) {
130 $json = is_string($value) ? json_decode( $value, true ) : null;
131 $data[ $key ] = $json !== null ? $json : $value;
132 }
133 }
134
135 Utils::update_session_data($session_id, $data);
136
137
138 //clear previous revert backup
139 $options = Utils::get_backup_options();
140 foreach ($options as $key => $value) {
141 delete_option("__templately_$key");
142 }
143 delete_option('templately_fsi_imported_list');
144 delete_option('templately_fsi_log');
145
146 wp_send_json_success([
147 'is_lightspeed' => !Helper::should_flush(),
148 'session_id' => $session_id,
149 ]);
150 }
151
152 public function import_close_feedback_modal() {
153 $return = null;
154 if(isset($_GET['closeAction']) && $_GET['closeAction']){
155 $review_email = isset($_POST['review-email']) ? sanitize_email($_POST['review-email']) : '';
156 $pack_id = get_user_meta(get_current_user_id(), 'templately_fsi_pack_id', true);
157
158 // Prepare the body of the request
159 $body = json_encode([
160 'action' => $_GET['closeAction'],
161 'email' => $review_email,
162 'pack_id' => (int) $pack_id,
163 ]);
164
165 // Send the request to the API
166 $response = wp_remote_post($this->get_api_url('v2', 'feedback/close'), [
167 'timeout' => 30,
168 'headers' => [
169 'Content-Type' => 'application/json',
170 'Authorization' => 'Bearer ' . $this->api_key,
171 'x-templately-ip' => Helper::get_ip(),
172 'x-templately-url' => home_url('/'),
173 'x-templately-version' => TEMPLATELY_VERSION,
174 ],
175 'body' => $body,
176 ]);
177 $body = wp_remote_retrieve_body($response);
178 $return = json_decode($body, true);
179 }
180 update_user_meta(get_current_user_id(), 'templately_fsi_complete', 'done');
181 wp_send_json_success($return);
182 }
183 public function feedback_form() {
184 // Get data from $_POST
185 $review_description = isset($_POST['review-description']) ? sanitize_textarea_field($_POST['review-description']) : '';
186 $review_email = isset($_POST['review-email']) ? sanitize_email($_POST['review-email']) : '';
187 $rating = isset($_POST['rating']) ? sanitize_text_field($_POST['rating']) : '';
188 $pack_id = get_user_meta(get_current_user_id(), 'templately_fsi_pack_id', true);
189
190 // Prepare the body of the request
191 $body = json_encode([
192 'description' => $review_description,
193 'email' => $review_email,
194 'rating' => (int) $rating,
195 'pack_id' => (int) $pack_id,
196 ]);
197
198 // Send the request to the API
199 $response = wp_remote_post($this->get_api_url('v2', 'feedback/store'), [
200 'timeout' => 30,
201 'headers' => [
202 'Content-Type' => 'application/json',
203 'Authorization' => 'Bearer ' . $this->api_key,
204 'x-templately-ip' => Helper::get_ip(),
205 'x-templately-url' => home_url('/'),
206 'x-templately-version' => TEMPLATELY_VERSION,
207 ],
208 'body' => $body,
209 ]);
210
211 if (is_wp_error($response)) {
212 wp_send_json_error($response->get_error_message());
213 }
214
215 if (wp_remote_retrieve_response_code($response) != 200 && wp_remote_retrieve_response_code($response) != 201) {
216 wp_send_json_error('API request failed with response code ' . wp_remote_retrieve_response_code($response), wp_remote_retrieve_response_code($response));
217 }
218
219 $body = wp_remote_retrieve_body($response);
220 $data = json_decode($body, true);
221
222 if (!isset($data['status']) || $data['status'] !== 'success') {
223 wp_send_json_error('API response indicates failure.');
224 }
225
226 if (!isset($data['message'])) {
227 wp_send_json_error('API response missing data.');
228 }
229
230 $result = $data['message'];
231
232 wp_send_json_success($result);
233 }
234
235 // Modified get_session_data to use the static version
236 protected function get_session_data() {
237 return Utils::get_session_data_by_id();
238 }
239
240 // Modified update_session_data to use the static version
241 protected function update_session_data($data) {
242 return Utils::update_session_data_by_id($data);
243 }
244
245 public function initialize_props() {
246 $data = $this->get_session_data();
247 if (isset($data['session_id'])) {
248 $this->session_id = $data['session_id'];
249 }
250 if (isset($data['dir_path'])) {
251 $this->dir_path = $data['dir_path'];
252 }
253 if (isset($data['zip_path'])) {
254 $this->filePath = $data['zip_path'];
255 }
256 if (isset($data['download_key'])) {
257 $this->download_key = $data['download_key'];
258 }
259 if (isset($data['dependency_data'])) {
260 $this->dependency_data = $data['dependency_data'];
261 }
262 if (isset($data['is_import_status_handled'])) {
263 $this->is_import_status_handled = $data['is_import_status_handled'];
264 }
265 }
266
267 private function clear_session_data(): bool {
268 return delete_site_option(self::SESSION_OPTION_KEY);
269 }
270
271 private function finishRequestHeaders() {
272 if(Helper::should_flush()) {
273 // Disable output buffering and compression
274 @ini_set('output_buffering', 'Off');
275 @ini_set('zlib.output_compression', 'Off');
276 @ini_set('implicit_flush', 1);
277
278 // Time to run the import! Set no limit
279 set_time_limit(0);
280
281
282 // Set headers to prevent caching and buffering
283 header('Content-Type: text/event-stream, charset=UTF-8');
284 header('Cache-Control: no-cache, must-revalidate');
285 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
286 header('Connection: Keep-Alive');
287 header('Pragma: no-cache');
288
289 if (!empty($GLOBALS['is_nginx'])) {
290 header('X-Accel-Buffering: no');
291 header('Content-Encoding: none');
292 }
293
294 flush();
295 ob_flush();
296 wp_ob_end_flush_all();
297 } else {
298 header("Cache-Control: no-store, no-cache");
299 // header( 'Content-Type: text/event-stream, charset=UTF-8' );
300 // header( "Connection: Keep-Alive" );
301
302 // Ignore user aborts and allow the script to run forever
303 // (Use with caution, consider progress updates or timeouts)
304 ignore_user_abort(true);
305
306 // Time to run the import! Set no limit
307 set_time_limit(0);
308
309
310 if (!empty($GLOBALS['is_nginx'])) {
311 header('X-Accel-Buffering: no');
312 header('Content-Encoding: none');
313 }
314
315 // Send output as soon as possible during long-running process
316 if (function_exists('fastcgi_finish_request')) {
317 fastcgi_finish_request();
318 } elseif (function_exists('litespeed_finish_request')) {
319 litespeed_finish_request();
320 } else {
321 wp_ob_end_flush_all();
322 }
323 }
324 }
325
326 public function import() {
327 if ( ! $this->dev_mode && ! wp_doing_ajax() ) {
328 exit;
329 }
330
331 add_filter( 'wp_image_editors', [ $this, 'wp_image_editors' ], 10, 1 );
332
333
334 define('TEMPLATELY_START_TIME', microtime(true));
335
336 // delete_option( 'templately_fsi_log' );
337
338 register_shutdown_function( [ $this, 'register_shutdown' ] );
339
340 $this->finishRequestHeaders();
341
342 try {
343 // TODO: Need to check if user is connected or not
344 if(!empty($_GET['session_id'])){
345 $this->session_id = sanitize_text_field($_GET['session_id']);
346 }
347 else {
348 $this->throw(__('Invalid Session ID.', 'templately'));
349 }
350
351
352 $this->request_params = $this->get_session_data();
353 $this->initialize_props();
354 $this->add_revert_hooks();
355 $progress = $this->request_params['progress'] ?? [];
356
357 if(empty($progress['create_log_dir'])){
358 // Create Log Directory and if fail then chose option method
359 LogHandler::create_log_dir();
360
361 $progress['create_log_dir'] = true;
362 $this->update_session_data( [
363 'progress' => $progress,
364 ] );
365 $this->sse_message( [
366 'type' => 'eventLog',
367 'action' => 'eventLog',
368 'info' => 'create_log_dir',
369 'results' => __METHOD__ . '::' . __LINE__,
370 ] );
371 }
372
373 $_id = isset($this->request_params['id']) ? (int) $this->request_params['id'] : null;
374
375 if ($_id === null) {
376 $this->throw(__('Invalid Pack ID.', 'templately'));
377 }
378
379 $this->sse_message( [
380 'type' => 'start',
381 'action' => 'eventLog',
382 'results' => __METHOD__ . '::' . __LINE__,
383 ] );
384
385 if(empty($progress['download_zip'])){
386 /**
387 * Check Writing Permission
388 */
389 $this->check_writing_permission();
390
391 /**
392 * Download the zip
393 */
394 $this->download_zip( $_id );
395
396 $progress['download_zip'] = true;
397 $this->update_session_data( [
398 'progress' => $progress,
399 ] );
400 $this->sse_message( [
401 'type' => 'continue',
402 'action' => 'continue',
403 'info' => 'download_zip',
404 'results' => __METHOD__ . '::' . __LINE__,
405 ] );
406 exit;
407 }
408
409
410
411
412 /**
413 * Reading Manifest File
414 */
415 $this->manifest = $this->read_manifest($this->request_params['dir_path']);
416
417 /**
418 * Version Check
419 */
420 if ( ! empty( $this->manifest['version'] ) && version_compare( $this->manifest['version'], $this->version, '>' ) ) {
421 /**
422 * FIXME: The message should be re-written (by content/support team).
423 */
424 $this->throw( __( 'Please update the templately plugin.', 'templately' ) );
425 }
426
427 $platform = $this->manifest['platform'] ?? '';
428 if($platform === 'elementor') {
429 Helper::enable_elementor_container();
430 }
431
432
433
434 update_option('templately_import_platform', $platform);
435
436
437 /**
438 * Should Revert Old Data
439 */
440 // $this->revert();
441
442 /**
443 * Platform Based Templates Import
444 */
445 $this->start_content_import();
446
447 } catch ( Exception $e ) {
448 $should_retry = $e instanceof RetryableErrorException;
449 $this->handle_import_status('failed', $e->getMessage());
450
451 $this->sse_message([
452 'action' => 'error',
453 'status' => 'error',
454 'type' => "error",
455 'retry' => $should_retry,
456 'title' => __("Oops!", "templately"),
457 'message' => $e->getMessage(),
458 'trace' => $e->getTraceAsString(),
459 ]);
460 }
461
462 // if($_GET['part'] === 'import'){
463 // TODO: cleanup
464 // $this->clear_session_data();
465 // }
466 }
467
468
469 public function wp_image_editors( $editors ) {
470 // If GD is available, use only GD. Otherwise, fallback to all available editors.
471 if ( is_callable( [ 'WP_Image_Editor_GD', 'test' ] ) && call_user_func( [ 'WP_Image_Editor_GD', 'test' ] ) ) {
472 return [ 'WP_Image_Editor_GD' ];
473 }
474 return $editors;
475 }
476
477 // Updated import_status method
478 public function import_status() {
479 $request_params = $this->get_session_data();
480
481 if (isset($request_params['log_type']) && $request_params['log_type'] == 'file') {
482 $log_index = isset($_GET['lastLogIndex']) ? (int) $_GET['lastLogIndex'] : 0;
483 $log = LogHandler::read_log_file($log_index);
484
485 wp_send_json(['count' => count($log), 'log' => $log]);
486 } else {
487 $log = get_option('templately_fsi_log');
488
489 if (!empty($log) && is_array($log) && isset($_GET['lastLogIndex'])) {
490 $lastLogIndex = (int) $_GET['lastLogIndex'];
491 $log = array_slice($log, $lastLogIndex);
492 }
493 wp_send_json(['count' => $log ? count($log) : 0, 'log' => $log]);
494 }
495 }
496
497 /**
498 * @throws Exception
499 */
500 private function throw($message, $code = 0) {
501 if ($this->dev_mode) {
502 error_log(print_r($message, 1));
503 }
504 throw new Exception($message);
505 }
506 /**
507 * @throws Exception
508 */
509 private function throw_non_retryable($message, $code = 0) {
510 if ($this->dev_mode) {
511 error_log(print_r($message, 1));
512 }
513 throw new NonRetryableErrorException($message);
514 }
515 /**
516 * @throws Exception
517 */
518 private function throw_retryable($message, $code = 0) {
519 if ($this->dev_mode) {
520 error_log(print_r($message, 1));
521 }
522 throw new RetryableErrorException($message);
523 }
524 /**
525 * @throws Exception
526 */
527 private function throw_unknown($message, $code = 0) {
528 if ($this->dev_mode) {
529 error_log(print_r($message, 1));
530 }
531 throw new UnknownErrorException($message);
532 }
533
534 /**
535 * @throws Exception
536 */
537 private function check_writing_permission() {
538 $upload_dir = wp_upload_dir();
539
540 if (!is_writable($upload_dir['basedir'])) {
541 $this->throw(__('Upload directory is not writable.', 'templately'));
542 }
543
544 $this->tmp_dir = trailingslashit($upload_dir['basedir']) . 'templately' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
545
546 if (!is_dir($this->tmp_dir)) {
547 wp_mkdir_p($this->tmp_dir);
548 }
549
550 $this->sse_log('writing_permission_check', __('Permission Passed', 'templately'), 100);
551 }
552
553 private function get_api_url($version, $end_point): string {
554 return $this->dev_mode ? "https://app.templately.dev/api/$version/" . $end_point : "https://app.templately.com/api/$version/" . $end_point;
555 }
556
557 private function info_get_api_url($id): string {
558 return $this->dev_mode ? 'https://app.templately.dev/api/v1/import/info/pack/' . $id : 'https://app.templately.com/api/v1/import/info/pack/' . $id;
559 }
560
561 /**
562 * @throws Exception
563 */
564 private function download_zip( $id ) {
565 $this->sse_log( 'download', __( 'Downloading Template Pack', 'templately' ), 1 );
566 $response = wp_remote_get( $this->get_api_url( "v2", "import/pack/$id" ), [
567 'timeout' => 30,
568 'headers' => [
569 'Content-Type' => 'application/json',
570 'Authorization' => 'Bearer ' . $this->api_key,
571 'x-templately-ip' => Helper::get_ip(),
572 'x-templately-url' => home_url('/'),
573 'x-templately-version' => TEMPLATELY_VERSION,
574 ]
575 ]);
576
577 $response_code = wp_remote_retrieve_response_code($response);
578 $content_type = wp_remote_retrieve_header($response, 'content-type');
579 $this->download_key = wp_remote_retrieve_header($response, 'download-key');
580
581 if (is_wp_error($response)) {
582 $this->throw_retryable(__('Template pack download failed', 'templately') . $response->get_error_message());
583 } else if ($response_code != 200) {
584 if (strpos($content_type, 'application/json') !== false) {
585 // Retrieve Data from Response Body.
586 $response_body = json_decode(wp_remote_retrieve_body($response), true);
587
588 // If the response body is JSON and it contains an error, throw an exception with the error message
589 if (isset($response_body['status']) && $response_body['status'] === 'error') {
590 $support_message = '';
591 if(strpos($response_body['message'], 'https://wpdeveloper.com/support') === false){
592 $support_message = sprintf(__(" Please try again or contact <a href='%s' target='_blank'>support</a>.", "templately"), 'https://wpdeveloper.com/support');
593 }
594 $this->throw_non_retryable($response_body['message'] . $support_message);
595 }
596 }
597 $this->throw_unknown(__('Template pack download failed with response code: ', 'templately') . $response_code);
598 }
599
600 $this->sse_log('download', __('Downloading Template Pack', 'templately'), 57);
601
602 $this->update_session_data([
603 'download_key' => $this->download_key,
604 ]);
605
606 if (file_put_contents($this->filePath, $response['body'])) { // phpcs:ignore
607 $this->sse_log('download', __('Downloading Template Pack', 'templately'), 100);
608
609 $this->unzip();
610 } else {
611 $this->throw_retryable(__('Downloading Failed. Please try again', 'templately'));
612 }
613 }
614
615 /**
616 * @throws Exception
617 */
618 protected function unzip() {
619 if (!WP_Filesystem()) {
620 $this->throw(__('WP_Filesystem cannot be initialized', 'templately'));
621 }
622
623 $unzip = unzip_file($this->filePath, $this->dir_path);
624 if (is_wp_error($unzip)) {
625 $unzip = $this->unzip_file($this->filePath, $this->dir_path);
626 }
627
628 if (is_wp_error($unzip)) {
629 $error = $unzip->get_error_message();
630 if (empty($error)) {
631 // Generic error message
632 Helper::log($unzip);
633 $error_message = sprintf(__("It seems we're experiencing technical difficulties. Please try again or contact <a href='%s' target='_blank'>support</a>.", "templately"), 'https://wpdeveloper.com/support');
634 $this->throw($error_message);
635 } else {
636 $this->throw($unzip->get_error_message());
637 }
638 }
639
640 if ($unzip) {
641 unlink($this->filePath);
642 }
643 }
644
645 /**
646 * Unzip a specified ZIP file to a location on the Filesystem.
647 *
648 * @param string $file Full path and filename of ZIP archive.
649 * @param string $to Full path on the filesystem to extract archive to.
650 * @return true|WP_Error True on success, WP_Error on failure.
651 */
652 function unzip_file($file, $to) {
653 try {
654 $zip = new \ZipArchive;
655
656 $res = $zip->open($file);
657 if ($res === TRUE) {
658 $zip->extractTo($to);
659 $zip->close();
660
661 return true;
662 }
663 } catch (\Throwable $th) {
664 return new \WP_Error('exception_caught', $th->getMessage());
665 }
666
667 if (isset($zip)) {
668 return new \WP_Error('zip_error_' . $zip->status, $zip->getStatusString());
669 } else {
670 return new \WP_Error('unknown_error', '');
671 }
672 }
673
674 /**
675 * @throws Exception
676 */
677 private function read_manifest($dir_path) {
678 $manifest_content = file_get_contents($dir_path . 'manifest.json');
679 if (empty($manifest_content)) {
680 $this->throw(__('Cannot be imported, as the manifest file is corrupted', 'templately'));
681 }
682
683 $manifest_content = json_decode($manifest_content, true);
684 $this->removeLog('temp');
685
686 return $manifest_content;
687 // TODO: Read & Broadcast the LOG for waiting list
688 // $this->sse_log( 'plugin', 'Installing required plugins', '--', 'updateLog', 'processing' );
689 // // $this->sse_log( 'extra-content', 'Import Extra Contents (i.e: Forms)', '--', 'updateLog', 'processing' );
690 // $this->sse_log( 'templates', 'Import Templates (i.e: Header, Footer etc)', '--', 'updateLog', 'processing' );
691 // // $this->sse_log( 'content', 'Import Pages, Posts etc', '--', 'updateLog', 'processing' );
692 // $this->sse_log( 'wp-content', 'Importing Pages, Posts, Navigation, etc', '--', 'updateLog', 'processing' );
693 // $this->sse_log( 'finalize', 'Finalizing Your Imports', '--', 'updateLog', 'processing' );
694 }
695
696 private function skipped_plugin(): bool {
697 return empty($this->request_params['plugins']) || !is_array($this->request_params['plugins']);
698 }
699
700
701 private function before_install_hook() {
702 // remove_all_actions( 'wp_loaded' );
703 // remove_all_actions( 'after_setup_theme' );
704 // remove_all_actions( 'plugins_loaded' );
705 // remove_all_actions( 'init' );
706
707 // making sure so that no redirection happens during plugin installation and hooks triggered bellow.
708 add_filter('wp_redirect', '__return_false', 999);
709 }
710
711 private function after_install_hook() {
712 // do_action( 'wp_loaded' );
713 // do_action( 'after_setup_theme' );
714 // do_action( 'plugins_loaded' );
715 // do_action( 'init' );
716 }
717
718 /**
719 * @throws Exception
720 */
721 private function start_content_import() {
722 add_filter('upload_mimes', array($this, 'allow_svg_upload'));
723 add_filter('elementor/files/allow_unfiltered_upload', '__return_true');
724
725 $request_params = $this->get_session_data();
726
727 $import = new Import(array_merge($request_params, [
728 'origin' => $this,
729 'manifest' => $this->manifest,
730 ]));
731 $imported_data = $import->run();
732
733 $import_status = $this->handle_import_status('success');
734
735 update_option('templately_flush_rewrite_rules', true, false);
736
737 $this->sse_message([
738 'type' => 'complete',
739 'action' => 'complete',
740 'results' => $this->normalize_imported_data($imported_data)
741 ]);
742
743 update_user_meta(get_current_user_id(), 'templately_fsi_pack_id', $request_params["id"]);
744 if(!empty($import_status['hasFeedback'])){
745 update_user_meta(get_current_user_id(), 'templately_fsi_complete', 'done');
746 }
747 else{
748 update_user_meta(get_current_user_id(), 'templately_fsi_complete', true);
749 }
750 }
751
752 private function normalize_imported_data($data) {
753 $attachments = !empty($data['attachments']['succeed']) ? count($data['attachments']['succeed']) : 0;
754 $attachments_fail = !empty($data['attachments']['failed']) ? count($data['attachments']['failed']) : 0;
755 $attachments_errors = !empty($data['attachments_errors']) ? $data['attachments_errors'] : [];
756 $templates = !empty($data['templates']['succeed']) ? count($data['templates']['succeed']) : 0;
757 $template_types = !empty($data['templates']['template_types']) ? $data['templates']['template_types'] : [];
758
759 $post_types = [];
760 $content_templates = [];
761 if (!empty($data['content']) && is_array($data['content'])) {
762 foreach ($data['content'] as $type => $type_data) {
763 $content_templates[$type] = !empty($type_data['succeed']) ? count($type_data['succeed']) : 0;
764 $post_types[] = $this->get_post_type_label_by_slug($type);
765 }
766 }
767
768 $contents = [];
769 if (!empty($data['wp-content']) && is_array($data['wp-content'])) {
770 foreach ($data['wp-content'] as $type => $type_data) {
771 $contents[$type] = !empty($type_data['succeed']) ? count($type_data['succeed']) : 0;
772 if (!in_array($type, ['wp_navigation', 'nav_menu_item'])) {
773 $post_types[] = $this->get_post_type_label_by_slug($type);
774 }
775 }
776 }
777
778 Helper::log($data);
779
780 return [
781 'attachments' => $attachments,
782 'attachments_fail' => $attachments_fail,
783 'attachments_errors' => $attachments_errors,
784 'templates' => $templates,
785 'contents' => $content_templates,
786 'wp-content' => $contents,
787 'post_types' => $post_types,
788 'template_types' => $template_types,
789 'dependency_data' => $this->dependency_data,
790 ];
791 }
792
793 public function get_request_params() {
794 return $this->request_params;
795 }
796
797 private function revert() {
798 // $request = $this->get_request_params();
799 // if ( isset( $request['revert'] ) && $request['revert'] ) {
800 // // TODO: Implement the Revert Process.
801 // }
802 }
803
804 public function redirect_for_archives($link, $post_id) {
805 $archive_settings = get_option('templately_post_archive');
806 if (!empty($archive_settings) && intval($archive_settings['post_id']) === intval($post_id)) {
807 $link = str_replace($post_id, $archive_settings['archive_id'], $link);
808 }
809
810 return $link;
811 }
812
813 public function allow_svg_upload($mimes) {
814 // Allow SVG
815 $mimes['svg'] = 'image/svg+xml';
816 return $mimes;
817 }
818
819 public function register_shutdown() {
820 $status = connection_status();
821 $last_error = error_get_last();
822 if ($last_error && ($last_error['type'] === E_ERROR || $last_error['type'] === E_CORE_ERROR || $last_error['type'] === E_COMPILE_ERROR || $last_error['type'] === E_USER_ERROR)) {
823 if (!empty($last_error['message'])) {
824 $full_message = $last_error['message'];
825 // Extract the first line from the error message
826 $firstLine = strtok($full_message, "\n");
827
828 // Remove absolute paths by replacing the WordPress directory path with a placeholder
829 $error_message = str_replace(ABSPATH, 'ABSPATH/', $firstLine);
830 } else {
831 // Generic error message
832 $error_message = sprintf(__("It seems we're experiencing technical difficulties. Please try again or contact <a href='%s' target='_blank'>support</a>.", "templately"), 'https://wpdeveloper.com/support');
833 }
834
835
836 $this->handle_import_status('failed', $error_message);
837 // Handle the error, e.g. log it or display a message to the user
838 $this->sse_message([
839 'action' => 'error',
840 'status' => 'error',
841 'type' => "error",
842 'retry' => true,
843 'title' => __("Oops!", "templately"),
844 'message' => $error_message,
845 // 'position' => 'plugin',
846 // 'progress' => '--',
847 ]);
848 }
849
850 $this->debug_log("Shutdown:.....");
851 $this->debug_log("connection_status: " . $this->getConnectionStatusText());
852 $this->debug_log($last_error);
853 }
854
855 public function handle_import_status($status, $description = '') {
856 if ($this->is_import_status_handled === $status) {
857 Helper::log("Import status already handled: $status");
858 return null;
859 }
860 $this->is_import_status_handled = $status;
861
862 $download_key = $this->download_key;
863
864 $headers = [
865 'Content-Type' => 'application/json',
866 'Authorization' => 'Bearer ' . $this->api_key,
867 'download_key' => $download_key,
868 'download-key' => $download_key,
869 'x-templately-ip' => Helper::get_ip(),
870 'x-templately-url' => home_url('/'),
871 ];
872
873 $args = [
874 'headers' => $headers,
875 ];
876
877
878 if ($status === 'success') {
879 $url = $this->get_api_url("v1", 'import/success');
880 $args['body'] = json_encode(['type' => 'pack']);
881 $response = wp_remote_post($url, $args);
882 } elseif ($status === 'failed') {
883 $url = $this->get_api_url("v1", 'import/failed');
884 $args['body'] = json_encode(['type' => 'pack', 'description' => $description ?: "Something Went wrong....."]);
885 $response = wp_remote_post($url, $args);
886 }
887
888 Helper::log($response);
889
890 if (is_wp_error($response)) {
891 // Handle error
892 Helper::log($response->get_error_message());
893 } else {
894
895 $this->update_session_data([
896 'is_import_status_handled' => $this->is_import_status_handled,
897 ]);
898 // Handle success
899 $body = wp_remote_retrieve_body($response);
900 $data = json_decode($body, true);
901 // Do something with $body
902 return $data;
903 }
904
905 return null;
906 }
907
908 protected function getConnectionStatusText() {
909 $status = connection_status();
910 switch ($status) {
911 case CONNECTION_NORMAL:
912 return "Normal";
913 case CONNECTION_ABORTED:
914 return "Aborted";
915 case CONNECTION_TIMEOUT:
916 return "Timeout";
917 default:
918 return "Unknown";
919 }
920 }
921
922 protected function get_post_type_label_by_slug($slug) {
923 $post_type_obj = get_post_type_object($slug);
924 if ($post_type_obj) {
925 return $post_type_obj->label;
926 }
927 return null;
928 }
929
930 public function import_info() {
931
932 $platform = isset($_GET['platform']) ? $_GET['platform'] : 'elementor';
933 $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
934
935 $response = wp_remote_get($this->info_get_api_url($id), [
936 'timeout' => 30,
937 'headers' => [
938 'Authorization' => 'Bearer ' . $this->api_key,
939 'x-templately-ip' => Helper::get_ip(),
940 'x-templately-url' => home_url('/'),
941 'x-templately-version' => TEMPLATELY_VERSION,
942 ]
943 ]);
944
945 if (is_wp_error($response)) {
946 wp_send_json_error($response->get_error_message());
947 return;
948 }
949 // If the response code is not 200, return the error message
950 if (wp_remote_retrieve_response_code($response) != 200) {
951 wp_send_json_error(json_decode(wp_remote_retrieve_body($response)), wp_remote_retrieve_response_code($response));
952 return;
953 }
954 // If the response body is JSON and it contains an error, return the error message
955 // Retrieve Data from Response Body.
956 $body = wp_remote_retrieve_body($response);
957 $data = json_decode($body, true);
958
959 if (isset($data['error'])) {
960 wp_send_json_error($data['error']);
961 return;
962 }
963
964 if (isset($data['data']['manifest'])) {
965 $data['data']['manifest'] = json_decode($data['data']['manifest'], true);
966 }
967 if (isset($data['data']['settings'])) {
968 $data['data']['settings'] = json_decode($data['data']['settings'], true);
969 }
970
971 // Return the response body
972 wp_send_json($data);
973 }
974
975 public function update_imported_list($type, $id) {
976 $imported_list = get_option('templately_fsi_imported_list', []);
977 $imported_list[$type][] = $id;
978 update_option('templately_fsi_imported_list', $imported_list);
979 }
980
981 /**
982 *
983 *
984 * @return void
985 */
986 protected function add_revert_hooks() {
987 add_action('wp_insert_post', function ($post_id) {
988 $this->update_imported_list('posts', $post_id);
989 });
990 add_action('add_attachment', function ($post_id) {
991 $this->update_imported_list('attachment', $post_id);
992 });
993 add_action('created_term', function ($term_id, $tt_id, $taxonomy, $args) {
994 $this->update_imported_list('term', [$term_id, $taxonomy]);
995 }, 10, 4);
996 add_action('registered_taxonomy', function ($taxonomy, $object_type, $taxonomy_object) {
997 $this->update_imported_list('taxonomy', $taxonomy);
998 }, 10, 3);
999 add_action('fluentform/form_imported', function ($formId){
1000 $this->update_imported_list('fluentform', $formId);
1001 }, 10, 1);
1002 }
1003
1004 public static function has_revert(){
1005 $options = Utils::get_backup_options();
1006 $imported_list = get_option('templately_fsi_imported_list', []);
1007 if(!empty($options) || !empty($imported_list)){
1008 return true;
1009 }
1010 return false;
1011 }
1012
1013 public function import_revert() {
1014
1015 // // Get the nonce value from the request (usually from $_POST or $_GET)
1016 // $received_nonce = isset($_REQUEST['_wpnonce']) ? $_REQUEST['_wpnonce'] : '';
1017
1018 // // Verify the nonce using wp_verify_nonce()
1019 // $verified = wp_verify_nonce($received_nonce, 'templately_pack_import_revert_nonce');
1020
1021 // if (!$verified) {
1022 // wp_send_json_error("Nonce not verified.");
1023 // }
1024
1025 delete_option('templately_import_platform');
1026
1027 $option_active = null;
1028 $options_deleted = false;
1029 $imported_list_deleted = false;
1030 $options = Utils::get_backup_options();
1031 $status_args = [ 'post_type' => 'templately_library' ];
1032 $all_post_url = add_query_arg( [
1033 "page" => "templately_settings",
1034 "path" => "settings/elementor/miscellaneous",
1035 ], admin_url('admin.php' ));
1036 // wp_send_json_success([$options]);
1037
1038 if(class_exists('Elementor\Plugin')){
1039 $kits_manager = Plugin::$instance->kits_manager;
1040 $option_active = $kits_manager::OPTION_ACTIVE;
1041 $kit = $kits_manager->get_active_kit();
1042
1043 if ( ! $kit->get_id() ) {
1044 $kit = $kits_manager->create_default();
1045 update_option( $kits_manager::OPTION_ACTIVE, $kit );
1046 }
1047 }
1048
1049
1050 if (!empty($options) && is_array($options)) {
1051 foreach ($options as $key => $value) {
1052 if ('stylesheet' === $key) {
1053 if (get_option('stylesheet') !== $value) {
1054 switch_theme($value);
1055 }
1056 } else if($option_active === $key && class_exists('Elementor\Plugin')) {
1057 $kits_manager->revert( (int) $kits_manager->get_active_id(), (int) $value, 0 );
1058 $kit = $kits_manager->get_active_kit();
1059 $settings = $kit->get_data('settings');
1060 if ( isset( $settings['site_logo'] ) ) {
1061 set_theme_mod( 'custom_logo', $settings['site_logo']['id'] );
1062 }
1063 } else {
1064 update_option($key, $value);
1065 }
1066 delete_option("__templately_$key");
1067 $options_deleted = true;
1068 }
1069 }
1070
1071 $imported_list = get_option('templately_fsi_imported_list', []);
1072 if (!empty($imported_list) && is_array($imported_list)) {
1073 $_GET['force_delete_kit'] = 1; // Fallback GET Ready!
1074 foreach ($imported_list as $type => $list) {
1075 if (empty($list) || !is_array($list)) {
1076 continue;
1077 }
1078 // Loop through each item ID and delete it
1079 foreach ($list as $key => $item_id) {
1080 switch ($type) {
1081 case 'posts':
1082 // making sure default kit don't get deleted.
1083 if($option_active && isset($options[$option_active]) && $options[$option_active] == $item_id){
1084 break;
1085 }
1086 wp_delete_post($item_id, true); // Set true for permanent deletion
1087 break;
1088 case 'attachment':
1089 wp_delete_attachment($item_id, true); // Set true for permanent deletion
1090 break;
1091 case 'term':
1092 list($term_id, $taxonomy) = $item_id;
1093 wp_delete_term($term_id, $taxonomy); // Use corresponding taxonomy
1094 break;
1095 case 'taxonomy':
1096 // Taxonomies cannot be directly deleted. Consider de-registering it.
1097 break;
1098 case 'fluentform':
1099 if(class_exists('\FluentForm\App\Models\Form')){
1100 \FluentForm\App\Models\Form::remove($item_id);
1101 }
1102 break;
1103 }
1104 }
1105 }
1106
1107 $imported_list_deleted = true;
1108 delete_option('templately_fsi_imported_list');
1109 }
1110
1111
1112 if($options_deleted || $imported_list_deleted){
1113 sleep(5);
1114 wp_send_json_success([ 'options' => $options_deleted, 'imported_list' => $imported_list_deleted, 'site_url' => home_url(), 'redirect' => $all_post_url ]);
1115 }
1116
1117 wp_send_json_error([ 'options' => $options_deleted, 'imported_list' => $imported_list_deleted, 'site_url' => home_url() ]);
1118 }
1119
1120 public function google_font() {
1121 $result = get_transient('templately-google-fonts');
1122
1123 if (false == $result) {
1124 $response = wp_remote_get($this->get_api_url('v2', 'google-font'), [
1125 'timeout' => 30,
1126 'headers' => [
1127 'Authorization' => 'Bearer ' . $this->api_key,
1128 'x-templately-ip' => Helper::get_ip(),
1129 'x-templately-url' => home_url( '/' ),
1130 'x-templately-version' => TEMPLATELY_VERSION,
1131 ]
1132 ]);
1133
1134 if (is_wp_error($response)) {
1135 wp_send_json_error($response->get_error_message());
1136 }
1137
1138 if (wp_remote_retrieve_response_code($response) != 200) {
1139 wp_send_json_error('API request failed with response code ' . wp_remote_retrieve_response_code($response), wp_remote_retrieve_response_code($response));
1140 }
1141
1142 $body = wp_remote_retrieve_body($response);
1143 $data = json_decode($body, true);
1144
1145 if (!isset($data['status']) || $data['status'] !== 'success') {
1146 wp_send_json_error('API response indicates failure.');
1147 }
1148
1149 if (!isset($data['data'])) {
1150 wp_send_json_error('API response missing data.');
1151 }
1152
1153 $result = $data['data'];
1154 set_transient('templately-google-fonts', $result, DAY_IN_SECONDS);
1155 }
1156
1157 wp_send_json_success($result);
1158 }
1159
1160
1161 }
1162