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

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

640 lines 19.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Core\Importer;
4
5 use EbStyleHandler;
6 use Exception;
7 use Templately\Utils\Base;
8 use Templately\Utils\Helper;
9 use Templately\Utils\Installer;
10 use Templately\Utils\Options;
11
12 class FullSiteImport extends Base {
13 use LogHelper;
14
15 const SESSION_OPTION_KEY = 'templately_import_session';
16 public $manifest;
17 protected $export;
18
19 private $version = '1.0.0';
20
21 public $session_id;
22 public $dir_path;
23 protected $filePath;
24 protected $tmp_dir = null;
25 protected $dev_mode = false;
26 protected $api_key = '';
27 public $request_params = [];
28 protected $documents_data = [];
29 protected $dependency_data = [];
30
31 public function __construct() {
32 $this->dev_mode = defined( 'TEMPLATELY_DEV' ) && TEMPLATELY_DEV;
33 $this->api_key = Options::get_instance()->get( 'api_key' );
34
35 add_action( 'wp_ajax_templately_import_settings', [ $this, 'import_settings' ] );
36 add_action( 'wp_ajax_templately_pack_import', [ $this, 'import' ] );
37
38 if ( $this->dev_mode ) {
39 add_filter( 'http_request_host_is_external', '__return_true' );
40 add_filter( 'http_request_args', function ( $args ) {
41 $args['sslverify'] = false;
42
43 return $args;
44 } );
45 }
46 add_action( 'eb_frontend_assets', [ $this, 'enqueue_template_assets' ], 10, 2 );
47 add_filter( 'the_content', [$this, 'filter_content'], 10 ,1);
48 }
49
50 public function import_settings() {
51 if( ! current_user_can( 'edit_posts' ) ) {
52 wp_send_json_error( __( 'Unauthorized action.', 'templately' ) );
53 }
54
55 $data = wp_unslash( $_POST );
56
57 // Upload the logo and add its URL to the data
58 $logo_url = $this->upload_logo();
59 if ($logo_url !== null) {
60 $data['logo'] = $logo_url;
61 }
62
63 wp_send_json_success( update_option( self::SESSION_OPTION_KEY, $data ) );
64 }
65
66 private function update_session_data( $data ): bool {
67 $old_data = get_option( self::SESSION_OPTION_KEY, [] );
68
69 return update_option( self::SESSION_OPTION_KEY, wp_parse_args( $data, $old_data ) );
70 }
71
72 public function get_session_data(): array {
73 $data = get_option( self::SESSION_OPTION_KEY, [] );
74
75 $options = [];
76
77 if ( is_array( $data ) && ! empty( $data ) ) {
78 foreach ( $data as $key => $value ) {
79 $json = json_decode( $value, true );
80 $options[ $key ] = $json !== null ? $json : $value;
81 }
82 }
83
84 return $options;
85 }
86
87 private function clear_session_data(): bool {
88 return delete_site_option( self::SESSION_OPTION_KEY );
89 }
90
91 public function import() {
92 if ( ! $this->dev_mode && ! wp_doing_ajax() ) {
93 exit;
94 }
95
96 header( "Cache-Control: no-store, no-cache" );
97 header( 'Content-Type: text/event-stream, charset=UTF-8' );
98 header( "Connection: Keep-Alive" );
99
100 if ( $GLOBALS['is_nginx'] ) {
101 header( 'X-Accel-Buffering: no' );
102 header( 'Content-Encoding: none' );
103 }
104
105 register_shutdown_function( [ $this, 'register_shutdown' ] );
106
107 // Time to run the import!
108 set_time_limit( 0 );
109
110 flush();
111 wp_ob_end_flush_all();
112
113 try {
114 // TODO: Need to check if user is connected or not
115
116 $this->request_params = $this->get_session_data();
117
118 $_id = isset( $this->request_params['id'] ) ? (int) $this->request_params['id'] : null;
119
120 if ( $_id === null ) {
121 $this->throw( __( 'Invalid Pack ID.', 'templately' ) );
122 }
123
124 /**
125 * Check Writing Permission
126 */
127 $this->check_writing_permission();
128
129 /**
130 * Download the zip
131 */
132 $this->download_zip( $_id );
133
134 /**
135 * Reading Manifest File
136 */
137 $this->read_manifest();
138
139 /**
140 * Version Check
141 */
142 if ( ! empty( $this->manifest['version'] ) && version_compare( $this->manifest['version'], $this->version, '>' ) ) {
143 /**
144 * FIXME: The message should be re-written (by content/support team).
145 */
146 $this->throw( __( 'Please update the templately plugin.', 'templately' ) );
147 }
148
149 /**
150 * Checking & Installing Plugin Dependencies
151 */
152 $this->install_dependencies();
153
154 /**
155 * Should Revert Old Data
156 */
157 $this->revert();
158
159 /**
160 * Platform Based Templates Import
161 */
162 $this->start_content_import();
163 } catch ( Exception $e ) {
164 $this->sse_message( [
165 'action' => 'error',
166 'status' => 'error',
167 'type' => "error",
168 'title' => __("Oops!", "templately"),
169 'message' => $e->getMessage()
170 ] );
171 }
172
173 // TODO: cleanup
174 $this->clear_session_data();
175 }
176
177 /**
178 * @throws Exception
179 */
180 private function throw( $message, $code = 0 ) {
181 if ( $this->dev_mode ) {
182 error_log( print_r( $message, 1 ) );
183 }
184 throw new Exception( $message );
185 }
186
187 /**
188 * @throws Exception
189 */
190 private function check_writing_permission() {
191 $upload_dir = wp_upload_dir();
192
193 if ( ! is_writable( $upload_dir['basedir'] ) ) {
194 $this->throw( __( 'Upload directory is not writable.', 'templately' ) );
195 }
196
197 $this->tmp_dir = trailingslashit( $upload_dir['basedir'] ) . 'templately' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
198
199 if ( ! is_dir( $this->tmp_dir ) ) {
200 wp_mkdir_p( $this->tmp_dir );
201 }
202
203 $this->sse_log( 'writing_permission_check', __( 'Permission Passed', 'templately' ), 100 );
204 }
205
206 private function get_api_url( $id ): string {
207 return $this->dev_mode ? 'https://app.templately.dev/api/v1/import/pack/' . $id : 'https://app.templately.com/api/v1/import/pack/' . $id;
208 }
209
210 /**
211 * @throws Exception
212 */
213 private function download_zip( $id ) {
214 // $this->sse_log( 'download', __( 'Downloading Template Pack', 'templately' ), 1 );
215 $response = wp_remote_get( $this->get_api_url( $id ), [
216 'headers' => [
217 'Authorization' => 'Bearer ' . $this->api_key,
218 'x-templately-ip' => Helper::get_ip(),
219 'x-templately-url' => home_url( '/' )
220 ]
221 ] );
222
223 if ( is_wp_error( $response ) ) {
224 $this->throw( __( 'Template pack download failed', 'templately' ) . $response->get_error_message() );
225 }
226
227 $this->sse_log( 'download', __( 'Template is getting ready', 'templately' ), 57 );
228
229 $session_id = uniqid();
230 $this->dir_path = $this->tmp_dir . $session_id . DIRECTORY_SEPARATOR;
231 $this->filePath = $this->tmp_dir . "{$session_id}.zip";
232 $this->session_id = $session_id;
233
234 $this->update_session_data( [ 'session_id' => $this->session_id ] );
235
236 if ( file_put_contents( $this->filePath, $response['body'] ) ) { // phpcs:ignore
237 $this->sse_log( 'download', __( 'Template is getting ready', 'templately' ), 100 );
238
239 $this->unzip();
240 } else {
241 $this->throw( __( 'Downloading Failed. Please try again', 'templately' ) );
242 }
243 }
244
245 /**
246 * @throws Exception
247 */
248 protected function unzip() {
249 if ( ! WP_Filesystem() ) {
250 $this->throw( __( 'WP_Filesystem cannot be initialized', 'templately' ) );
251 }
252
253 $unzip = unzip_file( $this->filePath, $this->dir_path );
254
255 if ( is_wp_error( $unzip ) ) {
256 $this->throw( sprintf( __( 'Unzipping failed: %s', 'templately' ), $unzip->get_error_message() ) );
257 }
258
259 if ( $unzip ) {
260 unlink( $this->filePath );
261 }
262 }
263
264 /**
265 * @throws Exception
266 */
267 private function read_manifest() {
268 $manifest_content = file_get_contents( $this->dir_path . DIRECTORY_SEPARATOR . 'manifest.json' );
269 if ( empty( $manifest_content ) ) {
270 $this->throw( __( 'Cannot be imported, as the manifest file is corrupted', 'templately' ) );
271 }
272
273 $this->manifest = json_decode( $manifest_content, true );
274 $this->removeLog( 'temp' );
275
276 // TODO: Read & Broadcast the LOG for waiting list
277 // $this->sse_log( 'plugin', 'Installing required plugins', '--', 'updateLog', 'processing' );
278 // // $this->sse_log( 'extra-content', 'Import Extra Contents (i.e: Forms)', '--', 'updateLog', 'processing' );
279 // $this->sse_log( 'templates', 'Import Templates (i.e: Header, Footer etc)', '--', 'updateLog', 'processing' );
280 // // $this->sse_log( 'content', 'Import Pages, Posts etc', '--', 'updateLog', 'processing' );
281 // $this->sse_log( 'wp-content', 'Importing Pages, Posts, Navigation, etc', '--', 'updateLog', 'processing' );
282 // $this->sse_log( 'finalize', 'Finalizing Your Imports', '--', 'updateLog', 'processing' );
283 }
284
285 private function skipped_plugin(): bool {
286 return empty( $this->request_params['plugins'] ) || ! is_array( $this->request_params['plugins'] );
287 }
288
289 private function install_dependencies() {
290 if ( ! empty( $this->request_params['theme'] ) && is_array( $this->request_params['theme'] ) ) {
291 // activate theme
292 $theme = $this->request_params['theme'];
293
294 $this->before_install_hook();
295
296 if (isset($theme['stylesheet'])) {
297 // do_action('before_theme_activation', $theme); // Trigger action before theme activation
298 $this->sse_log( 'theme', 'Installing and activating theme: ' . $theme['name'], 0 );
299 $plugin_status = Installer::get_instance()->install_and_activate_theme( $theme['stylesheet'] );
300
301 if (!$plugin_status['success']) {
302 $this->sse_message( [
303 'action' => 'updateLog',
304 'status' => 'error',
305 'message' => "Failed to activate theme: " . $theme['name'],
306 'type' => "theme",
307 'progress' => 0
308 ] );
309 $this->dependency_data['theme'] = [
310 'success' => false,
311 'name' => $theme['name'],
312 'slug' => $theme['stylesheet'],
313 'message' => $plugin_status['message'] ?? ''
314 ];
315 } else {
316 // do_action('after_theme_activation', $theme); // Trigger action after theme activation
317
318 $this->sse_message( [
319 'action' => 'updateLog',
320 'status' => 'complete',
321 'message' => "Activated theme: " . $theme['name'],
322 'type' => "theme",
323 'progress' => 100
324 ] );
325 $this->dependency_data['theme'] = [
326 'success' => true,
327 'name' => $theme['name'],
328 'slug' => $theme['stylesheet'],
329 'message' => $plugin_status['message'] ?? ''
330 ];
331 }
332 }
333
334 $this->after_install_hook();
335 }
336 else {
337 $this->removeLog( 'theme' );
338 }
339 if ( ! empty( $this->request_params['plugins'] ) && is_array( $this->request_params['plugins'] ) ) {
340 // $this->sse_log( 'plugin', 'Installing Plugins', 1 );
341 $total_plugin = count( $this->request_params['plugins'] );
342
343 $total_plugin_installed = $total_plugin;
344 $_installed_plugins = 0;
345
346 $this->before_install_hook();
347
348 foreach ( $this->request_params['plugins'] as $dependency ) {
349 $this->sse_log( 'plugin', 'Installing required plugins: ' . $dependency['name'], floor( ( 100 * $_installed_plugins / $total_plugin ) ) );
350
351 $dependency['slug'] = $dependency['plugin_original_slug'];
352 $plugin_status = Installer::get_instance()->install( $dependency );
353
354 if ( ! $plugin_status['success'] ) {
355 $this->sse_message( [
356 'position' => 'plugin',
357 'action' => 'updateLog',
358 'status' => 'error',
359 'message' => 'Installation Failed: ' . $dependency['name'] . ' (' . ( $plugin_status['message'] ?? '' ) . ')',
360 'type' => "plugin_{$dependency['plugin_original_slug']}",
361 'progress' => 0
362 ] );
363 $this->dependency_data['plugins']['failed'][] = [
364 'name' => $dependency['name'],
365 'slug' => $dependency['slug'],
366 'link' => $dependency['link'],
367 'message' => $plugin_status['message'] ?? ''
368 ];
369 } else {
370 $_installed_plugins++;
371 $total_plugin_installed--;
372 }
373 }
374
375 $this->after_install_hook();
376
377 $this->sse_message( [
378 'action' => 'updateLog',
379 'status' => 'complete',
380 'message' => "Installed required plugins ($_installed_plugins/$total_plugin)",
381 'type' => "plugin",
382 'progress' => 100
383 ] );
384 $this->dependency_data['plugins']['total'] = $total_plugin;
385 $this->dependency_data['plugins']['succeed'] = $_installed_plugins;
386 } else {
387 $this->removeLog( 'plugin' );
388 }
389
390 // $this->sse_log( 'plugin', 'Skipped Installing Plugins', '--', 'updateLog', 'skipped' );
391 }
392
393 private function before_install_hook() {
394 remove_all_actions( 'wp_loaded' );
395 remove_all_actions( 'after_setup_theme' );
396 remove_all_actions( 'plugins_loaded' );
397 remove_all_actions( 'init' );
398
399 // making sure so that no redirection happens during plugin installation and hooks triggered bellow.
400 add_filter('wp_redirect', '__return_false', 999);
401 }
402
403 private function after_install_hook() {
404 do_action( 'wp_loaded' );
405 do_action( 'after_setup_theme' );
406 do_action( 'plugins_loaded' );
407 do_action( 'init' );
408 }
409
410 /**
411 * @throws Exception
412 */
413 private function start_content_import() {
414 add_filter('upload_mimes', array($this, 'allow_svg_upload'));
415 add_filter('elementor/files/allow_unfiltered_upload', '__return_true');
416
417 $import = new Import( $this );
418 $imported_data = $import->run();
419
420 $this->sse_message( [
421 'type' => 'complete',
422 'action' => 'complete',
423 'results' => $this->normalize_imported_data( $imported_data )
424 ] );
425 }
426
427 private function normalize_imported_data( $data ) {
428 $templates = ! empty( $data['templates']['succeed'] ) ? count( $data['templates']['succeed'] ) : 0;
429 $template_types = ! empty( $data['templates']['template_types'] ) ? $data['templates']['template_types'] : [];
430
431 $post_types = [];
432 $content_templates = [];
433 if ( ! empty( $data['content'] ) && is_array( $data['content'] ) ) {
434 foreach ( $data['content'] as $type => $type_data ) {
435 $content_templates[ $type ] = ! empty( $type_data['succeed'] ) ? count( $type_data['succeed'] ) : 0;
436 $post_types[] = $this->get_post_type_label_by_slug($type);
437 }
438 }
439
440 $contents = [];
441 if ( ! empty( $data['wp-content'] ) && is_array( $data['wp-content'] ) ) {
442 foreach ( $data['wp-content'] as $type => $type_data ) {
443 $contents[ $type ] = ! empty( $type_data['succeed'] ) ? count( $type_data['succeed'] ) : 0;
444 if(!in_array($type, ['wp_navigation', 'nav_menu_item'])){
445 $post_types[] = $this->get_post_type_label_by_slug($type);
446 }
447 }
448 }
449
450 return [
451 'templates' => $templates,
452 'contents' => $content_templates,
453 'wp-content' => $contents,
454 'post_types' => $post_types,
455 'template_types' => $template_types,
456 'dependency_data' => $this->dependency_data,
457 ];
458 }
459
460 public function get_request_params() {
461 return $this->request_params;
462 }
463
464 private function revert() {
465 // $request = $this->get_request_params();
466 // if ( isset( $request['revert'] ) && $request['revert'] ) {
467 // // TODO: Implement the Revert Process.
468 // }
469 }
470
471 public function redirect_for_archives( $link, $post_id ) {
472 $archive_settings = get_option( 'templately_post_archive' );
473 if ( ! empty( $archive_settings ) && intval( $archive_settings['post_id'] ) === intval( $post_id ) ) {
474 $link = str_replace( $post_id, $archive_settings['archive_id'], $link );
475 }
476
477 return $link;
478 }
479
480 public function enqueue_template_assets( $path, $url ) {
481 $using_templately_builder = get_query_var( 'using_templately_template' );
482 if ( $using_templately_builder && function_exists( 'templately' ) ) {
483 $template_locations = [ 'header', 'footer', 'archive', 'single' ];
484 foreach ( $template_locations as $location ) {
485 $template = templately()->theme_builder::$conditions_manager->get_templates_by_location( $location );
486 if ( empty( $template ) ) {
487 return;
488 }
489 $template = array_pop( $template );
490 if ( $template->platform == 'gutenberg' ) {
491 $template = is_array( $template ) ? array_pop( $template ) : $template;
492 if ( ! file_exists( $path . 'eb-style-' . $template->get_main_id() . '.min.css' ) ) {
493 $st = EbStyleHandler::init();
494 $post = get_post( $template->get_main_id() );
495 $st->eb_write_css_from_content( $post, $post->ID, parse_blocks( $post->post_content ) );
496 }
497 wp_enqueue_style( 'templately-' . $location . '-' . $template->get_main_id(), $url . 'eb-style-' . $template->get_main_id() . '.min.css', [], substr( md5( microtime( true ) ), 0, 10 ) );
498 }
499 }
500 }
501 }
502
503 public function upload_logo() {
504 // Check if the upload file exists
505 if (isset($_FILES['logo'])) {
506 // Require the needed files if not already loaded
507 if (!function_exists('wp_handle_upload')) {
508 require_once(ABSPATH . 'wp-admin/includes/file.php');
509 }
510
511 // Manually upload the file
512 $uploadedfile = $_FILES['logo'];
513 $upload_overrides = array('test_form' => false);
514 $movefile = wp_handle_upload($uploadedfile, $upload_overrides);
515
516 if ($movefile && !isset($movefile['error'])) {
517 // The file was uploaded successfully, now we need to resize it
518 if (!function_exists('wp_get_image_editor')) {
519 require_once(ABSPATH . 'wp-admin/includes/image.php');
520 }
521
522 $filetype = wp_check_filetype($movefile['file']);
523
524 if ($filetype['ext'] == 'jpg' || $filetype['ext'] == 'jpeg' || $filetype['ext'] == 'png') {
525 $image = wp_get_image_editor($movefile['file']);
526 if (!is_wp_error($image)) {
527 $size = $image->get_size();
528 if ($size['width'] > 200 || $size['height'] > 80) {
529 $image->resize(200, 80, false);
530 $image->save($movefile['file']);
531 }
532 }
533 }
534
535 // Prepare an array of post data for the attachment
536 $attachment = array(
537 'guid' => $movefile['url'],
538 'post_mime_type' => $movefile['type'],
539 'post_title' => preg_replace('/\.[^.]+$/', '', basename($movefile['file'])),
540 'post_content' => '',
541 'post_status' => 'inherit'
542 );
543
544 // Insert the attachment
545 $attach_id = wp_insert_attachment($attachment, $movefile['file']);
546
547 // Generate the metadata for the attachment and update the database record
548 $attach_data = wp_generate_attachment_metadata($attach_id, $movefile['file']);
549 wp_update_attachment_metadata($attach_id, $attach_data);
550
551 // Return the URL of the uploaded file
552 return json_encode( [
553 'id' => $attach_id,
554 'url' => $movefile['url']
555 ]);
556 } else {
557 /**
558 * Error generated by _wp_handle_upload()
559 * @see _wp_handle_upload() in wp-admin/includes/file.php
560 */
561 // return $movefile['error'];
562 }
563 }
564
565 return null;
566 }
567
568 public function allow_svg_upload($mimes) {
569 // Allow SVG
570 $mimes['svg'] = 'image/svg+xml';
571 return $mimes;
572 }
573
574 public function register_shutdown(){
575 $last_error = error_get_last();
576 if ($last_error && $last_error['type'] === E_ERROR) {
577 if (defined('WP_DEBUG') && WP_DEBUG && !empty($last_error['message'])) {
578 $full_message = $last_error['message'];
579
580 // Split the message at the first newline to remove the stack trace
581 $message_parts = preg_split('/\n/', $full_message);
582
583 // The error message is the first part
584 $error_message = $message_parts[0];
585 } else {
586 // Generic error message
587 $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');
588 }
589
590 // Handle the error, e.g. log it or display a message to the user
591 $this->sse_message( [
592 'action' => 'error',
593 'status' => 'error',
594 'type' => "error",
595 'title' => __("Oops!", "templately"),
596 'message' => $error_message,
597 // 'position' => 'plugin',
598 // 'progress' => '--',
599 ] );
600 }
601
602 Helper::log( "Shutdown:....." );
603 Helper::log( "connection_status: " . $this->getConnectionStatusText() );
604 Helper::log( $last_error );
605 }
606
607 protected function getConnectionStatusText() {
608 $status = connection_status();
609 switch ($status) {
610 case CONNECTION_NORMAL:
611 return "Normal";
612 case CONNECTION_ABORTED:
613 return "Aborted";
614 case CONNECTION_TIMEOUT:
615 return "Timeout";
616 default:
617 return "Unknown";
618 }
619 }
620
621 protected function get_post_type_label_by_slug($slug) {
622 $post_type_obj = get_post_type_object($slug);
623 if($post_type_obj) {
624 return $post_type_obj->label;
625 }
626 return null;
627 }
628
629 public function filter_content( $content ) {
630 if(is_singular()){
631 global $post;
632 if(!empty($post) && $post->post_type === 'templately_library'){
633 if(in_array(get_post_meta($post->ID, '_templately_template_type', true), ['header', 'footer'])){
634 return '';
635 }
636 }
637 }
638 return $content;
639 }
640 }