| Server IP : 108.167.142.43 / Your IP : 216.73.217.58 Web Server : Apache System : Linux gator4174.hostgator.com 5.14.0-687.39.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Aug 18 06:09:16 EDT 2026 x86_64 User : delarice ( 2406) PHP Version : 8.3.36 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home2/delarice/www/wp-content/plugins/templately/modules/full-site-import/ |
Upload File : |
<?php
/**
* remove 'action' => 'continue',
* way to retry
* way to skip if failed multiple times
*
* @todo: on runner check for timeout and retry
* @todo: use ErrorException on runner to skip item when error occurs: not useful
*
*/
namespace Templately\Modules\FullSiteImport;
use Elementor\Plugin;
use Error;
use Exception;
use Templately\Modules\FullSiteImport\Exception\FatalErrorException;
use Templately\Modules\FullSiteImport\Exception\RetryableErrorException;
use Templately\Modules\FullSiteImport\Exception\UnknownErrorException;
use Templately\Modules\FullSiteImport\Runners\Finalizer;
use Templately\Modules\FullSiteImport\Utils\LogHandler;
use Templately\Modules\FullSiteImport\Utils\Utils;
use Templately\Modules\FullSiteImport\Utils\SessionData;
use Templately\Modules\FullSiteImport\Utils\AIUtils;
use Templately\Utils\Base;
use Templately\Utils\Response\AjaxResponder;
use Templately\Utils\Helper;
use Templately\Utils\Installer;
use Templately\Utils\Options;
class FullSiteImport extends Base {
use Concerns\LogHelper;
use Concerns\ThrowsErrors;
use Concerns\HandlesSession;
use Concerns\DownloadsPack;
use Concerns\RunsImport;
use Concerns\ReportsStatus;
use Concerns\NormalizesResult;
use Concerns\CleansUp;
use Concerns\TracksRevert;
use Concerns\HandlesAiEndpoints;
const SESSION_OPTION_KEY = 'templately_import_session';
/**
* Extensions a pack carries that WordPress does not accept as an upload.
*
* Everything else comes from get_allowed_mime_types(), so this list only has
* to name what the pack format adds on top of it:
*
* - `json` — every pack is built from JSON (manifest, templates, content),
* and WordPress has never allowed .json uploads.
* - `xml` — the WXR files under wp-content/. Also not an allowed upload:
* core hands .xml to the importer plugin rather than the media library.
* - `ai` — the marker segment in `{id}.ai.json`, an AI-generated page. Not
* the Illustrator format.
* - `svg` — packs ship SVG artwork. This plugin already allows SVG uploads
* for the duration of an import (see allow_svg_upload()), but that filter is
* only added in start_content_import(), which runs AFTER extraction — so at
* sweep time get_allowed_mime_types() does not have it and the pack's SVGs
* would be deleted. It has to be named here.
* - fonts — a pack may ship webfonts, and none of these are upload types.
* Passive assets; nothing here is executable by any server.
*
* Adding to this list is how a new pack file type gets through. It fails
* closed and logs, so a missing entry shows up as a dropped file in the
* import log — not as a silent hole.
*
* Lives on the class rather than the DownloadsPack trait that reads it:
* trait constants are PHP 8.2+, and the advertised floor here is 7.2.
*/
const PACK_EXTENSIONS = [
'json', 'xml', 'ai',
'svg', 'svgz',
'woff', 'woff2', 'ttf', 'otf', 'eot',
];
public $manifest;
protected $export;
private $version = '1.0.0';
public $download_key;
protected $dev_mode = false;
protected $api_key = '';
protected $session_id = '';
protected $documents_data = [];
private $is_import_status_handled = false;
public $dir_path;
protected $filePath;
protected $tmp_dir = null;
public $request_params = [];
// Polling-specific property for ai_poll_template()
private $polling_is_last_part = null;
public function __construct() {
$this->dev_mode = defined('TEMPLATELY_DEV') && TEMPLATELY_DEV;
$this->api_key = Options::get_instance()->get('api_key');
// Independent endpoint handlers (registered through the same nonce/cap wrapper).
// `import_revert` moved to modules/import-revert (2026-07-23) — it arrives
// through the templately_fsi_ajax_handlers seam below, like the feedback shims.
$asset = new Ajax\AssetController();
$pack_info = new Ajax\PackInfoController();
$this->add_ajax_action('import_settings', $this);
$this->add_ajax_action('create_session_and_download', $this);
$this->add_ajax_action('import_status', $this);
$this->add_ajax_action('import', $this);
$this->add_ajax_action('import_info', $pack_info);
$this->add_ajax_action('import_global_settings', $pack_info);
$this->add_ajax_action('google_font', $asset);
$this->add_ajax_action('ai_get_json', $this);
$this->add_ajax_action('ai_poll_template', $this);
$this->add_ajax_action('merge_settings_with_template', $asset);
// Cross-module AJAX handlers arrive through this seam instead of direct class
// imports (e.g. post-import-feedback supplies its two deprecated shims — see
// that module.php). Applied on 'init' rather than here because FSI boots before
// the supplying modules (alphabetical discovery order); admin-ajax.php fires
// wp_ajax_* well after 'init', so registration is never late.
add_action('init', function () {
$handlers = apply_filters('templately_fsi_ajax_handlers', []);
foreach ($handlers as $action => $handler) {
$this->add_ajax_action($action, $handler);
}
});
add_action('admin_init', [$this, 'admin_init']);
// Cleanup scheduling moved to `modules/utilities/` (spec 052). This module
// contributes tasks to the one shared sweep instead of running its own
// daily event, which pruned options only and never touched the disk.
if(isset($_GET['action']) && ($_GET['action'] == 'templately_pack_import' || $_GET['action'] == 'templately_pack_import_status')) {
add_filter('wp_redirect', '__return_false', 999);
}
if ($this->dev_mode) {
add_filter('http_request_host_is_external', '__return_true');
add_filter('http_request_args', function ($args) {
$args['sslverify'] = false;
return $args;
});
}
}
public function add_ajax_action($action, $object) {
add_action("wp_ajax_templately_pack_$action", function() use ($action, $object) {
// The nonce + capability gate lives in AjaxResponder::guard() so that it
// answers with the same envelope (and real machine codes) as everything
// else. It sends and dies on failure. Spec 043 / PRD PHP-5.
if (!AjaxResponder::guard('templately_nonce', ['install_plugins', 'install_themes'])) {
return;
}
// Every AI/FSI step arrives through here, so this is the one place that can say
// WHICH step a run reached and how the request ended. Without it a run that
// stopped between two steps was indistinguishable from one that never started
// the next.
//
// The end is reported from a shutdown handler and NOT gated on "did the handler
// return", because these handlers answer through AjaxResponder, which `die()`s —
// so the normal, successful path never comes back here. A flag set after
// `call_user_func()` would therefore mark every success as a failure. The
// shutdown handler states the facts instead and lets the reader judge:
// `connection_aborted=1` means the browser hung up (ignore_user_abort is 0, so
// PHP stops at the next write, silently), and a non-null `last_error` means PHP
// died.
$started = microtime( true );
Helper::log( sprintf( 'ajax: templately_pack_%s — start', $action ), 'fsi_ajax', 'info' );
register_shutdown_function( function () use ( $action, $started ) {
Helper::log(
sprintf(
'ajax: templately_pack_%s — ended after %.1fs (connection_aborted=%d, http_code=%s, last_error=%s)',
$action,
microtime( true ) - $started,
connection_aborted(),
function_exists( 'http_response_code' ) ? (string) http_response_code() : '?',
wp_json_encode( error_get_last() )
),
'fsi_ajax',
connection_aborted() || null !== error_get_last() ? 'warning' : 'info'
);
} );
// Call the actual handler method on the registered object (defaults to $this).
call_user_func([$object, $action]);
});
}
private function skipped_plugin(): bool {
return empty($this->request_params['plugins']) || !is_array($this->request_params['plugins']);
}
public function get_request_params() {
return $this->request_params;
}
/**
* Kept on FullSiteImport for backward compatibility (called as FullSiteImport::has_revert()).
* The revert surface moved to modules/import-revert (2026-07-23), which answers
* the `templately_has_revert` filter — no class reference in this direction.
*/
public static function has_revert() {
return (bool) apply_filters( 'templately_has_revert', false );
}
public function allow_svg_upload($mimes) {
// Allow SVG
$mimes['svg'] = 'image/svg+xml';
return $mimes;
}
}