File: /home/h340499/public_html/wp-content/plugins/duplicator-pro/src/Models/TemplateEntity.php
<?php
/**
* @package Duplicator\Models
* @copyright (c) 2022, Snap Creek LLC
*/
namespace Duplicator\Models;
use Duplicator\Utils\Logging\DupLog;
use Duplicator\Core\Models\AbstractEntity;
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
use Duplicator\Core\Models\TraitGenericModelList;
use Duplicator\Core\Models\UpdateFromInputInterface;
use Duplicator\Core\Views\TplMng;
use Duplicator\Installer\Package\ArchiveDescriptor;
use Duplicator\Libs\Snap\SnapUtil;
use Duplicator\Package\Create\BuildComponents;
use Duplicator\Package\NameFormat;
use Duplicator\Package\Recovery\RecoveryStatus;
use Duplicator\Utils\Crypt\CryptBlowfish;
use Duplicator\Libs\WpUtils\PathUtil;
use Duplicator\Package\Archive\PackageArchive;
use Duplicator\Utils\Settings\ModelMigrateSettingsInterface;
use Exception;
use ReflectionClass;
class TemplateEntity extends AbstractEntity implements UpdateFromInputInterface, ModelMigrateSettingsInterface
{
use TraitGenericModelList {
getAll as traitGetAll;
}
/** @var string */
public $name = '';
/** @var string */
public $package_name_format = NameFormat::DEFAULT_FORMAT;
/** @var string */
public $notes = '';
//MULTISITE:Filter
/** @var int[] */
public $filter_sites = [];
//ARCHIVE:Files
/** @var bool */
public $archive_export_onlydb = false;
/** @var bool */
public $archive_filter_on = false;
/** @var string */
public $archive_filter_dirs = '';
/** @var string */
public $archive_filter_exts = '';
/** @var string */
public $archive_filter_files = '';
/** @var bool */
public $archive_filter_names = false;
/** @var string[] */
public $components = [];
//ARCHIVE:Database
/** @var bool */
public $database_filter_on = false; // Enable Table Filters
/** @var bool */
public $databasePrefixFilter = false; // If true exclude tables without prefix
/** @var bool */
public $databasePrefixSubFilter = false; // If true exclude unexisting subsite id tables
/** @var string */
public $database_filter_tables = ''; // List of filtered tables
/** @var string */
public $database_compatibility_modes = ''; // Older style sql compatibility
//INSTALLER
//Setup
/** @var int */
public $installer_opts_secure_on = 0; // Enable Password Protection
/** @var string */
public $installerPassowrd = ''; // Password Protection password
/** @var bool */
public $installer_opts_skip_scan = false; // Skip Scanner
//Basic DB
/** @var string */
public $installer_opts_db_host = ''; // MySQL Server Host
/** @var string */
public $installer_opts_db_name = ''; // Database
/** @var string */
public $installer_opts_db_user = ''; // User
//cPanel Login
/** @var bool */
public $installer_opts_cpnl_enable = false;
/** @var string */
public $installer_opts_cpnl_host = '';
/** @var string */
public $installer_opts_cpnl_user = '';
/** @var string */
public $installer_opts_cpnl_pass = '';
//cPanel DB
/** @var string */
public $installer_opts_cpnl_db_action = 'create';
/** @var string */
public $installer_opts_cpnl_db_host = '';
/** @var string */
public $installer_opts_cpnl_db_name = '';
/** @var string */
public $installer_opts_cpnl_db_user = '';
//Brand
/** @var int */
public $installer_opts_brand = -2;
/** @var bool */
public $is_default = false;
/** @var bool */
public $is_manual = false;
/**
* Class constructor
*/
public function __construct()
{
$this->name = __('New Template', 'duplicator-pro');
$this->components = BuildComponents::COMPONENTS_DEFAULT;
}
/**
* Entity type
*
* @return string
*/
public static function getType(): string
{
return 'DUP_PRO_Package_Template_Entity';
}
/**
* Will be called, automatically, when Serialize
*
* @return array<string, mixed>
*/
public function __serialize(): array
{
$data = JsonSerialize::serializeToData($this, JsonSerialize::JSON_SKIP_MAGIC_METHODS | JsonSerialize::JSON_SKIP_CLASS_NAME);
$data['installerPassowrd'] = CryptBlowfish::encryptIfAvaiable($this->installerPassowrd, null, true);
return $data;
}
/**
* Serialize
*
* Wakeup method.
*
* @return void
*/
public function __wakeup(): void
{
$this->installerPassowrd = CryptBlowfish::decryptIfAvaiable($this->installerPassowrd, null, true);
if (is_array($this->database_compatibility_modes)) {
// for old version compatibility
$this->database_compatibility_modes = implode(',', $this->database_compatibility_modes);
}
$this->archive_filter_dirs = (string) $this->archive_filter_dirs;
$this->archive_filter_files = (string) $this->archive_filter_files;
$this->archive_filter_exts = (string) $this->archive_filter_exts;
$this->archive_filter_on = filter_var($this->archive_filter_on, FILTER_VALIDATE_BOOLEAN);
$this->database_filter_on = filter_var($this->database_filter_on, FILTER_VALIDATE_BOOLEAN);
if (is_string($this->filter_sites)) {
$this->filter_sites = [];
}
}
/**
* To export data
*
* @return array<string,mixed>
*/
public function settingsExport(): array
{
return JsonSerialize::serializeToData($this, JsonSerialize::JSON_SKIP_MAGIC_METHODS | JsonSerialize::JSON_SKIP_CLASS_NAME);
}
/**
* Update object properties from import data
*
* @param array<string, mixed> $data data to import
* @param string $dataVersion version of data
* @param array<string, mixed> $extraData extra data, useful form id mapping etc.
*
* @return bool True if success, otherwise false
*/
public function settingsImport(array $data, string $dataVersion, array $extraData = []): bool
{
$skipProps = ['id'];
$reflect = new ReflectionClass(self::class);
$props = $reflect->getProperties();
foreach ($props as $prop) {
if (in_array($prop->getName(), $skipProps)) {
continue;
}
if (!isset($data[$prop->getName()])) {
continue;
}
if (PHP_VERSION_ID < 80100) {
$prop->setAccessible(true);
}
$prop->setValue($this, $data[$prop->getName()]);
}
if (!isset($data['components'])) {
// Allow import of older templsates that did not have Backup components
if ($this->archive_export_onlydb) {
$this->components = [BuildComponents::COMP_DB];
} else {
$this->components = BuildComponents::COMPONENTS_DEFAULT;
}
}
return true;
}
/**
* Create default template
*
* @return void
*/
public static function createDefault(): void
{
if (self::getDefaultTemplate() == null) {
$template = new self();
$template->name = __('Default', 'duplicator-pro');
$template->notes = __('The default template.', 'duplicator-pro');
$template->is_default = true;
$template->save();
DupLog::trace('Created default template');
} else {
// Update it
DupLog::trace('Default template already exists so not creating');
}
}
/**
*
* @return bool
*/
public function isRecoveable()
{
$status = new RecoveryStatus($this);
return $status->isRecoveable();
}
/**
* Display HTML info
*
* @param bool $isList is list
*
* @return void
*/
public function recoveableHtmlInfo(bool $isList = false): void
{
TplMng::getInstance()->render('parts/recovery/widget/recoverable-template-info', [
'template' => $this,
'isList' => $isList,
]);
}
/**
* Set data from query input
*
* @param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV, SnapUtil::INPUT_REQUEST
*
* @return bool true on success or false on failure
*/
public function setFromInput(int $type): bool
{
$input = SnapUtil::getInputFromType($type);
$this->setFromArrayKey(
$input,
function ($key, $val) {
if (is_string($val)) {
$val = stripslashes($val);
}
return (is_scalar($val) ? SnapUtil::sanitizeNSChars($val) : $val);
}
);
$this->components = BuildComponents::getFromInput($input);
$this->database_filter_tables = isset($input['dbtables-list']) ? SnapUtil::sanitizeNSCharsNewlineTrim($input['dbtables-list']) : '';
if (isset($input['filter-paths'])) {
$filterPaths = SnapUtil::sanitizeNSChars($input['filter-paths']);
$this->archive_filter_dirs = PackageArchive::parseDirectoryFilter($filterPaths);
$this->archive_filter_files = PackageArchive::parseFileFilter($filterPaths);
} else {
$this->archive_filter_dirs = '';
$this->archive_filter_files = '';
}
if (isset($input['filter-exts'])) {
$post_filter_exts = SnapUtil::sanitizeNSCharsNewlineTrim($input['filter-exts']);
$this->archive_filter_exts = PackageArchive::parseExtensionFilter($post_filter_exts);
} else {
$this->archive_filter_exts = '';
}
$this->filter_sites = !empty($input['_mu_exclude']) ? $input['_mu_exclude'] : [];
//Archive
$this->archive_filter_on = isset($input['filter-on']);
$this->database_filter_on = isset($input['dbfilter-on']);
$this->databasePrefixFilter = isset($input['db-prefix-filter']);
$this->databasePrefixSubFilter = isset($input['db-prefix-sub-filter']);
$this->archive_filter_names = isset($input['archive_filter_names']);
//Installer
$this->installer_opts_secure_on = filter_input(INPUT_POST, 'secure-on', FILTER_VALIDATE_INT);
switch ($this->installer_opts_secure_on) {
case ArchiveDescriptor::SECURE_MODE_NONE:
case ArchiveDescriptor::SECURE_MODE_INST_PWD:
case ArchiveDescriptor::SECURE_MODE_ARC_ENCRYPT:
break;
default:
throw new Exception(__('Select valid secure mode', 'duplicator-pro'));
}
$this->installer_opts_skip_scan = isset($input['_installer_opts_skip_scan']);
$this->installer_opts_cpnl_enable = isset($input['installer_opts_cpnl_enable']);
$this->installerPassowrd = SnapUtil::sanitizeNSCharsNewline(stripslashes($input['secure-pass']));
$this->notes = SnapUtil::sanitizeNSCharsNewlineTrim(stripslashes($input['package-notes']));
return true;
}
/**
* Copy template from id
*
* @param int<0, max> $templateId template id
*
* @return void
*/
public function copyFromSourceId(int $templateId): void
{
if (($source = self::getById($templateId)) === false) {
throw new Exception('Can\'t get tempalte id' . $templateId);
}
$skipProps = [
'id',
'is_manual',
'is_default',
];
if ($this->getId() > 0) {
$skipProps[] = 'name';
}
$reflect = new ReflectionClass($this);
$props = $reflect->getProperties();
foreach ($props as $prop) {
if (in_array($prop->getName(), $skipProps)) {
continue;
}
if (PHP_VERSION_ID < 80100) {
$prop->setAccessible(true);
}
$prop->setValue($this, $prop->getValue($source));
}
$source_template_name = $source->is_manual ? __("Active Build Settings", 'duplicator-pro') : $source->name;
if ($this->getId() < 0) {
$this->name = sprintf(__('%1$s - Copy', 'duplicator-pro'), $source_template_name);
}
}
/**
* Gets a list of core WordPress folders that have been filtered
*
* @return string[] Returns and array of folders paths
*/
public function getWordPressCoreFilteredFoldersList(): array
{
return array_intersect(explode(';', $this->archive_filter_dirs), PathUtil::getWPCoreDirs());
}
/**
* Is any of the WordPress core folders in the folder filter list
*
* @return bool Returns true if a WordPress core path is being filtered
*/
public function isWordPressCoreFolderFiltered(): bool
{
return count($this->getWordPressCoreFilteredFoldersList()) > 0;
}
/**
* Get all entities of current type
*
* @param int<0, max> $page current page, if $pageSize is 0 o 1 $pase is the offset
* @param int<0, max> $pageSize page size, 0 return all entities
* @param callable $sortCallback sort function on items result
* @param callable $filterCallback filter on items result
* @param array{'col': string, 'mode': string} $orderby query ordder by
*
* @return static[]|false return entities list of false on failure
*/
public static function getAll(
$page = 0,
$pageSize = 0,
$sortCallback = null,
$filterCallback = null,
$orderby = [
'col' => 'id',
'mode' => 'ASC',
]
) {
if (is_null($sortCallback)) {
$sortCallback = function (self $a, self $b): int {
if ($a->is_default) {
return -1;
} elseif ($b->is_default) {
return 1;
} else {
return strcasecmp($a->name, $b->name);
}
};
}
return self::traitGetAll($page, $pageSize, $sortCallback, $filterCallback, $orderby);
}
/**
* Return list template json encoded data for javascript
*
* @param bool $echo echo data or return
*
* @return string
*/
public static function getTemplatesFrontendListData(bool $echo = true): string
{
$templates = self::getAll();
$result = JsonSerialize::serialize($templates, JsonSerialize::JSON_SKIP_MAGIC_METHODS | JsonSerialize::JSON_SKIP_CLASS_NAME);
if ($echo) {
echo $result; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return '';
} else {
return $result;
}
}
/**
* Get all entities of current type
*
* @param int<0, max> $page current page, if $pageSize is 0 o 1 $pase is the offset
* @param int<0, max> $pageSize page size, 0 return all entities
*
* @return static[]|false return entities list of false on failure
*/
public static function getAllWithoutManualMode(
$page = 0,
$pageSize = 0
) {
$filterManualCallback = (fn(self $obj): bool => $obj->is_manual === false);
return self::getAll($page, $pageSize, null, $filterManualCallback);
}
/**
* Get default template if exists
*
* @return ?self
*/
public static function getDefaultTemplate()
{
$templates = self::getAll();
foreach ($templates as $template) {
if ($template->is_default) {
return $template;
}
}
return null;
}
/**
* Return manual template
*
* @return self
*/
public static function getManualTemplate(): self
{
static $manualTemplate = null;
if ($manualTemplate === null) {
$templates = self::getAll();
foreach ($templates as $template) {
if ($template->is_manual) {
$manualTemplate = $template;
break;
}
}
if ($manualTemplate === null) {
$manualTemplate = self::createManual();
}
}
return $manualTemplate;
}
/**
* Create manual mode template
*
* @return self
*/
protected static function createManual(): self
{
$template = new self();
$template->name = __('[Manual Mode]', 'duplicator-pro');
$template->notes = '';
$template->is_manual = true;
$template->save();
DupLog::trace('Created manual mode template');
return $template;
}
}