first commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<Files *.php>
|
||||
Order Deny,Allow
|
||||
Deny from all
|
||||
</Files>
|
||||
|
||||
<Files index.php>
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</Files>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,681 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Bootstrap;
|
||||
|
||||
use Duplicator\Installer\Bootstrap\BootstrapRunner;
|
||||
use Duplicator\Libs\Shell\Shell;
|
||||
use Exception;
|
||||
use ZipArchive;
|
||||
|
||||
class BootstrapUtils
|
||||
{
|
||||
/**
|
||||
* Check if php.ini value is changeable
|
||||
*
|
||||
* @param string $setting php setting
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isIniValChangeable($setting): bool
|
||||
{
|
||||
static $ini_all;
|
||||
if (!isset($ini_all)) {
|
||||
$ini_all = false;
|
||||
// Sometimes `ini_get_all()` is disabled via the `disable_functions` option for "security purposes".
|
||||
if (function_exists('ini_get_all')) {
|
||||
$ini_all = ini_get_all();
|
||||
}
|
||||
}
|
||||
if (isset($ini_all[$setting]['access']) && (INI_ALL === ($ini_all[$setting]['access'] & 7) || INI_USER === ($ini_all[$setting]['access'] & 7))) {
|
||||
return true;
|
||||
}
|
||||
if (!is_array($ini_all)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check php version
|
||||
*
|
||||
* @param string $minPhpVer PHP minimum version required
|
||||
|
||||
* @return void
|
||||
*/
|
||||
public static function phpVersionCheck($minPhpVer): void
|
||||
{
|
||||
if (version_compare(PHP_VERSION, $minPhpVer, '>=')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$match = null;
|
||||
$phpVersion = preg_match("#^\d+(\.\d+)*#", PHP_VERSION, $match) ? $match[0] : PHP_VERSION;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<title>Duplicator Professional - issue</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>DUPLICATOR PRO ISSUE: PHP <?php echo $minPhpVer; ?> REQUIRED</h1>
|
||||
<p>
|
||||
This server is running PHP: <b><?php echo $phpVersion; ?></b>. <i>A minimum of <b>PHP
|
||||
<?php echo $minPhpVer; ?></b> is required</i>.<br><br>
|
||||
<b>Contact your hosting provider or server administrator and let them know you would like to upgrade your PHP version.</b>
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<?php
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get libzip version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLibzipVersion()
|
||||
{
|
||||
static $libzipVersion = null;
|
||||
|
||||
if (is_null($libzipVersion)) {
|
||||
ob_start();
|
||||
if (function_exists('phpinfo')) {
|
||||
phpinfo(INFO_MODULES);
|
||||
}
|
||||
$info = ob_get_clean();
|
||||
|
||||
if (preg_match('/<td\s.*?>\s*(libzip.*\sver.+?)\s*<\/td>\s*<td\s.*?>\s*(.+?)\s*<\/td>/i', $info, $matches) !== 1) {
|
||||
$libzipVersion = "0";
|
||||
} else {
|
||||
$libzipVersion = $matches[2];
|
||||
}
|
||||
}
|
||||
|
||||
return $libzipVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if ZipArchive or shell zip is available
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isZipAvailable(): bool
|
||||
{
|
||||
return (self::isPhpZipAvailable() || self::isShellZipAvailable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if ZipArchive class is avaliable
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPhpZipAvailable()
|
||||
{
|
||||
return self::classExists(ZipArchive::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if ZipArchive class is avaliable
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isShellZipAvailable(): bool
|
||||
{
|
||||
return (self::getUnzipFilePath() !== false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if zip archive is encrypted
|
||||
*
|
||||
* @param string $path zip archive path
|
||||
* @param string $fileToCheck fil path to check (must be a existing file in archive)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isZipArchiveEncrypted($path, $fileToCheck)
|
||||
{
|
||||
if (self::isPhpZipAvailable()) {
|
||||
$zip = new ZipArchive();
|
||||
|
||||
if (($zipOpenRes = $zip->open($path)) !== true) {
|
||||
$message = "[ERROR] Couldn't open archive archive file with ZipArchive CODE[" . $zipOpenRes . "]";
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
if (($stats = $zip->statName($fileToCheck, ZipArchive::FL_NODIR)) == false) {
|
||||
throw new Exception('Formatting archive error, cannot find file ' . $fileToCheck);
|
||||
}
|
||||
|
||||
if (isset($stats['encryption_method'])) {
|
||||
// Before PHP 7.2 encryption_method don't exsts
|
||||
$isEncrypt = ($stats['encryption_method'] > 0);
|
||||
} else {
|
||||
$isEncrypt = ($zip->getFromIndex($stats['index']) === false);
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
return $isEncrypt;
|
||||
} elseif (self::isShellZipAvailable()) {
|
||||
return self::isZipArchiveEncryptedShellUnzip($path, $fileToCheck);
|
||||
} else {
|
||||
throw new Exception('Zip archve isn\'t avaliable');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if zip archive is encrypted by using shell exec and unzip
|
||||
*
|
||||
* @param string $path zip archive path
|
||||
* @param string $fileToCheck file to check (must be an existing file in INSTALLER_DIR_NAME folder)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isZipArchiveEncryptedShellUnzip($path, $fileToCheck)
|
||||
{
|
||||
$tempFolderName = "temp_0oA8wkOvxjKtngR_dir";
|
||||
$unzipFilepath = self::getUnzipFilePath();
|
||||
$unzipCommand = escapeshellcmd($unzipFilepath) .
|
||||
" -o " . escapeshellcmd($path) . " " .
|
||||
escapeshellcmd("dup-installer/$fileToCheck") .
|
||||
" -d " . escapeshellcmd(dirname($path)) . "/" . escapeshellcmd($tempFolderName) . "/ 2>&1";
|
||||
$output = Shell::runCommandBuffered($unzipCommand);
|
||||
$encrypted = true;
|
||||
if (file_exists(dirname($path) . "/$tempFolderName/dup-installer/$fileToCheck")) {
|
||||
$encrypted = false;
|
||||
}
|
||||
BootstrapUtils::rrmdir(dirname($path) . "/$tempFolderName");
|
||||
return $encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if password fits encrypted zip archive
|
||||
*
|
||||
* @param string $archivePath encrypted zip archive path
|
||||
* @param string $password user's input, password to check
|
||||
* @param string $fileToCheck file to check (must be an existing file in archive)
|
||||
* @param int $zipMode One of BootstrapRunner constants
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function zipArchivePasswordCheck($archivePath, $password, $fileToCheck, $zipMode)
|
||||
{
|
||||
if ($zipMode == BootstrapRunner::ZIP_MODE_NONE) {
|
||||
throw new Exception("NOTICE: ZipArchive and Shell Exec are not enabled on this server. Please " .
|
||||
"talk to your host or server admin about enabling " .
|
||||
"<a target='_blank' href='https://duplicator.com/knowledge-base/how-to-fix-installer-archive-extraction-issues'>ZipArchive</a> " .
|
||||
"or <a target='_blank' href='http://php.net/manual/en/function.shell-exec.php'>Shell Exec</a> " .
|
||||
"on this server or manually extract archive then choose Advanced > Manual Extract in installer.");
|
||||
}
|
||||
if ($zipMode == BootstrapRunner::ZIP_MODE_ARCHIVE) {
|
||||
$zip = new ZipArchive();
|
||||
|
||||
if (($zipOpenRes = $zip->open($archivePath)) !== true) {
|
||||
$message = "[ERROR] Couldn't open archive archive file with ZipArchive CODE[" . $zipOpenRes . "]";
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
if (($stats = $zip->statName(basename($fileToCheck), ZipArchive::FL_NODIR)) == false) {
|
||||
throw new Exception("Formatting archive error, cannot find the file " . basename($fileToCheck));
|
||||
}
|
||||
|
||||
$zip->setPassword($password);
|
||||
$result = $zip->getFromIndex($stats['index']);
|
||||
$zip->close();
|
||||
return $result;
|
||||
}
|
||||
if ($zipMode == BootstrapRunner::ZIP_MODE_SHELL) {
|
||||
if ($password == "") {
|
||||
return false;
|
||||
}
|
||||
$destinationDir = dirname($archivePath) . "/tmp";
|
||||
$unzip_filepath = self::getUnzipFilePath();
|
||||
if ($unzip_filepath == null) {
|
||||
throw new Exception("Could not find unzip app, and ZIP_MODE_SHELL is chosen.");
|
||||
}
|
||||
|
||||
$params = "-o -P " . escapeshellarg($password);
|
||||
$unzip_command = escapeshellcmd($unzip_filepath) . ' ' . $params . ' ' .
|
||||
escapeshellarg($archivePath) . ' ' .
|
||||
escapeshellarg($fileToCheck) .
|
||||
' -d ' . escapeshellarg($destinationDir) . ' 2>&1';
|
||||
$shellOutput = Shell::runCommandBuffered($unzip_command);
|
||||
|
||||
if ($shellOutput->getCode() < 0) {
|
||||
$errorMsg = "[ERROR] Shell exec unzip failed. Shell::runCommandBuffered returned false.";
|
||||
self::rrmdir($destinationDir);
|
||||
throw new Exception($errorMsg);
|
||||
}
|
||||
|
||||
if (file_exists($destinationDir . "/" . $fileToCheck)) {
|
||||
self::rrmdir($destinationDir);
|
||||
return true; // Password is correct
|
||||
}
|
||||
|
||||
$shellOutputAsString = $shellOutput->getOutputAsString();
|
||||
$matchResult = preg_match('/skipping:.*incorrect password/', $shellOutputAsString);
|
||||
if ($matchResult) {
|
||||
self::rrmdir($destinationDir);
|
||||
return false; // Incorrect password
|
||||
}
|
||||
|
||||
// Some other error happened
|
||||
$errorMsg = "[ERROR] Shell exec unzip failed. Output={$shellOutputAsString}";
|
||||
$matchResult = preg_match('/skipping:.*need PK compat./', $shellOutputAsString);
|
||||
if ($matchResult) {
|
||||
$errorMsg .= "</br>It looks like you haven't used 'shell zip' engine when you created this archive. "
|
||||
. "Either create new package and use 'shell zip' as archive engine, or "
|
||||
. "contact the hosting manager and ask them to activate the ZipArchive class, then try again.";
|
||||
} else {
|
||||
$errorMsg .= "</br>If you can't fix the problem with 'shell unzip', contact the hosting manager and "
|
||||
. "ask them to activate the ZipArchive class, then try again.";
|
||||
}
|
||||
self::rrmdir($destinationDir);
|
||||
throw new Exception($errorMsg);
|
||||
}
|
||||
throw new Exception("Unrecognised zipMode = $zipMode passed to function zipArchivePasswordCheck.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current url
|
||||
*
|
||||
* @param bool $queryString If true the query string will also be returned.
|
||||
* @param bool $requestUri if true check request uri
|
||||
* @param int $getParentDirLevel if 0 get current script name or parent folder, if 1 parent folder if 2 parent of parent folder ...
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentUrl($queryString = true, $requestUri = false, $getParentDirLevel = 0): string
|
||||
{
|
||||
// *** HOST
|
||||
if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'])) {
|
||||
$host = $_SERVER['HTTP_X_ORIGINAL_HOST'];
|
||||
} else {
|
||||
$host = $_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME']; //WAS SERVER_NAME and caused problems on some boxes
|
||||
}
|
||||
|
||||
// *** PROTOCOL
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
|
||||
$_SERVER['HTTPS'] = 'on';
|
||||
}
|
||||
if (isset($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'https') {
|
||||
$_SERVER['HTTPS'] = 'on';
|
||||
}
|
||||
if (isset($_SERVER['HTTP_CF_VISITOR'])) {
|
||||
$visitor = json_decode($_SERVER['HTTP_CF_VISITOR']);
|
||||
if (is_object($visitor) && property_exists($visitor, 'scheme') && $visitor->scheme == 'https') {
|
||||
$_SERVER['HTTPS'] = 'on';
|
||||
}
|
||||
}
|
||||
$protocol = 'http' . ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') ? 's' : '');
|
||||
|
||||
if ($requestUri) {
|
||||
$serverUrlSelf = preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']);
|
||||
} else {
|
||||
// *** SCRIPT NAME
|
||||
$serverUrlSelf = $_SERVER['SCRIPT_NAME'];
|
||||
for ($i = 0; $i < $getParentDirLevel; $i++) {
|
||||
$serverUrlSelf = preg_match('/^[\\\\\/]?$/', dirname($serverUrlSelf)) ? '' : dirname($serverUrlSelf);
|
||||
}
|
||||
}
|
||||
|
||||
// *** QUERY STRING
|
||||
$query = ($queryString && isset($_SERVER['QUERY_STRING']) && strlen($_SERVER['QUERY_STRING']) > 0) ? '?' . $_SERVER['QUERY_STRING'] : '';
|
||||
|
||||
return $protocol . '://' . $host . $serverUrlSelf . $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function make a chmod only if the are different from perms input and if chmod function is enabled
|
||||
*
|
||||
* This function handles the variable MODE in a way similar to the chmod of lunux
|
||||
* So the MODE variable can be
|
||||
* 1) an octal number (0755)
|
||||
* 2) a string that defines an octal number ("644")
|
||||
* 3) a string with the following format [ugoa]*([-+=]([rwx]*)+
|
||||
*
|
||||
* examples
|
||||
* u+rw add read and write at the user
|
||||
* u+rw,uo-wx add read and write ad the user and remove wx at groupd and other
|
||||
* a=rw is equal at 666
|
||||
* u=rwx,go-rwx is equal at 700
|
||||
*
|
||||
* @param string $file file path
|
||||
* @param int|string $mode mode
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function chmod($file, $mode)
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$octalMode = 0;
|
||||
|
||||
if (is_int($mode)) {
|
||||
$octalMode = $mode;
|
||||
} elseif (is_string($mode)) {
|
||||
$mode = trim($mode);
|
||||
if (preg_match('/([0-7]{1,3})/', $mode)) {
|
||||
$octalMode = intval(('0' . $mode), 8);
|
||||
} elseif (preg_match_all('/(a|[ugo]{1,3})([-=+])([rwx]{1,3})/', $mode, $gMatch, PREG_SET_ORDER)) {
|
||||
if (!function_exists('fileperms')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// start by file permission
|
||||
$octalMode = (fileperms($file) & 0777);
|
||||
|
||||
foreach ($gMatch as $matches) {
|
||||
// [ugo] or a = ugo
|
||||
$group = $matches[1];
|
||||
if ($group === 'a') {
|
||||
$group = 'ugo';
|
||||
}
|
||||
// can be + - =
|
||||
$action = $matches[2];
|
||||
// [rwx]
|
||||
$gPerms = $matches[3];
|
||||
|
||||
// reset octal group perms
|
||||
$octalGroupMode = 0;
|
||||
|
||||
// Init sub perms
|
||||
$subPerm = 0;
|
||||
$subPerm += strpos($gPerms, 'x') !== false ? 1 : 0; // mask 001
|
||||
$subPerm += strpos($gPerms, 'w') !== false ? 2 : 0; // mask 010
|
||||
$subPerm += strpos($gPerms, 'r') !== false ? 4 : 0; // mask 100
|
||||
|
||||
$ugoLen = strlen($group);
|
||||
|
||||
if ($action === '=') {
|
||||
// generate octal group permsissions and ugo mask invert
|
||||
$ugoMaskInvert = 0777;
|
||||
for ($i = 0; $i < $ugoLen; $i++) {
|
||||
switch ($group[$i]) {
|
||||
case 'u':
|
||||
$octalGroupMode |= $subPerm << 6; // mask xxx000000
|
||||
$ugoMaskInvert &= 077;
|
||||
break;
|
||||
case 'g':
|
||||
$octalGroupMode |= $subPerm << 3; // mask 000xxx000
|
||||
$ugoMaskInvert &= 0707;
|
||||
break;
|
||||
case 'o':
|
||||
$octalGroupMode |= $subPerm; // mask 000000xxx
|
||||
$ugoMaskInvert &= 0770;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// apply = action
|
||||
$octalMode &= $ugoMaskInvert | $octalGroupMode;
|
||||
} else {
|
||||
// generate octal group permsissions
|
||||
for ($i = 0; $i < $ugoLen; $i++) {
|
||||
switch ($group[$i]) {
|
||||
case 'u':
|
||||
$octalGroupMode |= $subPerm << 6; // mask xxx000000
|
||||
break;
|
||||
case 'g':
|
||||
$octalGroupMode |= $subPerm << 3; // mask 000xxx000
|
||||
break;
|
||||
case 'o':
|
||||
$octalGroupMode |= $subPerm; // mask 000000xxx
|
||||
break;
|
||||
}
|
||||
}
|
||||
// apply + or - action
|
||||
switch ($action) {
|
||||
case '+':
|
||||
$octalMode |= $octalGroupMode;
|
||||
break;
|
||||
case '-':
|
||||
$octalMode &= ~$octalGroupMode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if input permissions are equal at file permissions return true without performing chmod
|
||||
if (function_exists('fileperms') && $octalMode === (fileperms($file) & 0777)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!function_exists('chmod')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return @chmod($file, $octalMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates a folder if it does not exist and performs a chmod.
|
||||
* it is different from the normal mkdir function to which an umask is applied to the input permissions.
|
||||
*
|
||||
* This function handles the variable MODE in a way similar to the chmod of lunux
|
||||
* So the MODE variable can be
|
||||
* 1) an octal number (0755)
|
||||
* 2) a string that defines an octal number ("644")
|
||||
* 3) a string with the following format [ugoa]*([-+=]([rwx]*)+
|
||||
*
|
||||
* @param string $path folder path
|
||||
* @param int|string $mode mode permissions
|
||||
* @param bool $recursive Allows the creation of nested directories specified in the pathname. Default to false.
|
||||
* @param resource $context not used for windows bug
|
||||
*
|
||||
* @return boolean bool TRUE on success or FALSE on failure.
|
||||
*
|
||||
* @todo check recursive true and multiple chmod
|
||||
*/
|
||||
public static function mkdir($path, $mode = 0777, $recursive = false, $context = null)
|
||||
{
|
||||
if (strlen($path) > PHP_MAXPATHLEN) {
|
||||
throw new Exception('Skipping a file that exceeds allowed max path length [' . PHP_MAXPATHLEN . ']. File: ' . $path);
|
||||
}
|
||||
|
||||
if (!file_exists($path)) {
|
||||
if (!function_exists('mkdir')) {
|
||||
return false;
|
||||
}
|
||||
if (!@mkdir($path, 0777, $recursive)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return self::chmod($path, $mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a string starts with specific characters
|
||||
*
|
||||
* @param string $haystack haystack
|
||||
* @param string $needle needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function startsWith($haystack, $needle): bool
|
||||
{
|
||||
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the server supports issuing commands to shell_exex
|
||||
*
|
||||
* @return bool Returns true shell_exec can be ran on this server
|
||||
*/
|
||||
public static function hasShellExec(): bool
|
||||
{
|
||||
if (!Shell::test()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the possible system commands for unzip on Linux
|
||||
*
|
||||
* @return bool|string Returns unzip file path that can execute the unzip command of false if don't exists
|
||||
*/
|
||||
public static function getUnzipFilePath()
|
||||
{
|
||||
static $filepath = null;
|
||||
|
||||
if ($filepath === null) {
|
||||
if (!self::hasShellExec()) {
|
||||
$filepath = false;
|
||||
} elseif (Shell::runCommandBuffered('hash unzip 2>&1')->getCode() >= 0) {
|
||||
$filepath = 'unzip';
|
||||
} else {
|
||||
$filepath = false;
|
||||
$possible_paths = [
|
||||
'/usr/bin/unzip',
|
||||
'/opt/local/bin/unzip',
|
||||
'/bin/unzip',
|
||||
'/usr/local/bin/unzip',
|
||||
'/usr/sfw/bin/unzip',
|
||||
'/usr/xdg4/bin/unzip',
|
||||
'/opt/bin/unzip',
|
||||
// RSR TODO put back in when we support shellexec on windows,
|
||||
];
|
||||
|
||||
foreach ($possible_paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
$filepath = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display human readable byte sizes such as 150MB
|
||||
*
|
||||
* @param int $size The size in bytes
|
||||
*
|
||||
* @return string A readable byte size format such as 100MB
|
||||
*/
|
||||
public static function readableByteSize($size): string
|
||||
{
|
||||
try {
|
||||
$units = [
|
||||
'B',
|
||||
'KB',
|
||||
'MB',
|
||||
'GB',
|
||||
'TB',
|
||||
];
|
||||
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
||||
$size /= 1024;
|
||||
}
|
||||
return round($size, 2) . $units[$i];
|
||||
} catch (Exception $e) {
|
||||
return "n/a";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely remove a directory and recursively files and directory upto multiple sublevels
|
||||
*
|
||||
* @param string $path The full path to the directory to remove
|
||||
*
|
||||
* @return bool Returns true if all content was removed
|
||||
*/
|
||||
public static function rrmdir($path)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
if (($dh = opendir($path)) === false) {
|
||||
return false;
|
||||
}
|
||||
while (($object = readdir($dh)) !== false) {
|
||||
if ($object == "." || $object == "..") {
|
||||
continue;
|
||||
}
|
||||
if (!self::rrmdir($path . "/" . $object)) {
|
||||
closedir($dh);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
return @rmdir($path);
|
||||
} else {
|
||||
if (is_writable($path)) {
|
||||
return @unlink($path);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes path safe for any OS for PHP
|
||||
*
|
||||
* Paths should ALWAYS READ be "/"
|
||||
* uni: /home/path/file.txt
|
||||
* win: D:/home/path/file.txt
|
||||
*
|
||||
* @param string $path TThe path to make safe
|
||||
*
|
||||
* @return string The original $path with a with all slashes facing '/'.
|
||||
*/
|
||||
public static function setSafePath($path): string
|
||||
{
|
||||
return str_replace("\\", "/", $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all non stamp chars from string and newline
|
||||
* trim string
|
||||
*
|
||||
* @param string $string input string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitizeNSCharsNewline($string): string
|
||||
{
|
||||
return (string) preg_replace(
|
||||
'/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\r\n]/u',
|
||||
'',
|
||||
(string) $string
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the class exists, false otherwise
|
||||
*
|
||||
* @param string $className Name of the class to check if it exists
|
||||
* @param boolean $autoload Parameter that will be passed to class_exists as second
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function classExists($className, $autoload = true)
|
||||
{
|
||||
if (function_exists("ini_get")) {
|
||||
$disabled = explode(',', ini_get('disable_classes'));
|
||||
return !in_array($className, $disabled);
|
||||
}
|
||||
|
||||
if (!class_exists($className, $autoload)) {
|
||||
return false;
|
||||
}
|
||||
// We can only suppose that it exists, can't be 100% sure, but it's the best guess
|
||||
return true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Bootstrap;
|
||||
|
||||
use Exception;
|
||||
|
||||
class LogHandler
|
||||
{
|
||||
/** @var bool */
|
||||
private static $initialized = false;
|
||||
|
||||
/** @var callable */
|
||||
private static $logCallback;
|
||||
|
||||
/**
|
||||
* This function only initializes the error handler the first time it is called
|
||||
*
|
||||
* @param callable $logCallback log callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initErrorHandler($logCallback): void
|
||||
{
|
||||
if (!self::$initialized) {
|
||||
if (!is_callable($logCallback)) {
|
||||
throw new Exception('Log callback must be callable');
|
||||
}
|
||||
self::$logCallback = $logCallback;
|
||||
|
||||
@set_error_handler([self::class, 'error']);
|
||||
@register_shutdown_function([self::class, 'shutdown']);
|
||||
self::$initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*
|
||||
* @param integer $errno Error level
|
||||
* @param string $errstr Error message
|
||||
* @param string $errfile Error file
|
||||
* @param integer $errline Error line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function error($errno, $errstr, $errfile, $errline): bool
|
||||
{
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
if (call_user_func(self::$logCallback, $log_message) === false) {
|
||||
$log_message = "Can\'t wrinte logfile\n\n" . $log_message;
|
||||
}
|
||||
die('<pre>' . htmlspecialchars($log_message) . '</pre>');
|
||||
case E_NOTICE:
|
||||
case E_WARNING:
|
||||
default:
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
call_user_func(self::$logCallback, $log_message);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message from error
|
||||
*
|
||||
* @param int $errno errno
|
||||
* @param string $errstr message
|
||||
* @param string $errfile file
|
||||
* @param int $errline line
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getMessage($errno, $errstr, $errfile, $errline): string
|
||||
{
|
||||
$result = '[PHP ERR]';
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
$result .= '[FATAL]';
|
||||
break;
|
||||
case E_WARNING:
|
||||
$result .= '[WARN]';
|
||||
break;
|
||||
case E_NOTICE:
|
||||
$result .= '[NOTICE]';
|
||||
break;
|
||||
default:
|
||||
$result .= '[ISSUE]';
|
||||
break;
|
||||
}
|
||||
$result .= ' MSG:';
|
||||
$result .= $errstr;
|
||||
return $result . (' [CODE:' . $errno . '|FILE:' . $errfile . '|LINE:' . $errline . ']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function shutdown(): void
|
||||
{
|
||||
if (($error = error_get_last())) {
|
||||
LogHandler::error($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class that collects the functions of initial checks on the requirements to run the plugin
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Addons;
|
||||
|
||||
abstract class InstAbstractAddonCore
|
||||
{
|
||||
const ADDON_DATA_CONTEXT = 'duplicator_addon';
|
||||
|
||||
/**
|
||||
* Addons instances
|
||||
*
|
||||
* @var self[]
|
||||
*/
|
||||
private static $instances = [];
|
||||
|
||||
/**
|
||||
* Current addon data
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected $addonData = [];
|
||||
|
||||
/**
|
||||
* Get curent addon instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
$class = static::class;
|
||||
if (!isset(self::$instances[$class])) {
|
||||
self::$instances[$class] = new static();
|
||||
}
|
||||
|
||||
return self::$instances[$class];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
final protected function __construct()
|
||||
{
|
||||
$reflect = new \ReflectionClass(static::class);
|
||||
$this->addonData = self::getInitAddonData($reflect->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called on addon init only if is available
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function init();
|
||||
|
||||
/**
|
||||
* Get main addon file path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonFile()
|
||||
{
|
||||
// To prevent the warning about static abstract functions that appears in PHP 5.4/5.6 I use this trick.
|
||||
throw new \Exception('this function have to overwritte on child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main addon folder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonPath()
|
||||
{
|
||||
// To prevent the warning about static abstract functions that appears in PHP 5.4/5.6 I use this trick.
|
||||
throw new \Exception('this function have to overwritte on child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get slug of current addon
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->addonData['slug'];
|
||||
}
|
||||
|
||||
/**
|
||||
* True if current addon is available
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEnable()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, $this->addonData['requiresPHP'], '<')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version_compare(DUPX_VERSION, $this->addonData['requiresDuplcator'], '<')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if addon has dependencies
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasDependencies()
|
||||
{
|
||||
$avaliableAddons = InstAddonsManager::getInstance()->getAvailableAddons();
|
||||
return !array_diff($this->addonData['requiresAddons'], $avaliableAddons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get addon data from header addon file
|
||||
*
|
||||
* @param string $class addon class
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected static function getInitAddonData($class)
|
||||
{
|
||||
$data = self::getFileFata(static::getAddonFile(), self::getDefaltHeaders());
|
||||
$getDefaultVal = self::getDefaultHeadersValues();
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
if (strlen($val) === 0) {
|
||||
$data[$key] = $getDefaultVal[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_array($data['requiresAddons'])) {
|
||||
$data['requiresAddons'] = explode(',', $data['requiresAddons']);
|
||||
}
|
||||
$data['requiresAddons'] = array_map('trim', $data['requiresAddons']);
|
||||
|
||||
$data['slug'] = $class;
|
||||
if (strlen($data['name']) === 0) {
|
||||
$data['name'] = $data['slug'];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retur default addon date headers
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultHeadersValues()
|
||||
{
|
||||
static $defaultHeaders = null;
|
||||
if (is_null($defaultHeaders)) {
|
||||
$defaultHeaders = [
|
||||
'name' => '',
|
||||
'addonURI' => '',
|
||||
'version' => '0',
|
||||
'description' => '',
|
||||
'author' => '',
|
||||
'authorURI' => '',
|
||||
'requiresWP' => '5.3',
|
||||
'requiresPHP' => '7.4',
|
||||
'requiresDuplcator' => '4.0.2',
|
||||
'requiresAddons' => [],
|
||||
];
|
||||
}
|
||||
return $defaultHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return headers list keys
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected static function getDefaltHeaders()
|
||||
{
|
||||
return [
|
||||
'name' => 'Name',
|
||||
'addonURI' => 'Addon URI',
|
||||
'version' => 'Version',
|
||||
'description' => 'Description',
|
||||
'author' => 'Author',
|
||||
'authorURI' => 'Author URI',
|
||||
'requiresWP' => 'Requires WP min version',
|
||||
'requiresPHP' => 'Requires PHP',
|
||||
'requiresDuplcator' => 'Requires Duplicator min version',
|
||||
'requiresAddons' => 'Requires addons',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve metadata from a file.
|
||||
*
|
||||
* Searches for metadata in the first 8 KB of a file, such as a plugin or theme.
|
||||
* Each piece of metadata must be on its own line. Fields can not span multiple
|
||||
* lines, the value will get cut at the end of the first line.
|
||||
*
|
||||
* If the file data is not within that first 8 KB, then the author should correct
|
||||
* their plugin file and move the data headers to the top.
|
||||
*
|
||||
* from WordPress get_file_data function
|
||||
*
|
||||
* @param string $file Absolute path to the file.
|
||||
* @param array<string, string> $defaultHeaders List of headers, in the format `array( 'HeaderKey' => 'Header Name' )`.
|
||||
*
|
||||
* @return array<string, mixed> Array of file headers in `HeaderKey => Header Value` format.
|
||||
*/
|
||||
protected static function getFileFata($file, $defaultHeaders)
|
||||
{
|
||||
// We don't need to write to the file, so just open for reading.
|
||||
$fp = fopen($file, 'r');
|
||||
|
||||
// Pull only the first 8 KB of the file in.
|
||||
$file_data = fread($fp, 8 * KB_IN_BYTES);
|
||||
|
||||
// PHP will close file handle, but we are good citizens.
|
||||
fclose($fp);
|
||||
|
||||
// Make sure we catch CR-only line endings.
|
||||
$file_data = str_replace("\r", "\n", $file_data);
|
||||
$all_headers = $defaultHeaders;
|
||||
|
||||
foreach ($all_headers as $field => $regex) {
|
||||
if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, '/') . ':(.*)$/mi', $file_data, $match) && $match[1]) {
|
||||
$all_headers[$field] = self::cleanupHeaderComment($match[1]);
|
||||
} else {
|
||||
$all_headers[$field] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $all_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip close comment and close php tags from file headers used by WP.
|
||||
*
|
||||
* From WordPress _cleanup_header_comment
|
||||
*
|
||||
* @param string $str Header comment to clean up.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function cleanupHeaderComment($str)
|
||||
{
|
||||
return trim(preg_replace('/\s*(?:\*\/|\?>).*/', '', $str));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class that collects the functions of initial checks on the requirements to run the plugin
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Addons;
|
||||
|
||||
use Duplicator\Installer\Core\Hooks\HooksMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
|
||||
final class InstAddonsManager
|
||||
{
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
/** @var InstAbstractAddonCore[] */
|
||||
private array $addons;
|
||||
/** @var InstAbstractAddonCore[] */
|
||||
private $enabledAddons = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inizialize addons
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
$this->addons = self::getAddonListFromFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* inizialize all abaiblae addons
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function inizializeAddons(): void
|
||||
{
|
||||
foreach ($this->addons as $addon) {
|
||||
if ($addon->canEnable() && $addon->hasDependencies()) {
|
||||
$this->enabledAddons[] = $addon->getSlug();
|
||||
$addon->init();
|
||||
Log::info('ADDON ' . $addon->getAddonFile() . ' ENABLED', Log::LV_DETAILED);
|
||||
} else {
|
||||
Log::info('CAN\'T ENABLE ADDON ' . $addon->getSlug());
|
||||
}
|
||||
}
|
||||
HooksMng::getInstance()->doAction('duplicator_addons_loaded');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAvailableAddons(): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->addons as $addon) {
|
||||
$result[] = $addon->getSlug();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return InstAbstractAddonCore[]
|
||||
*/
|
||||
public function getEnabledAddons()
|
||||
{
|
||||
return $this->enabledAddons;
|
||||
}
|
||||
|
||||
/**
|
||||
* return addons folder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAddonsPath(): string
|
||||
{
|
||||
return DUPX_INIT . '/addons';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return InstAbstractAddonCore[]
|
||||
*/
|
||||
private static function getAddonListFromFolder(): array
|
||||
{
|
||||
$addonList = [];
|
||||
|
||||
$checkDir = SnapIO::trailingslashit(self::getAddonsPath());
|
||||
|
||||
if (!is_dir($checkDir)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (($dh = opendir($checkDir)) == false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
while (($elem = readdir($dh)) !== false) {
|
||||
if ($elem === '.' || $elem === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fullPath = $checkDir . $elem;
|
||||
$addonMainFile = false;
|
||||
$addonMainClass = '';
|
||||
|
||||
if (!is_dir($fullPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($addonDh = opendir($fullPath)) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (($addonElem = readdir($addonDh)) !== false) {
|
||||
if ($addonElem === '.' || $addonElem === '..') {
|
||||
continue;
|
||||
}
|
||||
$info = pathinfo($fullPath . '/' . $addonElem);
|
||||
|
||||
if (strcasecmp($elem, $info['filename']) === 0) {
|
||||
$addonMainFile = $checkDir . $elem . '/' . $addonElem;
|
||||
$addonMainClass = '\\Duplicator\\Installer\\Addons\\' . $info['filename'] . '\\' . $info['filename'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($addonMainFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!is_subclass_of($addonMainClass, \Duplicator\Installer\Core\Addons\InstAbstractAddonCore::class)) {
|
||||
continue;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::info('Addon file ' . $addonMainFile . ' exists but not countain addon main core class, Exception: ' . $e->getMessage());
|
||||
continue;
|
||||
} catch (\Error $e) {
|
||||
Log::info('Addon file ' . $addonMainFile . ' exists but generate an error, Exception: ' . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
$addonObj = $addonMainClass::getInstance();
|
||||
$addonList[$addonObj->getSlug()] = $addonObj;
|
||||
}
|
||||
closedir($dh);
|
||||
|
||||
return $addonList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core;
|
||||
|
||||
use Duplicator\Installer\Core\Addons\InstAddonsManager;
|
||||
use Duplicator\Installer\Core\Deploy\ServerConfigs;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\InstallerOrigFileMng;
|
||||
use Duplicator\Installer\Utils\InstDescMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Utils\Log\LogHandler;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_DBInstall;
|
||||
use DUPX_Extraction;
|
||||
use DUPX_S3_Funcs;
|
||||
use DUPX_U;
|
||||
|
||||
/**
|
||||
* Class that collects the functions of initial duplicator Bootstrap
|
||||
*/
|
||||
class Bootstrap
|
||||
{
|
||||
const DESCRIPTORS_PREFIX = 'dup_descriptors_';
|
||||
const MINIMUM_PHP_VERSION = '7.4';
|
||||
|
||||
/**
|
||||
* this variable becomes false after the installer is initialized by skipping the shutdown function defined in the boot class
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $shutdownFunctionEnaled = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public static $dupInitFolderParentLevel = 1;
|
||||
|
||||
/**
|
||||
* Init installer
|
||||
*
|
||||
* @param integer $folderParentLevel num folder parents of home path
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init($folderParentLevel = 1): void
|
||||
{
|
||||
self::setHTTPHeaders();
|
||||
self::$dupInitFolderParentLevel = max(1, (int) $folderParentLevel);
|
||||
self::phpVersionCheck();
|
||||
|
||||
$GLOBALS['DUPX_ENFORCE_PHP_INI'] = false;
|
||||
|
||||
// INIT ERROR LOG FILE (called before evrithing)
|
||||
if (function_exists('register_shutdown_function')) {
|
||||
register_shutdown_function([self::class, 'bootShutdown']);
|
||||
}
|
||||
if (self::initPhpErrorLog(false) === false) {
|
||||
// Enable this only for debugging. Generate a log too alarmist.
|
||||
SnapUtil::errorLog('DUPLICATOR CAN\'T CHANGE THE PATH OF PHP ERROR LOG FILE');
|
||||
}
|
||||
|
||||
/*
|
||||
* INIZIALIZE
|
||||
*/
|
||||
define('DUPX_INIT_URL', SnapURL::getCurrentUrl(false, false, self::$dupInitFolderParentLevel));
|
||||
define('DUPX_ROOT_URL', SnapURL::getCurrentUrl(false, false, self::$dupInitFolderParentLevel + 1));
|
||||
|
||||
// includes main files
|
||||
self::includes();
|
||||
// set time for logging time
|
||||
Log::resetTime();
|
||||
// set all PHP.INI settings
|
||||
self::phpIni();
|
||||
// init log files
|
||||
self::initLogs();
|
||||
// init global values
|
||||
\DUPX_Constants::init();
|
||||
|
||||
// init addond before evrithing
|
||||
InstAddonsManager::getInstance()->inizializeAddons();
|
||||
// init templates
|
||||
self::templatesInit();
|
||||
// SECURITY CHECK
|
||||
Security::getInstance()->check();
|
||||
// init error handler after constant
|
||||
LogHandler::initErrorHandler();
|
||||
|
||||
// init params
|
||||
PrmMng::getInstance()->initParams();
|
||||
|
||||
// read params from request and init global value
|
||||
self::initInstallerFiles();
|
||||
|
||||
// check custom hosts
|
||||
\DUPX_Custom_Host_Manager::getInstance()->init();
|
||||
|
||||
$pathInfo = $_SERVER['PATH_INFO'] ?? '';
|
||||
Log::info("\n\n"
|
||||
. "==============================================\n"
|
||||
. "= BOOT INIT OK [" . $pathInfo . "]\n"
|
||||
. "==============================================\n", Log::LV_DETAILED);
|
||||
|
||||
if (Log::isLevel(Log::LV_DEBUG)) {
|
||||
Log::info('-------------------');
|
||||
Log::info('PARAMS');
|
||||
Log::info(PrmMng::getInstance()->getParamsToText());
|
||||
Log::info('-------------------');
|
||||
}
|
||||
|
||||
\DUPX_DB_Tables::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init ini_set and default constants
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function phpIni(): void
|
||||
{
|
||||
// Absolute path to the Installer directory. - necessary for php protection
|
||||
if (!defined('KB_IN_BYTES')) {
|
||||
define('KB_IN_BYTES', 1024);
|
||||
}
|
||||
if (!defined('MB_IN_BYTES')) {
|
||||
define('MB_IN_BYTES', 1024 * KB_IN_BYTES);
|
||||
}
|
||||
if (!defined('GB_IN_BYTES')) {
|
||||
define('GB_IN_BYTES', 1024 * MB_IN_BYTES);
|
||||
}
|
||||
if (!defined('DUPLICATOR_PHP_MAX_MEMORY')) {
|
||||
define('DUPLICATOR_PHP_MAX_MEMORY', 4096 * MB_IN_BYTES);
|
||||
}
|
||||
|
||||
date_default_timezone_set('UTC'); // Some machines don’t have this set so just do it here.
|
||||
if (strlen(@ini_get('date.timezone')) === 0) {
|
||||
// Some machines don’t date.timezone set
|
||||
@ini_set('date.timezone', 'UTC');
|
||||
}
|
||||
@ignore_user_abort(true);
|
||||
|
||||
@set_time_limit(3600);
|
||||
|
||||
$defaultCharset = ini_get("default_charset");
|
||||
if (empty($defaultCharset) && SnapUtil::isIniValChangeable('default_charset')) {
|
||||
@ini_set("default_charset", 'utf-8');
|
||||
}
|
||||
if (SnapUtil::isIniValChangeable('memory_limit')) {
|
||||
@ini_set('memory_limit', (string) DUPLICATOR_PHP_MAX_MEMORY);
|
||||
}
|
||||
if (SnapUtil::isIniValChangeable('max_input_time')) {
|
||||
@ini_set('max_input_time', '-1');
|
||||
}
|
||||
if (SnapUtil::isIniValChangeable('pcre.backtrack_limit')) {
|
||||
@ini_set('pcre.backtrack_limit', (string) PHP_INT_MAX);
|
||||
}
|
||||
|
||||
//PHP INI SETUP: all time in seconds
|
||||
if (!isset($GLOBALS['DUPX_ENFORCE_PHP_INI']) || !$GLOBALS['DUPX_ENFORCE_PHP_INI']) {
|
||||
if (SnapUtil::isIniValChangeable('mysql.connect_timeout')) {
|
||||
@ini_set('mysql.connect_timeout', '5000');
|
||||
}
|
||||
if (SnapUtil::isIniValChangeable('max_execution_time')) {
|
||||
@ini_set("max_execution_time", '5000');
|
||||
}
|
||||
if (SnapUtil::isIniValChangeable('max_input_time')) {
|
||||
@ini_set("max_input_time", '5000');
|
||||
}
|
||||
if (SnapUtil::isIniValChangeable('default_socket_timeout')) {
|
||||
@ini_set('default_socket_timeout', '5000');
|
||||
}
|
||||
@set_time_limit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include default utils files and constants
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function includes(): void
|
||||
{
|
||||
require_once(DUPX_INIT . '/classes/config/class.conf.wp.php');
|
||||
require_once(DUPX_INIT . '/classes/utilities/class.u.php');
|
||||
require_once(DUPX_INIT . '/classes/utilities/class.u.notices.manager.php');
|
||||
require_once(DUPX_INIT . '/classes/utilities/template/class.u.template.manager.php');
|
||||
require_once(DUPX_INIT . '/classes/validation/class.validation.manager.php');
|
||||
require_once(DUPX_INIT . '/classes/database/class.db.php');
|
||||
require_once(DUPX_INIT . '/classes/database/class.db.functions.php');
|
||||
require_once(DUPX_INIT . '/classes/database/class.db.tables.php');
|
||||
require_once(DUPX_INIT . '/classes/database/class.db.table.item.php');
|
||||
require_once(DUPX_INIT . '/classes/class.http.php');
|
||||
require_once(DUPX_INIT . '/classes/class.package.php');
|
||||
require_once(DUPX_INIT . '/classes/class.server.php');
|
||||
require_once(DUPX_INIT . '/classes/config/class.archive.config.php');
|
||||
require_once(DUPX_INIT . '/classes/config/class.constants.php');
|
||||
require_once(DUPX_INIT . '/classes/config/class.conf.utils.php');
|
||||
require_once(DUPX_INIT . '/ctrls/classes/class.ctrl.ajax.php');
|
||||
require_once(DUPX_INIT . '/ctrls/classes/class.ctrl.params.php');
|
||||
require_once(DUPX_INIT . '/ctrls/ctrl.base.php');
|
||||
require_once(DUPX_INIT . '/ctrls/classes/class.ctrl.extraction.php');
|
||||
require_once(DUPX_INIT . '/ctrls/classes/class.ctrl.dbinstall.php');
|
||||
require_once(DUPX_INIT . '/ctrls/classes/class.ctrl.s3.funcs.php');
|
||||
require_once(DUPX_INIT . '/classes/view-helpers/class.u.html.php');
|
||||
require_once(DUPX_INIT . '/classes/view-helpers/class.view.php');
|
||||
require_once(DUPX_INIT . '/classes/host/class.custom.host.manager.php');
|
||||
require_once(DUPX_INIT . '/classes/class.engine.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function moves the error_log.php into the dup-installer directory.
|
||||
* It is called before including any other file so it uses only native PHP functions.
|
||||
*
|
||||
* !!! Don't use any Duplicator function within this function. !!!
|
||||
*
|
||||
* @param bool $reset if true reset log file
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function initPhpErrorLog($reset = false): bool
|
||||
{
|
||||
if (!function_exists('ini_set')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$logFile = DUPX_INIT . '/' . InstDescMng::getInstance()->getName(InstDescMng::TYPE_INST_PHP_ERROR_LOG);
|
||||
|
||||
if (file_exists($logFile)) {
|
||||
if (!is_writable($logFile)) {
|
||||
return false;
|
||||
} elseif ($reset && function_exists('unlink')) {
|
||||
@unlink($logFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (function_exists('error_reporting')) {
|
||||
error_reporting(E_ALL);
|
||||
}
|
||||
|
||||
@ini_set("log_errors", '1');
|
||||
if (@ini_set("error_log", $logFile) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!file_exists($logFile)) {
|
||||
SnapUtil::errorLog("PHP ERROR LOG INIT");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* It is called before including any other file so it uses only native PHP functions.
|
||||
*
|
||||
* !!! Don't use any Duplicator function within this function. !!!
|
||||
*
|
||||
* @return bool|string package hash or false if fail
|
||||
*/
|
||||
public static function getPackageHash()
|
||||
{
|
||||
static $packageHash = null;
|
||||
if (is_null($packageHash)) {
|
||||
$searchStr = DUPX_INIT . '/' . self::DESCRIPTORS_PREFIX . '*';
|
||||
$config_files = glob($searchStr, GLOB_ONLYDIR);
|
||||
if (empty($config_files)) {
|
||||
$packageHash = false;
|
||||
} else {
|
||||
$descriptor_folder_path = array_pop($config_files);
|
||||
$descriptor_folder_name = basename($descriptor_folder_path);
|
||||
$packageHash = substr($descriptor_folder_name, strlen(self::DESCRIPTORS_PREFIX));
|
||||
}
|
||||
}
|
||||
return $packageHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function init all params before read from request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function initParamsBase()
|
||||
{
|
||||
// GET PARAMS FROM REQUEST
|
||||
\DUPX_Ctrl_Params::setParamsBase();
|
||||
|
||||
// set log level from params
|
||||
Log::setLogLevel();
|
||||
Log::setPostProcessCallback(['DUPX_CTRL', 'renderPostProcessings']);
|
||||
Log::setAfterFatalErrorCallback(function (): void {
|
||||
if (InstState::getInstance()->getMode() === InstState::MODE_OVR_INSTALL) {
|
||||
DUPX_U::maintenanceMode(false);
|
||||
}
|
||||
});
|
||||
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$GLOBALS['DUPX_DEBUG'] = $paramsManager->getValue(PrmMng::PARAM_DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure no caching mechanism is used during install
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function setHTTPHeaders()
|
||||
{
|
||||
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
|
||||
header("Cache-Control: post-check=0, pre-check=0", false);
|
||||
header("Pragma: no-cache");
|
||||
}
|
||||
|
||||
/**
|
||||
* Init log header
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function initLogs()
|
||||
{
|
||||
if (!chdir(DUPX_INIT)) {
|
||||
// RSR TODO: Can't change directories
|
||||
throw new \Exception("Can't change to directory " . DUPX_INIT);
|
||||
}
|
||||
|
||||
//Restart log if user starts from step 0
|
||||
if (self::isInit()) {
|
||||
self::initPhpErrorLog(true);
|
||||
Log::clearLog();
|
||||
Log::info("********************************************************************************");
|
||||
Log::info('* DUPLICATOR-PRO: Install-Log');
|
||||
Log::info('* STEP-0 START @ ' . @date('h:i:s'));
|
||||
Log::info('* NOTICE: Do NOT post to public sites or forums!!');
|
||||
Log::info("********************************************************************************");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init all installer files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function initInstallerFiles()
|
||||
{
|
||||
if (!chdir(DUPX_INIT)) {
|
||||
// RSR TODO: Can't change directories
|
||||
throw new \Exception("Can't change to directory " . DUPX_INIT);
|
||||
}
|
||||
|
||||
//Restart log if user starts from step 0
|
||||
if (self::isInit()) {
|
||||
self::logHeader();
|
||||
\DUPX_NOTICE_MANAGER::getInstance()->resetNotices();
|
||||
|
||||
// LOAD PARAMS AFTER LOG RESET
|
||||
$paramManager = PrmMng::getInstance();
|
||||
$paramManager->load(true);
|
||||
try {
|
||||
InstallerOrigFileMng::getInstance()->restoreAll([
|
||||
ServerConfigs::CONFIG_ORIG_FILE_USERINI_ID,
|
||||
ServerConfigs::CONFIG_ORIG_FILE_PHPINI_ID,
|
||||
ServerConfigs::CONFIG_ORIG_FILE_WEBCONFIG_ID,
|
||||
ServerConfigs::CONFIG_ORIG_FILE_HTACCESS_ID,
|
||||
ServerConfigs::CONFIG_ORIG_FILE_WPCONFIG_ID,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'CANT RESTORE CONFIG FILES FORM PREVISION INSTALLATION');
|
||||
\DUPX_NOTICE_MANAGER::getInstance()->addNextStepNotice([
|
||||
'shortMsg' => 'The installer cannot restore files from a previous installation. ',
|
||||
'longMsg' => 'This problem does not affect the current installation so you can continue.<br>'
|
||||
. 'This can happen if the root folder does not have write permissions.',
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
]);
|
||||
}
|
||||
|
||||
self::initParamsBase();
|
||||
|
||||
DUPX_Extraction::resetData();
|
||||
DUPX_DBInstall::resetData();
|
||||
DUPX_S3_Funcs::resetData();
|
||||
self::renameHtaccess();
|
||||
|
||||
// update state only if isn't set by param overwrite
|
||||
InstState::getInstance()->checkState(true, false);
|
||||
// On init remove maintenance mode
|
||||
\DUPX_U::maintenanceMode(false);
|
||||
} else {
|
||||
// INIT PARAMS
|
||||
$paramManager = PrmMng::getInstance();
|
||||
$paramManager->load();
|
||||
self::initParamsBase();
|
||||
}
|
||||
|
||||
$paramManager->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename .htaccess file in dup-installer folder if it exists, so that it does not interfere with installer
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function renameHtaccess()
|
||||
{
|
||||
$htaccessPath = DUPXABSPATH . "/.htaccess";
|
||||
if (!file_exists($htaccessPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$htaccessPathRenamed = DUPXABSPATH . "/renamed_" . date_format(new \DateTime(), 'mdYHis') . ".htaccess";
|
||||
if (!SnapIO::rename($htaccessPath, $htaccessPathRenamed)) {
|
||||
$errorMsg = "WARNING: Could not delete/rename file \"$htaccessPath\". That file could interfere with the installation.\n";
|
||||
$errorMsg .= "If you encounter problems like buttons not working, please remove it yourself manually and restart the installer.";
|
||||
Log::info($errorMsg);
|
||||
} else {
|
||||
Log::info(".htaccess file was found in dup-installer folder and it was renamed to avoid interference with installer.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write log header
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function logHeader()
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
$colSize = 60;
|
||||
$labelPadSize = 20;
|
||||
$os = defined('PHP_OS') ? PHP_OS : 'unknown';
|
||||
|
||||
$log = '';
|
||||
$log .= str_pad(
|
||||
str_pad('PACKAGE INFO', $labelPadSize, '_', STR_PAD_RIGHT) . ' ' . 'ORIGINAL SERVER',
|
||||
$colSize,
|
||||
' ',
|
||||
STR_PAD_RIGHT
|
||||
) . '|' . 'CURRENT SERVER' . "\n";
|
||||
$log .= str_pad(
|
||||
str_pad('OS', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . $archiveConfig->version_os,
|
||||
$colSize,
|
||||
' ',
|
||||
STR_PAD_RIGHT
|
||||
) . '|' . $os . "\n";
|
||||
$log .= str_pad(
|
||||
str_pad('PHP VERSION', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . $archiveConfig->version_php,
|
||||
$colSize,
|
||||
' ',
|
||||
STR_PAD_RIGHT
|
||||
) . '|' . phpversion() . "\n";
|
||||
$log .= "********************************************************************************";
|
||||
Log::info($log, Log::LV_DEFAULT);
|
||||
|
||||
Log::info("CURRENT SERVER INFO");
|
||||
Log::info(str_pad('PHP', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . phpversion() . ' | SAPI: ' . php_sapi_name());
|
||||
Log::info(str_pad('PHP MEMORY', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . $GLOBALS['PHP_MEMORY_LIMIT'] . ' | SUHOSIN: ' . $GLOBALS['PHP_SUHOSIN_ON']);
|
||||
Log::info(str_pad('ARCHITECTURE', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . SnapUtil::getArchitectureString());
|
||||
Log::info(str_pad('SERVER', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . $_SERVER['SERVER_SOFTWARE']);
|
||||
Log::info(str_pad('DOC ROOT', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str(DUPX_ROOT));
|
||||
Log::info(str_pad('REQUEST URL', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str(DUPX_ROOT_URL));
|
||||
Log::info("********************************************************************************");
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if is the first installer call from installer.php
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInit(): bool
|
||||
{
|
||||
// don't use param manager because isn't initialized
|
||||
$isFirstStep = isset($_REQUEST[PrmMng::PARAM_CTRL_ACTION]) && $_REQUEST[PrmMng::PARAM_CTRL_ACTION] === "ctrl-step1";
|
||||
$isInitialized = isset($_REQUEST[PrmMng::PARAM_STEP_ACTION]) && !empty($_REQUEST[PrmMng::PARAM_STEP_ACTION]);
|
||||
return $isFirstStep && !$isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function disables the shutdown function defined in the boot class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function disableBootShutdownFunction(): void
|
||||
{
|
||||
self::$shutdownFunctionEnaled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sets the shutdown function before the installer is initialized.
|
||||
* Prevents blank pages.
|
||||
*
|
||||
* After the plugin is initialized it will be set as a shudwon function LogHandler::shutdown
|
||||
*
|
||||
* !!! Don't use any Duplicator function within this function. !!!
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootShutdown(): void
|
||||
{
|
||||
if (!self::$shutdownFunctionEnaled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (($error = error_get_last())) {
|
||||
?>
|
||||
<h1>BOOT SHUTDOWN FATAL ERROR</H1>
|
||||
<pre><?php
|
||||
echo 'Error: ' . htmlspecialchars($error['message']) . "\n\n\n" .
|
||||
'Type: ' . $error['type'] . "\n" .
|
||||
'File: ' . htmlspecialchars($error['file']) . "\n" .
|
||||
'Line: ' . $error['line'] . "\n";
|
||||
?></pre>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this function is called before anything else. do not use duplicator functions because nothing is included at this level.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function phpVersionCheck(): bool
|
||||
{
|
||||
if (version_compare(PHP_VERSION, self::MINIMUM_PHP_VERSION, '>=')) {
|
||||
return true;
|
||||
}
|
||||
$match = null;
|
||||
$phpVersion = preg_match("#^\d+(\.\d+)*#", PHP_VERSION, $match) ? $match[0] : PHP_VERSION;
|
||||
// no html
|
||||
echo 'This server is running PHP: ' . $phpVersion . '. A minimum of PHP ' . self::MINIMUM_PHP_VERSION . ' is required to run the installer.'
|
||||
. ' Contact your hosting provider or server administrator and let them know you would like to upgrade your PHP version.';
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init templates
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function templatesInit()
|
||||
{
|
||||
$tpl = \DUPX_Template::getInstance();
|
||||
|
||||
$tpl->addTemplate(\DUPX_Template::TEMPLATE_BASE, DUPX_INIT . '/templates/base', \DUPX_Template::TEMPLATE_ADVANCED);
|
||||
$tpl->addTemplate(\DUPX_Template::TEMPLATE_IMPORT_ADVANCED, DUPX_INIT . '/templates/import-advanced', \DUPX_Template::TEMPLATE_ADVANCED);
|
||||
$tpl->addTemplate(\DUPX_Template::TEMPLATE_IMPORT_BASE, DUPX_INIT . '/templates/import-base', \DUPX_Template::TEMPLATE_IMPORT_ADVANCED);
|
||||
$tpl->addTemplate(\DUPX_Template::TEMPLATE_RECOVERY, DUPX_INIT . '/templates/recovery', \DUPX_Template::TEMPLATE_BASE);
|
||||
|
||||
$tpl->setTemplate(\DUPX_Template::TEMPLATE_ADVANCED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Chunk;
|
||||
|
||||
use ArrayIterator;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Chunking\Iterators\GenericSeekableIteratorInterface;
|
||||
use DUPX_DB_Tables;
|
||||
use DUPX_S3_Funcs;
|
||||
use DUPX_UpdateEngine;
|
||||
|
||||
/**
|
||||
* Description of class
|
||||
*
|
||||
* @author andrea
|
||||
*/
|
||||
class SiteUpdateChunkIterator implements GenericSeekableIteratorInterface
|
||||
{
|
||||
const STEP_START = 'start';
|
||||
const STEP_CLEANUP_EXTREA = 'cleanup_extra';
|
||||
const STEP_CLEANUP_PACKAGES = 'cleanup_packages';
|
||||
const STEP_CLEANUP_OPTIONS = 'cleanup_trans';
|
||||
const STEP_SEARCH_AND_REPLACE_INIT = 'init';
|
||||
const STEP_SEARCH_AND_REPLACE = 'search_replace';
|
||||
const STEP_REMOVE_MAINTENACE = 'rem_maintenance';
|
||||
const STEP_CREATE_ADMIN = 'create_admin';
|
||||
const STEP_CONF_UPDATE = 'config_update';
|
||||
const STEP_GEN_UPD = 'gen_update';
|
||||
const STEP_GEN_CLEAN = 'gen_clean';
|
||||
const STEP_NOTICE_TEST = 'notice_test';
|
||||
const STEP_CLEANUP_TMP_FILES = 'cleanup_tmp_files';
|
||||
const STEP_SET_FILE_PERMS = 'set_files_perms';
|
||||
const STEP_FINAL_REPORT_NOTICES = 'final_report';
|
||||
|
||||
/** @var array{l0: ?string, l1: ?string, l2: ?int} */
|
||||
protected $position = [
|
||||
'l0' => self::STEP_SEARCH_AND_REPLACE_INIT,
|
||||
'l1' => null,
|
||||
'l2' => null,
|
||||
];
|
||||
/** @var bool */
|
||||
protected $isValid = true;
|
||||
protected \ArrayIterator $tablesIterator; // @phpstan-ignore missingType.generics
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$tables = DUPX_DB_Tables::getInstance()->getReplaceTablesNames();
|
||||
$this->tablesIterator = new ArrayIterator($tables);
|
||||
$this->rewind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator rewind
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->isValid = true;
|
||||
$this->position = [
|
||||
'l0' => self::STEP_START,
|
||||
'l1' => null,
|
||||
'l2' => null,
|
||||
];
|
||||
$this->tablesIterator->rewind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iteratornext
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next(): void
|
||||
{
|
||||
switch ($this->position['l0']) {
|
||||
case self::STEP_START:
|
||||
$this->position['l0'] = self::STEP_CLEANUP_OPTIONS;
|
||||
break;
|
||||
case self::STEP_CLEANUP_OPTIONS:
|
||||
$this->position['l0'] = self::STEP_CLEANUP_EXTREA;
|
||||
break;
|
||||
case self::STEP_CLEANUP_EXTREA:
|
||||
$this->position['l0'] = self::STEP_CLEANUP_PACKAGES;
|
||||
break;
|
||||
case self::STEP_CLEANUP_PACKAGES:
|
||||
$this->position['l0'] = self::STEP_SEARCH_AND_REPLACE_INIT;
|
||||
break;
|
||||
case self::STEP_SEARCH_AND_REPLACE_INIT:
|
||||
if ($this->getNextSearchReplacePosition(true)) {
|
||||
// if search and replace is valid go to STEP_SEARCH_AND_REPLACE
|
||||
$this->position['l0'] = self::STEP_SEARCH_AND_REPLACE;
|
||||
} else {
|
||||
// if search and replace isn't valid skip STEP_SEARCH_AND_REPLACE and go to STEP_REMOVE_MAINTENACE
|
||||
$this->position['l0'] = self::STEP_REMOVE_MAINTENACE;
|
||||
}
|
||||
break;
|
||||
case self::STEP_SEARCH_AND_REPLACE:
|
||||
if (!$this->getNextSearchReplacePosition()) {
|
||||
$this->position['l0'] = self::STEP_REMOVE_MAINTENACE;
|
||||
}
|
||||
break;
|
||||
case self::STEP_REMOVE_MAINTENACE:
|
||||
$this->position['l0'] = self::STEP_CONF_UPDATE;
|
||||
break;
|
||||
case self::STEP_CONF_UPDATE:
|
||||
$this->position['l0'] = self::STEP_GEN_UPD;
|
||||
break;
|
||||
case self::STEP_GEN_UPD:
|
||||
$this->position['l0'] = self::STEP_GEN_CLEAN;
|
||||
break;
|
||||
case self::STEP_GEN_CLEAN:
|
||||
$this->position['l0'] = self::STEP_CREATE_ADMIN;
|
||||
break;
|
||||
case self::STEP_CREATE_ADMIN:
|
||||
$this->position['l0'] = self::STEP_NOTICE_TEST;
|
||||
break;
|
||||
case self::STEP_NOTICE_TEST:
|
||||
$this->position['l0'] = self::STEP_CLEANUP_TMP_FILES;
|
||||
break;
|
||||
case self::STEP_CLEANUP_TMP_FILES:
|
||||
$this->position['l0'] = self::STEP_SET_FILE_PERMS;
|
||||
break;
|
||||
case self::STEP_SET_FILE_PERMS:
|
||||
$this->position['l0'] = self::STEP_FINAL_REPORT_NOTICES;
|
||||
break;
|
||||
case self::STEP_FINAL_REPORT_NOTICES:
|
||||
default:
|
||||
$this->position['l0'] = null;
|
||||
$this->isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go haead in tables position
|
||||
*
|
||||
* @param bool $init if true init engine
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function getNextSearchReplacePosition(bool $init = false)
|
||||
{
|
||||
$valid = true;
|
||||
$s3func = DUPX_S3_Funcs::getInstance();
|
||||
$pages = $s3func->cTableParams['pages'] ?? 0;
|
||||
$this->position['l2'] = (int) $this->position['l2'];
|
||||
|
||||
$this->position['l2']++;
|
||||
if ($this->position['l2'] < $pages) {
|
||||
/* NEXT PAGE */
|
||||
Log::info('ITERATOR INCREMENT PAGE: ' . $this->position['l2'] . ' PAGES[' . $pages . ']', 3);
|
||||
$s3func->cTableParams['page'] = $this->position['l2'];
|
||||
} else {
|
||||
if ($init) {
|
||||
DUPX_UpdateEngine::loadInit();
|
||||
Log::info('ITERATOR FIRST TABLE: ' . $this->position['l2'] . ' PAGES[' . $pages . ']', 3);
|
||||
$this->tablesIterator->rewind();
|
||||
} else {
|
||||
Log::info('ITERATOR INCREMENT TABLE: ' . $this->position['l2'] . ' PAGES[' . $pages . ']', 3);
|
||||
if ($s3func->cTableParams['updated']) {
|
||||
$s3func->report['updt_tables']++;
|
||||
}
|
||||
$this->tablesIterator->next();
|
||||
}
|
||||
$this->position['l1'] = $this->tablesIterator->key();
|
||||
$this->position['l2'] = 0;
|
||||
|
||||
// search first table with rows and columns
|
||||
while ($this->tablesIterator->valid()) {
|
||||
Log::info('ITERATOR CHECK TABLE: ' . $this->tablesIterator->current(), 3);
|
||||
// init table params if isn't initialized
|
||||
if (DUPX_UpdateEngine::initTableParams($this->tablesIterator->current())) {
|
||||
// table with columns and rows found
|
||||
break;
|
||||
}
|
||||
// NEXT TABLE
|
||||
$this->tablesIterator->next();
|
||||
}
|
||||
|
||||
if ($this->tablesIterator->valid()) {
|
||||
$this->position['l1'] = $this->tablesIterator->key();
|
||||
$this->position['l2'] = 0;
|
||||
} else {
|
||||
$this->position['l1'] = null;
|
||||
$this->position['l2'] = null;
|
||||
$s3func->cTableParams = null;
|
||||
DUPX_UpdateEngine::loadEnd();
|
||||
DUPX_UpdateEngine::replaceSiteTable();
|
||||
DUPX_UpdateEngine::replaceBlogsTable();
|
||||
DUPX_UpdateEngine::logStats();
|
||||
DUPX_UpdateEngine::logErrors();
|
||||
$valid = false;
|
||||
}
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set position
|
||||
*
|
||||
* @param mixed[] $position position
|
||||
*
|
||||
* @return bool true on success, false on fail
|
||||
*/
|
||||
public function gSeek($position): bool
|
||||
{
|
||||
$this->position = $position;
|
||||
switch ($this->position['l0']) {
|
||||
case self::STEP_SEARCH_AND_REPLACE:
|
||||
$this->tablesIterator->seek($this->position['l1']);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position
|
||||
*
|
||||
* @return array{l0: string, l1: ?string, l2: ?int}
|
||||
*/
|
||||
public function getPosition()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return position key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return implode('_', $this->position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current
|
||||
*
|
||||
* @return array{l0: string, l1: ?string, l2: ?int}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
$result = [
|
||||
'l0' => $this->position['l0'],
|
||||
'l1' => null,
|
||||
'l2' => null,
|
||||
];
|
||||
$result['l0'] = $this->position['l0'];
|
||||
|
||||
switch ($this->position['l0']) {
|
||||
case self::STEP_SEARCH_AND_REPLACE:
|
||||
$result['l1'] = $this->tablesIterator->current();
|
||||
$result['l2'] = $this->position['l2'];
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop iteration and free resource
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stopIteration(): void
|
||||
{
|
||||
switch ($this->position['l0']) {
|
||||
case self::STEP_SEARCH_AND_REPLACE:
|
||||
DUPX_UpdateEngine::commitAndSave();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return valid status
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return progress percentage
|
||||
*
|
||||
* @return float progress percentage
|
||||
*/
|
||||
public function getProgressPerc()
|
||||
{
|
||||
$result = 0;
|
||||
$s3Func = DUPX_S3_Funcs::getInstance();
|
||||
|
||||
switch ($this->position['l0']) {
|
||||
case self::STEP_SEARCH_AND_REPLACE_INIT:
|
||||
$result = 5;
|
||||
break;
|
||||
case self::STEP_SEARCH_AND_REPLACE:
|
||||
$lowLimit = 10;
|
||||
$higthLimit = 90;
|
||||
$stepDelta = $higthLimit - $lowLimit;
|
||||
$tables = DUPX_DB_Tables::getInstance()->getReplaceTablesNames();
|
||||
$tableDelta = $stepDelta / (count($tables) + 1);
|
||||
$singePagePerc = $tableDelta / ($s3Func->cTableParams['pages'] + 1);
|
||||
$result = round($lowLimit + ($tableDelta * (int) $this->position['l1']) + ($singePagePerc * (int) $this->position['l2']), 2);
|
||||
break;
|
||||
case self::STEP_REMOVE_MAINTENACE:
|
||||
$result = 90;
|
||||
break;
|
||||
case self::STEP_CREATE_ADMIN:
|
||||
$result = 92;
|
||||
break;
|
||||
case self::STEP_CONF_UPDATE:
|
||||
$result = 93;
|
||||
break;
|
||||
case self::STEP_GEN_UPD:
|
||||
$result = 94;
|
||||
break;
|
||||
case self::STEP_GEN_CLEAN:
|
||||
$result = 95;
|
||||
break;
|
||||
case self::STEP_NOTICE_TEST:
|
||||
$result = 96;
|
||||
break;
|
||||
case self::STEP_CLEANUP_TMP_FILES:
|
||||
$result = 97;
|
||||
break;
|
||||
case self::STEP_SET_FILE_PERMS:
|
||||
$result = 98;
|
||||
break;
|
||||
case self::STEP_FINAL_REPORT_NOTICES:
|
||||
$result = 100;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Chunk;
|
||||
|
||||
use Duplicator\Installer\Core\Deploy\Database\DbCleanup;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Chunking\ChunkingManager;
|
||||
use Duplicator\Libs\Chunking\Persistance\PersistanceAdapterInterface;
|
||||
use DUPX_S3_Funcs;
|
||||
use DUPX_UpdateEngine;
|
||||
|
||||
/**
|
||||
* Chunk manager step 3
|
||||
*/
|
||||
class SiteUpdateChunkManager extends ChunkingManager
|
||||
{
|
||||
/**
|
||||
* Return iterator
|
||||
*
|
||||
* @param mixed $extraData extra data for manager used on extended classes
|
||||
*
|
||||
* @return SiteUpdateChunkIterator
|
||||
*/
|
||||
protected function getIterator($extraData = null): SiteUpdateChunkIterator
|
||||
{
|
||||
return new SiteUpdateChunkIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return persistance adapter
|
||||
*
|
||||
* @param mixed $extraData extra data for manager used on extended classes
|
||||
*
|
||||
* @return SiteUpdateChunkPersistanceAdapter
|
||||
*/
|
||||
protected function getPersistance($extraData = null): SiteUpdateChunkPersistanceAdapter
|
||||
{
|
||||
return new SiteUpdateChunkPersistanceAdapter($GLOBALS["CHUNK_DATA_FILE_PATH"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec action on current position
|
||||
*
|
||||
* @param mixed $key Current iterator key
|
||||
* @param mixed $current Current iterator position
|
||||
*
|
||||
* @return bool return true on success, false on failure
|
||||
*/
|
||||
protected function action($key, $current)
|
||||
{
|
||||
$s3FuncsManager = DUPX_S3_Funcs::getInstance();
|
||||
|
||||
Log::info('CHUNK ACTION: CURRENT [' . implode('][', $current) . ']');
|
||||
|
||||
switch ($current['l0']) {
|
||||
case SiteUpdateChunkIterator::STEP_START:
|
||||
$s3FuncsManager->initLog();
|
||||
$s3FuncsManager->initChunkLog($this->maxIteration, $this->timeOut, $this->throttling, $GLOBALS['DATABASE_PAGE_SIZE']);
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_CLEANUP_OPTIONS:
|
||||
DbCleanup::cleanupOptions();
|
||||
DbCleanup::cleanupActivityLogs();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_CLEANUP_EXTREA:
|
||||
DbCleanup::cleanupExtra();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_CLEANUP_PACKAGES:
|
||||
DbCleanup::cleanupPackages();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_SEARCH_AND_REPLACE_INIT:
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_SEARCH_AND_REPLACE:
|
||||
DUPX_UpdateEngine::evaluateTableRows($current['l1'], $current['l2']);
|
||||
DUPX_UpdateEngine::commitAndSave();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_REMOVE_MAINTENACE:
|
||||
$s3FuncsManager->removeMaintenanceMode();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_CREATE_ADMIN:
|
||||
$s3FuncsManager->createNewAdminUser();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_CONF_UPDATE:
|
||||
$s3FuncsManager->configFilesUpdate();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_GEN_UPD:
|
||||
$s3FuncsManager->generalUpdate();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_GEN_CLEAN:
|
||||
$s3FuncsManager->generalCleanup();
|
||||
$s3FuncsManager->forceLogoutOfAllUsers();
|
||||
$s3FuncsManager->duplicatorMigrationInfoSet();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_NOTICE_TEST:
|
||||
$s3FuncsManager->checkForIndexHtml();
|
||||
$s3FuncsManager->noticeTest();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_CLEANUP_TMP_FILES:
|
||||
$s3FuncsManager->cleanupFiles();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_SET_FILE_PERMS:
|
||||
$s3FuncsManager->setFilePermsission();
|
||||
break;
|
||||
case SiteUpdateChunkIterator::STEP_FINAL_REPORT_NOTICES:
|
||||
$s3FuncsManager->finalReportNotices();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
/**
|
||||
* At each iteration save the status in case of exit with timeout
|
||||
*/
|
||||
return $this->saveData();
|
||||
}
|
||||
|
||||
/**
|
||||
* stop iteration without save data.
|
||||
* It is already saved every iteration.
|
||||
*
|
||||
* @param bool $saveData not used
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function stop($saveData = false)
|
||||
{
|
||||
return parent::stop(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Chunk;
|
||||
|
||||
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Utils\Log\LogHandler;
|
||||
use Duplicator\Libs\Chunking\Persistance\FileJsonPersistanceAdapter;
|
||||
use Duplicator\Libs\Chunking\Iterators\GenericSeekableIteratorInterface;
|
||||
use DUPX_S3_Funcs;
|
||||
|
||||
class SiteUpdateChunkPersistanceAdapter extends FileJsonPersistanceAdapter
|
||||
{
|
||||
/**
|
||||
* Called after loadPersistanceData, so the data is available
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function afterLoadPersistanceData()
|
||||
{
|
||||
$position = $this->getPersistanceData();
|
||||
if ($position != null) {
|
||||
Log::info("CHUNK LOAD DATA: POSITION " . implode(' / ', $position), 2);
|
||||
} else {
|
||||
Log::info("CHUNK LOAD DATA: IS NULL ");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete stored data if exists
|
||||
*
|
||||
* @return bool This function returns true on success, or FALSE on failure.
|
||||
*/
|
||||
protected function doDeletePersistanceData(): bool
|
||||
{
|
||||
Log::info("CHUNK DELETE STORED DATA FILE:" . Log::v2str($this->path), 2);
|
||||
return parent::doDeletePersistanceData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the data before write
|
||||
*
|
||||
* @param mixed $position the position to save
|
||||
* @param GenericSeekableIteratorInterface $it current iterator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function beforeWritePersistanceData($position, GenericSeekableIteratorInterface $it)
|
||||
{
|
||||
$s3Funcs = DUPX_S3_Funcs::getInstance();
|
||||
$s3Funcs->report['chunk'] = 1;
|
||||
$s3Funcs->report['chunkPos'] = $position;
|
||||
$s3Funcs->report['pass'] = 0;
|
||||
$s3Funcs->report['progress_perc'] = $it->getProgressPerc();
|
||||
$s3Funcs->saveData();
|
||||
|
||||
// managed output for timeout shutdown
|
||||
LogHandler::setShutdownReturn(LogHandler::SHUTDOWN_TIMEOUT, JsonSerialize::serialize($s3Funcs->getJsonReport()));
|
||||
|
||||
Log::info("CHUNK SAVE DATA: POSITION " . implode(' / ', $position), 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy;
|
||||
|
||||
use Duplicator\Installer\Core\Deploy\Plugins\PluginsManager;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use Exception;
|
||||
use mysqli;
|
||||
|
||||
class CleanUp
|
||||
{
|
||||
/**
|
||||
* Remove users without any permissions
|
||||
*
|
||||
* @param string $subSiteId ID of the Sub Site
|
||||
* @param mysqli $dbh Database Connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function removeUsersWithoutPermissions($subSiteId, $dbh): void
|
||||
{
|
||||
Log::info("\n--------------------\n" .
|
||||
"REMOVING USERS WITHOUT PERMISSIONS");
|
||||
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$basePrefix = $paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);
|
||||
|
||||
$usersTableName = DUPX_DB_Functions::getUserTableName();
|
||||
$userMetaTableName = DUPX_DB_Functions::getUserMetaTableName();
|
||||
$superAdminUsersIds = Helpers::getSuperAdminsUserIds($dbh);
|
||||
$superAdminUsersStr = implode(',', $superAdminUsersIds);
|
||||
|
||||
$excludeSuperAdminsClause = (!empty($superAdminUsersIds))
|
||||
? " AND {$usersTableName}.id NOT IN ({$superAdminUsersStr})"
|
||||
: '';
|
||||
|
||||
$usersWithCapabilitiesSql = "SELECT {$userMetaTableName}.user_id FROM {$userMetaTableName}
|
||||
WHERE {$userMetaTableName}.user_id = {$usersTableName}.id
|
||||
AND({$userMetaTableName}.meta_key = '{$basePrefix}capabilities'
|
||||
OR {$userMetaTableName}.meta_key REGEXP '{$basePrefix}[0-9]+_capabilities')";
|
||||
|
||||
$sql = "SELECT {$usersTableName}.ID FROM {$usersTableName}
|
||||
WHERE NOT EXISTS ({$usersWithCapabilitiesSql})" . $excludeSuperAdminsClause;
|
||||
|
||||
$results = DUPX_DB::queryToArray($dbh, $sql);
|
||||
$removeUsers = [];
|
||||
foreach ($results as [$userId]) {
|
||||
$removeUsers[] = $userId;
|
||||
}
|
||||
$removeUsers = array_unique($removeUsers);
|
||||
$removeUsersStr = '(' . implode(',', $removeUsers) . ')';
|
||||
|
||||
$hasUsersToRemove = count($removeUsers) > 0;
|
||||
if ($hasUsersToRemove) {
|
||||
Log::info("REMOVE USER IDS: " . Log::v2str($removeUsers));
|
||||
DUPX_DB::chunksDelete($dbh, $usersTableName, "id IN " . $removeUsersStr);
|
||||
DUPX_DB::chunksDelete($dbh, $userMetaTableName, "user_id IN " . $removeUsersStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all deactivated plugins
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function removeUnusedPlugins(): void
|
||||
{
|
||||
Log::info("\n--------------------\n" .
|
||||
"DELETING INACTIVE PLUGINS");
|
||||
|
||||
PluginsManager::getInstance()->uninstallInactivePlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all deactivated plugins
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function removeUnusedThemes(): void
|
||||
{
|
||||
Log::info("\n--------------------\n" .
|
||||
"DELETING INACTIVE THEMES");
|
||||
|
||||
Helpers::loadWP();
|
||||
|
||||
$themes = DUPX_ArchiveConfig::getInstance()->wpInfo->themes;
|
||||
|
||||
foreach ($themes as $theme) {
|
||||
//Log::info('THEME: '.Log::v2str($theme));
|
||||
|
||||
if (Helpers::isThemeEnable($theme)) {
|
||||
Log::info('THEME: ' . Log::v2str($theme->slug) . ' ENABLE');
|
||||
continue;
|
||||
}
|
||||
if (Helpers::haveChildEnable($theme, $themes)) {
|
||||
Log::info('THEME: ' . Log::v2str($theme->slug) . ' CHILD ENABLE');
|
||||
continue;
|
||||
}
|
||||
if (delete_theme($theme->stylesheet, '')) {
|
||||
Log::info('THEME: ' . Log::v2str($theme->slug) . ' DELETED');
|
||||
} else {
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
$errorMsg = "**ERROR** The Inactive theme " . $theme->slug . " deletion failed";
|
||||
Log::info($errorMsg);
|
||||
|
||||
$fullPath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_CONTENT_NEW) . '/themes/' . $theme->stylesheet;
|
||||
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => $errorMsg,
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsg' => 'Please delete the path ' . $fullPath . ' manually',
|
||||
'sections' => 'general',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Database;
|
||||
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use DUPX_DBInstall;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
|
||||
/**
|
||||
* Class with db cleanup functions
|
||||
*/
|
||||
class DbCleanup
|
||||
{
|
||||
/**
|
||||
* Cleanup extra entities (views, procs, funcs)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanupExtra(): void
|
||||
{
|
||||
if (InstState::isRestoreBackup() || InstState::isAddSiteOnMultisite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info("CLEANUP EXTRA");
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
if (!$paramsManager->getValue(PrmMng::PARAM_DB_VIEW_CREATION)) {
|
||||
self::dropViews();
|
||||
Log::info("\t- VIEWS DROPPED");
|
||||
} else {
|
||||
Log::info("\t- SKIP DROP VIEWS");
|
||||
}
|
||||
|
||||
if (!$paramsManager->getValue(PrmMng::PARAM_DB_PROC_CREATION)) {
|
||||
self::dropProcs();
|
||||
Log::info("\t- PROCS DROPPED");
|
||||
} else {
|
||||
Log::info("\t- SKIP DROP PROCS");
|
||||
}
|
||||
|
||||
if (!$paramsManager->getValue(PrmMng::PARAM_DB_FUNC_CREATION)) {
|
||||
self::dropFuncs();
|
||||
Log::info("\t- FUNCS DROPPED");
|
||||
} else {
|
||||
Log::info("\t- SKIP DROP FUNCS");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup packages
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanupPackages(): void
|
||||
{
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (InstState::isRestoreBackup()) {
|
||||
Log::info("REMOVE CURRENT PACKAGE IN BACKUP");
|
||||
self::deletePackageInBackup();
|
||||
} else {
|
||||
Log::info("EMPTY PACKAGES TABLE");
|
||||
self::emptyDuplicatorPackages();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup activity logs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function cleanupActivityLogs(): void
|
||||
{
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$activityLogsTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getActivityLogsTableName());
|
||||
if (DUPX_DB::tableExists($dbh, $activityLogsTable)) {
|
||||
$count = DUPX_DB::chunksDelete($dbh, $activityLogsTable, '1 = 1');
|
||||
Log::info(sprintf('DATABASE ACTIVITY LOGS DELETED [ROWS:%6d]', $count));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup options tables (remove transientes ..)
|
||||
*
|
||||
* @return int return number of items deleted
|
||||
*/
|
||||
public static function cleanupOptions()
|
||||
{
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$optionsTableList = [];
|
||||
$deleteOptionConds = [];
|
||||
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = PrmMng::getInstance()->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
foreach ($overwriteMapping as $map) {
|
||||
$targetInfo = $map->getTargetSiteInfo();
|
||||
$optionsTableList[] = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getOptionsTableName($targetInfo['blog_prefix']));
|
||||
}
|
||||
} else {
|
||||
$optionsTableList[] = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getOptionsTableName());
|
||||
$deleteOptionConds[] = '`option_name` = "dupli_opt_plugin_data_stats"';
|
||||
$deleteOptionConds[] = '`option_name` = "dupli_opt_unique_id"';
|
||||
}
|
||||
|
||||
$deleteOptionConds[] = '`option_name` LIKE "\_transient%"';
|
||||
$deleteOptionConds[] = '`option_name` LIKE "\_site\_transient%"';
|
||||
|
||||
$opts_delete = [];
|
||||
foreach ($archiveConfig->opts_delete as $value) {
|
||||
$opts_delete[] = '"' . mysqli_real_escape_string($dbh, $value) . '"';
|
||||
}
|
||||
if (count($opts_delete) > 0) {
|
||||
$deleteOptionConds[] = '`option_name` IN (' . implode(',', $opts_delete) . ')';
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($optionsTableList as $optionsTable) {
|
||||
$log = "CLEAN OPTIONS [" . $optionsTable . "]";
|
||||
foreach ($deleteOptionConds as $cond) {
|
||||
$log .= "\n\t" . $cond;
|
||||
}
|
||||
Log::info($log);
|
||||
$count += DUPX_DB::chunksDelete($dbh, $optionsTable, implode(' OR ', $deleteOptionConds));
|
||||
Log::info(sprintf('DATABASE OPTIONS DELETED [ROWS:%6d]', $count));
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete current package in backup
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function deletePackageInBackup()
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$packageId = DUPX_ArchiveConfig::getInstance()->packInfo->packageId;
|
||||
Log::info("CLEANUP CURRENT PACKAGE STATUS ID " . $packageId);
|
||||
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
if (!$overwriteData['packagesTableExists']) {
|
||||
// Clean current package only if is extracted from backup
|
||||
$packagesTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getPackagesTableName());
|
||||
DUPX_DB::mysqli_query($dbh, 'DELETE FROM `' . $packagesTable . '` WHERE `id` = ' . $packageId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty duplicator packages table
|
||||
*
|
||||
* @return int return number of packages deleted
|
||||
*/
|
||||
protected static function emptyDuplicatorPackages()
|
||||
{
|
||||
Log::info("CLEAN PACKAGES");
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$packagesTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getPackagesTableName());
|
||||
if (DUPX_DB::tableExists($dbh, $packagesTable)) {
|
||||
$count = DUPX_DB::chunksDelete($dbh, $packagesTable, '1 = 1');
|
||||
Log::info(sprintf('DATABASE PACKAGE DELETED [ROWS:%6d]', $count));
|
||||
return $count;
|
||||
}
|
||||
Log::info('DATABASE PACKAGES TABLE MISSING');
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Drop db procedures
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function dropProcs(): void
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$dbName = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_NAME);
|
||||
|
||||
$sql = "SHOW PROCEDURE STATUS WHERE db='{$dbName}'";
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
if (!($result = DUPX_DB::mysqli_query($dbh, $sql))) {
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'PROCEDURE CLEAN ERROR: ' . mysqli_error($dbh),
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to get list of PROCEDURES from database "%s".', $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
|
||||
Log::info("PROCEDURE CLEAN ERROR: Could not get list of PROCEDURES to drop them.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
$proc_name = $row[1];
|
||||
$sql = "DROP PROCEDURE IF EXISTS `" . mysqli_real_escape_string($dbh, $dbName) . "`.`" . mysqli_real_escape_string($dbh, $proc_name) . "`";
|
||||
if (!DUPX_DB::mysqli_query($dbh, $sql)) {
|
||||
$err = mysqli_error($dbh);
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'PROCEDURE CLEAN ERROR',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to remove PROCEDURE "%s" from database "%s".<br/>', $proc_name, $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, 'drop-proc-fail-msg');
|
||||
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'PROCEDURE CLEAN ERROR: ' . $err,
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to remove PROCEDURE "%s" from database "%s".', $proc_name, $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
|
||||
Log::info("PROCEDURE CLEAN ERROR: '{$err}'\n\t[SQL=" . substr($sql, 0, DUPX_DBInstall::QUERY_ERROR_LOG_LEN) . "...]\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'PROCEDURE CLEAN ERROR',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf(
|
||||
'PROCEDURE CLEAN FAILURE. ' .
|
||||
'Please remove all procedures from this database and try the installation again. ' .
|
||||
'If no procedures show in the database, then Drop the database and re-create it.<br/>' .
|
||||
'ERROR MESSAGE: %s <br/><br/>',
|
||||
mysqli_error($dbh)
|
||||
),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE_PREPEND_IF_EXISTS, 'drop-proc-fail-msg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop db functions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function dropFuncs(): void
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$dbName = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_NAME);
|
||||
|
||||
$sql = "SHOW FUNCTION STATUS WHERE db='{$dbName}'";
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
if (!($result = DUPX_DB::mysqli_query($dbh, $sql))) {
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'FUNCTION CLEAN ERROR: ' . mysqli_error($dbh),
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to get list of FUNCTIONS from database "%s".', $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
|
||||
Log::info("FUNCTION CLEAN ERROR: Could not get list of FUNCTIONS to drop them.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
$func_name = $row[1];
|
||||
$sql = "DROP FUNCTION IF EXISTS `" . mysqli_real_escape_string($dbh, $dbName) . "`.`" . mysqli_real_escape_string($dbh, $func_name) . "`";
|
||||
if (!DUPX_DB::mysqli_query($dbh, $sql)) {
|
||||
$err = mysqli_error($dbh);
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'FUNCTION CLEAN ERROR',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to remove FUNCTION "%s" from database "%s".<br/>', $func_name, $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, 'drop-func-fail-msg');
|
||||
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'FUNCTION CLEAN ERROR: ' . $err,
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to remove FUNCTION "%s" from database "%s".', $func_name, $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
|
||||
Log::info("FUNCTION CLEAN ERROR: '{$err}'\n\t[SQL=" . substr($sql, 0, DUPX_DBInstall::QUERY_ERROR_LOG_LEN) . "...]\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'FUNCTION CLEAN ERROR',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf(
|
||||
'FUNCTION CLEAN FAILURE. ' .
|
||||
'Please remove all functions from this database and try the installation again. ' .
|
||||
'If no functions show in the database, then Drop the database and re-create it.<br/>' .
|
||||
'ERROR MESSAGE: %s <br/><br/>',
|
||||
mysqli_error($dbh)
|
||||
),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE_PREPEND_IF_EXISTS, 'drop-func-fail-msg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop db views
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function dropViews(): void
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$dbName = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_NAME);
|
||||
|
||||
$sql = "SHOW FULL TABLES WHERE Table_Type = 'VIEW'";
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
if (!($result = DUPX_DB::mysqli_query($dbh, $sql))) {
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'VIEW CLEAN ERROR: ' . mysqli_error($dbh),
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to get list of VIEWS from database "%s"', $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
|
||||
Log::info("VIEW CLEAN ERROR: Could not get list of VIEWS to drop them.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ($row = mysqli_fetch_row($result)) {
|
||||
$view_name = $row[0];
|
||||
$sql = "DROP VIEW `" . mysqli_real_escape_string($dbh, $dbName) . "`.`" . mysqli_real_escape_string($dbh, $view_name) . "`";
|
||||
if (!DUPX_DB::mysqli_query($dbh, $sql)) {
|
||||
$err = mysqli_error($dbh);
|
||||
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'VIEW CLEAN ERROR',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to remove VIEW "%s" from database "%s".<br/>', $view_name, $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, 'drop-view-fail-msg');
|
||||
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'VIEW CLEAN ERROR: ' . $err,
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf('Unable to remove VIEW "%s" from database "%s"', $view_name, $dbName),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
|
||||
Log::info("VIEW CLEAN ERROR: '{$err}'\n\t[SQL=" . substr($sql, 0, DUPX_DBInstall::QUERY_ERROR_LOG_LEN) . "...]\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'VIEW CLEAN ERROR',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => sprintf(
|
||||
'VIEW CLEAN FAILURE. ' .
|
||||
'Please remove all views from this database and try the installation again. ' .
|
||||
'If no views show in the database, then Drop the database and re-create it.<br/>' .
|
||||
'ERROR MESSAGE: %s <br/><br/>',
|
||||
mysqli_error($dbh)
|
||||
),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE_PREPEND_IF_EXISTS, 'drop-view-fail-msg');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Database;
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use DUPX_Package;
|
||||
use Iterator;
|
||||
|
||||
/**
|
||||
* @implements Iterator<mixed,mixed>
|
||||
*/
|
||||
class DbDumpIterator implements Iterator
|
||||
{
|
||||
/** @var int */
|
||||
private $pos = 0;
|
||||
|
||||
/** @var array<int, array{0: string, 1: int}> */
|
||||
private $sqlDumpFiles = [];
|
||||
|
||||
/** @var int */
|
||||
private $totalSize = 0;
|
||||
|
||||
/**
|
||||
* Initialize iterator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$dbDumpDirPath = DUPX_Package::getSqlDumpDirPath();
|
||||
if (($tmpFiles = scandir($dbDumpDirPath)) === false) {
|
||||
throw new \Exception('Can\'t read sql dump dir.');
|
||||
}
|
||||
|
||||
$tmpFiles = array_values(array_filter($tmpFiles, fn($file) => preg_match('/\.sql$/', $file)));
|
||||
|
||||
if (count($tmpFiles) < 1) {
|
||||
throw new \Exception('Couldn\'t find any SQL dump files.');
|
||||
}
|
||||
|
||||
foreach ($tmpFiles as $i => $file) {
|
||||
$path = SnapIO::trailingslashit($dbDumpDirPath) . $file;
|
||||
$size = filesize($path);
|
||||
$this->sqlDumpFiles[$i] = [
|
||||
$path,
|
||||
$size,
|
||||
];
|
||||
|
||||
$this->totalSize += $size;
|
||||
}
|
||||
|
||||
$this->rewind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->pos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of SQL dump files
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->sqlDumpFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Current
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->sqlDumpFiles[$this->pos][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current file size
|
||||
*
|
||||
* @return int Current file size
|
||||
*/
|
||||
public function currentSize()
|
||||
{
|
||||
return $this->sqlDumpFiles[$this->pos][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current file size
|
||||
*
|
||||
* @return int Current file size
|
||||
*/
|
||||
public function totalSize()
|
||||
{
|
||||
return $this->totalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the total offset
|
||||
*
|
||||
* @param int $offsetInFile Offset in the current file
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function totalOffset($offsetInFile)
|
||||
{
|
||||
$result = 0;
|
||||
for ($i = 0; $i < $this->pos; $i++) {
|
||||
$result += $this->sqlDumpFiles[$i][1];
|
||||
}
|
||||
|
||||
return $result + $offsetInFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment the position
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next(): void
|
||||
{
|
||||
++$this->pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if is valid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return isset($this->sqlDumpFiles[$this->pos]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,522 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Database;
|
||||
|
||||
use Duplicator\Installer\Core\Deploy\Multisite;
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB_Tables;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Utils\ReplaceEngine\ReplaceItem;
|
||||
use Duplicator\Installer\Utils\ReplaceEngine\ReplaceMng;
|
||||
use DUPX_U;
|
||||
use DUPX_UpdateEngine;
|
||||
use Exception;
|
||||
|
||||
class DbReplace
|
||||
{
|
||||
/** @var string */
|
||||
protected $mainUrlOld = '';
|
||||
/** @var string */
|
||||
protected $mainUrlNew = '';
|
||||
/** @var bool */
|
||||
protected $forceReplaceSiteSubfolders = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$prmMng = PrmMng::getInstance();
|
||||
$this->mainUrlOld = $prmMng->getValue(PrmMng::PARAM_URL_OLD);
|
||||
$this->mainUrlNew = $prmMng->getValue(PrmMng::PARAM_URL_NEW);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set search and replace strings
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setSearchReplace(): bool
|
||||
{
|
||||
$this->setCustomReplaceList();
|
||||
|
||||
switch (InstState::getInstType()) {
|
||||
case InstState::TYPE_MSUBDOMAIN:
|
||||
case InstState::TYPE_MSUBFOLDER:
|
||||
$this->setMultisiteSearchReplace();
|
||||
$this->setGlobalSearchAndReplaceList();
|
||||
break;
|
||||
case InstState::TYPE_STANDALONE:
|
||||
$this->setStandalonSearchReplace();
|
||||
$this->setGlobalSearchAndReplaceList();
|
||||
break;
|
||||
case InstState::TYPE_SINGLE:
|
||||
$this->setGlobalSearchAndReplaceList();
|
||||
break;
|
||||
case InstState::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
$this->setAddOnMultisiteSearchReplace();
|
||||
$this->setGlobalSearchAndReplaceList();
|
||||
break;
|
||||
case InstState::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
case InstState::TYPE_RBACKUP_MSUBFOLDER:
|
||||
case InstState::TYPE_RBACKUP_SINGLE:
|
||||
case InstState::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
case InstState::TYPE_RECOVERY_MSUBFOLDER:
|
||||
case InstState::TYPE_RECOVERY_SINGLE:
|
||||
throw new Exception('Replace engine isn\'t available for restore backup mode');
|
||||
case InstState::TYPE_NOT_SET:
|
||||
default:
|
||||
throw new Exception('Invalid installer mode');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global search replace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setGlobalSearchAndReplaceList(): void
|
||||
{
|
||||
$srManager = ReplaceMng::getInstance();
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
// DIRS PATHS
|
||||
$this->addReplaceEnginePaths();
|
||||
|
||||
Log::info('GLOBAL SEARCH REPLACE ', Log::LV_DETAILED);
|
||||
|
||||
if (
|
||||
!InstState::isInstallerCreatedInThisLocation() &&
|
||||
!InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
$uploadUrlOld = $paramsManager->getValue(PrmMng::PARAM_URL_UPLOADS_OLD);
|
||||
$uploadUrlNew = $paramsManager->getValue(PrmMng::PARAM_URL_UPLOADS_NEW);
|
||||
|
||||
if (self::checkRelativeAndAbsoluteDiff($this->mainUrlOld, $this->mainUrlNew, $uploadUrlOld, $uploadUrlNew)) {
|
||||
$srManager->addItem($uploadUrlOld, $uploadUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
|
||||
$siteUrlOld = $paramsManager->getValue(PrmMng::PARAM_SITE_URL_OLD);
|
||||
$siteUrlNew = $paramsManager->getValue(PrmMng::PARAM_SITE_URL);
|
||||
if (self::checkRelativeAndAbsoluteDiff($this->mainUrlOld, $this->mainUrlNew, $siteUrlOld, $siteUrlNew)) {
|
||||
$srManager->addItem($siteUrlOld, $siteUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3);
|
||||
}
|
||||
|
||||
$srManager->addItem($this->mainUrlOld, $this->mainUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3);
|
||||
}
|
||||
|
||||
$pluginsUrlOld = $paramsManager->getValue(PrmMng::PARAM_URL_PLUGINS_OLD);
|
||||
$pluginsUrlNew = $paramsManager->getValue(PrmMng::PARAM_URL_PLUGINS_NEW);
|
||||
if (
|
||||
$this->forceReplaceSiteSubfolders ||
|
||||
self::checkRelativeAndAbsoluteDiff($this->mainUrlOld, $this->mainUrlNew, $pluginsUrlOld, $pluginsUrlNew)
|
||||
) {
|
||||
$srManager->addItem($pluginsUrlOld, $pluginsUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
|
||||
$mupluginsUrlOld = $paramsManager->getValue(PrmMng::PARAM_URL_MUPLUGINS_OLD);
|
||||
$mupluginsUrlNew = $paramsManager->getValue(PrmMng::PARAM_URL_MUPLUGINS_NEW);
|
||||
if (
|
||||
$this->forceReplaceSiteSubfolders ||
|
||||
self::checkRelativeAndAbsoluteDiff($this->mainUrlOld, $this->mainUrlNew, $mupluginsUrlOld, $mupluginsUrlNew)
|
||||
) {
|
||||
$srManager->addItem($mupluginsUrlOld, $mupluginsUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
|
||||
$contentUrlOld = $paramsManager->getValue(PrmMng::PARAM_URL_CONTENT_OLD);
|
||||
$contentUrlNew = $paramsManager->getValue(PrmMng::PARAM_URL_CONTENT_NEW);
|
||||
if (
|
||||
$this->forceReplaceSiteSubfolders ||
|
||||
self::checkRelativeAndAbsoluteDiff($this->mainUrlOld, $this->mainUrlNew, $contentUrlOld, $contentUrlNew)
|
||||
) {
|
||||
$srManager->addItem($contentUrlOld, $contentUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P2);
|
||||
}
|
||||
|
||||
// Replace email address (xyz@oldomain.com to xyz@newdomain.com).
|
||||
if ($paramsManager->getValue(PrmMng::PARAM_EMAIL_REPLACE)) {
|
||||
$at_old_domain = '@' . DUPX_U::getDomain($this->mainUrlOld);
|
||||
$at_new_domain = '@' . DUPX_U::getDomain($this->mainUrlNew);
|
||||
$srManager->addItem($at_old_domain, $at_new_domain, ReplaceItem::TYPE_STRING, DUPX_UpdateEngine::SR_PRORITY_LOW);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add paths to replace on sear/replace engine
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addReplaceEnginePaths(): void
|
||||
{
|
||||
$srManager = ReplaceMng::getInstance();
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
if ($paramsManager->getValue(PrmMng::PARAM_SKIP_PATH_REPLACE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$originalPaths = $archiveConfig->getRealValue('originalPaths');
|
||||
$mainPathOld = $paramsManager->getValue(PrmMng::PARAM_PATH_OLD);
|
||||
$mainPathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_NEW);
|
||||
|
||||
if (
|
||||
!InstState::isInstallerCreatedInThisLocation() ||
|
||||
!InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
$uploadPathOld = $paramsManager->getValue(PrmMng::PARAM_PATH_UPLOADS_OLD);
|
||||
$uploadPathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_UPLOADS_NEW);
|
||||
if (self::checkRelativeAndAbsoluteDiff($mainPathOld, $mainPathNew, $uploadPathOld, $uploadPathNew)) {
|
||||
$srManager->addItem($uploadPathOld, $uploadPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
if (
|
||||
$originalPaths->uploads != $uploadPathOld &&
|
||||
self::checkRelativeAndAbsoluteDiff($originalPaths->home, $mainPathNew, $originalPaths->uploads, $uploadPathNew)
|
||||
) {
|
||||
$srManager->addItem($originalPaths->uploads, $uploadPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
|
||||
$corePathOld = $paramsManager->getValue(PrmMng::PARAM_PATH_WP_CORE_OLD);
|
||||
$corePathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW);
|
||||
if (self::checkRelativeAndAbsoluteDiff($mainPathOld, $mainPathNew, $corePathOld, $corePathNew)) {
|
||||
$srManager->addItem($corePathOld, $corePathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3);
|
||||
}
|
||||
if (
|
||||
$originalPaths->abs != $corePathOld &&
|
||||
self::checkRelativeAndAbsoluteDiff($originalPaths->home, $mainPathNew, $originalPaths->abs, $corePathNew)
|
||||
) {
|
||||
$srManager->addItem($originalPaths->abs, $corePathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3);
|
||||
}
|
||||
|
||||
$srManager->addItem($mainPathOld, $mainPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3);
|
||||
if ($originalPaths->home != $mainPathOld) {
|
||||
$srManager->addItem($originalPaths->home, $mainPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3);
|
||||
}
|
||||
}
|
||||
|
||||
$pluginsPathOld = $paramsManager->getValue(PrmMng::PARAM_PATH_PLUGINS_OLD);
|
||||
$pluginsPathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_PLUGINS_NEW);
|
||||
if (self::checkRelativeAndAbsoluteDiff($mainPathOld, $mainPathNew, $pluginsPathOld, $pluginsPathNew)) {
|
||||
$srManager->addItem($pluginsPathOld, $pluginsPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
if (
|
||||
$originalPaths->plugins != $pluginsPathOld &&
|
||||
self::checkRelativeAndAbsoluteDiff($originalPaths->home, $mainPathNew, $originalPaths->plugins, $pluginsPathNew)
|
||||
) {
|
||||
$srManager->addItem($originalPaths->plugins, $pluginsPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
|
||||
$mupluginsPathOld = $paramsManager->getValue(PrmMng::PARAM_PATH_MUPLUGINS_OLD);
|
||||
$mupluginsPathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_MUPLUGINS_NEW);
|
||||
if (self::checkRelativeAndAbsoluteDiff($mainPathOld, $mainPathNew, $mupluginsPathOld, $mupluginsPathNew)) {
|
||||
$srManager->addItem($mupluginsPathOld, $mupluginsPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
if (
|
||||
$originalPaths->muplugins != $mupluginsPathOld &&
|
||||
self::checkRelativeAndAbsoluteDiff($originalPaths->home, $mainPathNew, $originalPaths->muplugins, $mupluginsPathNew)
|
||||
) {
|
||||
$srManager->addItem($originalPaths->muplugins, $mupluginsPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1);
|
||||
}
|
||||
|
||||
$contentPathOld = $paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_OLD);
|
||||
$contentPathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW);
|
||||
if (self::checkRelativeAndAbsoluteDiff($mainPathOld, $mainPathNew, $contentPathOld, $contentPathNew)) {
|
||||
$srManager->addItem($contentPathOld, $contentPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P2);
|
||||
}
|
||||
if (
|
||||
$originalPaths->wpcontent != $contentPathOld &&
|
||||
self::checkRelativeAndAbsoluteDiff($originalPaths->home, $mainPathNew, $originalPaths->wpcontent, $contentPathNew)
|
||||
) {
|
||||
$srManager->addItem($originalPaths->wpcontent, $contentPathNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom search and replace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setCustomReplaceList(): void
|
||||
{
|
||||
$srManager = ReplaceMng::getInstance();
|
||||
$searchList = PrmMng::getInstance()->getValue(PrmMng::PARAM_CUSTOM_SEARCH);
|
||||
$replaceList = PrmMng::getInstance()->getValue(PrmMng::PARAM_CUSTOM_REPLACE);
|
||||
|
||||
foreach ($searchList as $search_index => $search_for) {
|
||||
if (strlen($search_for) > 0) {
|
||||
$replace_with = $replaceList[$search_index];
|
||||
$srManager->addItem($search_for, $replace_with, ReplaceItem::TYPE_STRING, DUPX_UpdateEngine::SR_PRORITY_CUSTOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multisite search and replace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setMultisiteSearchReplace(): void
|
||||
{
|
||||
$srManager = ReplaceMng::getInstance();
|
||||
$prmMng = PrmMng::getInstance();
|
||||
$arcConfig = DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$oldMuUrls = $arcConfig->getOldUrlsArrayIdVal();
|
||||
$newMuUrls = Multisite::getMappedSubisteURLs();
|
||||
|
||||
$mainSite = $arcConfig->getMainSiteInfo();
|
||||
|
||||
// put the main sub site at the end
|
||||
$subsitesIds = array_keys($oldMuUrls);
|
||||
if (($delKey = array_search($mainSite->id, $subsitesIds)) !== false) {
|
||||
unset($subsitesIds[$delKey]);
|
||||
}
|
||||
$subsitesIds[] = $mainSite->id;
|
||||
|
||||
Log::info("MAIN URL :" . Log::v2str($arcConfig->getUrlFromSubsiteObj($mainSite)), Log::LV_DETAILED);
|
||||
Log::info('-- SUBSITES --' . "\n" . print_r($arcConfig->subsites, true), Log::LV_DEBUG);
|
||||
|
||||
foreach ($subsitesIds as $currentSubid) {
|
||||
if (($subSiteObj = $arcConfig->getSubsiteObjById($currentSubid)) === false) {
|
||||
Log::info('INVALID SUBSITE ID: ' . $currentSubid);
|
||||
throw new Exception('Invalid subsite id');
|
||||
}
|
||||
|
||||
Log::info('SUBSITE ID:' . $currentSubid . 'OLD URL: ' . $oldMuUrls[$currentSubid] . ' NEW URL ' . $newMuUrls[$currentSubid], Log::LV_DEBUG);
|
||||
|
||||
$isMainSite = $currentSubid == $mainSite->id;
|
||||
|
||||
$search = $oldMuUrls[$currentSubid];
|
||||
$replace = $newMuUrls[$currentSubid];
|
||||
|
||||
// get table for search and replace scope for subsites
|
||||
if ($prmMng->getValue(PrmMng::PARAM_MULTISITE_CROSS_SEARCH) == false && !$isMainSite) {
|
||||
$tables = DUPX_DB_Tables::getInstance()->getSubsiteTablesNewNames($currentSubid);
|
||||
} else {
|
||||
// global scope
|
||||
$tables = true;
|
||||
}
|
||||
|
||||
$priority = ($isMainSite) ? DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P4 : DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE;
|
||||
$srManager->addItem($search, $replace, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, $priority, $tables);
|
||||
|
||||
// Replace email address (xyz@oldomain.com to xyz@newdomain.com).
|
||||
if ($prmMng->getValue(PrmMng::PARAM_EMAIL_REPLACE)) {
|
||||
$at_old_domain = '@' . DUPX_U::getDomain($search);
|
||||
$at_new_domain = '@' . DUPX_U::getDomain($replace);
|
||||
$srManager->addItem($at_old_domain, $at_new_domain, ReplaceItem::TYPE_STRING, DUPX_UpdateEngine::SR_PRORITY_LOW, $tables);
|
||||
}
|
||||
|
||||
// for domain host and path priority is on main site
|
||||
$sUrlInfo = parse_url($search);
|
||||
$sHost = $sUrlInfo['host'] ?? '';
|
||||
$sPath = $sUrlInfo['path'] ?? '';
|
||||
$rUrlInfo = parse_url($replace);
|
||||
$rHost = $rUrlInfo['host'] ?? '';
|
||||
$rPath = $rUrlInfo['path'] ?? '';
|
||||
|
||||
// add path and host scope for custom columns in database
|
||||
$srManager->addItem($sHost, $rHost, ReplaceItem::TYPE_URL, $priority, 'domain_host');
|
||||
$srManager->addItem($sPath, $rPath, ReplaceItem::TYPE_STRING, $priority, 'domain_path');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set standalone search replace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setStandalonSearchReplace(): void
|
||||
{
|
||||
$srManager = ReplaceMng::getInstance();
|
||||
$prmMng = PrmMng::getInstance();
|
||||
$arcConfig = DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$subsiteId = $prmMng->getValue(PrmMng::PARAM_SUBSITE_ID);
|
||||
|
||||
$originalPaths = $arcConfig->getRealValue('originalPaths');
|
||||
$contentPathOld = $prmMng->getValue(PrmMng::PARAM_PATH_CONTENT_OLD);
|
||||
$uploadPathOld = $prmMng->getValue(PrmMng::PARAM_PATH_UPLOADS_OLD);
|
||||
|
||||
if (($subsiteObj = $arcConfig->getSubsiteObjById($subsiteId)) == false) {
|
||||
throw new Exception('Subite id ' . $subsiteId . ' not valid');
|
||||
}
|
||||
|
||||
if ($subsiteId == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldSubsiteUrl = $arcConfig->getUrlFromSubsiteObj($subsiteObj);
|
||||
$newUrl = $prmMng->getValue(PrmMng::PARAM_URL_NEW);
|
||||
|
||||
// Need to swap the subsite prefix for the main table prefix
|
||||
$uploadsDirSubOld = $uploadPathOld . '/sites/' . $subsiteId;
|
||||
$uploadsNew = $prmMng->getValue(PrmMng::PARAM_PATH_UPLOADS_NEW);
|
||||
|
||||
if (!$prmMng->getValue(PrmMng::PARAM_SKIP_PATH_REPLACE)) {
|
||||
$srManager->addItem($uploadsDirSubOld, $uploadsNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
if ($originalPaths->uploads != $uploadPathOld) {
|
||||
$uploadsDirSubOld = $originalPaths->uploads . '/sites/' . $subsiteId;
|
||||
$srManager->addItem($uploadsDirSubOld, $uploadsNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
$uploadsUrlNew = $prmMng->getValue(PrmMng::PARAM_URL_UPLOADS_NEW);
|
||||
|
||||
$uploadsUrlSubOld = $arcConfig->getUploadsUrlFromSubsiteObj($subsiteObj) . '/sites/' . $subsiteId;
|
||||
$srManager->addItem($uploadsUrlSubOld, $uploadsUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
|
||||
$uploadsUrlSubOld = $prmMng->getValue(PrmMng::PARAM_URL_UPLOADS_OLD) . '/sites/' . $subsiteId;
|
||||
$srManager->addItem($uploadsUrlSubOld, $uploadsUrlNew, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
|
||||
//Replace WP 3.4.5 subsite uploads path in DB
|
||||
if ($arcConfig->mu_generation === 1) {
|
||||
$blogsDirOld = $contentPathOld . '/blogs.dir/' . $subsiteId . '/files';
|
||||
|
||||
if (!$prmMng->getValue(PrmMng::PARAM_SKIP_PATH_REPLACE)) {
|
||||
$srManager->addItem($blogsDirOld, $uploadsNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
if ($originalPaths->wpcontent != $contentPathOld) {
|
||||
$blogsDirOld = $originalPaths->wpcontent . '/blogs.dir/' . $subsiteId . '/files';
|
||||
$srManager->addItem($blogsDirOld, $uploadsNew, ReplaceItem::TYPE_PATH, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
$subSiteFilesUrl = $prmMng->getValue(PrmMng::PARAM_URL_NEW) . '/files';
|
||||
$uploadUrlNew = $prmMng->getValue(PrmMng::PARAM_URL_UPLOADS_NEW);
|
||||
$srManager->addItem($subSiteFilesUrl, $uploadUrlNew, ReplaceItem::TYPE_URL, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH);
|
||||
}
|
||||
|
||||
$srManager->addItem($oldSubsiteUrl, $newUrl, ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN, DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set addon multiste search replace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setAddOnMultisiteSearchReplace(): void
|
||||
{
|
||||
$srManager = ReplaceMng::getInstance();
|
||||
$prmMng = PrmMng::getInstance();
|
||||
$arcConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$tablesMng = DUPX_DB_Tables::getInstance();
|
||||
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = PrmMng::getInstance()->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
|
||||
$mainSite = (array) $arcConfig->getMainSiteInfo();
|
||||
$this->forceReplaceSiteSubfolders = true;
|
||||
|
||||
foreach ($overwriteMapping as $map) {
|
||||
$sourceInfo = $map->getSourceSiteInfo();
|
||||
$targetInfo = $map->getTargetSiteInfo();
|
||||
|
||||
$isMainSite = ($sourceInfo['id'] == $mainSite['id']);
|
||||
|
||||
// get table for search and replace scope for subsites/
|
||||
$scope = $tablesMng->getSubsiteTablesNewNames($sourceInfo['id']);
|
||||
|
||||
if (!$prmMng->getValue(PrmMng::PARAM_SKIP_PATH_REPLACE)) {
|
||||
$srManager->addItem(
|
||||
$sourceInfo['fullUploadPath'],
|
||||
$targetInfo['fullUploadPath'],
|
||||
ReplaceItem::TYPE_PATH,
|
||||
($sourceInfo['id'] == 1 ? DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1 : DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH),
|
||||
$scope
|
||||
);
|
||||
if ($sourceInfo['fullUploadPath'] != $sourceInfo['fullUploadSafePath']) {
|
||||
$srManager->addItem(
|
||||
$sourceInfo['fullUploadSafePath'],
|
||||
$targetInfo['fullUploadPath'],
|
||||
ReplaceItem::TYPE_PATH,
|
||||
($sourceInfo['id'] == 1 ? DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1 : DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE_HIGH),
|
||||
$scope
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$srManager->addItem(
|
||||
$sourceInfo['fullUploadUrl'],
|
||||
$targetInfo['fullUploadUrl'],
|
||||
ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN,
|
||||
($isMainSite ? DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P1 : DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE),
|
||||
$scope
|
||||
);
|
||||
|
||||
if (!($isMainSite && InstState::isInstallerCreatedInThisLocation())) {
|
||||
$srManager->addItem(
|
||||
$sourceInfo['fullHomeUrl'],
|
||||
$targetInfo['fullHomeUrl'],
|
||||
ReplaceItem::TYPE_URL_NORMALIZE_DOMAIN,
|
||||
($isMainSite ? DUPX_UpdateEngine::SR_PRORITY_GENERIC_SUBST_P3 : DUPX_UpdateEngine::SR_PRORITY_NETWORK_SUBSITE),
|
||||
$scope
|
||||
);
|
||||
}
|
||||
|
||||
// Replace email address (xyz@oldomain.com to xyz@newdomain.com).
|
||||
if ($prmMng->getValue(PrmMng::PARAM_EMAIL_REPLACE)) {
|
||||
$at_old_domain = '@' . DUPX_U::getDomain($sourceInfo['fullUploadUrl']);
|
||||
$at_new_domain = '@' . DUPX_U::getDomain($targetInfo['fullUploadUrl']);
|
||||
$srManager->addItem(
|
||||
$at_old_domain,
|
||||
$at_new_domain,
|
||||
ReplaceItem::TYPE_STRING,
|
||||
DUPX_UpdateEngine::SR_PRORITY_LOW,
|
||||
$scope
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if sub path if different
|
||||
*
|
||||
* @param string $mainOld main old path
|
||||
* @param string $mainNew main new path
|
||||
* @param string $old old sub path
|
||||
* @param string $new new sub path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function checkRelativeAndAbsoluteDiff($mainOld, $mainNew, $old, $new): bool
|
||||
{
|
||||
$mainOld = SnapIO::safePath($mainOld);
|
||||
$mainNew = SnapIO::safePath($mainNew);
|
||||
$old = SnapIO::safePath($old);
|
||||
$new = SnapIO::safePath($new);
|
||||
|
||||
$log = "CHECK REL AND ABS DIF\n" .
|
||||
"\tMAIN OLD: " . Log::v2str($mainOld) . "\n" .
|
||||
"\tMAIN NEW: " . Log::v2str($mainNew) . "\n" .
|
||||
"\tOLD: " . Log::v2str($old) . "\n" .
|
||||
"\tNEW: " . Log::v2str($new);
|
||||
Log::info($log, Log::LV_DEBUG);
|
||||
|
||||
$isRelativePathDifferent = substr($old, strlen($mainOld)) !== substr($new, strlen($mainNew));
|
||||
|
||||
if ($old === $mainOld) {
|
||||
// If the main path coincides with the current path it is not possible to distinguish
|
||||
// the two paths and make different substitutions so I skip the substitution
|
||||
Log::info("\t*** RESULT: FALSE", Log::LV_DEBUG);
|
||||
return false;
|
||||
} elseif (strpos($old, $mainOld) !== 0 || strpos($new, $mainNew) !== 0 || $isRelativePathDifferent) {
|
||||
Log::info("\t*** RESULT: TRUE", Log::LV_DEBUG);
|
||||
return true;
|
||||
} else {
|
||||
Log::info("\t*** RESULT: FALSE", Log::LV_DEBUG);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,663 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Database;
|
||||
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamDescUsers;
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Models\ImportUser;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\ViewHelpers\Resources;
|
||||
use VendorDuplicator\Amk\JsonSerialize\AbstractJsonSerializable;
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use DUPX_DB_Tables;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use DUPX_UpdateEngine;
|
||||
use Error;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class DbUserMode
|
||||
*
|
||||
* Manages WordPress user table special handling during site migration.
|
||||
* Provides key functionalities for:
|
||||
* 1. User Data Management (ID mapping, remapping, meta data migration)
|
||||
* 2. Security Features (sensitive data protection, user permissions)
|
||||
* 3. Multisite Support (table prefix changes, user relationships)
|
||||
* 4. Data Integrity (consistency, auto-increment values, meta key updates)
|
||||
*
|
||||
* It's separated from core table handling due to special security and migration requirements.
|
||||
*/
|
||||
class DbUserMode extends AbstractJsonSerializable
|
||||
{
|
||||
/** @var ImportUser[] */
|
||||
protected $targetUsersById = [];
|
||||
/** @var ImportUser[] */
|
||||
protected $targetUsersByMail = [];
|
||||
/** @var ImportUser[] */
|
||||
protected $targetUsersByLogin = [];
|
||||
/** @var int */
|
||||
protected $usersAutoIncrement = -1;
|
||||
/** @var int */
|
||||
protected $usersMetaAutoIncrement = -1;
|
||||
/** @var bool[] */
|
||||
protected $addedUsers = [];
|
||||
/** @var int[] */
|
||||
protected $mappingIds = [];
|
||||
/** @var (bool|int[])[] */
|
||||
protected $existingsMetaIsd = [];
|
||||
/** @var int */
|
||||
protected $userTableNumCols = 0;
|
||||
/** @var string */
|
||||
protected $userMode = ParamDescUsers::USER_MODE_OVERWRITE;
|
||||
protected string $prefixMetaRegexCheck;
|
||||
/** @var array<string, string> */
|
||||
protected $prefixMetaMapping = [];
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$prmMng = PrmMng::getInstance();
|
||||
$this->userMode = ParamDescUsers::getUsersMode();
|
||||
$this->prefixMetaRegexCheck = '/^' . preg_quote(DUPX_ArchiveConfig::getInstance()->wp_tableprefix, '/') . '(?:(\d+)_)?(.*)$/';
|
||||
|
||||
switch (InstState::getInstType()) {
|
||||
case InstState::TYPE_SINGLE:
|
||||
$this->addPrefixMetaMapping(
|
||||
0,
|
||||
$prmMng->getValue(PrmMng::PARAM_DB_TABLE_PREFIX)
|
||||
);
|
||||
break;
|
||||
case InstState::TYPE_STANDALONE:
|
||||
$this->addPrefixMetaMapping(
|
||||
$prmMng->getValue(PrmMng::PARAM_SUBSITE_ID),
|
||||
$prmMng->getValue(PrmMng::PARAM_DB_TABLE_PREFIX)
|
||||
);
|
||||
break;
|
||||
case InstState::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = $prmMng->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
|
||||
foreach ($overwriteMapping as $map) {
|
||||
if (($targetInfo = $map->getTargetSiteInfo()) == false) {
|
||||
throw new Exception('Target site info ' . $map->getTargetId() . ' don\'t exists');
|
||||
}
|
||||
|
||||
$this->addPrefixMetaMapping(
|
||||
$map->getSourceId(),
|
||||
$targetInfo['blog_prefix']
|
||||
);
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_MSUBDOMAIN:
|
||||
case InstState::TYPE_MSUBFOLDER:
|
||||
case InstState::TYPE_RBACKUP_SINGLE:
|
||||
case InstState::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
case InstState::TYPE_RBACKUP_MSUBFOLDER:
|
||||
case InstState::TYPE_RECOVERY_SINGLE:
|
||||
case InstState::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
case InstState::TYPE_RECOVERY_MSUBFOLDER:
|
||||
break;
|
||||
case InstState::TYPE_NOT_SET:
|
||||
throw new Exception('Cannot change setup with current installation type [' . InstState::getInstType() . ']');
|
||||
default:
|
||||
throw new Exception('Unknown mode');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta prefix meta mapping
|
||||
*
|
||||
* @param int $subsiteId subsite id
|
||||
* @param string $prefix replace value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addPrefixMetaMapping($subsiteId, $prefix)
|
||||
{
|
||||
Log::info('ADD PREFIX META MAP ID ' . $subsiteId . ' ' . $prefix);
|
||||
$key = ($subsiteId == 1 ? 0 : $subsiteId);
|
||||
$this->prefixMetaMapping[$key] = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function renames the user tables of the target site, also updates the user meta keys
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function moveTargetUserTablesOnCurrentPrefix(): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
if (ParamDescUsers::getUsersMode() === ParamDescUsers::USER_MODE_OVERWRITE) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info("\nKEEP TARGET SITE USERS TABLES - USER MODE " . ParamDescUsers::getUsersMode());
|
||||
|
||||
$dbFunc = DUPX_DB_Functions::getInstance();
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
|
||||
if ($overwriteData['table_prefix'] == $paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX)) {
|
||||
Log::info('TABLE NAMES ARE THE SAME, SO SKIP USERS TABLES RENAME');
|
||||
return;
|
||||
}
|
||||
|
||||
$targetUserTable = DUPX_DB_Functions::getUserTableName($overwriteData['table_prefix']);
|
||||
$targetUserMetaTable = DUPX_DB_Functions::getUserMetaTableName($overwriteData['table_prefix']);
|
||||
$currentUserTableName = DUPX_DB_Functions::getUserTableName();
|
||||
$currentUserMetaTableName = DUPX_DB_Functions::getUserMetaTableName();
|
||||
|
||||
$dbFunc->renameTable($targetUserTable, $currentUserTableName, true);
|
||||
$dbFunc->renameTable($targetUserMetaTable, $currentUserMetaTableName, true);
|
||||
|
||||
// Update table prefix on meta key
|
||||
DUPX_UpdateEngine::updateTablePrefix(
|
||||
$dbFunc->dbConnection(),
|
||||
$currentUserMetaTableName,
|
||||
'meta_key',
|
||||
$overwriteData['table_prefix'],
|
||||
$paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX)
|
||||
);
|
||||
Log::info("USER TABLES RENAMED");
|
||||
}
|
||||
|
||||
/**
|
||||
* This function removes all meta keys of the current prefix in the usermeta table.
|
||||
* This is needed to replace them with the meta keys that will be imported
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAllUserMetaKeysOfCurrentPrefix(): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
if (
|
||||
ParamDescUsers::getUsersMode() !== ParamDescUsers::USER_MODE_IMPORT_USERS ||
|
||||
!InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
|
||||
$loggedInUserId = (int) $overwriteData['loggedUser']['ID'];
|
||||
|
||||
foreach ($this->prefixMetaMapping as $overwriteId => $prefix) {
|
||||
$where = 'user_id != ' . $loggedInUserId;
|
||||
$escPergPrefix = mysqli_real_escape_string($dbh, SnapDB::quoteRegex($prefix));
|
||||
|
||||
if ($prefix == $overwriteData['table_prefix']) {
|
||||
Log::info("\nREMOVE EXISTING USER META KEY WITH PREFIX " . $prefix . ' EXCEPT ' . $prefix . '[0-9]+_');
|
||||
// SELECT * FROM `prefix_usermeta` WHERE user_id != 2 AND meta_key REGEXP "^prefix_" AND meta_key NOT REGEXP "^prefix_[0-9]+_"
|
||||
$where .= ' AND meta_key REGEXP "^' . $escPergPrefix . '" AND meta_key NOT REGEXP "^' . $escPergPrefix . '[0-9]+_"';
|
||||
} else {
|
||||
Log::info("\nREMOVE EXISTING USER META KEY WITH PREFIX " . $prefix);
|
||||
// SELECT * FROM `prefix_usermeta` WHERE user_id != 2 AND meta_key REGEXP "^prefix_2_"
|
||||
$where .= ' AND meta_key REGEXP "^' . $escPergPrefix . '"';
|
||||
}
|
||||
|
||||
DUPX_DB::chunksDelete($dbh, DUPX_DB_Functions::getUserMetaTableName(), $where);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter props on json encode
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
$props = array_keys(get_object_vars($this));
|
||||
return array_diff($props, ['targetUsersByMail', 'targetUsersByLogin']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after json decode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
foreach ($this->targetUsersById as $user) {
|
||||
$this->targetUsersByMail[$user->getMail()] = $user;
|
||||
$this->targetUsersByLogin[$user->getLogin()] = $user;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of columns that contain user id to remap in an array( table => numberColumn)
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected static function getTableColIdsToRemap()
|
||||
{
|
||||
static $remapTables = null;
|
||||
if (is_null($remapTables)) {
|
||||
$remapTables = [];
|
||||
|
||||
foreach (DUPX_DB_Tables::getInstance()->getTablesByNameWithoutPrefix('posts') as $table) {
|
||||
$remapTables[$table] = 1;
|
||||
}
|
||||
|
||||
Log::info('REMAP USERS TABLES/COLUMN ' . Log::v2str($remapTables));
|
||||
}
|
||||
return $remapTables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load from users table the user list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initTargetSiteUsersData(): void
|
||||
{
|
||||
if ($this->userMode !== ParamDescUsers::USER_MODE_IMPORT_USERS) {
|
||||
return;
|
||||
}
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
Log::info('INIT IMPORT TARGET USER TABLE DATA');
|
||||
|
||||
$dbName = mysqli_real_escape_string($dbh, PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_NAME));
|
||||
$userTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getUserTableName());
|
||||
|
||||
// count num cols of user table, can be different from source to target
|
||||
$query = 'SELECT count(*) AS num_cols FROM information_schema.columns WHERE table_schema = "' . $dbName . '" AND table_name = "' . $userTable . '"';
|
||||
if (($queryRes = DUPX_DB::mysqli_query($dbh, $query)) === false) {
|
||||
$err = mysqli_error($dbh);
|
||||
throw new Exception('Query error: ' . $err);
|
||||
}
|
||||
$row = $queryRes->fetch_array();
|
||||
$this->userTableNumCols = (int) $row[0];
|
||||
Log::info('USER TABLE COLUMNS COUNT ' . $this->userTableNumCols, Log::LV_DETAILED);
|
||||
|
||||
$query = 'SELECT `ID`,`user_login`,`user_email` FROM `' . $userTable . '`';
|
||||
if (($queryRes = DUPX_DB::mysqli_query($dbh, $query)) === false) {
|
||||
$err = mysqli_error($dbh);
|
||||
throw new Exception('Query error: ' . $err);
|
||||
}
|
||||
|
||||
$this->usersAutoIncrement = -1;
|
||||
while ($row = $queryRes->fetch_assoc()) {
|
||||
$rowId = (int) $row['ID'];
|
||||
$user = new ImportUser($rowId, $row['user_login'], $row['user_email']);
|
||||
|
||||
$this->targetUsersById[$user->getId()] = $user;
|
||||
$this->targetUsersByMail[$user->getMail()] = $user;
|
||||
$this->targetUsersByLogin[$user->getLogin()] = $user;
|
||||
|
||||
if ($rowId > $this->usersAutoIncrement) {
|
||||
$this->usersAutoIncrement = $rowId;
|
||||
}
|
||||
}
|
||||
$this->usersAutoIncrement++;
|
||||
$queryRes->free_result();
|
||||
Log::info('EXISTING USERS COUNT ' . count($this->targetUsersById), Log::LV_DETAILED);
|
||||
Log::info('USERS TABLE AUTOINCREMENT VALUE ' . $this->usersAutoIncrement, Log::LV_DETAILED);
|
||||
|
||||
$this->initTargetSiteUserMetaData();
|
||||
}
|
||||
|
||||
/**
|
||||
* For each existing meta key, a list of IDs is associated with each user who has that key or true if all users have the key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initTargetSiteUserMetaData()
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
Log::info('INIT IMPORT TARGET USERMETA TABLE DATA');
|
||||
|
||||
$userTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getUserTableName());
|
||||
$userMetaTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getUserMetaTableName());
|
||||
|
||||
$query = 'SELECT max(umeta_id) AS maxId FROM `' . $userMetaTable . '`';
|
||||
if (($queryRes = DUPX_DB::mysqli_query($dbh, $query)) === false) {
|
||||
$err = mysqli_error($dbh);
|
||||
throw new Exception('Query error: ' . $err);
|
||||
}
|
||||
$row = $queryRes->fetch_assoc();
|
||||
$this->usersMetaAutoIncrement = ((int) $row['maxId']) + 1;
|
||||
$queryRes->free_result();
|
||||
Log::info('USERMETA TABLE AUTOINCREMENT VALUE ' . $this->usersAutoIncrement, Log::LV_DETAILED);
|
||||
|
||||
$query = 'SELECT COUNT(*) FROM `' . $userTable . '`';
|
||||
if (($queryRes = DUPX_DB::mysqli_query($dbh, $query)) === false) {
|
||||
$err = mysqli_error($dbh);
|
||||
throw new Exception('Query error: ' . $err);
|
||||
}
|
||||
$row = $queryRes->fetch_array();
|
||||
$maxNumIds = $row[0];
|
||||
|
||||
$query = 'SELECT `meta_key`, IF(COUNT(`user_id`) >= ' . $maxNumIds . ', "ALL", GROUP_CONCAT(`user_id` ORDER BY `user_id` ASC)) AS IDS ' .
|
||||
'FROM `' . $userMetaTable . '`' .
|
||||
'WHERE `user_id` IN (SELECT `ID` FROM `' . $userTable . '`) GROUP BY meta_key';
|
||||
if (($queryRes = DUPX_DB::mysqli_query($dbh, $query)) === false) {
|
||||
$err = mysqli_error($dbh);
|
||||
throw new Exception('Query error: ' . $err);
|
||||
}
|
||||
|
||||
while ($row = $queryRes->fetch_assoc()) {
|
||||
$this->existingsMetaIsd[$row['meta_key']] = (
|
||||
($row['IDS'] === 'ALL') ?
|
||||
true :
|
||||
array_map('intval', explode(',', $row['IDS']))
|
||||
);
|
||||
}
|
||||
Log::info('NUM META KEYS READ ' . count($this->existingsMetaIsd), Log::LV_DETAILED);
|
||||
$queryRes->free_result();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply inser query user fixes
|
||||
*
|
||||
* @param string $query query string
|
||||
*
|
||||
* @return string if the string is empty no query must be executed
|
||||
*/
|
||||
public function applyUsersFixes(&$query)
|
||||
{
|
||||
if ($this->userMode == ParamDescUsers::USER_MODE_OVERWRITE) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
if (preg_match('/^\s*(?:\/\*.*\*\/|#.*\n|--.*\n)?\s*INSERT\s+(?:IGNORE\s+)?INTO\s+`?([^\s`]*?)`?\s+VALUES/s', $query, $matches) !== 1) {
|
||||
return $query;
|
||||
}
|
||||
|
||||
$tableName = SnapDB::parsedQueryValueToString($matches[1]);
|
||||
|
||||
if ($this->userMode == ParamDescUsers::USER_MODE_IMPORT_USERS) {
|
||||
if ($tableName == DUPX_DB_Functions::getUserTableName()) {
|
||||
return $this->getUserTableQueryFix(SnapDB::getValuesFromQueryInsert($query));
|
||||
} elseif ($tableName == DUPX_DB_Functions::getUserMetaTableName()) {
|
||||
return $this->getUserMetaTableQueryFix(SnapDB::getValuesFromQueryInsert($query));
|
||||
}
|
||||
}
|
||||
|
||||
$tablesColRemap = self::getTableColIdsToRemap();
|
||||
if (in_array($tableName, array_keys($tablesColRemap))) {
|
||||
return $this->getTableUserRemapQueryFix(
|
||||
$tableName,
|
||||
$tablesColRemap[$tableName],
|
||||
SnapDB::getValuesFromQueryInsert($query)
|
||||
);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate import final report
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function generateImportReport(): void
|
||||
{
|
||||
if ($this->userMode !== ParamDescUsers::USER_MODE_IMPORT_USERS) {
|
||||
return;
|
||||
}
|
||||
|
||||
$numAdded = 0;
|
||||
$numChanged = 0;
|
||||
|
||||
if (($fp = fopen(DUPX_INIT . '/' . self::getCsvReportName(), 'w')) === false) {
|
||||
Log::info('Can\'t open report file ' . DUPX_INIT . '/' . self::getCsvReportName());
|
||||
} else {
|
||||
fputcsv($fp, ImportUser::getArrayReportTitles(), ',', '"', '\\');
|
||||
}
|
||||
|
||||
foreach ($this->targetUsersById as $user) {
|
||||
if ($user->isAdded()) {
|
||||
$numAdded++;
|
||||
} elseif ($user->isChanged()) {
|
||||
$numChanged++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if ($fp != false) {
|
||||
fputcsv($fp, $user->getArrayReport(), ',', '"', '\\');
|
||||
}
|
||||
}
|
||||
|
||||
if ($fp != false) {
|
||||
fclose($fp);
|
||||
$csvUrl = Resources::getAssetsBaseUrl() . '/' . self::getCsvReportName();
|
||||
} else {
|
||||
$csvUrl = false;
|
||||
}
|
||||
|
||||
$longMsg = dupxTplRender(
|
||||
'parts/reports/import_report',
|
||||
[
|
||||
'numAdded' => $numAdded,
|
||||
'numChanged' => $numChanged,
|
||||
'csvUrl' => $csvUrl,
|
||||
],
|
||||
false
|
||||
);
|
||||
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
$nManager->addFinalReportNotice(
|
||||
[
|
||||
'shortMsg' => 'User import report',
|
||||
'level' => DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => $longMsg,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'general',
|
||||
]
|
||||
);
|
||||
$nManager->saveNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return csv report file name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getCsvReportName()
|
||||
{
|
||||
return 'dup-installer-import-report__' . Security::getInstance()->getSecondaryPackageHash() . '.csv';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply query fix for user table
|
||||
*
|
||||
* @param mixed[] $queryValues two dimensional array where each item is a row containing the list of values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserTableQueryFix($queryValues)
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$resultValues = [];
|
||||
$numColsQueryVals = isset($queryValues[0]) ? count($queryValues[0]) : 0;
|
||||
$colsDeltaDiff = $this->userTableNumCols - $numColsQueryVals;
|
||||
|
||||
foreach ($queryValues as $rowVals) {
|
||||
$rowId = SnapDB::parsedQueryValueToInt($rowVals[0]);
|
||||
$rowLogin = SnapDB::parsedQueryValueToString($rowVals[1]);
|
||||
$rowMail = SnapDB::parsedQueryValueToString($rowVals[4]);
|
||||
|
||||
if (isset($this->targetUsersByMail[$rowMail])) {
|
||||
$targetUser = $this->targetUsersByMail[$rowMail];
|
||||
$targetId = $targetUser->getId();
|
||||
$targetLogin = $targetUser->getLogin();
|
||||
|
||||
if ($rowId != $targetId) {
|
||||
$targetUser->setOldId($rowId);
|
||||
$this->mappingIds[$rowId] = $targetId;
|
||||
}
|
||||
|
||||
if ($rowLogin != $targetLogin) {
|
||||
$targetUser->setOldLogin($rowLogin);
|
||||
}
|
||||
} else {
|
||||
$rowVals[0] = $targetId = $this->usersAutoIncrement;
|
||||
$this->usersAutoIncrement++;
|
||||
|
||||
if ($rowId != $targetId) {
|
||||
$this->mappingIds[$rowId] = $targetId;
|
||||
}
|
||||
|
||||
$newLogin = $rowLogin;
|
||||
$postfixIndex = 0;
|
||||
while (isset($this->targetUsersByLogin[$newLogin])) {
|
||||
$postfixIndex++;
|
||||
$newLogin = $rowLogin . $postfixIndex;
|
||||
}
|
||||
if ($rowLogin != $newLogin) {
|
||||
$rowVals[1] = '"' . mysqli_real_escape_string($dbh, $newLogin) . '"';
|
||||
|
||||
$niceName = SnapDB::parsedQueryValueToString($rowVals[3]);
|
||||
$rowVals[3] = '"' . mysqli_real_escape_string($dbh, $niceName) . $postfixIndex . '"';
|
||||
|
||||
$displayName = SnapDB::parsedQueryValueToString($rowVals[9]);
|
||||
if ($rowLogin == $displayName) {
|
||||
$rowVals[9] = '"' . mysqli_real_escape_string($dbh, $newLogin) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
$user = new ImportUser($targetId, $newLogin, $rowMail, $rowId, $rowLogin, true);
|
||||
$this->targetUsersById[$user->getId()] = $user;
|
||||
$this->targetUsersByMail[$user->getMail()] = $user;
|
||||
$this->targetUsersByLogin[$user->getLogin()] = $user;
|
||||
$this->addedUsers[$user->getOldId()] = true;
|
||||
|
||||
if ($colsDeltaDiff == 0) {
|
||||
$resultValues[] = $rowVals;
|
||||
} elseif ($colsDeltaDiff < 0) {
|
||||
$resultValues[] = array_slice($rowVals, 0, $this->userTableNumCols);
|
||||
} else {
|
||||
for ($i = 0; $i < $colsDeltaDiff; $i++) {
|
||||
$rowVals[] = '"0"';
|
||||
}
|
||||
$resultValues[] = $rowVals;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($resultValues)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'INSERT IGNORE INTO `' . mysqli_real_escape_string($dbh, DUPX_DB_Functions::getUserTableName()) . '` ' .
|
||||
'VALUES ' . SnapDB::getQueryInsertValuesFromArray($resultValues) . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply query fix for usermeta table
|
||||
*
|
||||
* @param mixed[] $queryValues two dimensional array where each item is a row containing the list of values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserMetaTableQueryFix($queryValues)
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
$resultValues = [];
|
||||
|
||||
// reset value
|
||||
$user = new ImportUser(-1, '', '');
|
||||
|
||||
$prefixMatches = null;
|
||||
foreach ($queryValues as $rowVals) {
|
||||
try {
|
||||
$rowUserId = SnapDB::parsedQueryValueToInt($rowVals[1]);
|
||||
$rowMetakey = SnapDB::parsedQueryValueToString($rowVals[2]);
|
||||
|
||||
if ($user->getId() != $rowUserId) {
|
||||
$userId = $this->mappingIds[$rowUserId] ?? $rowUserId;
|
||||
if (isset($this->targetUsersById[$userId])) {
|
||||
$user = $this->targetUsersById[$userId];
|
||||
} else {
|
||||
// This happens if there is a meta key that has a user id that does not belong to any user, it is an anomalous thing so it is skipped.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match($this->prefixMetaRegexCheck, $rowMetakey, $prefixMatches) === 1) {
|
||||
$currentId = (int)$prefixMatches[1];
|
||||
if (!isset($this->prefixMetaMapping[$currentId])) {
|
||||
// if the attribute is not of the selected sub-site then it is not inserted in the import
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowMetakey = $this->prefixMetaMapping[$currentId] . $prefixMatches[2];
|
||||
$rowVals[2] = '"' . mysqli_real_escape_string($dbh, $rowMetakey) . '"';
|
||||
}
|
||||
|
||||
if (
|
||||
$user->isAdded() ||
|
||||
!isset($this->existingsMetaIsd[$rowMetakey]) ||
|
||||
(
|
||||
$this->existingsMetaIsd[$rowMetakey] !== true &&
|
||||
SnapUtil::binarySearch($this->existingsMetaIsd[$rowMetakey], $user->getId()) == false
|
||||
)
|
||||
) {
|
||||
$rowVals[0] = $this->usersMetaAutoIncrement;
|
||||
$this->usersMetaAutoIncrement++;
|
||||
$rowVals[1] = $user->getId();
|
||||
|
||||
if ($rowMetakey == 'nickname') {
|
||||
// update nickname
|
||||
$rowMetaValue = SnapDB::parsedQueryValueToString($rowVals[3]);
|
||||
if ($rowMetaValue == $user->getOldLogin()) {
|
||||
$rowVals[3] = '"' . mysqli_real_escape_string($dbh, $user->getLogin()) . '"';
|
||||
}
|
||||
}
|
||||
|
||||
$resultValues[] = $rowVals;
|
||||
}
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'Error on parse user meta row');
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($resultValues)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return 'INSERT IGNORE INTO `' . mysqli_real_escape_string($dbh, DUPX_DB_Functions::getUserMetaTableName()) .
|
||||
'` VALUES ' . SnapDB::getQueryInsertValuesFromArray($resultValues) . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply query fix for table/colum user id
|
||||
*
|
||||
* @param string $tableName table name
|
||||
* @param int $colNum column index, 0 is first
|
||||
* @param array<int, mixed[]> $queryValues two dimensional array where each item is a row containing the list of values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTableUserRemapQueryFix($tableName, $colNum, $queryValues)
|
||||
{
|
||||
$dbh = DUPX_DB_Functions::getInstance()->dbConnection();
|
||||
|
||||
for ($i = 0; $i < count($queryValues); $i++) {
|
||||
$rowUserId = SnapDB::parsedQueryValueToInt($queryValues[$i][$colNum]);
|
||||
if (isset($this->mappingIds[$rowUserId])) {
|
||||
$queryValues[$i][$colNum] = $this->mappingIds[$rowUserId];
|
||||
}
|
||||
}
|
||||
|
||||
return 'INSERT IGNORE INTO `' . mysqli_real_escape_string($dbh, $tableName) .
|
||||
'` VALUES ' . SnapDB::getQueryInsertValuesFromArray($queryValues) . ';';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Database;
|
||||
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use mysqli;
|
||||
|
||||
/**
|
||||
* Database utilities
|
||||
*/
|
||||
class DbUtils
|
||||
{
|
||||
/**
|
||||
* Update option value or create if don't exists
|
||||
*
|
||||
* @param mysqli $dbh database resource
|
||||
* @param string $name option name
|
||||
* @param string $value option value
|
||||
* @param ?string $prefix table prefix, if null is wp main prefix
|
||||
* @param bool $autoload option autoload
|
||||
*
|
||||
* @return bool true on success, false on failure
|
||||
*/
|
||||
public static function updateWpOption(mysqli $dbh, $name, $value, $prefix = null, $autoload = true): bool
|
||||
{
|
||||
|
||||
$table = DUPX_DB_Functions::getOptionsTableName($prefix);
|
||||
Log::info('UPDATE OPTION ' . $name . ' ON TABLE ' . $table);
|
||||
$escapedTable = mysqli_real_escape_string($dbh, $table);
|
||||
$name = mysqli_real_escape_string($dbh, $name);
|
||||
$value = mysqli_real_escape_string($dbh, $value);
|
||||
$autoload = ($autoload ? 'yes' : 'no');
|
||||
|
||||
$query = "INSERT INTO `" . $escapedTable . "` (option_name, option_value, autoload) " .
|
||||
"VALUES ('" . $name . "','" . $value . "', '" . $autoload . "') " .
|
||||
"ON DUPLICATE KEY UPDATE " .
|
||||
"option_value = '" . $value . "', " .
|
||||
"autoload = '" . $autoload . "'";
|
||||
return (DUPX_DB::mysqli_query($dbh, $query) !== false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Database;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use VendorDuplicator\Amk\JsonSerialize\AbstractJsonSerializable;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB_Functions;
|
||||
use DUPX_DB_Tables;
|
||||
use DUPX_DBInstall;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
|
||||
class QueryFixes extends AbstractJsonSerializable
|
||||
{
|
||||
const TEMP_POSTFIX = 't_';
|
||||
const USER_DEFINER_REPLACE_PATTERN = "/^(\s*(?:\/\*!\d+\s)?\s*(?:CREATE.+)?DEFINER\s*=)([^\*\s]+)(.*)$/m";
|
||||
const USER_DEFINER_REMOVE_PATTERN = "/^(\s*(?:\/\*!\d+\s)?\s*(?:CREATE.+)?)(DEFINER\s*=\s*\S+)(.*)$/m";
|
||||
const USER_DEFINER_REMOVE_REPLACE = '$1 $3';
|
||||
const SQL_SECURITY_INVOKER_PATTERN = "/^(\s*CREATE.+(?:PROCEDURE|FUNCTION)[\s\S]*)(BEGIN)([\s\S]*)$/";
|
||||
const SQL_SECURITY_INVOKER_REPLACE = "$1SQL SECURITY INVOKER\n$2$3";
|
||||
|
||||
/** @var array{search: mixed[], replace: mixed[]} */
|
||||
protected $globalRules = [
|
||||
'search' => [],
|
||||
'replace' => [],
|
||||
];
|
||||
/** @var mixed[] */
|
||||
protected $tablesPrefixRules = [];
|
||||
/** @var string */
|
||||
protected $generatorLog = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->rulesProcAndViews();
|
||||
$this->rulesMySQLEngine();
|
||||
$this->legacyCharsetAndCollation();
|
||||
$this->rulesTableNames();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter props on json encode
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
$props = array_keys(get_object_vars($this));
|
||||
return array_diff($props, ['generatorLog']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write rules in log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function logRules(): void
|
||||
{
|
||||
if (strlen($this->generatorLog) == 0) {
|
||||
Log::info('NO GENERAL QUERY FIXES');
|
||||
} else {
|
||||
Log::info('QUERY FIXES');
|
||||
Log::info($this->generatorLog);
|
||||
}
|
||||
|
||||
if (count($this->globalRules['search']) > 0) {
|
||||
Log::info('QUERY FIXES GLOBAL RULES');
|
||||
Log::incIndent();
|
||||
foreach ($this->globalRules['search'] as $index => $search) {
|
||||
Log::info('SEARCH => ' . $search);
|
||||
Log::info('REPLACE => ' . $this->globalRules['replace'][$index] . "\n");
|
||||
}
|
||||
Log::resetIndent();
|
||||
}
|
||||
|
||||
if (count($this->tablesPrefixRules) > 0) {
|
||||
Log::info('QUERY FIXES TABLES RULES');
|
||||
Log::incIndent();
|
||||
foreach ($this->tablesPrefixRules as $indexRulesSet => $ruleSet) {
|
||||
Log::info('RULESET ' . ($indexRulesSet + 1));
|
||||
Log::incIndent();
|
||||
foreach ($ruleSet['search'] as $index => $search) {
|
||||
Log::info('SEARCH => ' . $search);
|
||||
Log::info('REPLACE => ' . $ruleSet['replace'][$index] . "\n");
|
||||
}
|
||||
Log::decIndent();
|
||||
}
|
||||
Log::resetIndent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query query to fix
|
||||
*
|
||||
* @return string The query with appropriate substitutions done
|
||||
*/
|
||||
public function applyFixes($query)
|
||||
{
|
||||
$query = preg_replace($this->globalRules['search'], $this->globalRules['replace'], $query);
|
||||
|
||||
foreach ($this->tablesPrefixRules as $ruleSet) {
|
||||
$query = preg_replace($ruleSet['search'], $ruleSet['replace'], $query);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set search and replace rules
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function rulesProcAndViews()
|
||||
{
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_REMOVE_DEFINER)) {
|
||||
$this->globalRules['search'][] = self::USER_DEFINER_REMOVE_PATTERN;
|
||||
$this->globalRules['replace'][] = self::USER_DEFINER_REMOVE_REPLACE;
|
||||
} else {
|
||||
$this->globalRules['search'][] = self::USER_DEFINER_REPLACE_PATTERN;
|
||||
|
||||
$dbHost = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_HOST);
|
||||
$dbUser = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_USER);
|
||||
|
||||
$definerHost = (($dbHost == "localhost" || $dbHost == "127.0.0.1") ? $dbHost : '%');
|
||||
$this->globalRules['replace'][] = '$1' . addcslashes("`" . $dbUser . "`@`" . $definerHost . "`", '\\$') . '$3';
|
||||
}
|
||||
|
||||
$this->globalRules['search'][] = self::SQL_SECURITY_INVOKER_PATTERN;
|
||||
$this->globalRules['replace'][] = self::SQL_SECURITY_INVOKER_REPLACE;
|
||||
|
||||
$this->generatorLog .= "GLOBAL RULES ADDED: PROC AND VIEWS\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check invalid SQL engines
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function rulesMySQLEngine()
|
||||
{
|
||||
$invalidEngines = array_map(fn($engine): string => preg_quote($engine, '/'), DUPX_ArchiveConfig::getInstance()->invalidEngines());
|
||||
|
||||
if (empty($invalidEngines)) {
|
||||
return;
|
||||
}
|
||||
$this->globalRules['search'][] = '/^(\s*(?:\/\*!\d+\s)?\s*CREATE.+ENGINE=)(' . implode('|', $invalidEngines) . ')(.*)$/ms';
|
||||
$this->globalRules['replace'][] = '$1' . DUPX_DB_Functions::getInstance()->getDefaultEngine() . '$3';
|
||||
|
||||
$this->generatorLog .= "GLOBAL RULES ADDED: MYSQL ENGINES\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set legacy charset adn collation rules
|
||||
*
|
||||
* Regex managed examples
|
||||
* - `meta_value` longtext CHARACTER SET utf16 COLLATE utf16_slovak_ci DEFAULT NULL,
|
||||
* - `comment_author` tinytext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
* - ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci_test;
|
||||
* - ) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4;
|
||||
*
|
||||
* accept ['"`]charset['"`]
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function legacyCharsetAndCollation(): void
|
||||
{
|
||||
$invalidCharsets = DUPX_ArchiveConfig::getInstance()->invalidCharsets();
|
||||
$invalidCollations = DUPX_ArchiveConfig::getInstance()->invalidCollations();
|
||||
$defCharsetRegex = addcslashes(DUPX_DB_Functions::getInstance()->getRealCharsetByParam(), '\\$');
|
||||
$defCollateRegex = addcslashes(DUPX_DB_Functions::getInstance()->getRealCollateByParam(), '\\$');
|
||||
|
||||
if (count($invalidCharsets) > 0) {
|
||||
$invalidChrRegex = '(?:' . implode('|', array_map(fn($val): string => preg_quote($val, '/'), $invalidCharsets)) . ')';
|
||||
|
||||
$this->globalRules['search'][] = '/(^.*(?:CHARSET|CHARACTER SET)\s*[\s=]\s*[`\'"]?)(' .
|
||||
$invalidChrRegex . ')([`\'"]?\s.*COLLATE\s*[\s=]\s*[`\'"]?)([^`\'"\s;,]+)([`\'"]?.*$)/m';
|
||||
$this->globalRules['replace'][] = '$1' . $defCharsetRegex . '$3' . $defCollateRegex . '$5';
|
||||
$this->globalRules['search'][] = '/(^.*COLLATE\s*[\s=]\s*[`\'"]?)([^`\'"\s;,]+)([`\'"]?\s.*(?:CHARSET|CHARACTER SET)\s*[\s=]\s*[`\'"]?)(' .
|
||||
$invalidChrRegex . ')([`\'"]?[\s;,].*$)/m';
|
||||
$this->globalRules['replace'][] = '$1' . $defCollateRegex . '$3' . $defCharsetRegex . '$5';
|
||||
$this->globalRules['search'][] = '/(^.*(?:CHARSET|CHARACTER SET)\s*[\s=]\s*[`\'"]?)(' . $invalidChrRegex . ')([`\'"]?[\s;,].*$)/m';
|
||||
$this->globalRules['replace'][] = '$1' . $defCharsetRegex . '$3';
|
||||
|
||||
$this->generatorLog .= "GLOBAL RULES ADDED: INVALID CHARSETS\n";
|
||||
}
|
||||
|
||||
if (count($invalidCollations) > 0) {
|
||||
$invalidColRegex = '(?:' . implode('|', array_map(fn($val): string => preg_quote($val, '/'), $invalidCollations)) . ')';
|
||||
|
||||
$this->globalRules['search'][] = '/(^.*(?:CHARSET|CHARACTER SET)\s*[\s=]\s*[`\'"]?)([^`\'"\s;,]+)([`\'"]?\s.*COLLATE\s*[\s=]\s*[`\'"]?)(' .
|
||||
$invalidColRegex . ')([`\'"]?[\s;,].*$)/m';
|
||||
$this->globalRules['replace'][] = '$1' . $defCharsetRegex . '$3' . $defCollateRegex . '$5';
|
||||
$this->globalRules['search'][] = '/(^.*COLLATE\s*[\s=]\s*[`\'"]?)(' .
|
||||
$invalidColRegex . ')([`\'"]?\s.*(?:CHARSET|CHARACTER SET)\s*[\s=]\s*[`\'"]?)([^`\'"\s;,]+)([`\'"]?.*$)/m';
|
||||
$this->globalRules['replace'][] = '$1' . $defCollateRegex . '$3' . $defCharsetRegex . '$5';
|
||||
$this->globalRules['search'][] = '/(^.*COLLATE\s*[\s=]\s*[`\'"]?)(' . $invalidColRegex . ')([`\'"]?[\s;,].*$)/m';
|
||||
$this->globalRules['replace'][] = '$1' . $defCollateRegex . '$3';
|
||||
|
||||
$this->generatorLog .= "GLOBAL RULES ADDED: INVALID COLLATIONS\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set search and replace table prefix rules
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function rulesTableNames()
|
||||
{
|
||||
$mapping = DUPX_DB_Tables::getInstance()->getRenameTablesMapping();
|
||||
|
||||
$oldPrefixes = array_keys($mapping);
|
||||
$newPrefixes = [];
|
||||
foreach ($mapping as $oldPrefix => $newMapping) {
|
||||
$newPrefixes = array_merge($newPrefixes, array_keys($newMapping));
|
||||
}
|
||||
$newPrefixes = array_unique($newPrefixes);
|
||||
|
||||
// Prevent double transformation with temp prefix
|
||||
$doublePrefixes = array_intersect($oldPrefixes, $newPrefixes);
|
||||
if (count($doublePrefixes) > 0) {
|
||||
$this->generatorLog .= 'DOUBLE PREFIXES ' . Log::v2str($doublePrefixes);
|
||||
}
|
||||
|
||||
foreach ($mapping as $oldPrefix => $newMapping) {
|
||||
$rulesSet = [
|
||||
'search' => [],
|
||||
'replace' => [],
|
||||
];
|
||||
|
||||
$quoteOldPrefix = preg_quote($oldPrefix, '/');
|
||||
|
||||
foreach ($newMapping as $newPrefix => $commons) {
|
||||
$this->generatorLog .= "TABLES RULES ADDED: CHANGE TABLES PREFIX " . $oldPrefix . " TO " . $newPrefix ;
|
||||
if (in_array($newPrefix, $doublePrefixes)) {
|
||||
$this->generatorLog .= " [USE TMP PREFIX]\n";
|
||||
$newPrefix .= self::TEMP_POSTFIX;
|
||||
} else {
|
||||
$this->generatorLog .= "\n";
|
||||
}
|
||||
$this->generatorLog .= "\tFOR TABLES " . implode(',', $commons) . "\n";
|
||||
|
||||
$quoteNewPrefix = addcslashes($newPrefix, '\\$');
|
||||
$quoteCommons = array_map(
|
||||
fn($val): string => preg_quote($val, '/'),
|
||||
$commons
|
||||
);
|
||||
|
||||
for ($i = 0; $i < ceil(count($quoteCommons) / DUPX_DBInstall::TABLES_REGEX_CHUNK_SIZE); $i++) {
|
||||
$subArray = array_slice($quoteCommons, $i * DUPX_DBInstall::TABLES_REGEX_CHUNK_SIZE, DUPX_DBInstall::TABLES_REGEX_CHUNK_SIZE);
|
||||
|
||||
if (count($subArray) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$rulesSet['search'][] = '/' . $quoteOldPrefix . '(' . implode('|', $subArray) . ')/m';
|
||||
$rulesSet['replace'][] = $quoteNewPrefix . '$1';
|
||||
}
|
||||
|
||||
$rulesSet['search'][] = '/(CONSTRAINT[\s\t]+[`\'"]?.+)(?-i)' . $quoteOldPrefix . '(?i)(.+[`\'"]?[\s\t]+FOREIGN[\s\t]+KEY)/mi';
|
||||
$rulesSet['replace'][] = '$1' . $quoteNewPrefix . '$2';
|
||||
}
|
||||
|
||||
if (count($rulesSet['search']) > 0) {
|
||||
$this->tablesPrefixRules[] = $rulesSet;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($doublePrefixes)) {
|
||||
// REMOVE TEMP PREFIXES
|
||||
$rulesSet = [
|
||||
'search' => [],
|
||||
'replace' => [],
|
||||
];
|
||||
|
||||
foreach ($doublePrefixes as $prefix) {
|
||||
$quoteTempPrefix = preg_quote($prefix . self::TEMP_POSTFIX, '/');
|
||||
$quotePrefix = addcslashes($prefix, '\\$');
|
||||
|
||||
$rulesSet['search'][] = '/' . $quoteTempPrefix . '/m';
|
||||
$rulesSet['replace'][] = $quotePrefix . '$1';
|
||||
}
|
||||
|
||||
$this->tablesPrefixRules[] = $rulesSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Dup archive expander
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\DupArchive;
|
||||
|
||||
use DupArchiveStateBase;
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\DupArchive\DupArchiveEngine;
|
||||
use Duplicator\Libs\DupArchive\Processors\DupArchiveFileProcessor;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_Package;
|
||||
use Exception;
|
||||
use stdClass;
|
||||
|
||||
class Daws
|
||||
{
|
||||
const DEFAULT_WORKER_TIME = 18;
|
||||
|
||||
/** @var ?resource */
|
||||
protected $lock_handle;
|
||||
/** @var ?callable */
|
||||
protected $failureCallback;
|
||||
protected string $lockFile;
|
||||
protected string $cancelFile;
|
||||
|
||||
/**
|
||||
* Class contruct
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
DawsLogger::init();
|
||||
date_default_timezone_set('UTC'); // Some machines don’t have this set so just do it here.
|
||||
DupArchiveEngine::init(new DawsLogger(), '');
|
||||
$this->lockFile = DUPX_INIT . '/dup-installer-dawslock__' . DUPX_Package::getPackageHash() . '.bin';
|
||||
$this->cancelFile = DUPX_INIT . '/dup-installer-dawscancel__' . DUPX_Package::getPackageHash() . '.bin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Failure callback
|
||||
*
|
||||
* @param callable $callback callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFailureCallBack($callback): void
|
||||
{
|
||||
if (is_callable($callback)) {
|
||||
$this->failureCallback = $callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract dup archvie
|
||||
*
|
||||
* @param array<string, mixed> $params dup archvie params
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function processRequest($params): stdClass
|
||||
{
|
||||
$retVal = new stdClass();
|
||||
$retVal->pass = false;
|
||||
$action = $params['action'];
|
||||
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
if (!DupArchiveFileProcessor::setNewFilePathCallback([$archiveConfig, 'destFileFromArchiveName'])) {
|
||||
Log::info('ERROR: CAN\'T SET THE PATH SE CALLBACK FUNCTION');
|
||||
} else {
|
||||
Log::info('PATH SE CALLBACK FUNCTION OK ', Log::LV_DEBUG);
|
||||
}
|
||||
|
||||
$throttleDelayInMs = SnapUtil::getArrayValue($params, 'throttle_delay', false, 0);
|
||||
$expandState = null;
|
||||
|
||||
if ($action == 'start_expand') {
|
||||
Log::info('DAWN START EXPAND');
|
||||
|
||||
DawsExpandState::purgeStatefile();
|
||||
SnapIO::rm($this->cancelFile);
|
||||
$archiveFilepath = SnapUtil::getArrayValue($params, 'archive_filepath');
|
||||
$restoreDirectory = SnapUtil::getArrayValue($params, 'restore_directory');
|
||||
$workerTime = SnapUtil::getArrayValue($params, 'worker_time', false, self::DEFAULT_WORKER_TIME);
|
||||
$filteredDirectories = SnapUtil::getArrayValue($params, 'filtered_directories', false, []);
|
||||
$excludedDirWithoutChilds = SnapUtil::getArrayValue($params, 'excludedDirWithoutChilds', false, []);
|
||||
$filteredFiles = SnapUtil::getArrayValue($params, 'filtered_files', false, []);
|
||||
$fileRenames = SnapUtil::getArrayValue($params, 'fileRenames', false, []);
|
||||
$fileModeOverride = SnapUtil::getArrayValue($params, 'file_mode_override', false, 0644);
|
||||
$includedFiles = SnapUtil::getArrayValue($params, 'includedFiles', false, []);
|
||||
$directoryModeOverride = SnapUtil::getArrayValue($params, 'dir_mode_override', false, 0755);
|
||||
$keepFileTime = SnapUtil::getArrayValue($params, 'keep_file_time', false, false);
|
||||
|
||||
$archveHeader = DupArchiveEngine::getArchiveHeader(
|
||||
$archiveFilepath,
|
||||
Security::getInstance()->getArchivePassword()
|
||||
);
|
||||
$expandState = new DawsExpandState($archveHeader);
|
||||
$expandState->archivePath = $archiveFilepath;
|
||||
$expandState->working = true;
|
||||
$expandState->timeSliceInSecs = $workerTime;
|
||||
$expandState->basePath = $restoreDirectory;
|
||||
$expandState->filteredDirectories = $filteredDirectories;
|
||||
$expandState->excludedDirWithoutChilds = $excludedDirWithoutChilds;
|
||||
$expandState->includedFiles = $includedFiles;
|
||||
$expandState->filteredFiles = $filteredFiles;
|
||||
$expandState->fileRenames = $fileRenames;
|
||||
$expandState->fileModeOverride = $fileModeOverride;
|
||||
$expandState->directoryModeOverride = $directoryModeOverride;
|
||||
$expandState->throttleDelayInUs = 1000 * $throttleDelayInMs;
|
||||
$expandState->keepFileTime = $keepFileTime;
|
||||
$expandState->save();
|
||||
|
||||
$action = 'expand';
|
||||
} else {
|
||||
Log::info('DAWN CONTINUE EXPAND');
|
||||
$expandState = DawsExpandState::getFromFile();
|
||||
}
|
||||
|
||||
if ($action == 'expand') {
|
||||
$this->lock_handle = SnapIO::fopen($this->lockFile, 'c+');
|
||||
SnapIO::flock($this->lock_handle, LOCK_EX);
|
||||
|
||||
if ($expandState->working) {
|
||||
DupArchiveEngine::expandArchive($expandState);
|
||||
}
|
||||
|
||||
if (!$expandState->working) {
|
||||
$deltaTime = time() - $expandState->startTimestamp;
|
||||
Log::info("DAWN EXPAND DONE, SECONDS: " . $deltaTime, Log::LV_DETAILED);
|
||||
|
||||
if (count($expandState->failures) > 0) {
|
||||
Log::info('DAWN EXPAND ERRORS DETECTED');
|
||||
|
||||
foreach ($expandState->failures as $failure) {
|
||||
Log::info("{$failure->subject}:{$failure->description}");
|
||||
if (is_callable($this->failureCallback)) {
|
||||
call_user_func($this->failureCallback, $failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log::info("DAWN EXPAND CONTINUE", Log::LV_DETAILED);
|
||||
}
|
||||
|
||||
SnapIO::flock($this->lock_handle, LOCK_UN);
|
||||
|
||||
$retVal->pass = true;
|
||||
$retVal->status = $this->getStatus($expandState);
|
||||
} elseif ($action == 'get_status') {
|
||||
$retVal->pass = true;
|
||||
$retVal->status = $this->getStatus($expandState);
|
||||
} elseif ($action == 'cancel') {
|
||||
if (!SnapIO::touch($this->cancelFile)) {
|
||||
throw new Exception("Couldn't update time on " . $this->cancelFile);
|
||||
}
|
||||
$retVal->pass = true;
|
||||
} else {
|
||||
throw new Exception('Unknown command.');
|
||||
}
|
||||
session_write_close();
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dup archive status
|
||||
*
|
||||
* @param DawsExpandState $state dup archive state
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
private function getStatus(DawsExpandState $state): stdClass
|
||||
{
|
||||
$ret_val = new stdClass();
|
||||
$ret_val->archive_offset = $state->archiveOffset;
|
||||
$ret_val->archive_size = @filesize($state->archivePath);
|
||||
$ret_val->failures = $state->failures;
|
||||
$ret_val->file_index = $state->fileWriteCount;
|
||||
$ret_val->is_done = !$state->working;
|
||||
$ret_val->timestamp = time();
|
||||
|
||||
return $ret_val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Dup archvie expand state
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\DupArchive;
|
||||
|
||||
use Duplicator\Libs\DupArchive\Headers\DupArchiveHeader;
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
||||
use Duplicator\Libs\DupArchive\Utils\DupArchiveUtil;
|
||||
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use DUPX_Package;
|
||||
use Exception;
|
||||
|
||||
class DawsExpandState extends DupArchiveExpandState
|
||||
{
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param DupArchiveHeader $archiveHeader archive header
|
||||
*/
|
||||
public function __construct(DupArchiveHeader $archiveHeader)
|
||||
{
|
||||
parent::__construct($archiveHeader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read object from file
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getFromFile()
|
||||
{
|
||||
$stateFilepath = self::dataFilePath();
|
||||
|
||||
if (!file_exists($stateFilepath)) {
|
||||
throw new Exception('Data file don\'t exists');
|
||||
}
|
||||
|
||||
if (($json = file_get_contents($stateFilepath)) === false) {
|
||||
throw new Exception('Can\'t read data file');
|
||||
}
|
||||
|
||||
$result = JsonSerialize::unserialize($json);
|
||||
if ($result instanceof self) {
|
||||
return $result;
|
||||
} else {
|
||||
$msg = "Invalid data file. It is possible that your disc is full, ";
|
||||
$msg .= "in which case you need to free up some space, then restart the installer.";
|
||||
throw new Exception($msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove state file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function purgeStatefile(): bool
|
||||
{
|
||||
$stateFilepath = self::dataFilePath();
|
||||
if (!file_exists($stateFilepath)) {
|
||||
return true;
|
||||
}
|
||||
SnapIO::rm($stateFilepath, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset state
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
parent::reset();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save state
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
$stateFilepath = self::dataFilePath();
|
||||
$stateHandle = SnapIO::fopen($stateFilepath, 'w');
|
||||
SnapIO::flock($stateHandle, LOCK_EX);
|
||||
DupArchiveUtil::tlog("Saving state");
|
||||
SnapIO::fwrite($stateHandle, JsonSerialize::serialize($this));
|
||||
SnapIO::flock($stateHandle, LOCK_UN);
|
||||
SnapIO::fclose($stateHandle);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function dataFilePath()
|
||||
{
|
||||
static $path = null;
|
||||
if (is_null($path)) {
|
||||
$path = DUPX_INIT . '/dup-dawn-extraction__' . DUPX_Package::getPackageHash() . '.json';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Logger for dup archive
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\DupArchive;
|
||||
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\DupArchive\DupArchiveLoggerBase;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Logger for dup archive
|
||||
*/
|
||||
class DawsLogger extends DupArchiveLoggerBase
|
||||
{
|
||||
/**
|
||||
* Init logger
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
set_error_handler([self::class, "terminateMissingVariables"], E_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log function
|
||||
*
|
||||
* @param string $s string to log
|
||||
* @param boolean $flush if true flish log
|
||||
* @param ?callable $callingFunctionOverride call back function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log($s, $flush = false, $callingFunctionOverride = null): void
|
||||
{
|
||||
Log::info($s, Log::LV_DEFAULT, $flush);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw exception on php error
|
||||
*
|
||||
* @param int $errno errno
|
||||
* @param string $errstr error message
|
||||
* @param string $errfile file
|
||||
* @param int $errline line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function terminateMissingVariables($errno, $errstr, $errfile, $errline): bool
|
||||
{
|
||||
Log::info("ERROR $errno, $errstr, {$errfile}:{$errline}");
|
||||
/**
|
||||
* INTERCEPT ON processRequest AND RETURN JSON STATUS
|
||||
*/
|
||||
throw new Exception("ERROR:{$errfile}:{$errline} | " . $errstr, $errno);
|
||||
|
||||
return true; // @phpstan-ignore-line
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Files;
|
||||
|
||||
use DUPX_Extraction;
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
use DUPX_ArchiveConfig;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Libs\Index\FileIndexManager;
|
||||
use DUPX_Package;
|
||||
use DUPX_Server;
|
||||
use Exception;
|
||||
|
||||
class FilterMng
|
||||
{
|
||||
/**
|
||||
* Return filter (folder/files) for extraction
|
||||
*
|
||||
* @param string $subFolderArchive sub folder archive
|
||||
*
|
||||
* @return Filters
|
||||
*/
|
||||
public static function getExtractFilters($subFolderArchive): Filters
|
||||
{
|
||||
Log::info("INITIALIZE FILTERS");
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$result = new Filters();
|
||||
$relContentPath = '';
|
||||
|
||||
$filterFilesChildOfFolders = [];
|
||||
$acceptFolderOfFilterChilds = [];
|
||||
|
||||
$result->addFile($archiveConfig->installer_backup_name);
|
||||
$result->addDir(ltrim($subFolderArchive . '/' . DUPX_Extraction::DUP_FOLDER_NAME, '/'));
|
||||
|
||||
if (self::filterWpCoreFiles()) {
|
||||
$relAbsPath = $archiveConfig->getRelativePathsInArchive('abs');
|
||||
$relAbsPath .= (strlen($relAbsPath) > 0 ? '/' : '');
|
||||
$rootWpCoreItems = SnapWP::getWpCoreFilesListInFolder();
|
||||
foreach ($rootWpCoreItems['dirs'] as $name) {
|
||||
$result->addDir($relAbsPath . $name);
|
||||
}
|
||||
|
||||
foreach ($rootWpCoreItems['files'] as $name) {
|
||||
$result->addFile($relAbsPath . $name);
|
||||
}
|
||||
}
|
||||
|
||||
if (self::filterAllExceptPlugingThemesMedia()) {
|
||||
Log::info('FILTER ALL EXCEPT MEDIA');
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('home');
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('wpcontent');
|
||||
|
||||
$acceptFolderOfFilterChilds[] = $archiveConfig->getRelativePathsInArchive('uploads');
|
||||
$acceptFolderOfFilterChilds[] = $archiveConfig->getRelativePathsInArchive('wpcontent') . '/blogs.dir';
|
||||
$acceptFolderOfFilterChilds[] = $archiveConfig->getRelativePathsInArchive('plugins');
|
||||
$acceptFolderOfFilterChilds[] = $archiveConfig->getRelativePathsInArchive('muplugins');
|
||||
$acceptFolderOfFilterChilds[] = $archiveConfig->getRelativePathsInArchive('themes');
|
||||
}
|
||||
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
if (($pos = array_search($archiveConfig->getRelativePathsInArchive('uploads'), $acceptFolderOfFilterChilds)) !== false) {
|
||||
unset($acceptFolderOfFilterChilds[$pos]);
|
||||
}
|
||||
|
||||
if (($pos = array_search($archiveConfig->getRelativePathsInArchive('wpcontent') . '/blogs.dir', $acceptFolderOfFilterChilds)) !== false) {
|
||||
unset($acceptFolderOfFilterChilds[$pos]);
|
||||
}
|
||||
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('uploads') . '/sites';
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('wpcontent') . '/blogs.dir';
|
||||
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = $paramsManager->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
|
||||
$mainSiteInSource = false;
|
||||
foreach ($overwriteMapping as $map) {
|
||||
if (($subsiteInfo = $map->getSourceSiteInfo()) == false) {
|
||||
throw new Exception('Source site id ' . $map->getSourceId() . ' not valid');
|
||||
}
|
||||
if ($map->getSourceId() == 1) {
|
||||
$mainSiteInSource = true;
|
||||
}
|
||||
$acceptFolderOfFilterChilds[] = $subsiteInfo['uploadPath'];
|
||||
}
|
||||
|
||||
if (!$mainSiteInSource) {
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('uploads');
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
InstState::isInstType(
|
||||
[InstState::TYPE_STANDALONE]
|
||||
)
|
||||
) {
|
||||
Log::info('FILTER ALL MEDIA EXCEPT STANDALONE');
|
||||
$subSiteObj = $archiveConfig->getSubsiteObjById($paramsManager->getValue(PrmMng::PARAM_SUBSITE_ID));
|
||||
if ($subSiteObj->id == 1) {
|
||||
$result->addDir($archiveConfig->getRelativePathsInArchive('uploads') . '/sites');
|
||||
$result->addDir($archiveConfig->getRelativePathsInArchive('wpcontent') . '/blogs.dir');
|
||||
} else {
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('uploads');
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('uploads') . '/sites';
|
||||
$filterFilesChildOfFolders[] = $archiveConfig->getRelativePathsInArchive('wpcontent') . '/blogs.dir';
|
||||
$acceptFolderOfFilterChilds[] = $subSiteObj->uploadPath;
|
||||
|
||||
$result->addDir(DUPX_ArchiveConfig::getInstance()->getRelativePathsInArchive('uploads') . '/sites', true);
|
||||
$result->addDir(DUPX_ArchiveConfig::getInstance()->getRelativePathsInArchive('wpcontent') . '/blogs.dir', true);
|
||||
}
|
||||
}
|
||||
|
||||
if (self::filterExistsPlugins()) {
|
||||
$newPluginDir = $paramsManager->getValue(PrmMng::PARAM_PATH_PLUGINS_NEW);
|
||||
if (is_dir($newPluginDir)) {
|
||||
$relPlugPath = $archiveConfig->getRelativePathsInArchive('plugins');
|
||||
$relPlugPath .= (strlen($relPlugPath) > 0 ? '/' : '');
|
||||
|
||||
SnapIO::regexGlobCallback($newPluginDir, function ($item) use ($relPlugPath, &$result): void {
|
||||
if (is_dir($item)) {
|
||||
$result->addDir($relPlugPath . pathinfo($item, PATHINFO_BASENAME));
|
||||
} else {
|
||||
$result->addFile($relPlugPath . pathinfo($item, PATHINFO_BASENAME));
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
$newMuPluginDir = $paramsManager->getValue(PrmMng::PARAM_PATH_MUPLUGINS_NEW);
|
||||
if (is_dir($newMuPluginDir)) {
|
||||
$relMuPlugPath = $archiveConfig->getRelativePathsInArchive('muplugins');
|
||||
$relMuPlugPath .= (strlen($relMuPlugPath) > 0 ? '/' : '');
|
||||
|
||||
SnapIO::regexGlobCallback($newMuPluginDir, function ($item) use ($relMuPlugPath, &$result): void {
|
||||
if (is_dir($item)) {
|
||||
$result->addDir($relMuPlugPath . pathinfo($item, PATHINFO_BASENAME));
|
||||
} else {
|
||||
$result->addFile($relMuPlugPath . pathinfo($item, PATHINFO_BASENAME));
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
$newWpContentDir = $paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW) . '/';
|
||||
if (is_dir($newWpContentDir)) {
|
||||
$relContentPath = $archiveConfig->getRelativePathsInArchive('wpcontent');
|
||||
$relContentPath .= (strlen($relContentPath) > 0 ? '/' : '');
|
||||
foreach (SnapWP::getDropinsPluginsNames() as $dropinsPlugin) {
|
||||
if (file_exists($newWpContentDir . $dropinsPlugin)) {
|
||||
$result->addFile($relContentPath . $dropinsPlugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self::filterExistsThemes()) {
|
||||
$newThemesDir = $paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW) . '/themes';
|
||||
if (is_dir($newThemesDir)) {
|
||||
$relThemesPath = $archiveConfig->getRelativePathsInArchive('themes');
|
||||
$relThemesPath .= (strlen($relContentPath) > 0 ? '/' : '');
|
||||
|
||||
SnapIO::regexGlobCallback($newThemesDir, function ($item) use ($relThemesPath, &$result): void {
|
||||
if (is_dir($item)) {
|
||||
$result->addDir($relThemesPath . pathinfo($item, PATHINFO_BASENAME));
|
||||
} else {
|
||||
$result->addFile($relThemesPath . pathinfo($item, PATHINFO_BASENAME));
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
|
||||
self::filterAllChildsOfPathExcept($result, $filterFilesChildOfFolders, $acceptFolderOfFilterChilds);
|
||||
$result->optmizeFilters();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create filters for remove files
|
||||
*
|
||||
* @param Filters|null $baseFilters base extraction filters
|
||||
*
|
||||
* @return Filters
|
||||
*/
|
||||
public static function getRemoveFilters(?Filters $baseFilters = null): Filters
|
||||
{
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$security = Security::getInstance();
|
||||
|
||||
$result = new Filters();
|
||||
if (!is_null($baseFilters)) {
|
||||
// convert all relative path from archive to absolute destination path
|
||||
foreach ($baseFilters->getDirs() as $dir) {
|
||||
$result->addDir($archiveConfig->destFileFromArchiveName($dir));
|
||||
}
|
||||
foreach ($baseFilters->getDirsWithoutChilds() as $dir) {
|
||||
$result->addDir($archiveConfig->destFileFromArchiveName($dir), true);
|
||||
}
|
||||
foreach ($baseFilters->getFiles() as $file) {
|
||||
$result->addFile($archiveConfig->destFileFromArchiveName($file));
|
||||
}
|
||||
}
|
||||
|
||||
$result->addFile($security->getArchivePath());
|
||||
$result->addFile($security->getBootFilePath());
|
||||
$result->addFile($security->getBootLogFile());
|
||||
|
||||
$result->addDir(DUPX_INIT);
|
||||
foreach (DUPX_Server::getWpAddonsSiteLists() as $addonPath) {
|
||||
$result->addDir($addonPath);
|
||||
}
|
||||
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
foreach ($overwriteData['removeFilters']['dirs'] as $dir) {
|
||||
$result->addDir($dir);
|
||||
}
|
||||
foreach ($overwriteData['removeFilters']['files'] as $file) {
|
||||
$result->addFile($file);
|
||||
}
|
||||
|
||||
$result->optmizeFilters();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function update filters from $filterFilesChildOfFolders and $acceptFolders values
|
||||
*
|
||||
* @param Filters $filters Filters
|
||||
* @param string[] $filterFilesChildOfFolders Filter contents of these paths
|
||||
* @param string[] $acceptFolders Folders not to filtered
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function filterAllChildsOfPathExcept(Filters $filters, array $filterFilesChildOfFolders, array $acceptFolders = []): void
|
||||
{
|
||||
//No sense adding filters if not folders specified
|
||||
if (count($filterFilesChildOfFolders) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$acceptFolders = array_unique($acceptFolders);
|
||||
$filterFilesChildOfFolders = array_unique($filterFilesChildOfFolders);
|
||||
|
||||
Log::info('ACCEPT FOLDERS ' . Log::v2str($acceptFolders), Log::LV_DETAILED);
|
||||
Log::info('CHILDS FOLDERS ' . Log::v2str($filterFilesChildOfFolders), Log::LV_DETAILED);
|
||||
|
||||
foreach (DUPX_Package::getIndexManager()->iteratePaths(FileIndexManager::LIST_TYPE_DIRS) as $path) {
|
||||
if (in_array($path, $filterFilesChildOfFolders)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($acceptFolders as $acceptFolder) {
|
||||
if (SnapIO::isChildPath($path, $acceptFolder, true)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$parentFolder = SnapIO::getRelativeDirname($path);
|
||||
|
||||
if (in_array($parentFolder, $filterFilesChildOfFolders)) {
|
||||
$filters->addDir($path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (DUPX_Package::getIndexManager()->iteratePaths(FileIndexManager::LIST_TYPE_FILES) as $path) {
|
||||
$parentFolder = SnapIO::getRelativeDirname($path);
|
||||
if (in_array($parentFolder, $filterFilesChildOfFolders)) {
|
||||
$filters->addFile($path);
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('FILTERS RESULT ' . Log::v2str($filters), log::LV_DETAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function filterWpCoreFiles(): bool
|
||||
{
|
||||
switch (PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES)) {
|
||||
case DUPX_Extraction::FILTER_NONE:
|
||||
return false;
|
||||
case DUPX_Extraction::FILTER_SKIP_WP_CORE:
|
||||
case DUPX_Extraction::FILTER_SKIP_CORE_PLUG_THEMES:
|
||||
case DUPX_Extraction::FILTER_ONLY_MEDIA_PLUG_THEMES:
|
||||
return true;
|
||||
default:
|
||||
throw new Exception('Unknown filter type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function filterExistsPlugins(): bool
|
||||
{
|
||||
switch (PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES)) {
|
||||
case DUPX_Extraction::FILTER_NONE:
|
||||
case DUPX_Extraction::FILTER_SKIP_WP_CORE:
|
||||
return false;
|
||||
case DUPX_Extraction::FILTER_SKIP_CORE_PLUG_THEMES:
|
||||
case DUPX_Extraction::FILTER_ONLY_MEDIA_PLUG_THEMES:
|
||||
return true;
|
||||
default:
|
||||
throw new Exception('Unknown filter type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function filterExistsThemes(): bool
|
||||
{
|
||||
switch (PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES)) {
|
||||
case DUPX_Extraction::FILTER_NONE:
|
||||
case DUPX_Extraction::FILTER_SKIP_WP_CORE:
|
||||
return false;
|
||||
case DUPX_Extraction::FILTER_SKIP_CORE_PLUG_THEMES:
|
||||
case DUPX_Extraction::FILTER_ONLY_MEDIA_PLUG_THEMES:
|
||||
return true;
|
||||
default:
|
||||
throw new Exception('Unknown filter type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function filterAllExceptPlugingThemesMedia(): bool
|
||||
{
|
||||
switch (PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES)) {
|
||||
case DUPX_Extraction::FILTER_NONE:
|
||||
case DUPX_Extraction::FILTER_SKIP_WP_CORE:
|
||||
case DUPX_Extraction::FILTER_SKIP_CORE_PLUG_THEMES:
|
||||
return false;
|
||||
case DUPX_Extraction::FILTER_ONLY_MEDIA_PLUG_THEMES:
|
||||
return true;
|
||||
default:
|
||||
throw new Exception('Unknown filter type');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Files;
|
||||
|
||||
use VendorDuplicator\Amk\JsonSerialize\AbstractJsonSerializable;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
/**
|
||||
* Manage filters for extraction
|
||||
*/
|
||||
class Filters extends AbstractJsonSerializable
|
||||
{
|
||||
/** @var string[] */
|
||||
protected $files = [];
|
||||
/** @var string[] */
|
||||
protected $dirsWithoutChilds = [];
|
||||
/** @var string[] */
|
||||
protected array $dirs;
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*
|
||||
* @param string[] $dirs dirs filters
|
||||
* @param string[] $dirsWithoutChilds dirs without child filters
|
||||
* @param string[] $files files filters
|
||||
*/
|
||||
public function __construct($dirs = [], $dirsWithoutChilds = [], $files = [])
|
||||
{
|
||||
$this->files = (array) $files;
|
||||
$this->dirs = (array) $dirs;
|
||||
$this->dirsWithoutChilds = (array) $dirsWithoutChilds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if passe path is filterd
|
||||
*
|
||||
* @param string $path path to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFiltered($path)
|
||||
{
|
||||
if (in_array($path, $this->dirsWithoutChilds)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->dirs as $dirFilter) {
|
||||
if (SnapIO::isChildPath($path, $dirFilter)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return in_array($path, $this->files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dir filter
|
||||
*
|
||||
* @param string $dir dir path
|
||||
* @param bool $withoutChild if true add dir filter without childs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addDir($dir, $withoutChild = false): void
|
||||
{
|
||||
if ($withoutChild) {
|
||||
$this->dirsWithoutChilds[] = (string) $dir;
|
||||
} else {
|
||||
$this->dirs[] = (string) $dir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add file fo filters
|
||||
*
|
||||
* @param string $file file path
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFile($file): void
|
||||
{
|
||||
$this->files[] = (string) $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize and sort filters
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function optmizeFilters()
|
||||
{
|
||||
$this->files = array_values(array_unique($this->files));
|
||||
$this->dirsWithoutChilds = array_values(array_unique($this->dirsWithoutChilds));
|
||||
$this->dirs = array_values(array_unique($this->dirs));
|
||||
|
||||
$optimizedDirs = [];
|
||||
$optimizedFiles = [];
|
||||
|
||||
for ($i = 0; $i < count($this->dirs); $i++) {
|
||||
$exclude = false;
|
||||
for ($j = 0; $j < count($this->dirs); $j++) {
|
||||
if ($i === $j) {
|
||||
continue;
|
||||
}
|
||||
if (SnapIO::isChildPath($this->dirs[$i], $this->dirs[$j])) {
|
||||
$exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exclude) {
|
||||
$optimizedDirs[] = $this->dirs[$i];
|
||||
}
|
||||
}
|
||||
|
||||
$optimizedDirs = SnapIO::sortBySubfoldersCount($optimizedDirs);
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
$exclude = false;
|
||||
foreach ($optimizedDirs as $cDir) {
|
||||
if (SnapIO::isChildPath($file, $cDir)) {
|
||||
$exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exclude) {
|
||||
$optimizedFiles[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
$this->files = $optimizedFiles;
|
||||
$this->dirs = $optimizedDirs;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of files
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getFiles()
|
||||
{
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of dirs
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDirs()
|
||||
{
|
||||
return $this->dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of dirsWithoutChilds
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDirsWithoutChilds()
|
||||
{
|
||||
return $this->dirsWithoutChilds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Files;
|
||||
|
||||
use DUPX_Extraction;
|
||||
use Duplicator\Installer\Core\Deploy\Plugins\PluginsManager;
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_Custom_Host_Manager;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use Error;
|
||||
use Exception;
|
||||
|
||||
class RemoveFiles
|
||||
{
|
||||
protected \Duplicator\Installer\Core\Deploy\Files\Filters $removeFilters;
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*
|
||||
* @param Filters $filters fles filters
|
||||
*/
|
||||
public function __construct(Filters $filters)
|
||||
{
|
||||
$this->removeFilters = $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove file if action is enableds
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remove(): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
$this->removeAddonSiteToMultisite();
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ACTION)) {
|
||||
case DUPX_Extraction::ACTION_REMOVE_ALL_FILES:
|
||||
$this->removeAllFiles();
|
||||
break;
|
||||
case DUPX_Extraction::ACTION_REMOVE_WP_FILES:
|
||||
$this->removeWpFiles();
|
||||
break;
|
||||
case DUPX_Extraction::ACTION_REMOVE_UPLOADS:
|
||||
$this->removeUploads();
|
||||
break;
|
||||
case DUPX_Extraction::ACTION_DO_NOTHING:
|
||||
$this->removeDoNothing();
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Invalid engine action ' . $paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ACTION));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function remove files before extraction
|
||||
*
|
||||
* @param string[] $paths Paths lists
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeFiles($paths = [])
|
||||
{
|
||||
Log::info('REMOVE FILES');
|
||||
|
||||
$filesFilters = $this->removeFilters->getFiles();
|
||||
|
||||
$excludeFiles = array_map(fn($value): string => '/^' . preg_quote($value, '/') . '$/', $filesFilters);
|
||||
|
||||
$excludeFolders = array_map(fn($value): string => '/^' . preg_quote($value, '/') . '(?:\/.*)?$/', $this->removeFilters->getDirs());
|
||||
$excludeFolders[] = '/.+\/backups-dup-(lite|pro)$/';
|
||||
|
||||
$excludeDirsWithoutChilds = $this->removeFilters->getDirsWithoutChilds();
|
||||
|
||||
foreach ($paths as $path) {
|
||||
if (is_file($path)) {
|
||||
if (in_array($path, $excludeFiles)) {
|
||||
continue;
|
||||
}
|
||||
Log::info('REMOVE FILE ' . Log::v2str($path));
|
||||
unlink($path);
|
||||
} else {
|
||||
Log::info('REMOVE FOLDER ' . Log::v2str($path));
|
||||
SnapIO::regexGlobCallback($path, function ($path) use ($excludeDirsWithoutChilds): void {
|
||||
foreach ($excludeDirsWithoutChilds as $excludePath) {
|
||||
if (SnapIO::isChildPath($excludePath, $path)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$result = (is_dir($path) ? rmdir($path) : unlink($path));
|
||||
if ($result == false) {
|
||||
$lastError = error_get_last();
|
||||
$message = ($lastError['message'] ?? 'Couldn\'t remove file');
|
||||
RemoveFiles::reportRemoveNotices($path, $message);
|
||||
}
|
||||
}, [
|
||||
'regexFile' => $excludeFiles,
|
||||
'regexFolder' => $excludeFolders,
|
||||
'checkFullPath' => true,
|
||||
'recursive' => true,
|
||||
'invert' => true,
|
||||
'childFirst' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove worpdress core files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeWpFiles()
|
||||
{
|
||||
try {
|
||||
Log::info('REMOVE WP FILES');
|
||||
Log::resetTime(Log::LV_DEFAULT, false);
|
||||
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$absDir = SnapIO::safePathTrailingslashit($paramsManager->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW));
|
||||
if (!is_dir($absDir) || !is_readable($absDir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$removeFolders = [];
|
||||
|
||||
if (!FilterMng::filterWpCoreFiles() && ($dh = opendir($absDir))) {
|
||||
while (($elem = readdir($dh)) !== false) {
|
||||
if ($elem === '.' || $elem === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SnapWP::isWpCore($elem, SnapWP::PATH_RELATIVE)) {
|
||||
$fullPath = $absDir . $elem;
|
||||
if (is_dir($fullPath)) {
|
||||
$removeFolders[] = $fullPath;
|
||||
} else {
|
||||
if (is_writable($fullPath)) {
|
||||
unlink($fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
|
||||
if (!InstState::isAddSiteOnMultisite()) {
|
||||
$removeFolders[] = $paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW);
|
||||
}
|
||||
$removeFolders[] = $paramsManager->getValue(PrmMng::PARAM_PATH_UPLOADS_NEW);
|
||||
$removeFolders[] = $paramsManager->getValue(PrmMng::PARAM_PATH_PLUGINS_NEW);
|
||||
$removeFolders[] = $paramsManager->getValue(PrmMng::PARAM_PATH_MUPLUGINS_NEW);
|
||||
|
||||
$this->removeFiles(array_unique($removeFolders));
|
||||
Log::logTime('FOLDERS REMOVED', Log::LV_DEFAULT, false);
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean uplod forlser of selectes subsites
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeAddonSiteToMultisite()
|
||||
{
|
||||
Log::info('CLEAN UPLOAD FOLDERS FOR ADD SITES');
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = $paramsManager->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
|
||||
foreach ($overwriteMapping as $map) {
|
||||
if (($subsiteInfo = $map->getTargetSiteInfo()) == false) {
|
||||
throw new Exception('Target site id ' . $map->getTargetId() . ' not valid');
|
||||
}
|
||||
|
||||
Log::info("\tEMPTY " . $subsiteInfo['fullUploadPath']);
|
||||
if ($map->getTargetId() == 1) {
|
||||
SnapIO::emptyDir($subsiteInfo['fullUploadPath'], ['sites']);
|
||||
} else {
|
||||
SnapIO::emptyDir($subsiteInfo['fullUploadPath']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ony uploads files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeUploads()
|
||||
{
|
||||
try {
|
||||
Log::info('REMOVE UPLOADS FILES');
|
||||
Log::resetTime(Log::LV_DEFAULT, false);
|
||||
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
$removePaths = [];
|
||||
$removePaths[] = $paramsManager->getValue(PrmMng::PARAM_PATH_UPLOADS_NEW);
|
||||
foreach (PluginsManager::getInstance()->getAllPluginsPaths(true, true) as $pluginPath) {
|
||||
$removePaths[] = $pluginPath;
|
||||
}
|
||||
|
||||
$this->removeFiles(array_unique($removePaths));
|
||||
Log::logTime('FOLDERS REMOVED', Log::LV_DEFAULT, false);
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove ony uploads files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeDoNothing()
|
||||
{
|
||||
try {
|
||||
Log::info('REMOVE DONOTHING FILES');
|
||||
Log::resetTime(Log::LV_DEFAULT, false);
|
||||
|
||||
$removePaths = [];
|
||||
foreach (PluginsManager::getInstance()->getAllPluginsPaths(true, true) as $pluginPath) {
|
||||
$removePaths[] = $pluginPath;
|
||||
}
|
||||
|
||||
$this->removeFiles(array_unique($removePaths));
|
||||
Log::logTime('FOLDERS REMOVED', Log::LV_DEFAULT, false);
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all files before extraction
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function removeAllFiles()
|
||||
{
|
||||
try {
|
||||
Log::info('REMOVE ALL FILES');
|
||||
Log::resetTime(Log::LV_DEFAULT, false);
|
||||
$pathsMapping = DUPX_ArchiveConfig::getInstance()->getPathsMapping();
|
||||
$folders = is_string($pathsMapping) ? [$pathsMapping] : array_values($pathsMapping);
|
||||
|
||||
$this->removeFiles($folders);
|
||||
Log::logTime('FOLDERS REMOVED', Log::LV_DEFAULT, false);
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $fileName package relative path
|
||||
* @param string $errorMessage error message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reportRemoveNotices($fileName, $errorMessage): void
|
||||
{
|
||||
if (DUPX_Custom_Host_Manager::getInstance()->skipWarningExtractionForManaged($fileName)) {
|
||||
// @todo skip warning for managed hostiong (it's a temp solution)
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info('Remove ' . $fileName . ' error message: ' . $errorMessage);
|
||||
if (is_dir($fileName)) {
|
||||
// Skip warning message for folders
|
||||
return;
|
||||
}
|
||||
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
if (SnapWP::isWpCore($fileName, SnapWP::PATH_RELATIVE)) {
|
||||
Log::info("FILE CORE REMOVE ERROR: {$fileName} | MSG:" . $errorMessage);
|
||||
$shortMsg = 'Can\'t remove wp core files';
|
||||
$errLevel = DUPX_NOTICE_ITEM::CRITICAL;
|
||||
$idManager = 'wp-remove-error-file-core';
|
||||
} else {
|
||||
Log::info("FILE REMOVE ERROR: {$fileName} | MSG:" . $errorMessage);
|
||||
$shortMsg = 'Can\'t remove files';
|
||||
$errLevel = DUPX_NOTICE_ITEM::HARD_WARNING;
|
||||
$idManager = 'wp-remove-error-file-no-core';
|
||||
}
|
||||
|
||||
$longMsg = 'FILE: <b>' . htmlspecialchars($fileName) . '</b><br>Message: ' . htmlspecialchars($errorMessage) . '<br><br>';
|
||||
|
||||
$nManager->addBothNextAndFinalReportNotice(
|
||||
[
|
||||
'shortMsg' => $shortMsg,
|
||||
'longMsg' => $longMsg,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'level' => $errLevel,
|
||||
'sections' => ['files'],
|
||||
],
|
||||
DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND,
|
||||
$idManager
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Package\DescriptorTheme;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use Exception;
|
||||
use mysqli;
|
||||
|
||||
class Helpers
|
||||
{
|
||||
/**
|
||||
* Load WordPress dependencies
|
||||
*
|
||||
* @return bool $loaded
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function loadWP()
|
||||
{
|
||||
static $loaded = null;
|
||||
if (is_null($loaded)) {
|
||||
$wpRootDir = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW);
|
||||
require_once($wpRootDir . '/wp-load.php');
|
||||
if (!class_exists('WP_Privacy_Policy_Content')) {
|
||||
require_once($wpRootDir . '/wp-admin/includes/misc.php');
|
||||
}
|
||||
if (!function_exists('request_filesystem_credentials')) {
|
||||
require_once($wpRootDir . '/wp-admin/includes/file.php');
|
||||
}
|
||||
if (!function_exists('get_plugins')) {
|
||||
require_once $wpRootDir . '/wp-admin/includes/plugin.php';
|
||||
}
|
||||
if (!function_exists('delete_theme')) {
|
||||
require_once $wpRootDir . '/wp-admin/includes/theme.php';
|
||||
}
|
||||
$GLOBALS['wpdb']->show_errors(false);
|
||||
$loaded = true;
|
||||
}
|
||||
return $loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Theme is enabled
|
||||
*
|
||||
* @param DescriptorTheme $theme Theme object
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function isThemeEnable(DescriptorTheme $theme): bool
|
||||
{
|
||||
switch (InstState::getInstType()) {
|
||||
case InstState::TYPE_SINGLE:
|
||||
case InstState::TYPE_RBACKUP_SINGLE:
|
||||
case InstState::TYPE_RECOVERY_SINGLE:
|
||||
if ($theme->isActive) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_MSUBDOMAIN:
|
||||
case InstState::TYPE_MSUBFOLDER:
|
||||
case InstState::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
case InstState::TYPE_RBACKUP_MSUBFOLDER:
|
||||
case InstState::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
case InstState::TYPE_RECOVERY_MSUBFOLDER:
|
||||
if (count($theme->isActive) > 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_STANDALONE:
|
||||
if (in_array(PrmMng::getInstance()->getValue(PrmMng::PARAM_SUBSITE_ID), $theme->isActive)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
return true;
|
||||
case InstState::TYPE_NOT_SET:
|
||||
default:
|
||||
throw new Exception('Invalid installer type');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a parent theme has a child theme enabled
|
||||
*
|
||||
* @param DescriptorTheme $parentTheme Parent Theme Object
|
||||
* @param DescriptorTheme[] $themes Themes List
|
||||
*
|
||||
* @return boolean
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function haveChildEnable(DescriptorTheme $parentTheme, &$themes): bool
|
||||
{
|
||||
foreach ($themes as $theme) {
|
||||
if ($theme->parentTheme === $parentTheme->slug) {
|
||||
if (Helpers::isThemeEnable($theme)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mysqli $dbh Database connection
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getSuperAdminsUserIds(\mysqli $dbh): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if (InstState::isNewSiteIsMultisite()) {
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$basePrefix = $paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);
|
||||
$usersTableName = "{$basePrefix}users";
|
||||
$superAdminsList = self::getSuperAdminUsernames($dbh, $basePrefix);
|
||||
|
||||
if (!empty($superAdminsList)) {
|
||||
$sql = "SELECT ID FROM {$usersTableName} WHERE user_login IN ('" . implode("','", $superAdminsList) . "')";
|
||||
|
||||
$queryResult = DUPX_DB::queryToArray($dbh, $sql);
|
||||
foreach ($queryResult as $superAdminsResult) {
|
||||
$result[] = $superAdminsResult[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Super Admin Users names
|
||||
*
|
||||
* @param mysqli $dbh Database connection
|
||||
* @param string $basePrefix WordPress Tables Prefix
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getSuperAdminUsernames(\mysqli $dbh, string $basePrefix): array
|
||||
{
|
||||
$result = [];
|
||||
$siteMetaTableName = "{$basePrefix}sitemeta";
|
||||
|
||||
if (InstState::isNewSiteIsMultisite() && DUPX_DB_Functions::getInstance()->tablesExist($siteMetaTableName)) {
|
||||
$sql = "SELECT meta_value FROM {$siteMetaTableName} WHERE meta_key = 'site_admins'";
|
||||
$superAdminsResults = DUPX_DB::queryToArray($dbh, $sql);
|
||||
|
||||
if (isset($superAdminsResults[0][0])) {
|
||||
$result = unserialize($superAdminsResults[0][0]);
|
||||
Log::info('SUPER ADMIN USERS: ' . print_r($result, true));
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy;
|
||||
|
||||
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamDescMultisite;
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\REST\RESTPoints;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_Ctrl_Params;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use DUPX_U;
|
||||
use Exception;
|
||||
|
||||
class Multisite
|
||||
{
|
||||
/**
|
||||
* Init new subistes info
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function overwriteSubsitesInit(): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = PrmMng::getInstance()->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
$sendData = JsonSerialize::serialize($overwriteMapping, JsonSerialize::JSON_SKIP_CLASS_NAME);
|
||||
|
||||
$errorMessage = '';
|
||||
$numSubsites = count($overwriteMapping);
|
||||
if (($subsitesInfo = RESTPoints::getInstance()->subsiteActions($sendData, $numSubsites, $errorMessage)) == false) {
|
||||
Log::info('Creation subsites error, message: ' . $errorMessage);
|
||||
throw new Exception('Can\'t create a new sub site message :' . $errorMessage);
|
||||
}
|
||||
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
|
||||
foreach ($subsitesInfo as $subsiteInfo) {
|
||||
switch ($subsiteInfo['targetId']) {
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_SLUG:
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN:
|
||||
$overwriteData['subsites'][] = $subsiteInfo['info'];
|
||||
Log::info('NEW SUBSITE CREATED ON ID: ' . $subsiteInfo['info']['id'] . ' URL ' . $subsiteInfo['info']['fullSiteUrl']);
|
||||
|
||||
if (($owrMap = ParamDescMultisite::getOwrMapBySourceId($subsiteInfo['sourceId'])) == false) {
|
||||
throw new Exception('OwrMap object not boud by id :' . $subsiteInfo['sourceId']);
|
||||
}
|
||||
$owrMap->setTargetId($subsiteInfo['info']['id']);
|
||||
break;
|
||||
default:
|
||||
// none
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$paramsManager->setValue(PrmMng::PARAM_OVERWRITE_SITE_DATA, $overwriteData);
|
||||
$paramsManager->setValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING, $overwriteMapping);
|
||||
|
||||
DUPX_Ctrl_Params::setParamsOnAddSiteOnMultisite();
|
||||
$paramsManager->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new subsite URLs
|
||||
*
|
||||
* @return array<int, string> Return mapped URLs
|
||||
*/
|
||||
public static function getMappedSubisteURLs()
|
||||
{
|
||||
static $mappedURLs = null;
|
||||
|
||||
if (is_null($mappedURLs)) {
|
||||
$mappedURLs = [];
|
||||
|
||||
$config = DUPX_ArchiveConfig::getInstance();
|
||||
/** @var SiteOwrMap[] */
|
||||
$customMap = PrmMng::getInstance()->getValue(PrmMng::PARAM_MU_REPLACE);
|
||||
$mainSiteDomain = SnapURL::parseUrl(PrmMng::getInstance()->getValue(PrmMng::PARAM_URL_NEW), PHP_URL_HOST);
|
||||
|
||||
$sourceMainSiteIndex = $config->getMainSiteIndex();
|
||||
$sourceMainUrl = $config->getUrlFromSubsiteObj($config->subsites[$sourceMainSiteIndex]);
|
||||
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$subdomain = (isset($overwriteData['subdomain']) && $overwriteData['subdomain']);
|
||||
} else {
|
||||
$subdomain = $config->isSubdomain();
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($config->subsites); $i++) {
|
||||
$subsite = $config->subsites[$i];
|
||||
for ($j = 0; $j < count($customMap); $j++) {
|
||||
if ($subsite->id != $customMap[$j]->getSourceId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mappedURLs[$subsite->id] = $customMap[$j]->getNewSlugFullUrl($mainSiteDomain, $subdomain, true);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($j == count($customMap)) {
|
||||
$mappedURLs[$subsite->id] = DUPX_U::getDefaultURL($config->getUrlFromSubsiteObj($subsite), $sourceMainUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mappedURLs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Plugins;
|
||||
|
||||
class PluginCustomActions
|
||||
{
|
||||
const BY_DEFAULT_AUTO = 'plugin_def_auto';
|
||||
const BY_DEFAULT_DISABLED = 'plugin_def_disabled';
|
||||
const BY_DEFAULT_ENABLED = 'plugin_def_enabled';
|
||||
|
||||
/** @var string */
|
||||
protected $slug;
|
||||
/** @var string|callable */
|
||||
protected $byDefaultStatus = self::BY_DEFAULT_AUTO;
|
||||
/** @var bool|callable */
|
||||
protected $enableAfterLogin = false;
|
||||
/** @var string */
|
||||
protected $byDefaultMessage = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $slug plugin slug
|
||||
* @param string|callable $byDefaultStatus set plugin status
|
||||
* @param bool|callable $enableAfterLogin enable plugin after login
|
||||
* @param string|callable $byDefaultMessage message if status change
|
||||
*/
|
||||
public function __construct(
|
||||
$slug,
|
||||
$byDefaultStatus = self::BY_DEFAULT_AUTO,
|
||||
$enableAfterLogin = false,
|
||||
$byDefaultMessage = ''
|
||||
) {
|
||||
$this->slug = $slug;
|
||||
$this->byDefaultStatus = $byDefaultStatus;
|
||||
$this->enableAfterLogin = $enableAfterLogin;
|
||||
$this->byDefaultMessage = $byDefaultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return by defualt status
|
||||
*
|
||||
* @return string by default enum
|
||||
*/
|
||||
public function byDefaultStatus()
|
||||
{
|
||||
if (is_callable($this->byDefaultStatus)) {
|
||||
return call_user_func($this->byDefaultStatus, $this);
|
||||
} else {
|
||||
return $this->byDefaultStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if plugin must be enabled after login
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEnableAfterLogin()
|
||||
{
|
||||
if (is_callable($this->enableAfterLogin)) {
|
||||
return call_user_func($this->enableAfterLogin, $this);
|
||||
} else {
|
||||
return $this->enableAfterLogin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* By default message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function byDefaultMessage()
|
||||
{
|
||||
if (is_callable($this->byDefaultMessage)) {
|
||||
return call_user_func($this->byDefaultMessage, $this);
|
||||
} else {
|
||||
return $this->byDefaultMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Plugins;
|
||||
|
||||
use Duplicator\Installer\Core\Deploy\Helpers;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Package\DescriptorPlugin;
|
||||
use Duplicator\Installer\Utils\InstallerOrigFileMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use Error;
|
||||
use Exception;
|
||||
|
||||
use const WP_PLUGIN_DIR;
|
||||
|
||||
/**
|
||||
* Pplugin item descriptor
|
||||
*/
|
||||
class PluginItem extends DescriptorPlugin
|
||||
{
|
||||
const STATUS_ACTIVE = 'active';
|
||||
const STATUS_INACTIVE = 'inactive';
|
||||
const STATUS_NETWORK_ACTIVE = 'network-active';
|
||||
const STATUS_DROP_INS = 'drop-ins';
|
||||
const STATUS_MUST_USE = 'must-use';
|
||||
|
||||
/** @var bool */
|
||||
public $activateAction = false;
|
||||
/** @var bool */
|
||||
public $deactivateAction = false;
|
||||
/** @var null|string[] */
|
||||
public $deactivateMessage;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $subsite // if -1 it checks that at least one site exists in which it is active in the netowrk
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isActive($subsite = -1): bool
|
||||
{
|
||||
if ($this->active === true) {
|
||||
return true;
|
||||
} elseif ($subsite === -1 && !empty($this->active)) {
|
||||
return true;
|
||||
} elseif (
|
||||
is_array($this->active) &&
|
||||
in_array($subsite, $this->active)
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin slug
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isNetworkActive()
|
||||
{
|
||||
return $this->networkActive;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isMustUse()
|
||||
{
|
||||
return $this->mustUse;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isDropIns()
|
||||
{
|
||||
return $this->dropIns;
|
||||
}
|
||||
|
||||
/**
|
||||
* is true if all active status are false
|
||||
*
|
||||
* @param int $subsite if -1 it checks that at least one site exists in which it is active in the netowrk
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInactive($subsite = -1): bool
|
||||
{
|
||||
return !$this->isActive($subsite) && !$this->isNetworkActive() && !$this->isMustUse() && !$this->isDropIns();
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if isn't networkActive or must-use or drop-ins
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isNetworkInactive(): bool
|
||||
{
|
||||
return !$this->isNetworkActive() && !$this->isMustUse() && !$this->isDropIns();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isIgnore(): bool
|
||||
{
|
||||
return in_array($this->slug, PrmMng::getInstance()->getValue(PrmMng::PARAM_IGNORE_PLUGINS));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isForceDisabled(): bool
|
||||
{
|
||||
return in_array($this->slug, PrmMng::getInstance()->getValue(PrmMng::PARAM_FORCE_DIABLE_PLUGINS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set activate action true if the plugin is active or if deactivateAction is enabled
|
||||
*
|
||||
* @param int $subsite current subsite id
|
||||
* @param bool $networkCheck if true check only on network or check by subsite id
|
||||
* @param bool $forceActivation if true skip all pluginstati check and set activation action
|
||||
*
|
||||
* @return bool return activateAction
|
||||
*/
|
||||
public function setActivationAction($subsite = -1, $networkCheck = false, $forceActivation = false)
|
||||
{
|
||||
if ($this->isIgnore()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($forceActivation) {
|
||||
Log::info(
|
||||
'PLUGINS [' . __FUNCTION__ . ']: set forced activation action ' . Log::v2str($this->slug),
|
||||
Log::LV_DEBUG
|
||||
);
|
||||
return ($this->activateAction = true);
|
||||
}
|
||||
|
||||
$activate = false;
|
||||
if ($networkCheck) {
|
||||
if ($this->isNetworkInactive()) {
|
||||
$activate = true;
|
||||
}
|
||||
} else {
|
||||
if ($this->isInactive($subsite) || ($subsite > -1 && $this->isNetworkActive())) {
|
||||
$activate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($activate || $this->deactivateAction) {
|
||||
Log::info(
|
||||
'PLUGINS [' . __FUNCTION__ . ']: set activation action ' . Log::v2str($this->slug),
|
||||
Log::LV_DEBUG
|
||||
);
|
||||
$this->activateAction = true;
|
||||
}
|
||||
|
||||
return $this->activateAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set deactivation action if the plugin isn't inactive
|
||||
*
|
||||
* @param int $subsite Id of a Subsite
|
||||
* @param ?string $shortMsg Short message for deactivation log
|
||||
* @param ?string $longMsg long message for deactivation log
|
||||
* @param boolean $networkCheck if true check if is active only on network
|
||||
*
|
||||
* @return boolean return deactivaeAction status
|
||||
*/
|
||||
public function setDeactivateAction($subsite = -1, $shortMsg = null, $longMsg = null, $networkCheck = false)
|
||||
{
|
||||
if ($this->isIgnore()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$deactivate = false;
|
||||
if ($networkCheck) {
|
||||
if (!$this->isNetworkInactive()) {
|
||||
$deactivate = true;
|
||||
}
|
||||
} else {
|
||||
if (!$this->isInactive($subsite)) {
|
||||
$deactivate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($deactivate) {
|
||||
Log::info(
|
||||
'PLUGINS [' . __FUNCTION__ . ']: set deactivate action ' . Log::v2str($this->slug),
|
||||
Log::LV_DEBUG
|
||||
);
|
||||
$this->deactivateAction = true;
|
||||
if (!empty($shortMsg)) {
|
||||
$this->deactivateMessage = [
|
||||
'shortMsg' => $shortMsg,
|
||||
'longMsg' => $longMsg,
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->deactivateAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin archive path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluginArchivePath(): string
|
||||
{
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
if ($this->isMustUse()) {
|
||||
$mainDir = $archiveConfig->getRelativePathsInArchive('muplugins');
|
||||
} elseif ($this->isDropIns()) {
|
||||
$mainDir = $archiveConfig->getRelativePathsInArchive('wpcontent');
|
||||
} else {
|
||||
$mainDir = $archiveConfig->getRelativePathsInArchive('plugins');
|
||||
}
|
||||
return $mainDir . '/' . $this->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin path, folder if plugin has a folder or file if is a single file plugin (like drop-ins)
|
||||
*
|
||||
* @param bool $filterNotExists if true filter paths that don't exists
|
||||
*
|
||||
* @return false|string false if plugin path doesn't exists
|
||||
*/
|
||||
public function getPluginPath($filterNotExists = true)
|
||||
{
|
||||
$paramManager = PrmMng::getInstance();
|
||||
$mainDir = false;
|
||||
if ($this->isMustUse()) {
|
||||
$mainDir = $paramManager->getValue(PrmMng::PARAM_PATH_MUPLUGINS_NEW);
|
||||
} elseif ($this->isDropIns()) {
|
||||
$mainDir = $paramManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW);
|
||||
} else {
|
||||
$mainDir = $paramManager->getValue(PrmMng::PARAM_PATH_PLUGINS_NEW);
|
||||
}
|
||||
|
||||
if ($mainDir === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dirNameRelative = dirname($this->slug);
|
||||
$relativePath = strlen($dirNameRelative) == 0 || $dirNameRelative == '.' ? $this->slug : $dirNameRelative;
|
||||
|
||||
$result = $mainDir . '/' . $relativePath;
|
||||
if ($filterNotExists && !file_exists($result)) {
|
||||
return false;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function deactivate(): bool
|
||||
{
|
||||
if (!$this->deactivateAction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log::info('[PLUGINS MANAGER] deactivate ' . Log::v2str($this->slug), Log::LV_DETAILED);
|
||||
$deactivated = false;
|
||||
$origFileManager = InstallerOrigFileMng::getInstance();
|
||||
if ($this->isMustUse() || $this->isDropIns()) {
|
||||
if (($pluginPath = $this->getPluginPath()) == false) {
|
||||
Log::info('PLUGINS: can\'t remove plugin ' . $this->slug . ' because it doesn\'t exists');
|
||||
} else {
|
||||
$origFileManager->addEntry($this->slug, $pluginPath, InstallerOrigFileMng::MODE_MOVE);
|
||||
$deactivated = true;
|
||||
}
|
||||
} else {
|
||||
// for other type of plugins do nothing. They are not activated because they are missing in the table list of plugins
|
||||
$deactivated = true;
|
||||
}
|
||||
|
||||
if ($deactivated) {
|
||||
if (is_null($this->deactivateMessage)) {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => $this->name . ' has been deactivated',
|
||||
'level' => DUPX_NOTICE_ITEM::NOTICE,
|
||||
'sections' => 'plugins',
|
||||
]);
|
||||
} else {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => $this->deactivateMessage['shortMsg'],
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => $this->deactivateMessage['longMsg'],
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'plugins',
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t deactivate the plugin ' . $this->name,
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => 'Folder of the plugin not found',
|
||||
'sections' => 'plugins',
|
||||
]);
|
||||
}
|
||||
|
||||
// prevent multiple decativation action
|
||||
$this->deactivateAction = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $subsiteId Subsite ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrgiStatus($subsiteId): string
|
||||
{
|
||||
if ($this->isMustUse()) {
|
||||
return self::STATUS_MUST_USE;
|
||||
} elseif ($this->isDropIns()) {
|
||||
return self::STATUS_DROP_INS;
|
||||
} elseif ($this->isNetworkActive()) {
|
||||
return self::STATUS_NETWORK_ACTIVE;
|
||||
} elseif ($this->isActive($subsiteId)) {
|
||||
return self::STATUS_ACTIVE;
|
||||
} else {
|
||||
return self::STATUS_INACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $status Plugin Status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStatusLabel($status): string
|
||||
{
|
||||
switch ($status) {
|
||||
case self::STATUS_MUST_USE:
|
||||
return 'must-use';
|
||||
case self::STATUS_DROP_INS:
|
||||
return 'drop-in';
|
||||
case self::STATUS_NETWORK_ACTIVE:
|
||||
return 'network active';
|
||||
case self::STATUS_ACTIVE:
|
||||
return 'active';
|
||||
case self::STATUS_INACTIVE:
|
||||
return 'inactive';
|
||||
default:
|
||||
throw new Exception('Invalid status');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall a single plugin.
|
||||
*
|
||||
* Calls the uninstall hook, if it is available.
|
||||
*
|
||||
* @return bool True if a plugin's uninstall.php file has been found and included.
|
||||
*/
|
||||
public function uninstall(): bool
|
||||
{
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
try {
|
||||
Helpers::loadWP();
|
||||
// UNINSTALL PLUGIN IF IS ACTIVE
|
||||
$level = error_reporting(E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING
|
||||
| E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR);
|
||||
if (SnapUtil::isIniValChangeable('display_errors')) {
|
||||
@ini_set('display_errors', '0');
|
||||
}
|
||||
Log::info("UNINSTALL PLUGIN " . Log::v2str($this->slug), Log::LV_DEBUG);
|
||||
$pluginFile = plugin_basename($this->slug);
|
||||
$uninstallable_plugins = (array)get_option('uninstall_plugins');
|
||||
/**
|
||||
* Fires in uninstall_plugin() immediately before the plugin is uninstalled.
|
||||
*
|
||||
* @param string $plugin Path to the main plugin file from plugins directory.
|
||||
* @param array $uninstallable_plugins Uninstallable plugins.
|
||||
*/
|
||||
do_action('pre_uninstall_plugin', $this->slug, $uninstallable_plugins);
|
||||
if (file_exists(WP_PLUGIN_DIR . '/' . dirname($pluginFile) . '/uninstall.php')) {
|
||||
if (isset($uninstallable_plugins[$pluginFile])) {
|
||||
unset($uninstallable_plugins[$pluginFile]);
|
||||
update_option('uninstall_plugins', $uninstallable_plugins);
|
||||
}
|
||||
unset($uninstallable_plugins);
|
||||
if (defined('WP_UNINSTALL_PLUGIN')) {
|
||||
$already_defined_uninstall_const = true;
|
||||
} else {
|
||||
define('WP_UNINSTALL_PLUGIN', $pluginFile);
|
||||
$already_defined_uninstall_const = false;
|
||||
}
|
||||
|
||||
wp_register_plugin_realpath(WP_PLUGIN_DIR . '/' . $pluginFile);
|
||||
if ($already_defined_uninstall_const) {
|
||||
$uninstall_file_content = file_get_contents(WP_PLUGIN_DIR . '/' . dirname($pluginFile) . '/uninstall.php');
|
||||
/*
|
||||
$regexProhibited = array(
|
||||
'dirname[\t\s]*\([\t\s]*WP_UNINSTALL_PLUGIN[\t\s]*\)',
|
||||
'WP_UNINSTALL_PLUGIN[\t\s]*\!?=',
|
||||
'\!?=[\t\s]*WP_UNINSTALL_PLUGIN',
|
||||
'current_user_can'
|
||||
); */
|
||||
$prohibited_codes = [
|
||||
'dirname( WP_UNINSTALL_PLUGIN )',
|
||||
'dirname(WP_UNINSTALL_PLUGIN )',
|
||||
'dirname( WP_UNINSTALL_PLUGIN)',
|
||||
'dirname(WP_UNINSTALL_PLUGIN)',
|
||||
'WP_UNINSTALL_PLUGIN =',
|
||||
'WP_UNINSTALL_PLUGIN !=',
|
||||
'WP_UNINSTALL_PLUGIN=',
|
||||
'WP_UNINSTALL_PLUGIN!=',
|
||||
'= WP_UNINSTALL_PLUGIN',
|
||||
'!= WP_UNINSTALL_PLUGIN',
|
||||
'=WP_UNINSTALL_PLUGIN=',
|
||||
'!=WP_UNINSTALL_PLUGIN',
|
||||
'current_user_can',
|
||||
];
|
||||
foreach ($prohibited_codes as $prohibited_code) {
|
||||
if (false !== stripos($uninstall_file_content, $prohibited_code)) {
|
||||
Log::info("Can't include uninstall.php file of the " . $this->slug . " because prohibited code found");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
include(WP_PLUGIN_DIR . '/' . dirname($pluginFile) . '/uninstall.php');
|
||||
} elseif (isset($uninstallable_plugins[$pluginFile])) {
|
||||
$callable = $uninstallable_plugins[$pluginFile];
|
||||
unset($uninstallable_plugins[$pluginFile]);
|
||||
update_option('uninstall_plugins', $uninstallable_plugins);
|
||||
unset($uninstallable_plugins);
|
||||
wp_register_plugin_realpath(WP_PLUGIN_DIR . '/' . $pluginFile);
|
||||
include_once(WP_PLUGIN_DIR . '/' . $pluginFile);
|
||||
add_action("uninstall_{$pluginFile}", $callable);
|
||||
/**
|
||||
* Fires in uninstall_plugin() once the plugin has been uninstalled.
|
||||
*
|
||||
* The action concatenates the 'uninstall_' prefix with the basename of the
|
||||
* plugin passed to uninstall_plugin() to create a dynamically-named action.
|
||||
*
|
||||
* @since 2.7.0
|
||||
*/
|
||||
do_action("uninstall_{$pluginFile}");
|
||||
// Extra
|
||||
// Extra
|
||||
} else {
|
||||
// The plugin was never activated so no need to call uninstallation hook
|
||||
}
|
||||
|
||||
// store plugin in original file folder
|
||||
$origFileManager = InstallerOrigFileMng::getInstance();
|
||||
if (($pluginPath = $this->getPluginPath()) == false) {
|
||||
Log::info('PLUGINS: can\'t remove plugin ' . $this->slug . ' because doesn\'t exist');
|
||||
} else {
|
||||
$origFileManager->addEntry($this->slug, $pluginPath, InstallerOrigFileMng::MODE_MOVE);
|
||||
}
|
||||
} catch (Exception | Error $e) {
|
||||
$errorMsg = "**ERROR** The Inactive plugin " . $this->name . " can't be deleted";
|
||||
$longMsg = 'Please delete the plugin ' . $this->name . ' (' . $this->slug . ') manually' . PHP_EOL .
|
||||
'Exception message: ' . $e->getMessage() . PHP_EOL .
|
||||
'Trace: ' . $e->getTraceAsString();
|
||||
Log::info($errorMsg);
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => $errorMsg,
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsg' => $longMsg,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
|
||||
'sections' => 'plugins',
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
error_reporting($level);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,622 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy\Plugins;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Original installer files manager
|
||||
* singleton class
|
||||
*/
|
||||
final class PluginsManager
|
||||
{
|
||||
const SLUG_WOO_ADMIN = 'woocommerce-admin/woocommerce-admin.php';
|
||||
const SLUG_SIMPLE_SSL = 'really-simple-ssl/rlrsssl-really-simple-ssl.php';
|
||||
const SLUG_ONE_CLICK_SSL = 'one-click-ssl/ssl.php';
|
||||
const SLUG_WP_FORCE_SSL = 'wp-force-ssl/wp-force-ssl.php';
|
||||
const SLUG_RECAPTCHA = 'simple-google-recaptcha/simple-google-recaptcha.php';
|
||||
const SLUG_WPBAKERY_PAGE_BUILDER = 'js_composer/js_composer.php';
|
||||
const SLUG_DUPLICATOR_PRO = 'duplicator-pro/duplicator-pro.php';
|
||||
const SLUG_DUPLICATOR_LITE = 'duplicator/duplicator.php';
|
||||
const SLUG_DUPLICATOR_TESTER = 'duplicator-tester-plugin/duplicator-tester.php';
|
||||
const SLUG_POPUP_MAKER = 'popup-maker/popup-maker.php';
|
||||
const SLUG_JETPACK = 'jetpack/jetpack.php';
|
||||
const SLUG_WP_ROCKET = 'wp-rocket/wp-rocket.php';
|
||||
const SLUG_BETTER_WP_SECURITY = 'better-wp-security/better-wp-security.php';
|
||||
const SLUG_HTTPS_REDIRECTION = 'https-redirection/https-redirection.php';
|
||||
const SLUG_LOGIN_NOCAPTCHA = 'login-recaptcha/login-nocaptcha.php';
|
||||
const SLUG_GOOGLE_CAPTCHA = 'google-captcha/google-captcha.php';
|
||||
const SLUG_ADVANCED_CAPTCHA = 'advanced-google-recaptcha/advanced-google-recaptcha.php';
|
||||
const OPTION_ACTIVATE_PLUGINS = 'dupli_opt_activate_plugins_after_installation';
|
||||
|
||||
const FORCE_ACTIVATE_PLUGINS = [
|
||||
self::SLUG_DUPLICATOR_PRO,
|
||||
self::SLUG_DUPLICATOR_TESTER,
|
||||
];
|
||||
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
/** @var PluginItem[] */
|
||||
private $plugins = [];
|
||||
/** @var PluginItem[] */
|
||||
private $unistallList = [];
|
||||
/** @var PluginCustomActions[] */
|
||||
private $customPluginsActions = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
|
||||
foreach (DUPX_ArchiveConfig::getInstance()->wpInfo->plugins as $pluginInfo) {
|
||||
$this->plugins[$pluginInfo->slug] = new PluginItem((array)$pluginInfo);
|
||||
}
|
||||
|
||||
$this->setCustomPluginsActions();
|
||||
|
||||
Log::info('CONSTRUCT PLUGINS OBJECTS: ' . Log::v2str($this->plugins), Log::LV_HARD_DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method prepares customPluginActions for further processing
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setCustomPluginsActions(): void
|
||||
{
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
$default = PluginCustomActions::BY_DEFAULT_DISABLED;
|
||||
$afterLogin = false;
|
||||
$longMsg = 'The plugin is disabled in the single site because it is active on the network.';
|
||||
} else {
|
||||
$default = PluginCustomActions::BY_DEFAULT_ENABLED;
|
||||
$afterLogin = true;
|
||||
$longMsg = '';
|
||||
}
|
||||
|
||||
$this->customPluginsActions[self::SLUG_DUPLICATOR_PRO] = new PluginCustomActions(
|
||||
self::SLUG_DUPLICATOR_PRO,
|
||||
$default,
|
||||
$afterLogin,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$this->customPluginsActions[self::SLUG_DUPLICATOR_TESTER] = new PluginCustomActions(
|
||||
self::SLUG_DUPLICATOR_TESTER,
|
||||
$default,
|
||||
$afterLogin,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$this->customPluginsActions[self::SLUG_DUPLICATOR_LITE] = new PluginCustomActions(
|
||||
self::SLUG_DUPLICATOR_LITE,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
false,
|
||||
'Duplicator LITE has been deactivated because in the new versions it is not possible to ' .
|
||||
'have Duplicator LITE active at the same time as PRO.'
|
||||
);
|
||||
|
||||
$longMsg = "This plugin is deactivated by default automatically. "
|
||||
. "<strong>You must reactivate from the WordPress admin panel after completing the installation</strong> "
|
||||
. "or from the plugins tab."
|
||||
. " Your site's frontend will render properly after reactivating the plugin.";
|
||||
|
||||
$this->customPluginsActions[self::SLUG_WPBAKERY_PAGE_BUILDER] = new PluginCustomActions(
|
||||
self::SLUG_WPBAKERY_PAGE_BUILDER,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
true,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$this->customPluginsActions[self::SLUG_JETPACK] = new PluginCustomActions(
|
||||
self::SLUG_JETPACK,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
true,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$longMsg = "This plugin is deactivated by default automatically due to issues that one may encounter when migrating. "
|
||||
. "<strong>You must reactivate from the WordPress admin panel after completing the installation</strong> "
|
||||
. "or from the plugins tab."
|
||||
. " Your site's frontend will render properly after reactivating the plugin.";
|
||||
|
||||
$this->customPluginsActions[self::SLUG_POPUP_MAKER] = new PluginCustomActions(
|
||||
self::SLUG_POPUP_MAKER,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
true,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$this->customPluginsActions[self::SLUG_WP_ROCKET] = new PluginCustomActions(
|
||||
self::SLUG_WP_ROCKET,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
true,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$longMsg = "This plugin is deactivated by default automatically due to issues that one may encounter when migrating. "
|
||||
. "<strong>You must reactivate from the WordPress admin panel after completing the installation</strong> "
|
||||
. "or from the plugins tab.";
|
||||
|
||||
$this->customPluginsActions[self::SLUG_WOO_ADMIN] = new PluginCustomActions(
|
||||
self::SLUG_WOO_ADMIN,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
true,
|
||||
$longMsg
|
||||
);
|
||||
|
||||
$this->customPluginsActions[self::SLUG_BETTER_WP_SECURITY] = new PluginCustomActions(
|
||||
self::SLUG_BETTER_WP_SECURITY,
|
||||
PluginCustomActions::BY_DEFAULT_DISABLED,
|
||||
true,
|
||||
$longMsg
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return PluginItem[]
|
||||
*/
|
||||
public function getPlugins()
|
||||
{
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDropInsPaths()
|
||||
{
|
||||
static $dropInsPaths = null;
|
||||
|
||||
if (is_null($dropInsPaths)) {
|
||||
$dropInsPaths = [];
|
||||
foreach ($this->plugins as $plugin) {
|
||||
if ($plugin->isDropIns()) {
|
||||
$dropInsPaths[] = $plugin->getPluginArchivePath();
|
||||
}
|
||||
}
|
||||
Log::info('DROP INS PATHS: ' . Log::v2str($dropInsPaths));
|
||||
}
|
||||
return $dropInsPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function performs status checks on plugins and disables those that must disable creating user messages
|
||||
*
|
||||
* @param int|null $subsiteId ID of a subsite
|
||||
|
||||
* @return void
|
||||
*/
|
||||
public function preViewChecks($subsiteId = null): void
|
||||
{
|
||||
$noticeManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$activePlugins = $paramsManager->getValue(PrmMng::PARAM_PLUGINS);
|
||||
$saveParams = false;
|
||||
|
||||
foreach ($this->customPluginsActions as $slug => $customPlugin) {
|
||||
if (!isset($this->plugins[$slug])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($customPlugin->byDefaultStatus()) {
|
||||
case PluginCustomActions::BY_DEFAULT_DISABLED:
|
||||
if (($delKey = array_search($slug, $activePlugins)) !== false) {
|
||||
$saveParams = true;
|
||||
unset($activePlugins[$delKey]);
|
||||
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => 'Plugin ' . $this->plugins[$slug]->name . ' disabled by default',
|
||||
'level' => DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => $customPlugin->byDefaultMessage(),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'plugins',
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE, 'custom_plugin_action' . $slug);
|
||||
}
|
||||
break;
|
||||
case PluginCustomActions::BY_DEFAULT_ENABLED:
|
||||
if (!in_array($slug, $activePlugins)) {
|
||||
$saveParams = true;
|
||||
$activePlugins[] = $slug;
|
||||
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => 'Plugin ' . $this->plugins[$slug]->name . ' enabled by default',
|
||||
'level' => DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => $customPlugin->byDefaultMessage(),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
'sections' => 'plugins',
|
||||
], DUPX_NOTICE_MANAGER::ADD_UNIQUE, 'custom_plugin_action' . $slug);
|
||||
}
|
||||
break;
|
||||
case PluginCustomActions::BY_DEFAULT_AUTO:
|
||||
Log::info("AUTO ACTION WAS TRIGGERED");
|
||||
$saveParams = false;
|
||||
if (!$this->plugins[$slug]->isInactive($subsiteId) && $customPlugin->isEnableAfterLogin()) {
|
||||
$this->plugins[$slug]->setActivationAction($subsiteId);
|
||||
} elseif (!$customPlugin->isEnableAfterLogin()) {
|
||||
$this->plugins[$slug]->setDeactivateAction(
|
||||
$subsiteId,
|
||||
'Deactivated plugin: ' . $this->plugins[$slug]->name,
|
||||
$customPlugin->byDefaultMessage()
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($saveParams) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_PLUGINS, $activePlugins);
|
||||
$paramsManager->save();
|
||||
$noticeManager->saveNotices();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $subsiteId ID of a subsite
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public function getStatusCounts($subsiteId = -1): array
|
||||
{
|
||||
$result = [
|
||||
PluginItem::STATUS_MUST_USE => 0,
|
||||
PluginItem::STATUS_DROP_INS => 0,
|
||||
PluginItem::STATUS_NETWORK_ACTIVE => 0,
|
||||
PluginItem::STATUS_ACTIVE => 0,
|
||||
PluginItem::STATUS_INACTIVE => 0,
|
||||
];
|
||||
|
||||
foreach ($this->plugins as $plugin) {
|
||||
$result[$plugin->getOrgiStatus($subsiteId)]++;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $subsiteId ID of a subsite
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getDefaultActivePluginsList($subsiteId = -1): array
|
||||
{
|
||||
$result = [];
|
||||
$networkInstall = InstState::isNewSiteIsMultisite();
|
||||
foreach ($this->plugins as $plugin) {
|
||||
if ($networkInstall) {
|
||||
if ($plugin->isNetworkActive() || $plugin->isMustUse() || $plugin->isDropIns()) {
|
||||
$result[] = $plugin->getSlug();
|
||||
}
|
||||
} else {
|
||||
if (!$plugin->isInactive($subsiteId)) {
|
||||
$result[] = $plugin->getSlug();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* return alla plugins slugs list
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllPluginsSlugs()
|
||||
{
|
||||
return array_keys($this->plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all archive plugins archive paths related to target site, folder if plugin has a folder or file if is a single file plugin (like drop-ins)
|
||||
*
|
||||
* @param bool $filterNotExists if true filter not exists folders
|
||||
* @param bool $filterNotInArchive if true filter that plugins files that are not in archive
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllPluginsPaths($filterNotExists = true, $filterNotInArchive = false): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->plugins as $plugin) {
|
||||
if (($path = $plugin->getPluginPath($filterNotExists)) == false) {
|
||||
continue;
|
||||
}
|
||||
if ($filterNotInArchive && !$plugin->isInArchive) {
|
||||
continue;
|
||||
}
|
||||
$result[] = $path;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $plugins List of plugins
|
||||
* @param integer $subsiteId ID of a subsite
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setActions($plugins, $subsiteId = -1): void
|
||||
{
|
||||
Log::info('FUNCTION [' . __FUNCTION__ . ']: plugins ' . Log::v2str($plugins), Log::LV_DEBUG);
|
||||
$networkInstall = InstState::isNewSiteIsMultisite();
|
||||
|
||||
foreach ($this->plugins as $slug => $plugin) {
|
||||
$deactivate = false;
|
||||
|
||||
if ($plugin->isForceDisabled()) {
|
||||
$deactivate = true;
|
||||
} else {
|
||||
if ($networkInstall) {
|
||||
if (!$this->plugins[$slug]->isNetworkInactive() && !in_array($slug, $plugins)) {
|
||||
$deactivate = true;
|
||||
}
|
||||
} else {
|
||||
if (!$this->plugins[$slug]->isInactive($subsiteId) && !in_array($slug, $plugins)) {
|
||||
$deactivate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($deactivate) {
|
||||
$this->plugins[$slug]->setDeactivateAction($subsiteId, null, null, $networkInstall);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($plugins as $slug) {
|
||||
if (isset($this->plugins[$slug])) {
|
||||
$this->plugins[$slug]->setActivationAction($subsiteId, $networkInstall);
|
||||
}
|
||||
}
|
||||
|
||||
///Start
|
||||
$paramManager = PrmMng::getInstance();
|
||||
$casesToHandle = [
|
||||
[
|
||||
'slugs' => [
|
||||
self::SLUG_SIMPLE_SSL,
|
||||
self::SLUG_WP_FORCE_SSL,
|
||||
self::SLUG_HTTPS_REDIRECTION,
|
||||
],
|
||||
'longMsg' => "The plugin '%name%' has been deactivated because you are migrating from SSL (HTTPS) to Non-SSL (HTTP).<br>" .
|
||||
"If it was not deactivated, you would not be able to login.",
|
||||
'info' => '%name% [as Non-SSL installation] will be deactivated',
|
||||
'condition' => !SnapURL::isCurrentUrlSSL(),
|
||||
],
|
||||
[
|
||||
'slugs' => [
|
||||
self::SLUG_RECAPTCHA,
|
||||
self::SLUG_LOGIN_NOCAPTCHA,
|
||||
self::SLUG_GOOGLE_CAPTCHA,
|
||||
self::SLUG_ADVANCED_CAPTCHA,
|
||||
],
|
||||
'longMsg' => "The plugin '%name%' has been deactivated because reCaptcha requires a site key which is bound to the site's address." .
|
||||
"Your package site's address and installed site's address don't match. " .
|
||||
"You can reactivate it after finishing with the installation.<br>" .
|
||||
"<strong>Please do not forget to change the reCaptcha site key after activating it.</strong>",
|
||||
'info' => '%name% [as package creation site URL and the installation site URL are different] will be deactivated',
|
||||
'condition' => $paramManager->getValue(PrmMng::PARAM_SITE_URL_OLD) != $paramManager->getValue(PrmMng::PARAM_SITE_URL),
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($casesToHandle as $case) {
|
||||
foreach ($case['slugs'] as $slug) {
|
||||
if (isset($this->plugins[$slug]) && $this->plugins[$slug]->isActive($subsiteId) && $case['condition']) {
|
||||
$info = str_replace('%name%', $this->plugins[$slug]->name, $case['info']);
|
||||
$longMsg = str_replace('%name%', $this->plugins[$slug]->name, $case['longMsg']);
|
||||
Log::info($info, Log::LV_DEBUG);
|
||||
$this->customPluginsActions[$slug] = new PluginCustomActions(
|
||||
$slug,
|
||||
PluginCustomActions::BY_DEFAULT_AUTO,
|
||||
false,
|
||||
$longMsg
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///end
|
||||
DUPX_NOTICE_MANAGER::getInstance()->saveNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \mysqli $dbh Connection
|
||||
* @param integer $subsiteId Subsite ID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function executeActions($dbh, $subsiteId = -1): bool
|
||||
{
|
||||
$activePluginsList = [];
|
||||
$activateOnLoginPluginsList = [];
|
||||
$removeInactivePlugins = PrmMng::getInstance()->getValue(PrmMng::PARAM_REMOVE_RENDUNDANT);
|
||||
$this->unistallList = [];
|
||||
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
Log::info('SKIP PLUGIN ACTION FOR ADD SITE IN MULTISITE');
|
||||
return true;
|
||||
}
|
||||
|
||||
$escapedTablePrefix = mysqli_real_escape_string(
|
||||
$dbh,
|
||||
PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_TABLE_PREFIX)
|
||||
);
|
||||
|
||||
$noticeManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
Log::info('PLUGINS OBJECTS: ' . Log::v2str($this->plugins), Log::LV_HARD_DEBUG);
|
||||
|
||||
foreach ($this->customPluginsActions as $slug => $customPlugin) {
|
||||
if (!isset($this->plugins[$slug])) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->plugins[$slug]->isInactive($subsiteId) && $customPlugin->isEnableAfterLogin()) {
|
||||
$this->plugins[$slug]->setActivationAction($subsiteId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->plugins as $plugin) {
|
||||
// Force activate plugins are handled seperately
|
||||
if (in_array($plugin->getSlug(), self::FORCE_ACTIVATE_PLUGINS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$deactivated = false;
|
||||
if ($plugin->deactivateAction) {
|
||||
$plugin->deactivate();
|
||||
// can't remove deactivate after login
|
||||
$deactivated = true;
|
||||
} elseif (InstState::isNewSiteIsMultisite()) {
|
||||
if ($plugin->isNetworkActive()) {
|
||||
$activePluginsList[$plugin->getSlug()] = time();
|
||||
}
|
||||
} else {
|
||||
if ($plugin->isActive($subsiteId)) {
|
||||
$activePluginsList[] = $plugin->getSlug();
|
||||
}
|
||||
}
|
||||
|
||||
if ($plugin->activateAction) {
|
||||
$activateOnLoginPluginsList[] = $plugin->getSlug();
|
||||
$noticeManager->addFinalReportNotice([
|
||||
'shortMsg' => 'Activate ' . $plugin->name . ' after you login.',
|
||||
'level' => DUPX_NOTICE_ITEM::NOTICE,
|
||||
'sections' => 'plugins',
|
||||
]);
|
||||
} else {
|
||||
// remove only if isn't activated
|
||||
if ($removeInactivePlugins && ($plugin->isInactive($subsiteId) || $deactivated)) {
|
||||
$this->unistallList[] = $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::FORCE_ACTIVATE_PLUGINS as $slug) {
|
||||
$this->forceActivatePlugin($slug, $activePluginsList);
|
||||
}
|
||||
Log::info('Active plugins: ' . Log::v2str($activePluginsList), Log::LV_DEFAULT);
|
||||
|
||||
$value = mysqli_real_escape_string($dbh, @serialize($activePluginsList));
|
||||
if (InstState::isNewSiteIsMultisite()) {
|
||||
$table = $escapedTablePrefix . 'sitemeta';
|
||||
$query = "UPDATE `" . $table . "` SET meta_value = '" . $value . "' WHERE meta_key = 'active_sitewide_plugins'";
|
||||
} else {
|
||||
$optionTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getOptionsTableName());
|
||||
$query = "UPDATE `" . $optionTable . "` SET option_value = '" . $value . "' WHERE option_name = 'active_plugins' ";
|
||||
}
|
||||
|
||||
if (!DUPX_DB::mysqli_query($dbh, $query)) {
|
||||
$noticeManager->addFinalReportNotice([
|
||||
'shortMsg' => 'QUERY ERROR: MySQL',
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsg' => "Error description: " . mysqli_error($dbh),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
throw new Exception("Database error description: " . mysqli_error($dbh));
|
||||
}
|
||||
|
||||
if (!InstState::isNewSiteIsMultisite()) {
|
||||
$value = mysqli_real_escape_string($dbh, @serialize($activateOnLoginPluginsList));
|
||||
$optionTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getOptionsTableName());
|
||||
$query = "INSERT INTO `" . $optionTable . "` (option_name, option_value)
|
||||
VALUES('" . self::OPTION_ACTIVATE_PLUGINS . "','" . $value . "') ON DUPLICATE KEY UPDATE option_name=\"" . self::OPTION_ACTIVATE_PLUGINS . "\"";
|
||||
if (!DUPX_DB::mysqli_query($dbh, $query)) {
|
||||
$noticeManager->addFinalReportNotice([
|
||||
'shortMsg' => 'QUERY ERROR: MySQL',
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsg' => "Error description: " . mysqli_error($dbh),
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'sections' => 'database',
|
||||
]);
|
||||
throw new Exception("Database error description: " . mysqli_error($dbh));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove inactive plugins
|
||||
* this method must calle after wp-config set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function uninstallInactivePlugins(): void
|
||||
{
|
||||
Log::info('FUNCTION [' . __FUNCTION__ . ']: uninstall inactive plugins');
|
||||
|
||||
/** @var PluginItem $plugin */
|
||||
foreach ($this->unistallList as $plugin) {
|
||||
if ($plugin->uninstall()) {
|
||||
Log::info("UNINSTALL PLUGIN " . Log::v2str($plugin->getSlug()) . ' DONE');
|
||||
} else {
|
||||
Log::info("UNINSTALL PLUGIN " . Log::v2str($plugin->getSlug()) . ' FAILED');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force activate a plugin if not already in the active list.
|
||||
*
|
||||
* @param string $slug Plugin slug to activate
|
||||
* @param array<int|string, int|string> $activePluginsList Active plugins list (passed by reference)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function forceActivatePlugin(string $slug, array &$activePluginsList): void
|
||||
{
|
||||
if (!array_key_exists($slug, $this->plugins)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$isMultisite = InstState::isNewSiteIsMultisite();
|
||||
$pluginInList = $isMultisite
|
||||
? array_key_exists($slug, $activePluginsList)
|
||||
: in_array($slug, $activePluginsList, true);
|
||||
|
||||
if (!$pluginInList) {
|
||||
if ($isMultisite) {
|
||||
$activePluginsList[$slug] = time();
|
||||
} else {
|
||||
$activePluginsList[] = $slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,653 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator/Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy;
|
||||
|
||||
use DUPX_Extraction;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\InstallerOrigFileMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use DUPX_Package;
|
||||
use DUPX_U;
|
||||
use DUPX_WPConfig;
|
||||
use Error;
|
||||
use Exception;
|
||||
use mysqli;
|
||||
|
||||
class ServerConfigs
|
||||
{
|
||||
const INSTALLER_HOST_ENTITY_PREFIX = 'installer_host_';
|
||||
const CONFIG_ORIG_FILE_USERINI_ID = 'userini';
|
||||
const CONFIG_ORIG_FILE_HTACCESS_ID = 'htaccess';
|
||||
const CONFIG_ORIG_FILE_WPCONFIG_ID = 'wpconfig';
|
||||
const CONFIG_ORIG_FILE_PHPINI_ID = 'phpini';
|
||||
const CONFIG_ORIG_FILE_WEBCONFIG_ID = 'webconfig';
|
||||
const CONFIG_ORIG_FILE_USERINI_ID_OVERWRITE_SITE = 'installer_host_userini';
|
||||
const CONFIG_ORIG_FILE_HTACCESS_ID_OVERWRITE_SITE = 'installer_host_htaccess';
|
||||
const CONFIG_ORIG_FILE_WPCONFIG_ID_OVERWRITE_SITE = 'installer_host_wpconfig';
|
||||
const CONFIG_ORIG_FILE_PHPINI_ID_OVERWRITE_SITE = 'installer_host_phpini';
|
||||
const CONFIG_ORIG_FILE_WEBCONFIG_ID_OVERWRITE_SITE = 'installer_host_webconfig';
|
||||
|
||||
const ACTION_WPCONF_MODIFY = 'modify';
|
||||
const ACTION_WPCONF_NEW = 'new';
|
||||
const ACTION_WPCONF_NOTHING = 'nothing';
|
||||
|
||||
/**
|
||||
* Common timestamp of all members of this class
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public static function getFixedTimestamp()
|
||||
{
|
||||
static $time = null;
|
||||
|
||||
if (is_null($time)) {
|
||||
$time = date("ymdHis");
|
||||
}
|
||||
|
||||
return $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the original server config file and resets the original to blank
|
||||
*
|
||||
* @param string $rootPath The root path to the location of the server config files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset($rootPath): void
|
||||
{
|
||||
$rootPath = SnapIO::trailingslashit($rootPath);
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
Log::info("\n*** RESET CONFIG FILES IN CURRENT HOSTING");
|
||||
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_WP_CONFIG)) {
|
||||
case self::ACTION_WPCONF_NEW:
|
||||
case self::ACTION_WPCONF_MODIFY:
|
||||
if (InstState::isBridgeInstall()) {
|
||||
// if bridge wp-config must be mantained
|
||||
break;
|
||||
}
|
||||
if (self::runReset($rootPath . 'wp-config.php', self::CONFIG_ORIG_FILE_WPCONFIG_ID) === false) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_WP_CONFIG, 'nothing');
|
||||
}
|
||||
break;
|
||||
case self::ACTION_WPCONF_NOTHING:
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_HTACCESS_CONFIG)) {
|
||||
case 'new':
|
||||
case 'original':
|
||||
if (self::runReset($rootPath . '.htaccess', self::CONFIG_ORIG_FILE_HTACCESS_ID) === false) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_HTACCESS_CONFIG, 'nothing');
|
||||
}
|
||||
break;
|
||||
case 'nothing':
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_OTHER_CONFIG)) {
|
||||
case 'new':
|
||||
case 'original':
|
||||
if (self::runReset($rootPath . 'web.config', self::CONFIG_ORIG_FILE_WEBCONFIG_ID) === false) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_OTHER_CONFIG, 'nothing');
|
||||
}
|
||||
if (self::runReset($rootPath . '.user.ini', self::CONFIG_ORIG_FILE_USERINI_ID) === false) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_OTHER_CONFIG, 'nothing');
|
||||
}
|
||||
if (self::runReset($rootPath . 'php.ini', self::CONFIG_ORIG_FILE_PHPINI_ID) === false) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_OTHER_CONFIG, 'nothing');
|
||||
}
|
||||
break;
|
||||
case 'nothing':
|
||||
break;
|
||||
}
|
||||
|
||||
$paramsManager->save();
|
||||
Log::info("*** RESET CONFIG FILES END");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set config files in target root folder
|
||||
*
|
||||
* @param string $rootPath target root path
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setFiles($rootPath): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$origFiles = InstallerOrigFileMng::getInstance();
|
||||
Log::info("SET CONFIG FILES");
|
||||
|
||||
$entryKey = self::CONFIG_ORIG_FILE_WPCONFIG_ID;
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_WP_CONFIG)) {
|
||||
case 'new':
|
||||
if (SnapIO::copy(DUPX_Package::getWpconfigSamplePath(), DUPX_WPConfig::getWpConfigPath()) === false) {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t reset wp-config.php to wp-config-sample.php',
|
||||
'level' => DUPX_NOTICE_ITEM::CRITICAL,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Target file entry ' . Log::v2str(DUPX_WPConfig::getWpConfigPath()),
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} else {
|
||||
Log::info("Copy wp-config-sample.php to target:" . DUPX_WPConfig::getWpConfigPath());
|
||||
}
|
||||
break;
|
||||
case 'modify':
|
||||
if (SnapIO::copy($origFiles->getEntryStoredPath($entryKey), DUPX_WPConfig::getWpConfigPath()) === false) {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Unable to restore the original ' . $entryKey . '.php entries. Please check the file permission on this server.',
|
||||
'level' => DUPX_NOTICE_ITEM::CRITICAL,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Target file entry ' . Log::v2str(DUPX_WPConfig::getWpConfigPath()),
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} else {
|
||||
Log::info("Retained original entry " . $entryKey . " target:" . DUPX_WPConfig::getWpConfigPath());
|
||||
}
|
||||
break;
|
||||
case 'nothing':
|
||||
break;
|
||||
}
|
||||
|
||||
$entryKey = self::CONFIG_ORIG_FILE_HTACCESS_ID;
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_HTACCESS_CONFIG)) {
|
||||
case 'new':
|
||||
$targetHtaccess = self::getHtaccessTargetPath();
|
||||
if (SnapIO::touch($targetHtaccess) === false) {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t create new .htaccess file',
|
||||
'level' => DUPX_NOTICE_ITEM::CRITICAL,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Target file entry ' . $targetHtaccess,
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} else {
|
||||
Log::info("New .htaccess file created:" . $targetHtaccess);
|
||||
}
|
||||
break;
|
||||
case 'original':
|
||||
if (($storedHtaccess = $origFiles->getEntryStoredPath($entryKey)) === false) {
|
||||
Log::info("Retained original entry. .htaccess doesn\'t exist in original site");
|
||||
break;
|
||||
}
|
||||
|
||||
$targetHtaccess = self::getHtaccessTargetPath();
|
||||
if (SnapIO::copy($storedHtaccess, $targetHtaccess) === false) {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Unable to restore the original ' . $entryKey . ' entries. Please check the file permission on this server.',
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Target file entry ' . Log::v2str($targetHtaccess),
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} else {
|
||||
Log::info("Retained original entry " . $entryKey . " target:" . $targetHtaccess);
|
||||
}
|
||||
break;
|
||||
case 'nothing':
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($paramsManager->getValue(PrmMng::PARAM_OTHER_CONFIG)) {
|
||||
case 'new':
|
||||
if ($origFiles->getEntry(self::CONFIG_ORIG_FILE_WEBCONFIG_ID_OVERWRITE_SITE)) {
|
||||
//IIS: This is reset because on some instances of IIS having old values cause issues
|
||||
//Recommended fix for users who want it because errors are triggered is to have
|
||||
//them check the box for ignoring the web.config files on step 1 of installer
|
||||
$xml_contents = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$xml_contents .= "<!-- Reset by Duplicator Installer. Original can be found in the original_files_ folder-->\n";
|
||||
$xml_contents .= "<configuration></configuration>\n";
|
||||
if (file_put_contents($rootPath . "/web.config", $xml_contents) === false) {
|
||||
Log::info('RESET: can\'t create a new empty web.config');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'original':
|
||||
$entries = [
|
||||
self::CONFIG_ORIG_FILE_USERINI_ID,
|
||||
self::CONFIG_ORIG_FILE_WEBCONFIG_ID,
|
||||
self::CONFIG_ORIG_FILE_PHPINI_ID,
|
||||
];
|
||||
foreach ($entries as $entryKey) {
|
||||
if ($origFiles->getEntry($entryKey) !== false) {
|
||||
if (SnapIO::copy($origFiles->getEntryStoredPath($entryKey), $origFiles->getEntryTargetPath($entryKey)) === false) {
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Unable to restore the original ' . $entryKey . ' entries. Please check the file permission on this server.',
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Target file entry ' . Log::v2str($origFiles->getEntryTargetPath($entryKey)),
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} else {
|
||||
Log::info("Retained original entry " . $entryKey . " target:" . $origFiles->getEntryTargetPath($entryKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'nothing':
|
||||
break;
|
||||
}
|
||||
|
||||
DUPX_NOTICE_MANAGER::getInstance()->saveNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get htaccess target path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getHtaccessTargetPath()
|
||||
{
|
||||
if (($targetEnty = InstallerOrigFileMng::getInstance()->getEntryTargetPath(self::CONFIG_ORIG_FILE_HTACCESS_ID)) !== false) {
|
||||
return $targetEnty;
|
||||
} else {
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW) . '/.htaccess';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the original server config file and resets the original to blank per file
|
||||
*
|
||||
* @param string $filePath file path to store
|
||||
* @param string $storedName if not false rename
|
||||
*
|
||||
* @return bool Returns true if the file was backed-up and reset or there was no file to reset
|
||||
*/
|
||||
private static function runReset(string $filePath, string $storedName): bool
|
||||
{
|
||||
$fileName = basename($filePath);
|
||||
|
||||
try {
|
||||
if (file_exists($filePath)) {
|
||||
if (!SnapIO::chmod($filePath, 'u+rw') || !is_readable($filePath) || !is_writable($filePath)) {
|
||||
throw new Exception("RESET CONFIG FILES: permissions error on file config path " . $filePath);
|
||||
}
|
||||
|
||||
$origFiles = InstallerOrigFileMng::getInstance();
|
||||
$filePath = SnapIO::safePathUntrailingslashit($filePath);
|
||||
|
||||
Log::info("RESET CONFIG FILES: I'M GOING TO MOVE CONFIG FILE " . Log::v2str($fileName) . " IN ORIGINAL FOLDER");
|
||||
|
||||
if (
|
||||
$origFiles->addEntry(
|
||||
self::INSTALLER_HOST_ENTITY_PREFIX . $storedName,
|
||||
$filePath,
|
||||
InstallerOrigFileMng::MODE_MOVE,
|
||||
self::INSTALLER_HOST_ENTITY_PREFIX . $storedName
|
||||
)
|
||||
) {
|
||||
Log::info("\tCONFIG FILE HAS BEEN RESET");
|
||||
} else {
|
||||
throw new Exception("can\'t stored file " . Log::v2str($fileName) . " in orginal file folder");
|
||||
}
|
||||
} else {
|
||||
Log::info("RESET CONFIG FILES: " . Log::v2str($fileName) . " does not exist, no need for rest", Log::LV_DETAILED);
|
||||
}
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'RESET CONFIG FILES ERROR: ');
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addBothNextAndFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t reset config file ' . Log::v2str($fileName) . ' so it will not be modified.',
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Message: ' . $e->getMessage(),
|
||||
'sections' => 'general',
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return wp-config path stored in orig folder of target site
|
||||
*
|
||||
* @return false|string false if local config don't exists
|
||||
*/
|
||||
public static function getWpConfigLocalStoredPath()
|
||||
{
|
||||
return InstallerOrigFileMng::getInstance()->getEntryStoredPath(self::CONFIG_ORIG_FILE_WPCONFIG_ID_OVERWRITE_SITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return wp-config path stored in orig folder of source site
|
||||
*
|
||||
* @return false|string false if old wp config does not exist
|
||||
*/
|
||||
public static function getSourceWpConfigPath()
|
||||
{
|
||||
return InstallerOrigFileMng::getInstance()->getEntryStoredPath(self::CONFIG_ORIG_FILE_WPCONFIG_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AddHandler line from existing WP .htaccess file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getOldHtaccessAddhandlerLine(): string
|
||||
{
|
||||
$origFiles = InstallerOrigFileMng::getInstance();
|
||||
$backupHtaccessPath = $origFiles->getEntryStoredPath(self::CONFIG_ORIG_FILE_HTACCESS_ID_OVERWRITE_SITE);
|
||||
Log::info("Installer Host Htaccess path: " . $backupHtaccessPath, Log::LV_DEBUG);
|
||||
|
||||
if ($backupHtaccessPath !== false && file_exists($backupHtaccessPath)) {
|
||||
$htaccessContent = file_get_contents($backupHtaccessPath);
|
||||
if (!empty($htaccessContent)) {
|
||||
// match and trim non commented line "AddHandler application/x-httpd-XXXX .php" case insenstive
|
||||
$re = '/^[\s\t]*[^#]?[\s\t]*(AddHandler[\s\t]+.+\.php[ \t]?.*?)[\s\t]*$/mi';
|
||||
$matches = [];
|
||||
if (preg_match($re, $htaccessContent, $matches)) {
|
||||
return "\n" . $matches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the web config file based on the inputs from the installer forms.
|
||||
*
|
||||
* @param mysqli $dbh The database connection handle for this request
|
||||
* @param string $path The path to the config file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setup(\mysqli $dbh, $path): void
|
||||
{
|
||||
Log::info("\nWEB SERVER CONFIGURATION FILE UPDATED:");
|
||||
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$htAccessPath = "{$path}/.htaccess";
|
||||
$mu_generation = DUPX_ArchiveConfig::getInstance()->mu_generation;
|
||||
|
||||
// SKIP HTACCESS
|
||||
$skipHtaccessConfigVals = [
|
||||
'nothing',
|
||||
'original',
|
||||
];
|
||||
if (in_array($paramsManager->getValue(PrmMng::PARAM_HTACCESS_CONFIG), $skipHtaccessConfigVals)) {
|
||||
if (!InstState::isRestoreBackup() && !InstState::isAddSiteOnMultisite()) {
|
||||
// on restore packup mode no warning needed
|
||||
$longMsg = 'Retaining the original .htaccess files may cause '
|
||||
. 'issues with the initial setup of your site. '
|
||||
. 'If you encounter any problems, check the contents of the configuration files manually or '
|
||||
. 'reinstall the site again by changing the configuration file settings.';
|
||||
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t update new .htaccess file',
|
||||
'level' => DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => $longMsg,
|
||||
'sections' => 'general',
|
||||
]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$timestamp = date("Y-m-d H:i:s");
|
||||
$post_url_new = $paramsManager->getValue(PrmMng::PARAM_URL_NEW);
|
||||
$newdata = parse_url($post_url_new);
|
||||
$newpath = DUPX_U::addSlash($newdata['path'] ?? "");
|
||||
$update_msg = "# This file was updated by Duplicator Pro on {$timestamp}.\n";
|
||||
$update_msg .= "# See the original_files_ folder for the original source_site_htaccess file.";
|
||||
$update_msg .= self::getOldHtaccessAddhandlerLine();
|
||||
|
||||
switch (InstState::getInstType()) {
|
||||
case InstState::TYPE_SINGLE:
|
||||
case InstState::TYPE_STANDALONE:
|
||||
case InstState::TYPE_RBACKUP_SINGLE:
|
||||
case InstState::TYPE_RECOVERY_SINGLE:
|
||||
$tmp_htaccess = self::htAcccessNoMultisite($update_msg, $newpath, $dbh);
|
||||
Log::info("- Preparing .htaccess file with basic setup.");
|
||||
break;
|
||||
case InstState::TYPE_MSUBDOMAIN:
|
||||
case InstState::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
case InstState::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
if ($mu_generation == 1) {
|
||||
$tmp_htaccess = self::htAccessSubdomainPre53($update_msg, $newpath);
|
||||
} else {
|
||||
$tmp_htaccess = self::htAccessSubdomain($update_msg, $newpath);
|
||||
}
|
||||
Log::info("- Preparing .htaccess file with multisite subdomain setup.");
|
||||
break;
|
||||
case InstState::TYPE_MSUBFOLDER:
|
||||
case InstState::TYPE_RBACKUP_MSUBFOLDER:
|
||||
case InstState::TYPE_RECOVERY_MSUBFOLDER:
|
||||
if ($mu_generation == 1) {
|
||||
$tmp_htaccess = self::htAccessSubdirectoryPre35($update_msg, $newpath);
|
||||
} else {
|
||||
$tmp_htaccess = self::htAccessSubdirectory($update_msg, $newpath);
|
||||
}
|
||||
Log::info("- Preparing .htaccess file with multisite subdirectory setup.");
|
||||
break;
|
||||
case InstState::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
case InstState::TYPE_NOT_SET:
|
||||
throw new Exception('Cannot change setup with current installation type [' . InstState::getInstType() . ']');
|
||||
default:
|
||||
throw new Exception('Unknown mode');
|
||||
}
|
||||
|
||||
if (file_exists($htAccessPath) && SnapIO::chmod($htAccessPath, 'u+rw') === false) {
|
||||
Log::info("WARNING: Unable to update htaccess file permessition.");
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t update new htaccess file',
|
||||
'level' => DUPX_NOTICE_ITEM::CRITICAL,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Unable to update the .htaccess file! Please check the permission on the root directory and make sure the .htaccess exists.',
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} elseif (file_put_contents($htAccessPath, $tmp_htaccess) === false) {
|
||||
Log::info("WARNING: Unable to update the .htaccess file! Please check the permission on the root directory and make sure the .htaccess exists.");
|
||||
DUPX_NOTICE_MANAGER::getInstance()->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t update new htaccess file',
|
||||
'level' => DUPX_NOTICE_ITEM::CRITICAL,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'longMsg' => 'Unable to update the .htaccess file! Please check the permission on the root directory and make sure the .htaccess exists.',
|
||||
'sections' => 'general',
|
||||
]);
|
||||
} else {
|
||||
DUPX_Extraction::setPermsFromParams($htAccessPath);
|
||||
Log::info("HTACCESS FILE - Successfully updated the .htaccess file setting.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get htaccess content no multisite
|
||||
*
|
||||
* @param string $update_msg update message
|
||||
* @param string $newpath target path
|
||||
* @param mysqli $dbh dtabase connection
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function htAcccessNoMultisite(string $update_msg, string $newpath, \mysqli $dbh): string
|
||||
{
|
||||
$result = '';
|
||||
// no multisite
|
||||
$empty_htaccess = false;
|
||||
$optonsTable = mysqli_real_escape_string($dbh, DUPX_DB_Functions::getOptionsTableName());
|
||||
$query_result = DUPX_DB::mysqli_query($dbh, "SELECT option_value FROM `" . $optonsTable . "` WHERE option_name = 'permalink_structure' ");
|
||||
|
||||
if ($query_result) {
|
||||
$row = @mysqli_fetch_array($query_result);
|
||||
if ($row != null) {
|
||||
$permalink_structure = trim($row[0]);
|
||||
$empty_htaccess = empty($permalink_structure);
|
||||
}
|
||||
}
|
||||
|
||||
if ($empty_htaccess) {
|
||||
Log::info('NO PERMALINK STRUCTURE FOUND: set htaccess without directives');
|
||||
$result = <<<EMPTYHTACCESS
|
||||
{$update_msg}
|
||||
# BEGIN WordPress
|
||||
# The directives (lines) between `BEGIN WordPress` and `END WordPress` are
|
||||
# dynamically generated, and should only be modified via WordPress filters.
|
||||
# Any changes to the directives between these markers will be overwritten.
|
||||
|
||||
# END WordPress
|
||||
EMPTYHTACCESS;
|
||||
} else {
|
||||
$result = <<<HTACCESS
|
||||
{$update_msg}
|
||||
# BEGIN WordPress
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase {$newpath}
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . {$newpath}index.php [L]
|
||||
</IfModule>
|
||||
# END WordPress
|
||||
HTACCESS;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get htaccess content subdomain multisite pre WP 5.3
|
||||
*
|
||||
* @param string $update_msg update message
|
||||
* @param string $newpath target path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function htAccessSubdomainPre53(string $update_msg, $newpath): string
|
||||
{
|
||||
// Pre WordPress 3.5
|
||||
$result = <<<HTACCESS
|
||||
{$update_msg}
|
||||
# BEGIN WordPress (Pre 3.5 Multisite Subdomain)
|
||||
RewriteEngine On
|
||||
RewriteBase {$newpath}
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
# uploaded files
|
||||
RewriteRule ^files/(.+) wp-includes/ms-files.php?file=$1 [L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule . index.php [L]
|
||||
# END WordPress
|
||||
HTACCESS;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get htaccess content subdomain multisite
|
||||
*
|
||||
* @param string $update_msg update message
|
||||
* @param string $newpath target path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function htAccessSubdomain(string $update_msg, $newpath): string
|
||||
{
|
||||
// 3.5+
|
||||
$result = <<<HTACCESS
|
||||
{$update_msg}
|
||||
# BEGIN WordPress (3.5+ Multisite Subdomain)
|
||||
RewriteEngine On
|
||||
RewriteBase {$newpath}
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^wp-admin$ wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^(wp-(content|admin|includes).*) $1 [L]
|
||||
RewriteRule ^(.*\.php)$ $1 [L]
|
||||
RewriteRule . index.php [L]
|
||||
# END WordPress
|
||||
HTACCESS;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get htaccess content subfolder multisite pre WP 5.3
|
||||
*
|
||||
* @param string $update_msg update message
|
||||
* @param string $newpath target path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function htAccessSubdirectoryPre35(string $update_msg, $newpath): string
|
||||
{
|
||||
// Pre 3.5
|
||||
$result = <<<HTACCESS
|
||||
{$update_msg}
|
||||
# BEGIN WordPress (Pre 3.5 Multisite Subdirectory)
|
||||
RewriteEngine On
|
||||
RewriteBase {$newpath}
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
# uploaded files
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
|
||||
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^[_0-9a-zA-Z-]+/(wp-(content|admin|includes).*) $1 [L]
|
||||
RewriteRule ^[_0-9a-zA-Z-]+/(.*\.php)$ $1 [L]
|
||||
RewriteRule . index.php [L]
|
||||
# END WordPress
|
||||
HTACCESS;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get htaccess content subfolder multisite
|
||||
*
|
||||
* @param string $update_msg update message
|
||||
* @param string $newpath target path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function htAccessSubdirectory(string $update_msg, $newpath): string
|
||||
{
|
||||
return <<<HTACCESS
|
||||
{$update_msg}
|
||||
# BEGIN WordPress (3.5+ Multisite Subdirectory)
|
||||
RewriteEngine On
|
||||
RewriteBase {$newpath}
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
# add a trailing slash to /wp-admin
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
RewriteRule . index.php [L]
|
||||
# END WordPress
|
||||
HTACCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Core\Deploy;
|
||||
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamDescUsers;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use DUPX_UpdateEngine;
|
||||
use DUPX_WPConfig;
|
||||
use mysqli;
|
||||
|
||||
/**
|
||||
* Standalone migration functions
|
||||
*/
|
||||
class Standalone
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param int $subsiteId subsite ID
|
||||
* @param mysqli $dbh database connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateOptionsTable($subsiteId, $dbh): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$base_prefix = $paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);
|
||||
$retained_subsite_prefix = $archiveConfig->getSubsitePrefixByParam($subsiteId);
|
||||
$optionsTable = DUPX_DB_Functions::getOptionsTableName();
|
||||
|
||||
if ($retained_subsite_prefix != $base_prefix) {
|
||||
DUPX_UpdateEngine::updateTablePrefix($dbh, $optionsTable, 'option_name', $retained_subsite_prefix, $base_prefix);
|
||||
}
|
||||
|
||||
if ($archiveConfig->mu_generation < 2) {
|
||||
$escapedOptionsTable = mysqli_real_escape_string($dbh, $optionsTable);
|
||||
$uploadsPath = $paramsManager->getValue(PrmMng::PARAM_PATH_UPLOADS_NEW); //upload_url_path','uploadPath
|
||||
$sql = "UPDATE `$escapedOptionsTable` SET `option_value` = '$uploadsPath' " .
|
||||
"WHERE `option_name` = 'uploadPath' AND `option_value` != ''";
|
||||
DUPX_DB::queryNoReturn($dbh, $sql);
|
||||
|
||||
$uploadsUrl = $paramsManager->getValue(PrmMng::PARAM_URL_UPLOADS_NEW);
|
||||
$sql = "UPDATE `$escapedOptionsTable` SET `option_value` = '$uploadsUrl' WHERE `option_name` = 'upload_url_path' AND `option_value` != ''";
|
||||
DUPX_DB::queryNoReturn($dbh, $sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge non_site where meta_key in wp_usermeta starts with data from other subsite or root site.
|
||||
*
|
||||
* @param int $subsiteId subsite ID
|
||||
* @param mysqli $dbh database connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function purgeRedundantData($subsiteId, $dbh): void
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
if (ParamDescUsers::getUsersMode() != ParamDescUsers::USER_MODE_OVERWRITE) {
|
||||
Log::info("STANDALONE: skip purging redundant data beacause user mode is " . ParamDescUsers::getUsersMode());
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info("STANDALONE: purging redundant data. Considering ");
|
||||
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$base_prefix = $paramsManager->getValue(PrmMng::PARAM_DB_TABLE_PREFIX);
|
||||
$usermeta_table_name = DUPX_DB_Functions::getUserMetaTableName();
|
||||
$retained_subsite_prefix = $archiveConfig->getSubsitePrefixByParam($subsiteId);
|
||||
$superAdminUsersIds = Helpers::getSuperAdminsUserIds($dbh);
|
||||
Log::info("SUPER USER IDS: " . Log::v2str($superAdminUsersIds), Log::LV_DETAILED);
|
||||
|
||||
// Remove unused metauser key prefix
|
||||
$escPergPrefix = mysqli_real_escape_string($dbh, SnapDB::quoteRegex($base_prefix));
|
||||
$escPergSubsitePrefix = mysqli_real_escape_string($dbh, SnapDB::quoteRegex($retained_subsite_prefix));
|
||||
if ($retained_subsite_prefix == $base_prefix) {
|
||||
Log::info('CLEAN META KEYS ON USER META ' . $base_prefix . '[0-9]+_');
|
||||
$where = "meta_key REGEXP '^" . $escPergPrefix . "[0-9]+_'";
|
||||
} else {
|
||||
Log::info('CLEAN META KEYS ON USER META ' . $base_prefix . ' EXCEPT ' . $retained_subsite_prefix);
|
||||
$where = "meta_key NOT REGEXP '^" . $escPergSubsitePrefix . "' AND meta_key REGEXP '^" . $escPergPrefix . "'";
|
||||
}
|
||||
DUPX_DB::chunksDelete($dbh, $usermeta_table_name, $where);
|
||||
|
||||
if ($retained_subsite_prefix != $base_prefix) {
|
||||
DUPX_UpdateEngine::updateTablePrefix($dbh, $usermeta_table_name, 'meta_key', $retained_subsite_prefix, $base_prefix);
|
||||
}
|
||||
|
||||
if (!empty($superAdminUsersIds)) {
|
||||
$updateables = [
|
||||
$base_prefix . 'capabilities' => mysqli_real_escape_string($dbh, DUPX_WPConfig::ADMIN_SERIALIZED_SECURITY_STRING),
|
||||
$base_prefix . 'user_level' => DUPX_WPConfig::ADMIN_LEVEL,
|
||||
];
|
||||
|
||||
// Ad permission for superadmin users
|
||||
foreach ($superAdminUsersIds as $suId) {
|
||||
foreach ($updateables as $meta_key => $meta_value) {
|
||||
$query = "SELECT `umeta_id` FROM {$usermeta_table_name} WHERE `user_id` = {$suId} AND meta_key = '{$meta_key}'";
|
||||
if (($result = DUPX_DB::mysqli_query($dbh, $query)) !== false) {
|
||||
//If entry is present UPDATE otherwise INSERT
|
||||
if ($result->num_rows > 0) {
|
||||
$umeta_id = $result->fetch_object()->umeta_id;
|
||||
$query = "UPDATE {$usermeta_table_name} SET `meta_value` = '{$meta_value}' WHERE `umeta_id` = {$umeta_id}";
|
||||
if (DUPX_DB::mysqli_query($dbh, $query) === false) {
|
||||
Log::info("Could not update meta field {$meta_key} for user with id {$suId}");
|
||||
}
|
||||
} else {
|
||||
$query = "INSERT INTO `{$usermeta_table_name}` (user_id, meta_key, meta_value) VALUES ({$suId}, '{$meta_key}', '$meta_value')";
|
||||
if (DUPX_DB::mysqli_query($dbh, $query) === false) {
|
||||
Log::info("Could not populate meta field {$meta_key} with the value {$meta_value} for user with id {$suId}");
|
||||
}
|
||||
}
|
||||
$result->free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* From WordPress WP_Hook class
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Hooks;
|
||||
|
||||
/**
|
||||
* Core class used to implement action and filter hook functionality.
|
||||
*
|
||||
* @implements \Iterator<mixed,mixed>
|
||||
* @implements \ArrayAccess<mixed,mixed>
|
||||
*/
|
||||
final class Hook implements \Iterator, \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* Hook callbacks.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
public $callbacks = [];
|
||||
|
||||
/**
|
||||
* The priority keys of actively running iterations of a hook.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $iterations = [];
|
||||
|
||||
/**
|
||||
* The current priority of actively running iterations of a hook.
|
||||
*
|
||||
* @var int[]
|
||||
*/
|
||||
private $currentPriority = [];
|
||||
|
||||
/**
|
||||
* Number of levels this hook can be recursively called.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $nestingLevel = 0;
|
||||
|
||||
/**
|
||||
* Flag for if we're current doing an action, rather than a filter.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $doingAction = false;
|
||||
|
||||
/**
|
||||
* Hooks a function or method to a specific filter action.
|
||||
*
|
||||
* @param string $tag The name of the filter to hook the $function_to_add callback to.
|
||||
* @param callable $function_to_add The callback to be run when the filter is applied.
|
||||
* @param int $priority The order in which the functions associated with a particular action
|
||||
* are executed. Lower numbers correspond with earlier execution,
|
||||
* and functions with the same priority are executed in the order
|
||||
* in which they were added to the action.
|
||||
* @param int $accepted_args The number of arguments the function accepts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addFilter($tag, $function_to_add, $priority, $accepted_args): void
|
||||
{
|
||||
$idx = self::wpFilterBuildUniqueId($tag, $function_to_add, $priority);
|
||||
|
||||
$priority_existed = isset($this->callbacks[$priority]);
|
||||
|
||||
$this->callbacks[$priority][$idx] = [
|
||||
'function' => $function_to_add,
|
||||
'accepted_args' => $accepted_args,
|
||||
];
|
||||
|
||||
// If we're adding a new priority to the list, put them back in sorted order.
|
||||
if (!$priority_existed && count($this->callbacks) > 1) {
|
||||
ksort($this->callbacks, SORT_NUMERIC);
|
||||
}
|
||||
|
||||
if ($this->nestingLevel > 0) {
|
||||
$this->resortActiveIterations($priority, $priority_existed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles resetting callback priority keys mid-iteration.
|
||||
*
|
||||
* @param false|int $new_priority Optional. The priority of the new filter being added. Default false,
|
||||
* for no priority being added.
|
||||
* @param bool $priority_existed Optional. Flag for whether the priority already existed before the new
|
||||
* filter was added. Default false.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function resortActiveIterations($new_priority = false, bool $priority_existed = false): void
|
||||
{
|
||||
$new_priorities = array_keys($this->callbacks);
|
||||
|
||||
// If there are no remaining hooks, clear out all running iterations.
|
||||
if (!$new_priorities) {
|
||||
foreach ($this->iterations as $index => $iteration) {
|
||||
$this->iterations[$index] = $new_priorities;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$min = min($new_priorities);
|
||||
foreach ($this->iterations as $index => &$iteration) {
|
||||
$current = current($iteration);
|
||||
// If we're already at the end of this iteration, just leave the array pointer where it is.
|
||||
if (false === $current) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$iteration = $new_priorities;
|
||||
|
||||
if ($current < $min) {
|
||||
array_unshift($iteration, $current);
|
||||
continue;
|
||||
}
|
||||
|
||||
while (current($iteration) < $current) {
|
||||
if (false === next($iteration)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a new priority that didn't exist, but ::applyFilters() or ::doAction() thinks it's the current priority...
|
||||
if ($new_priority === $this->currentPriority[$index] && !$priority_existed) {
|
||||
/*
|
||||
* ...and the new priority is the same as what $this->iterations thinks is the previous
|
||||
* priority, we need to move back to it.
|
||||
*/
|
||||
|
||||
if (false === current($iteration)) {
|
||||
// If we've already moved off the end of the array, go back to the last element.
|
||||
$prev = end($iteration);
|
||||
} else {
|
||||
// Otherwise, just go back to the previous element.
|
||||
$prev = prev($iteration);
|
||||
}
|
||||
if (false === $prev) {
|
||||
// Start of the array. Reset, and go about our day.
|
||||
reset($iteration);
|
||||
} elseif ($new_priority !== $prev) {
|
||||
// Previous wasn't the same. Move forward again.
|
||||
next($iteration);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($iteration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhooks a function or method from a specific filter action.
|
||||
*
|
||||
* @param string $tag The filter hook to which the function to be removed is hooked.
|
||||
* @param callable $function_to_remove The callback to be removed from running when the filter is applied.
|
||||
* @param int $priority The exact priority used when adding the original filter callback.
|
||||
*
|
||||
* @return bool Whether the callback existed before it was removed.
|
||||
*/
|
||||
public function removeFilter($tag, $function_to_remove, $priority)
|
||||
{
|
||||
$function_key = self::wpFilterBuildUniqueId($tag, $function_to_remove, $priority);
|
||||
|
||||
$exists = isset($this->callbacks[$priority][$function_key]);
|
||||
if ($exists) {
|
||||
unset($this->callbacks[$priority][$function_key]);
|
||||
if (!$this->callbacks[$priority]) {
|
||||
unset($this->callbacks[$priority]);
|
||||
if ($this->nestingLevel > 0) {
|
||||
$this->resortActiveIterations();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific action has been registered for this hook.
|
||||
*
|
||||
* When using the `$function_to_check` argument, this function may return a non-boolean value
|
||||
* that evaluates to false (e.g. 0), so use the `===` operator for testing the return value.
|
||||
*
|
||||
* @param string $tag Optional. The name of the filter hook. Default empty.
|
||||
* @param callable|false $function_to_check Optional. The callback to check for. Default false.
|
||||
*
|
||||
* @return bool|int If `$function_to_check` is omitted, returns boolean for whether the hook has
|
||||
* anything registered. When checking a specific function, the priority of that
|
||||
* hook is returned, or false if the function is not attached.
|
||||
*/
|
||||
public function hasFilter($tag = '', $function_to_check = false)
|
||||
{
|
||||
if (false === $function_to_check) {
|
||||
return $this->hasFilters();
|
||||
}
|
||||
|
||||
$function_key = self::wpFilterBuildUniqueId($tag, $function_to_check, 0);
|
||||
if (!$function_key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->callbacks as $priority => $callbacks) {
|
||||
if (isset($callbacks[$function_key])) {
|
||||
return $priority;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any callbacks have been registered for this hook.
|
||||
*
|
||||
* @return bool True if callbacks have been registered for the current hook, otherwise false.
|
||||
*/
|
||||
public function hasFilters(): bool
|
||||
{
|
||||
foreach ($this->callbacks as $callbacks) {
|
||||
if ($callbacks) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all callbacks from the current filter.
|
||||
*
|
||||
* @param int|false $priority Optional. The priority number to remove. Default false.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeAllFilters($priority = false): void
|
||||
{
|
||||
if (!$this->callbacks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (false === $priority) {
|
||||
$this->callbacks = [];
|
||||
} elseif (isset($this->callbacks[$priority])) {
|
||||
unset($this->callbacks[$priority]);
|
||||
}
|
||||
|
||||
if ($this->nestingLevel > 0) {
|
||||
$this->resortActiveIterations();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the callback functions that have been added to a filter hook.
|
||||
*
|
||||
* @param mixed $value The value to filter.
|
||||
* @param mixed[] $args Additional parameters to pass to the callback functions.
|
||||
* This array is expected to include $value at index 0.
|
||||
*
|
||||
* @return mixed The filtered value after all hooked functions are applied to it.
|
||||
*/
|
||||
public function applyFilters($value, $args)
|
||||
{
|
||||
if (!$this->callbacks) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$nestingLevel = $this->nestingLevel++;
|
||||
|
||||
$this->iterations[$nestingLevel] = array_keys($this->callbacks);
|
||||
$num_args = count($args);
|
||||
|
||||
do {
|
||||
$this->currentPriority[$nestingLevel] = current($this->iterations[$nestingLevel]);
|
||||
$priority = $this->currentPriority[$nestingLevel];
|
||||
|
||||
foreach ($this->callbacks[$priority] as $the_) {
|
||||
if (!$this->doingAction) {
|
||||
$args[0] = $value;
|
||||
}
|
||||
|
||||
// Avoid the array_slice() if possible.
|
||||
if (0 == $the_['accepted_args']) {
|
||||
$value = call_user_func($the_['function']);
|
||||
} elseif ($the_['accepted_args'] >= $num_args) {
|
||||
$value = call_user_func_array($the_['function'], $args);
|
||||
} else {
|
||||
$value = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
|
||||
}
|
||||
}
|
||||
} while (false !== next($this->iterations[$nestingLevel]));
|
||||
|
||||
unset($this->iterations[$nestingLevel]);
|
||||
unset($this->currentPriority[$nestingLevel]);
|
||||
|
||||
$this->nestingLevel--;
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the callback functions that have been added to an action hook.
|
||||
*
|
||||
* @param mixed[] $args Parameters to pass to the callback functions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function doAction($args): void
|
||||
{
|
||||
$this->doingAction = true;
|
||||
$this->applyFilters('', $args);
|
||||
|
||||
// If there are recursive calls to the current action, we haven't finished it until we get to the last one.
|
||||
if (!$this->nestingLevel) {
|
||||
$this->doingAction = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the functions hooked into the 'all' hook.
|
||||
*
|
||||
* @param mixed[] $args Arguments to pass to the hook callbacks. Passed by reference.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function doAllHook(&$args): void
|
||||
{
|
||||
$nestingLevel = $this->nestingLevel++;
|
||||
$this->iterations[$nestingLevel] = array_keys($this->callbacks);
|
||||
|
||||
do {
|
||||
$priority = current($this->iterations[$nestingLevel]);
|
||||
foreach ($this->callbacks[$priority] as $the_) {
|
||||
call_user_func_array($the_['function'], $args);
|
||||
}
|
||||
} while (false !== next($this->iterations[$nestingLevel]));
|
||||
|
||||
unset($this->iterations[$nestingLevel]);
|
||||
$this->nestingLevel--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current priority level of the currently running iteration of the hook.
|
||||
*
|
||||
* @return int|false If the hook is running, return the current priority level. If it isn't running, return false.
|
||||
*/
|
||||
public function currentPriority()
|
||||
{
|
||||
if (false === current($this->iterations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return current(current($this->iterations));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes filters set up before WordPress has initialized to Hook objects.
|
||||
*
|
||||
* The `$filters` parameter should be an array keyed by hook name, with values
|
||||
* containing either:
|
||||
*
|
||||
* - A `Hook` instance
|
||||
* - An array of callbacks keyed by their priorities
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* $filters = array(
|
||||
* 'wp_fatal_error_handler_enabled' => array(
|
||||
* 10 => array(
|
||||
* array(
|
||||
* 'accepted_args' => 0,
|
||||
* 'function' => function() {
|
||||
* return false;
|
||||
* },
|
||||
* ),
|
||||
* ),
|
||||
* ),
|
||||
* );
|
||||
*
|
||||
* @param mixed[] $filters Filters to normalize. See documentation above for details.
|
||||
*
|
||||
* @return self[] Array of normalized filters.
|
||||
*/
|
||||
public static function buildPreinitializedHooks($filters)
|
||||
{
|
||||
/**
|
||||
* @var self[] $normalized
|
||||
*/
|
||||
$normalized = [];
|
||||
|
||||
foreach ($filters as $tag => $callback_groups) {
|
||||
if (is_object($callback_groups) && $callback_groups instanceof self) {
|
||||
$normalized[$tag] = $callback_groups;
|
||||
continue;
|
||||
}
|
||||
$hook = new self();
|
||||
|
||||
// Loop through callback groups.
|
||||
foreach ($callback_groups as $priority => $callbacks) {
|
||||
// Loop through callbacks.
|
||||
foreach ($callbacks as $cb) {
|
||||
$hook->addFilter($tag, $cb['function'], $priority, $cb['accepted_args']);
|
||||
}
|
||||
}
|
||||
$normalized[$tag] = $hook;
|
||||
}
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an offset value exists.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/arrayaccess.offsetexists.php
|
||||
*
|
||||
* @param mixed $offset An offset to check for.
|
||||
*
|
||||
* @return bool True if the offset exists, false otherwise.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->callbacks[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value at a specified offset.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/arrayaccess.offsetget.php
|
||||
*
|
||||
* @param mixed $offset The offset to retrieve.
|
||||
*
|
||||
* @return mixed If set, the value at the specified offset, null otherwise.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->callbacks[$offset] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value at a specified offset.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/arrayaccess.offsetset.php
|
||||
*
|
||||
* @param mixed $offset The offset to assign the value to.
|
||||
* @param mixed $value The value to set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
if (is_null($offset)) {
|
||||
$this->callbacks[] = $value;
|
||||
} else {
|
||||
$this->callbacks[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a specified offset.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/arrayaccess.offsetunset.php
|
||||
*
|
||||
* @param mixed $offset The offset to unset.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->callbacks[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current element.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/iterator.current.php
|
||||
*
|
||||
* @return mixed[] Of callbacks at current priority.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return current($this->callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves forward to the next element.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/iterator.next.php
|
||||
*
|
||||
* @return mixed[] Of callbacks at next priority.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
return next($this->callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of the current element.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/iterator.key.php
|
||||
*
|
||||
* @return mixed Returns current priority on success, or NULL on failure
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return key($this->callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current position is valid.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/iterator.valid.php
|
||||
*
|
||||
* @return bool Whether the current position is valid.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return key($this->callbacks) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds the Iterator to the first element.
|
||||
*
|
||||
* @link https://www.php.net/manual/en/iterator.rewind.php
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind(): void
|
||||
{
|
||||
reset($this->callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Unique ID for storage and retrieval.
|
||||
*
|
||||
* The old way to serialize the callback caused issues and this function is the
|
||||
* solution. It works by checking for objects and creating a new property in
|
||||
* the class to keep track of the object and new objects of the same class that
|
||||
* need to be added.
|
||||
*
|
||||
* It also allows for the removal of actions and filters for objects after they
|
||||
* change class properties. It is possible to include the property $wp_filter_id
|
||||
* in your class and set it to "null" or a number to bypass the workaround.
|
||||
* However this will prevent you from adding new classes and any new classes
|
||||
* will overwrite the previous hook by the same class.
|
||||
*
|
||||
* Functions and static method callbacks are just returned as strings and
|
||||
* shouldn't have any speed penalty.
|
||||
*
|
||||
* @link https://core.trac.wordpress.org/ticket/3875
|
||||
*
|
||||
* @since 2.2.3
|
||||
* @since 5.3.0 Removed workarounds for spl_object_hash().
|
||||
* `$tag` and `$priority` are no longer used,
|
||||
* and the function always returns a string.
|
||||
* @access private
|
||||
*
|
||||
* @param string $tag Unused. The name of the filter to build ID for.
|
||||
* @param callable $function The function to generate ID for.
|
||||
* @param int $priority Unused. The order in which the functions
|
||||
* associated with a particular action are executed.
|
||||
*
|
||||
* @return string Unique function ID for usage as array key.
|
||||
*/
|
||||
protected static function wpFilterBuildUniqueId($tag, $function, $priority): string
|
||||
{
|
||||
if (is_string($function)) {
|
||||
return $function;
|
||||
}
|
||||
|
||||
if (is_object($function)) {
|
||||
// Closures are currently implemented as objects.
|
||||
$function = [
|
||||
$function,
|
||||
'',
|
||||
];
|
||||
} else {
|
||||
$function = (array) $function;
|
||||
}
|
||||
|
||||
if (is_object($function[0])) {
|
||||
// Object class calling.
|
||||
return spl_object_hash($function[0]) . $function[1];
|
||||
} elseif (is_string($function[0])) {
|
||||
// Static calling.
|
||||
return $function[0] . '::' . $function[1];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Installer Hooks Manager
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Hooks;
|
||||
|
||||
final class HooksMng
|
||||
{
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
/** @var Hook[] */
|
||||
private $filters = [];
|
||||
/** @var string[] */
|
||||
private $currentFilter = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init params and load
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook a function or method to a specific filter action.
|
||||
*
|
||||
* WordPress offers filter hooks to allow plugins to modify
|
||||
* various types of internal data at runtime.
|
||||
*
|
||||
* A plugin can modify data by binding a callback to a filter hook. When the filter
|
||||
* is later applied, each bound callback is run in order of priority, and given
|
||||
* the opportunity to modify a value by returning a new value.
|
||||
*
|
||||
* The following example shows how a callback function is bound to a filter hook.
|
||||
*
|
||||
* Note that `$example` is passed to the callback, (maybe) modified, then returned:
|
||||
*
|
||||
* function example_callback( $example ) {
|
||||
* // Maybe modify $example in some way.
|
||||
* return $example;
|
||||
* }
|
||||
* add_filter( 'example_filter', 'example_callback' );
|
||||
*
|
||||
* Bound callbacks can accept from none to the total number of arguments passed as parameters
|
||||
* in the corresponding applyFilters() call.
|
||||
*
|
||||
* In other words, if an applyFilters() call passes four total arguments, callbacks bound to
|
||||
* it can accept none (the same as 1) of the arguments or up to four. The important part is that
|
||||
* the `$accepted_args` value must reflect the number of arguments the bound callback *actually*
|
||||
* opted to accept. If no arguments were accepted by the callback that is considered to be the
|
||||
* same as accepting 1 argument. For example:
|
||||
*
|
||||
* // Filter call.
|
||||
* $value = applyFilters( 'hook', $value, $arg2, $arg3 );
|
||||
*
|
||||
* // Accepting zero/one arguments.
|
||||
* function example_callback() {
|
||||
* ...
|
||||
* return 'some value';
|
||||
* }
|
||||
* add_filter( 'hook', 'example_callback' ); // Where $priority is default 10, $accepted_args is default 1.
|
||||
*
|
||||
* // Accepting two arguments (three possible).
|
||||
* function example_callback( $value, $arg2 ) {
|
||||
* ...
|
||||
* return $maybe_modified_value;
|
||||
* }
|
||||
* add_filter( 'hook', 'example_callback', 10, 2 ); // Where $priority is 10, $accepted_args is 2.
|
||||
*
|
||||
* *Note:* The function will return true whether or not the callback is valid.
|
||||
* It is up to you to take care. This is done for optimization purposes, so
|
||||
* everything is as quick as possible.
|
||||
*
|
||||
* @param string $tag The name of the filter to hook the $function_to_add callback to.
|
||||
* @param callable $function_to_add The callback to be run when the filter is applied.
|
||||
* @param int $priority Optional. Used to specify the order in which the functions
|
||||
* associated with a particular action are executed.
|
||||
* Lower numbers correspond with earlier execution,
|
||||
* and functions with the same priority are executed
|
||||
* in the order in which they were added to the action. Default 10.
|
||||
* @param int $accepted_args Optional. The number of arguments the function accepts. Default 1.
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
public function addFilter($tag, $function_to_add, $priority = 10, $accepted_args = 1): bool
|
||||
{
|
||||
if (!isset($this->filters[$tag])) {
|
||||
$this->filters[$tag] = new Hook();
|
||||
}
|
||||
$this->filters[$tag]->addFilter($tag, $function_to_add, $priority, $accepted_args);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any filter has been registered for a hook.
|
||||
*
|
||||
* When using the `$function_to_check` argument, this function may return a non-boolean value
|
||||
* that evaluates to false (e.g. 0), so use the `===` operator for testing the return value.
|
||||
*
|
||||
* @param string $tag The name of the filter hook.
|
||||
* @param callable|false $function_to_check Optional. The callback to check for. Default false.
|
||||
*
|
||||
* @return bool|int If `$function_to_check` is omitted, returns boolean for whether the hook has
|
||||
* anything registered. When checking a specific function, the priority of that
|
||||
* hook is returned, or false if the function is not attached.
|
||||
*/
|
||||
public function hasFilter($tag, $function_to_check = false)
|
||||
{
|
||||
if (!isset($this->filters[$tag])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->filters[$tag]->hasFilter($tag, $function_to_check);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the callback functions that have been added to a filter hook.
|
||||
*
|
||||
* The callback functions attached to the filter hook are invoked by calling
|
||||
* this function. This function can be used to create a new filter hook by
|
||||
* simply calling this function with the name of the new hook specified using
|
||||
* the `$tag` parameter.
|
||||
*
|
||||
* The function also allows for multiple additional arguments to be passed to hooks.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* // The filter callback function.
|
||||
* function example_callback( $string, $arg1, $arg2 ) {
|
||||
* // (maybe) modify $string.
|
||||
* return $string;
|
||||
* }
|
||||
* add_filter( 'example_filter', 'example_callback', 10, 3 );
|
||||
*
|
||||
* /*
|
||||
* * Apply the filters by calling the 'example_callback()' function
|
||||
* * that's hooked onto `example_filter` above.
|
||||
* *
|
||||
* * - 'example_filter' is the filter hook.
|
||||
* * - 'filter me' is the value being filtered.
|
||||
* * - $arg1 and $arg2 are the additional arguments passed to the callback.
|
||||
* $value = applyFilters( 'example_filter', 'filter me', $arg1, $arg2 );
|
||||
*
|
||||
* @param string $tag The name of the filter hook.
|
||||
* @param mixed $value The value to filter.
|
||||
* ... $args Additional parameters to pass to the callback functions.
|
||||
*
|
||||
* @return mixed The filtered value after all hooked functions are applied to it.
|
||||
*/
|
||||
public function applyFilters($tag, $value)
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
// Do 'all' actions first.
|
||||
if (isset($this->filters['all'])) {
|
||||
$this->currentFilter[] = $tag;
|
||||
$this->callAllHook($args);
|
||||
}
|
||||
|
||||
if (!isset($this->filters[$tag])) {
|
||||
if (isset($this->filters['all'])) {
|
||||
array_pop($this->currentFilter);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!isset($this->filters['all'])) {
|
||||
$this->currentFilter[] = $tag;
|
||||
}
|
||||
|
||||
// Don't pass the tag name to Hook.
|
||||
array_shift($args);
|
||||
|
||||
$filtered = $this->filters[$tag]->applyFilters($value, $args);
|
||||
|
||||
array_pop($this->currentFilter);
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a function from a specified filter hook.
|
||||
*
|
||||
* This function removes a function attached to a specified filter hook. This
|
||||
* method can be used to remove default functions attached to a specific filter
|
||||
* hook and possibly replace them with a substitute.
|
||||
*
|
||||
* To remove a hook, the $function_to_remove and $priority arguments must match
|
||||
* when the hook was added. This goes for both filters and actions. No warning
|
||||
* will be given on removal failure.
|
||||
*
|
||||
* @param string $tag The filter hook to which the function to be removed is hooked.
|
||||
* @param callable $function_to_remove The name of the function which should be removed.
|
||||
* @param int $priority Optional. The priority of the function. Default 10.
|
||||
*
|
||||
* @return bool Whether the function existed before it was removed.
|
||||
*/
|
||||
public function removeFilter($tag, $function_to_remove, $priority = 10)
|
||||
{
|
||||
$r = false;
|
||||
if (isset($this->filters[$tag])) {
|
||||
$r = $this->filters[$tag]->removeFilter($tag, $function_to_remove, $priority);
|
||||
if (!$this->filters[$tag]->callbacks) {
|
||||
unset($this->filters[$tag]);
|
||||
}
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all of the hooks from a filter.
|
||||
*
|
||||
* @param string $tag The filter to remove hooks from.
|
||||
* @param int|false $priority Optional. The priority number to remove. Default false.
|
||||
*
|
||||
* @return true True when finished.
|
||||
*/
|
||||
public function removeAllFilters($tag, $priority = false): bool
|
||||
{
|
||||
if (isset($this->filters[$tag])) {
|
||||
$this->filters[$tag]->removeAllFilters($priority);
|
||||
if (!$this->filters[$tag]->hasFilters()) {
|
||||
unset($this->filters[$tag]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks a function on to a specific action.
|
||||
*
|
||||
* Actions are the hooks that the WordPress core launches at specific points
|
||||
* during execution, or when specific events occur. Plugins can specify that
|
||||
* one or more of its PHP functions are executed at these points, using the
|
||||
* Action API.
|
||||
*
|
||||
* @param string $tag The name of the action to which the $function_to_add is hooked.
|
||||
* @param callable $function_to_add The name of the function you wish to be called.
|
||||
* @param int $priority Optional. Used to specify the order in which the functions
|
||||
* associated with a particular action are executed. Default 10.
|
||||
* Lower numbers correspond with earlier execution,
|
||||
* and functions with the same priority are executed
|
||||
* in the order in which they were added to the action.
|
||||
* @param int $accepted_args Optional. The number of arguments the function accepts. Default 1.
|
||||
*
|
||||
* @return true Will always return true.
|
||||
*/
|
||||
public function addAction($tag, $function_to_add, $priority = 10, $accepted_args = 1): bool
|
||||
{
|
||||
return $this->addFilter($tag, $function_to_add, $priority, $accepted_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute functions hooked on a specific action hook.
|
||||
*
|
||||
* This function invokes all functions attached to action hook `$tag`. It is
|
||||
* possible to create new action hooks by simply calling this function,
|
||||
* specifying the name of the new hook using the `$tag` parameter.
|
||||
*
|
||||
* You can pass extra arguments to the hooks, much like you can with `applyFilters()`.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* // The action callback function.
|
||||
* function example_callback( $arg1, $arg2 ) {
|
||||
* // (maybe) do something with the args.
|
||||
* }
|
||||
* add_action( 'example_action', 'example_callback', 10, 2 );
|
||||
*
|
||||
* /*
|
||||
* * Trigger the actions by calling the 'example_callback()' function
|
||||
* * that's hooked onto `example_action` above.
|
||||
* *
|
||||
* * - 'example_action' is the action hook.
|
||||
* * - $arg1 and $arg2 are the additional arguments passed to the callback.
|
||||
* $value = do_action( 'example_action', $arg1, $arg2 );
|
||||
*
|
||||
* @global Hook[] $this->filters Stores all of the filters and actions.
|
||||
* @global string[] $this->currentFilter Stores the list of current filters with the current one last.
|
||||
*
|
||||
* @param string $tag The name of the action to be executed.
|
||||
* ...$arg Optional. Additional arguments which are passed on to the
|
||||
* functions hooked to the action. Default empty.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function doAction($tag): void
|
||||
{
|
||||
$arg = $all_args = func_get_args();
|
||||
array_shift($arg); // remove tag action
|
||||
|
||||
// Do 'all' actions first.
|
||||
if (isset($this->filters['all'])) {
|
||||
$this->currentFilter[] = $tag;
|
||||
$this->callAllHook($all_args);
|
||||
}
|
||||
|
||||
if (!isset($this->filters[$tag])) {
|
||||
if (isset($this->filters['all'])) {
|
||||
array_pop($this->currentFilter);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($this->filters['all'])) {
|
||||
$this->currentFilter[] = $tag;
|
||||
}
|
||||
|
||||
if (empty($arg)) {
|
||||
$arg[] = '';
|
||||
} elseif (is_array($arg[0]) && 1 === count($arg[0]) && isset($arg[0][0]) && is_object($arg[0][0])) {
|
||||
// Backward compatibility for PHP4-style passing of `array( &$this )` as action `$arg`.
|
||||
$arg[0] = $arg[0][0];
|
||||
}
|
||||
|
||||
$this->filters[$tag]->doAction($arg);
|
||||
|
||||
array_pop($this->currentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any action has been registered for a hook.
|
||||
*
|
||||
* When using the `$function_to_check` argument, this function may return a non-boolean value
|
||||
* that evaluates to false (e.g. 0), so use the `===` operator for testing the return value.
|
||||
*
|
||||
* @see hasFilter() has_action() is an alias of hasFilter().
|
||||
*
|
||||
* @param string $tag The name of the action hook.
|
||||
* @param callable|false $function_to_check Optional. The callback to check for. Default false.
|
||||
*
|
||||
* @return bool|int If `$function_to_check` is omitted, returns boolean for whether the hook has
|
||||
* anything registered. When checking a specific function, the priority of that
|
||||
* hook is returned, or false if the function is not attached.
|
||||
*/
|
||||
public function hasAction($tag, $function_to_check = false)
|
||||
{
|
||||
return $this->hasFilter($tag, $function_to_check);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a function from a specified action hook.
|
||||
*
|
||||
* This function removes a function attached to a specified action hook. This
|
||||
* method can be used to remove default functions attached to a specific filter
|
||||
* hook and possibly replace them with a substitute.
|
||||
*
|
||||
* @param string $tag The action hook to which the function to be removed is hooked.
|
||||
* @param callable $function_to_remove The name of the function which should be removed.
|
||||
* @param int $priority Optional. The priority of the function. Default 10.
|
||||
*
|
||||
* @return bool Whether the function is removed.
|
||||
*/
|
||||
public function removeAction($tag, $function_to_remove, $priority = 10)
|
||||
{
|
||||
return $this->removeFilter($tag, $function_to_remove, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all of the hooks from an action.
|
||||
*
|
||||
* @param string $tag The action to remove hooks from.
|
||||
* @param int|false $priority The priority number to remove them from. Default false.
|
||||
*
|
||||
* @return true True when finished.
|
||||
*/
|
||||
public function removeAllActions($tag, $priority = false): bool
|
||||
{
|
||||
return $this->removeAllFilters($tag, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the current filter or action.
|
||||
*
|
||||
* @global string[] $wp_current_filter Stores the list of current filters with the current one last
|
||||
*
|
||||
* @return string Hook name of the current filter or action.
|
||||
*/
|
||||
public function currentFilter()
|
||||
{
|
||||
return end($this->currentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name of the current action.
|
||||
*
|
||||
* @return string Hook name of the current action.
|
||||
*/
|
||||
public function currentAction()
|
||||
{
|
||||
return $this->currentFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the 'all' hook, which will process the functions hooked into it.
|
||||
*
|
||||
* The 'all' hook passes all of the arguments or parameters that were used for
|
||||
* the hook, which this function was called for.
|
||||
*
|
||||
* This function is used internally for apply_filters(), do_action(), and
|
||||
* do_action_ref_array() and is not meant to be used from outside those
|
||||
* functions. This function does not check for the existence of the all hook, so
|
||||
* it will fail unless the all hook exists prior to this function call.
|
||||
*
|
||||
* @global Hook[] $this->filters Stores all of the filters and actions.
|
||||
*
|
||||
* @param mixed[] $args The collected parameters from the hook that was called.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function callAllHook($args): void
|
||||
{
|
||||
$this->filters['all']->doAllHook($args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,906 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core;
|
||||
|
||||
use Duplicator\Installer\Core\Deploy\ServerConfigs;
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamDescConfigs;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Models\MigrateData;
|
||||
use Duplicator\Installer\Utils\InstallerOrigFileMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DB;
|
||||
use DUPX_DB_Functions;
|
||||
use DUPX_DBInstall;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use DUPX_Package;
|
||||
use DUPX_Template;
|
||||
use DUPX_WPConfig;
|
||||
use Error;
|
||||
use Exception;
|
||||
|
||||
class InstState
|
||||
{
|
||||
/**
|
||||
* modes
|
||||
*/
|
||||
const MODE_UNKNOWN = -1;
|
||||
const MODE_STD_INSTALL = 0;
|
||||
const MODE_OVR_INSTALL = 1;
|
||||
|
||||
const LOGIC_MODE_IMPORT = 'IMPORT';
|
||||
const LOGIC_MODE_RECOVERY = 'RECOVERY';
|
||||
const LOGIC_MODE_CLASSIC = 'CLASSIC';
|
||||
const LOGIC_MODE_OVERWRITE = 'OVERWRITE';
|
||||
const LOGIC_MODE_BRIDGE = 'BRIDGE';
|
||||
const LOGIC_MODE_RESTORE_BACKUP = 'RESTORE_BACKUP';
|
||||
|
||||
/**
|
||||
* install types
|
||||
*/
|
||||
const TYPE_NOT_SET = -2;
|
||||
const TYPE_SINGLE = -1;
|
||||
const TYPE_STANDALONE = 0;
|
||||
const TYPE_MSUBDOMAIN = 2;
|
||||
const TYPE_MSUBFOLDER = 3;
|
||||
const TYPE_SINGLE_ON_SUBDOMAIN = 4;
|
||||
const TYPE_SINGLE_ON_SUBFOLDER = 5;
|
||||
const TYPE_SUBSITE_ON_SUBDOMAIN = 6;
|
||||
const TYPE_SUBSITE_ON_SUBFOLDER = 7;
|
||||
const TYPE_RBACKUP_SINGLE = 8;
|
||||
const TYPE_RBACKUP_MSUBDOMAIN = 9;
|
||||
const TYPE_RBACKUP_MSUBFOLDER = 10;
|
||||
const TYPE_RECOVERY_SINGLE = 11;
|
||||
const TYPE_RECOVERY_MSUBDOMAIN = 12;
|
||||
const TYPE_RECOVERY_MSUBFOLDER = 13;
|
||||
|
||||
const SUBSITE_IMPORT_WP_MIN_VERSION = '4.6';
|
||||
|
||||
/** @var int */
|
||||
protected $mode = self::MODE_UNKNOWN;
|
||||
/** @var string */
|
||||
protected $ovr_wp_content_dir = '';
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* return installer mode
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMode()
|
||||
{
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_INSTALLER_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* check current installer mode
|
||||
*
|
||||
* @param bool $onlyIfUnknown check se state only if is unknow state
|
||||
* @param bool $saveParams if true update params
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkState($onlyIfUnknown = true, $saveParams = true)
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
if ($onlyIfUnknown && $paramsManager->getValue(PrmMng::PARAM_INSTALLER_MODE) !== self::MODE_UNKNOWN) {
|
||||
return true;
|
||||
}
|
||||
$isOverwrite = false;
|
||||
$overwriteData = false;
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
try {
|
||||
if (self::isImportFromBackendMode() || self::isRecoveryMode()) {
|
||||
$overwriteData = $this->getOverwriteDataFromParams();
|
||||
} else {
|
||||
$overwriteData = $this->getOverwriteDataFromWpConfig();
|
||||
}
|
||||
|
||||
if (!empty($overwriteData)) {
|
||||
if (!DUPX_DB::testConnection($overwriteData['dbhost'], $overwriteData['dbuser'], $overwriteData['dbpass'], $overwriteData['dbname'])) {
|
||||
throw new Exception('wp-config.php exists but database data connection isn\'t valid. Continuing with standard install');
|
||||
}
|
||||
|
||||
$isOverwrite = true;
|
||||
|
||||
if (self::isClassicInstall()) {
|
||||
//Add additional overwrite data for standard installs
|
||||
$overwriteData['adminUsers'] = $this->getAdminUsersOnOverwriteDatabase($overwriteData);
|
||||
$overwriteData['wpVersion'] = $this->getWordPressVersionOverwrite();
|
||||
$this->updateOverwriteDataFromDb($overwriteData);
|
||||
}
|
||||
}
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e);
|
||||
$longMsg = "Exception message: " . $e->getMessage() . "\n\n";
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'wp-config.php exists but isn\'t valid. Continue on standard install.',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => $longMsg,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
|
||||
]);
|
||||
$nManager->saveNotices();
|
||||
}
|
||||
|
||||
|
||||
if ($isOverwrite) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_INSTALLER_MODE, self::MODE_OVR_INSTALL);
|
||||
$paramsManager->setValue(PrmMng::PARAM_OVERWRITE_SITE_DATA, $overwriteData);
|
||||
} else {
|
||||
$paramsManager->setValue(PrmMng::PARAM_INSTALLER_MODE, self::MODE_STD_INSTALL);
|
||||
}
|
||||
|
||||
if ($saveParams) {
|
||||
return $this->save();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install type to string
|
||||
*
|
||||
* @param ?int $type install type Enum InstState::TYPE_*, if null get current install type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function installTypeToString($type = null): string
|
||||
{
|
||||
if (is_null($type)) {
|
||||
$type = self::getInstType();
|
||||
}
|
||||
switch ($type) {
|
||||
case self::TYPE_MSUBDOMAIN:
|
||||
return 'multisite subdomain';
|
||||
case self::TYPE_MSUBFOLDER:
|
||||
return 'multisite subfolder';
|
||||
case self::TYPE_STANDALONE:
|
||||
return 'standalone subsite';
|
||||
case self::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
return 'subsite on subdomain multisite';
|
||||
case self::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
return 'subsite on subfolder multisite';
|
||||
case self::TYPE_SINGLE:
|
||||
return 'single site';
|
||||
case self::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
return 'single site on subdomain multisite';
|
||||
case self::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
return 'single site on subfolder multisite';
|
||||
case self::TYPE_RBACKUP_SINGLE:
|
||||
return 'restore single site';
|
||||
case self::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
return 'restore subdomain multisite';
|
||||
case self::TYPE_RBACKUP_MSUBFOLDER:
|
||||
return 'restore subfolder multisite';
|
||||
case self::TYPE_RECOVERY_SINGLE:
|
||||
return 'restore single site';
|
||||
case self::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
return 'restore subdomain multisite';
|
||||
case self::TYPE_RECOVERY_MSUBFOLDER:
|
||||
return 'restore subfolder multisite';
|
||||
case self::TYPE_NOT_SET:
|
||||
return 'NOT SET';
|
||||
default:
|
||||
throw new Exception('Invalid installer mode');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite data default values
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function overwriteDataDefault(): array
|
||||
{
|
||||
return [
|
||||
'dupVersion' => '0',
|
||||
'wpVersion' => '0',
|
||||
'dbhost' => '',
|
||||
'dbname' => '',
|
||||
'dbuser' => '',
|
||||
'dbpass' => '',
|
||||
'table_prefix' => '',
|
||||
'restUrl' => '',
|
||||
'restNonce' => '',
|
||||
'restAuthUser' => '',
|
||||
'restAuthPassword' => '',
|
||||
'ustatIdentifier' => '',
|
||||
'isMultisite' => false,
|
||||
'subdomain' => false,
|
||||
'subsites' => [],
|
||||
'nextSubsiteIdAI' => -1,
|
||||
'adminUsers' => [],
|
||||
'paths' => [],
|
||||
'urls' => [],
|
||||
'dupLicense' => 0,
|
||||
'loggedUser' => [
|
||||
'ID' => -1,
|
||||
'user_login' => '',
|
||||
],
|
||||
'packagesTableExists' => false,
|
||||
'removeFilters' => [
|
||||
'dirs' => [],
|
||||
'files' => [],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overwrite data from params
|
||||
*
|
||||
* @return false|array<string, mixed>
|
||||
*/
|
||||
protected function getOverwriteDataFromParams()
|
||||
{
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
if (empty($overwriteData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($overwriteData['dbhost']) || !isset($overwriteData['dbname']) || !isset($overwriteData['dbuser']) || !isset($overwriteData['dbpass'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_merge(self::overwriteDataDefault(), $overwriteData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overwrite data from wp-config.php
|
||||
*
|
||||
* @return false|array<string, mixed>
|
||||
*/
|
||||
protected function getOverwriteDataFromWpConfig()
|
||||
{
|
||||
if (($wpConfigPath = ServerConfigs::getWpConfigLocalStoredPath()) === false) {
|
||||
$wpConfigPath = DUPX_WPConfig::getWpConfigPath();
|
||||
if (!file_exists($wpConfigPath)) {
|
||||
$wpConfigPath = DUPX_WPConfig::getWpConfigDeafultPath();
|
||||
}
|
||||
}
|
||||
|
||||
$overwriteData = false;
|
||||
|
||||
Log::info('CHECK STATE INSTALLER WP CONFIG PATH: ' . Log::v2str($wpConfigPath), Log::LV_DETAILED);
|
||||
|
||||
if (!file_exists($wpConfigPath)) {
|
||||
return $overwriteData;
|
||||
}
|
||||
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
try {
|
||||
if (DUPX_WPConfig::getLocalConfigTransformer() === false) {
|
||||
throw new Exception('wp-config.php exist but isn\'t valid. continue on standard install');
|
||||
}
|
||||
|
||||
$overwriteData = array_merge(
|
||||
self::overwriteDataDefault(),
|
||||
[
|
||||
'dbhost' => DUPX_WPConfig::getValueFromLocalWpConfig('DB_HOST'),
|
||||
'dbname' => DUPX_WPConfig::getValueFromLocalWpConfig('DB_NAME'),
|
||||
'dbuser' => DUPX_WPConfig::getValueFromLocalWpConfig('DB_USER'),
|
||||
'dbpass' => DUPX_WPConfig::getValueFromLocalWpConfig('DB_PASSWORD'),
|
||||
'table_prefix' => DUPX_WPConfig::getValueFromLocalWpConfig('table_prefix', 'variable'),
|
||||
]
|
||||
);
|
||||
|
||||
if (DUPX_WPConfig::getValueFromLocalWpConfig('MULTISITE', 'constant', false)) {
|
||||
$overwriteData['isMultisite'] = true;
|
||||
$overwriteData['subdomain'] = DUPX_WPConfig::getValueFromLocalWpConfig('SUBDOMAIN_INSTALL', 'constant', false);
|
||||
}
|
||||
} catch (Exception | Error $e) {
|
||||
$overwriteData = false;
|
||||
Log::logException($e);
|
||||
$longMsg = "Exception message: " . $e->getMessage() . "\n\n";
|
||||
$nManager->addNextStepNotice([
|
||||
'shortMsg' => 'wp-config.php exists but isn\'t valid. Continue on standard install.',
|
||||
'level' => DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => $longMsg,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
|
||||
]);
|
||||
$nManager->saveNotices();
|
||||
}
|
||||
|
||||
return $overwriteData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is bridge install
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBridgeInstall(): bool
|
||||
{
|
||||
return defined('DUPLICATOR_MU_PLUGIN_VERSION');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is recovery mode
|
||||
*
|
||||
* @param ?int $type install type Enum InstState::TYPE_*, if null get current install type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRecoveryMode($type = null): bool
|
||||
{
|
||||
return self::isInstType(
|
||||
[
|
||||
self::TYPE_RECOVERY_SINGLE,
|
||||
self::TYPE_RECOVERY_MSUBDOMAIN,
|
||||
self::TYPE_RECOVERY_MSUBFOLDER,
|
||||
],
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is restore backup
|
||||
*
|
||||
* @param ?int $type install type Enum InstState::TYPE_*, if null get current install type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isRestoreBackup($type = null): bool
|
||||
{
|
||||
return self::isInstType(
|
||||
[
|
||||
self::TYPE_RBACKUP_SINGLE,
|
||||
self::TYPE_RBACKUP_MSUBDOMAIN,
|
||||
self::TYPE_RBACKUP_MSUBFOLDER,
|
||||
self::TYPE_RECOVERY_SINGLE,
|
||||
self::TYPE_RECOVERY_MSUBDOMAIN,
|
||||
self::TYPE_RECOVERY_MSUBFOLDER,
|
||||
],
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is import from backend mode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isImportFromBackendMode(): bool
|
||||
{
|
||||
$template = PrmMng::getInstance()->getValue(PrmMng::PARAM_TEMPLATE);
|
||||
return ($template === DUPX_Template::TEMPLATE_IMPORT_BASE ||
|
||||
$template === DUPX_Template::TEMPLATE_IMPORT_ADVANCED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is classic install (non import from backend)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isClassicInstall(): bool
|
||||
{
|
||||
return (!self::isImportFromBackendMode() && !self::isRecoveryMode());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if new target site is multisite
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isNewSiteIsMultisite(): bool
|
||||
{
|
||||
return self::isInstType(
|
||||
[
|
||||
InstState::TYPE_MSUBDOMAIN,
|
||||
InstState::TYPE_MSUBFOLDER,
|
||||
InstState::TYPE_RBACKUP_MSUBDOMAIN,
|
||||
InstState::TYPE_RBACKUP_MSUBFOLDER,
|
||||
InstState::TYPE_RECOVERY_MSUBDOMAIN,
|
||||
InstState::TYPE_RECOVERY_MSUBFOLDER,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is install type is add site on multisite
|
||||
*
|
||||
* @param ?int $type install type Enum InstState::TYPE_*, if null get current install type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAddSiteOnMultisite($type = null): bool
|
||||
{
|
||||
return self::isInstType(
|
||||
[
|
||||
self::TYPE_SINGLE_ON_SUBDOMAIN,
|
||||
self::TYPE_SINGLE_ON_SUBFOLDER,
|
||||
self::TYPE_SUBSITE_ON_SUBDOMAIN,
|
||||
self::TYPE_SUBSITE_ON_SUBFOLDER,
|
||||
],
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if is multisite install
|
||||
*
|
||||
* @param ?int $type install type Enum InstState::TYPE_*, if null get current install type
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMultisiteInstall($type = null): bool
|
||||
{
|
||||
return self::isInstType(
|
||||
[
|
||||
self::TYPE_MSUBDOMAIN,
|
||||
self::TYPE_MSUBFOLDER,
|
||||
],
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if install type is available
|
||||
*
|
||||
* @param int|int[] $type install type Enum InstState::TYPE_*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function instTypeAvailable($type): bool
|
||||
{
|
||||
$acceptList = ParamDescConfigs::getInstallTypesAcceptValues();
|
||||
$typesToCheck = is_array($type) ? $type : [$type];
|
||||
$typesAvaliables = array_intersect($acceptList, $typesToCheck);
|
||||
return (count($typesAvaliables) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if add site on multisite install is available
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAddSiteOnMultisiteAvailable(): bool
|
||||
{
|
||||
return self::instTypeAvailable(
|
||||
[
|
||||
self::TYPE_SINGLE_ON_SUBDOMAIN,
|
||||
self::TYPE_SINGLE_ON_SUBFOLDER,
|
||||
self::TYPE_SUBSITE_ON_SUBDOMAIN,
|
||||
self::TYPE_SUBSITE_ON_SUBFOLDER,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if multisite install is available
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMultisiteInstallAvailable(): bool
|
||||
{
|
||||
return self::instTypeAvailable(
|
||||
[
|
||||
self::TYPE_MSUBDOMAIN,
|
||||
self::TYPE_MSUBFOLDER,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function in case of an error returns an empty array but never generates exceptions
|
||||
*
|
||||
* @param array<string, mixed> $overwriteData Overwrite data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getAdminUsersOnOverwriteDatabase($overwriteData)
|
||||
{
|
||||
$adminUsers = [];
|
||||
$dbFuncs = null;
|
||||
try {
|
||||
$dbFuncs = DUPX_DB_Functions::getInstance();
|
||||
|
||||
if (!$dbFuncs->dbConnection($overwriteData)) {
|
||||
throw new Exception('GET USERS ON CURRENT DATABASE FAILED. Can\'t connect');
|
||||
}
|
||||
|
||||
$usersTables = [
|
||||
$dbFuncs->getUserTableName($overwriteData['table_prefix']),
|
||||
$dbFuncs->getUserMetaTableName($overwriteData['table_prefix']),
|
||||
];
|
||||
|
||||
if (!$dbFuncs->tablesExist($usersTables)) {
|
||||
throw new Exception(
|
||||
'GET USERS ON CURRENT DATABASE FAILED. Users tables doesn\'t exist, ' .
|
||||
'continue with orverwrite installation but with option keep users disabled' . "\n"
|
||||
);
|
||||
}
|
||||
|
||||
if (($adminUsers = $dbFuncs->getAdminUsers($overwriteData['table_prefix'])) === false) {
|
||||
$adminUsers = [];
|
||||
throw new Exception('GET USERS ON CURRENT DATABASE FAILED. OVERWRITE DB USERS NOT FOUND');
|
||||
}
|
||||
} catch (Exception | Error $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'GET ADMIN USER EXECPTION BUT CONTINUE');
|
||||
} finally {
|
||||
if ($dbFuncs instanceof DUPX_DB_Functions) {
|
||||
$dbFuncs->closeDbConnection();
|
||||
}
|
||||
}
|
||||
|
||||
return $adminUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WP version from the ./wp-includes/version.php file if it exists, otherwise '0'
|
||||
*
|
||||
* @return string WP version
|
||||
*/
|
||||
protected function getWordPressVersionOverwrite(): string
|
||||
{
|
||||
$wp_version = '0';
|
||||
try {
|
||||
$versionFilePath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW) . "/wp-includes/version.php";
|
||||
if (!file_exists($versionFilePath) || !is_readable($versionFilePath)) {
|
||||
Log::info("WordPress Version file does not exist or is not readable at path: {$versionFilePath}");
|
||||
return $wp_version;
|
||||
}
|
||||
|
||||
include($versionFilePath);
|
||||
return $wp_version;
|
||||
} catch (Exception $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'EXCEPTION GETTING WordPress VERSION, BUT CONTINUE');
|
||||
} catch (Error $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'ERROR GETTING WordPress VERSION, BUT CONTINUE');
|
||||
}
|
||||
|
||||
return $wp_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Duplicator Pro version if it exists, otherwise '0'
|
||||
*
|
||||
* @param array<string, mixed> $overwriteData Overwrite data
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
protected function updateOverwriteDataFromDb(&$overwriteData): bool
|
||||
{
|
||||
$dbFuncs = null;
|
||||
try {
|
||||
$dbFuncs = DUPX_DB_Functions::getInstance();
|
||||
|
||||
if (!$dbFuncs->dbConnection($overwriteData)) {
|
||||
throw new Exception('OVERWRITE DATA ERROR: Can\'t connect');
|
||||
}
|
||||
|
||||
$optionsTable = DUPX_DB_Functions::getOptionsTableName($overwriteData['table_prefix']);
|
||||
|
||||
if (!$dbFuncs->tablesExist($optionsTable)) {
|
||||
throw new Exception("OVERWRITE DATA ERROR: Options tables doesn't exist.\n");
|
||||
}
|
||||
|
||||
$duplicatorProVersion = $dbFuncs->getDuplicatorVersion($overwriteData['table_prefix']);
|
||||
|
||||
$overwriteData['dupVersion'] = (empty($duplicatorProVersion) ? '0' : $duplicatorProVersion);
|
||||
$overwriteData['ustatIdentifier'] = $dbFuncs->getUniqueId($overwriteData['table_prefix']);
|
||||
$overwriteData['packagesTableExists'] = $dbFuncs->tablesExist($dbFuncs->getPackagesTableName($overwriteData['table_prefix']));
|
||||
} catch (Exception $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'GET DUPLICATOR VERSION EXECPTION BUT CONTINUE');
|
||||
return false;
|
||||
} catch (Error $e) {
|
||||
Log::logException($e, Log::LV_DEFAULT, 'GET DUPLICATOR VERSION ERROR BUT CONTINUE');
|
||||
return false;
|
||||
} finally {
|
||||
if ($dbFuncs instanceof DUPX_DB_Functions) {
|
||||
$dbFuncs->closeDbConnection();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* getHtmlModeHeader
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHtmlModeHeader(): string
|
||||
{
|
||||
$additional_info = '<span class="requires-no-db"> - No database actions';
|
||||
$additional_info .= DUPX_ArchiveConfig::getInstance()->isDBExcluded() ? ' (Database Excluded)' : '';
|
||||
$additional_info .= '</span>';
|
||||
|
||||
$additional_info .= (DUPX_ArchiveConfig::getInstance()->isDBOnly()) ? ' - Database Only' : '';
|
||||
$additional_info .= ($GLOBALS['DUPX_ENFORCE_PHP_INI']) ? '<i style="color:red"><br/>*PHP ini enforced*</i>' : '';
|
||||
|
||||
switch ($this->getMode()) {
|
||||
case self::MODE_OVR_INSTALL:
|
||||
$label = 'Overwrite Install';
|
||||
$class = 'dupx-overwrite mode_overwrite';
|
||||
break;
|
||||
case self::MODE_STD_INSTALL:
|
||||
$label = 'Standard Install';
|
||||
$class = 'dupx-overwrite mode_standard';
|
||||
break;
|
||||
case self::MODE_UNKNOWN:
|
||||
default:
|
||||
$label = 'Custom Install';
|
||||
$class = 'mode_unknown';
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen($additional_info)) {
|
||||
return '<span class="' . $class . '">' . $label . ' ' . $additional_info . '</span>';
|
||||
} else {
|
||||
return "<span class=\"{$class}\">{$label}</span>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reset current mode
|
||||
*
|
||||
* @param boolean $saveParams save params
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function resetState($saveParams = true)
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$paramsManager->setValue(PrmMng::PARAM_INSTALLER_MODE, self::MODE_UNKNOWN);
|
||||
if ($saveParams) {
|
||||
return $this->save();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current installer state
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
return PrmMng::getInstance()->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return stru if is overwrite install
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isOverwrite(): bool
|
||||
{
|
||||
return (PrmMng::getInstance()->getValue(PrmMng::PARAM_INSTALLER_MODE) === self::MODE_OVR_INSTALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the DB action is set to do nothing
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function dbDoNothing(): bool
|
||||
{
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_ACTION) === DUPX_DBInstall::DBACTION_DO_NOTHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the installer is running in staging mode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isStagingMode(): bool
|
||||
{
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
return !empty($overwriteData['stagingMode']);
|
||||
}
|
||||
|
||||
/**
|
||||
* this function returns true if both the URL and path old and new path are identical
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstallerCreatedInThisLocation(): bool
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
$urlNew = null;
|
||||
$pathNew = null;
|
||||
|
||||
if (InstState::isImportFromBackendMode()) {
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
if (isset($overwriteData['urls']['home']) && isset($overwriteData['paths']['home'])) {
|
||||
$urlNew = $overwriteData['urls']['home'];
|
||||
$pathNew = $overwriteData['paths']['home'];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($urlNew) || is_null($pathNew)) {
|
||||
$pathNew = $paramsManager->getValue(PrmMng::PARAM_PATH_NEW);
|
||||
$urlNew = $paramsManager->getValue(PrmMng::PARAM_URL_NEW);
|
||||
}
|
||||
|
||||
return self::urlAndPathAreSameOfArchive($urlNew, $pathNew);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the url and path are the same of the archive
|
||||
*
|
||||
* @param string $urlNew new url
|
||||
* @param string $pathNew new path
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function urlAndPathAreSameOfArchive($urlNew, $pathNew): bool
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
$urlOld = rtrim($archiveConfig->getRealValue('homeUrl'), '/');
|
||||
$paths = $archiveConfig->getRealValue('archivePaths');
|
||||
$pathOld = $paths->home;
|
||||
$paths = $archiveConfig->getRealValue('originalPaths');
|
||||
$pathOldOrig = $paths->home;
|
||||
|
||||
$urlNew = SnapIO::untrailingslashit($urlNew);
|
||||
$urlOld = SnapIO::untrailingslashit($urlOld);
|
||||
$pathNew = SnapIO::untrailingslashit($pathNew);
|
||||
$pathOld = SnapIO::untrailingslashit($pathOld);
|
||||
$pathOldOrig = SnapIO::untrailingslashit($pathOldOrig);
|
||||
|
||||
return (($pathNew === $pathOld || $pathNew === $pathOldOrig) && $urlNew === $urlOld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get migration data to store in wp-options
|
||||
*
|
||||
* @return MigrateData
|
||||
*/
|
||||
public static function getMigrationData(): MigrateData
|
||||
{
|
||||
$sec = Security::getInstance();
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
$result = new MigrateData();
|
||||
$ac = DUPX_ArchiveConfig::getInstance();
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
|
||||
$result->installerVersion = DUPX_VERSION;
|
||||
$result->installType = $paramsManager->getValue(PrmMng::PARAM_INST_TYPE);
|
||||
$result->installTime = gmdate("Y-m-d H:i:s");
|
||||
$result->logicModes = self::getLogicModes();
|
||||
$result->template = PrmMng::getInstance()->getValue(PrmMng::PARAM_TEMPLATE);
|
||||
$result->restoreBackupMode = self::isRestoreBackup();
|
||||
$result->recoveryMode = self::isRecoveryMode();
|
||||
$result->archivePath = $sec->getArchivePath();
|
||||
$result->packageId = $ac->packInfo->packageId;
|
||||
$result->packageHash = $ac->packInfo->packageHash;
|
||||
$result->installerPath = $sec->getBootFilePath();
|
||||
$result->installerBootLog = $sec->getBootLogFile();
|
||||
$result->installerLog = Log::getLogFilePath();
|
||||
$result->dupInstallerPath = DUPX_INIT;
|
||||
$result->origFileFolderPath = InstallerOrigFileMng::getInstance()->getMainFolder();
|
||||
$result->safeMode = $paramsManager->getValue(PrmMng::PARAM_SAFE_MODE);
|
||||
$result->cleanInstallerFiles = $paramsManager->getValue(PrmMng::PARAM_AUTO_CLEAN_INSTALLER_FILES);
|
||||
$result->licenseType = $ac->license_type;
|
||||
$result->phpVersion = $ac->version_php;
|
||||
$result->archiveType = $ac->isZipArchive() ? 'zip' : 'dup';
|
||||
$result->siteSize = $ac->fileInfo->size;
|
||||
$result->siteNumFiles = ($ac->fileInfo->dirCount + $ac->fileInfo->fileCount);
|
||||
$result->siteDbSize = $ac->dbInfo->tablesSizeOnDisk;
|
||||
$result->siteDBNumTables = $ac->dbInfo->tablesFinalCount;
|
||||
$result->components = $ac->components;
|
||||
$result->ustatIdentifier = $overwriteData['ustatIdentifier'];
|
||||
|
||||
// Staging mode data - grouped in array for organization
|
||||
$result->staging = [
|
||||
'enabled' => !empty($overwriteData['stagingMode']),
|
||||
'mainSiteUrl' => $overwriteData['mainSiteUrl'] ?? '',
|
||||
'pageUrl' => $overwriteData['stagingPageUrl'] ?? '',
|
||||
'identifier' => $overwriteData['stagingIdentifier'] ?? '',
|
||||
'colorScheme' => $overwriteData['colorScheme'] ?? 'fresh',
|
||||
'title' => $overwriteData['stagingTitle'] ?? '',
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get admin login url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAdminLogin()
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
if (self::isAddSiteOnMultisite()) {
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$loginUrl = $overwriteData['urls']['login'];
|
||||
} else {
|
||||
$oldUrl = $paramsManager->getValue(PrmMng::PARAM_SITE_URL_OLD);
|
||||
$newUrl = $paramsManager->getValue(PrmMng::PARAM_SITE_URL);
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$loginUrl = DUPX_ArchiveConfig::getNewSubUrl($oldUrl, $newUrl, $archiveConfig->getRealValue("loginUrl"));
|
||||
}
|
||||
return $loginUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get install type
|
||||
*
|
||||
* @return int install type Enum self::TYPE_*
|
||||
*/
|
||||
public static function getInstType()
|
||||
{
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_INST_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|int[] $type list of types to check
|
||||
* @param int $typeToCheck if is null get param install time or check this
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstType($type, $typeToCheck = null): bool
|
||||
{
|
||||
$currentType = is_null($typeToCheck) ? self::getInstType() : $typeToCheck;
|
||||
if (is_array($type)) {
|
||||
return in_array($currentType, $type);
|
||||
} else {
|
||||
return $currentType === $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get install logic modes
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getLogicModes(): array
|
||||
{
|
||||
$modes = [];
|
||||
if (self::isImportFromBackendMode()) {
|
||||
$modes[] = self::LOGIC_MODE_IMPORT;
|
||||
}
|
||||
if (self::isRecoveryMode()) {
|
||||
$modes[] = self::LOGIC_MODE_RECOVERY;
|
||||
}
|
||||
if (self::isClassicInstall()) {
|
||||
$modes[] = self::LOGIC_MODE_CLASSIC;
|
||||
}
|
||||
if (self::isOverwrite()) {
|
||||
$modes[] = self::LOGIC_MODE_OVERWRITE;
|
||||
}
|
||||
if (self::isBridgeInstall()) {
|
||||
$modes[] = self::LOGIC_MODE_BRIDGE;
|
||||
}
|
||||
if (self::isRestoreBackup()) {
|
||||
$modes[] = self::LOGIC_MODE_RESTORE_BACKUP;
|
||||
}
|
||||
return $modes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Installer params manager
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
||||
*
|
||||
* @package SC\DUPX\U
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
|
||||
interface DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param (ParamItem|ParamForm)[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params);
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param (ParamItem|ParamForm)[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params);
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CPanel params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamsDescriptors;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormPass;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescCPanel implements DescriptorInterface
|
||||
{
|
||||
const INVALID_EMPTY = 'can\'t be empty';
|
||||
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param (ParamItem|ParamForm)[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_CPNL_CAN_SELECTED] = new ParamItem(
|
||||
PrmMng::PARAM_CPNL_CAN_SELECTED,
|
||||
ParamItem::TYPE_BOOL,
|
||||
['default' => true]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_HOST] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_HOST,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => "https://" . parse_url(DUPX_ROOT_URL, PHP_URL_HOST) . ":2083",
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfCpanel',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'CPanel host:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'attr' => [
|
||||
'required' => 'true',
|
||||
'placeholder' => 'cPanel url',
|
||||
],
|
||||
'subNote' => '<span id="cpnl-host-warn">'
|
||||
. 'Caution: The cPanel host name and URL in the browser address bar do not match, '
|
||||
. 'in rare cases this may be intentional.'
|
||||
. 'Please be sure this is the correct server to avoid data loss.</span>',
|
||||
'postfix' => [
|
||||
'type' => 'button',
|
||||
'label' => 'get',
|
||||
'btnAction' => 'DUPX.getcPanelURL(this);',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_USER] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_USER,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfCpanel',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'CPanel username:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'attr' => [
|
||||
'required' => 'required',
|
||||
'data-parsley-pattern' => '/^[\w.-~]+$/',
|
||||
'placeholder' => 'cPanel username',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_PASS] = new ParamFormPass(
|
||||
PrmMng::PARAM_CPNL_PASS,
|
||||
ParamFormPass::TYPE_STRING,
|
||||
ParamFormPass::FORM_TYPE_PWD_TOGGLE,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfCpanel',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'CPanel password:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'attr' => [
|
||||
'required' => 'true',
|
||||
'placeholder' => 'cPanel password',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
/** @var ParamForm */
|
||||
$paramFormObj = $params[PrmMng::PARAM_DB_ACTION];
|
||||
$paramFormObj = $paramFormObj->getCopyWithNewName(
|
||||
PrmMng::PARAM_CPNL_DB_ACTION,
|
||||
[],
|
||||
[
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
]
|
||||
);
|
||||
// force create database enable for cpanel
|
||||
$paramFormObj->setOptionStatus(0, ParamOption::OPT_ENABLED);
|
||||
$params[PrmMng::PARAM_CPNL_DB_ACTION] = $paramFormObj;
|
||||
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_PREFIX] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_PREFIX,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_HIDDEN,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Control panel prefix',
|
||||
'renderLabel' => false,
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
/** @var ParamForm */
|
||||
$paramFormObj = $params[PrmMng::PARAM_DB_HOST];
|
||||
$paramFormObj = $paramFormObj->getCopyWithNewName(
|
||||
PrmMng::PARAM_CPNL_DB_HOST,
|
||||
[
|
||||
'default' => 'localhost',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfCpanel',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
'attr' => ['required' => 'true'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_HOST] = $paramFormObj;
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_NAME_SEL] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_DB_NAME_SEL,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => null,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Database:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
'attr' => [
|
||||
'required' => 'true',
|
||||
'data-parsley-pattern' => '^((?!-- Select Database --).)*$',
|
||||
],
|
||||
'subNote' => '<span class="s2-warning-emptydb">'
|
||||
. 'Warning: The selected "Action" above will remove <u>all data</u> from this database!'
|
||||
. '</span>',
|
||||
]
|
||||
);
|
||||
|
||||
/** @var ParamForm */
|
||||
$paramFormObj = $params[PrmMng::PARAM_DB_NAME];
|
||||
$paramFormObj = $paramFormObj->getCopyWithNewName(
|
||||
PrmMng::PARAM_CPNL_DB_NAME_TXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
$paramManager = PrmMng::getInstance();
|
||||
if (
|
||||
$paramManager->getValue(PrmMng::PARAM_DB_VIEW_MODE) === 'cpnl' &&
|
||||
$paramManager->getValue(PrmMng::PARAM_CPNL_DB_ACTION) === \DUPX_DBInstall::DBACTION_CREATE
|
||||
) {
|
||||
return ParamsDescriptors::validateNotEmpty($value, $paramObj);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'Database:',
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
'attr' => [
|
||||
'required' => 'true',
|
||||
'data-parsley-pattern' => '/^[\w.-~]+$/',
|
||||
'data-parsley-errors-container' => '#cpnl-dbname-txt-error',
|
||||
],
|
||||
'subNote' => '<span id="cpnl-dbname-txt-error"></span>',
|
||||
'prefix' => [
|
||||
'type' => 'label',
|
||||
'label' => '',
|
||||
'id' => 'cpnl-prefix-dbname',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_NAME_TXT] = $paramFormObj;
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_USER_SEL] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_DB_USER_SEL,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => null,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'User:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
'attr' => [
|
||||
'required' => 'true',
|
||||
'data-parsley-pattern' => '^((?!-- Select User --).)*$',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
/** @var ParamForm */
|
||||
$paramFormObj = $params[PrmMng::PARAM_DB_USER];
|
||||
$paramFormObj = $paramFormObj->getCopyWithNewName(
|
||||
PrmMng::PARAM_CPNL_DB_USER_TXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
$paramManager = PrmMng::getInstance();
|
||||
if (
|
||||
$paramManager->getValue(PrmMng::PARAM_DB_VIEW_MODE) === 'cpnl' &&
|
||||
$paramManager->getValue(PrmMng::PARAM_CPNL_DB_USER_CHK) === true
|
||||
) {
|
||||
return ParamsDescriptors::validateNotEmpty($value, $paramObj);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'User:',
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
'attr' => [
|
||||
'required' => 'true',
|
||||
'data-parsley-pattern' => '/^[a-zA-Z0-9-_]+$/',
|
||||
'data-parsley-errors-container' => '#cpnl-dbuser-txt-error',
|
||||
'data-parsley-cpnluser' => "16",
|
||||
],
|
||||
'subNote' => '<span id="cpnl-dbuser-txt-error"></span>',
|
||||
'prefix' => [
|
||||
'type' => 'label',
|
||||
'label' => '',
|
||||
'id' => 'cpnl-prefix-dbuser',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_USER_TXT] = $paramFormObj;
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_USER_CHK] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_DB_USER_CHK,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => ' ',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'checkboxLabel' => 'Create New Database User',
|
||||
]
|
||||
);
|
||||
|
||||
/** @var ParamForm */
|
||||
$paramFormObj = $params[PrmMng::PARAM_DB_PASS];
|
||||
$paramFormObj = $paramFormObj->getCopyWithNewName(
|
||||
PrmMng::PARAM_CPNL_DB_PASS,
|
||||
[],
|
||||
[
|
||||
'status' => ParamForm::STATUS_DISABLED,
|
||||
'attr' => ['required' => 'true'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_DB_PASS] = $paramFormObj;
|
||||
|
||||
$params[PrmMng::PARAM_CPNL_IGNORE_PREFIX] = new ParamForm(
|
||||
PrmMng::PARAM_CPNL_IGNORE_PREFIX,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'CPanel Prefix',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'checkboxLabel' => 'Ignore',
|
||||
'attr' => ['onclick' => 'DUPX.cpnlPrefixIgnore();'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate function for cpanle params
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param ParamItem $paramObj current param object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function validateNoEmptyIfCpanel($value, ParamItem $paramObj)
|
||||
{
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_VIEW_MODE) !== 'cpnl') {
|
||||
return true;
|
||||
}
|
||||
return ParamsDescriptors::validateNotEmpty($value, $paramObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Configs(htaccess, wp-config ...) params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Addons\ProBase\License;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use DUPX_Template;
|
||||
use DUPX_WPConfig;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescConfigs implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_INST_TYPE] = new ParamForm(
|
||||
PrmMng::PARAM_INST_TYPE,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_RADIO,
|
||||
[
|
||||
'default' => InstState::TYPE_NOT_SET,
|
||||
'acceptValues' => [
|
||||
self::class,
|
||||
'getInstallTypesAcceptValues',
|
||||
],
|
||||
],
|
||||
[
|
||||
'status' => ParamForm::STATUS_ENABLED,
|
||||
'label' => 'Install Type:',
|
||||
'wrapperClasses' => [
|
||||
'group-block',
|
||||
'revalidate-on-change',
|
||||
],
|
||||
'options' => self::getInstallTypeOptions(),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONFIG] = new ParamForm(
|
||||
PrmMng::PARAM_WP_CONFIG,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => 'modify',
|
||||
'acceptValues' => [
|
||||
'modify',
|
||||
'nothing',
|
||||
'new',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'WordPress:',
|
||||
'classes' => ['requires-db-disable'],
|
||||
'wrapperClasses' => 'medium',
|
||||
'status' => function (ParamItem $paramObj): string {
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
new ParamOption('nothing', 'Do nothing'),
|
||||
new ParamOption(
|
||||
'modify',
|
||||
'Modify original',
|
||||
fn(ParamOption $opt): string => DUPX_WPConfig::isSourceWpConfigValid() ? ParamOption::OPT_ENABLED : ParamOption::OPT_DISABLED
|
||||
),
|
||||
new ParamOption('new', 'Create new from wp-config sample'),
|
||||
],
|
||||
'subNote' => 'wp-config.php',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_HTACCESS_CONFIG] = new ParamForm(
|
||||
PrmMng::PARAM_HTACCESS_CONFIG,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => 'new',
|
||||
'acceptValues' => [
|
||||
'new',
|
||||
'original',
|
||||
'nothing',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Apache:',
|
||||
'classes' => ['requires-db-disable'],
|
||||
'wrapperClasses' => 'medium',
|
||||
'status' => function (ParamItem $paramObj): string {
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
new ParamOption('nothing', 'Do nothing'),
|
||||
new ParamOption('original', 'Retain original from Archive.zip/daf'),
|
||||
new ParamOption('new', 'Create new'),
|
||||
],
|
||||
'subNote' => '.htaccess',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_OTHER_CONFIG] = new ParamForm(
|
||||
PrmMng::PARAM_OTHER_CONFIG,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => 'new',
|
||||
'acceptValues' => [
|
||||
'new',
|
||||
'original',
|
||||
'nothing',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'General:',
|
||||
'classes' => ['requires-db-disable'],
|
||||
'wrapperClasses' => 'medium',
|
||||
'status' => function (ParamItem $paramObj): string {
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
new ParamOption('nothing', 'Do nothing'),
|
||||
new ParamOption('original', 'Retain original from Archive.zip/daf'),
|
||||
new ParamOption('new', 'Reset'),
|
||||
],
|
||||
'subNote' => 'includes: php.ini, user.ini, web.config',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
$installType = $params[PrmMng::PARAM_INST_TYPE]->getValue();
|
||||
if ($installType == InstState::TYPE_NOT_SET) {
|
||||
$acceptValues = $params[PrmMng::PARAM_INST_TYPE]->getAcceptValues();
|
||||
$params[PrmMng::PARAM_INST_TYPE]->setValue(self::getInstTypeByPriority($acceptValues));
|
||||
}
|
||||
|
||||
$installType = $params[PrmMng::PARAM_INST_TYPE]->getValue();
|
||||
if (InstState::isRestoreBackup($installType)) {
|
||||
if (\DUPX_Custom_Host_Manager::getInstance()->isManaged()) {
|
||||
$params[PrmMng::PARAM_WP_CONFIG]->setValue('nothing');
|
||||
$params[PrmMng::PARAM_HTACCESS_CONFIG]->setValue('nothing');
|
||||
$params[PrmMng::PARAM_OTHER_CONFIG]->setValue('nothing');
|
||||
} else {
|
||||
$params[PrmMng::PARAM_WP_CONFIG]->setValue('modify');
|
||||
$params[PrmMng::PARAM_HTACCESS_CONFIG]->setValue('original');
|
||||
$params[PrmMng::PARAM_OTHER_CONFIG]->setValue('original');
|
||||
}
|
||||
}
|
||||
|
||||
if (!DUPX_WPConfig::isSourceWpConfigValid()) {
|
||||
if ($params[PrmMng::PARAM_WP_CONFIG]->getValue() === 'modify') {
|
||||
$params[PrmMng::PARAM_WP_CONFIG]->setValue('new');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default install type from install types enabled
|
||||
*
|
||||
* @param int[] $acceptValues install types enabled
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected static function getInstTypeByPriority($acceptValues): int
|
||||
{
|
||||
$defaultPriority = [
|
||||
InstState::TYPE_RECOVERY_MSUBDOMAIN,
|
||||
InstState::TYPE_RECOVERY_MSUBFOLDER,
|
||||
InstState::TYPE_RECOVERY_SINGLE,
|
||||
InstState::TYPE_RBACKUP_MSUBDOMAIN,
|
||||
InstState::TYPE_RBACKUP_MSUBFOLDER,
|
||||
InstState::TYPE_RBACKUP_SINGLE,
|
||||
InstState::TYPE_SINGLE_ON_SUBDOMAIN,
|
||||
InstState::TYPE_SINGLE_ON_SUBFOLDER,
|
||||
InstState::TYPE_SUBSITE_ON_SUBDOMAIN,
|
||||
InstState::TYPE_SUBSITE_ON_SUBFOLDER,
|
||||
InstState::TYPE_MSUBDOMAIN,
|
||||
InstState::TYPE_MSUBFOLDER,
|
||||
InstState::TYPE_SINGLE,
|
||||
InstState::TYPE_STANDALONE,
|
||||
];
|
||||
|
||||
foreach ($defaultPriority as $current) {
|
||||
if (in_array($current, $acceptValues)) {
|
||||
return $current;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('No default value found on proprity list');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
protected static function getInstallTypeOptions(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$option = new ParamOption(InstState::TYPE_RBACKUP_SINGLE, 'Restore single site', [self::class, 'typeOptionsVisibility']);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_RBACKUP_MSUBDOMAIN,
|
||||
'<b>Restore</b> multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_RBACKUP_MSUBFOLDER,
|
||||
'<b>Restore</b> multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_SINGLE,
|
||||
'<b>Full</b> install single site',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_MSUBDOMAIN,
|
||||
'<b>Full</b> install multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_MSUBFOLDER,
|
||||
'<b>Full</b> install multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_STANDALONE,
|
||||
'<b>Convert</b> network subsite to standalone site',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_SINGLE_ON_SUBDOMAIN,
|
||||
'<b>Import</b> single site into multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_SINGLE_ON_SUBFOLDER,
|
||||
'<b>Import</b> single site into multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_SUBSITE_ON_SUBDOMAIN,
|
||||
'<b>Import</b> subsite into multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_SUBSITE_ON_SUBFOLDER,
|
||||
'<b>Import</b> subsite into multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_RECOVERY_SINGLE,
|
||||
'<b>Restore Backup</b> single site',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_RECOVERY_MSUBDOMAIN,
|
||||
'<b>Restore Backup</b> multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
$option = new ParamOption(
|
||||
InstState::TYPE_RECOVERY_MSUBFOLDER,
|
||||
'<b>Restore Backup</b> multisite network',
|
||||
[
|
||||
self::class,
|
||||
'typeOptionsVisibility',
|
||||
]
|
||||
);
|
||||
$option->setNote([self::class, 'getInstallTypesNotes']);
|
||||
$result[] = $option;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option type status
|
||||
*
|
||||
* @param ParamOption $option install type option
|
||||
*
|
||||
* @return string option status
|
||||
*/
|
||||
public static function typeOptionsVisibility(ParamOption $option): string
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$isOwrMode = PrmMng::getInstance()->getValue(PrmMng::PARAM_INSTALLER_MODE) === InstState::MODE_OVR_INSTALL;
|
||||
|
||||
switch ($option->value) {
|
||||
case InstState::TYPE_SINGLE:
|
||||
if ($archiveConfig->mu_mode != 0) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_MSUBDOMAIN:
|
||||
if ($archiveConfig->mu_mode != 1) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_MSUBFOLDER:
|
||||
if ($archiveConfig->mu_mode != 2) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_STANDALONE:
|
||||
if ($archiveConfig->mu_mode == 0) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
if (!$isOwrMode || $archiveConfig->mu_mode > 0 || !$overwriteData['isMultisite'] || !$overwriteData['subdomain']) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
if (!$isOwrMode || $archiveConfig->mu_mode > 0 || !$overwriteData['isMultisite'] || $overwriteData['subdomain']) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
if (!$isOwrMode || $archiveConfig->mu_mode == 0 || !$overwriteData['isMultisite'] || !$overwriteData['subdomain']) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
if (!$isOwrMode || $archiveConfig->mu_mode == 0 || !$overwriteData['isMultisite'] || $overwriteData['subdomain']) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_RBACKUP_SINGLE:
|
||||
if (
|
||||
$archiveConfig->mu_mode != 0 ||
|
||||
!InstState::isInstallerCreatedInThisLocation()
|
||||
) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
if (
|
||||
$archiveConfig->mu_mode != 1 ||
|
||||
!InstState::isInstallerCreatedInThisLocation()
|
||||
) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_RBACKUP_MSUBFOLDER:
|
||||
if (
|
||||
$archiveConfig->mu_mode != 2 ||
|
||||
!InstState::isInstallerCreatedInThisLocation()
|
||||
) {
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
}
|
||||
break;
|
||||
case InstState::TYPE_RECOVERY_SINGLE:
|
||||
case InstState::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
case InstState::TYPE_RECOVERY_MSUBFOLDER:
|
||||
return ParamOption::OPT_HIDDEN;
|
||||
case InstState::TYPE_NOT_SET:
|
||||
default:
|
||||
throw new Exception('Install type not valid ' . $option->value);
|
||||
}
|
||||
|
||||
$acceptValues = self::getInstallTypesAcceptValues();
|
||||
return in_array($option->value, $acceptValues) ? ParamOption::OPT_ENABLED : ParamOption::OPT_DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getInstallTypesAcceptValues(): array
|
||||
{
|
||||
$acceptValues = [];
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_TEMPLATE) === DUPX_Template::TEMPLATE_RECOVERY) {
|
||||
switch ($archiveConfig->mu_mode) {
|
||||
case 0:
|
||||
$acceptValues[] = InstState::TYPE_RECOVERY_SINGLE;
|
||||
break;
|
||||
case 1:
|
||||
$acceptValues[] = InstState::TYPE_RECOVERY_MSUBDOMAIN;
|
||||
break;
|
||||
case 2:
|
||||
$acceptValues[] = InstState::TYPE_RECOVERY_MSUBFOLDER;
|
||||
break;
|
||||
}
|
||||
return $acceptValues;
|
||||
}
|
||||
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$isManaged = \DUPX_Custom_Host_Manager::getInstance()->isManaged();
|
||||
$isSameLocation = InstState::isInstallerCreatedInThisLocation();
|
||||
|
||||
switch ($archiveConfig->mu_mode) {
|
||||
case 0:
|
||||
$acceptValues[] = InstState::TYPE_SINGLE;
|
||||
if ($isSameLocation) {
|
||||
$acceptValues[] = InstState::TYPE_RBACKUP_SINGLE;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!$isManaged && !$archiveConfig->isPartialNetwork()) {
|
||||
$acceptValues[] = InstState::TYPE_MSUBDOMAIN;
|
||||
if ($isSameLocation) {
|
||||
$acceptValues[] = InstState::TYPE_RBACKUP_MSUBDOMAIN;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!$isManaged && !$archiveConfig->isPartialNetwork()) {
|
||||
$acceptValues[] = InstState::TYPE_MSUBFOLDER;
|
||||
if ($isSameLocation) {
|
||||
$acceptValues[] = InstState::TYPE_RBACKUP_MSUBFOLDER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
$archiveConfig->mu_mode > 0 &&
|
||||
License::can(License::CAPABILITY_MULTISITE_PLUS)
|
||||
) {
|
||||
$acceptValues[] = InstState::TYPE_STANDALONE;
|
||||
}
|
||||
|
||||
if (
|
||||
InstState::isImportFromBackendMode() &&
|
||||
$overwriteData['isMultisite'] &&
|
||||
License::can(License::CAPABILITY_MULTISITE_PLUS)
|
||||
) {
|
||||
if (version_compare($overwriteData['wpVersion'], InstState::SUBSITE_IMPORT_WP_MIN_VERSION, '>=')) {
|
||||
if ($archiveConfig->mu_mode == 0) {
|
||||
if ($overwriteData['subdomain']) {
|
||||
$acceptValues[] = InstState::TYPE_SINGLE_ON_SUBDOMAIN;
|
||||
} else {
|
||||
$acceptValues[] = InstState::TYPE_SINGLE_ON_SUBFOLDER;
|
||||
}
|
||||
} else {
|
||||
if ($overwriteData['subdomain']) {
|
||||
$acceptValues[] = InstState::TYPE_SUBSITE_ON_SUBDOMAIN;
|
||||
} else {
|
||||
$acceptValues[] = InstState::TYPE_SUBSITE_ON_SUBFOLDER;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$msg = "The option to import the site into the multisite network has been disabled " .
|
||||
"since it's only available for WordPress <b>" . InstState::SUBSITE_IMPORT_WP_MIN_VERSION . " +</b>.<br>";
|
||||
$msg .= " To overcome the issue please update WordPress to the most recent version.";
|
||||
|
||||
$noticeManager = \DUPX_NOTICE_MANAGER::getInstance();
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => 'Import site into network disabled',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => $msg,
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE, 'import-site-into-network-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
return $acceptValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return install type option note
|
||||
*
|
||||
* @param ParamOption $option install type option
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getInstallTypesNotes(ParamOption $option): string
|
||||
{
|
||||
switch ($option->value) {
|
||||
case InstState::TYPE_SINGLE:
|
||||
case InstState::TYPE_MSUBDOMAIN:
|
||||
case InstState::TYPE_MSUBFOLDER:
|
||||
return '';
|
||||
case InstState::TYPE_STANDALONE:
|
||||
return (License::can(License::CAPABILITY_MULTISITE_PLUS) ? '' : License::getLicenseUpdateText());
|
||||
case InstState::TYPE_SINGLE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SINGLE_ON_SUBFOLDER:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBDOMAIN:
|
||||
case InstState::TYPE_SUBSITE_ON_SUBFOLDER:
|
||||
$notes = [];
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
if (!InstState::isImportFromBackendMode()) {
|
||||
$notes[] = 'This functionality is active only in the Drag&Drop import.';
|
||||
} else {
|
||||
if (
|
||||
!isset($overwriteData['wpVersion']) ||
|
||||
version_compare($overwriteData['wpVersion'], InstState::SUBSITE_IMPORT_WP_MIN_VERSION, '<')
|
||||
) {
|
||||
$notes[] = 'WordPress ' . InstState::SUBSITE_IMPORT_WP_MIN_VERSION .
|
||||
'+ is required on current multisite to enabled this function.';
|
||||
}
|
||||
}
|
||||
|
||||
if (!License::can(License::CAPABILITY_MULTISITE_PLUS)) {
|
||||
$notes[] = License::getLicenseUpdateText();
|
||||
}
|
||||
return implode('<br>', $notes);
|
||||
case InstState::TYPE_RBACKUP_SINGLE:
|
||||
case InstState::TYPE_RBACKUP_MSUBDOMAIN:
|
||||
case InstState::TYPE_RBACKUP_MSUBFOLDER:
|
||||
case InstState::TYPE_RECOVERY_SINGLE:
|
||||
case InstState::TYPE_RECOVERY_MSUBDOMAIN:
|
||||
case InstState::TYPE_RECOVERY_MSUBFOLDER:
|
||||
return '';
|
||||
case InstState::TYPE_NOT_SET:
|
||||
default:
|
||||
throw new Exception('Install type not valid ' . $option->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Controller params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescController implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_FINAL_REPORT_DATA] = new ParamItem(
|
||||
PrmMng::PARAM_FINAL_REPORT_DATA,
|
||||
ParamItem::TYPE_ARRAY_MIXED,
|
||||
[
|
||||
'default' => [
|
||||
'extraction' => [
|
||||
'table_count' => 0,
|
||||
'table_rows' => 0,
|
||||
'query_errs' => 0,
|
||||
],
|
||||
'replace' => [
|
||||
'scan_tables' => 0,
|
||||
'scan_rows' => 0,
|
||||
'scan_cells' => 0,
|
||||
'updt_tables' => 0,
|
||||
'updt_rows' => 0,
|
||||
'updt_cells' => 0,
|
||||
'errsql' => 0,
|
||||
'errser' => 0,
|
||||
'errkey' => 0,
|
||||
'errsql_sum' => 0,
|
||||
'errser_sum' => 0,
|
||||
'errkey_sum' => 0,
|
||||
'err_all' => 0,
|
||||
'warn_all' => 0,
|
||||
'warnlist' => [],
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_INSTALLER_MODE] = new ParamItem(
|
||||
PrmMng::PARAM_INSTALLER_MODE,
|
||||
ParamItem::TYPE_INT,
|
||||
[
|
||||
'default' => InstState::MODE_UNKNOWN,
|
||||
'acceptValues' => [
|
||||
InstState::MODE_UNKNOWN,
|
||||
InstState::MODE_STD_INSTALL,
|
||||
InstState::MODE_OVR_INSTALL,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_OVERWRITE_SITE_DATA] = new ParamItem(
|
||||
PrmMng::PARAM_OVERWRITE_SITE_DATA,
|
||||
ParamItem::TYPE_ARRAY_MIXED,
|
||||
[
|
||||
'default' => InstState::overwriteDataDefault(),
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
$params[PrmMng::PARAM_DEBUG] = new ParamItem(
|
||||
PrmMng::PARAM_DEBUG,
|
||||
ParamItem::TYPE_BOOL,
|
||||
[
|
||||
'persistence' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DEBUG_PARAMS] = new ParamItem(
|
||||
PrmMng::PARAM_DEBUG_PARAMS,
|
||||
ParamItem::TYPE_BOOL,
|
||||
[
|
||||
'persistence' => true,
|
||||
'default' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CTRL_ACTION] = new ParamItem(
|
||||
PrmMng::PARAM_CTRL_ACTION,
|
||||
ParamForm::TYPE_STRING,
|
||||
[
|
||||
'persistence' => false,
|
||||
'default' => '',
|
||||
'acceptValues' => [
|
||||
'',
|
||||
'ajax',
|
||||
'secure',
|
||||
'ctrl-step1',
|
||||
'ctrl-step2',
|
||||
'ctrl-step3',
|
||||
'ctrl-step4',
|
||||
'help',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_STEP_ACTION] = new ParamItem(
|
||||
PrmMng::PARAM_STEP_ACTION,
|
||||
ParamForm::TYPE_STRING,
|
||||
[
|
||||
'persistence' => false,
|
||||
'default' => '',
|
||||
'acceptValues' => [
|
||||
'',
|
||||
\DUPX_CTRL::ACTION_STEP_INIZIALIZED,
|
||||
\DUPX_CTRL::ACTION_STEP_ON_VALIDATE,
|
||||
\DUPX_CTRL::ACTION_STEP_SET_TEMPLATE,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[Security::CTRL_TOKEN] = new ParamItem(
|
||||
Security::CTRL_TOKEN,
|
||||
ParamForm::TYPE_STRING,
|
||||
[
|
||||
'persistence' => false,
|
||||
'default' => null,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_ROUTER_ACTION] = new ParamItem(
|
||||
PrmMng::PARAM_ROUTER_ACTION,
|
||||
ParamItem::TYPE_STRING,
|
||||
[
|
||||
'persistence' => false,
|
||||
'default' => 'router',
|
||||
'acceptValues' => ['router'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_TEMPLATE] = new ParamItem(
|
||||
PrmMng::PARAM_TEMPLATE,
|
||||
ParamForm::TYPE_STRING,
|
||||
[
|
||||
'default' => \DUPX_Template::TEMPLATE_BASE,
|
||||
'acceptValues' => [
|
||||
\DUPX_Template::TEMPLATE_BASE,
|
||||
\DUPX_Template::TEMPLATE_ADVANCED,
|
||||
\DUPX_Template::TEMPLATE_IMPORT_BASE,
|
||||
\DUPX_Template::TEMPLATE_IMPORT_ADVANCED,
|
||||
\DUPX_Template::TEMPLATE_RECOVERY,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Database params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Addons\ProBase\License;
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamsDescriptors;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormTables;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormPass;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescDatabase implements DescriptorInterface
|
||||
{
|
||||
const INVALID_EMPTY = 'can\'t be empty';
|
||||
const EMPTY_COLLATION_LABEL = ' --- DEFAULT --- ';
|
||||
const DEFAULT_CHARSET_POSTFIX = ' (Default)';
|
||||
const DEFAULT_COLLATE_POSTFIX = ' (Default)';
|
||||
const SPLIT_CREATE_MAX_VALUE_TO_DEFAULT = 200;
|
||||
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$params[PrmMng::PARAM_DB_DISPLAY_OVERWIRE_WARNING] = new ParamItem(
|
||||
PrmMng::PARAM_DB_DISPLAY_OVERWIRE_WARNING,
|
||||
ParamItem::TYPE_BOOL,
|
||||
['default' => true]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_VIEW_MODE] = new ParamForm(
|
||||
PrmMng::PARAM_DB_VIEW_MODE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_BGROUP,
|
||||
[
|
||||
'default' => 'basic',
|
||||
'acceptValues' => [
|
||||
'basic',
|
||||
'cpnl',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Database view mode',
|
||||
'renderLabel' => false,
|
||||
'options' => [
|
||||
new ParamOption('basic', 'Default'),
|
||||
new ParamOption('cpnl', 'CPanel'),
|
||||
],
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'align-right',
|
||||
'requires-db-hide',
|
||||
],
|
||||
'inputContainerClasses' => ['small'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_HOST] = new ParamForm(
|
||||
PrmMng::PARAM_DB_HOST,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'persistence' => true,
|
||||
'default' => 'localhost',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfBasic',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'Host:',
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'requires-db-hide',
|
||||
],
|
||||
'attr' => [
|
||||
'required' => 'required',
|
||||
'placeholder' => 'localhost',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_NAME] = new ParamForm(
|
||||
PrmMng::PARAM_DB_NAME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'persistence' => true,
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfBasic',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'Database:',
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'requires-db-hide',
|
||||
],
|
||||
'attr' => [
|
||||
'required' => 'required',
|
||||
'placeholder' => 'new or existing database name',
|
||||
],
|
||||
'subNote' => dupxTplRender('parts/params/db-name-notes', [], false),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_USER] = new ParamForm(
|
||||
PrmMng::PARAM_DB_USER,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'persistence' => true,
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
'validateCallback' => [
|
||||
self::class,
|
||||
'validateNoEmptyIfBasic',
|
||||
],
|
||||
'invalidMessage' => self::INVALID_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'User:',
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'requires-db-hide',
|
||||
],
|
||||
'attr' => [
|
||||
'placeholder' => 'valid database username',
|
||||
// Can be written field wise
|
||||
// Ref. https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||
'autocomplete' => "off",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_PASS] = new ParamFormPass(
|
||||
PrmMng::PARAM_DB_PASS,
|
||||
ParamFormPass::TYPE_STRING,
|
||||
ParamFormPass::FORM_TYPE_PWD_TOGGLE,
|
||||
[
|
||||
'persistence' => true,
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Password:',
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'requires-db-hide',
|
||||
],
|
||||
'attr' => [
|
||||
'placeholder' => 'valid database user password',
|
||||
// Can be written field wise
|
||||
// Ref. https://devBasicBasiceloper.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||
'autocomplete' => "off",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_FLAG] = new ParamItem(
|
||||
PrmMng::PARAM_DB_FLAG,
|
||||
ParamForm::TYPE_INT,
|
||||
[
|
||||
'default' => \DUPX_DB::DB_CONNECTION_FLAG_NOT_SET,
|
||||
'acceptValues' => function (ParamItem $param): array {
|
||||
$result = [
|
||||
\DUPX_DB::MYSQLI_CLIENT_NO_FLAGS,
|
||||
MYSQLI_CLIENT_SSL,
|
||||
];
|
||||
if (defined("MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT")) {
|
||||
// phpcs:ignore PHPCompatibility.Constants.NewConstants.mysqli_client_ssl_dont_verify_server_certFound
|
||||
$result[] = MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
|
||||
}
|
||||
return $result;
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_CHARSET] = new ParamForm(
|
||||
PrmMng::PARAM_DB_CHARSET,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => $archiveConfig->getWpConfigDefineValue('DB_CHARSET', ''),
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => ParamForm::VALIDATE_REGEX_AZ_NUMBER_SEP_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'Charset:',
|
||||
'status' => function (ParamForm $param): string {
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
self::class,
|
||||
'getCharsetSelectOptions',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_COLLATE] = new ParamForm(
|
||||
PrmMng::PARAM_DB_COLLATE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => $archiveConfig->getWpConfigDefineValue('DB_COLLATE', ''),
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => ParamForm::VALIDATE_REGEX_AZ_NUMBER_SEP_EMPTY,
|
||||
],
|
||||
[
|
||||
'label' => 'Collation:',
|
||||
'status' => function (): string {
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
self::class,
|
||||
'getCollationSelectOptions',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$tablePrefixWarning = "Changing this setting alters the database table prefix by renaming all tables and references to them.\n"
|
||||
. "Change it only if you're sure you know what you're doing!";
|
||||
|
||||
$params[PrmMng::PARAM_DB_TABLE_PREFIX] = new ParamForm(
|
||||
PrmMng::PARAM_DB_TABLE_PREFIX,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => \DUPX_ArchiveConfig::getInstance()->wp_tableprefix,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => ParamForm::VALIDATE_REGEX_AZ_NUMBER_SEP,
|
||||
],
|
||||
[
|
||||
'status' => function (): string {
|
||||
if (!License::can(License::CAPABILITY_CHANGE_TABLE_PREFIX)) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
}
|
||||
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_READONLY;
|
||||
}
|
||||
},
|
||||
'label' => 'Table Prefix:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'postfix' => [
|
||||
'type' => 'button',
|
||||
'label' => 'edit',
|
||||
'btnAction' => 'DUPX.editActivate(this, ' . SnapJson::jsonEncode($tablePrefixWarning) . ');',
|
||||
],
|
||||
'subNote' => (License::can(License::CAPABILITY_CHANGE_TABLE_PREFIX) ? '' : License::getLicenseUpdateText()),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_VIEW_CREATION] = new ParamForm(
|
||||
PrmMng::PARAM_DB_VIEW_CREATION,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => true],
|
||||
[
|
||||
'label' => 'Objects:',
|
||||
'checkboxLabel' => 'Enable View Creation',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_PROC_CREATION] = new ParamForm(
|
||||
PrmMng::PARAM_DB_PROC_CREATION,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => true],
|
||||
[
|
||||
'label' => ' ',
|
||||
'checkboxLabel' => 'Enable Stored Procedure Creation',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_FUNC_CREATION] = new ParamForm(
|
||||
PrmMng::PARAM_DB_FUNC_CREATION,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => true],
|
||||
[
|
||||
'label' => ' ',
|
||||
'checkboxLabel' => 'Enable Function Creation',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_REMOVE_DEFINER] = new ParamForm(
|
||||
PrmMng::PARAM_DB_REMOVE_DEFINER,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => ' ',
|
||||
'checkboxLabel' => 'Remove security DEFINER declarations',
|
||||
]
|
||||
);
|
||||
|
||||
$numTables = count((array) \DUPX_ArchiveConfig::getInstance()->dbInfo->tablesList);
|
||||
$params[PrmMng::PARAM_DB_SPLIT_CREATES] = new ParamForm(
|
||||
PrmMng::PARAM_DB_SPLIT_CREATES,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => ($numTables <= self::SPLIT_CREATE_MAX_VALUE_TO_DEFAULT),
|
||||
],
|
||||
[
|
||||
'label' => 'Create:',
|
||||
'checkboxLabel' => 'Run all CREATE SQL statements at once',
|
||||
]
|
||||
);
|
||||
|
||||
$newObj = new ParamForm(
|
||||
PrmMng::PARAM_DB_MYSQL_MODE_OPTS,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'validateRegex' => '/^[A-Za-z0-9_\-,]*$/', // db options with , and can be empty
|
||||
'sanitizeCallback' => function ($value): string {
|
||||
$value = SnapUtil::sanitizeNSCharsNewlineTrim($value);
|
||||
return str_replace(' ', '', $value);
|
||||
},
|
||||
],
|
||||
[
|
||||
'label' => ' ', // for aligment at PARAM_DB_MYSQL_MODE
|
||||
'wrapperClasses' => 'no-display',
|
||||
'subNote' => 'Separate additional ' . \DUPX_View_Funcs::helpLink('step2', 'sql modes', false) . ' with commas & no spaces.<br>'
|
||||
. 'Example: <i>NO_ENGINE_SUBSTITUTION,NO_ZERO_IN_DATE,...</i>.</small>',
|
||||
]
|
||||
);
|
||||
$params[PrmMng::PARAM_DB_MYSQL_MODE_OPTS] = $newObj;
|
||||
$modeOptsWrapper = $newObj->getFormWrapperId();
|
||||
|
||||
$params[PrmMng::PARAM_DB_MYSQL_MODE] = new ParamForm(
|
||||
PrmMng::PARAM_DB_MYSQL_MODE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_RADIO,
|
||||
[
|
||||
'default' => 'DEFAULT',
|
||||
'acceptValues' => [
|
||||
'DEFAULT',
|
||||
'DISABLE',
|
||||
'CUSTOM',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Mode:',
|
||||
'options' => [
|
||||
new ParamOption('DEFAULT', 'Default', ParamOption::OPT_ENABLED, [
|
||||
'onchange' => "if ($(this).is(':checked')) { "
|
||||
. "jQuery('#" . $modeOptsWrapper . "').addClass('no-display');"
|
||||
. "}",
|
||||
]),
|
||||
new ParamOption('DISABLE', 'Disable', ParamOption::OPT_ENABLED, [
|
||||
'onchange' => "if ($(this).is(':checked')) { "
|
||||
. "jQuery('#" . $modeOptsWrapper . "').addClass('no-display');"
|
||||
. "}",
|
||||
]),
|
||||
new ParamOption('CUSTOM', 'Custom', ParamOption::OPT_ENABLED, [
|
||||
'onchange' => "if ($(this).is(':checked')) { "
|
||||
. "jQuery('#" . $modeOptsWrapper . "').removeClass('no-display');"
|
||||
. "}",
|
||||
]),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_TABLES] = new ParamFormTables(
|
||||
PrmMng::PARAM_DB_TABLES,
|
||||
ParamFormTables::TYPE_ARRAY_TABLES,
|
||||
ParamFormTables::FORM_TYPE_TABLES_SELECT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => [],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'Tables',
|
||||
'renderLabel' => false,
|
||||
'status' => function (ParamForm $paramObj): string {
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate function for database params
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param ParamItem $paramObj current param object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function validateNoEmptyIfBasic($value, ParamItem $paramObj)
|
||||
{
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_VIEW_MODE) !== 'basic') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ParamsDescriptors::validateNotEmpty($value, $paramObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get charset options list
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getCharsetSelectOptions(): array
|
||||
{
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_VALIDATION_LEVEL) < \DUPX_Validation_manager::MIN_LEVEL_VALID) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = \DUPX_DB_Functions::getInstance()->getCharsetAndCollationData();
|
||||
$charsetDef = \DUPX_DB_Functions::getInstance()->getDefaultCharset();
|
||||
|
||||
$options = [];
|
||||
|
||||
foreach ($data as $charset => $charsetInfo) {
|
||||
$label = $charset . ($charset == $charsetDef ? self::DEFAULT_CHARSET_POSTFIX : '');
|
||||
$options[] = new ParamOption($charset, $label, ParamOption::OPT_ENABLED, [
|
||||
'data-collations' => json_encode($charsetInfo['collations']),
|
||||
'data-collation-default' => $charsetInfo['defCollation'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get collation options list
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getCollationSelectOptions(): array
|
||||
{
|
||||
$options = [new ParamOption('', self::EMPTY_COLLATION_LABEL)];
|
||||
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_VALIDATION_LEVEL) < \DUPX_Validation_manager::MIN_LEVEL_VALID) {
|
||||
return $options;
|
||||
}
|
||||
|
||||
$data = \DUPX_DB_Functions::getInstance()->getCharsetAndCollationData();
|
||||
$currentCharset = PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_CHARSET);
|
||||
|
||||
if (!isset($data[$currentCharset])) {
|
||||
return $options;
|
||||
}
|
||||
|
||||
$defaultCollation = \DUPX_DB_Functions::getInstance()->getDefaultCollateOfCharset($currentCharset);
|
||||
// if charset exists update default
|
||||
$options = [new ParamOption('', self::EMPTY_COLLATION_LABEL . ' [' . $defaultCollation . ']')];
|
||||
|
||||
foreach ($data[$currentCharset]['collations'] as $collation) {
|
||||
$label = $collation . ($collation == $data[$currentCharset]['defCollation'] ? self::DEFAULT_COLLATE_POSTFIX : '');
|
||||
$options[] = new ParamOption($collation, $label);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Charset and collate param by database settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateCharsetAndCollateByDatabaseSettings(): void
|
||||
{
|
||||
if (InstState::dbDoNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$data = \DUPX_DB_Functions::getInstance()->getCharsetAndCollationData();
|
||||
$charsetDef = \DUPX_DB_Functions::getInstance()->getDefaultCharset();
|
||||
|
||||
$currentCharset = $paramsManager->getValue(PrmMng::PARAM_DB_CHARSET);
|
||||
$currentCollate = $paramsManager->getValue(PrmMng::PARAM_DB_COLLATE);
|
||||
|
||||
if (!array_key_exists($currentCharset, $data)) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_DB_CHARSET, $charsetDef);
|
||||
$paramsManager->setValue(PrmMng::PARAM_DB_COLLATE, '');
|
||||
Log::info('DEFAULT DB_CHARSET [' . $currentCharset . '] isn\'t valid, update DB_CHARSET to ' . $charsetDef . ' and DB_COLLATE set empty');
|
||||
} elseif (strlen($currentCollate) > 0 && !in_array($currentCollate, $data[$currentCharset]['collations'])) {
|
||||
$paramsManager->setValue(PrmMng::PARAM_DB_COLLATE, '');
|
||||
Log::info('DEFAULT DB_COLLATE [' . $currentCollate . '] isn\'t valid, DB_COLLATE set empty');
|
||||
}
|
||||
$paramsManager->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_DB_TABLES]->setValue(\DUPX_DB_Tables::getInstance()->getDefaultParamValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Engines params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use DUPX_Extraction;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Installer\Package\PComponents;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_DBInstall;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescEngines implements DescriptorInterface
|
||||
{
|
||||
const AUTO_SKIP_PATH_REPLACE_LIST = [
|
||||
'',
|
||||
'/html',
|
||||
];
|
||||
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$statusRemoveActions = ($archiveConfig->isDBOnly() ? ParamOption::OPT_DISABLED : ParamOption::OPT_ENABLED);
|
||||
|
||||
$params[PrmMng::PARAM_ARCHIVE_ACTION] = new ParamForm(
|
||||
PrmMng::PARAM_ARCHIVE_ACTION,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => DUPX_Extraction::ACTION_DO_NOTHING,
|
||||
'acceptValues' => [
|
||||
DUPX_Extraction::ACTION_DO_NOTHING,
|
||||
DUPX_Extraction::ACTION_REMOVE_WP_FILES,
|
||||
DUPX_Extraction::ACTION_REMOVE_ALL_FILES,
|
||||
DUPX_Extraction::ACTION_REMOVE_UPLOADS,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Archive Action:',
|
||||
'status' => function ($paramObj): string {
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
return ParamForm::STATUS_SKIP;
|
||||
} elseif (InstState::isRecoveryMode()) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
new ParamOption(DUPX_Extraction::ACTION_DO_NOTHING, 'Extract files over current files'),
|
||||
new ParamOption(DUPX_Extraction::ACTION_REMOVE_WP_FILES, 'Remove WP core and content and extract', $statusRemoveActions),
|
||||
new ParamOption(DUPX_Extraction::ACTION_REMOVE_ALL_FILES, 'Remove all files except addon sites and extract', $statusRemoveActions),
|
||||
new ParamOption(DUPX_Extraction::ACTION_REMOVE_UPLOADS, 'Empty only uploads folder', $statusRemoveActions),
|
||||
],
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'subNote' => fn($param) => dupxTplRender('parts/params/archive-action-notes', [
|
||||
'currentAction' => $param->getValue(),
|
||||
], false),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES] = new ParamForm(
|
||||
PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => DUPX_Extraction::FILTER_NONE,
|
||||
'acceptValues' => [
|
||||
DUPX_Extraction::FILTER_NONE,
|
||||
DUPX_Extraction::FILTER_SKIP_WP_CORE,
|
||||
DUPX_Extraction::FILTER_SKIP_CORE_PLUG_THEMES,
|
||||
DUPX_Extraction::FILTER_ONLY_MEDIA_PLUG_THEMES,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Skip Files:',
|
||||
'status' => function ($paramObj): string {
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
new ParamOption(DUPX_Extraction::FILTER_NONE, 'Extract all files'),
|
||||
new ParamOption(DUPX_Extraction::FILTER_SKIP_WP_CORE, 'Skip extraction of WP core files'),
|
||||
new ParamOption(
|
||||
DUPX_Extraction::FILTER_SKIP_CORE_PLUG_THEMES,
|
||||
'Skip extraction of WP core files and plugins/themes existing on the host'
|
||||
),
|
||||
new ParamOption(DUPX_Extraction::FILTER_ONLY_MEDIA_PLUG_THEMES, 'Extract only media files and new plugins and themes'),
|
||||
],
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'subNote' => dupxTplRender('parts/params/extract-skip-notes', [
|
||||
'currentSkipMode' => DUPX_Extraction::FILTER_NONE,
|
||||
], false),
|
||||
]
|
||||
);
|
||||
|
||||
$engineOptions = self::getArchiveEngineOptions();
|
||||
|
||||
$params[PrmMng::PARAM_ARCHIVE_ENGINE] = new ParamForm(
|
||||
PrmMng::PARAM_ARCHIVE_ENGINE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => $engineOptions['default'],
|
||||
'acceptValues' => $engineOptions['acceptValues'],
|
||||
'sanitizeCallback' => function ($value) {
|
||||
if (
|
||||
PrmMng::getInstance()->getValue(
|
||||
PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES
|
||||
) !== DUPX_Extraction::FILTER_NONE && $value === DUPX_Extraction::ENGINE_ZIP_SHELL
|
||||
) {
|
||||
return DUPX_Extraction::ENGINE_ZIP_CHUNK;
|
||||
}
|
||||
return $value;
|
||||
},
|
||||
],
|
||||
[
|
||||
'label' => 'Extraction Mode:',
|
||||
'options' => $engineOptions['options'],
|
||||
'size' => 0,
|
||||
'subNote' => $engineOptions['subNote'],
|
||||
'attr' => ['onchange' => 'DUPX.onSafeModeSwitch();'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_ZIP_THROTTLING] = new ParamForm(
|
||||
PrmMng::PARAM_ZIP_THROTTLING,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Server Throttling:',
|
||||
'checkboxLabel' => 'Enable archive extraction throttling',
|
||||
'status' => function (): string {
|
||||
if (
|
||||
PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE) === DUPX_Extraction::ENGINE_ZIP
|
||||
|| PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE) === DUPX_Extraction::ENGINE_ZIP_CHUNK
|
||||
) {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_ACTION] = new ParamForm(
|
||||
PrmMng::PARAM_DB_ACTION,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => DUPX_DBInstall::DBACTION_EMPTY,
|
||||
'acceptValues' => [
|
||||
DUPX_DBInstall::DBACTION_CREATE,
|
||||
DUPX_DBInstall::DBACTION_EMPTY,
|
||||
DUPX_DBInstall::DBACTION_REMOVE_ONLY_TABLES,
|
||||
DUPX_DBInstall::DBACTION_RENAME,
|
||||
DUPX_DBInstall::DBACTION_MANUAL,
|
||||
DUPX_DBInstall::DBACTION_ONLY_CONNECT,
|
||||
DUPX_DBInstall::DBACTION_DO_NOTHING,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Action:',
|
||||
'status' => function ($paramObj): string {
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'options' => [
|
||||
new ParamOption(
|
||||
DUPX_DBInstall::DBACTION_CREATE,
|
||||
'Create New Database',
|
||||
function (): string {
|
||||
if (InstState::getInstance()->getMode() === InstState::MODE_STD_INSTALL) {
|
||||
return ParamOption::OPT_ENABLED;
|
||||
} else {
|
||||
return ParamOption::OPT_DISABLED;
|
||||
}
|
||||
}
|
||||
),
|
||||
new ParamOption(DUPX_DBInstall::DBACTION_EMPTY, 'Empty Database'),
|
||||
new ParamOption(DUPX_DBInstall::DBACTION_REMOVE_ONLY_TABLES, 'Overwrite Existing Tables'),
|
||||
new ParamOption(DUPX_DBInstall::DBACTION_MANUAL, 'Skip Database Extraction'),
|
||||
new ParamOption(DUPX_DBInstall::DBACTION_RENAME, 'Backup and Rename Existing Tables'),
|
||||
new ParamOption(DUPX_DBInstall::DBACTION_DO_NOTHING, 'Only Extract Files'),
|
||||
new ParamOption(DUPX_DBInstall::DBACTION_ONLY_CONNECT, 'Do Nothing (Advanced)', ParamOption::OPT_HIDDEN),
|
||||
],
|
||||
'subNote' => dupxTplRender('parts/params/db-action-notes', [], false),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_ENGINE] = new ParamForm(
|
||||
PrmMng::PARAM_DB_ENGINE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => DUPX_DBInstall::ENGINE_CHUNK,
|
||||
'acceptValues' => [
|
||||
DUPX_DBInstall::ENGINE_CHUNK,
|
||||
DUPX_DBInstall::ENGINE_NORMAL,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Processing:',
|
||||
'size' => 0,
|
||||
'options' => [
|
||||
new ParamOption(DUPX_DBInstall::ENGINE_CHUNK, 'Chunking mode'),
|
||||
new ParamOption(DUPX_DBInstall::ENGINE_NORMAL, 'Single step'),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DB_CHUNK] = new ParamItem(
|
||||
PrmMng::PARAM_DB_CHUNK,
|
||||
ParamForm::TYPE_BOOL,
|
||||
[
|
||||
'default' => ($params[PrmMng::PARAM_DB_ENGINE]->getValue() === DUPX_DBInstall::ENGINE_CHUNK),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_REPLACE_ENGINE] = new ParamItem(
|
||||
PrmMng::PARAM_REPLACE_ENGINE,
|
||||
ParamForm::TYPE_INT,
|
||||
[
|
||||
'default' => \DUPX_S3_Funcs::MODE_CHUNK,
|
||||
'acceptValues' => [
|
||||
\DUPX_S3_Funcs::MODE_NORMAL,
|
||||
\DUPX_S3_Funcs::MODE_CHUNK,
|
||||
\DUPX_S3_Funcs::MODE_SKIP,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$oldHomePath = DUPX_ArchiveConfig::getInstance()->getRealValue('archivePaths')->home;
|
||||
$params[PrmMng::PARAM_SKIP_PATH_REPLACE] = new ParamForm(
|
||||
PrmMng::PARAM_SKIP_PATH_REPLACE,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => in_array($oldHomePath, self::AUTO_SKIP_PATH_REPLACE_LIST),
|
||||
],
|
||||
[
|
||||
'label' => 'Skip path replace:',
|
||||
'checkboxLabel' => 'Skips the replacement of the source path',
|
||||
'status' => function (ParamForm $paramObj): string {
|
||||
$sourcePath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_OLD);
|
||||
if (strlen($sourcePath) == 0) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
if (
|
||||
$params[PrmMng::PARAM_ARCHIVE_ACTION]->getStatus() !== ParamItem::STATUS_OVERWRITE &&
|
||||
InstState::isRestoreBackup($params[PrmMng::PARAM_INST_TYPE]->getValue()) &&
|
||||
PComponents::isFullFilesComponents($archiveConfig->components) &&
|
||||
$archiveConfig->mu_is_filtered === false
|
||||
) {
|
||||
Log::info('IS RESTORE BACKUP WITH FULL FILES COMPONENTS: SET ARCHIVE ACTION TO REMOVE WP FILES');
|
||||
$params[PrmMng::PARAM_ARCHIVE_ACTION]->setValue(DUPX_Extraction::ACTION_REMOVE_WP_FILES);
|
||||
}
|
||||
|
||||
if (
|
||||
$params[PrmMng::PARAM_DB_ACTION]->getStatus() !== ParamItem::STATUS_OVERWRITE &&
|
||||
InstState::isRestoreBackup($params[PrmMng::PARAM_INST_TYPE]->getValue()) &&
|
||||
$archiveConfig->dbInfo->tablesBaseCount != $archiveConfig->dbInfo->tablesFinalCount &&
|
||||
$archiveConfig->mu_is_filtered === false
|
||||
) {
|
||||
Log::info('IS RESTORE BACKUP WITH PARTIAL DB: SET DB ACTION TO REMOVE ONLY TABLES');
|
||||
$params[PrmMng::PARAM_DB_ACTION]->setValue(DUPX_DBInstall::DBACTION_REMOVE_ONLY_TABLES);
|
||||
}
|
||||
|
||||
if (
|
||||
InstState::dbDoNothing() ||
|
||||
InstState::isRestoreBackup($params[PrmMng::PARAM_INST_TYPE]->getValue())
|
||||
) {
|
||||
$default = \DUPX_S3_Funcs::MODE_SKIP;
|
||||
} elseif ($params[PrmMng::PARAM_DB_ENGINE]->getValue() === DUPX_DBInstall::ENGINE_CHUNK) {
|
||||
$default = \DUPX_S3_Funcs::MODE_CHUNK;
|
||||
} else {
|
||||
$default = \DUPX_S3_Funcs::MODE_NORMAL;
|
||||
}
|
||||
$params[PrmMng::PARAM_REPLACE_ENGINE]->setValue($default);
|
||||
|
||||
if (
|
||||
($params[PrmMng::PARAM_ARCHIVE_ENGINE]->getValue() === DUPX_Extraction::ENGINE_ZIP
|
||||
|| $params[PrmMng::PARAM_ARCHIVE_ENGINE]->getValue() === DUPX_Extraction::ENGINE_ZIP_CHUNK)
|
||||
&& \DUPX_Custom_Host_Manager::getInstance()->isHosting(\DUPX_Custom_Host_Manager::HOST_SITEGROUND)
|
||||
) {
|
||||
$params[PrmMng::PARAM_ZIP_THROTTLING]->setValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get db chunk engine value
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function getDbChunkFromParams(): bool
|
||||
{
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_ENGINE) === DUPX_DBInstall::ENGINE_CHUNK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get replace engine mode
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getReplaceEngineModeFromParams(): int
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
if (InstState::dbDoNothing() || InstState::isRestoreBackup()) {
|
||||
return \DUPX_S3_Funcs::MODE_SKIP;
|
||||
} elseif ($paramsManager->getValue(PrmMng::PARAM_DB_ENGINE) === DUPX_DBInstall::ENGINE_CHUNK) {
|
||||
return \DUPX_S3_Funcs::MODE_CHUNK;
|
||||
} else {
|
||||
return \DUPX_S3_Funcs::MODE_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get archive engine options
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function getArchiveEngineOptions(): array
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$acceptValues = [];
|
||||
$subNote = null;
|
||||
if (($manualEnable = \DUPX_Conf_Utils::isManualExtractFilePresent()) === true) {
|
||||
$acceptValues[] = DUPX_Extraction::ENGINE_MANUAL;
|
||||
} else {
|
||||
$subNote = <<<SUBNOTEHTML
|
||||
* Option enabled when archive has been pre-extracted
|
||||
<a href="https://duplicator.com/knowledge-base/how-to-handle-various-install-scenarios" target="_blank">[more info]</a>
|
||||
SUBNOTEHTML;
|
||||
}
|
||||
if (($zipEnable = ($archiveConfig->isZipArchive() && \DUPX_Conf_Utils::archiveExists() && \DUPX_Conf_Utils::isPhpZipAvailable())) === true) {
|
||||
$acceptValues[] = DUPX_Extraction::ENGINE_ZIP;
|
||||
$acceptValues[] = DUPX_Extraction::ENGINE_ZIP_CHUNK;
|
||||
}
|
||||
if (($shellZipEnable = ($archiveConfig->isZipArchive() && \DUPX_Conf_Utils::archiveExists() && \DUPX_Conf_Utils::isShellZipAvailable())) === true) {
|
||||
$acceptValues[] = DUPX_Extraction::ENGINE_ZIP_SHELL;
|
||||
}
|
||||
if (($dupEnable = (!$archiveConfig->isZipArchive() && \DUPX_Conf_Utils::archiveExists())) === true) {
|
||||
$acceptValues[] = DUPX_Extraction::ENGINE_DUP;
|
||||
}
|
||||
|
||||
$options = [];
|
||||
$options[] = new ParamOption(
|
||||
DUPX_Extraction::ENGINE_MANUAL,
|
||||
'Manual Archive Extraction',
|
||||
$manualEnable ? ParamOption::OPT_ENABLED : ParamOption::OPT_DISABLED
|
||||
);
|
||||
|
||||
if ($archiveConfig->isZipArchive()) {
|
||||
//ZIP-ARCHIVE
|
||||
$options[] = new ParamOption(
|
||||
DUPX_Extraction::ENGINE_ZIP,
|
||||
'PHP ZipArchive',
|
||||
$zipEnable ? ParamOption::OPT_ENABLED : ParamOption::OPT_DISABLED
|
||||
);
|
||||
|
||||
$options[] = new ParamOption(
|
||||
DUPX_Extraction::ENGINE_ZIP_CHUNK,
|
||||
'PHP ZipArchive Chunking',
|
||||
$zipEnable ? ParamOption::OPT_ENABLED : ParamOption::OPT_DISABLED
|
||||
);
|
||||
|
||||
$options[] = new ParamOption(
|
||||
DUPX_Extraction::ENGINE_ZIP_SHELL,
|
||||
'Shell Exec Unzip',
|
||||
function (): string {
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
$pathsMapping = $archiveConfig->getPathsMapping();
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES) !== DUPX_Extraction::FILTER_NONE) {
|
||||
return ParamOption::OPT_DISABLED;
|
||||
}
|
||||
if (is_array($pathsMapping) && count($pathsMapping) > 1) {
|
||||
return ParamOption::OPT_DISABLED;
|
||||
}
|
||||
if ($archiveConfig->isZipArchive() && \DUPX_Conf_Utils::archiveExists() && \DUPX_Conf_Utils::isShellZipAvailable()) {
|
||||
return ParamOption::OPT_ENABLED;
|
||||
}
|
||||
return ParamOption::OPT_DISABLED;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// DUPARCHIVE
|
||||
$options[] = new ParamOption(
|
||||
DUPX_Extraction::ENGINE_DUP,
|
||||
'DupArchive',
|
||||
$dupEnable ? ParamOption::OPT_ENABLED : ParamOption::OPT_DISABLED
|
||||
);
|
||||
}
|
||||
|
||||
if ($manualEnable) {
|
||||
$default = DUPX_Extraction::ENGINE_MANUAL;
|
||||
} elseif ($zipEnable) {
|
||||
$default = DUPX_Extraction::ENGINE_ZIP_CHUNK;
|
||||
} elseif ($shellZipEnable) {
|
||||
$default = DUPX_Extraction::ENGINE_ZIP_SHELL;
|
||||
} elseif ($dupEnable) {
|
||||
$default = DUPX_Extraction::ENGINE_DUP;
|
||||
} else {
|
||||
$default = null;
|
||||
}
|
||||
|
||||
return [
|
||||
'options' => $options,
|
||||
'acceptValues' => $acceptValues,
|
||||
'default' => $default,
|
||||
'subNote' => $subNote,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormPass;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapServer;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_ArchiveConfig;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescGeneric implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$newObj = new ParamForm(
|
||||
PrmMng::PARAM_FILE_PERMS_VALUE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '644',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => '/^[ugorwx,\s\+\-0-7]+$/', // octal + ugo rwx,
|
||||
],
|
||||
[
|
||||
'label' => 'File permissions',
|
||||
'renderLabel' => false,
|
||||
'status' => SnapServer::isWindows() ? ParamForm::STATUS_SKIP : ParamForm::STATUS_ENABLED,
|
||||
'wrapperClasses' => ['display-inline-block'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_FILE_PERMS_VALUE] = $newObj;
|
||||
$permItemId = $newObj->getFormItemId();
|
||||
$params[PrmMng::PARAM_SET_FILE_PERMS] = new ParamForm(
|
||||
PrmMng::PARAM_SET_FILE_PERMS,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_SWITCH,
|
||||
[
|
||||
'default' => !SnapServer::isWindows(),
|
||||
],
|
||||
[
|
||||
'status' => SnapServer::isWindows() ? ParamForm::STATUS_SKIP : ParamForm::STATUS_ENABLED,
|
||||
'label' => 'File permissions:',
|
||||
'checkboxLabel' => 'All files',
|
||||
'wrapperClasses' => ['display-inline-block'],
|
||||
'attr' => [
|
||||
'onclick' => "jQuery('#" . $permItemId . "').prop('disabled', !jQuery(this).is(':checked'));",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$newObj = new ParamForm(
|
||||
PrmMng::PARAM_DIR_PERMS_VALUE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '755',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => '/^[ugorwx,\s\+\-0-7]+$/', // octal + ugo rwx
|
||||
],
|
||||
[
|
||||
'label' => 'Folder permissions',
|
||||
'renderLabel' => false,
|
||||
'status' => SnapServer::isWindows() ? ParamForm::STATUS_SKIP : ParamForm::STATUS_ENABLED,
|
||||
'wrapperClasses' => ['display-inline-block'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_DIR_PERMS_VALUE] = $newObj;
|
||||
$permItemId = $newObj->getFormItemId();
|
||||
$params[PrmMng::PARAM_SET_DIR_PERMS] = new ParamForm(
|
||||
PrmMng::PARAM_SET_DIR_PERMS,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_SWITCH,
|
||||
[
|
||||
'default' => !SnapServer::isWindows(),
|
||||
],
|
||||
[
|
||||
'status' => SnapServer::isWindows() ? ParamForm::STATUS_SKIP : ParamForm::STATUS_ENABLED,
|
||||
'label' => 'Dir permissions:',
|
||||
'checkboxLabel' => 'All Directories',
|
||||
'wrapperClasses' => ['display-inline-block'],
|
||||
'attr' => [
|
||||
'onclick' => "jQuery('#" . $permItemId . "').prop('disabled', !jQuery(this).is(':checked'));",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_SAFE_MODE] = new ParamForm(
|
||||
PrmMng::PARAM_SAFE_MODE,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => 0,
|
||||
'acceptValues' => [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Safe Mode:',
|
||||
'status' => function (ParamItem $paramObj): string {
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'options' => [
|
||||
new ParamOption(0, 'Disabled'),
|
||||
new ParamOption(1, 'Enabled'),
|
||||
],
|
||||
'attr' => ['onchange' => 'DUPX.onSafeModeSwitch();'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_FILE_TIME] = new ParamForm(
|
||||
PrmMng::PARAM_FILE_TIME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_RADIO,
|
||||
[
|
||||
'default' => 'current',
|
||||
'acceptValues' => [
|
||||
'current',
|
||||
'original',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'File Times:',
|
||||
'status' => ParamForm::STATUS_ENABLED,
|
||||
'options' => [
|
||||
new ParamOption('current', 'Current', ParamOption::OPT_ENABLED, ['title' => 'Set the files current date time to now']),
|
||||
new ParamOption('original', 'Original', ParamOption::OPT_ENABLED, ['title' => 'Keep the files date time the same']),
|
||||
],
|
||||
'subNote' => 'This option is not supported for extraction mode Shell Exec Unzip',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_LOGGING] = new ParamForm(
|
||||
PrmMng::PARAM_LOGGING,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_RADIO,
|
||||
[
|
||||
'default' => Log::LV_DEFAULT,
|
||||
'acceptValues' => [
|
||||
Log::LV_DEFAULT,
|
||||
Log::LV_DETAILED,
|
||||
Log::LV_DEBUG,
|
||||
Log::LV_HARD_DEBUG,
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Logging:',
|
||||
'options' => [
|
||||
new ParamOption(Log::LV_DEFAULT, 'Light'),
|
||||
new ParamOption(Log::LV_DETAILED, 'Detailed'),
|
||||
new ParamOption(Log::LV_DEBUG, 'Debug'),
|
||||
// enabled only with overwrite params
|
||||
new ParamOption(Log::LV_HARD_DEBUG, 'Hard debug', ParamOption::OPT_HIDDEN),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_REMOVE_RENDUNDANT] = new ParamForm(
|
||||
PrmMng::PARAM_REMOVE_RENDUNDANT,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Cleanup:',
|
||||
'checkboxLabel' => 'Remove disabled plugins/themes',
|
||||
'wrapperClasses' => ['requires-db-hide'],
|
||||
'status' => function (ParamItem $paramObj): string {
|
||||
if (InstState::isRestoreBackup() || InstState::isAddSiteOnMultisite()) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_REMOVE_USERS_WITHOUT_PERMISSIONS] = new ParamForm(
|
||||
PrmMng::PARAM_REMOVE_USERS_WITHOUT_PERMISSIONS,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => ' ',
|
||||
'checkboxLabel' => 'Remove users without permissions',
|
||||
'wrapperClasses' => ['requires-db-hide'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_RECOVERY_LINK] = new ParamItem(
|
||||
PrmMng::PARAM_RECOVERY_LINK,
|
||||
ParamFormPass::TYPE_STRING,
|
||||
['default' => '']
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_FROM_SITE_IMPORT_INFO] = new ParamItem(
|
||||
PrmMng::PARAM_FROM_SITE_IMPORT_INFO,
|
||||
ParamFormPass::TYPE_ARRAY_MIXED,
|
||||
[
|
||||
'default' => [],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_AUTO_CLEAN_INSTALLER_FILES] = new ParamForm(
|
||||
PrmMng::PARAM_AUTO_CLEAN_INSTALLER_FILES,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => true],
|
||||
[
|
||||
'label' => 'CLean installation files',
|
||||
'renderLabel' => false,
|
||||
'checkboxLabel' => 'Auto delete installer files after login to secure site (recommended!)',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Multisite params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormSitesOwrMap;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormURLMapping;
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use DUPX_ArchiveConfig;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescMultisite implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$archive_config = \DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$params[PrmMng::PARAM_SUBSITE_ID] = new ParamForm(
|
||||
PrmMng::PARAM_SUBSITE_ID,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => -1,
|
||||
'acceptValues' => [
|
||||
self::class,
|
||||
'getSubSiteIdsAcceptValues',
|
||||
],
|
||||
],
|
||||
[
|
||||
'status' => function (ParamItem $paramObj): string {
|
||||
if (
|
||||
InstState::isInstType(
|
||||
[InstState::TYPE_STANDALONE]
|
||||
)
|
||||
) {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
},
|
||||
'label' => 'Subsite:',
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
'options' => [
|
||||
self::class,
|
||||
'getSubSiteIdsOptions',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING] = new ParamFormSitesOwrMap(
|
||||
PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING,
|
||||
ParamFormSitesOwrMap::TYPE_ARRAY_SITES_OWR_MAP,
|
||||
ParamFormSitesOwrMap::FORM_TYPE_SITES_OWR_MAP,
|
||||
[
|
||||
'default' => [],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
/** @var SiteOwrMap[] $value */
|
||||
|
||||
if (!InstState::isAddSiteOnMultisite()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$mainSiteURL = $overwriteData['urls']['home'];
|
||||
$subdomain = (isset($overwriteData['subdomain']) && $overwriteData['subdomain']);
|
||||
$newFullURLs = [];
|
||||
|
||||
foreach ($value as $map) {
|
||||
switch ($map->getTargetId()) {
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_SLUG:
|
||||
if (($newFullUrl = $map->getNewSlugFullUrl($mainSiteURL, $subdomain)) == false) {
|
||||
$paramObj->setInvalidMessage('New sub site can\'t have new ' . ($subdomain ? 'subdomain' : 'subpath') . ' empty');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN:
|
||||
if (($newFullUrl = $map->getNewSlugFullUrl($mainSiteURL, $subdomain)) == false) {
|
||||
$paramObj->setInvalidMessage('New sub site URL can\'t be empty');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue 2;
|
||||
}
|
||||
|
||||
$newFullURLs[] = $newFullUrl;
|
||||
foreach ($overwriteData['subsites'] as $subsite) {
|
||||
$subsiteFullUrl = $subsite['domain'] . $subsite['path'];
|
||||
if (strcmp($newFullUrl, $subsiteFullUrl) === 0) {
|
||||
$paramObj->setInvalidMessage('New subsite URL already exists');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($newFullURLs) !== count(array_unique($newFullURLs))) {
|
||||
$paramObj->setInvalidMessage('Different new sub-sites cannot have the same URL ');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
'label' => 'Overwrite mapping',
|
||||
'renderLabel' => false,
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_MU_REPLACE] = new ParamFormURLMapping(
|
||||
PrmMng::PARAM_MU_REPLACE,
|
||||
ParamFormURLMapping::TYPE_ARRAY_SITES_OWR_MAP,
|
||||
ParamFormURLMapping::FORM_TYPE_URL_MAPPING,
|
||||
[
|
||||
'default' => [],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
/** @var SiteOwrMap[] $value */
|
||||
|
||||
if (!InstState::isMultisiteInstall()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$config = DUPX_ArchiveConfig::getInstance();
|
||||
$subdomain = $config->isSubdomain();
|
||||
|
||||
foreach ($value as $map) {
|
||||
switch ($map->getTargetId()) {
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_SLUG:
|
||||
if (strlen($map->getNewSlug()) == 0) {
|
||||
$paramObj->setInvalidMessage('New sub site can\'t have new ' . ($subdomain ? 'subdomain' : 'subpath') . ' empty');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN:
|
||||
if (strlen($map->getNewSlug()) == 0) {
|
||||
$paramObj->setInvalidMessage('New sub site URL can\'t be empty');
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$paramObj->setInvalidMessage('Invalid param');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
'label' => 'URLs mapping',
|
||||
'renderLabel' => false,
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_MULTISITE_CROSS_SEARCH] = new ParamForm(
|
||||
PrmMng::PARAM_MULTISITE_CROSS_SEARCH,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => (count($archive_config->subsites) <= MAX_SITES_TO_DEFAULT_ENABLE_CORSS_SEARCH),
|
||||
],
|
||||
[
|
||||
'status' => function ($paramObj): string {
|
||||
if (InstState::isNewSiteIsMultisite()) {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_SKIP;
|
||||
}
|
||||
},
|
||||
'label' => 'Database search:',
|
||||
'checkboxLabel' => 'Cross-search between the sites of the network.',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overwrite map by source id
|
||||
*
|
||||
* @param int $sourceId subsite source id
|
||||
*
|
||||
* @return SiteOwrMap|bool false if don't exists
|
||||
*/
|
||||
public static function getOwrMapBySourceId($sourceId)
|
||||
{
|
||||
static $indexCache = [];
|
||||
|
||||
if (!isset($indexCache[$sourceId])) {
|
||||
/** @var SiteOwrMap[] $overwriteMapping */
|
||||
$overwriteMapping = PrmMng::getInstance()->getValue(PrmMng::PARAM_SUBSITE_OVERWRITE_MAPPING);
|
||||
|
||||
foreach ($overwriteMapping as $map) {
|
||||
if ($map->getSourceId() == $sourceId) {
|
||||
$indexCache[$sourceId] = $map;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isset($indexCache[$sourceId])) {
|
||||
$indexCache[$sourceId] = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $indexCache[$sourceId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getSubSiteIdsOptions(): array
|
||||
{
|
||||
$archive_config = \DUPX_ArchiveConfig::getInstance();
|
||||
$options = [];
|
||||
foreach ($archive_config->subsites as $subsite) {
|
||||
$label = $subsite->domain . $subsite->path;
|
||||
$options[] = new ParamOption($subsite->id, $label, ParamFormSitesOwrMap::getSourceIdOptionStatus($subsite));
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getSubSiteIdsAcceptValues(): array
|
||||
{
|
||||
$archive_config = \DUPX_ArchiveConfig::getInstance();
|
||||
$acceptValues = [-1];
|
||||
foreach ($archive_config->subsites as $subsite) {
|
||||
if (ParamFormSitesOwrMap::isQualifiedSourceIdForImport($subsite)) {
|
||||
$acceptValues[] = $subsite->id;
|
||||
}
|
||||
}
|
||||
return $acceptValues;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* New admin params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormPass;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescNewAdmin implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_WP_ADMIN_CREATE_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_WP_ADMIN_CREATE_NEW,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_SWITCH,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Create New User:',
|
||||
'status' => function ($paramObj): string {
|
||||
if (ParamDescUsers::getUsersMode() != ParamDescUsers::USER_MODE_OVERWRITE) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'checkboxLabel' => '',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADMIN_NAME] = new ParamForm(
|
||||
PrmMng::PARAM_WP_ADMIN_NAME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
if (!PrmMng::getInstance()->getValue(PrmMng::PARAM_WP_ADMIN_CREATE_NEW)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen($value) < 4) {
|
||||
$paramObj->setInvalidMessage('Must have 4 or more characters');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
'status' => [
|
||||
self::class,
|
||||
'getStatuOfNewAdminParams',
|
||||
],
|
||||
'label' => 'Username:',
|
||||
'classes' => 'new-admin-field',
|
||||
'attr' => [
|
||||
'title' => '4 characters minimum',
|
||||
'placeholder' => "(4 or more characters)",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADMIN_PASSWORD] = new ParamFormPass(
|
||||
PrmMng::PARAM_WP_ADMIN_PASSWORD,
|
||||
ParamFormPass::TYPE_STRING,
|
||||
ParamFormPass::FORM_TYPE_PWD_TOGGLE,
|
||||
[
|
||||
'default' => \DUPX_ArchiveConfig::getInstance()->cpnl_pass,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
if (!PrmMng::getInstance()->getValue(PrmMng::PARAM_WP_ADMIN_CREATE_NEW)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen($value) < \DUPX_Constants::MIN_NEW_PASSWORD_LEN) {
|
||||
$paramObj->setInvalidMessage('Must have ' . \DUPX_Constants::MIN_NEW_PASSWORD_LEN . ' or more characters');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
'status' => [
|
||||
self::class,
|
||||
'getStatuOfNewAdminParams',
|
||||
],
|
||||
'label' => 'Password:',
|
||||
'classes' => [
|
||||
'strength-pwd-check',
|
||||
'new-admin-field',
|
||||
],
|
||||
'attr' => [
|
||||
'placeholder' => '(' . \DUPX_Constants::MIN_NEW_PASSWORD_LEN . ' or more characters)',
|
||||
'title' => \DUPX_Constants::MIN_NEW_PASSWORD_LEN . ' characters minimum',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADMIN_MAIL] = new ParamForm(
|
||||
PrmMng::PARAM_WP_ADMIN_MAIL,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
if (!PrmMng::getInstance()->getValue(PrmMng::PARAM_WP_ADMIN_CREATE_NEW)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen($value) < 4) {
|
||||
$paramObj->setInvalidMessage('Email name must have 4 or more characters');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_var($value, FILTER_VALIDATE_EMAIL) == false) {
|
||||
$paramObj->setInvalidMessage('Email "' . $value . '" isn\'t valid');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[
|
||||
'status' => [
|
||||
self::class,
|
||||
'getStatuOfNewAdminParams',
|
||||
],
|
||||
'label' => 'Email:',
|
||||
'classes' => 'new-admin-field',
|
||||
'attr' => [
|
||||
'title' => '4 characters minimum',
|
||||
'placeholder' => "(4 or more characters)",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADMIN_NICKNAME] = new ParamForm(
|
||||
PrmMng::PARAM_WP_ADMIN_NICKNAME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
],
|
||||
[
|
||||
'status' => [
|
||||
self::class,
|
||||
'getStatuOfNewAdminParams',
|
||||
],
|
||||
'label' => 'Nickname:',
|
||||
'classes' => 'new-admin-field',
|
||||
'attr' => [
|
||||
'title' => 'if username is empty',
|
||||
'placeholder' => "(if username is empty)",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADMIN_FIRST_NAME] = new ParamForm(
|
||||
PrmMng::PARAM_WP_ADMIN_FIRST_NAME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
],
|
||||
[
|
||||
'status' => [
|
||||
self::class,
|
||||
'getStatuOfNewAdminParams',
|
||||
],
|
||||
'label' => 'First Name:',
|
||||
'classes' => 'new-admin-field',
|
||||
'attr' => [
|
||||
'title' => 'optional',
|
||||
'placeholder' => "(optional)",
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADMIN_LAST_NAME] = new ParamForm(
|
||||
PrmMng::PARAM_WP_ADMIN_LAST_NAME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
],
|
||||
[
|
||||
'status' => [
|
||||
self::class,
|
||||
'getStatuOfNewAdminParams',
|
||||
],
|
||||
'label' => 'Last Name:',
|
||||
'classes' => 'new-admin-field',
|
||||
'attr' => [
|
||||
'title' => 'optional',
|
||||
'placeholder' => "(optional)",
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStatuOfNewAdminParams(): string
|
||||
{
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_WP_ADMIN_CREATE_NEW)) {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plugins params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormPlugins;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescPlugins implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_PLUGINS] = new ParamFormPlugins(
|
||||
PrmMng::PARAM_PLUGINS,
|
||||
ParamFormPlugins::TYPE_ARRAY_STRING,
|
||||
ParamFormPlugins::FORM_TYPE_PLUGINS_SELECT,
|
||||
[
|
||||
'default' => [],
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Plugins',
|
||||
'renderLabel' => false,
|
||||
'status' => function ($paramObj): string {
|
||||
if (
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_IGNORE_PLUGINS] = new ParamItem(
|
||||
PrmMng::PARAM_IGNORE_PLUGINS,
|
||||
ParamItem::TYPE_ARRAY_STRING,
|
||||
[
|
||||
'default' => [],
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_FORCE_DIABLE_PLUGINS] = new ParamItem(
|
||||
PrmMng::PARAM_FORCE_DIABLE_PLUGINS,
|
||||
ParamItem::TYPE_ARRAY_STRING,
|
||||
[
|
||||
'default' => [],
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Replace params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescReplace implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_BLOGNAME] = new ParamForm(
|
||||
PrmMng::PARAM_BLOGNAME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => function ($value): string {
|
||||
$value = SnapUtil::sanitizeNSCharsNewline($value);
|
||||
return htmlspecialchars_decode((empty($value) ? 'No Blog Title Set' : $value), ENT_QUOTES);
|
||||
},
|
||||
],
|
||||
[
|
||||
'label' => 'Site Title:',
|
||||
'status' => function ($paramObj): string {
|
||||
if (InstState::isRestoreBackup()) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'requires-db-hide',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CUSTOM_SEARCH] = new ParamItem(
|
||||
PrmMng::PARAM_CUSTOM_SEARCH,
|
||||
ParamForm::TYPE_ARRAY_STRING,
|
||||
[
|
||||
'default' => [],
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_CUSTOM_REPLACE] = new ParamItem(
|
||||
PrmMng::PARAM_CUSTOM_REPLACE,
|
||||
ParamForm::TYPE_ARRAY_STRING,
|
||||
[
|
||||
'default' => [],
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_EMPTY_SCHEDULE_STORAGE] = new ParamForm(
|
||||
PrmMng::PARAM_EMPTY_SCHEDULE_STORAGE,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => true],
|
||||
[
|
||||
'label' => 'Cleanup:',
|
||||
'checkboxLabel' => 'Remove schedules and storage endpoints',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_EMAIL_REPLACE] = new ParamForm(
|
||||
PrmMng::PARAM_EMAIL_REPLACE,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Email Domains:',
|
||||
'checkboxLabel' => 'Update',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_FULL_SEARCH] = new ParamForm(
|
||||
PrmMng::PARAM_FULL_SEARCH,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Database Search:',
|
||||
'checkboxLabel' => 'Full Search Mode',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_POSTGUID] = new ParamForm(
|
||||
PrmMng::PARAM_POSTGUID,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Post GUID:',
|
||||
'checkboxLabel' => 'Keep Unchanged',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_MAX_SERIALIZE_CHECK] = new ParamForm(
|
||||
PrmMng::PARAM_MAX_SERIALIZE_CHECK,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_NUMBER,
|
||||
[
|
||||
'default' => \DUPX_Constants::DEFAULT_MAX_STRLEN_SERIALIZED_CHECK_IN_M,
|
||||
],
|
||||
[
|
||||
'min' => 0,
|
||||
'max' => 99,
|
||||
'step' => 1,
|
||||
'wrapperClasses' => ['small'],
|
||||
'label' => 'Serialized obj max size:',
|
||||
'postfix' => [
|
||||
'type' => 'label',
|
||||
'label' => 'MB',
|
||||
],
|
||||
'subNote' => 'If the serialized object stored in the database exceeds this size, it will not be parsed for replacement.'
|
||||
. '<br><b>Too large a size in low memory installations can generate a fatal error.</b>',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
if ($params[PrmMng::PARAM_BLOGNAME]->getStatus() !== ParamItem::STATUS_OVERWRITE) {
|
||||
$params[PrmMng::PARAM_BLOGNAME]->setValue(\DUPX_ArchiveConfig::getInstance()->getBlognameFromSelectedSubsiteId());
|
||||
}
|
||||
|
||||
$installType = $params[PrmMng::PARAM_INST_TYPE]->getValue();
|
||||
if (InstState::isRestoreBackup($installType)) {
|
||||
$params[PrmMng::PARAM_EMPTY_SCHEDULE_STORAGE]->setValue(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Generic params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormPass;
|
||||
use DUPX_ArchiveConfig;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_View_Funcs;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescSecurity implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_SECURE_PASS] = new ParamFormPass(
|
||||
PrmMng::PARAM_SECURE_PASS,
|
||||
ParamFormPass::TYPE_STRING,
|
||||
ParamFormPass::FORM_TYPE_PWD_TOGGLE,
|
||||
[
|
||||
'persistence' => false,
|
||||
'default' => null,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewline',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Password:',
|
||||
'status' => function (ParamForm $param): string {
|
||||
if (Security::getInstance()->getSecurityType() == Security::SECURITY_PASSWORD) {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
},
|
||||
'wrapperClasses' => 'margin-bottom-2',
|
||||
'attr' => [
|
||||
'placeholder' => (DUPX_ArchiveConfig::getInstance()->secure_on ? '' : 'Password not enabled'),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_SECURE_ARCHIVE_HASH] = new ParamForm(
|
||||
PrmMng::PARAM_SECURE_ARCHIVE_HASH,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'persistence' => false,
|
||||
'default' => null,
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'Backup File Name:',
|
||||
'status' => function (ParamForm $param): string {
|
||||
if (!InstState::isOverwrite()) {
|
||||
return ParamForm::STATUS_SKIP;
|
||||
} elseif (Security::getInstance()->getSecurityType() == Security::SECURITY_ARCHIVE) {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
},
|
||||
'wrapperClasses' => 'margin-bottom-4',
|
||||
'attr' => ['placeholder' => 'example: [full-unique-name]_archive.zip'],
|
||||
'subNote' => DUPX_View_Funcs::helpLink('secure', 'How to get archive file name?', false),
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_SECURE_OK] = new ParamItem(
|
||||
PrmMng::PARAM_SECURE_OK,
|
||||
ParamForm::TYPE_BOOL,
|
||||
['default' => false]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,689 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Urls and paths params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescUrlsPaths implements DescriptorInterface
|
||||
{
|
||||
const INVALID_PATH_EMPTY = 'can\'t be empty';
|
||||
const INVALID_URL_EMPTY = 'can\'t be empty';
|
||||
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$archive_config = \DUPX_ArchiveConfig::getInstance();
|
||||
$paths = $archive_config->getRealValue('archivePaths');
|
||||
|
||||
$oldMainPath = $paths->home;
|
||||
$newMainPath = DUPX_ROOT;
|
||||
|
||||
$oldHomeUrl = rtrim($archive_config->getRealValue('homeUrl'), '/');
|
||||
$newHomeUrl = rtrim(DUPX_ROOT_URL, '/');
|
||||
|
||||
$oldSiteUrl = rtrim($archive_config->getRealValue('siteUrl'), '/');
|
||||
$oldContentUrl = rtrim($archive_config->getRealValue('contentUrl'), '/');
|
||||
$oldUploadUrl = rtrim($archive_config->getRealValue('uploadBaseUrl'), '/');
|
||||
$oldPluginsUrl = rtrim($archive_config->getRealValue('pluginsUrl'), '/');
|
||||
$oldMuPluginsUrl = rtrim($archive_config->getRealValue('mupluginsUrl'), '/');
|
||||
|
||||
$oldWpAbsPath = $paths->abs;
|
||||
$oldContentPath = $paths->wpcontent;
|
||||
$oldUploadsBasePath = $paths->uploads;
|
||||
$oldPluginsPath = $paths->plugins;
|
||||
$oldMuPluginsPath = $paths->muplugins;
|
||||
|
||||
$defValEdit = "This default value is automatically generated.\n"
|
||||
. "Change it only if you're sure you know what you're doing!";
|
||||
|
||||
$params[PrmMng::PARAM_URL_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_URL_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldHomeUrl]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_ADDON_SITES_PATHS] = new ParamItem(
|
||||
PrmMng::PARAM_WP_ADDON_SITES_PATHS,
|
||||
ParamForm::TYPE_ARRAY_STRING,
|
||||
[
|
||||
'default' => [],
|
||||
]
|
||||
);
|
||||
|
||||
$newObj = new ParamForm(
|
||||
PrmMng::PARAM_URL_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => $newHomeUrl,
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizeUrl',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validateUrlWithScheme',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'New Site URL:',
|
||||
'status' => function (ParamForm $param): string {
|
||||
if (
|
||||
PrmMng::getInstance()->getValue(PrmMng::PARAM_TEMPLATE) !== \DUPX_Template::TEMPLATE_ADVANCED ||
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'cant-be-empty',
|
||||
'requires-db-hide',
|
||||
],
|
||||
'subNote' => function (ParamForm $param): string {
|
||||
$archive_config = \DUPX_ArchiveConfig::getInstance();
|
||||
$oldHomeUrl = rtrim($archive_config->getRealValue('homeUrl'), '/');
|
||||
$subsiteId = PrmMng::getInstance()->getValue(PrmMng::PARAM_SUBSITE_ID);
|
||||
if (
|
||||
InstState::isInstType(
|
||||
[InstState::TYPE_STANDALONE]
|
||||
) &&
|
||||
$subsiteId > 0
|
||||
) {
|
||||
$subsiteObj = $archive_config->getSubsiteObjById($subsiteId);
|
||||
$oldHomeUrl = $subsiteObj->fullHomeUrl ?? $oldHomeUrl;
|
||||
}
|
||||
return 'Old value: <b>' . \DUPX_U::esc_html($oldHomeUrl) . '</b>';
|
||||
},
|
||||
'postfix' => [
|
||||
'type' => 'button',
|
||||
'label' => 'get',
|
||||
'btnAction' => 'DUPX.getNewUrlByDomObj(this);',
|
||||
],
|
||||
]
|
||||
);
|
||||
$params[PrmMng::PARAM_URL_NEW] = $newObj;
|
||||
$urlNewInputId = $newObj->getFormItemId();
|
||||
|
||||
$params[PrmMng::PARAM_PATH_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_PATH_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldMainPath]
|
||||
);
|
||||
|
||||
$newObj = new ParamForm(
|
||||
PrmMng::PARAM_PATH_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => $newMainPath,
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
if (strlen($value) == 0) {
|
||||
$paramObj->setInvalidMessage('The new path can\'t be empty.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// if home path is root path is necessary do a trailingslashit
|
||||
$realPath = SnapIO::safePathTrailingslashit($value);
|
||||
if (!is_dir($realPath)) {
|
||||
$paramObj->setInvalidMessage(
|
||||
'The new path must be an existing folder on the server.<br>' .
|
||||
'It is not possible to continue the installation without first creating the folder <br>' .
|
||||
'<b>' . $value . '</b>'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// don't check the return of chmod, if fail the installer must continue
|
||||
SnapIO::chmod($realPath, 'u+rwx');
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'New Path:',
|
||||
'status' => function (ParamForm $param): string {
|
||||
if (
|
||||
PrmMng::getInstance()->getValue(PrmMng::PARAM_TEMPLATE) !== \DUPX_Template::TEMPLATE_ADVANCED ||
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldMainPath) . '</b>',
|
||||
'wrapperClasses' => [
|
||||
'revalidate-on-change',
|
||||
'cant-be-empty',
|
||||
'requires-db-hide',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_NEW] = $newObj;
|
||||
$pathNewInputId = $newObj->getFormItemId();
|
||||
|
||||
$params[PrmMng::PARAM_SITE_URL_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_SITE_URL_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldSiteUrl]
|
||||
);
|
||||
|
||||
$wrapClasses = [
|
||||
'revalidate-on-change',
|
||||
'cant-be-empty',
|
||||
'auto-updatable',
|
||||
'autoupdate-enabled',
|
||||
];
|
||||
$postfixElement = [
|
||||
'type' => 'button',
|
||||
'label' => 'Auto',
|
||||
'btnAction' => 'DUPX.autoUpdateToggle(this, ' . SnapJson::jsonEncode($defValEdit) . ');',
|
||||
];
|
||||
|
||||
$params[PrmMng::PARAM_SITE_URL] = new ParamForm(
|
||||
PrmMng::PARAM_SITE_URL,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizeUrl',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validateUrlWithScheme',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'WP core URL:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldSiteUrl) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $urlNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_CONTENT_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_PATH_CONTENT_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldContentPath]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_CONTENT_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_PATH_CONTENT_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validatePath',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'WP-content path:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldContentPath) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $pathNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_WP_CORE_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_PATH_WP_CORE_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldWpAbsPath]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_WP_CORE_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_PATH_WP_CORE_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
$homePath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW);
|
||||
|
||||
if (!SnapIO::isChildPath($value, $homePath)) {
|
||||
$paramObj->setInvalidMessage(
|
||||
'ABSPATH have to be a equal or a child of HOMEPATH' .
|
||||
'<pre>' .
|
||||
'ABSPATH : ' . $value . '<br>' .
|
||||
'HOMEPATH: ' . $homePath . '<br>' .
|
||||
'</pre>'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'WP core path:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldWpAbsPath) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $pathNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_UPLOADS_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_PATH_UPLOADS_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldUploadsBasePath]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_UPLOADS_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_PATH_UPLOADS_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
$result = (
|
||||
SnapIO::isChildPath($value, $paramsManager->getValue(PrmMng::PARAM_PATH_NEW), false, false) ||
|
||||
SnapIO::isChildPath($value, $paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW), false, false)
|
||||
);
|
||||
|
||||
if ($result == false) {
|
||||
$paramObj->setInvalidMessage('Upload path have to be a child of wp-content path');
|
||||
}
|
||||
|
||||
return $result;
|
||||
},
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'Uploads path:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldUploadsBasePath) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $pathNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_CONTENT_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_URL_CONTENT_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldContentUrl]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_CONTENT_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_URL_CONTENT_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizeUrl',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validateUrlWithScheme',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'WP-content URL:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldContentUrl) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $urlNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_UPLOADS_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_URL_UPLOADS_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldUploadUrl]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_UPLOADS_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_URL_UPLOADS_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizeUrl',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validateUrlWithScheme',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'Uploads URL:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldUploadUrl) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $urlNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_PLUGINS_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_URL_PLUGINS_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldPluginsUrl]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_PLUGINS_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_URL_PLUGINS_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizeUrl',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validateUrlWithScheme',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'Plugins URL:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldPluginsUrl) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $urlNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_PLUGINS_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_PATH_PLUGINS_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
[
|
||||
'default' => $oldPluginsPath,
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validatePath',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_PLUGINS_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_PATH_PLUGINS_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validatePath',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'Plugins path:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldPluginsPath) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $pathNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_MUPLUGINS_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_URL_MUPLUGINS_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldMuPluginsUrl]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_URL_MUPLUGINS_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_URL_MUPLUGINS_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizeUrl',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validateUrlWithScheme',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'MU-plugins URL:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldMuPluginsUrl) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $urlNewInputId],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_MUPLUGINS_OLD] = new ParamItem(
|
||||
PrmMng::PARAM_PATH_MUPLUGINS_OLD,
|
||||
ParamForm::TYPE_STRING,
|
||||
['default' => $oldMuPluginsPath]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_PATH_MUPLUGINS_NEW] = new ParamForm(
|
||||
PrmMng::PARAM_PATH_MUPLUGINS_NEW,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[// ITEM ATTRIBUTES
|
||||
'default' => '',
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
'validateCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'validatePath',
|
||||
],
|
||||
],
|
||||
[// FORM ATTRIBUTES
|
||||
'label' => 'MU-plugins path:',
|
||||
'status' => [
|
||||
self::class,
|
||||
'statusFormOtherPathsUrls',
|
||||
],
|
||||
'postfix' => $postfixElement,
|
||||
'subNote' => 'Old value: <b>' . \DUPX_U::esc_html($oldMuPluginsPath) . '</b>',
|
||||
'wrapperClasses' => $wrapClasses,
|
||||
'wrapperAttr' => ['data-auto-update-from-input' => $pathNewInputId],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return statu form for paths and urls options
|
||||
*
|
||||
* @param ParamForm $param current param
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function statusFormOtherPathsUrls(ParamForm $param): string
|
||||
{
|
||||
if (
|
||||
PrmMng::getInstance()->getValue(PrmMng::PARAM_TEMPLATE) !== \DUPX_Template::TEMPLATE_ADVANCED ||
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_INFO_ONLY;
|
||||
} else {
|
||||
return ParamForm::STATUS_READONLY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
PrmMng::getInstance();
|
||||
|
||||
$archive_config = \DUPX_ArchiveConfig::getInstance();
|
||||
$paths = $archive_config->getRealValue('archivePaths');
|
||||
|
||||
$oldMainPath = $paths->home;
|
||||
$newMainPath = $params[PrmMng::PARAM_PATH_NEW]->getValue();
|
||||
|
||||
$oldHomeUrl = rtrim($archive_config->getRealValue('homeUrl'), '/');
|
||||
$newHomeUrl = $params[PrmMng::PARAM_URL_NEW]->getValue();
|
||||
|
||||
$oldSiteUrl = rtrim($archive_config->getRealValue('siteUrl'), '/');
|
||||
$oldContentUrl = rtrim($archive_config->getRealValue('contentUrl'), '/');
|
||||
$oldUploadUrl = rtrim($archive_config->getRealValue('uploadBaseUrl'), '/');
|
||||
$oldPluginsUrl = rtrim($archive_config->getRealValue('pluginsUrl'), '/');
|
||||
$oldMuPluginsUrl = rtrim($archive_config->getRealValue('mupluginsUrl'), '/');
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_PATH_WP_CORE_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubString($oldMainPath, $newMainPath, $paths->abs);
|
||||
$params[PrmMng::PARAM_PATH_WP_CORE_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_PATH_CONTENT_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubString($oldMainPath, $newMainPath, $paths->wpcontent);
|
||||
$params[PrmMng::PARAM_PATH_CONTENT_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_PATH_UPLOADS_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubString($oldMainPath, $newMainPath, $paths->uploads);
|
||||
$params[PrmMng::PARAM_PATH_UPLOADS_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_PATH_PLUGINS_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubString($oldMainPath, $newMainPath, $paths->plugins);
|
||||
$params[PrmMng::PARAM_PATH_PLUGINS_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_PATH_MUPLUGINS_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubString($oldMainPath, $newMainPath, $paths->muplugins);
|
||||
$params[PrmMng::PARAM_PATH_MUPLUGINS_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_SITE_URL]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubUrl($oldHomeUrl, $newHomeUrl, $oldSiteUrl);
|
||||
$params[PrmMng::PARAM_SITE_URL]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_URL_CONTENT_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubUrl($oldHomeUrl, $newHomeUrl, $oldContentUrl);
|
||||
$params[PrmMng::PARAM_URL_CONTENT_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_URL_UPLOADS_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubUrl($oldHomeUrl, $newHomeUrl, $oldUploadUrl);
|
||||
$params[PrmMng::PARAM_URL_UPLOADS_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_URL_PLUGINS_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubUrl($oldHomeUrl, $newHomeUrl, $oldPluginsUrl);
|
||||
$params[PrmMng::PARAM_URL_PLUGINS_NEW]->setValue($newVal);
|
||||
}
|
||||
|
||||
// if empty value isn't overwritten
|
||||
if (strlen($params[PrmMng::PARAM_URL_MUPLUGINS_NEW]->getValue()) == 0) {
|
||||
$newVal = \DUPX_ArchiveConfig::getNewSubUrl($oldHomeUrl, $newHomeUrl, $oldMuPluginsUrl);
|
||||
$params[PrmMng::PARAM_URL_MUPLUGINS_NEW]->setValue($newVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Users params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormUsersReset;
|
||||
use DUPX_DBInstall;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescUsers implements DescriptorInterface
|
||||
{
|
||||
const USER_MODE_OVERWRITE = 'overwrite';
|
||||
const USER_MODE_IMPORT_USERS = 'import_users';
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_USERS_MODE] = new ParamForm(
|
||||
PrmMng::PARAM_USERS_MODE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_RADIO,
|
||||
[
|
||||
'default' => self::USER_MODE_OVERWRITE,
|
||||
'sanitizeCallback' => function ($value) {
|
||||
if (
|
||||
InstState::getInstance()->getMode() !== InstState::MODE_OVR_INSTALL ||
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite()
|
||||
) {
|
||||
// if is restore backup user mode must be overwrite
|
||||
return ParamDescUsers::USER_MODE_OVERWRITE;
|
||||
}
|
||||
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
if ($overwriteData['isMultisite']) {
|
||||
return ParamDescUsers::USER_MODE_OVERWRITE;
|
||||
}
|
||||
|
||||
// disable keep users for some db actions
|
||||
switch (PrmMng::getInstance()->getValue(PrmMng::PARAM_DB_ACTION)) {
|
||||
case DUPX_DBInstall::DBACTION_CREATE:
|
||||
case DUPX_DBInstall::DBACTION_MANUAL:
|
||||
case DUPX_DBInstall::DBACTION_ONLY_CONNECT:
|
||||
return ParamDescUsers::USER_MODE_OVERWRITE;
|
||||
case DUPX_DBInstall::DBACTION_EMPTY:
|
||||
case DUPX_DBInstall::DBACTION_REMOVE_ONLY_TABLES:
|
||||
case DUPX_DBInstall::DBACTION_RENAME:
|
||||
return $value;
|
||||
}
|
||||
},
|
||||
'acceptValues' => [
|
||||
self::USER_MODE_OVERWRITE,
|
||||
self::USER_MODE_IMPORT_USERS,
|
||||
],
|
||||
],
|
||||
[
|
||||
'status' => function (): string {
|
||||
// Hide user mode instandalone migration for now
|
||||
return ParamForm::STATUS_SKIP;
|
||||
if (InstState::getInstance()->getMode() !== InstState::MODE_OVR_INSTALL) { // @phpstan-ignore-line
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
|
||||
if (
|
||||
$overwriteData['isMultisite'] ||
|
||||
InstState::isRestoreBackup() ||
|
||||
InstState::isAddSiteOnMultisite() ||
|
||||
InstState::isNewSiteIsMultisite()
|
||||
) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
}
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
},
|
||||
'label' => 'Users:',
|
||||
'options' => function ($item): array {
|
||||
$result = [];
|
||||
$result[] = new ParamOption(ParamDescUsers::USER_MODE_OVERWRITE, 'Overwrite');
|
||||
$result[] = new ParamOption(ParamDescUsers::USER_MODE_IMPORT_USERS, 'Merge');
|
||||
return $result;
|
||||
},
|
||||
'inlineHelp' => dupxTplRender('parts/params/inline_helps/user_mode', [], false),
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_ADD_SUBSITE_USER_MODE] = new ParamForm(
|
||||
PrmMng::PARAM_ADD_SUBSITE_USER_MODE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_RADIO,
|
||||
[
|
||||
'default' => ParamDescUsers::USER_MODE_IMPORT_USERS,
|
||||
'acceptValues' => [ParamDescUsers::USER_MODE_IMPORT_USERS],
|
||||
],
|
||||
[
|
||||
'status' => fn(): string => ParamForm::STATUS_SKIP,
|
||||
'label' => 'Users:',
|
||||
'options' => function ($item): array {
|
||||
$result = [];
|
||||
$result[] = new ParamOption(ParamDescUsers::USER_MODE_IMPORT_USERS, 'Import');
|
||||
return $result;
|
||||
},
|
||||
'inlineHelpTitle' => 'User Mode',
|
||||
'inlineHelp' => dupxTplRender('parts/params/inline_helps/subsite_user_mode', [], false),
|
||||
'wrapperClasses' => ['revalidate-on-change'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_USERS_PWD_RESET] = new ParamFormUsersReset(
|
||||
PrmMng::PARAM_USERS_PWD_RESET,
|
||||
ParamFormUsersReset::TYPE_ARRAY_STRING,
|
||||
ParamFormUsersReset::FORM_TYPE_USERS_PWD_RESET,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => array_map(fn($value): string => '', \DUPX_ArchiveConfig::getInstance()->getUsersLists()),
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateCallback' => function ($value, ParamItem $paramObj): bool {
|
||||
if (strlen($value) > 0 && strlen($value) < \DUPX_Constants::MIN_NEW_PASSWORD_LEN) {
|
||||
$paramObj->setInvalidMessage('New password must have ' . \DUPX_Constants::MIN_NEW_PASSWORD_LEN . ' or more characters');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'status' => function ($paramObj): string {
|
||||
if (ParamDescUsers::getUsersMode() != ParamDescUsers::USER_MODE_OVERWRITE) {
|
||||
return ParamForm::STATUS_DISABLED;
|
||||
} else {
|
||||
return ParamForm::STATUS_ENABLED;
|
||||
}
|
||||
},
|
||||
'label' => 'Existing user reset password:',
|
||||
'classes' => 'strength-pwd-check',
|
||||
'attr' => [
|
||||
'title' => \DUPX_Constants::MIN_NEW_PASSWORD_LEN . ' characters minimum',
|
||||
'placeholder' => "Reset user password",
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return import users mode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getUsersMode()
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
|
||||
if (InstState::isAddSiteOnMultisite()) {
|
||||
return $paramsManager->getValue(PrmMng::PARAM_ADD_SUBSITE_USER_MODE);
|
||||
} else {
|
||||
return $paramsManager->getValue(PrmMng::PARAM_USERS_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Validation params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescValidation implements DescriptorInterface
|
||||
{
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$params[PrmMng::PARAM_VALIDATION_LEVEL] = new ParamItem(
|
||||
PrmMng::PARAM_VALIDATION_LEVEL,
|
||||
ParamItem::TYPE_INT,
|
||||
[
|
||||
'default' => \DUPX_Validation_abstract_item::LV_FAIL,
|
||||
'acceptValues' => [
|
||||
\DUPX_Validation_abstract_item::LV_FAIL,
|
||||
\DUPX_Validation_abstract_item::LV_HARD_WARNING,
|
||||
\DUPX_Validation_abstract_item::LV_SOFT_WARNING,
|
||||
\DUPX_Validation_abstract_item::LV_GOOD,
|
||||
\DUPX_Validation_abstract_item::LV_PASS,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_VALIDATION_ACTION_ON_START] = new ParamItem(
|
||||
PrmMng::PARAM_VALIDATION_ACTION_ON_START,
|
||||
ParamForm::TYPE_STRING,
|
||||
[
|
||||
'default' => \DUPX_Validation_manager::ACTION_ON_START_NORMAL,
|
||||
'acceptValues' => [
|
||||
\DUPX_Validation_manager::ACTION_ON_START_NORMAL,
|
||||
\DUPX_Validation_manager::ACTION_ON_START_AUTO,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_VALIDATION_SHOW_ALL] = new ParamForm(
|
||||
PrmMng::PARAM_VALIDATION_SHOW_ALL,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_SWITCH,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Show all',
|
||||
'wrapperClasses' => 'align-right',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_ACCEPT_TERM_COND] = new ParamForm(
|
||||
PrmMng::PARAM_ACCEPT_TERM_COND,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
['default' => false],
|
||||
[
|
||||
'label' => 'Accept term and conditions',
|
||||
'renderLabel' => false,
|
||||
'checkboxLabel' => 'I have read and accept all <a href="#" onclick="DUPX.viewTerms()" >terms & notices</a>*',
|
||||
'subNote' => '<div class="required-txt">* required to continue</div>',
|
||||
'attr' => ['onclick' => 'DUPX.acceptWarning();'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,765 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* WP-config params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamOption;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamFormWpConfig;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Installer\Addons\ProBase\License;
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use DUPX_ArchiveConfig;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamDescWpConfig implements DescriptorInterface
|
||||
{
|
||||
const NOTICE_ID_WP_CONF_PARAM_PATHS_EMPTY = 'wp_conf_param_paths_empty_to_validate';
|
||||
const NOTICE_ID_WP_CONF_FORCE_SSL_ADMIN = 'wp_conf_disabled_force_ssl_admin';
|
||||
const NOTICE_ID_WP_CONF_PARAM_DOMAINS_MODIFIED = 'wp_conf_param_domains_empty_to_validate';
|
||||
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(&$params): void
|
||||
{
|
||||
$archiveConfig = \DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_DISALLOW_FILE_EDIT] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_DISALLOW_FILE_EDIT,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue(
|
||||
'DISALLOW_FILE_EDIT'
|
||||
),
|
||||
],
|
||||
[
|
||||
'label' => 'DISALLOW_FILE_EDIT:',
|
||||
'checkboxLabel' => 'Disable the Plugin/Theme Editor',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_DISALLOW_FILE_MODS] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_DISALLOW_FILE_MODS,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue(
|
||||
'DISALLOW_FILE_MODS',
|
||||
[
|
||||
'value' => false,
|
||||
'inWpConfig' => false,
|
||||
]
|
||||
),
|
||||
],
|
||||
[
|
||||
'label' => 'DISALLOW_FILE_MODS:',
|
||||
'checkboxLabel' => 'This will block users being able to use the plugin and theme installation/update ' .
|
||||
'functionality from the WordPress admin area',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_AUTOSAVE_INTERVAL] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_AUTOSAVE_INTERVAL,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_NUMBER,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue(
|
||||
'AUTOSAVE_INTERVAL',
|
||||
[
|
||||
'value' => 60,
|
||||
'inWpConfig' => false,
|
||||
]
|
||||
),
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'AUTOSAVE_INTERVAL:',
|
||||
'subNote' => 'Auto-save interval in seconds (default:60)',
|
||||
'min' => 5,
|
||||
'step' => 1,
|
||||
'wrapperClasses' => ['small'],
|
||||
'postfix' => [
|
||||
'type' => 'label',
|
||||
'label' => 'Sec.',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_POST_REVISIONS] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_POST_REVISIONS,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_NUMBER,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue(
|
||||
'WP_POST_REVISIONS',
|
||||
[
|
||||
'value' => true,
|
||||
'inWpConfig' => false,
|
||||
]
|
||||
),
|
||||
'sanitizeCallback' => function ($value) {
|
||||
//convert bool on int
|
||||
if ($value === true) {
|
||||
$value = PHP_INT_MAX;
|
||||
}
|
||||
if ($value === false) {
|
||||
$value = 0;
|
||||
}
|
||||
return $value;
|
||||
},
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'WP_POST_REVISIONS:',
|
||||
'subNote' => 'Number of article revisions. Select 0 to disable revisions. Disable the field to enable revisions.',
|
||||
'min' => 0,
|
||||
'step' => 1,
|
||||
'wrapperClasses' => ['small'],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_FORCE_SSL_ADMIN] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_FORCE_SSL_ADMIN,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => self::getDefaultForceSSLAdminConfig(),
|
||||
],
|
||||
[
|
||||
'label' => 'FORCE_SSL_ADMIN:',
|
||||
'checkboxLabel' => 'Enforce Admin SSL',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_AUTOMATIC_UPDATER_DISABLED] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_AUTOMATIC_UPDATER_DISABLED,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue(
|
||||
'AUTOMATIC_UPDATER_DISABLED',
|
||||
[
|
||||
'value' => false,
|
||||
'inWpConfig' => false,
|
||||
]
|
||||
),
|
||||
],
|
||||
[
|
||||
'label' => 'AUTOMATIC_UPDATER_DISABLED:',
|
||||
'checkboxLabel' => 'Disable automatic updater',
|
||||
]
|
||||
);
|
||||
|
||||
$autoUpdateValue = $archiveConfig->getWpConfigDefineValue('WP_AUTO_UPDATE_CORE');
|
||||
if (is_bool($autoUpdateValue)) {
|
||||
$autoUpdateValue = ($autoUpdateValue ? 'true' : 'false');
|
||||
}
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_AUTO_UPDATE_CORE] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_AUTO_UPDATE_CORE,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[
|
||||
'default' => [
|
||||
'value' => $autoUpdateValue,
|
||||
'inWpConfig' => $archiveConfig->inWpConfigDefine('WP_AUTO_UPDATE_CORE'),
|
||||
],
|
||||
'acceptValues' => [
|
||||
'',
|
||||
'false',
|
||||
'true',
|
||||
'minor',
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'WP_AUTO_UPDATE_CORE:',
|
||||
'options' => [
|
||||
new ParamOption('minor', 'Enable only core minor updates - Default'),
|
||||
new ParamOption('false', 'Disable all core updates'),
|
||||
new ParamOption('true', 'Enable all core updates'),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_IMAGE_EDIT_OVERWRITE] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_IMAGE_EDIT_OVERWRITE,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue(
|
||||
'IMAGE_EDIT_OVERWRITE',
|
||||
[
|
||||
'value' => true,
|
||||
'inWpConfig' => false,
|
||||
]
|
||||
),
|
||||
],
|
||||
[
|
||||
'label' => 'IMAGE_EDIT_OVERWRITE:',
|
||||
'checkboxLabel' => 'Create only one set of image edits',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_CACHE] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_CACHE,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_CACHE'),
|
||||
],
|
||||
[
|
||||
'label' => 'WP_CACHE:',
|
||||
'checkboxLabel' => 'Keep Enabled',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WPCACHEHOME] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WPCACHEHOME,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue("WPCACHEHOME"),
|
||||
'sanitizeCallback' => function ($value): string {
|
||||
$result = SnapUtil::sanitizeNSCharsNewlineTrim($value);
|
||||
// WPCACHEHOME want final slash
|
||||
return SnapIO::safePathTrailingslashit($result);
|
||||
},
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'WPCACHEHOME:',
|
||||
'subNote' => 'This define is not part of the WordPress core but is a define used by WP Super Cache.',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_TEMP_DIR] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_TEMP_DIR,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue("WP_TEMP_DIR"),
|
||||
'sanitizeCallback' => [
|
||||
ParamsDescriptors::class,
|
||||
'sanitizePath',
|
||||
],
|
||||
],
|
||||
['label' => 'WP_TEMP_DIR:']
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_DEBUG] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_DEBUG,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_DEBUG'),
|
||||
],
|
||||
[
|
||||
'label' => 'WP_DEBUG:',
|
||||
'checkboxLabel' => 'Display errors and warnings',
|
||||
]
|
||||
);
|
||||
|
||||
$debugLogValue = $archiveConfig->getWpConfigDefineValue('WP_DEBUG_LOG');
|
||||
if (is_string($debugLogValue)) {
|
||||
$debugLogValue = !empty($debugLogValue);
|
||||
}
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_DEBUG_LOG] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_DEBUG_LOG,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => [
|
||||
'value' => $debugLogValue,
|
||||
'inWpConfig' => $archiveConfig->inWpConfigDefine('WP_DEBUG_LOG'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'label' => 'WP_DEBUG_LOG:',
|
||||
'checkboxLabel' => 'Log errors and warnings',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_DISABLE_FATAL_ERROR_HANDLER] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_DISABLE_FATAL_ERROR_HANDLER,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_DISABLE_FATAL_ERROR_HANDLER'),
|
||||
],
|
||||
[
|
||||
'label' => 'WP_DISABLE_FATAL_ERROR_HANDLER:',
|
||||
'checkboxLabel' => 'Disable fatal error handler',
|
||||
'status' => version_compare($archiveConfig->version_wp, '5.2.0', '<') ? ParamForm::STATUS_SKIP : ParamForm::STATUS_ENABLED,
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_DEBUG_DISPLAY] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_DEBUG_DISPLAY,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_DEBUG_DISPLAY'),
|
||||
],
|
||||
[
|
||||
'label' => 'WP_DEBUG_DISPLAY:',
|
||||
'checkboxLabel' => 'Display errors and warnings',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_SCRIPT_DEBUG] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_SCRIPT_DEBUG,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('SCRIPT_DEBUG'),
|
||||
],
|
||||
[
|
||||
'label' => 'SCRIPT_DEBUG:',
|
||||
'checkboxLabel' => 'JavaScript or CSS errors',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_CONCATENATE_SCRIPTS] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_CONCATENATE_SCRIPTS,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('CONCATENATE_SCRIPTS', [
|
||||
'value' => false,
|
||||
'inWpConfig' => false,
|
||||
]),
|
||||
],
|
||||
[
|
||||
'label' => 'CONCATENATE_SCRIPTS:',
|
||||
'checkboxLabel' => 'Concatenate all JavaScript files into one URL',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_SAVEQUERIES] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_SAVEQUERIES,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('SAVEQUERIES'),
|
||||
],
|
||||
[
|
||||
'label' => 'SAVEQUERIES:',
|
||||
'checkboxLabel' => 'Save database queries in an array ($wpdb->queries)',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_ALTERNATE_WP_CRON] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_ALTERNATE_WP_CRON,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('ALTERNATE_WP_CRON', [
|
||||
'value' => false,
|
||||
'inWpConfig' => false,
|
||||
]),
|
||||
],
|
||||
[
|
||||
'label' => 'ALTERNATE_WP_CRON:',
|
||||
'checkboxLabel' => 'Use an alternative Cron with WP',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_DISABLE_WP_CRON] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_DISABLE_WP_CRON,
|
||||
ParamForm::TYPE_BOOL,
|
||||
ParamForm::FORM_TYPE_CHECKBOX,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('DISABLE_WP_CRON', [
|
||||
'value' => false,
|
||||
'inWpConfig' => false,
|
||||
]),
|
||||
],
|
||||
[
|
||||
'label' => 'DISABLE_WP_CRON:',
|
||||
'checkboxLabel' => 'Disable cron entirely',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_CRON_LOCK_TIMEOUT] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_CRON_LOCK_TIMEOUT,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_NUMBER,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_CRON_LOCK_TIMEOUT', [
|
||||
'value' => 60,
|
||||
'inWpConfig' => false,
|
||||
]),
|
||||
'min_range' => 1,
|
||||
],
|
||||
[
|
||||
'min' => 1,
|
||||
'step' => 1,
|
||||
'label' => 'WP_CRON_LOCK_TIMEOUT:',
|
||||
'wrapperClasses' => ['small'],
|
||||
'subNote' => 'Cron process cannot run more than once every WP_CRON_LOCK_TIMEOUT seconds',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_EMPTY_TRASH_DAYS] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_EMPTY_TRASH_DAYS,
|
||||
ParamForm::TYPE_INT,
|
||||
ParamForm::FORM_TYPE_NUMBER,
|
||||
[
|
||||
'default' => $archiveConfig->getDefineArrayValue('EMPTY_TRASH_DAYS', [
|
||||
'value' => 30,
|
||||
'inWpConfig' => false,
|
||||
]),
|
||||
'min_range' => 0,
|
||||
],
|
||||
[
|
||||
'min' => 0,
|
||||
'step' => 1,
|
||||
'label' => 'EMPTY_TRASH_DAYS:',
|
||||
'wrapperClasses' => ['small'],
|
||||
'subNote' => 'How many days deleted post should be kept in trash before being deleted permanently',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_COOKIE_DOMAIN] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_COOKIE_DOMAIN,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue("COOKIE_DOMAIN"),
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'COOKIE_DOMAIN:',
|
||||
'subNote' => 'Set <a href="http://www.askapache.com/htaccess/apache-speed-subdomains.html" target="_blank">' .
|
||||
'different domain</a> for cookies.subdomain.example.com',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_MEMORY_LIMIT] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_MEMORY_LIMIT,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_MEMORY_LIMIT'),
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => ParamItem::VALIDATE_REGEX_AZ_NUMBER,
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'WP_MEMORY_LIMIT:',
|
||||
'wrapperClasses' => ['small'],
|
||||
'subNote' => 'PHP memory limit (default:30M; Multisite default:64M)',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_WP_MAX_MEMORY_LIMIT] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_WP_MAX_MEMORY_LIMIT,
|
||||
ParamForm::TYPE_STRING,
|
||||
ParamForm::FORM_TYPE_TEXT,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => $archiveConfig->getDefineArrayValue('WP_MAX_MEMORY_LIMIT'),
|
||||
'sanitizeCallback' => [
|
||||
SnapUtil::class,
|
||||
'sanitizeNSCharsNewlineTrim',
|
||||
],
|
||||
'validateRegex' => ParamItem::VALIDATE_REGEX_AZ_NUMBER,
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'WP_MAX_MEMORY_LIMIT:',
|
||||
'wrapperClasses' => ['small'],
|
||||
'subNote' => 'Wordpress admin maximum memory limit (default:256M)',
|
||||
]
|
||||
);
|
||||
|
||||
$params[PrmMng::PARAM_WP_CONF_MYSQL_CLIENT_FLAGS] = new ParamFormWpConfig(
|
||||
PrmMng::PARAM_WP_CONF_MYSQL_CLIENT_FLAGS,
|
||||
ParamForm::TYPE_ARRAY_INT,
|
||||
ParamForm::FORM_TYPE_SELECT,
|
||||
[ // ITEM ATTRIBUTES
|
||||
'default' => self::getMysqlClientFlagsDefaultVals(),
|
||||
],
|
||||
[ // FORM ATTRIBUTES
|
||||
'label' => 'MYSQL_CLIENT_FLAGS:',
|
||||
'options' => self::getMysqlClientFlagsOptions(),
|
||||
'multiple' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
//UPDATE PATHS AUTOMATICALLY
|
||||
self::setDefaultWpConfigPathValue($params, PrmMng::PARAM_WP_CONF_WP_TEMP_DIR, 'WP_TEMP_DIR');
|
||||
self::setDefaultWpConfigPathValue($params, PrmMng::PARAM_WP_CONF_WPCACHEHOME, 'WPCACHEHOME');
|
||||
self::wpConfigPathsNotices();
|
||||
|
||||
//UPDATE DOMAINS AUTOMATICALLY
|
||||
self::setDefaultWpConfigDomainValue($params, PrmMng::PARAM_WP_CONF_COOKIE_DOMAIN, "COOKIE_DOMAIN");
|
||||
self::wpConfigDomainNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wp counfi default value
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getMysqlClientFlagsDefaultVals()
|
||||
{
|
||||
$result = DUPX_ArchiveConfig::getInstance()->getDefineArrayValue(
|
||||
'MYSQL_CLIENT_FLAGS',
|
||||
[
|
||||
'value' => [],
|
||||
'inWpConfig' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$result['value'] = array_intersect($result['value'], SnapDB::getMysqlConnectFlagsList(false));
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of options of the mysql real connect flags
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
protected static function getMysqlClientFlagsOptions(): array
|
||||
{
|
||||
$result = [];
|
||||
foreach (SnapDB::getMysqlConnectFlagsList() as $flag) {
|
||||
$result[] = new ParamOption(constant($flag), $flag);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to replace the old path with the new path for the given wp config define.
|
||||
* If that's not possible returns a notice to the user.
|
||||
*
|
||||
* @param ParamItem[] $params params list
|
||||
* @param string $paramKey param key
|
||||
* @param string $wpConfigKey wp config key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function setDefaultWpConfigPathValue(&$params, $paramKey, $wpConfigKey): void
|
||||
{
|
||||
if (!self::wpConfigNeedsUpdate($params, $paramKey, $wpConfigKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldMainPath = $params[PrmMng::PARAM_PATH_OLD]->getValue();
|
||||
$newMainPath = $params[PrmMng::PARAM_PATH_NEW]->getValue();
|
||||
$wpConfigVal = \DUPX_ArchiveConfig::getInstance()->getDefineArrayValue($wpConfigKey);
|
||||
|
||||
// TRY TO CHANGE THE VALUE OR RESET
|
||||
if (($wpConfigVal['value'] = \DUPX_ArchiveConfig::getNewSubString($oldMainPath, $newMainPath, $wpConfigVal['value'])) === false) {
|
||||
$wpConfigVal['inWpConfig'] = false;
|
||||
$wpConfigVal['value'] = '';
|
||||
|
||||
\DUPX_NOTICE_MANAGER::getInstance()->addNextStepNotice([
|
||||
'shortMsg' => 'WP CONFIG custom paths disabled.',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => "The " . $params[$paramKey]->getLabel() . " path could not be set programmatically and has been disabled<br>\n",
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, self::NOTICE_ID_WP_CONF_PARAM_PATHS_EMPTY);
|
||||
}
|
||||
|
||||
$params[$paramKey]->setValue($wpConfigVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to replace the old domain with the new domain for the given wp config define.
|
||||
* If that's not possible returns a notice to the user.
|
||||
*
|
||||
* @param ParamItem[] $params params list
|
||||
* @param string $paramKey param key
|
||||
* @param string $wpConfigKey wp config key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function setDefaultWpConfigDomainValue(&$params, $paramKey, $wpConfigKey): void
|
||||
{
|
||||
if (!self::wpConfigNeedsUpdate($params, $paramKey, $wpConfigKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$wpConfigVal = \DUPX_ArchiveConfig::getInstance()->getDefineArrayValue($wpConfigKey);
|
||||
$parsedUrlNew = parse_url(PrmMng::getInstance()->getValue(PrmMng::PARAM_URL_NEW));
|
||||
$parsedUrlOld = parse_url(PrmMng::getInstance()->getValue(PrmMng::PARAM_URL_OLD));
|
||||
|
||||
if ($wpConfigVal['value'] == $parsedUrlOld['host']) {
|
||||
$wpConfigVal['value'] = $parsedUrlNew['host'];
|
||||
} else {
|
||||
$wpConfigVal['inWpConfig'] = false;
|
||||
$wpConfigVal['value'] = '';
|
||||
|
||||
\DUPX_NOTICE_MANAGER::getInstance()->addNextStepNotice([
|
||||
'shortMsg' => 'WP CONFIG domains disabled.',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => "The " . $params[$paramKey]->getLabel() . " domain could not be set programmatically and has been disabled<br>\n",
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, self::NOTICE_ID_WP_CONF_PARAM_DOMAINS_MODIFIED);
|
||||
}
|
||||
|
||||
$params[$paramKey]->setValue($wpConfigVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if wp config key need update
|
||||
*
|
||||
* @param ParamItem[] $params params list
|
||||
* @param string $paramKey param key
|
||||
* @param string $wpConfigKey wp config key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function wpConfigNeedsUpdate(&$params, $paramKey, $wpConfigKey): bool
|
||||
{
|
||||
if (
|
||||
InstState::isRestoreBackup($params[PrmMng::PARAM_INST_TYPE]->getValue())
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SKIP IF PARAM IS OVERWRITTEN
|
||||
if ($params[$paramKey]->getStatus() === ParamItem::STATUS_OVERWRITE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SKIP IF EMPTY
|
||||
$wpConfigVal = \DUPX_ArchiveConfig::getInstance()->getDefineArrayValue($wpConfigKey);
|
||||
if (strlen($wpConfigVal['value']) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// EMPTY IF DISABLED
|
||||
if ($wpConfigVal['inWpConfig'] == false) {
|
||||
$wpConfigVal['value'] = '';
|
||||
$params[$paramKey]->setValue($wpConfigVal);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wp config paths notices
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function wpConfigPathsNotices(): void
|
||||
{
|
||||
$noticeManager = \DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
// PREPEND IF EXISTS
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => '',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => "It was found that the following config paths were outside of the source site's home path (" .
|
||||
\DUPX_ArchiveConfig::getInstance()->getRealValue("originalPaths")->home . "):<br><br>\n",
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_PREPEND_IF_EXISTS, self::NOTICE_ID_WP_CONF_PARAM_PATHS_EMPTY);
|
||||
|
||||
// APPEND IF EXISTS
|
||||
$msg = '<br>Keeping config paths that are outside of the home path may cause malfunctions, so these settings have been disabled by default,';
|
||||
$msg .= ' but you can set them manually if necessary by switching the install mode ';
|
||||
$msg .= 'to "Advanced" and at Step 3 navigating to "Options" > "WP-Config File"';
|
||||
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => '',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => $msg,
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND_IF_EXISTS, self::NOTICE_ID_WP_CONF_PARAM_PATHS_EMPTY);
|
||||
|
||||
$noticeManager->saveNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wp config domain notices
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function wpConfigDomainNotices(): void
|
||||
{
|
||||
$noticeManager = \DUPX_NOTICE_MANAGER::getInstance();
|
||||
|
||||
// PREPEND IF EXISTS
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => '',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => "The following config domains were disabled:<br><br>\n",
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_PREPEND_IF_EXISTS, self::NOTICE_ID_WP_CONF_PARAM_DOMAINS_MODIFIED);
|
||||
|
||||
// APPEND IF EXISTS
|
||||
$msg = '<br>The plugin was unable to automatically replace the domain, so the setting has been disabled by default.';
|
||||
$msg .= ' Please review them by switching the install mode to "Advanced" and at Step 3 navigating to "Options" > "WP-Config File"';
|
||||
|
||||
$noticeManager->addNextStepNotice([
|
||||
'shortMsg' => '',
|
||||
'level' => \DUPX_NOTICE_ITEM::NOTICE,
|
||||
'longMsg' => $msg,
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND_IF_EXISTS, self::NOTICE_ID_WP_CONF_PARAM_DOMAINS_MODIFIED);
|
||||
|
||||
$noticeManager->saveNotices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default config value for FORCE_SSL_ADMIN depending on current site's settings
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultForceSSLAdminConfig()
|
||||
{
|
||||
$forceAdminSSLConfig = \DUPX_ArchiveConfig::getInstance()->getDefineArrayValue('FORCE_SSL_ADMIN');
|
||||
if (!SnapURL::isCurrentUrlSSL() && $forceAdminSSLConfig['inWpConfig'] === true) {
|
||||
$noticeMng = \DUPX_NOTICE_MANAGER::getInstance();
|
||||
$noticeMng->addFinalReportNotice(
|
||||
[
|
||||
'shortMsg' => "FORCE_SSL_ADMIN was enabled on none SSL",
|
||||
'level' => \DUPX_NOTICE_ITEM::SOFT_WARNING,
|
||||
'longMsg' => 'It was found that FORCE_SSL_ADMIN is enabled and you are installing on a site without SSL, ' .
|
||||
'so that config has been disabled.',
|
||||
'sections' => 'general',
|
||||
],
|
||||
\DUPX_NOTICE_MANAGER::ADD_UNIQUE,
|
||||
self::NOTICE_ID_WP_CONF_FORCE_SSL_ADMIN
|
||||
);
|
||||
$noticeMng->saveNotices();
|
||||
$forceAdminSSLConfig['value'] = false;
|
||||
}
|
||||
return $forceAdminSSLConfig;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Main params descriptions
|
||||
*
|
||||
* @category Duplicator
|
||||
* @package Installer
|
||||
* @author Snapcreek <admin@snapcreek.com>
|
||||
* @copyright 2011-2021 Snapcreek LLC
|
||||
* @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Descriptors;
|
||||
|
||||
use Duplicator\Installer\Core\Hooks\HooksMng;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
/**
|
||||
* class where all parameters are initialized. Used by the param manager
|
||||
*/
|
||||
final class ParamsDescriptors
|
||||
{
|
||||
/**
|
||||
* Params init
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
HooksMng::getInstance()->addAction('after_params_overwrite', [self::class, 'updateParamsAfterOverwrite']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init params
|
||||
*
|
||||
* @param array<ParamItem|ParamForm> $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initParams(&$params): void
|
||||
{
|
||||
ParamDescUrlsPaths::init($params);
|
||||
ParamDescController::init($params);
|
||||
ParamDescSecurity::init($params);
|
||||
ParamDescGeneric::init($params);
|
||||
ParamDescConfigs::init($params);
|
||||
ParamDescEngines::init($params);
|
||||
ParamDescValidation::init($params);
|
||||
ParamDescDatabase::init($params);
|
||||
ParamDescCPanel::init($params);
|
||||
ParamDescReplace::init($params);
|
||||
ParamDescMultisite::init($params);
|
||||
ParamDescPlugins::init($params);
|
||||
ParamDescUsers::init($params);
|
||||
ParamDescNewAdmin::init($params);
|
||||
ParamDescWpConfig::init($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params after overwrite logic
|
||||
*
|
||||
* @param ParamItem[]|ParamForm[] $params params list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function updateParamsAfterOverwrite($params): void
|
||||
{
|
||||
Log::info('UPDATE PARAMS AFTER OVERWRITE', Log::LV_DETAILED);
|
||||
ParamDescUrlsPaths::updateParamsAfterOverwrite($params);
|
||||
ParamDescController::updateParamsAfterOverwrite($params);
|
||||
ParamDescSecurity::updateParamsAfterOverwrite($params);
|
||||
ParamDescGeneric::updateParamsAfterOverwrite($params);
|
||||
ParamDescConfigs::updateParamsAfterOverwrite($params);
|
||||
ParamDescEngines::updateParamsAfterOverwrite($params);
|
||||
ParamDescValidation::updateParamsAfterOverwrite($params);
|
||||
ParamDescDatabase::updateParamsAfterOverwrite($params);
|
||||
ParamDescCPanel::updateParamsAfterOverwrite($params);
|
||||
ParamDescReplace::updateParamsAfterOverwrite($params);
|
||||
ParamDescMultisite::updateParamsAfterOverwrite($params);
|
||||
ParamDescPlugins::updateParamsAfterOverwrite($params);
|
||||
ParamDescUsers::updateParamsAfterOverwrite($params);
|
||||
ParamDescNewAdmin::updateParamsAfterOverwrite($params);
|
||||
ParamDescWpConfig::updateParamsAfterOverwrite($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate function, return true if value isn't empty
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param ParamItem $paramObj current param object
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function validateNotEmpty($value, ParamItem $paramObj): bool
|
||||
{
|
||||
$result = is_string($value) ? strlen($value) > 0 : !empty($value);
|
||||
|
||||
if ($result == false) {
|
||||
$paramObj->setInvalidMessage('Can\'t be empty');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize path
|
||||
*
|
||||
* @param string $value input value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitizePath($value): string
|
||||
{
|
||||
$result = SnapUtil::sanitizeNSCharsNewlineTrim($value);
|
||||
return SnapIO::safePathUntrailingslashit($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The path can't be empty
|
||||
*
|
||||
* @param string $value input value
|
||||
* @param ParamItem $paramObj current param object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validatePath($value, ParamItem $paramObj): bool
|
||||
{
|
||||
if (strlen($value) > 1) {
|
||||
return true;
|
||||
} else {
|
||||
$paramObj->setInvalidMessage('Path can\'t empty');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize URL
|
||||
*
|
||||
* @param string $value input value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitizeUrl($value): string
|
||||
{
|
||||
$result = SnapUtil::sanitizeNSCharsNewlineTrim($value);
|
||||
if (empty($value)) {
|
||||
return '';
|
||||
}
|
||||
// if scheme not set add http by default
|
||||
if (!preg_match('/^[a-zA-Z]+\:\/\//', $result)) {
|
||||
$result = 'http://' . ltrim($result, '/');
|
||||
}
|
||||
return rtrim($result, '/\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL can't be empty
|
||||
*
|
||||
* @param string $value input value
|
||||
* @param ParamItem $paramObj current param object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateUrlWithScheme($value, ParamItem $paramObj): bool
|
||||
{
|
||||
if (strlen($value) == 0) {
|
||||
$paramObj->setInvalidMessage('URL can\'t be empty');
|
||||
return false;
|
||||
}
|
||||
if (($parsed = parse_url($value)) === false) {
|
||||
$paramObj->setInvalidMessage('URL isn\'t valid');
|
||||
return false;
|
||||
}
|
||||
if (!isset($parsed['host']) || empty($parsed['host'])) {
|
||||
$paramObj->setInvalidMessage('URL must be a valid host');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
/**
|
||||
* this class manages a password type input with the hide / show password button
|
||||
*/
|
||||
class ParamFormPass extends ParamForm
|
||||
{
|
||||
const FORM_TYPE_PWD_TOGGLE = 'pwdtoggle';
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlItem()
|
||||
{
|
||||
if ($this->formType == self::FORM_TYPE_PWD_TOGGLE) {
|
||||
$this->pwdToggleHtml();
|
||||
} else {
|
||||
parent::htmlItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the text of current object for info only status
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function valueToInfo(): string
|
||||
{
|
||||
return '**********';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render PWD toggle element
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function pwdToggleHtml()
|
||||
{
|
||||
$attrs = [
|
||||
'value' => $this->getInputValue(),
|
||||
];
|
||||
|
||||
if ($this->isDisabled()) {
|
||||
$attrs['disabled'] = 'disabled';
|
||||
}
|
||||
|
||||
if ($this->isReadonly()) {
|
||||
$attrs['readonly'] = 'readonly';
|
||||
}
|
||||
|
||||
if (!is_null($this->formAttr['maxLength'])) {
|
||||
$attrs['maxLength'] = $this->formAttr['maxLength'];
|
||||
}
|
||||
|
||||
if (!is_null($this->formAttr['size'])) {
|
||||
$attrs['size'] = $this->formAttr['size'];
|
||||
}
|
||||
|
||||
$attrs = array_merge($attrs, $this->formAttr['attr']);
|
||||
|
||||
\DUPX_U_Html::inputPasswordToggle($this->getAttrName(), $this->formAttr['id'], $this->formAttr['classes'], $attrs, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default form attributes
|
||||
*
|
||||
* @param string $formType form type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForFormType($formType): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForFormType($formType);
|
||||
if ($formType == self::FORM_TYPE_PWD_TOGGLE) {
|
||||
$attrs['maxLength'] = null; // if null have no limit
|
||||
$attrs['size'] = null;
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
/**
|
||||
* this class handles the entire block selection block.
|
||||
*/
|
||||
class ParamFormPlugins extends ParamForm
|
||||
{
|
||||
const FORM_TYPE_PLUGINS_SELECT = 'pluginssel';
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlItem()
|
||||
{
|
||||
if ($this->formType == self::FORM_TYPE_PLUGINS_SELECT) {
|
||||
$this->pluginSelectHtml();
|
||||
} else {
|
||||
parent::htmlItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render plugin selector HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function pluginSelectHtml()
|
||||
{
|
||||
$pluginsManager = \Duplicator\Installer\Core\Deploy\Plugins\PluginsManager::getInstance();
|
||||
$plugns_list = $pluginsManager->getPlugins();
|
||||
|
||||
$attrs = [
|
||||
'id' => $this->formAttr['id'],
|
||||
'name' => $this->getAttrName() . '[]',
|
||||
'multiple' => '',
|
||||
];
|
||||
$this->formAttr['classes'][] = 'no-display';
|
||||
|
||||
if (!empty($this->formAttr['classes'])) {
|
||||
$attrs['class'] = implode(' ', array_unique($this->formAttr['classes']));
|
||||
}
|
||||
|
||||
if ($this->isDisabled()) {
|
||||
$attrs['disabled'] = 'disabled';
|
||||
}
|
||||
|
||||
if ($this->isReadonly()) {
|
||||
$attrs['readonly'] = 'readonly';
|
||||
}
|
||||
|
||||
$attrs = array_merge($attrs, $this->formAttr['attr']);
|
||||
?>
|
||||
<select <?php echo \DUPX_U_Html::arrayAttrToHtml($attrs); ?> >
|
||||
<?php
|
||||
foreach ($plugns_list as $pluginSlug => $plugin) {
|
||||
if ($plugin->isIgnore() || $plugin->isForceDisabled()) {
|
||||
continue;
|
||||
}
|
||||
$optAttr = ['value' => $pluginSlug];
|
||||
if (self::isValueInValue($pluginSlug, $this->getInputValue())) {
|
||||
// can't be selected if is disabled
|
||||
$optAttr['selected'] = 'selected';
|
||||
}
|
||||
?>
|
||||
<option <?php echo \DUPX_U_Html::arrayAttrToHtml($optAttr); ?> >
|
||||
<?php echo \DUPX_U::esc_html($plugin->name); ?>
|
||||
</option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
echo $this->getSubNote();
|
||||
$this->pluginsSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render plugin selector
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function pluginsSelector()
|
||||
{
|
||||
$pluginsManager = \Duplicator\Installer\Core\Deploy\Plugins\PluginsManager::getInstance();
|
||||
$plugns_list = $pluginsManager->getPlugins();
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$subsiteId = $paramsManager->getValue(PrmMng::PARAM_SUBSITE_ID);
|
||||
$safe_mode = $paramsManager->getValue(PrmMng::PARAM_SAFE_MODE);
|
||||
?>
|
||||
<div>
|
||||
<?php if (!$this->isDisabled()) { ?>
|
||||
<?php
|
||||
if ($safe_mode > 0) {
|
||||
echo
|
||||
'<div class="s3-warn">'
|
||||
. '<i class="fas fa-exclamation-triangle"></i> Safe Mode Enabled: <i>Only Duplicator Pro will be enabled during install.</i>'
|
||||
. '</div>';
|
||||
}
|
||||
?>
|
||||
<div class="s3-allnonelinks" style="<?php echo ($safe_mode > 0) ? 'display:none' : ''; ?>">
|
||||
<button type="button" id="select-all-plugins" class="no-layout">[All]</button>
|
||||
<button type="button" id="unselect-all-plugins" class="no-layout">[None]</button>
|
||||
</div><br style="clear:both" />
|
||||
<?php } ?>
|
||||
</div>
|
||||
<ul id="plugins-filters" >
|
||||
<li class="all" data-filter-target="all" >
|
||||
<a href="#" class="current">
|
||||
All <span class="count">(<?php echo count($plugns_list); ?>)</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
foreach ($pluginsManager->getStatusCounts($subsiteId) as $status => $count) {
|
||||
if ($count) {
|
||||
?>
|
||||
<li class="<?php echo \DUPX_U::esc_attr($status); ?>" data-filter-target="orig-<?php echo \DUPX_U::esc_attr($status); ?>" >
|
||||
<a href="#">
|
||||
<?php echo \DUPX_U::esc_html(\Duplicator\Installer\Core\Deploy\Plugins\PluginItem::getStatusLabel($status)); ?>
|
||||
<span class="count"> (<?php echo $count; ?>)</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
|
||||
<table id="plugins_list_table_selector" class="list_table_selector<?php echo ($safe_mode > 0) ? ' disabled' : ''; ?>" >
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="check_input" ></th>
|
||||
<th class="name" >Name</th>
|
||||
<th class="info" >Details</th>
|
||||
<th class="orig_status" >Original<br>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($plugns_list as $pluginObj) {
|
||||
if ($pluginObj->isIgnore() || $pluginObj->isForceDisabled()) {
|
||||
continue;
|
||||
}
|
||||
$this->pluginHtmlItem($pluginObj, $subsiteId);
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
$this->pluginTableSelectorJs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render plugin item
|
||||
*
|
||||
* @param \Duplicator\Installer\Core\Deploy\Plugins\PluginItem $pluginObj plugin object
|
||||
* @param int $subsiteId selected subsite id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function pluginHtmlItem($pluginObj, $subsiteId)
|
||||
{
|
||||
$itemClasses = ['table-item'];
|
||||
$orgiStats = $pluginObj->getOrgiStatus($subsiteId);
|
||||
$itemClasses[] = 'orig-' . $orgiStats;
|
||||
$itemClasses[] = self::isValueInValue($pluginObj->getSlug(), $this->getInputValue()) ? 'active' : 'inactive';
|
||||
|
||||
//$authorURI = $pluginObj->authorURI;
|
||||
if (empty($pluginObj->authorURI)) {
|
||||
$author = \DUPX_U::esc_html($pluginObj->author);
|
||||
} else {
|
||||
$author = '<a href="' . \DUPX_U::esc_attr($pluginObj->authorURI) . '" target="_blank">' . \DUPX_U::esc_html($pluginObj->author) . '</a>';
|
||||
}
|
||||
?>
|
||||
<tr class="<?php echo implode(' ', $itemClasses); ?>" data-plugin-slug="<?php echo \DUPX_U::esc_attr($pluginObj->getSlug()); ?>">
|
||||
<td class="check_input" >
|
||||
<input type="checkbox" <?php echo $this->isReadonly() ? 'readonly' : ''; ?> <?php echo $this->isDisabled() ? 'disabled' : ''; ?>>
|
||||
</td>
|
||||
<td class="name" ><?php echo \DUPX_U::esc_html($pluginObj->name); ?></td>
|
||||
<td class="info" >
|
||||
Version: <?php echo \DUPX_U::esc_html($pluginObj->version); ?><br>
|
||||
URL: <a href="<?php echo \DUPX_U::esc_attr($pluginObj->pluginURI); ?>" target="_blank" class="plugin-link" >
|
||||
<?php echo \DUPX_U::esc_html($pluginObj->pluginURI); ?>
|
||||
</a><br/>
|
||||
Author: <?php echo $author; ?><br>
|
||||
</td>
|
||||
<td class="orig_status" ><?php echo \DUPX_U::esc_html($pluginObj->getStatusLabel($orgiStats)); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render javascript
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function pluginTableSelectorJs()
|
||||
{
|
||||
?>
|
||||
<script>
|
||||
(function ($) {
|
||||
var pluginsWrapper = $('#' + <?php echo SnapJson::jsonEncode($this->formAttr['wrapperId']); ?>);
|
||||
var pluginsSelect = $('#' + <?php echo SnapJson::jsonEncode($this->formAttr['id']); ?>);
|
||||
var tableSelect = $('#plugins_list_table_selector');
|
||||
|
||||
var pluginsSelectIsDisabled = pluginsWrapper.hasClass('param-wrapper-disabled');
|
||||
|
||||
function setItemTable(item, enable) {
|
||||
if (enable) {
|
||||
item.removeClass('inactive').addClass('active');
|
||||
item.find('.check_input input').prop('checked', true);
|
||||
pluginsSelect.find('option[value="' + item.data('plugin-slug') + '"]').prop('selected', true);
|
||||
} else {
|
||||
item.removeClass('active').addClass('inactive');
|
||||
item.find('.check_input input').prop('checked', false);
|
||||
pluginsSelect.find('option[value="' + item.data('plugin-slug') + '"]').prop('selected', false);
|
||||
}
|
||||
}
|
||||
|
||||
// prevent select on unselect on external link click
|
||||
tableSelect.find('.table-item a').click(function (event) {
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
});
|
||||
|
||||
tableSelect.find('.table-item').each(function () {
|
||||
var current = $(this);
|
||||
|
||||
// init select element
|
||||
if (current.hasClass('active')) {
|
||||
setItemTable(current, true);
|
||||
} else {
|
||||
setItemTable(current, false);
|
||||
}
|
||||
|
||||
// change on click
|
||||
current.click(function () {
|
||||
if (pluginsSelectIsDisabled) {
|
||||
return;
|
||||
}
|
||||
if (current.hasClass('active')) {
|
||||
setItemTable(current, false);
|
||||
} else {
|
||||
setItemTable(current, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#select-all-plugins').click(function () {
|
||||
tableSelect.find('.table-item').each(function () {
|
||||
setItemTable($(this), true);
|
||||
});
|
||||
});
|
||||
|
||||
$('#unselect-all-plugins').click(function () {
|
||||
tableSelect.find('.table-item').each(function () {
|
||||
setItemTable($(this), false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#plugins-filters a').click(function () {
|
||||
var obj = $(this);
|
||||
if (obj.hasClass('current')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#plugins-filters a').removeClass('current');
|
||||
obj.addClass('current');
|
||||
|
||||
var filterTarget = obj.parent().data('filter-target');
|
||||
if (filterTarget === 'all') {
|
||||
tableSelect.find('.table-item').removeClass('no-display');
|
||||
} else {
|
||||
tableSelect.find('.table-item').removeClass('no-display').not('.' + filterTarget).addClass('no-display');
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
})(jQuery);
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default form attribute
|
||||
*
|
||||
* @param string $formType form type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForFormType($formType): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForFormType($formType);
|
||||
if ($formType == self::FORM_TYPE_PLUGINS_SELECT) {
|
||||
$attrs['wrapperContainerTag'] = 'div';
|
||||
$attrs['inputContainerTag'] = 'div';
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,771 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_ArchiveConfig;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use DUPX_U;
|
||||
use DUPX_U_Html;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Item for overwrite mapping
|
||||
*/
|
||||
class ParamFormSitesOwrMap extends ParamForm
|
||||
{
|
||||
const SOFT_LIMIT_NUM = 10;
|
||||
const HARD_LIMIT_NUM = 20;
|
||||
|
||||
const TYPE_ARRAY_SITES_OWR_MAP = 'arrayowrmap';
|
||||
const FORM_TYPE_SITES_OWR_MAP = 'sitesowrmap';
|
||||
|
||||
const NAME_POSTFIX_SOURCE_ID = '_source_id';
|
||||
const NAME_POSTFIX_TARGET_ID = '_target_id';
|
||||
const NAME_POSTFIX_NEW_SLUG = '_new_slug';
|
||||
|
||||
const STRING_ADD_NEW_SUBSITE = "Add as New Subsite in Network";
|
||||
|
||||
/** @var ?mixed[] */
|
||||
protected $extraData;
|
||||
/** @var int<0,max> minimum item in list */
|
||||
protected $minListItems = 1;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name param identifier
|
||||
* @param string $type Enum: TYPE_STRING | TYPE_ARRAY_STRING | ...
|
||||
* @param string $formType FORM_TYPE_HIDDEN | FORM_TYPE_TEXT | ...
|
||||
* @param array<string, mixed> $attr list of attributes
|
||||
* @param array<string, mixed> $formAttr list of form attributes
|
||||
*/
|
||||
public function __construct($name, $type, $formType, array $attr = [], array $formAttr = [])
|
||||
{
|
||||
if ($type != static::TYPE_ARRAY_SITES_OWR_MAP) {
|
||||
throw new Exception('the type must be ' . static::TYPE_ARRAY_SITES_OWR_MAP);
|
||||
}
|
||||
|
||||
if ($formType != static::FORM_TYPE_SITES_OWR_MAP) {
|
||||
throw new Exception('the form type must be ' . static::FORM_TYPE_SITES_OWR_MAP);
|
||||
}
|
||||
|
||||
parent::__construct($name, $type, $formType, $attr, $formAttr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlItem()
|
||||
{
|
||||
if ($this->formType == static::FORM_TYPE_SITES_OWR_MAP) {
|
||||
$this->sitesOrwHtml();
|
||||
} else {
|
||||
parent::htmlItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return soft limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getEmptyListMessage(): string
|
||||
{
|
||||
return 'Add site to import';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return full list limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFullListMessage(): string
|
||||
{
|
||||
return 'All available sites have been added to the list.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return soft limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getSoftLimitMessage(): string
|
||||
{
|
||||
return 'It is possible to import a larger number of sites simultaneously,' .
|
||||
'but multiple installations are recommended to prevent stability errors.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hard limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getHardLimitMesssage(): string
|
||||
{
|
||||
return 'Maximum number of sites that can be imported in a single installation reached. (' .
|
||||
static::HARD_LIMIT_NUM .
|
||||
') If you wish to import several sites, carry out separate installations.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render subsite owr mapping
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function sitesOrwHtml()
|
||||
{
|
||||
$extraData = $this->getListExtraData();
|
||||
$numSites = $extraData['sourceInfo']['numSites'];
|
||||
$haveMultipleItems = $numSites > 1;
|
||||
|
||||
$numListItem = 0;
|
||||
$hardLimit = false;
|
||||
$softLimit = false;
|
||||
|
||||
if (count($this->value) >= static::HARD_LIMIT_NUM) {
|
||||
$hardLimit = true;
|
||||
} elseif (count($this->value) >= static::SOFT_LIMIT_NUM) {
|
||||
$softLimit = true;
|
||||
}
|
||||
|
||||
$addDisabled = (count($this->value) >= $extraData['sourceInfo']['numSites'] || $hardLimit);
|
||||
|
||||
?>
|
||||
<div
|
||||
class="overwrite_sites_list <?php echo ($haveMultipleItems ? '' : 'no-multiple'); ?>"
|
||||
data-list-info="<?php echo DUPX_U::esc_attr(json_encode($this->getListExtraData())); ?>">
|
||||
<div class="overwrite_site_item title">
|
||||
<div class="col">
|
||||
<?php echo $this->getItemsLabels('sourceSite'); ?>
|
||||
</div>
|
||||
<div class="col">
|
||||
<?php echo $this->getItemsLabels('targetSite'); ?>
|
||||
</div>
|
||||
<div class="col del">
|
||||
<span class="del_item hidden">
|
||||
<i class="fa fa-minus-square"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if (empty($this->value)) {
|
||||
for ($i = 0; $i < $this->minListItems; $i++) {
|
||||
if (($defaultId = static::getDefaultSourceId($i)) == false) {
|
||||
break;
|
||||
}
|
||||
$defaultItem = new SiteOwrMap($defaultId, SiteOwrMap::NEW_SUBSITE_WITH_SLUG, '');
|
||||
$this->itemOwrHtml($defaultItem, 0, false);
|
||||
$numListItem++;
|
||||
}
|
||||
} else {
|
||||
$canDelete = (count($this->value) > $this->minListItems);
|
||||
foreach ($this->value as $index => $siteMap) {
|
||||
$this->itemOwrHtml($siteMap, $index, $canDelete);
|
||||
$numListItem++;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="overwrite_site_item add_item">
|
||||
<div class="full">
|
||||
<button
|
||||
type="button"
|
||||
class="secondary-btn float-right add_button"
|
||||
data-new-item="<?php echo DUPX_U::esc_attr($this->itemOwrHtml(null, 0, false, false)); ?>"
|
||||
<?php echo ($addDisabled ? 'disabled' : ''); ?>>
|
||||
<?php echo $this->getItemsLabels('addItem'); ?>
|
||||
</button>
|
||||
<?php if (strlen(static::getEmptyListMessage())) { ?>
|
||||
<div class="overwrite_msg overwrite_site_empty_list_msg <?php echo ($numListItem == 0 ? '' : 'no-display'); ?>">
|
||||
<i class="fas fa-info-circle"></i> <?php echo static::getEmptyListMessage(); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php if (strlen(static::getFullListMessage())) { ?>
|
||||
<div
|
||||
class="overwrite_msg overwrite_site_full_list_msg <?php echo ($numListItem > $numSites ? '' : 'no-display'); ?>">
|
||||
<i class="fas fa-info-circle"></i> <?php echo static::getFullListMessage(); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php if (strlen(static::getSoftLimitMessage())) { ?>
|
||||
<div class="overwrite_msg overwrite_site_soft_limit_msg maroon <?php echo ($softLimit ? '' : 'no-display'); ?>">
|
||||
<i class="fas fa-exclamation-triangle"></i> <?php echo static::getSoftLimitMessage(); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php if (strlen(static::getHardLimitMesssage())) { ?>
|
||||
<div class="overwrite_msg overwrite_site_hard_limit_msg maroon <?php echo ($hardLimit ? '' : 'no-display'); ?>">
|
||||
<i class="fas fa-exclamation-triangle"></i> <?php echo static::getHardLimitMesssage(); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get add itm button label
|
||||
*
|
||||
* @param string $key label key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getItemsLabels($key): string
|
||||
{
|
||||
$paramLabels = [
|
||||
'addItem' => 'Add Site to Import',
|
||||
'sourceSite' => 'Source Site',
|
||||
'targetSite' => 'Target Site',
|
||||
];
|
||||
|
||||
return ($paramLabels[$key] ?? 'unknown label key');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render item html
|
||||
*
|
||||
* @param SiteOwrMap|null $map map item
|
||||
* @param int $index current item inder
|
||||
* @param bool $canDelete if false disable delete button
|
||||
* @param bool $echo if false return HTML
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function itemOwrHtml(?SiteOwrMap $map = null, $index = 0, $canDelete = false, $echo = true)
|
||||
{
|
||||
ob_start();
|
||||
$selectSourceAttrs = [
|
||||
'name' => $this->getName() . static::NAME_POSTFIX_SOURCE_ID . '[]',
|
||||
'class' => 'source_id js-select ' . $this->getFormItemId() . static::NAME_POSTFIX_SOURCE_ID,
|
||||
];
|
||||
|
||||
$selectTargetAttrs = [
|
||||
'name' => $this->getName() . static::NAME_POSTFIX_TARGET_ID . '[]',
|
||||
'class' => 'target_id js-select ' . $this->getFormItemId() . static::NAME_POSTFIX_TARGET_ID,
|
||||
];
|
||||
|
||||
$newSlugAttrs = [
|
||||
'name' => $this->getName() . static::NAME_POSTFIX_NEW_SLUG . '[]',
|
||||
'class' => 'new_slug ' . $this->getFormItemId() . static::NAME_POSTFIX_NEW_SLUG,
|
||||
];
|
||||
|
||||
$extraData = $this->getListExtraData();
|
||||
|
||||
if (is_null($map)) {
|
||||
$selectSourceAttrs['disabled'] = true;
|
||||
$selectedSource = false;
|
||||
$noteSourceSlug = '';
|
||||
$selectTargetAttrs['disabled'] = true;
|
||||
$selectedTarget = SiteOwrMap::NEW_SUBSITE_WITH_SLUG;
|
||||
$noteTargetSlug = '_____';
|
||||
$newSlugAttrs['disabled'] = true;
|
||||
$newSlugAttrs['value'] = '';
|
||||
} else {
|
||||
$selectedSource = $map->getSourceId();
|
||||
$selectedSourceInfo = $extraData['sourceInfo']['sites']['id_' . $selectedSource];
|
||||
$noteSourceSlug = $selectedSourceInfo['domain'] . $selectedSourceInfo['path'];
|
||||
$selectedTarget = $map->getTargetId();
|
||||
$selectedTargetInfo = $extraData['targetInfo']['sites']['id_' . $selectedTarget];
|
||||
|
||||
switch ($selectedTarget) {
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_SLUG:
|
||||
$noteTargetSlug = (
|
||||
strlen($selectedTargetInfo['slug']) == 0 ?
|
||||
'_____' :
|
||||
$extraData['sourceInfo']['urlPrefix'] . $selectedTargetInfo['slug'] . $extraData['sourceInfo']['urlPostfix']
|
||||
);
|
||||
break;
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN:
|
||||
$noteTargetSlug = (strlen($selectedTargetInfo['slug']) == 0 ? '_____/_____' : $selectedTargetInfo['slug']);
|
||||
break;
|
||||
default:
|
||||
$noteTargetSlug = $selectedTargetInfo['domain'] . $selectedTargetInfo['path'];
|
||||
break;
|
||||
}
|
||||
$newSlugAttrs['value'] = $map->getNewSlug();
|
||||
}
|
||||
|
||||
$sourceIdsOptions = static::getSourceIdsOptions();
|
||||
?>
|
||||
<div class="overwrite_site_item">
|
||||
<div class="col">
|
||||
<select <?php echo DUPX_U_Html::arrayAttrToHtml($selectSourceAttrs); ?>>
|
||||
<?php static::renderSelectOptions($sourceIdsOptions, $selectedSource); ?>
|
||||
</select>
|
||||
<div class="sub-note source-site-note">
|
||||
<span class="site-prefix-slug"><?php echo DUpx_u::esc_html($extraData['sourceInfo']['urlScheme']); ?></span>
|
||||
<span class="site-slug"><?php echo DUpx_u::esc_html($noteSourceSlug); ?></span><span class="site-postfix-slug"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="target_select_wrapper">
|
||||
<select <?php echo DUPX_U_Html::arrayAttrToHtml($selectTargetAttrs); ?>>
|
||||
<?php static::renderSelectOptions(static::getTargetIdsOptions(), $selectedTarget); ?>
|
||||
</select>
|
||||
<div class="new-slug-wrapper">
|
||||
<input
|
||||
type="text" <?php echo DUPX_U_Html::arrayAttrToHtml($newSlugAttrs); ?>
|
||||
placeholder="Insert the new site slug">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub-note target-site-note">
|
||||
<span class="site-prefix-slug"><?php echo DUpx_u::esc_html($extraData['targetInfo']['urlScheme']); ?></span>
|
||||
<span class="site-slug"><?php echo DUpx_u::esc_html($noteTargetSlug); ?></span><span class="site-postfix-slug"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col del">
|
||||
<span class="del_item <?php echo $canDelete ? '' : 'disabled'; ?>" title="Remove this site">
|
||||
<i class="fa fa-minus-square"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if ($echo) {
|
||||
ob_end_flush();
|
||||
return '';
|
||||
} else {
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default type attributes
|
||||
*
|
||||
* @param string $type param value type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForType($type): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForType($type);
|
||||
if ($type == static::TYPE_ARRAY_SITES_OWR_MAP) {
|
||||
$attrs['default'] = [];
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filter to value input
|
||||
*
|
||||
* @param mixed[] $superObject query string super object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValueFilter($superObject)
|
||||
{
|
||||
if (($items = json_decode($superObject[$this->getName()], true)) == false) {
|
||||
throw new Exception('Invalid json string');
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sanitized value
|
||||
*
|
||||
* @param mixed $value value input
|
||||
*
|
||||
* @return SiteOwrMap[]
|
||||
*/
|
||||
public function getSanitizeValue($value)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($value); $i++) {
|
||||
$sourceId = (isset($value[$i]['sourceId']) ? (int) $value[$i]['sourceId'] : SiteOwrMap::NEW_SUBSITE_NOT_VALID);
|
||||
$targetId = (isset($value[$i]['targetId']) ? (int) $value[$i]['targetId'] : SiteOwrMap::NEW_SUBSITE_NOT_VALID);
|
||||
$newSlug = (isset($value[$i]['newSlug']) ? SnapUtil::sanitizeNSCharsNewlineTrim($value[$i]['newSlug']) : '');
|
||||
switch ($targetId) {
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_SLUG:
|
||||
$newSlug = preg_replace('/[\s"\'\\\\\/&?#,\.:;]+/m', '', $newSlug);
|
||||
break;
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN:
|
||||
$newSlug = preg_replace('/[\s"\'\\\\&?#,:;]+/m', '', $newSlug);
|
||||
if (strlen($newSlug) > 0) {
|
||||
$newSlug = SnapIO::trailingslashit($newSlug);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$newSlug = '';
|
||||
break;
|
||||
}
|
||||
$value[$i] = [
|
||||
'sourceId' => $sourceId,
|
||||
'targetId' => $targetId,
|
||||
'newSlug' => $newSlug,
|
||||
];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if value is valid
|
||||
*
|
||||
* @param mixed $value value
|
||||
* @param mixed $validateValue variable passed by reference. Updated to validated value in the case, the value is a valid value.
|
||||
*
|
||||
* @return bool true if is a valid value for this object
|
||||
*/
|
||||
public function isValid($value, &$validateValue = null)
|
||||
{
|
||||
$validateValue = [];
|
||||
|
||||
try {
|
||||
foreach ($value as $item) {
|
||||
if ($item instanceof SiteOwrMap) {
|
||||
$validateValue[] = $item;
|
||||
continue;
|
||||
}
|
||||
|
||||
$validateValue[] = new SiteOwrMap(
|
||||
$item['sourceId'],
|
||||
$item['targetId'],
|
||||
$item['newSlug']
|
||||
);
|
||||
}
|
||||
|
||||
if (($result = $this->callValidateCallback($validateValue)) === false) {
|
||||
$validateValue = null;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::info('Validation error message: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value from array. This function is used to set data from json array
|
||||
*
|
||||
* @param array<string, mixed> $data form data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function fromArrayData($data)
|
||||
{
|
||||
return parent::fromArrayData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* return array dato to store in json array data
|
||||
*
|
||||
* @return array{value: mixed, status: string}
|
||||
*/
|
||||
public function toArrayData(): array
|
||||
{
|
||||
$result = parent::toArrayData();
|
||||
$result['value'] = [];
|
||||
foreach ($this->value as $obj) {
|
||||
$result['value'][] = $obj->jsonSerialize();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subsite slug by subsitedata
|
||||
*
|
||||
* @param object|array<string, mixed> $subsite subsite info
|
||||
* @param string $mainUrl main site url
|
||||
* @param bool $isSubdomain if true is subdomain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getSubsiteSlug($subsite, $mainUrl, $isSubdomain): string
|
||||
{
|
||||
$subsite = (object) $subsite;
|
||||
if ($isSubdomain) {
|
||||
$mainDomain = SnapURL::wwwRemove(SnapURL::parseUrl($mainUrl, PHP_URL_HOST));
|
||||
$subDomain = SnapURL::wwwRemove($subsite->domain);
|
||||
|
||||
if ($subDomain == $mainDomain) {
|
||||
return '/';
|
||||
} elseif (strpos($subDomain, '.' . $mainDomain) !== false) {
|
||||
return (string)substr($subDomain, 0, strpos($subDomain, '.' . $mainDomain));
|
||||
} else {
|
||||
return $subDomain;
|
||||
}
|
||||
} else {
|
||||
$maiPath = SnapIO::trailingslashit((string) SnapURL::parseUrl($mainUrl, PHP_URL_PATH));
|
||||
$subsitePath = SnapIO::trailingslashit($subsite->path);
|
||||
|
||||
if ($maiPath == $subsitePath) {
|
||||
return '/';
|
||||
} else {
|
||||
return trim(SnapIO::getRelativePath($subsitePath, $maiPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subsites list in packages
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getSourceIdsOptions()
|
||||
{
|
||||
static $sourceOpt = null;
|
||||
|
||||
if (is_null($sourceOpt)) {
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$sourceOpt = [];
|
||||
|
||||
foreach ($archiveConfig->subsites as $subsite) {
|
||||
$option = new ParamOption(
|
||||
$subsite->id,
|
||||
$subsite->path,
|
||||
ParamFormSitesOwrMap::getSourceIdOptionStatus($subsite)
|
||||
);
|
||||
$option->setOptGroup($subsite->domain);
|
||||
|
||||
$sourceOpt[] = $option;
|
||||
}
|
||||
}
|
||||
return $sourceOpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $subsite Sub Site Object
|
||||
*
|
||||
* @return string String that indicated if the option should be enabled or disabled
|
||||
*/
|
||||
public static function getSourceIdOptionStatus($subsite): string
|
||||
{
|
||||
return (self::isQualifiedSourceIdForImport($subsite))
|
||||
? ParamOption::OPT_ENABLED
|
||||
: ParamOption::OPT_DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $subsite Sub Site Object
|
||||
*
|
||||
* @return bool true or false if the site object if the source can be imported
|
||||
*/
|
||||
public static function isQualifiedSourceIdForImport($subsite): bool
|
||||
{
|
||||
return (!InstState::isImportFromBackendMode() || count($subsite->filteredTables) === 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default source id
|
||||
*
|
||||
* @param int $index default index
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
protected static function getDefaultSourceId($index = 0)
|
||||
{
|
||||
if (!isset(DUPX_ArchiveConfig::getInstance()->subsites[$index])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DUPX_ArchiveConfig::getInstance()->subsites[$index]->id;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
protected static function getSubSiteIdsAcceptValues(): array
|
||||
{
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$acceptValues = [-1];
|
||||
foreach ($archiveConfig->subsites as $subsite) {
|
||||
if (ParamFormSitesOwrMap::isQualifiedSourceIdForImport($subsite)) {
|
||||
$acceptValues[] = $subsite->id;
|
||||
}
|
||||
}
|
||||
return $acceptValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing subsites list on import site
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getTargetIdsOptions()
|
||||
{
|
||||
static $targetOpt = null;
|
||||
|
||||
if (is_null($targetOpt)) {
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$targetOpt = [];
|
||||
|
||||
if (!is_array($overwriteData) || !isset($overwriteData['subsites'])) {
|
||||
return $targetOpt;
|
||||
}
|
||||
|
||||
$targetOpt[] = new ParamOption(
|
||||
SiteOwrMap::NEW_SUBSITE_WITH_SLUG,
|
||||
'New ' . ($overwriteData['subdomain'] ? 'Domain' : 'Path'),
|
||||
ParamOption::OPT_ENABLED
|
||||
);
|
||||
|
||||
$targetOpt[] = new ParamOption(
|
||||
SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN,
|
||||
'New URL',
|
||||
ParamOption::OPT_ENABLED
|
||||
);
|
||||
|
||||
foreach ($overwriteData['subsites'] as $subsite) {
|
||||
$subsite = (object) $subsite;
|
||||
$option = new ParamOption(
|
||||
$subsite->id,
|
||||
$subsite->path,
|
||||
ParamOption::OPT_ENABLED
|
||||
);
|
||||
$option->setOptGroup($subsite->domain);
|
||||
$targetOpt[] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
return $targetOpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra fata for data attribute list
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getListExtraData()
|
||||
{
|
||||
if (is_null($this->extraData)) {
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$this->extraData = [
|
||||
'minListItems' => $this->minListItems,
|
||||
'softLimit' => static::SOFT_LIMIT_NUM,
|
||||
'hardLimit' => static::HARD_LIMIT_NUM,
|
||||
];
|
||||
|
||||
$isSubdomain = ($archiveConfig->mu_mode == 1);
|
||||
$mainSiteUrl = $archiveConfig->getRealValue('siteUrl');
|
||||
$this->extraData['sourceInfo'] = [
|
||||
'numSites' => count(static::getSourceIdsOptions()),
|
||||
'urlScheme' => static::getUrlScheme($mainSiteUrl),
|
||||
'urlPrefix' => static::prefixSlugByURL($mainSiteUrl, $isSubdomain),
|
||||
'urlPostfix' => static::postfixSlugByURL($mainSiteUrl, $isSubdomain),
|
||||
'sites' => [],
|
||||
];
|
||||
foreach ($archiveConfig->subsites as $subsite) {
|
||||
$this->extraData['sourceInfo']['sites']['id_' . $subsite->id] = [
|
||||
'domain' => $subsite->domain,
|
||||
'path' => $subsite->path,
|
||||
'slug' => static::getSubsiteSlug($subsite, $mainSiteUrl, $isSubdomain),
|
||||
/** @todo remove */
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
$targetData = $this->getTargetData();
|
||||
$isSubdomain = $targetData['isSubdomain'];
|
||||
$mainSiteUrl = $targetData['mainSiteUrl'];
|
||||
|
||||
$this->extraData['targetInfo'] = [
|
||||
'numSites' => count(static::getTargetIdsOptions()),
|
||||
'urlScheme' => static::getUrlScheme($mainSiteUrl),
|
||||
'urlPrefix' => static::prefixSlugByURL($mainSiteUrl, $isSubdomain),
|
||||
'urlPostfix' => static::postfixSlugByURL($mainSiteUrl, $isSubdomain),
|
||||
'sites' => [
|
||||
'id_' . SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN => [
|
||||
'id' => SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN,
|
||||
'slug' => '_____/_____',
|
||||
'domain' => '',
|
||||
'path' => '',
|
||||
],
|
||||
'id_' . SiteOwrMap::NEW_SUBSITE_WITH_SLUG => [
|
||||
'id' => SiteOwrMap::NEW_SUBSITE_WITH_SLUG,
|
||||
'slug' => '_____',
|
||||
'domain' => '',
|
||||
/** @todo set set according to the type of multisite */
|
||||
'path' => '',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($targetData['subsites'] as $subsite) {
|
||||
$subsite = (object) $subsite;
|
||||
$this->extraData['targetInfo']['sites']['id_' . $subsite->id] = [
|
||||
'slug' => static::getSubsiteSlug($subsite, $mainSiteUrl, $isSubdomain),
|
||||
'domain' => $subsite->domain,
|
||||
'path' => $subsite->path,
|
||||
];
|
||||
}
|
||||
}
|
||||
return $this->extraData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return target data
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getTargetData(): array
|
||||
{
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
$result = [];
|
||||
$result['isSubdomain'] = $overwriteData['subdomain'];
|
||||
$result['mainSiteUrl'] = $overwriteData['urls']['home'];
|
||||
$result['subsites'] = $overwriteData['subsites'];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL scheme
|
||||
*
|
||||
* @param string $url URL input
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getUrlScheme($url): string
|
||||
{
|
||||
return SnapURL::parseUrl($url, PHP_URL_SCHEME) . '://';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prefix URL slug
|
||||
*
|
||||
* @param string $url URL string
|
||||
* @param bool $isSubdomain if true is subdomain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function prefixSlugByURL($url, $isSubdomain = false): string
|
||||
{
|
||||
if ($isSubdomain) {
|
||||
return '';
|
||||
} else {
|
||||
$parseUrl = SnapURL::parseUrl($url);
|
||||
return $parseUrl['host'] . SnapIO::trailingslashit($parseUrl['path']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get postifx URL slug
|
||||
*
|
||||
* @param string $url URL string
|
||||
* @param bool $isSubdomain if true is subdomain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function postfixSlugByURL($url, $isSubdomain = false): string
|
||||
{
|
||||
if (!$isSubdomain) {
|
||||
return '/';
|
||||
}
|
||||
$parseUrl = SnapURL::parseUrl($url);
|
||||
return '.' . SnapURL::wwwRemove($parseUrl['host']) . SnapIO::trailingslashit($parseUrl['path']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
/**
|
||||
* this class handles the entire block selection block.
|
||||
*/
|
||||
class ParamFormTables extends ParamForm
|
||||
{
|
||||
const TYPE_ARRAY_TABLES = 'arraytbl';
|
||||
const FORM_TYPE_TABLES_SELECT = 'tablessel';
|
||||
const TABLE_ITEM_POSTFIX = '_item';
|
||||
const TABLE_NAME_POSTFIX_TNAME = '_tname';
|
||||
const TABLE_NAME_POSTFIX_EXTRACT = '_extract';
|
||||
const TABLE_NAME_POSTFIX_REPLACE = '_replace';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name param identifier
|
||||
* @param string $type Enum: TYPE_STRING | TYPE_ARRAY_STRING | ...
|
||||
* @param string $formType FORM_TYPE_HIDDEN | FORM_TYPE_TEXT | ...
|
||||
* @param array<string, mixed> $attr list of attributes
|
||||
* @param array<string, mixed> $formAttr list of form attributes
|
||||
*/
|
||||
public function __construct($name, $type, $formType, array $attr = [], array $formAttr = [])
|
||||
{
|
||||
if ($type != self::TYPE_ARRAY_TABLES) {
|
||||
throw new \Exception('the type must be ' . self::TYPE_ARRAY_TABLES);
|
||||
}
|
||||
|
||||
if ($formType != self::FORM_TYPE_TABLES_SELECT) {
|
||||
throw new \Exception('the form type must be ' . self::FORM_TYPE_TABLES_SELECT);
|
||||
}
|
||||
parent::__construct($name, $type, $formType, $attr, $formAttr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlItem()
|
||||
{
|
||||
if ($this->formType == self::FORM_TYPE_TABLES_SELECT) {
|
||||
$this->tablesSelectHtml();
|
||||
} else {
|
||||
parent::htmlItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render tables selector HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function tablesSelectHtml()
|
||||
{
|
||||
$tables = \DUPX_DB_Tables::getInstance();
|
||||
$value = $this->getInputValue();
|
||||
?>
|
||||
<table id="plugins_list_table_selector" class="list_table_selector list-import-upt-tables">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="name"></td>
|
||||
<td class="info toggle-all">
|
||||
Toggle All
|
||||
</td>
|
||||
<td class="action">
|
||||
<span title="Check all Extract" class="checkbox-switch">
|
||||
<input class="select-all-import" checked type="checkbox" >
|
||||
<span class="slider"></span>
|
||||
</span>
|
||||
</td>
|
||||
<td class="action">
|
||||
<span title="Check all Replace" class="checkbox-switch">
|
||||
<input class="select-all-replace" checked type="checkbox">
|
||||
<span class="slider"></span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="name">Original Name</th>
|
||||
<th class="info">New Name</th>
|
||||
<th class="action">Import</th>
|
||||
<th class="action">Update</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$index = 0;
|
||||
foreach ($value as $name => $tableVals) {
|
||||
$this->tableHtmlItem($tableVals, $tables->getTableObjByName($name), $index);
|
||||
$index++;
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th class="name">Original Name</th>
|
||||
<th class="info">New Name</th>
|
||||
<th class="action">Import</th>
|
||||
<th class="action">Update</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Renter tables items selector
|
||||
*
|
||||
* @param array<string, array{name: string, extract: bool, replace: bool}> $vals form values
|
||||
* @param \DUPX_DB_Table_item $tableOjb table object
|
||||
* @param integer $index infex of current item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function tableHtmlItem($vals, \DUPX_DB_Table_item $tableOjb, $index)
|
||||
{
|
||||
$itemClasses = [
|
||||
'table-item',
|
||||
$this->getFormItemId() . self::TABLE_ITEM_POSTFIX,
|
||||
];
|
||||
$hiddenNameAttrs = [
|
||||
'id' => $this->getFormItemId() . self::TABLE_NAME_POSTFIX_TNAME . '_' . $index,
|
||||
'type' => 'hidden',
|
||||
'name' => $this->getName() . '[]',
|
||||
'class' => $this->getFormItemId() . self::TABLE_NAME_POSTFIX_TNAME,
|
||||
'value' => $tableOjb->getOriginalName(),
|
||||
];
|
||||
$extractCheckboxAttrs = [
|
||||
'id' => $this->getFormItemId() . self::TABLE_NAME_POSTFIX_EXTRACT . '_' . $index,
|
||||
'name' => $this->getName() . self::TABLE_NAME_POSTFIX_EXTRACT . '[]',
|
||||
'class' => $this->getFormItemId() . self::TABLE_NAME_POSTFIX_EXTRACT,
|
||||
'value' => 1,
|
||||
];
|
||||
$replaceCheckboxAttrs = [
|
||||
'id' => $this->getFormItemId() . self::TABLE_NAME_POSTFIX_REPLACE . '_' . $index,
|
||||
'name' => $this->getName() . self::TABLE_NAME_POSTFIX_REPLACE . '[]',
|
||||
'class' => $this->getFormItemId() . self::TABLE_NAME_POSTFIX_REPLACE,
|
||||
'value' => 1,
|
||||
];
|
||||
|
||||
if ($tableOjb->canBeExctracted()) {
|
||||
if ($vals['extract']) {
|
||||
$extractCheckboxAttrs['checked'] = '';
|
||||
}
|
||||
|
||||
if ($vals['replace']) {
|
||||
$replaceCheckboxAttrs['checked'] = '';
|
||||
}
|
||||
} else {
|
||||
$itemClasses[] = 'no-display';
|
||||
$extractCheckboxAttrs['disabled'] = '';
|
||||
$replaceCheckboxAttrs['disabled'] = '';
|
||||
}
|
||||
|
||||
if ($this->isDisabled() || $this->isReadonly()) {
|
||||
$extractCheckboxAttrs['disabled'] = '';
|
||||
$replaceCheckboxAttrs['disabled'] = '';
|
||||
|
||||
$skipSendValue = true;
|
||||
} else {
|
||||
$skipSendValue = false;
|
||||
}
|
||||
?>
|
||||
<tr class="<?php echo implode(' ', $itemClasses); ?>" >
|
||||
<td class="name" >
|
||||
<span class="table-name" ><?php echo \DUPX_U::esc_html($tableOjb->getOriginalName()); ?></span><br>
|
||||
Rows: <b><?php echo $tableOjb->getRows(); ?></b> Size: <b><?php echo $tableOjb->getSize(true); ?></b>
|
||||
</td>
|
||||
<td class="info" >
|
||||
<span class="table-name" ><b><?php echo \DUPX_U::esc_html($tableOjb->getNewName()); ?></b></span><br>
|
||||
|
||||
</td>
|
||||
<td class="action extract" >
|
||||
<?php
|
||||
if (!$skipSendValue) {
|
||||
// if is disabled or readonly don't senta tables nme so params isn't updated
|
||||
?>
|
||||
<input <?php echo \DUPX_U_Html::arrayAttrToHtml($hiddenNameAttrs); ?> >
|
||||
<?php
|
||||
}
|
||||
\DUPX_U_Html::checkboxSwitch(
|
||||
$extractCheckboxAttrs,
|
||||
['title' => 'Extract in database']
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
<td class="action replace" >
|
||||
<?php
|
||||
\DUPX_U_Html::checkboxSwitch(
|
||||
$replaceCheckboxAttrs,
|
||||
['title' => 'Apply replace engine at URLs and paths in database']
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if value is valid
|
||||
*
|
||||
* @param mixed $value value
|
||||
* @param mixed $validateValue variable passed by reference. Updated to validated value in the case, the value is a valid value.
|
||||
*
|
||||
* @return bool true if is a valid value for this object
|
||||
*/
|
||||
public function isValid($value, &$validateValue = null): bool
|
||||
{
|
||||
$validateValue = (array) $value;
|
||||
|
||||
$availableTables = \DUPX_DB_Tables::getInstance()->getTablesNames();
|
||||
$validateTables = array_keys($validateValue);
|
||||
|
||||
// all tables in list have to exist in available tables
|
||||
foreach ($validateValue as $table => $tableValues) {
|
||||
if (!in_array($table, $availableTables)) {
|
||||
Log::info('INVALID ' . $table . ' ISN\'T IN AVAILABLE LIST: ' . Log::v2str($availableTables));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// all tables abaliable have to exists in list
|
||||
foreach ($availableTables as $avaibleTable) {
|
||||
if (!in_array($avaibleTable, $validateTables)) {
|
||||
Log::info('AVAILABLE ' . $avaibleTable . ' ISN\'T IN PARAM LIST TABLE');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appli filter to value input
|
||||
*
|
||||
* @param array<string, mixed> $superObject query string values
|
||||
*
|
||||
* @return array<string, array{name: string, extract: bool, replace: bool}>
|
||||
*/
|
||||
public function getValueFilter($superObject): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if (($tables = json_decode($superObject[$this->getName()])) == false) {
|
||||
throw new \Exception('Invalid json string');
|
||||
}
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$table = (array) $table;
|
||||
if ($table['extract'] == false) {
|
||||
// replace can't be true if extract if false
|
||||
$table['replace'] = false;
|
||||
}
|
||||
$result[$table['name']] = $table;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sanitized value
|
||||
*
|
||||
* @param mixed $value value input
|
||||
*
|
||||
* @return array<string, array{name: string, extract: bool,replace: bool}>
|
||||
*/
|
||||
public function getSanitizeValue($value): array
|
||||
{
|
||||
$newValues = (array) $value;
|
||||
$sanitizeValues = [];
|
||||
|
||||
foreach ($newValues as $key => $newValue) {
|
||||
$sanitizedKey = SnapUtil::sanitizeNSCharsNewlineTrim($key);
|
||||
$newValue = (array) $newValue;
|
||||
|
||||
$sanitizedNewValue = self::getParamItemValueFromData();
|
||||
$sanitizedNewValue['name'] = isset($newValue['name']) ? SnapUtil::sanitizeNSCharsNewlineTrim($newValue['name']) : '';
|
||||
$sanitizedNewValue['extract'] = isset($newValue['extract']) && filter_var($newValue['extract'], FILTER_VALIDATE_BOOLEAN);
|
||||
$sanitizedNewValue['replace'] = isset($newValue['replace']) && filter_var($newValue['replace'], FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
$sanitizeValues[$sanitizedKey] = $sanitizedNewValue;
|
||||
}
|
||||
return $sanitizeValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default type attributes
|
||||
*
|
||||
* @param string $type param value type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForType($type): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForType($type);
|
||||
if ($type == self::TYPE_ARRAY_TABLES) {
|
||||
$attrs['default'] = [];
|
||||
}
|
||||
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default form attributes
|
||||
*
|
||||
* @param string $formType form type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForFormType($formType): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForFormType($formType);
|
||||
if ($formType == self::FORM_TYPE_TABLES_SELECT) {
|
||||
$attrs['wrapperContainerTag'] = 'div';
|
||||
$attrs['inputContainerTag'] = 'div';
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return param item from data
|
||||
*
|
||||
* @param string $name table name
|
||||
* @param bool $extract extract
|
||||
* @param bool $replace replace
|
||||
*
|
||||
* @return array{name: string, extract: bool, replace: bool}
|
||||
*/
|
||||
public static function getParamItemValueFromData($name = '', $extract = false, $replace = false): array
|
||||
{
|
||||
return [
|
||||
'name' => $name,
|
||||
'extract' => $extract,
|
||||
'replace' => $replace,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
use Duplicator\Installer\Core\Params\Models\SiteOwrMap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use DUPX_ArchiveConfig;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* this class handles the entire block selection block.
|
||||
*/
|
||||
class ParamFormURLMapping extends ParamFormSitesOwrMap
|
||||
{
|
||||
const SOFT_LIMIT_NUM = PHP_INT_MAX;
|
||||
const HARD_LIMIT_NUM = PHP_INT_MAX;
|
||||
|
||||
const FORM_TYPE_URL_MAPPING = 'url_mapping';
|
||||
|
||||
/** @var int<-1, max> */
|
||||
protected $currentSubsiteId = -1;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name param identifier
|
||||
* @param string $type Enum: TYPE_STRING | TYPE_ARRAY_STRING | ...
|
||||
* @param string $formType FORM_TYPE_HIDDEN | FORM_TYPE_TEXT | ...
|
||||
* @param array<string, mixed> $attr list of attributes
|
||||
* @param array<string, mixed> $formAttr list of form attributes
|
||||
*/
|
||||
public function __construct($name, $type, $formType, array $attr = [], array $formAttr = [])
|
||||
{
|
||||
if ($type != self::TYPE_ARRAY_SITES_OWR_MAP) {
|
||||
throw new Exception('the type must be ' . self::TYPE_ARRAY_SITES_OWR_MAP);
|
||||
}
|
||||
|
||||
if ($formType != self::FORM_TYPE_URL_MAPPING) {
|
||||
throw new Exception('the form type must be ' . self::FORM_TYPE_URL_MAPPING);
|
||||
}
|
||||
|
||||
ParamForm::__construct($name, $type, $formType, $attr, $formAttr);
|
||||
$this->minListItems = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlItem()
|
||||
{
|
||||
if ($this->formType == self::FORM_TYPE_URL_MAPPING) {
|
||||
$this->sitesOrwHtml();
|
||||
} else {
|
||||
parent::htmlItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return soft limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getEmptyListMessage(): string
|
||||
{
|
||||
return 'It\'s possible customize the subfolder/subodmains or full domains of subsites, <br>' .
|
||||
'to change the main site use the "new site URL" option in the advanced mode.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return soft limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getSoftLimitMessage(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hard limit message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getHardLimitMesssage(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get add itm button label
|
||||
*
|
||||
* @param string $key label key
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getItemsLabels($key): string
|
||||
{
|
||||
$paramLabels = [
|
||||
'addItem' => 'Add Custom URL',
|
||||
'sourceSite' => 'Source Site',
|
||||
'targetSite' => 'Custom URL',
|
||||
];
|
||||
|
||||
return ($paramLabels[$key] ?? 'unknown label key');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return target data
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getTargetData(): array
|
||||
{
|
||||
$result = [];
|
||||
$result['isSubdomain'] = (DUPX_ArchiveConfig::getInstance()->mu_mode === 1);
|
||||
$result['mainSiteUrl'] = PrmMng::getInstance()->getValue(PrmMng::PARAM_URL_NEW);
|
||||
$result['subsites'] = [];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subsites list in packages
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getSourceIdsOptions()
|
||||
{
|
||||
$mainSiteId = DUPX_ArchiveConfig::getInstance()->main_site_id;
|
||||
$options = parent::getSourceIdsOptions();
|
||||
foreach ($options as $index => $opt) {
|
||||
if ($opt->value == $mainSiteId) {
|
||||
unset($options[$index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return array_values($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing subsites list on import site
|
||||
*
|
||||
* @return ParamOption[]
|
||||
*/
|
||||
public static function getTargetIdsOptions()
|
||||
{
|
||||
static $targetOpt = null;
|
||||
|
||||
if (is_null($targetOpt)) {
|
||||
$targetOpt[] = new ParamOption(
|
||||
SiteOwrMap::NEW_SUBSITE_WITH_SLUG,
|
||||
'New ' . (DUPX_ArchiveConfig::getInstance()->mu_mode == 1 ? 'Domain' : 'Path'),
|
||||
ParamOption::OPT_ENABLED
|
||||
);
|
||||
|
||||
$targetOpt[] = new ParamOption(
|
||||
SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN,
|
||||
'New URL',
|
||||
ParamOption::OPT_ENABLED
|
||||
);
|
||||
}
|
||||
|
||||
return $targetOpt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
/**
|
||||
* this class manages a password type input with the hide / show passwrd button
|
||||
*/
|
||||
class ParamFormUsersReset extends ParamFormPass
|
||||
{
|
||||
const FORM_TYPE_USERS_PWD_RESET = 'usrpwdreset';
|
||||
|
||||
/** @var int<-1, max> */
|
||||
protected $currentUserId = -1;
|
||||
|
||||
/**
|
||||
* Get html form option of current item
|
||||
*
|
||||
* @param bool $echo if true echo html
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHtml($echo = true)
|
||||
{
|
||||
if ($this->formType == self::FORM_TYPE_USERS_PWD_RESET) {
|
||||
$result = '';
|
||||
$users = \DUPX_ArchiveConfig::getInstance()->getUsersLists();
|
||||
|
||||
$mainInputId = $this->formAttr['id'];
|
||||
foreach ($users as $userId => $login) {
|
||||
$this->currentUserId = $userId;
|
||||
$this->formAttr['id'] = $mainInputId . '_' . $this->currentUserId;
|
||||
$this->formAttr['label'] = $login;
|
||||
$result .= parent::getHtml($echo);
|
||||
}
|
||||
$this->currentUserId = -1;
|
||||
$this->formAttr['id'] = $mainInputId;
|
||||
return $result;
|
||||
} else {
|
||||
return parent::getHtml($echo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the html input of current item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlItem()
|
||||
{
|
||||
if ($this->formType == self::FORM_TYPE_USERS_PWD_RESET) {
|
||||
$this->pwdToggleHtml();
|
||||
} else {
|
||||
parent::htmlItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return attribute name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAttrName(): string
|
||||
{
|
||||
return $this->name . '[' . $this->currentUserId . ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return input value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getInputValue()
|
||||
{
|
||||
return $this->value[$this->currentUserId] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default form attributes
|
||||
*
|
||||
* @param string $formType form type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForFormType($formType): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForFormType($formType);
|
||||
if ($formType == self::FORM_TYPE_USERS_PWD_RESET) {
|
||||
$attrs['maxLength'] = null; // if null have no limit
|
||||
$attrs['size'] = null;
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
|
||||
/**
|
||||
* this class manages a password type input with the hide / show passwrd button
|
||||
*/
|
||||
class ParamFormWpConfig extends ParamForm
|
||||
{
|
||||
const IN_WP_CONF_POSTFIX = '_inwpc';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name param identifier
|
||||
* @param string $type TYPE_STRING | TYPE_ARRAY_STRING | ...
|
||||
* @param string $formType FORM_TYPE_HIDDEN | FORM_TYPE_TEXT | ...
|
||||
* @param array<string, mixed> $attr list of attributes
|
||||
* @param array<string, mixed> $formAttr list of form attributes
|
||||
*/
|
||||
public function __construct($name, $type, $formType, array $attr = [], array $formAttr = [])
|
||||
{
|
||||
parent::__construct($name, $type, $formType, $attr, $formAttr);
|
||||
$this->attr['defaultFromInput'] = $this->attr['default'];
|
||||
$this->attr['defaultFromInput']['inWpConfig'] = false;
|
||||
|
||||
if ($type === self::TYPE_BOOL) {
|
||||
$this->attr['defaultFromInput']['value'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this function is calle before sanitization.
|
||||
* Is use in extendend classs and transform value before the sanitization and validation process
|
||||
*
|
||||
* @param array<string,mixed> $superObject query string super object
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getValueFilter($superObject): array
|
||||
{
|
||||
$result = [
|
||||
'value' => parent::getValueFilter($superObject),
|
||||
'inWpConfig' => filter_var($superObject[$this->name . self::IN_WP_CONF_POSTFIX], FILTER_VALIDATE_BOOLEAN),
|
||||
];
|
||||
|
||||
if (!parent::isValueInInput($superObject)) {
|
||||
$result['value'] = $this->attr['defaultFromInput']['value'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sanitized value
|
||||
*
|
||||
* @param mixed $value input value
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getSanitizeValue($value): array
|
||||
{
|
||||
$result = (array) $value;
|
||||
$result['value'] = parent::getSanitizeValue($result['value']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value info from value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function valueToInfo(): string
|
||||
{
|
||||
if ($this->value['inWpConfig']) {
|
||||
return 'Set in wp config with value ' . parent::valueToInfo();
|
||||
} else {
|
||||
return 'Not set in wp config';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return input value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getInputValue()
|
||||
{
|
||||
return $this->value['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if value is in input method
|
||||
*
|
||||
* @param array<string, mixed> $superObject query string super object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValueInInput($superObject): bool
|
||||
{
|
||||
return parent::isValueInInput($superObject) || isset($superObject[$this->name . self::IN_WP_CONF_POSTFIX]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if input value is valid
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param mixed $validateValue variable passed by reference. Updated to validated value in the case, the value is a valid value.
|
||||
*
|
||||
* @return bool true if is a valid value for this object
|
||||
*/
|
||||
public function isValid($value, &$validateValue = null): bool
|
||||
{
|
||||
if (!is_array($value) || !isset($value['value']) || !isset($value['inWpConfig'])) {
|
||||
Log::info('WP CONFIG INVALID ARRAY VAL:' . Log::v2str($value));
|
||||
return false;
|
||||
}
|
||||
|
||||
// IF isn't in wp config the value isn't validate
|
||||
if ($value['inWpConfig'] === false) {
|
||||
$validateValue = $value;
|
||||
return true;
|
||||
} else {
|
||||
$confValidValue = $value['value'];
|
||||
if (parent::isValid($value['value'], $confValidValue) === false) {
|
||||
Log::info('WP CONFIG INVALID VALUE:' . Log::v2str($confValidValue));
|
||||
return false;
|
||||
} else {
|
||||
$validateValue = $value;
|
||||
$validateValue['value'] = $confValidValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML input before content
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function htmlInputContBefore()
|
||||
{
|
||||
if ($this->getFormStatus() == self::STATUS_INFO_ONLY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->value['inWpConfig']) {
|
||||
$this->formAttr['inputContainerClasses'][] = 'no-display';
|
||||
if ($this->formAttr['status'] == self::STATUS_ENABLED) {
|
||||
$this->formAttr['status'] = self::STATUS_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
$inputAttrs = [
|
||||
'name' => $this->name . self::IN_WP_CONF_POSTFIX,
|
||||
'value' => 1,
|
||||
];
|
||||
if ($this->value['inWpConfig']) {
|
||||
$inputAttrs['checked'] = 'checked';
|
||||
}
|
||||
echo '<span class="wpinconf-check-wrapper" >';
|
||||
\DUPX_U_Html::checkboxSwitch(
|
||||
$inputAttrs,
|
||||
['title' => 'Add in wp config']
|
||||
);
|
||||
echo '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return the default attr for each type.
|
||||
* in the constructor an array merge is made between the result of this function and the parameters passed.
|
||||
* In this way the values in $ this -> ['attr'] are always consistent.
|
||||
*
|
||||
* @param string $type param value type
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForType($type): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForType($type);
|
||||
$valFromInput = $attrs['defaultFromInput'];
|
||||
|
||||
$attrs['defaultFromInput'] = [
|
||||
'value' => $valFromInput,
|
||||
'inWpConfig' => false,
|
||||
];
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function return the default formAttr for each type.
|
||||
* in the constructor an array merge is made between the result of this function and the parameters passed.
|
||||
* In this way the values in $ this -> ['attr'] are always consistent.
|
||||
*
|
||||
* @param string $formType form type
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForFormType($formType): array
|
||||
{
|
||||
$attrs = parent::getDefaultAttrForFormType($formType);
|
||||
|
||||
$attrs['wrapperClasses'][] = 'wp-config-item';
|
||||
$attrs['wrapperContainerTag'] = 'div';
|
||||
return $attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
|
||||
/**
|
||||
* this class describes the value of a parameter.
|
||||
* therefore the type of data is sanitization and validation.
|
||||
* In addition to other features such as, for example, if it is a persistent parameter.
|
||||
*/
|
||||
class ParamItem
|
||||
{
|
||||
const INPUT_GET = 'g';
|
||||
const INPUT_POST = 'p';
|
||||
const INPUT_REQUEST = 'r';
|
||||
const INPUT_COOKIE = 'c';
|
||||
const INPUT_SERVER = 's';
|
||||
const INPUT_ENV = 'e';
|
||||
const TYPE_STRING = 'str';
|
||||
const TYPE_ARRAY_STRING = 'arr_str';
|
||||
const TYPE_ARRAY_MIXED = 'arr_mix';
|
||||
const TYPE_INT = 'int';
|
||||
const TYPE_ARRAY_INT = 'arr_int';
|
||||
const TYPE_BOOL = 'bool';
|
||||
const STATUS_INIT = 'init';
|
||||
const STATUS_OVERWRITE = 'owr';
|
||||
const STATUS_UPD_FROM_INPUT = 'updinp';
|
||||
|
||||
/**
|
||||
* validate regexes for test input
|
||||
*/
|
||||
const VALIDATE_REGEX_INT_NUMBER = '/^[\+\-]?[0-9]+$/';
|
||||
const VALIDATE_REGEX_INT_NUMBER_EMPTY = '/^[\+\-]?[0-9]*$/'; // can be empty
|
||||
const VALIDATE_REGEX_AZ_NUMBER = '/^[A-Za-z0-9]+$/';
|
||||
const VALIDATE_REGEX_AZ_NUMBER_EMPTY = '/^[A-Za-z0-9]*$/'; // can be empty
|
||||
const VALIDATE_REGEX_AZ_NUMBER_SEP = '/^[A-Za-z0-9_\-]+$/'; // laddate Az 09 plus - and _
|
||||
const VALIDATE_REGEX_AZ_NUMBER_SEP_EMPTY = '/^[A-Za-z0-9_\-]*$/'; // laddate Az 09 plus - and _, can be empty
|
||||
const VALIDATE_REGEX_DIR_PATH = '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/';
|
||||
const VALIDATE_REGEX_FILE_PATH = '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/';
|
||||
|
||||
/** @var string */
|
||||
protected $name = '';
|
||||
/** @var string */
|
||||
protected $type = '';
|
||||
/** @var array<string, mixed> */
|
||||
protected array $attr;
|
||||
/** @var mixed */
|
||||
protected $value;
|
||||
/** @var string */
|
||||
protected $status = self::STATUS_INIT;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $name param identifier
|
||||
* @param string $type TYPE_STRING | TYPE_ARRAY_STRING | ...
|
||||
* @param array<string, mixed> $attr list of attributes
|
||||
*/
|
||||
public function __construct($name, $type, array $attr = [])
|
||||
{
|
||||
if (empty($name) || strlen($name) < 4) {
|
||||
throw new \Exception('the name can\'t be empty or len can\'t be minor of 4');
|
||||
}
|
||||
$this->type = $type;
|
||||
$this->attr = array_merge(static::getDefaultAttrForType($type), (array) $attr);
|
||||
if ($type == self::TYPE_ARRAY_STRING || $type == self::TYPE_ARRAY_INT || $type == self::TYPE_ARRAY_MIXED) {
|
||||
$this->attr['default'] = (array) $this->attr['default'];
|
||||
}
|
||||
$this->name = $name;
|
||||
$this->value = $this->getSanitizeValue($this->attr['default']);
|
||||
|
||||
if (is_null($this->attr['defaultFromInput'])) {
|
||||
$this->attr['defaultFromInput'] = $this->attr['default'];
|
||||
} else {
|
||||
if ($type == self::TYPE_ARRAY_STRING || $type == self::TYPE_ARRAY_INT || $type == self::TYPE_ARRAY_MIXED) {
|
||||
$this->attr['defaultFromInput'] = (array) $this->attr['defaultFromInput'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this funtion return the discursive label
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current item identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current item value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string // STATUS_INIT | STATUS_OVERWRITE | STATUS_UPD_FROM_INPUT
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item status with overwrite value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOveriteStatus(): void
|
||||
{
|
||||
$this->status = self::STATUS_OVERWRITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* if it is true, this object is defined as persistent and will be saved in the parameter persistence file otherwise the param manager
|
||||
* will not save this value and at each call of the script the parameter will assume the default value.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPersistent()
|
||||
{
|
||||
return $this->attr['persistence'];
|
||||
}
|
||||
|
||||
/**
|
||||
* return the invalid param message or empty string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInvalidMessage()
|
||||
{
|
||||
if (is_callable($this->attr['invalidMessage'])) {
|
||||
return call_user_func($this->attr['invalidMessage'], $this);
|
||||
} else {
|
||||
return (string) $this->attr['invalidMessage'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the invalid param message
|
||||
*
|
||||
* @param string $message invalid message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInvalidMessage($message): void
|
||||
{
|
||||
$this->attr['invalidMessage'] = (string) $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update item attribute
|
||||
*
|
||||
* @param string $key attribute key
|
||||
* @param mixed $value value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setAttr($key, $value): void
|
||||
{
|
||||
$this->attr[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set param value
|
||||
*
|
||||
* @param mixed $value value to set
|
||||
*
|
||||
* @return boolean false if value isn't validated
|
||||
*/
|
||||
public function setValue($value): bool
|
||||
{
|
||||
$validateValue = null;
|
||||
if (!$this->isValid($value, $validateValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->value = $validateValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get super object from method
|
||||
*
|
||||
* @param string $method query string method
|
||||
*
|
||||
* @return array<string, mixed> return the reference
|
||||
*/
|
||||
protected static function getSuperObjectByMethod($method): array
|
||||
{
|
||||
$superObject = [];
|
||||
switch ($method) {
|
||||
case self::INPUT_GET:
|
||||
$superObject = &$_GET;
|
||||
break;
|
||||
case self::INPUT_POST:
|
||||
$superObject = &$_POST;
|
||||
break;
|
||||
case self::INPUT_REQUEST:
|
||||
$superObject = &$_REQUEST;
|
||||
break;
|
||||
case self::INPUT_COOKIE:
|
||||
$superObject = &$_COOKIE;
|
||||
break;
|
||||
case self::INPUT_SERVER:
|
||||
$superObject = &$_SERVER;
|
||||
break;
|
||||
case self::INPUT_ENV:
|
||||
$superObject = &$_ENV;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('INVALID SUPER OBJECT METHOD ' . Log::v2str($method));
|
||||
}
|
||||
return $superObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if value is in input method
|
||||
*
|
||||
* @param mixed[] $superObject query string super object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValueInInput($superObject): bool
|
||||
{
|
||||
return isset($superObject[$this->name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* update the value from input if exists ot set the default
|
||||
* sanitation and validation are performed
|
||||
*
|
||||
* @param string $method query string method
|
||||
*
|
||||
* @return boolean false if value isn't validated
|
||||
*/
|
||||
public function setValueFromInput($method = self::INPUT_POST)
|
||||
{
|
||||
$superObject = self::getSuperObjectByMethod($method);
|
||||
|
||||
Log::info(
|
||||
'SET VALUE FROM INPUT KEY [' . $this->name . '] VALUE[' .
|
||||
Log::v2str($superObject[$this->name] ?? '') .
|
||||
']',
|
||||
Log::LV_DEBUG
|
||||
);
|
||||
|
||||
if (!$this->isValueInInput($superObject)) {
|
||||
$inputValue = $this->attr['defaultFromInput'];
|
||||
} else {
|
||||
// get value from input
|
||||
$inputValue = $this->getValueFilter($superObject);
|
||||
// sanitize value
|
||||
$inputValue = $this->getSanitizeValue($inputValue);
|
||||
}
|
||||
|
||||
if (($result = $this->setValue($inputValue)) === false) {
|
||||
$msg = 'PARAM [' . $this->name . '] ERROR: Invalid value ' . Log::v2str($inputValue);
|
||||
Log::info($msg);
|
||||
return false;
|
||||
} else {
|
||||
$this->status = self::STATUS_UPD_FROM_INPUT;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if input value is valid
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param mixed $validateValue variable passed by reference. Updated to validated value in the case, the value is a valid value.
|
||||
*
|
||||
* @return bool true if is a valid value for this object
|
||||
*/
|
||||
public function isValid($value, &$validateValue = null)
|
||||
{
|
||||
switch ($this->type) {
|
||||
case self::TYPE_STRING:
|
||||
case self::TYPE_BOOL:
|
||||
case self::TYPE_INT:
|
||||
return $this->isValidScalar($value, $validateValue);
|
||||
case self::TYPE_ARRAY_STRING:
|
||||
case self::TYPE_ARRAY_INT:
|
||||
case self::TYPE_ARRAY_MIXED:
|
||||
return $this->isValidArray($value, $validateValue);
|
||||
default:
|
||||
throw new \Exception('ITEM ERROR invalid type ' . $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate function for scalar value
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param mixed $validateValue variable passed by reference. Updated to validated value in the case, the value is a valid value.
|
||||
*
|
||||
* @return boolean false if value isn't a valid value
|
||||
*/
|
||||
protected function isValidScalar($value, &$validateValue = null)
|
||||
{
|
||||
if (!is_null($value) && !is_scalar($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = true;
|
||||
switch ($this->type) {
|
||||
case self::TYPE_STRING:
|
||||
case self::TYPE_ARRAY_STRING:
|
||||
$validateValue = (string) $value;
|
||||
if (strlen($validateValue) < $this->attr['min_len']) {
|
||||
$this->setInvalidMessage('Must have ' . $this->attr['min_len'] . ' or more characters');
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if ($this->attr['max_len'] > 0 && strlen($validateValue) > $this->attr['max_len']) {
|
||||
$this->setInvalidMessage('Must have max ' . $this->attr['mimax_lenn_len'] . ' characters');
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if (!empty($this->attr['validateRegex']) && preg_match($this->attr['validateRegex'], $validateValue) !== 1) {
|
||||
$this->setInvalidMessage('String isn\'t valid');
|
||||
$result = false;
|
||||
}
|
||||
break;
|
||||
case self::TYPE_INT:
|
||||
case self::TYPE_ARRAY_INT:
|
||||
$validateValue = filter_var($value, FILTER_VALIDATE_INT, [
|
||||
'options' => [
|
||||
'default' => false, // value to return if the filter fails
|
||||
'min_range' => $this->attr['min_range'],
|
||||
'max_range' => $this->attr['max_range'],
|
||||
],
|
||||
]);
|
||||
|
||||
if ($validateValue === false) {
|
||||
$this->setInvalidMessage('Isn\'t a valid number');
|
||||
$result = false;
|
||||
}
|
||||
break;
|
||||
case self::TYPE_BOOL:
|
||||
$validateValue = is_bool($value) ? $value : filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
if (($result = !is_null($validateValue)) === false) {
|
||||
$this->setInvalidMessage('Isn\'t a valid value');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \Exception('ITEM ERROR ' . $this->name . ' Invalid type ' . $this->type);
|
||||
}
|
||||
|
||||
if ($result == true) {
|
||||
$acceptValues = $this->getAcceptValues();
|
||||
if (empty($acceptValues)) {
|
||||
$result = $this->callValidateCallback($validateValue);
|
||||
} else {
|
||||
if (in_array($validateValue, $acceptValues)) {
|
||||
$result = true;
|
||||
} else {
|
||||
$this->setInvalidMessage('Isn\'t a accepted value');
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
$validateValue = null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate function for array value
|
||||
*
|
||||
* @param mixed $value input value
|
||||
* @param mixed $validateValue variable passed by reference. Updated to validated value in the case, the value is a valid value.
|
||||
*
|
||||
* @return boolean false if value isn't a valid value
|
||||
*/
|
||||
protected function isValidArray($value, &$validateValue = null)
|
||||
{
|
||||
$newValues = (array) $value;
|
||||
$validateValue = [];
|
||||
$validValue = null;
|
||||
|
||||
if ($this->type == self::TYPE_ARRAY_MIXED) {
|
||||
$validateValue = $newValues;
|
||||
return $this->callValidateCallback($newValues);
|
||||
} else {
|
||||
foreach ($newValues as $key => $newValue) {
|
||||
if (!$this->isValidScalar($newValue, $validValue)) {
|
||||
return false;
|
||||
}
|
||||
$validateValue[$key] = $validValue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call attribute validate callback
|
||||
*
|
||||
* @param mixed $value input value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callValidateCallback($value)
|
||||
{
|
||||
if (is_callable($this->attr['validateCallback'])) {
|
||||
return call_user_func($this->attr['validateCallback'], $value, $this);
|
||||
} elseif (!is_null($this->attr['validateCallback'])) {
|
||||
throw new \Exception('PARAM ' . $this->name . ' validateCallback isn\'t null and isn\'t callable');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this function is calle before sanitization.
|
||||
* Is use in extendend classs and transform value before the sanitization and validation process
|
||||
*
|
||||
* @param mixed[] $superObject query string super object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getValueFilter($superObject)
|
||||
{
|
||||
if (isset($superObject[$this->name])) {
|
||||
return $superObject[$this->name];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sanitized value
|
||||
*
|
||||
* @param mixed $value input value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSanitizeValue($value)
|
||||
{
|
||||
switch ($this->type) {
|
||||
case self::TYPE_STRING:
|
||||
case self::TYPE_BOOL:
|
||||
case self::TYPE_INT:
|
||||
return $this->getSanitizeValueScalar($value);
|
||||
case self::TYPE_ARRAY_STRING:
|
||||
case self::TYPE_ARRAY_INT:
|
||||
return $this->getSanitizeValueArray($value);
|
||||
case self::TYPE_ARRAY_MIXED:
|
||||
// global sanitize for mixed
|
||||
return $this->getSanitizeValueScalar($value);
|
||||
default:
|
||||
throw new \Exception('ITEM ERROR invalid type ' . $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If sanitizeCallback is apply sanitizeCallback at current value else return value.
|
||||
*
|
||||
* @param mixed $value input value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getSanitizeValueScalar($value)
|
||||
{
|
||||
if (is_callable($this->attr['sanitizeCallback'])) {
|
||||
return call_user_func($this->attr['sanitizeCallback'], $value);
|
||||
} elseif (!is_null($this->attr['sanitizeCallback'])) {
|
||||
throw new \Exception('PARAM ' . $this->name . ' sanitizeCallback isn\'t null and isn\'t callable');
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If sanitizeCallback is apply sanitizeCallback at each value of array.
|
||||
*
|
||||
* @param mixed $value input value
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getSanitizeValueArray($value): array
|
||||
{
|
||||
$newValues = (array) $value;
|
||||
$sanitizeValues = [];
|
||||
|
||||
foreach ($newValues as $key => $newValue) {
|
||||
$sanitizeValues[$key] = $this->getSanitizeValueScalar($newValue);
|
||||
}
|
||||
|
||||
return $sanitizeValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accept values
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function getAcceptValues()
|
||||
{
|
||||
if (is_callable($this->attr['acceptValues'])) {
|
||||
return call_user_func($this->attr['acceptValues'], $this);
|
||||
} else {
|
||||
return $this->attr['acceptValues'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value from array. This function is used to set data from json array
|
||||
*
|
||||
* @param array{value: mixed, status?: string} $data param data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function fromArrayData($data)
|
||||
{
|
||||
$data = (array) $data;
|
||||
if (isset($data['status'])) {
|
||||
$this->status = $data['status'];
|
||||
}
|
||||
|
||||
// only if value is different from current value
|
||||
if (isset($data['value']) && $data['value'] !== $this->value) {
|
||||
$sanitizedVal = $this->getSanitizeValue($data['value']);
|
||||
return $this->setValue($sanitizedVal);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array dato to store in json array data
|
||||
*
|
||||
* @return array{value: mixed, status: string}
|
||||
*/
|
||||
public function toArrayData(): array
|
||||
{
|
||||
return [
|
||||
'value' => $this->value,
|
||||
'status' => $this->status,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this object with a new name ad overwrite attr
|
||||
*
|
||||
* @param string $newName new name
|
||||
* @param array<string, mixed> $attr overwrite attributes
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function getCopyWithNewName($newName, $attr = [])
|
||||
{
|
||||
$copy = clone $this;
|
||||
$reflect = new \ReflectionObject($copy);
|
||||
|
||||
$nameProp = $reflect->getProperty('name');
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$nameProp->setAccessible(true);
|
||||
}
|
||||
$nameProp->setValue($copy, $newName);
|
||||
|
||||
$attrProp = $reflect->getProperty('attr');
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$attrProp->setAccessible(true);
|
||||
}
|
||||
$newAttr = array_merge($attrProp->getValue($copy), $attr);
|
||||
$attrProp->setValue($copy, $newAttr);
|
||||
|
||||
$valueProp = $reflect->getProperty('value');
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$valueProp->setAccessible(true);
|
||||
}
|
||||
$valueProp->setValue($copy, $newAttr['default']);
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return the default attr for each type.
|
||||
* in the constructor an array merge is made between the result of this function and the parameters passed.
|
||||
* In this way the values in $ this -> ['attr'] are always consistent.
|
||||
*
|
||||
* @param string $type param value type
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected static function getDefaultAttrForType($type): array
|
||||
{
|
||||
$attrs = [
|
||||
'default' => null, // the default value on init
|
||||
'defaultFromInput' => null, // if value isn't set in query form when setValueFromInput is called set this valus.
|
||||
// (normally defaultFromInput is equal to default)
|
||||
'acceptValues' => [], // if not empty accept only values in list | callback
|
||||
'sanitizeCallback' => null, // function (ParamItem $obj, $inputValue)
|
||||
'validateCallback' => null, // function (ParamItem $obj, $validateValue, $originalValue)
|
||||
'persistence' => true, // if false don't store value in persistence file
|
||||
'invalidMessage' => '',//this message is added at next step validation error message if not empty
|
||||
];
|
||||
|
||||
switch ($type) {
|
||||
case self::TYPE_STRING: // value type is a string
|
||||
$attrs['min_len'] = 0; // min string len. used in validation
|
||||
$attrs['max_len'] = 0; // max string len. used in validation
|
||||
$attrs['default'] = ''; // set default at empty string
|
||||
$attrs['validateRegex'] = null; // if isn;t null this regex is called to pass for validation.
|
||||
//Can be combined with validateCallback. If both are active, the validation must pass both.
|
||||
break;
|
||||
case self::TYPE_ARRAY_STRING: // value type is array of string
|
||||
$attrs['min_len'] = 0; // min string len. used in validation
|
||||
$attrs['max_len'] = 0; // max string len. used in validation
|
||||
$attrs['default'] = []; // set default at empty array
|
||||
$attrs['validateRegex'] = null; // if isn;t null this regex is called to pass for validation.
|
||||
// Can be combined with validateCallback. If both are active, the validation must pass both.
|
||||
break;
|
||||
case self::TYPE_INT: // value type is a int
|
||||
$attrs['min_range'] = PHP_INT_MAX * -1;
|
||||
$attrs['max_range'] = PHP_INT_MAX;
|
||||
$attrs['default'] = 0; // set default at 0
|
||||
break;
|
||||
case self::TYPE_ARRAY_INT: // value type is an array of int
|
||||
$attrs['min_range'] = PHP_INT_MAX * -1;
|
||||
$attrs['max_range'] = PHP_INT_MAX;
|
||||
$attrs['default'] = []; // set default at empty array
|
||||
break;
|
||||
case self::TYPE_BOOL:
|
||||
$attrs['default'] = false; // set default fals
|
||||
$attrs['defaultFromInput'] = false; // if value isn't set in input the default must be false for bool values
|
||||
break;
|
||||
case self::TYPE_ARRAY_MIXED:
|
||||
break;
|
||||
default:
|
||||
// accepts unknown values because this class can be extended
|
||||
}
|
||||
return $attrs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Items;
|
||||
|
||||
/**
|
||||
* this class describes the options for select, radio and multiple checboxes
|
||||
*/
|
||||
class ParamOption
|
||||
{
|
||||
const OPT_ENABLED = 'opt_enabled';
|
||||
const OPT_DISABLED = 'opt_disabled';
|
||||
const OPT_HIDDEN = 'opt_hidden';
|
||||
|
||||
/** @var string */
|
||||
public $value = '';
|
||||
/** @var string */
|
||||
public $label = '';
|
||||
/** @var array<string, mixed> */
|
||||
public $attrs = [];
|
||||
/** @var string */
|
||||
protected $optStatus = self::OPT_ENABLED;
|
||||
/** @var string */
|
||||
protected $note = '';
|
||||
/** @var string */
|
||||
protected $groupLabel = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param mixed $value option value
|
||||
* @param string $label label
|
||||
* @param string|callable $optStatus option status. can be a fixed status or a callback
|
||||
* @param array<string, mixed> $attrs option attributes
|
||||
*/
|
||||
public function __construct($value, $label, $optStatus = self::OPT_ENABLED, $attrs = [])
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->label = $label;
|
||||
$this->optStatus = $optStatus;
|
||||
$this->attrs = (array) $attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* get current statis.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
if (is_callable($this->optStatus)) {
|
||||
return call_user_func($this->optStatus, $this);
|
||||
} else {
|
||||
return $this->optStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options status
|
||||
*
|
||||
* @param string|callable $optStatus option status. can be a fixed status or a callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setStatus($optStatus): void
|
||||
{
|
||||
$this->optStatus = $optStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set option note
|
||||
*
|
||||
* @param string|callable $note option note
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setNote($note): void
|
||||
{
|
||||
$this->note = is_callable($note) ? $note : ((string) $note);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNote(): string
|
||||
{
|
||||
$note = is_callable($this->note) ? call_user_func($this->note, $this) : $this->note;
|
||||
return (empty($note) ? '' : '<div class="sub-note" >' . $note . '</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set option group, used on select
|
||||
*
|
||||
* @param string $label optiongroup label is empty reset option
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOptGroup($label): void
|
||||
{
|
||||
$this->groupLabel = (string) $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option group label, empty if not set
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOptGroup()
|
||||
{
|
||||
return $this->groupLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnable(): bool
|
||||
{
|
||||
return $this->getStatus() == self::OPT_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
return $this->getStatus() == self::OPT_DISABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isHidden(): bool
|
||||
{
|
||||
return $this->getStatus() == self::OPT_HIDDEN;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params\Models;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use VendorDuplicator\Amk\JsonSerialize\AbstractJsonSerializable;
|
||||
use DUPX_ArchiveConfig;
|
||||
use Exception;
|
||||
|
||||
class SiteOwrMap extends AbstractJsonSerializable
|
||||
{
|
||||
const NEW_SUBSITE_WITH_SLUG = 0;
|
||||
const NEW_SUBSITE_WITH_FULL_DOMAIN = -1;
|
||||
const NEW_SUBSITE_NOT_VALID = -2;
|
||||
|
||||
/** @var int */
|
||||
protected $sourceId = -1;
|
||||
/** @var int */
|
||||
protected $targetId = -1;
|
||||
/** @var string */
|
||||
protected $newSlug = '';
|
||||
/** @var string */
|
||||
protected $blogName;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param int $sourceId source subsite id
|
||||
* @param int $targetId target subsite id
|
||||
* @param string $newSlug new slug on new site
|
||||
*/
|
||||
public function __construct($sourceId, $targetId, $newSlug = '')
|
||||
{
|
||||
if ($sourceId < 1) {
|
||||
throw new Exception('Source id [' . $sourceId . '] invalid ');
|
||||
}
|
||||
|
||||
if ($targetId <= self::NEW_SUBSITE_NOT_VALID) {
|
||||
throw new Exception('Target id [' . $targetId . '] invalid ');
|
||||
}
|
||||
|
||||
if (($sourceObj = DUPX_ArchiveConfig::getInstance()->getSubsiteObjById($sourceId)) === false) {
|
||||
throw new Exception('Source site info don\'t exists');
|
||||
}
|
||||
|
||||
$this->sourceId = $sourceId;
|
||||
$this->targetId = $targetId;
|
||||
$this->newSlug = $newSlug;
|
||||
$this->blogName = $sourceObj->blogname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of targetId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTargetId()
|
||||
{
|
||||
return $this->targetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update target id
|
||||
*
|
||||
* @param int $targetId new target id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTargetId($targetId): void
|
||||
{
|
||||
$this->targetId = (int) $targetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of sourceId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSourceId()
|
||||
{
|
||||
return $this->sourceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of newSlug
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNewSlug()
|
||||
{
|
||||
return $this->newSlug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return full URL from new slug or false if isn't new mode
|
||||
*
|
||||
* @param string $mainSiteURL Main site domain
|
||||
* @param bool $subdomain if true is subdomain else subfolder
|
||||
* @param bool $scheme if true return URL with scheme
|
||||
*
|
||||
* @return false|string
|
||||
*/
|
||||
public function getNewSlugFullUrl($mainSiteURL, $subdomain, $scheme = false)
|
||||
{
|
||||
$mainSiteDomain = SnapURL::parseUrl($mainSiteURL, PHP_URL_HOST);
|
||||
if (($schemeURL = SnapURL::parseUrl($mainSiteURL, PHP_URL_SCHEME)) == false) {
|
||||
$schemeURL = 'http';
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
switch ($this->targetId) {
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_SLUG:
|
||||
if (strlen($this->newSlug) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($subdomain) {
|
||||
$result = $this->newSlug . '.' . SnapURL::wwwRemove($mainSiteDomain);
|
||||
} else {
|
||||
$result = $mainSiteDomain . '/' . $this->newSlug;
|
||||
}
|
||||
break;
|
||||
case SiteOwrMap::NEW_SUBSITE_WITH_FULL_DOMAIN:
|
||||
if (strlen($this->newSlug) == 0) {
|
||||
return false;
|
||||
}
|
||||
$result = $this->newSlug;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($scheme) {
|
||||
$result = $schemeURL . '://' . $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source sibsite info
|
||||
*
|
||||
* @return false|array<string, mixed>
|
||||
*/
|
||||
public function getSourceSiteInfo()
|
||||
{
|
||||
if (($info = \DUPX_ArchiveConfig::getInstance()->getSubsiteObjById($this->sourceId)) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (array) $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return target site info
|
||||
*
|
||||
* @return false|array<string, mixed>
|
||||
*/
|
||||
public function getTargetSiteInfo()
|
||||
{
|
||||
$overwriteData = PrmMng::getInstance()->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
foreach ($overwriteData['subsites'] as $subsite) {
|
||||
if ($subsite['id'] == $this->targetId) {
|
||||
return $subsite;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,877 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core\Params;
|
||||
|
||||
use Duplicator\Installer\Core\Bootstrap;
|
||||
use Duplicator\Installer\Core\Hooks\HooksMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamForm;
|
||||
use Duplicator\Installer\Core\Params\Descriptors\ParamsDescriptors;
|
||||
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
||||
use Duplicator\Installer\Utils\InstDescMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
use Duplicator\Libs\Snap\SnapString;
|
||||
|
||||
/**
|
||||
* Params Manager
|
||||
*/
|
||||
class PrmMng
|
||||
{
|
||||
const ENV_PARAMS_KEY = 'DUPLICATOR_PARAMS';
|
||||
|
||||
/**
|
||||
* overwrite file content example
|
||||
* <?php
|
||||
* $json = <<<JSON
|
||||
* {
|
||||
* "debug": {
|
||||
* "value": false
|
||||
* },
|
||||
* "debug_params": {
|
||||
* "value": true
|
||||
* },
|
||||
* "logging": {
|
||||
* "value": 2
|
||||
* }
|
||||
* }
|
||||
* JSON;
|
||||
* // OVERWRITE FILE END
|
||||
*/
|
||||
const LOCAL_OVERWRITE_PARAMS = 'duplicator_params_overwrite';
|
||||
const LOCAL_OVERWRITE_PARAMS_LEGACY = 'duplicator_pro_params_overwrite';
|
||||
const LOCAL_OVERWRITE_PARAMS_EXTENSION = '.php';
|
||||
// actionsLOCAL_OVERWRITE_PARAMS
|
||||
const PARAM_INSTALLER_MODE = 'inst_mode';
|
||||
const PARAM_OVERWRITE_SITE_DATA = 'ovr_site_data';
|
||||
const PARAM_CTRL_ACTION = 'ctrl_action';
|
||||
const PARAM_ROUTER_ACTION = 'router-action';
|
||||
const PARAM_SECURE_PASS = 'secure-pass';
|
||||
const PARAM_SECURE_ARCHIVE_HASH = 'secure-archive';
|
||||
const PARAM_SECURE_OK = 'secure-ok';
|
||||
const PARAM_STEP_ACTION = 'step-action';
|
||||
const PARAM_TEMPLATE = 'template';
|
||||
const PARAM_VALIDATION_LEVEL = 'valid-level';
|
||||
const PARAM_VALIDATION_ACTION_ON_START = 'valid-act';
|
||||
const PARAM_VALIDATION_SHOW_ALL = 'valid-show-all';
|
||||
const PARAM_ACCEPT_TERM_COND = 'accept-warnings';
|
||||
const PARAM_RECOVERY_LINK = 'recovery-link';
|
||||
const PARAM_FROM_SITE_IMPORT_INFO = 'import-info';
|
||||
// input params
|
||||
const PARAM_DEBUG = 'debug';
|
||||
const PARAM_DEBUG_PARAMS = 'debug_params';
|
||||
const PARAM_ARCHIVE_ENGINE = 'archive_engine';
|
||||
const PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES = 'archive_engine_wpskip';
|
||||
const PARAM_ARCHIVE_ACTION = 'archive_action';
|
||||
const PARAM_LOGGING = 'logging';
|
||||
const PARAM_ZIP_THROTTLING = 'zip_throttling';
|
||||
const PARAM_REMOVE_RENDUNDANT = 'remove-redundant';
|
||||
const PARAM_REMOVE_USERS_WITHOUT_PERMISSIONS = 'remove-users-without-permissions';
|
||||
const PARAM_FILE_TIME = 'zip_filetime';
|
||||
const PARAM_HTACCESS_CONFIG = 'ht_config';
|
||||
const PARAM_OTHER_CONFIG = 'other_config';
|
||||
const PARAM_WP_CONFIG = 'wp_config';
|
||||
const PARAM_SAFE_MODE = 'exe_safe_mode';
|
||||
const PARAM_SET_FILE_PERMS = 'set_file_perms';
|
||||
const PARAM_FILE_PERMS_VALUE = 'file_perms_value';
|
||||
const PARAM_SET_DIR_PERMS = 'set_dir_perms';
|
||||
const PARAM_DIR_PERMS_VALUE = 'dir_perms_value';
|
||||
const PARAM_INST_TYPE = 'install-type';
|
||||
const PARAM_SUBSITE_ID = 'subsite_id';
|
||||
const PARAM_SUBSITE_OVERWRITE_MAPPING = 'subsite_owr_mapping';
|
||||
const PARAM_DB_DISPLAY_OVERWIRE_WARNING = 'db-display-overwarn';
|
||||
const PARAM_DB_ENGINE = 'db-engine';
|
||||
const PARAM_DB_VIEW_MODE = 'view_mode';
|
||||
const PARAM_DB_ACTION = 'dbaction';
|
||||
const PARAM_DB_HOST = 'dbhost';
|
||||
const PARAM_DB_NAME = 'dbname';
|
||||
const PARAM_DB_USER = 'dbuser';
|
||||
const PARAM_DB_FLAG = 'dbflag';
|
||||
const PARAM_DB_TABLE_PREFIX = 't_prefix';
|
||||
const PARAM_DB_PASS = 'dbpass';
|
||||
const PARAM_DB_CHARSET = 'dbcharset';
|
||||
const PARAM_DB_COLLATE = 'dbcollate';
|
||||
const PARAM_DB_CHUNK = 'dbchunk';
|
||||
const PARAM_DB_VIEW_CREATION = 'dbobj_views';
|
||||
const PARAM_DB_PROC_CREATION = 'dbobj_procs';
|
||||
const PARAM_DB_FUNC_CREATION = 'dbobj_funcs';
|
||||
const PARAM_DB_REMOVE_DEFINER = 'db_remove_definer';
|
||||
const PARAM_DB_SPLIT_CREATES = 'dbsplit_creates';
|
||||
const PARAM_DB_MYSQL_MODE = 'dbmysqlmode';
|
||||
const PARAM_DB_MYSQL_MODE_OPTS = 'dbmysqlmode_opts';
|
||||
const PARAM_DB_DONE_CREATES = 'db_done_creates';
|
||||
const PARAM_CPNL_CAN_SELECTED = 'cpnl-can-sel';
|
||||
const PARAM_CPNL_HOST = 'cpnl-host';
|
||||
const PARAM_CPNL_USER = 'cpnl-user';
|
||||
const PARAM_CPNL_PASS = 'cpnl-pass';
|
||||
const PARAM_CPNL_IGNORE_PREFIX = 'cpnl_ignore_prefix';
|
||||
const PARAM_CPNL_DB_ACTION = 'cpnl-dbaction';
|
||||
const PARAM_CPNL_DB_HOST = 'cpnl-dbhost';
|
||||
const PARAM_CPNL_PREFIX = 'cpnl-prefix';
|
||||
const PARAM_CPNL_DB_NAME_SEL = 'cpnl-dbname-select';
|
||||
const PARAM_CPNL_DB_NAME_TXT = 'cpnl-dbname-txt';
|
||||
const PARAM_CPNL_DB_USER_SEL = 'cpnl-dbuser-select';
|
||||
const PARAM_CPNL_DB_USER_TXT = 'cpnl-dbuser-txt';
|
||||
const PARAM_CPNL_DB_USER_CHK = 'cpnl-dbuser-chk';
|
||||
const PARAM_CPNL_DB_PASS = 'cpnl-dbpass';
|
||||
const PARAM_URL_OLD = 'url_old';
|
||||
const PARAM_URL_NEW = 'url_new';
|
||||
const PARAM_SITE_URL_OLD = 'siteurl_old';
|
||||
const PARAM_SITE_URL = 'siteurl';
|
||||
const PARAM_PATH_WP_CORE_OLD = 'path_core_old';
|
||||
const PARAM_PATH_WP_CORE_NEW = 'path_core_new';
|
||||
const PARAM_PATH_OLD = 'path_old';
|
||||
const PARAM_PATH_NEW = 'path_new';
|
||||
const PARAM_PATH_CONTENT_OLD = 'path_cont_old';
|
||||
const PARAM_PATH_CONTENT_NEW = 'path_cont_new';
|
||||
const PARAM_URL_CONTENT_OLD = 'url_cont_old';
|
||||
const PARAM_URL_CONTENT_NEW = 'url_cont_new';
|
||||
const PARAM_PATH_UPLOADS_OLD = 'path_upl_old';
|
||||
const PARAM_PATH_UPLOADS_NEW = 'path_upl_new';
|
||||
const PARAM_URL_UPLOADS_OLD = 'url_upl_old';
|
||||
const PARAM_URL_UPLOADS_NEW = 'url_upl_new';
|
||||
const PARAM_PATH_PLUGINS_OLD = 'path_plug_old';
|
||||
const PARAM_PATH_PLUGINS_NEW = 'path_plug_new';
|
||||
const PARAM_URL_PLUGINS_OLD = 'url_plug_old';
|
||||
const PARAM_URL_PLUGINS_NEW = 'url_plug_new';
|
||||
const PARAM_PATH_MUPLUGINS_OLD = 'path_muplug_old';
|
||||
const PARAM_PATH_MUPLUGINS_NEW = 'path_muplug_new';
|
||||
const PARAM_URL_MUPLUGINS_OLD = 'url_muplug_old';
|
||||
const PARAM_URL_MUPLUGINS_NEW = 'url_muplug_new';
|
||||
const PARAM_WP_ADDON_SITES_PATHS = 'wpaddon_sites';
|
||||
const PARAM_BLOGNAME = 'blogname';
|
||||
const PARAM_MU_REPLACE = 'mu_replace';
|
||||
const PARAM_REPLACE_ENGINE = 'mode_chunking';
|
||||
const PARAM_SKIP_PATH_REPLACE = 'skip_path_replace';
|
||||
const PARAM_EMPTY_SCHEDULE_STORAGE = 'empty_schedule_storage';
|
||||
const PARAM_DB_TABLES = 'tables';
|
||||
const PARAM_EMAIL_REPLACE = 'search_replace_email_domain';
|
||||
const PARAM_FULL_SEARCH = 'fullsearch';
|
||||
const PARAM_POSTGUID = 'postguid';
|
||||
const PARAM_MAX_SERIALIZE_CHECK = 'mstrlim';
|
||||
const PARAM_MULTISITE_CROSS_SEARCH = 'cross_search';
|
||||
const PARAM_PLUGINS = 'plugins';
|
||||
const PARAM_IGNORE_PLUGINS = 'ignore_plugins';
|
||||
const PARAM_FORCE_DIABLE_PLUGINS = 'fd_plugins';
|
||||
const PARAM_CUSTOM_SEARCH = 'search';
|
||||
const PARAM_CUSTOM_REPLACE = 'replace';
|
||||
const PARAM_USERS_MODE = 'users_mode';
|
||||
const PARAM_ADD_SUBSITE_USER_MODE = 'subsite_users_mode';
|
||||
const PARAM_USERS_PWD_RESET = 'users_pwd_reset';
|
||||
const PARAM_WP_ADMIN_CREATE_NEW = 'wp_new_admin';
|
||||
const PARAM_WP_ADMIN_NAME = 'wp_username';
|
||||
const PARAM_WP_ADMIN_PASSWORD = 'wp_password';
|
||||
const PARAM_WP_ADMIN_MAIL = 'wp_mail';
|
||||
const PARAM_WP_ADMIN_NICKNAME = 'wp_nickname';
|
||||
const PARAM_WP_ADMIN_FIRST_NAME = 'wp_first_name';
|
||||
const PARAM_WP_ADMIN_LAST_NAME = 'wp_last_name';
|
||||
// WP CONFIG
|
||||
const PARAM_GEN_WP_AUTH_KEY = 'auth_keys_and_salts';
|
||||
const PARAM_WP_CONF_WP_SITEURL = 'wpc_WP_SITEURL';
|
||||
const PARAM_WP_CONF_MYSQL_CLIENT_FLAGS = 'wpc_MYSQL_CLIENT_FLAGS';
|
||||
const PARAM_WP_CONF_WP_HOME = 'wpc_WP_HOME';
|
||||
const PARAM_WP_CONF_WP_CONTENT_DIR = 'wpc_WP_CONTENT_DIR';
|
||||
const PARAM_WP_CONF_WP_CONTENT_URL = 'wpc_WP_CONTENT_URL';
|
||||
const PARAM_WP_CONF_WP_PLUGIN_DIR = 'wpc_WP_PLUGIN_DIR';
|
||||
const PARAM_WP_CONF_WP_PLUGIN_URL = 'wpc_WP_PLUGIN_URL';
|
||||
const PARAM_WP_CONF_PLUGINDIR = 'wpc_PLUGINDIR';
|
||||
const PARAM_WP_CONF_UPLOADS = 'wpc_UPLOADS';
|
||||
const PARAM_WP_CONF_AUTOSAVE_INTERVAL = 'wpc_AUTOSAVE_INTERVAL';
|
||||
const PARAM_WP_CONF_WP_POST_REVISIONS = 'wpc_WP_POST_REVISIONS';
|
||||
const PARAM_WP_CONF_COOKIE_DOMAIN = 'wpc_COOKIE_DOMAIN';
|
||||
const PARAM_WP_CONF_WP_ALLOW_MULTISITE = 'wpc_WP_ALLOW_MULTISITE';
|
||||
const PARAM_WP_CONF_NOBLOGREDIRECT = 'wpc_NOBLOGREDIRECT';
|
||||
const PARAM_WP_CONF_WP_DEBUG = 'wpc_WP_DEBUG';
|
||||
const PARAM_WP_CONF_SCRIPT_DEBUG = 'wpc_SCRIPT_DEBUG';
|
||||
const PARAM_WP_CONF_CONCATENATE_SCRIPTS = 'wpc_CONCATENATE_SCRIPTS';
|
||||
const PARAM_WP_CONF_WP_DEBUG_LOG = 'wpc_WP_DEBUG_LOG';
|
||||
const PARAM_WP_CONF_WP_DISABLE_FATAL_ERROR_HANDLER = 'wpc_WP_DISABLE_FATAL_ERROR_HANDLER';
|
||||
const PARAM_WP_CONF_WP_DEBUG_DISPLAY = 'wpc_WP_DEBUG_DISPLAY';
|
||||
const PARAM_WP_CONF_WP_MEMORY_LIMIT = 'wpc_WP_MEMORY_LIMIT';
|
||||
const PARAM_WP_CONF_WP_MAX_MEMORY_LIMIT = 'wpc_WP_MAX_MEMORY_LIMIT';
|
||||
const PARAM_WP_CONF_WP_TEMP_DIR = 'wpc_WP_TEMP_DIR';
|
||||
const PARAM_WP_CONF_WP_CACHE = 'wpc_WP_CACHE';
|
||||
const PARAM_WP_CONF_CUSTOM_USER_TABLE = 'wpc_CUSTOM_USER_TABLE';
|
||||
const PARAM_WP_CONF_CUSTOM_USER_META_TABLE = 'wpc_CUSTOM_USER_META_TABLE';
|
||||
const PARAM_WP_CONF_WPLANG = 'wpc_WPLANG';
|
||||
const PARAM_WP_CONF_WP_LANG_DIR = 'wpc_WP_LANG_DIR';
|
||||
const PARAM_WP_CONF_SAVEQUERIES = 'wpc_SAVEQUERIES';
|
||||
const PARAM_WP_CONF_FS_CHMOD_DIR = 'wpc_FS_CHMOD_DIR';
|
||||
const PARAM_WP_CONF_FS_CHMOD_FILE = 'wpc_FS_CHMOD_FILE';
|
||||
const PARAM_WP_CONF_FS_METHOD = 'wpc_FS_METHOD';
|
||||
const PARAM_WP_CONF_ALTERNATE_WP_CRON = 'wpc_ALTERNATE_WP_CRON';
|
||||
const PARAM_WP_CONF_DISABLE_WP_CRON = 'wpc_DISABLE_WP_CRON';
|
||||
const PARAM_WP_CONF_WP_CRON_LOCK_TIMEOUT = 'wpc_WP_CRON_LOCK_TIMEOUT';
|
||||
const PARAM_WP_CONF_COOKIEPATH = 'wpc_COOKIEPATH';
|
||||
const PARAM_WP_CONF_SITECOOKIEPATH = 'wpc_SITECOOKIEPATH';
|
||||
const PARAM_WP_CONF_ADMIN_COOKIE_PATH = 'wpc_ADMIN_COOKIE_PATH';
|
||||
const PARAM_WP_CONF_PLUGINS_COOKIE_PATH = 'wpc_PLUGINS_COOKIE_PATH';
|
||||
const PARAM_WP_CONF_TEMPLATEPATH = 'wpc_TEMPLATEPATH';
|
||||
const PARAM_WP_CONF_STYLESHEETPATH = 'wpc_STYLESHEETPATH';
|
||||
const PARAM_WP_CONF_EMPTY_TRASH_DAYS = 'wpc_EMPTY_TRASH_DAYS';
|
||||
const PARAM_WP_CONF_WP_ALLOW_REPAIR = 'wpc_WP_ALLOW_REPAIR';
|
||||
const PARAM_WP_CONF_DO_NOT_UPGRADE_GLOBAL_TABLES = 'wpc_DO_NOT_UPGRADE_GLOBAL_TABLES';
|
||||
const PARAM_WP_CONF_DISALLOW_FILE_EDIT = 'wpc_DISALLOW_FILE_EDIT';
|
||||
const PARAM_WP_CONF_DISALLOW_FILE_MODS = 'wpc_DISALLOW_FILE_MODS';
|
||||
const PARAM_WP_CONF_FORCE_SSL_ADMIN = 'wpc_FORCE_SSL_ADMIN';
|
||||
const PARAM_WP_CONF_WP_HTTP_BLOCK_EXTERNAL = 'wpc_WP_HTTP_BLOCK_EXTERNAL';
|
||||
const PARAM_WP_CONF_WP_ACCESSIBLE_HOSTS = 'wpc_WP_ACCESSIBLE_HOSTS';
|
||||
const PARAM_WP_CONF_AUTOMATIC_UPDATER_DISABLED = 'wpc_AUTOMATIC_UPDATER_DISABLED';
|
||||
const PARAM_WP_CONF_WP_AUTO_UPDATE_CORE = 'wpc_WP_AUTO_UPDATE_CORE';
|
||||
const PARAM_WP_CONF_IMAGE_EDIT_OVERWRITE = 'wpc_IMAGE_EDIT_OVERWRITE';
|
||||
const PARAM_WP_CONF_WPMU_PLUGIN_DIR = 'wpc_WPMU_PLUGIN_DIR';
|
||||
const PARAM_WP_CONF_WPMU_PLUGIN_URL = 'wpc_WPMU_PLUGIN_URL';
|
||||
const PARAM_WP_CONF_MUPLUGINDIR = 'wpc_MUPLUGINDIR';
|
||||
// OTHER WP CONFIG SETTINGS NOT IN WP CORE
|
||||
const PARAM_WP_CONF_WPCACHEHOME = 'wpc_WPCACHEHOME';
|
||||
const PARAM_FINAL_REPORT_DATA = 'final_report';
|
||||
const PARAM_AUTO_CLEAN_INSTALLER_FILES = 'auto-delete';
|
||||
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
/** @var bool */
|
||||
private static $initialized = false;
|
||||
/** @var array<ParamItem|ParamForm> */
|
||||
private $params = [];
|
||||
/** @var string[] */
|
||||
private $paramsHtmlInfo = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* __construct
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
ParamsDescriptors::init();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function initParams(): bool
|
||||
{
|
||||
if (self::$initialized) {
|
||||
// prevent multiple inizialization
|
||||
return true;
|
||||
}
|
||||
self::$initialized = true;
|
||||
|
||||
ParamsDescriptors::initParams($this->params);
|
||||
$this->params = HooksMng::getInstance()->applyFilters('installer_get_init_params', $this->params);
|
||||
|
||||
$this->paramsHtmlInfo[] = '***** INIT PARAMS WITH STD VALUlES';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* get value of param key.
|
||||
* thorw execption if key don't exists
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getValue($key)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
return $this->params[$key]->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get param status
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return string // STATUS_INIT | STATUS_OVERWRITE | STATUS_UPD_FROM_INPUT
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getInitStatus($key)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'does not exists');
|
||||
}
|
||||
return $this->params[$key]->getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the label of param key.
|
||||
* thorw execption if key don't exists
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getLabel($key): string
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
return rtrim($this->params[$key]->getLabel(), ": \n\t");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set param value
|
||||
*
|
||||
* @param string $key param key
|
||||
* @param mixed $value value
|
||||
*
|
||||
* @return boolean // return false if params isn't valid
|
||||
*
|
||||
* @throws \Exception // if key don't exists
|
||||
*/
|
||||
public function setValue($key, $value): bool
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
return $this->params[$key]->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* this cungion set value get from input method.
|
||||
*
|
||||
* @param string $key param key
|
||||
* @param string $method input method (GET, POST ... )
|
||||
* @param boolean $thowException if true throw exception if value isn't valid.
|
||||
* @param boolean $nextStepErrorMessage if true and param isn't valid add next step message
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setValueFromInput($key, $method = ParamForm::INPUT_POST, $thowException = true, $nextStepErrorMessage = false)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (($result = $this->params[$key]->setValueFromInput($method)) === false) {
|
||||
$this->paramsHtmlInfo[] = 'INVALID VALUE INPUT <b>' . $key . '</b>';
|
||||
|
||||
if ($nextStepErrorMessage) {
|
||||
$this->addParamValidationFaliedNotice($key);
|
||||
}
|
||||
if ($thowException) {
|
||||
$errorMessage = 'Parameter "' . $this->getLabel($key) . '" have invalid value';
|
||||
throw new \Exception('PARAM ERROR: ' . $errorMessage);
|
||||
}
|
||||
} else {
|
||||
$this->paramsHtmlInfo[] = 'SET FROM INPUT <b>' . $key . '</b> VALUE: ' . Log::v2str($this->params[$key]->getValue());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add next step validation failed notice
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addParamValidationFaliedNotice($key): void
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
$longMessage = '<b>' . $this->getLabel($key) . '</b> ' . $this->params[$key]->getInvalidMessage() . "<br>\n";
|
||||
\DUPX_NOTICE_MANAGER::getInstance()->addNextStepNotice([
|
||||
'shortMsg' => 'Parameter validation failed',
|
||||
'level' => \DUPX_NOTICE_ITEM::CRITICAL,
|
||||
'longMsg' => $longMessage,
|
||||
'longMsgMode' => \DUPX_NOTICE_ITEM::MSG_MODE_HTML,
|
||||
], \DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, 'params_validation_fail');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form param wrapper id
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return boolean|string return false if the item key isn't a instance of ParamForm
|
||||
*/
|
||||
public function getFormWrapperId($key)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'getFormWrapperId')) {
|
||||
return $this->params[$key]->getFormWrapperId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return form item id
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return boolean|string return false if the item key isn't a instance of ParamForm
|
||||
*/
|
||||
public function getFormItemId($key)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'getFormItemId')) {
|
||||
return $this->params[$key]->getFormItemId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get param form status
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return boolean|string return false if the item key isn't a instance of ParamForm
|
||||
*/
|
||||
public function getFormStatus($key)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'getFormStatus')) {
|
||||
return $this->params[$key]->getFormStatus();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form param status
|
||||
*
|
||||
* @param string $key param key
|
||||
* @param string|callable $status STATUS_ENABLED , STATUS_READONLY or callable function
|
||||
*
|
||||
* @return bool return false if the item key isn't a instance of ParamForm
|
||||
*/
|
||||
public function setFormStatus($key, $status): bool
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'setFormAttr')) {
|
||||
$this->params[$key]->setFormAttr('status', $status);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form wrapper class
|
||||
*
|
||||
* @param string $key param key
|
||||
* @param string $class wrapper class
|
||||
*
|
||||
* @return bool return false if the item key isn't a instance of ParamForm
|
||||
*/
|
||||
public function addFormWrapperClass($key, $class): bool
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'addWrapperClass')) {
|
||||
$this->params[$key]->addWrapperClass($class);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove form wrapper class
|
||||
*
|
||||
* @param string $key param key
|
||||
* @param string $class wrapper class
|
||||
*
|
||||
* @return bool return false if the item key isn't a instance of ParamForm
|
||||
*/
|
||||
public function removeFormWrapperClass($key, $class): bool
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'removeWrapperClass')) {
|
||||
$this->params[$key]->removeWrapperClass($class);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This tunction add o remove note on the param form
|
||||
*
|
||||
* @param string $key param key
|
||||
* @param string $htmlString true if is html
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setFormNote($key, $htmlString): bool
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (method_exists($this->params[$key], 'setFormAttr')) {
|
||||
$this->params[$key]->setFormAttr('subNote', $htmlString);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if the input exists in html form
|
||||
* false if isn't ParamForm object or status is STATUS_INFO_ONLY or STATUS_SKIP
|
||||
*
|
||||
* @param string $key param key
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isHtmlInput($key): bool
|
||||
{
|
||||
$status = $this->getFormStatus($key);
|
||||
switch ($status) {
|
||||
case ParamForm::STATUS_ENABLED:
|
||||
case ParamForm::STATUS_READONLY:
|
||||
case ParamForm::STATUS_DISABLED:
|
||||
return true;
|
||||
case ParamForm::STATUS_INFO_ONLY:
|
||||
case ParamForm::STATUS_SKIP:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the input form html
|
||||
*
|
||||
* @param string $key the param identifier
|
||||
* @param mixed $overwriteValue if not null set overwriteValue begore get html
|
||||
* (IMPORTANT: the stored param value don't change. To change it use setValue.)
|
||||
* @param bool $echo true echo else return string
|
||||
*
|
||||
* @return bool|string return false if the item kay isn't a instance of ParamForm
|
||||
*/
|
||||
public function getHtmlFormParam($key, $overwriteValue = null, $echo = true)
|
||||
{
|
||||
if (!isset($this->params[$key])) {
|
||||
throw new \Exception('Param key ' . Log::v2str($key) . 'don\' exists');
|
||||
}
|
||||
|
||||
if (!($this->params[$key] instanceof ParamForm)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($overwriteValue)) {
|
||||
return $this->params[$key]->getHtml($echo);
|
||||
} else {
|
||||
$tmpParam = clone $this->params[$key];
|
||||
if ($tmpParam->setValue($overwriteValue) === false) {
|
||||
throw new \Exception('Can\'t set overwriteValue ' . Log::v2str($overwriteValue) . ' in param:' . $tmpParam->getName());
|
||||
}
|
||||
|
||||
return $tmpParam->getHtml($echo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load params from persistance files
|
||||
*
|
||||
* @param boolean $reset if true reset params
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function load($reset = false): bool
|
||||
{
|
||||
if ($reset) {
|
||||
$this->resetParams();
|
||||
$this->initParamsOverwrite();
|
||||
return true;
|
||||
} else {
|
||||
if (($json = SnapIO::safeFileGetContents(self::getPersistanceFilePath())) === false) {
|
||||
return false;
|
||||
}
|
||||
$this->paramsHtmlInfo[] = '***** LOAD PARAMS FROM PERSISTENCE FILE';
|
||||
|
||||
$arrayData = json_decode($json, true);
|
||||
if ($this->setParamsValues($arrayData) === false) {
|
||||
throw new \Exception('Can\'t set params from persistence file ' . Log::v2str(self::getPersistanceFilePath()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove persistance file and all params and reinit all
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function resetParams(): bool
|
||||
{
|
||||
$this->paramsHtmlInfo[] = '***** RESET PARAMS';
|
||||
SnapIO::rm(self::getPersistanceFilePath());
|
||||
$this->params = [];
|
||||
self::$initialized = false;
|
||||
return $this->initParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ovrewrite params from sources
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function initParamsOverwrite(): bool
|
||||
{
|
||||
Log::info('OVERWRITE PARAMS');
|
||||
$this->paramsHtmlInfo[] = '***** LOAD OVERWRITE INFO';
|
||||
/**
|
||||
* @todo temp disabled require major study
|
||||
* if (isset($_ENV[self::ENV_PARAMS_KEY])) {
|
||||
* $this->paramsHtmlInfo[] = 'LOAD FROM ENV VARS';
|
||||
$arrayData = json_decode($_ENV[self::ENV_PARAMS_KEY]);
|
||||
$this->setParamsValues($arrayData, true);
|
||||
} */
|
||||
// LOAD PARAMS FROM PACKAGE OVERWRITE
|
||||
$arrayData = (array) \DUPX_ArchiveConfig::getInstance()->overwriteInstallerParams;
|
||||
if (!empty($arrayData)) {
|
||||
$this->paramsHtmlInfo[] = '***** LOAD FROM PACKAGE OVERWRITE';
|
||||
Log::info(' *** FROM PACKAGE');
|
||||
if ($this->setParamsValues($arrayData, true, Log::LV_DEFAULT) === false) {
|
||||
throw new \Exception('Can\'t set params from package overwrite ');
|
||||
}
|
||||
}
|
||||
|
||||
// LOAD PARAMS FROM LOCAL OVERWRITE
|
||||
$localOverwritePath = DUPX_ROOT . '/' . self::LOCAL_OVERWRITE_PARAMS . self::LOCAL_OVERWRITE_PARAMS_EXTENSION;
|
||||
if (is_readable($localOverwritePath)) {
|
||||
// json file is set in $localOverwritePath php file
|
||||
/** @var ?string $json */
|
||||
$json = null;
|
||||
include($localOverwritePath);
|
||||
if (empty($json)) {
|
||||
Log::info('LOCAL OVERWRITE PARAMS FILE ISN\'T WELL FORMED');
|
||||
} else {
|
||||
$arrayData = json_decode($json, true);
|
||||
if (!empty($arrayData)) {
|
||||
$this->paramsHtmlInfo[] = '***** LOAD FROM LOCAL OVERWRITE';
|
||||
Log::info(' *** FROM LOCAL FILE');
|
||||
if ($this->setParamsValues($arrayData, true, Log::LV_DEFAULT) === false) {
|
||||
throw new \Exception('Can\'t set params from local overwrite ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LOAD PARAMS FROM LOCAL OVERWRITE PACKAGE_HASH
|
||||
// Try new prefix first, fallback to legacy prefix for packages created by older Duplicator Pro versions
|
||||
$localOverwritePath = DUPX_ROOT . '/' . self::LOCAL_OVERWRITE_PARAMS . '_' . Bootstrap::getPackageHash() . '.json';
|
||||
if (!is_readable($localOverwritePath)) {
|
||||
$localOverwritePath = DUPX_ROOT . '/' . self::LOCAL_OVERWRITE_PARAMS_LEGACY . '_' . Bootstrap::getPackageHash() . '.json';
|
||||
}
|
||||
if (is_readable($localOverwritePath)) {
|
||||
if (($json = file_get_contents($localOverwritePath)) === false) {
|
||||
Log::info('CAN\'T READ LOCAL OVERWRITE PARAM HASH FILE');
|
||||
} else {
|
||||
$arrayData = json_decode($json, true);
|
||||
if (!empty($arrayData)) {
|
||||
$this->paramsHtmlInfo[] = '***** LOAD FROM LOCAL OVERWRITE HASH';
|
||||
Log::info(' *** FROM LOCAL FILE');
|
||||
if ($this->setParamsValues($arrayData, true, Log::LV_DEFAULT) === false) {
|
||||
throw new \Exception('Can\'t set params from local overwrite ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HooksMng::getInstance()->doAction('after_params_overwrite', $this->params);
|
||||
|
||||
Log::info("********************************************************************************");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update params values from arrayData
|
||||
*
|
||||
* @param array<string, mixed> $arrayData params data
|
||||
* @param boolean $overwrite if true overwrite status
|
||||
* @param integer $logginLevelSet log level
|
||||
*
|
||||
* @return bool returns false if a parameter has not been set
|
||||
*/
|
||||
protected function setParamsValues($arrayData, $overwrite = false, $logginLevelSet = Log::LV_DEBUG)
|
||||
{
|
||||
|
||||
if (!is_array($arrayData)) {
|
||||
throw new \Exception('Invalid data params ');
|
||||
}
|
||||
$result = true;
|
||||
|
||||
foreach ($arrayData as $key => $arrayValues) {
|
||||
if (isset($this->params[$key])) {
|
||||
$arrayValues = (array) $arrayValues;
|
||||
$arrayValValToStr = array_map([Log::class, 'v2str'], $arrayValues);
|
||||
|
||||
$this->paramsHtmlInfo[] = 'SET PARAM <b>' . $key . '</b> ARRAY DATA: ' .
|
||||
SnapString::implodeKeyVals(', ', $arrayValValToStr, '[<b>%s</b> = %s]');
|
||||
if ($this->params[$key]->fromArrayData($arrayValues) === false) {
|
||||
Log::info('PARAM ISSUE SET KEY[' . $key . '] VALUE: ' . SnapString::implodeKeyVals(', ', $arrayValValToStr, '[%s = %s]'));
|
||||
// phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
||||
Log::info(Log::traceToString(debug_backtrace()));
|
||||
// $result = false;
|
||||
} else {
|
||||
$log = 'PARAM SET KEY[' . $key . ']';
|
||||
$log .= (Log::isLevel(Log::LV_DEBUG) ? (' VALUE: ' . SnapString::implodeKeyVals(', ', $arrayValValToStr, '[%s = %s]')) : '');
|
||||
Log::info($log, $logginLevelSet);
|
||||
if ($overwrite) {
|
||||
$this->params[$key]->setOveriteStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* update persistance file
|
||||
*
|
||||
* @return bool|int // This function returns the number of bytes that were written to the file, or FALSE on failure.
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
Log::info("SAVE PARAMS\n" . Log::traceToString(debug_backtrace(), 0, 1), Log::LV_DEBUG);
|
||||
|
||||
$arrayData = [];
|
||||
foreach ($this->params as $param) {
|
||||
if ($param->isPersistent()) {
|
||||
$arrayData[$param->getName()] = $param->toArrayData();
|
||||
}
|
||||
}
|
||||
$json = SnapJson::jsonEncodePPrint($arrayData);
|
||||
if (($result = file_put_contents(self::getPersistanceFilePath(), $json, LOCK_EX)) === false) {
|
||||
Log::info('PRAMS: can\'t save persistence file');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getPersistanceFilePath()
|
||||
{
|
||||
static $path = null;
|
||||
|
||||
if (is_null($path)) {
|
||||
$path = DUPX_INIT . '/' . InstDescMng::getInstance()->getName(InstDescMng::TYPE_PARAMS);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* html params info for debug params
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getParamsHtmlInfo(): void
|
||||
{
|
||||
if (!$this->getValue(self::PARAM_DEBUG_PARAMS)) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div id="params-html-info">
|
||||
<h3>CURRENT VALUES</h3>
|
||||
<ul class="values">
|
||||
<?php foreach ($this->params as $param) { ?>
|
||||
<li>
|
||||
PARAM <b><?php echo $param->getName(); ?></b> VALUE: <b><?php echo htmlentities(Log::v2str($param->getValue())); ?></b>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<h3>LOAD SEQUENCE</h3>
|
||||
<ul class="load-sequence">
|
||||
<?php foreach ($this->paramsHtmlInfo as $info) { ?>
|
||||
<li>
|
||||
<?php echo $info; ?>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<h3>ARCHIVE PARAM DATA</h3>
|
||||
<pre><?php
|
||||
$data = \DUPX_ArchiveConfig::getInstance();
|
||||
var_dump($data);
|
||||
?></pre>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get params value list for log
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getParamsToText(): string
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->params as $param) {
|
||||
if (method_exists($param, 'getFormStatus')) {
|
||||
$line = 'PARAM FORM ' . $param->getName() . ' VALUE: ' . Log::v2str($param->getValue()) . ' STATUS: ' . $param->getFormStatus();
|
||||
} else {
|
||||
$line = 'PARAM ITEM ' . $param->getName() . ' VALUE: ' . Log::v2str($param->getValue());
|
||||
}
|
||||
|
||||
$result[] = $line;
|
||||
}
|
||||
|
||||
return implode("\n", $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent clone object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __clone()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Interface that collects the functions of initial duplicator Bootstrap
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Core;
|
||||
|
||||
use Duplicator\Installer\Bootstrap\BootstrapRunner;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Core\Bootstrap;
|
||||
use Duplicator\Installer\Core\Descriptors\ArchiveConfig;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Package\ArchiveDescriptor;
|
||||
use Duplicator\Installer\Utils\SecureCsrf;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use DUPX_ArchiveConfig;
|
||||
use DUPX_Ctrl_ajax;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* singleton class
|
||||
*
|
||||
* In this class all installer security checks are performed. If the security checks are not passed, an exception is thrown and the installer is stopped.
|
||||
* This happens before anything else so the class must work without the initialization of all global duplicator variables.
|
||||
*/
|
||||
class Security
|
||||
{
|
||||
const CTRL_TOKEN = 'ctrl_csrf_token';
|
||||
const ROUTER_TOKEN = 'router_csrf_token';
|
||||
|
||||
const SECURITY_NONE = 'none';
|
||||
const SECURITY_PASSWORD = 'pwd';
|
||||
const SECURITY_ARCHIVE = 'archive';
|
||||
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
/** @var string read from from csrf file */
|
||||
private $archivePath;
|
||||
/** @var string read from from csrf file */
|
||||
private $bootloader;
|
||||
/** @var string read from from csrf file */
|
||||
private $bootUrl;
|
||||
/** @var string read from from csrf file */
|
||||
private $bootFilePath;
|
||||
/** @var string read from from csrf file */
|
||||
private $bootLogFile;
|
||||
/** @var string read from from csrf file */
|
||||
private $packageHash;
|
||||
/** @var string read from from csrf file */
|
||||
private $secondaryPackageHash;
|
||||
/** @var string read from from csrf file */
|
||||
private $archivePwd = '';
|
||||
/** @var bool read from csrf file */
|
||||
private $isManualExtractFound = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
SecureCsrf::init(DUPX_INIT, Bootstrap::getPackageHash());
|
||||
|
||||
if (!file_exists(SecureCsrf::getFilePath())) {
|
||||
throw new Exception("CSRF FILE NOT FOUND\n"
|
||||
. "Please, check webroot file permsission and dup-installer folder permission");
|
||||
}
|
||||
|
||||
$this->bootloader = SecureCsrf::getVal('bootloader');
|
||||
$this->bootUrl = SecureCsrf::getVal('booturl');
|
||||
$this->bootLogFile = SnapIO::safePath(SecureCsrf::getVal('bootLogFile'));
|
||||
$this->bootFilePath = SnapIO::safePath(SecureCsrf::getVal('installerOrigPath'));
|
||||
$this->archivePath = SnapIO::safePath(SecureCsrf::getVal('archive'));
|
||||
$this->archivePwd = SecureCsrf::getVal(BootstrapRunner::CSRF_KEY_ARCHIVE_PASSWORD);
|
||||
$this->packageHash = SecureCsrf::getVal('package_hash');
|
||||
$this->secondaryPackageHash = SecureCsrf::getVal('secondaryHash');
|
||||
$this->isManualExtractFound = SecureCsrf::getVal('isManualExtractFound');
|
||||
}
|
||||
|
||||
/**
|
||||
* archive path read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getArchivePath()
|
||||
{
|
||||
return $this->archivePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* installer full path read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBootFilePath()
|
||||
{
|
||||
return $this->bootFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* boot log file full path read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBootLogFile()
|
||||
{
|
||||
return $this->bootLogFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* bootloader path read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBootloader()
|
||||
{
|
||||
return $this->bootloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* bootloader path read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBootUrl()
|
||||
{
|
||||
return $this->bootUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* package hash read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPackageHash()
|
||||
{
|
||||
return $this->packageHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archvie password
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getArchivePassword()
|
||||
{
|
||||
return $this->archivePwd;
|
||||
}
|
||||
|
||||
/**
|
||||
* package public hash read from intaller.php passed by DUPX_CSFR
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSecondaryPackageHash()
|
||||
{
|
||||
return $this->secondaryPackageHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get original installer URL, used if installer is called from mu-plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalInstallerUrl()
|
||||
{
|
||||
return SecureCsrf::getVal('originalDupInstallerUrl');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
* @throws Exception // if fail throw exception of return true
|
||||
*/
|
||||
public function check(): bool
|
||||
{
|
||||
try {
|
||||
// check if current package hash is equal at bootloader package hash
|
||||
if ($this->packageHash !== Bootstrap::getPackageHash()) {
|
||||
throw new Exception('Incorrect hash package');
|
||||
}
|
||||
|
||||
// checks if the version of the package descriptor is consistent with the version of the files.
|
||||
if (DUPX_ArchiveConfig::getInstance()->version_dup !== DUPX_VERSION) {
|
||||
throw new Exception('The version of the archive is different from the version of the PHP scripts');
|
||||
}
|
||||
|
||||
$token_tested = false;
|
||||
$debug = self::isDebug();
|
||||
|
||||
$action = null;
|
||||
if (DUPX_Ctrl_ajax::isAjax($action) == true) {
|
||||
if (($token = self::getTokenFromInput(DUPX_Ctrl_ajax::TOKEN_NAME)) === false) {
|
||||
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . DUPX_Ctrl_ajax::TOKEN_NAME : '');
|
||||
throw new Exception($msg);
|
||||
}
|
||||
if (!SecureCsrf::check(self::getTokenFromInput(DUPX_Ctrl_ajax::TOKEN_NAME), DUPX_Ctrl_ajax::getTokenKeyByAction($action))) {
|
||||
$msg = 'Security issue';
|
||||
if ($debug) {
|
||||
$msg .= ' LINE: ' . __LINE__ .
|
||||
' TOKEN: ' . $token .
|
||||
' KEY NAME: ' . DUPX_Ctrl_ajax::getTokenKeyByAction($action) .
|
||||
' KEY VALUE ' . DUPX_Ctrl_ajax::getTokenKeyByAction($action);
|
||||
}
|
||||
throw new Exception($msg);
|
||||
}
|
||||
$token_tested = true;
|
||||
} elseif (($token = self::getTokenFromInput(self::CTRL_TOKEN)) !== false) {
|
||||
if (!isset($_REQUEST[PrmMng::PARAM_CTRL_ACTION])) {
|
||||
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . PrmMng::PARAM_CTRL_ACTION : '');
|
||||
throw new Exception($msg);
|
||||
}
|
||||
if (!SecureCsrf::check($token, $_REQUEST[PrmMng::PARAM_CTRL_ACTION])) {
|
||||
$msg = 'Security issue';
|
||||
if ($debug) {
|
||||
$msg .= ' LINE: ' . __LINE__ .
|
||||
' TOKEN: ' . $token .
|
||||
' KEY NAME: ' . PrmMng::PARAM_CTRL_ACTION .
|
||||
' KEY VALUE ' . $_REQUEST[PrmMng::PARAM_CTRL_ACTION];
|
||||
}
|
||||
throw new Exception($msg);
|
||||
}
|
||||
$token_tested = true;
|
||||
}
|
||||
|
||||
if (($token = self::getTokenFromInput(self::ROUTER_TOKEN)) !== false) {
|
||||
if (!isset($_REQUEST[PrmMng::PARAM_ROUTER_ACTION])) {
|
||||
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . PrmMng::PARAM_ROUTER_ACTION : '');
|
||||
throw new Exception($msg);
|
||||
}
|
||||
if (!SecureCsrf::check($token, $_REQUEST[PrmMng::PARAM_ROUTER_ACTION])) {
|
||||
$msg = 'Security issue';
|
||||
if ($debug) {
|
||||
$msg .= ' LINE: ' . __LINE__ .
|
||||
' TOKEN: ' . $token .
|
||||
' KEY NAME: ' . PrmMng::PARAM_ROUTER_ACTION .
|
||||
' KEY VALUE ' . $_REQUEST[PrmMng::PARAM_ROUTER_ACTION];
|
||||
}
|
||||
throw new Exception($msg);
|
||||
}
|
||||
$token_tested = true;
|
||||
}
|
||||
|
||||
// At least one token must always and in any case be tested
|
||||
if (!$token_tested) {
|
||||
throw new Exception('Security Check Validation - No Token Found');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if (function_exists('error_clear_last')) {
|
||||
// comment error_clear_last if you want see te exception html on shutdown
|
||||
error_clear_last(); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.error_clear_lastFound
|
||||
}
|
||||
|
||||
Log::logException($e, Log::LV_DEFAULT, 'SECURITY CHECK: ');
|
||||
dupxTplRender('page-security-error', [
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
die();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return tru if is debug
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function isDebug(): bool
|
||||
{
|
||||
/** @todo connect with global debug */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sanitized token frominput
|
||||
*
|
||||
* @param string $tokenName token name
|
||||
*
|
||||
* @return false|string get token or false if don't exists
|
||||
*/
|
||||
protected static function getTokenFromInput($tokenName)
|
||||
{
|
||||
return SnapUtil::sanitizeDefaultInput(SnapUtil::INPUT_REQUEST, $tokenName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get security tipe (NONE, PASSWORD, ARCHIVE)
|
||||
*
|
||||
* @return string enum type
|
||||
*/
|
||||
public function getSecurityType(): string
|
||||
{
|
||||
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_SECURE_OK) == true) {
|
||||
return self::SECURITY_NONE;
|
||||
}
|
||||
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
|
||||
if ($this->isManualExtractFound && $archiveConfig->secure_on === ArchiveDescriptor::SECURE_MODE_ARC_ENCRYPT) {
|
||||
$archiveConfig->secure_on = ArchiveDescriptor::SECURE_MODE_INST_PWD;
|
||||
}
|
||||
|
||||
if ($archiveConfig->secure_on === ArchiveDescriptor::SECURE_MODE_INST_PWD) {
|
||||
return self::SECURITY_PASSWORD;
|
||||
}
|
||||
|
||||
if (
|
||||
strlen($this->archivePwd) == 0 &&
|
||||
InstState::isOverwrite() &&
|
||||
basename($this->bootFilePath) == 'installer.php' &&
|
||||
!in_array($_SERVER['REMOTE_ADDR'], self::getSecurityAddrWhitelist())
|
||||
) {
|
||||
return self::SECURITY_ARCHIVE;
|
||||
}
|
||||
|
||||
return self::SECURITY_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IPs white list for remote requests
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private static function getSecurityAddrWhitelist(): array
|
||||
{
|
||||
// uncomment this to test security archive on localhost
|
||||
// return array();
|
||||
// -------
|
||||
return [
|
||||
'127.0.0.1',
|
||||
'::1',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if security check is passed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function securityCheck()
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
||||
$result = false;
|
||||
switch ($this->getSecurityType()) {
|
||||
case self::SECURITY_NONE:
|
||||
$result = true;
|
||||
break;
|
||||
case self::SECURITY_PASSWORD:
|
||||
$paramsManager->setValueFromInput(PrmMng::PARAM_SECURE_PASS);
|
||||
$inputPwd = $paramsManager->getValue(PrmMng::PARAM_SECURE_PASS);
|
||||
$result = hash_equals($archiveConfig->secure_pass, self::passwordHash($inputPwd));
|
||||
break;
|
||||
case self::SECURITY_ARCHIVE:
|
||||
$paramsManager->setValueFromInput(PrmMng::PARAM_SECURE_ARCHIVE_HASH);
|
||||
$result = (strcmp(basename($this->archivePath), $paramsManager->getValue(PrmMng::PARAM_SECURE_ARCHIVE_HASH)) == 0);
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Security type not valid ' . $this->getSecurityType());
|
||||
}
|
||||
|
||||
$paramsManager->setValue(PrmMng::PARAM_SECURE_OK, $result);
|
||||
$paramsManager->save();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash password
|
||||
*
|
||||
* @param string $password input password
|
||||
*
|
||||
* @return string Returns a string containing the calculated message digest as lowercase hexit
|
||||
*/
|
||||
public static function passwordHash($password): string
|
||||
{
|
||||
return hash('sha512', $password);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Models;
|
||||
|
||||
class ImportUser
|
||||
{
|
||||
protected int $id;
|
||||
/** @var string */
|
||||
protected $login = '';
|
||||
/** @var string */
|
||||
protected $mail = '';
|
||||
protected int $oldId;
|
||||
/** @var string */
|
||||
protected $oldLogin = '';
|
||||
/** @var bool */
|
||||
protected $isAdded = false;
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*
|
||||
* @param int $id user id
|
||||
* @param string $login user login
|
||||
* @param string $mail user e-mail
|
||||
* @param integer $oldId old user id, if 0 isn't changed
|
||||
* @param string $oldLogin old user login, if empty isn't changed
|
||||
* @param boolean $isAdded if true this is new user
|
||||
*/
|
||||
public function __construct($id, $login, $mail, $oldId = 0, $oldLogin = '', $isAdded = false)
|
||||
{
|
||||
$this->id = (int) $id;
|
||||
$this->login = $login;
|
||||
$this->mail = $mail;
|
||||
$this->oldId = (int) $oldId;
|
||||
$this->oldLogin = $oldLogin;
|
||||
$this->isAdded = $isAdded;
|
||||
|
||||
if ($this->id == $this->oldId) {
|
||||
$this->oldId = 0;
|
||||
}
|
||||
if ($this->login == $this->oldLogin) {
|
||||
$this->oldLogin = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return CSV report columns title
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getArrayReportTitles(): array
|
||||
{
|
||||
return [
|
||||
'e-mail',
|
||||
'original login',
|
||||
'new login',
|
||||
'original id',
|
||||
'new id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array for CSV report
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
public function getArrayReport(): array
|
||||
{
|
||||
$result = [$this->mail];
|
||||
if (strlen($this->oldLogin) == 0) {
|
||||
$result[] = $this->login;
|
||||
$result[] = '';
|
||||
} else {
|
||||
$result[] = $this->oldLogin;
|
||||
$result[] = $this->login;
|
||||
}
|
||||
|
||||
if ($this->oldId == 0) {
|
||||
$result[] = $this->id;
|
||||
$result[] = '';
|
||||
} else {
|
||||
$result[] = $this->oldId;
|
||||
$result[] = $this->id;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of login
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLogin()
|
||||
{
|
||||
return $this->login;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of mail
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMail()
|
||||
{
|
||||
return $this->mail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of oldId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getOldId(): int
|
||||
{
|
||||
return ($this->oldId == 0 ? $this->id : $this->oldId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of oldId
|
||||
*
|
||||
* @param int $oldId old mapped id
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOldId($oldId): void
|
||||
{
|
||||
$this->oldId = (int) ($this->id == $oldId ? 0 : $oldId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of oldLogin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOldLogin()
|
||||
{
|
||||
return (strlen($this->oldLogin) == 0 ? $this->login : $this->oldLogin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of oldLogin
|
||||
*
|
||||
* @param string $oldLogin old login
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setOldLogin($oldLogin): void
|
||||
{
|
||||
$this->oldLogin = ($this->login == $oldLogin ? '' : $oldLogin);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if current user have changed values (login or id)
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isChanged(): bool
|
||||
{
|
||||
return ($this->oldId > 0 || strlen($this->oldLogin) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* True if current user is added
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdded()
|
||||
{
|
||||
return $this->isAdded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Models;
|
||||
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
|
||||
class MigrateData
|
||||
{
|
||||
/** @var string */
|
||||
public $plugin = 'dup-pro';
|
||||
/** @var string */
|
||||
public $installerVersion = '';
|
||||
/** @var int */
|
||||
public $installType = InstState::TYPE_NOT_SET;
|
||||
/** @var string */
|
||||
public $installTime = '';
|
||||
/** @var string[] */
|
||||
public $logicModes = [];
|
||||
/** @var string */
|
||||
public $template = '';
|
||||
/** @var bool */
|
||||
public $restoreBackupMode = false;
|
||||
/** @var bool */
|
||||
public $recoveryMode = false;
|
||||
/** @var string */
|
||||
public $archivePath = '';
|
||||
/** @var int */
|
||||
public $packageId = 0;
|
||||
/** @var string */
|
||||
public $packageHash = '';
|
||||
/** @var string */
|
||||
public $installerPath = '';
|
||||
/** @var string */
|
||||
public $installerBootLog = '';
|
||||
/** @var string */
|
||||
public $installerLog = '';
|
||||
/** @var string */
|
||||
public $dupInstallerPath = '';
|
||||
/** @var string */
|
||||
public $origFileFolderPath = '';
|
||||
/** @var bool */
|
||||
public $safeMode = false;
|
||||
/** @var bool */
|
||||
public $cleanInstallerFiles = false;
|
||||
/** @var int */
|
||||
public $licenseType = -1;
|
||||
/** @var string */
|
||||
public $phpVersion = '';
|
||||
/** @var string */
|
||||
public $archiveType = '';
|
||||
/** @var float */
|
||||
public $siteSize = 0.0;
|
||||
/** @var int */
|
||||
public $siteNumFiles = 0;
|
||||
/** @var float */
|
||||
public $siteDbSize = 0.0;
|
||||
/** @var int */
|
||||
public $siteDBNumTables = 0;
|
||||
/** @var string[] */
|
||||
public $components = [];
|
||||
/** @var string */
|
||||
public $ustatIdentifier = '';
|
||||
/** @var bool */
|
||||
public $stagingMode = false;
|
||||
/** @var string */
|
||||
public $mainSiteUrl = '';
|
||||
/** @var string */
|
||||
public $stagingPageUrl = '';
|
||||
/** @var string */
|
||||
public $stagingIdentifier = '';
|
||||
/** @var string */
|
||||
public $colorScheme = 'fresh';
|
||||
/** @var string */
|
||||
public $stagingTitle = '';
|
||||
/** @var array{enabled: bool, mainSiteUrl: string, pageUrl: string, identifier: string, colorScheme: string, title: string} */
|
||||
public $staging = [
|
||||
'enabled' => false,
|
||||
'mainSiteUrl' => '',
|
||||
'pageUrl' => '',
|
||||
'identifier' => '',
|
||||
'colorScheme' => 'fresh',
|
||||
'title' => '',
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Models;
|
||||
|
||||
use DUPX_Package;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Package scan info
|
||||
*/
|
||||
final class ScanInfo
|
||||
{
|
||||
/** @var array<string, mixed> */
|
||||
private $data = [];
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton class constructor
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
$scanFile = DUPX_Package::getScanJsonPath();
|
||||
if (!file_exists($scanFile)) {
|
||||
throw new Exception("Archive file $scanFile doesn't exist");
|
||||
}
|
||||
|
||||
if (($contents = file_get_contents($scanFile)) === false) {
|
||||
throw new Exception("Can\'t read Archive file $scanFile");
|
||||
}
|
||||
|
||||
if (($this->data = json_decode($contents, true)) === null) {
|
||||
throw new Exception("Can\'t decode archive json");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uncompressed size, -1 unknown
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUSize()
|
||||
{
|
||||
return $this->data['ARC']['Usize'] ?? -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if package has filtered core folders
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFilteredCoreFolders()
|
||||
{
|
||||
return $this->data['ARC']['Status']['HasFilteredCoreFolders'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
use Duplicator\Installer\Addons\ProBase\AbstractLicense;
|
||||
|
||||
class ArchiveDescriptor
|
||||
{
|
||||
const SECURE_MODE_NONE = 0;
|
||||
const SECURE_MODE_INST_PWD = 1;
|
||||
const SECURE_MODE_ARC_ENCRYPT = 2;
|
||||
|
||||
const GEN_FILE_REGEX_PATTERN = '/^(.+_[a-z0-9]{7,}_[0-9]{14})_.+\.(?:zip|daf|php|bak)$/';
|
||||
const ARCHIVE_REGEX_PATTERN = '/^(.+_[a-z0-9]{7,}_[0-9]{14})_archive\.(?:zip|daf)$/';
|
||||
const INSTALLER_REGEX_PATTERN = '/^(?:.+_[a-z0-9]{7,}_[0-9]{14}_)?installer(?:-backup)?\.(php|php\.bak)$/';
|
||||
const NAME_PARTS_REGEX_PATTERN = '/^(.+)_([a-z0-9]{7,})_([0-9]{14})_.+\.(?:zip|daf|php|bak)$/';
|
||||
|
||||
/** @var string */
|
||||
public $dup_type = 'pro';
|
||||
/** @var string */
|
||||
public $created = '';
|
||||
/** @var string */
|
||||
public $version_dup = '';
|
||||
/** @var string */
|
||||
public $version_wp = '';
|
||||
/** @var string */
|
||||
public $version_db = '';
|
||||
/** @var string */
|
||||
public $version_php = '';
|
||||
/** @var string */
|
||||
public $version_os = '';
|
||||
/** @var string */
|
||||
public $blogname = '';
|
||||
/** @var bool */
|
||||
public $exportOnlyDB = false;
|
||||
/** @var int<0,2> */
|
||||
public $secure_on = self::SECURE_MODE_NONE;
|
||||
/** @var string */
|
||||
public $secure_pass = '';
|
||||
/** @var ?string */
|
||||
public $dbhost;
|
||||
/** @var ?string */
|
||||
public $dbname;
|
||||
/** @var ?string */
|
||||
public $dbuser;
|
||||
/** @var ?string */
|
||||
public $cpnl_host;
|
||||
/** @var ?string */
|
||||
public $cpnl_user;
|
||||
/** @var ?string */
|
||||
public $cpnl_pass;
|
||||
/** @var ?string */
|
||||
public $cpnl_enable;
|
||||
/** @var string */
|
||||
public $wp_tableprefix = '';
|
||||
/** @var int<0, 2> */
|
||||
public $mu_mode = 0;
|
||||
/** @var int<0, 2> */
|
||||
public $mu_generation = 0;
|
||||
/** @var string[] */
|
||||
public $mu_siteadmins = [];
|
||||
/** @var DescriptorSubsite[] */
|
||||
public $subsites = [];
|
||||
/** @var int */
|
||||
public $main_site_id = 1;
|
||||
/** @var bool */
|
||||
public $mu_is_filtered = false;
|
||||
/** @var int */
|
||||
public $license_limit = 0;
|
||||
/** @var int */
|
||||
public $license_type = AbstractLicense::TYPE_UNLICENSED;
|
||||
/** @var ?DescriptorDBInfo */
|
||||
public $dbInfo;
|
||||
/** @var ?DescriptorPackageInfo */
|
||||
public $packInfo;
|
||||
/** @var ?DescriptorFileInfo*/
|
||||
public $fileInfo;
|
||||
/** @var ?DescriptorWpInfo */
|
||||
public $wpInfo;
|
||||
/** @var int<-1,max> */
|
||||
public $defaultStorageId = -1;
|
||||
/** @var string[] */
|
||||
public $components = [];
|
||||
/** @var string[] */
|
||||
public $opts_delete = [];
|
||||
/** @var array<string, mixed> */
|
||||
public $brand = [];
|
||||
/** @var array<string, mixed> */
|
||||
public $overwriteInstallerParams = [];
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @deprecated This option is no longer taken into account
|
||||
*/
|
||||
public $installer_base_name = '';
|
||||
/** @var string */
|
||||
public $installer_backup_name = '';
|
||||
/** @var string */
|
||||
public $package_name = '';
|
||||
/** @var string */
|
||||
public $package_hash = '';
|
||||
/** @var string */
|
||||
public $package_notes = '';
|
||||
|
||||
/**
|
||||
* Check if the file path is have a valid archive file name
|
||||
*
|
||||
* @param string $filePath File path, accept full path or just file name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isArchiveFile($filePath)
|
||||
{
|
||||
if (empty($filePath)) {
|
||||
return false;
|
||||
}
|
||||
$filePath = basename($filePath);
|
||||
return preg_match(self::GEN_FILE_REGEX_PATTERN, $filePath) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archive name parts, name, hash, date, package hash or false if name is invalid
|
||||
*
|
||||
* @param string $filePath Archive or installer name path, accept full path or just file name
|
||||
*
|
||||
* @return array{name:string,hash:string,date:string,packageHash:string}|false
|
||||
*/
|
||||
public static function getArchiveNameParts($filePath)
|
||||
{
|
||||
if (empty($filePath)) {
|
||||
return false;
|
||||
}
|
||||
$filePath = basename($filePath);
|
||||
$matches = [];
|
||||
if (preg_match(self::NAME_PARTS_REGEX_PATTERN, $filePath, $matches)) {
|
||||
return [
|
||||
'name' => $matches[1],
|
||||
'hash' => $matches[2],
|
||||
'date' => $matches[3],
|
||||
'packageHash' => substr($matches[2], 0, 7) . '-' . substr($matches[3], -8),
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* The database descriptor is used to store the database meta data
|
||||
*/
|
||||
class DescriptorDBInfo
|
||||
{
|
||||
/** @var string The SQL file was built with mysqldump or PHP */
|
||||
public $buildMode = 'PHP';
|
||||
/** @var string[] A unique list of all the charSet table types used in the database */
|
||||
public $charSetList = [];
|
||||
/** @var string[] A unique list of all the collation table types used in the database */
|
||||
public $collationList = [];
|
||||
/** @var string[] A unique list of all the engine types used in the database */
|
||||
public $engineList = [];
|
||||
/** @var bool Does any filtered table have an upper case character in it */
|
||||
public $isTablesUpperCase = false;
|
||||
/** @var int Value of the DB variable lower_case_table_names */
|
||||
public $lowerCaseTableNames = 0;
|
||||
/** @var bool Does the database name have any filtered characters in it */
|
||||
public $isNameUpperCase = false;
|
||||
/** @var string The real name of the database */
|
||||
public $name = '';
|
||||
/** @var int he full count of all tables in the database */
|
||||
public $tablesBaseCount = 0;
|
||||
/** @var int The count of tables after the tables filter has been applied */
|
||||
public $tablesFinalCount = 0;
|
||||
/** @var int The count of tables filtered programmatically for multi-site purposes */
|
||||
public $muFilteredTableCount = 0;
|
||||
/** @var int The number of rows from all filtered tables in the database */
|
||||
public $tablesRowCount = 0;
|
||||
/** @var int The estimated data size on disk from all filtered tables in the database */
|
||||
public $tablesSizeOnDisk = 0;
|
||||
/** @var DescriptorDBTableInfo[] */
|
||||
public $tablesList = [];
|
||||
/** @var string The database engine (MySQL/MariaDB/Percona) */
|
||||
public $dbEngine = '';
|
||||
/** @var string The simple numeric version number of the database server @exmaple: 5.5 */
|
||||
public $version = '0';
|
||||
/** @var string The full text version number of the database server @exmaple: 10.2 mariadb.org binary distribution */
|
||||
public $versionComment = '';
|
||||
/** @var int Number of VIEWs in the database */
|
||||
public $viewCount = 0;
|
||||
/** @var int Number of PROCEDUREs in the database */
|
||||
public $procCount = 0;
|
||||
/** @var int Number of PROCEDUREs in the database */
|
||||
public $funcCount = 0;
|
||||
/** @var array<string, array{event: string, table: string, timing: string, create: string}> Trigger information */
|
||||
public $triggerList = [];
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* The database table descriptor
|
||||
*/
|
||||
class DescriptorDBTableInfo
|
||||
{
|
||||
/** @var int<0, max> */
|
||||
public $inaccurateRows = 0;
|
||||
/** @var false|int<0, max> */
|
||||
public $insertedRows = 0;
|
||||
/** @var int<0, max> */
|
||||
public $size = 0;
|
||||
|
||||
/**
|
||||
* Classs constructor
|
||||
*
|
||||
* @param int<0, max> $inaccurateRows This data is intended as a preliminary count and therefore not necessarily accurate
|
||||
* @param int<0, max> $size This data is intended as a preliminary count and therefore not necessarily accurate
|
||||
* @param false|int<0, max> $insertedRows This value, if other than false, is the exact line value inserted into the dump file
|
||||
*/
|
||||
public function __construct($inaccurateRows = 0, $size = 0, $insertedRows = false)
|
||||
{
|
||||
$this->inaccurateRows = $inaccurateRows;
|
||||
$this->insertedRows = (int) $insertedRows;
|
||||
$this->size = (int) $size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* The file descriptor is used to store the file meta data
|
||||
*/
|
||||
class DescriptorFileInfo
|
||||
{
|
||||
/** @var int */
|
||||
public $dirCount = 0;
|
||||
/** @var int */
|
||||
public $fileCount = 0;
|
||||
/** @var int */
|
||||
public $size = 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
class DescriptorPackageInfo
|
||||
{
|
||||
/** @var int */
|
||||
public $packageId = 0;
|
||||
/** @var string */
|
||||
public $packageName = '';
|
||||
/** @var string */
|
||||
public $packageHash = '';
|
||||
/** @var string */
|
||||
public $secondaryHash = '';
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* Plugin descriptor
|
||||
*/
|
||||
class DescriptorPlugin
|
||||
{
|
||||
/** @var string */
|
||||
public $slug = '';
|
||||
/** @var string */
|
||||
public $name = '';
|
||||
/** @var string */
|
||||
public $version = '';
|
||||
/** @var string */
|
||||
public $pluginURI = '';
|
||||
/** @var string */
|
||||
public $author = '';
|
||||
/** @var string */
|
||||
public $authorURI = '';
|
||||
/** @var string */
|
||||
public $description = '';
|
||||
/** @var string */
|
||||
public $title = '';
|
||||
/** @var bool */
|
||||
public $networkActive = false;
|
||||
/** @var bool|int[] list of subsites ids if is multisite */
|
||||
public $active = false;
|
||||
/** @var bool */
|
||||
public $mustUse = false;
|
||||
/** @var bool */
|
||||
public $dropIns = false;
|
||||
/** @var bool */
|
||||
public $isInArchive = true;
|
||||
|
||||
const DEFAULT_DATA = [
|
||||
'slug' => '',
|
||||
'name' => '',
|
||||
'version' => '',
|
||||
'pluginURI' => '',
|
||||
'author' => '',
|
||||
'authorURI' => '',
|
||||
'description' => '',
|
||||
'title' => '',
|
||||
'networkActive' => false,
|
||||
'active' => false,
|
||||
'mustUse' => false,
|
||||
'dropIns' => false,
|
||||
'isInArchive' => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array<string, mixed> $pluginData plugin info data
|
||||
*/
|
||||
public function __construct(array $pluginData = [])
|
||||
{
|
||||
$data = array_merge(self::DEFAULT_DATA, $pluginData);
|
||||
$this->slug = $data['slug'];
|
||||
$this->name = $data['name'];
|
||||
$this->version = $data['version'];
|
||||
$this->pluginURI = $data['pluginURI'];
|
||||
$this->author = $data['author'];
|
||||
$this->authorURI = $data['authorURI'];
|
||||
$this->description = $data['description'];
|
||||
$this->title = $data['title'];
|
||||
$this->networkActive = $data['networkActive'];
|
||||
$this->active = $data['active'];
|
||||
$this->mustUse = $data['mustUse'];
|
||||
$this->dropIns = $data['dropIns'];
|
||||
$this->isInArchive = $data['isInArchive'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* Subsite descriptor
|
||||
*/
|
||||
class DescriptorSubsite
|
||||
{
|
||||
/** @var int */
|
||||
public $id = 0;
|
||||
/** @var string */
|
||||
public $domain = '';
|
||||
/** @var string */
|
||||
public $path = '';
|
||||
/** @var string */
|
||||
public $blogname = '';
|
||||
/** @var string */
|
||||
public $blog_prefix = '';
|
||||
/** @var string[] */
|
||||
public $filteredTables = [];
|
||||
/** @var array<object{ID: int, user_login: string}> */
|
||||
public $adminUsers = [];
|
||||
/** @var string */
|
||||
public $fullHomeUrl = '';
|
||||
/** @var string */
|
||||
public $fullSiteUrl = '';
|
||||
/** @var string */
|
||||
public $uploadPath = '';
|
||||
/** @var string */
|
||||
public $fullUploadPath = '';
|
||||
/** @var string */
|
||||
public $fullUploadSafePath = '';
|
||||
/** @var string */
|
||||
public $fullUploadUrl = '';
|
||||
/** @var string[] */
|
||||
public $filteredPaths = [];
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* Theme descriptor
|
||||
*/
|
||||
class DescriptorTheme
|
||||
{
|
||||
/** @var string */
|
||||
public $slug = '';
|
||||
/** @var string */
|
||||
public $themeName = '';
|
||||
/** @var string */
|
||||
public $version = '';
|
||||
/** @var string */
|
||||
public $themeURI = '';
|
||||
/** @var string */
|
||||
public $parentTheme = '';
|
||||
/** @var string */
|
||||
public $template = '';
|
||||
/** @var string */
|
||||
public $stylesheet = '';
|
||||
/** @var string */
|
||||
public $description = '';
|
||||
/** @var string */
|
||||
public $author = '';
|
||||
/** @var string */
|
||||
public $authorURI = '';
|
||||
/** @var string[] */
|
||||
public $tags = [];
|
||||
/** @var bool */
|
||||
public $isAllowed = false;
|
||||
/** @var bool|int[] list of subsites ids if is multisite */
|
||||
public $isActive = false;
|
||||
/** @var bool */
|
||||
public $defaultTheme = false;
|
||||
|
||||
const DEFAULT_DATA = [
|
||||
'slug' => '',
|
||||
'themeName' => '',
|
||||
'version' => '',
|
||||
'themeURI' => '',
|
||||
'parentTheme' => '',
|
||||
'template' => '',
|
||||
'stylesheet' => '',
|
||||
'description' => '',
|
||||
'author' => '',
|
||||
'authorURI' => '',
|
||||
'tags' => [],
|
||||
'isAllowed' => false,
|
||||
'isActive' => false,
|
||||
'defaultTheme' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array<string, mixed> $themeData theme info data
|
||||
*/
|
||||
public function __construct($themeData = [])
|
||||
{
|
||||
$data = array_merge(self::DEFAULT_DATA, $themeData);
|
||||
$this->slug = $data['slug'];
|
||||
$this->themeName = $data['themeName'];
|
||||
$this->version = $data['version'];
|
||||
$this->themeURI = $data['themeURI'];
|
||||
$this->parentTheme = $data['parentTheme'];
|
||||
$this->template = $data['template'];
|
||||
$this->stylesheet = $data['stylesheet'];
|
||||
$this->description = $data['description'];
|
||||
$this->author = $data['author'];
|
||||
$this->authorURI = $data['authorURI'];
|
||||
$this->tags = $data['tags'];
|
||||
$this->isAllowed = $data['isAllowed'];
|
||||
$this->isActive = $data['isActive'];
|
||||
$this->defaultTheme = $data['defaultTheme'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* The wp descriptor is used to store the wp meta data
|
||||
*/
|
||||
class DescriptorWpInfo
|
||||
{
|
||||
/** @var string */
|
||||
public $version = '';
|
||||
/** @var bool */
|
||||
public $is_multisite = false;
|
||||
/** @var int */
|
||||
public $network_id = 1;
|
||||
/** @var string */
|
||||
public $targetRoot = '';
|
||||
/** @var string[] */
|
||||
public $targetPaths = [];
|
||||
/** @var array<object{ID: int, user_login: string}> */
|
||||
public $adminUsers = [];
|
||||
/** @var ?stdClass */
|
||||
public $configs;
|
||||
/** @var DescriptorPlugin[] */
|
||||
public $plugins = [];
|
||||
/** @var DescriptorTheme[] */
|
||||
public $themes = [];
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->configs = new stdClass();
|
||||
$this->configs->defines = new stdClass();
|
||||
$this->configs->realValues = new stdClass();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2024, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
class InstallerDescriptors
|
||||
{
|
||||
const FIRST_VERSION_NEW_ARCHIVE_FILES = '4.5.20';
|
||||
const DESCRIPTORS_FOLDER_PREFIX = 'dup_descriptors_';
|
||||
|
||||
const TYPE_ORIG_FILES = 'orig_files';
|
||||
const TYPE_ARCHIVE_CONFIG = 'archive_config';
|
||||
const TYPE_SCAN = 'scan';
|
||||
const TYPE_INDEX = 'file_index';
|
||||
const TYPE_FILE_LIST = 'file_list';
|
||||
const TYPE_DIR_LIST = 'dir_list';
|
||||
const TYPE_MANUAL_EXTRACT = 'manual_extract';
|
||||
const TYPE_DB_DUMP = 'db_dump';
|
||||
const TYPE_PARAMS = 'params';
|
||||
const TYPE_INST_CHUNK_DATA = 'inst_chunk_data';
|
||||
const TYPE_INST_NOTICES = 'inst_notices';
|
||||
const TYPE_INST_DB_DATA = 'inst_db_data';
|
||||
const TYPE_INST_DB_SEEK_TELL_LOG = 'inst_db_seek_log';
|
||||
const TYPE_INST_EXTRACTION_DATA = 'inst_extraction_data';
|
||||
const TYPE_INST_S3_DATA = 'inst_s3_data';
|
||||
const TYPE_INST_PHP_ERROR_LOG = 'inst_php_error_log';
|
||||
|
||||
/** @var string The backup hash */
|
||||
protected $hash = '';
|
||||
|
||||
/** @var string The backup date in YmdHis format */
|
||||
protected $date = '';
|
||||
|
||||
/** @var array<string,string> Names mapping*/
|
||||
protected $mapping = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $hash The backup hash
|
||||
* @param string $date The backup date in YmdHis format
|
||||
*/
|
||||
public function __construct($hash, $date)
|
||||
{
|
||||
if (empty($hash)) {
|
||||
throw new \Exception('The hash is required');
|
||||
}
|
||||
|
||||
if (empty($date)) {
|
||||
throw new \Exception('The date is required');
|
||||
}
|
||||
|
||||
$this->hash = $hash;
|
||||
$this->date = $date;
|
||||
$this->initMapping();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the folder in which all descriptors are stored
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescriptorsFolder(): string
|
||||
{
|
||||
return self::DESCRIPTORS_FOLDER_PREFIX . $this->hash . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Init descriptors mapping
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initMapping()
|
||||
{
|
||||
$this->mapping = [
|
||||
self::TYPE_ORIG_FILES => 'orig_files',
|
||||
self::TYPE_ARCHIVE_CONFIG => 'archive.txt',
|
||||
self::TYPE_SCAN => 'scan.json',
|
||||
self::TYPE_FILE_LIST => 'scanned-files.txt',
|
||||
self::TYPE_DIR_LIST => 'scanned-dirs.txt',
|
||||
self::TYPE_INDEX => 'index.txt',
|
||||
self::TYPE_MANUAL_EXTRACT => 'manual-extract',
|
||||
self::TYPE_DB_DUMP => 'db_dumps/' . $this->date . '-dump.sql',
|
||||
self::TYPE_INST_CHUNK_DATA => 'installer-chunk-data.json',
|
||||
self::TYPE_INST_NOTICES => 'installer-notices.json',
|
||||
self::TYPE_INST_DB_DATA => 'installer-db-data.json',
|
||||
self::TYPE_INST_DB_SEEK_TELL_LOG => 'installer-db-seek-tell-log.txt',
|
||||
self::TYPE_INST_EXTRACTION_DATA => 'installer-extraction.json',
|
||||
self::TYPE_INST_S3_DATA => 'installer-s3data.json',
|
||||
self::TYPE_INST_PHP_ERROR_LOG => 'php_error.log',
|
||||
self::TYPE_PARAMS => 'params.json',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the file name of the descriptor file with hash
|
||||
*
|
||||
* @param string $descriptorType The archive file type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName($descriptorType): string
|
||||
{
|
||||
if (!isset($this->mapping[$descriptorType])) {
|
||||
throw new \Exception('Invalid descriptor type ' . $descriptorType);
|
||||
}
|
||||
|
||||
return $this->getDescriptorsFolder() . $this->mapping[$descriptorType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name of the descriptor file without hash or date
|
||||
*
|
||||
* @param string $descriptorType The archive file type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGenericName($descriptorType): string
|
||||
{
|
||||
if (!isset($this->mapping[$descriptorType])) {
|
||||
throw new \Exception('Invalid descriptor type ' . $descriptorType);
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
[
|
||||
$this->hash,
|
||||
$this->date,
|
||||
],
|
||||
[
|
||||
'[HASH]',
|
||||
'[DATE]',
|
||||
],
|
||||
$this->getDescriptorsFolder() . $this->mapping[$descriptorType]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file name of the descriptor file with hash
|
||||
*
|
||||
* @param string $descriptorType The archive file type
|
||||
* @param string $hash (Optional) The hash of the archive
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOldName($descriptorType, $hash = ''): string
|
||||
{
|
||||
if (empty($hash)) {
|
||||
$hash = $this->hash;
|
||||
}
|
||||
|
||||
return self::getHashedOldName($descriptorType, $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location of the old archive files
|
||||
*
|
||||
* @param string $descriptorType The descriptor type
|
||||
* @param string $hash The hash of the archive
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getHashedOldName($descriptorType, $hash): string
|
||||
{
|
||||
$mapping = [
|
||||
self::TYPE_ORIG_FILES => 'orig_files__' . $hash,
|
||||
self::TYPE_ARCHIVE_CONFIG => 'dup-archive__' . $hash . '.txt',
|
||||
self::TYPE_SCAN => 'dup-scan__' . $hash . '.json',
|
||||
self::TYPE_FILE_LIST => 'dup-scanned-files__' . $hash . '.txt',
|
||||
self::TYPE_DIR_LIST => 'dup-scanned-dirs__' . $hash . '.txt',
|
||||
self::TYPE_MANUAL_EXTRACT => 'dup-manual-extract__' . $hash,
|
||||
self::TYPE_DB_DUMP => 'dup-database__' . $hash . '.sql',
|
||||
self::TYPE_INST_CHUNK_DATA => 'dup-installer-chunk__' . $hash . '.json',
|
||||
self::TYPE_INST_NOTICES => 'dup-installer-notices__' . $hash . '.json',
|
||||
self::TYPE_INST_DB_DATA => 'dup-installer-dbinstall__' . $hash . '.json',
|
||||
self::TYPE_INST_DB_SEEK_TELL_LOG => 'dup-database-seek-tell-log__' . $hash . '.txt',
|
||||
self::TYPE_INST_EXTRACTION_DATA => 'dup-installer-extraction__' . $hash . '.json',
|
||||
self::TYPE_INST_S3_DATA => 'dup-installer-s3data__' . $hash . '.json',
|
||||
self::TYPE_INST_PHP_ERROR_LOG => 'php_error__' . $hash . '.log',
|
||||
self::TYPE_PARAMS => 'dup-params__' . $hash . '.json',
|
||||
];
|
||||
|
||||
if (!isset($mapping[$descriptorType])) {
|
||||
throw new \Exception('Invalid descriptor type');
|
||||
}
|
||||
|
||||
return $mapping[$descriptorType];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2024, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
/**
|
||||
* This class is used to manage old installer files previously version 4.5.20
|
||||
*/
|
||||
class LegacyInstallerDescriptors extends InstallerDescriptors
|
||||
{
|
||||
/**
|
||||
* Init descriptors mapping
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function initMapping()
|
||||
{
|
||||
$this->mapping = [
|
||||
self::TYPE_ORIG_FILES => 'orig_files__' . $this->hash,
|
||||
self::TYPE_ARCHIVE_CONFIG => 'dup-archive__' . $this->hash . '.txt',
|
||||
self::TYPE_SCAN => 'dup-scan__' . $this->hash . '.json',
|
||||
self::TYPE_FILE_LIST => 'dup-scanned-files__' . $this->hash . '.txt',
|
||||
self::TYPE_DIR_LIST => 'dup-scanned-dirs__' . $this->hash . '.txt',
|
||||
self::TYPE_MANUAL_EXTRACT => 'dup-manual-extract__' . $this->hash,
|
||||
self::TYPE_DB_DUMP => 'dup-database__' . $this->hash . '.sql',
|
||||
self::TYPE_INST_CHUNK_DATA => 'dup-installer-chunk__' . $this->hash . '.json',
|
||||
self::TYPE_INST_NOTICES => 'dup-installer-notices__' . $this->hash . '.json',
|
||||
self::TYPE_INST_DB_DATA => 'dup-installer-dbinstall__' . $this->hash . '.json',
|
||||
self::TYPE_INST_DB_SEEK_TELL_LOG => 'dup-database-seek-tell-log__' . $this->hash . '.txt',
|
||||
self::TYPE_INST_EXTRACTION_DATA => 'dup-installer-extraction__' . $this->hash . '.json',
|
||||
self::TYPE_INST_S3_DATA => 'dup-installer-s3data__' . $this->hash . '.json',
|
||||
self::TYPE_INST_PHP_ERROR_LOG => 'php_error__' . $this->hash . '.log',
|
||||
self::TYPE_PARAMS => 'dup-params__' . $this->hash . '.json',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the folder in which all descriptors are stored
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescriptorsFolder(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Package;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PComponents
|
||||
{
|
||||
const COMP_DB = 'package_component_db';
|
||||
const COMP_CORE = 'package_component_core';
|
||||
const COMP_PLUGINS = 'package_component_plugins';
|
||||
const COMP_PLUGINS_ACTIVE = 'package_component_plugins_active';
|
||||
const COMP_THEMES = 'package_component_themes';
|
||||
const COMP_THEMES_ACTIVE = 'package_component_themes_active';
|
||||
const COMP_UPLOADS = 'package_component_uploads';
|
||||
const COMP_OTHER = 'package_component_other';
|
||||
|
||||
const COMPONENTS = [
|
||||
self::COMP_DB,
|
||||
self::COMP_CORE,
|
||||
self::COMP_PLUGINS,
|
||||
self::COMP_PLUGINS_ACTIVE,
|
||||
self::COMP_THEMES,
|
||||
self::COMP_THEMES_ACTIVE,
|
||||
self::COMP_UPLOADS,
|
||||
self::COMP_OTHER,
|
||||
];
|
||||
|
||||
const COMPONENTS_DEFAULT = [
|
||||
self::COMP_DB,
|
||||
self::COMP_CORE,
|
||||
self::COMP_PLUGINS,
|
||||
self::COMP_THEMES,
|
||||
self::COMP_UPLOADS,
|
||||
self::COMP_OTHER,
|
||||
];
|
||||
|
||||
const SUB_OPTIONS = [
|
||||
self::COMP_PLUGINS_ACTIVE,
|
||||
self::COMP_THEMES_ACTIVE,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get label by compoentent
|
||||
*
|
||||
* @param string $component The component
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLabel($component): string
|
||||
{
|
||||
switch ($component) {
|
||||
case self::COMP_DB:
|
||||
return 'Database';
|
||||
case self::COMP_CORE:
|
||||
return 'Core';
|
||||
case self::COMP_PLUGINS:
|
||||
return 'Plugins';
|
||||
case self::COMP_PLUGINS_ACTIVE:
|
||||
return 'Only Active Plugins';
|
||||
case self::COMP_THEMES:
|
||||
return 'Themes';
|
||||
case self::COMP_THEMES_ACTIVE:
|
||||
return 'Only Active Themes';
|
||||
case self::COMP_UPLOADS:
|
||||
return 'Media';
|
||||
case self::COMP_OTHER:
|
||||
return 'Other';
|
||||
default:
|
||||
throw new Exception('Invalid component: ' . $component);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component labels imploded by seperator
|
||||
*
|
||||
* @param string[] $components array of components
|
||||
* @param string $seperator the seperator string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function displayComponentsList($components, $seperator = ', '): string
|
||||
{
|
||||
return implode($seperator, array_map(fn($component): string => self::getLabel($component), $components));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a DB only package
|
||||
*
|
||||
* @param string[] $components active components
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDBOnly($components): bool
|
||||
{
|
||||
return count($components) === 1 && in_array(self::COMP_DB, $components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a DB only package
|
||||
*
|
||||
* @param string[] $components active components
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMediaOnly($components): bool
|
||||
{
|
||||
return count($components) === 1 && in_array(self::COMP_UPLOADS, $components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the DB package component has been excluded
|
||||
*
|
||||
* @param string[] $activeComponents list of active components
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDBExcluded($activeComponents): bool
|
||||
{
|
||||
return !in_array(self::COMP_DB, $activeComponents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are all files components
|
||||
*
|
||||
* @param string[] $components list of active components
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFullFilesComponents($components): bool
|
||||
{
|
||||
$result = array_diff(
|
||||
[
|
||||
self::COMP_CORE,
|
||||
self::COMP_PLUGINS,
|
||||
self::COMP_THEMES,
|
||||
self::COMP_UPLOADS,
|
||||
],
|
||||
$components
|
||||
);
|
||||
return count($result) === 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\REST;
|
||||
|
||||
use VendorDuplicator\WpOrg\Requests\Auth;
|
||||
use VendorDuplicator\WpOrg\Requests\Auth\Basic;
|
||||
use VendorDuplicator\WpOrg\Requests\Hooks;
|
||||
|
||||
class RESTAuth implements Auth
|
||||
{
|
||||
/** @var string */
|
||||
protected $nonce = '';
|
||||
/** @var string */
|
||||
protected $basicAuthUser = '';
|
||||
/** @var string */
|
||||
protected $basicAuthPassword = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $nonce nonce user
|
||||
* @param string $basicAuthUser auth user
|
||||
* @param string $basicAuthPassword auth password
|
||||
*/
|
||||
public function __construct($nonce, $basicAuthUser = "", $basicAuthPassword = "")
|
||||
{
|
||||
$this->nonce = $nonce;
|
||||
$this->basicAuthUser = $basicAuthUser;
|
||||
$this->basicAuthPassword = $basicAuthPassword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register auth hooks
|
||||
*
|
||||
* @param Hooks $hooks hooks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(Hooks $hooks): void
|
||||
{
|
||||
if (strlen($this->basicAuthUser) > 0) {
|
||||
$basicAuth = new Basic([
|
||||
$this->basicAuthUser,
|
||||
$this->basicAuthPassword,
|
||||
]);
|
||||
$basicAuth->register($hooks);
|
||||
}
|
||||
|
||||
$hooks->register('requests.before_request', [$this, 'beforeRequest']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Before request hook
|
||||
*
|
||||
* @param string $url request URL
|
||||
* @param mixed[] $headers headers
|
||||
* @param mixed[] $data data
|
||||
* @param string $type type
|
||||
* @param mixed[] $options options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function beforeRequest(&$url, &$headers, &$data, &$type, &$options): void
|
||||
{
|
||||
$data['_wpnonce'] = $this->nonce;
|
||||
foreach ($_COOKIE as $key => $val) {
|
||||
$options['cookies'][$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\REST;
|
||||
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Exception;
|
||||
use VendorDuplicator\WpOrg\Requests\Requests;
|
||||
use VendorDuplicator\WpOrg\Requests\Response;
|
||||
|
||||
class RESTPoints
|
||||
{
|
||||
const DUPLICATOR_NAMESPACE = 'duplicator/v1/';
|
||||
|
||||
/** @var string */
|
||||
private $nonce = '';
|
||||
/** @var string */
|
||||
private $basicAuthUser = "";
|
||||
/** @var string */
|
||||
private $basicAuthPassword = "";
|
||||
private string $url = '';
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
$paramsManager = PrmMng::getInstance();
|
||||
$overwriteData = $paramsManager->getValue(PrmMng::PARAM_OVERWRITE_SITE_DATA);
|
||||
|
||||
if (is_array($overwriteData)) {
|
||||
if (
|
||||
isset($overwriteData['restUrl']) &&
|
||||
strlen($overwriteData['restUrl']) > 0 &&
|
||||
isset($overwriteData['restNonce']) &&
|
||||
strlen($overwriteData['restNonce']) > 0
|
||||
) {
|
||||
$this->url = SnapIO::untrailingslashit($overwriteData['restUrl']);
|
||||
$this->nonce = $overwriteData['restNonce'];
|
||||
}
|
||||
|
||||
if (strlen($overwriteData['restAuthUser']) > 0) {
|
||||
$this->basicAuthUser = $overwriteData['restAuthUser'];
|
||||
$this->basicAuthPassword = $overwriteData['restAuthPassword'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if REST is available
|
||||
*
|
||||
* @param bool $reset re-check
|
||||
* @param string $errorMessage Error message
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function checkRest($reset = false, &$errorMessage = "")
|
||||
{
|
||||
static $success = null;
|
||||
if (is_null($success) || $reset) {
|
||||
try {
|
||||
$success = true;
|
||||
if (strlen($this->nonce) == 0) {
|
||||
throw new Exception("Nonce is not set.");
|
||||
}
|
||||
|
||||
if (strlen($testUrl = $this->getRestUrl('versions')) === 0) {
|
||||
throw new Exception("Couldn't get REST API backed URL to do tests. REST API URL was empty.");
|
||||
}
|
||||
|
||||
$response = Requests::get($testUrl, [], $this->getRequestAuthOptions());
|
||||
if ($response->success == false) {
|
||||
Log::info(Log::v2str($response));
|
||||
throw new Exception("REST API request on $testUrl failed");
|
||||
}
|
||||
|
||||
if (($result = json_decode($response->body, true)) === null) {
|
||||
throw new Exception("Can't decode json.");
|
||||
}
|
||||
|
||||
if (!isset($result["dup"])) {
|
||||
Log::info('RESPONSE BODY ' . Log::v2str($response->body));
|
||||
throw new Exception("Did not receive the expected result.");
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$success = false;
|
||||
$errorMessage = $ex->getMessage();
|
||||
Log::info("FAILED REST API CHECK. MESSAGE: " . $ex->getMessage());
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return wp and dup version
|
||||
*
|
||||
* @return false|string[] false on failure
|
||||
*/
|
||||
public function getVersions()
|
||||
{
|
||||
$response = Requests::get($this->getRestUrl('versions'), [], $this->getRequestAuthOptions());
|
||||
if (!$response->success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (($result = json_decode($response->body)) === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new subsites
|
||||
*
|
||||
* @param string $data data
|
||||
* @param int $numSubisites subsites number
|
||||
* @param string $errorMessage essromessage
|
||||
*
|
||||
* @return false|array<string, mixed> return subistes info or false on failure
|
||||
*/
|
||||
public function subsiteActions($data, $numSubisites, &$errorMessage = '')
|
||||
{
|
||||
if (Log::isLevel(Log::LV_DETAILED)) {
|
||||
Log::info('SUBSITE ACTION CALL NUM SUBISTES ' . $numSubisites . ' DATA: ' . $data);
|
||||
}
|
||||
|
||||
$options = $this->getRequestAuthOptions();
|
||||
|
||||
// ten seconds foreach subsite
|
||||
$options['timeout'] = 10 * max(1, $numSubisites);
|
||||
|
||||
/** @var Response */
|
||||
$response = Requests::post(
|
||||
$this->getRestUrl('multisite/subsite/actions'),
|
||||
[],
|
||||
['data' => $data],
|
||||
$options
|
||||
);
|
||||
|
||||
if (($result = json_decode($response->body, true)) === null) {
|
||||
Log::info('REST CALL: can\'t decode json ' . $response->body);
|
||||
$errorMessage = 'REST CALL: can\'t decode json response';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$response->success) {
|
||||
Log::info('REST CALL FAIL REPONSE: ' . Log::v2str($response));
|
||||
|
||||
$errorMessage = $result['message'] ?? 'REST call fail, error code: ' . $response->status_code;
|
||||
return false;
|
||||
} elseif (Log::isLevel(Log::LV_DEBUG)) {
|
||||
Log::info('REST CALL REPONSE: ' . Log::v2str($response));
|
||||
}
|
||||
|
||||
if (!$result['success']) {
|
||||
$errorMessage = $result['message'] ?? 'REST call fail, invalid reponse values';
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result['subsitesInfo'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return request auth options
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getRequestAuthOptions(): array
|
||||
{
|
||||
return [
|
||||
'auth' => new RESTAuth($this->nonce, $this->basicAuthUser, $this->basicAuthPassword),
|
||||
'verify' => false,
|
||||
'verifyname' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return RST URL
|
||||
*
|
||||
* @param string $subPath sub path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getRestUrl(string $subPath = ''): string
|
||||
{
|
||||
return $this->url ? $this->url . '/' . self::DUPLICATOR_NAMESPACE . $subPath : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils;
|
||||
|
||||
/**
|
||||
* Autoloader class
|
||||
*/
|
||||
final class Autoloader
|
||||
{
|
||||
const ROOT_NAMESPACE = 'Duplicator\\';
|
||||
const ROOT_INSTALLER_NAMESPACE = self::ROOT_NAMESPACE . 'Installer\\';
|
||||
const ROOT_ADDON_INSTALLER_NAMESPACE = self::ROOT_INSTALLER_NAMESPACE . 'Addons\\';
|
||||
const ROOT_LIBS_NAMESPACE = self::ROOT_NAMESPACE . 'Libs\\';
|
||||
const ROOT_VENDOR = 'VendorDuplicator\\';
|
||||
const VENDOR_PATH = DUPX_INIT . '/vendor-prefixed/';
|
||||
|
||||
/**
|
||||
* register autooader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
spl_autoload_register([self::class, 'load']);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $className class name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function load($className): void
|
||||
{
|
||||
if (strpos($className, self::ROOT_NAMESPACE) === 0) {
|
||||
if (($filepath = self::getAddonFile($className)) === false) {
|
||||
foreach (self::getNamespacesMapping() as $namespace => $mappedPath) {
|
||||
if (strpos($className, (string) $namespace) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filepath = self::getFilenameFromClass($className, $namespace, $mappedPath);
|
||||
if (file_exists($filepath)) {
|
||||
include $filepath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (file_exists($filepath)) {
|
||||
include $filepath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} elseif (strpos($className, self::ROOT_VENDOR) === 0) {
|
||||
foreach (self::getNamespacesVendorMapping() as $namespace => $mappedPath) {
|
||||
if (strpos($className, (string) $namespace) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filepath = self::getFilenameFromClass($className, $namespace, $mappedPath);
|
||||
if (file_exists($filepath)) {
|
||||
include $filepath;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PHP file full class from class name
|
||||
*
|
||||
* @param string $class Name of class
|
||||
* @param string $namespace Base namespace
|
||||
* @param string $mappedPath Base path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getFilenameFromClass($class, $namespace, $mappedPath): string
|
||||
{
|
||||
$subPath = str_replace('\\', '/', substr($class, strlen($namespace))) . '.php';
|
||||
$subPath = ltrim($subPath, '/');
|
||||
return rtrim($mappedPath, '\\/') . '/' . $subPath;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $class class name
|
||||
*
|
||||
* @return boolean|string
|
||||
*/
|
||||
protected static function getAddonFile($class)
|
||||
{
|
||||
$matches = null;
|
||||
if (preg_match('/^\\\\?Duplicator\\\\Installer\\\\Addons\\\\(.+?)\\\\(.+)$/', $class, $matches) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$addonName = $matches[1];
|
||||
$subClass = $matches[2];
|
||||
$basePath = DUPX_INIT . '/addons/' . strtolower($addonName) . '/';
|
||||
|
||||
if (self::endsWith($class, $addonName) === false) {
|
||||
$basePath .= 'src/';
|
||||
}
|
||||
|
||||
return $basePath . str_replace('\\', '/', $subClass) . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return duplicator clases mapping
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function getNamespacesMapping(): array
|
||||
{
|
||||
// the order is important, it is necessary to insert the longest namespaces first
|
||||
return [
|
||||
self::ROOT_ADDON_INSTALLER_NAMESPACE => DUPX_INIT . '/addons/',
|
||||
self::ROOT_INSTALLER_NAMESPACE => DUPX_INIT . '/src/',
|
||||
self::ROOT_LIBS_NAMESPACE => DUPX_INIT . '/libs/',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return namespace mapping
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected static function getNamespacesVendorMapping(): array
|
||||
{
|
||||
return [
|
||||
self::ROOT_VENDOR . 'WpOrg\\Requests' => self::VENDOR_PATH . 'rmccue/requests/src',
|
||||
self::ROOT_VENDOR . 'Amk\\JsonSerialize' => self::VENDOR_PATH . 'andreamk/jsonserialize/src/',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the $haystack string end with the $needle, only for internal use
|
||||
*
|
||||
* @param string $haystack The full string to search in
|
||||
* @param string $needle The string to for
|
||||
*
|
||||
* @return bool Returns true if the $haystack string starts with the $needle
|
||||
*/
|
||||
protected static function endsWith($haystack, string $needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
if ($length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (substr($haystack, -$length) === $needle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils;
|
||||
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Package\ArchiveDescriptor;
|
||||
use Duplicator\Installer\Package\InstallerDescriptors;
|
||||
|
||||
/**
|
||||
* Descriptors Manager class for installer
|
||||
*
|
||||
* singleton class
|
||||
*/
|
||||
final class InstDescMng extends InstallerDescriptors
|
||||
{
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (($nameInfo = ArchiveDescriptor::getArchiveNameParts(Security::getInstance()->getArchivePath())) === false) {
|
||||
throw new \Exception('PACKAGE ERROR: can\'t read archive name parts');
|
||||
}
|
||||
parent::__construct($nameInfo['packageHash'], $nameInfo['date']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils;
|
||||
|
||||
use Duplicator\Installer\Core\Bootstrap;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapOrigFileManager;
|
||||
|
||||
/**
|
||||
* Original installer files manager
|
||||
*
|
||||
* This class saves a file or folder in the original files folder and saves the original location persistant.
|
||||
* By entry we mean a file or a folder but not the files contained within it.
|
||||
* In this way it is possible, for example, to move an entire plugin to restore it later.
|
||||
*
|
||||
* singleton class
|
||||
*/
|
||||
final class InstallerOrigFileMng extends SnapOrigFileManager
|
||||
{
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
* This class should be singleton, but unfortunately it is not possible to change the constructor in private with versions prior to PHP 7.2.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//Init Original File Manager
|
||||
$packageHash = Bootstrap::getPackageHash();
|
||||
$root = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW);
|
||||
parent::__construct($root, DUPX_INIT, $packageHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate orig folder path
|
||||
*
|
||||
* @param string $origFolderParentPath orig files folder parent path
|
||||
* @param string $hash package hash
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateOrigFolderPath($origFolderParentPath, $hash): string
|
||||
{
|
||||
return SnapIO::safePathTrailingslashit($origFolderParentPath) . InstDescMng::getInstance()->getName(InstDescMng::TYPE_ORIG_FILES);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class used to log information
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
||||
*
|
||||
* @package SC\DUPX\Log
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils\Log;
|
||||
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\ViewHelpers\Resources;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Error;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Log
|
||||
* Class used to log information
|
||||
*/
|
||||
class Log
|
||||
{
|
||||
/**
|
||||
* Maximum length of the log on the log.
|
||||
* Prevents uncontrolled increase in log size. This dimension should never be reached
|
||||
*/
|
||||
const MAX_LENGTH_FWRITE = 50000;
|
||||
const LV_DEFAULT = 1;
|
||||
const LV_DETAILED = 2;
|
||||
const LV_DEBUG = 3;
|
||||
const LV_HARD_DEBUG = 4;
|
||||
|
||||
/**
|
||||
* if true throw exception on error else die on error
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $thowExceptionOnError = false;
|
||||
|
||||
/**
|
||||
* log level
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private static $logLevel = self::LV_DEFAULT;
|
||||
|
||||
/**
|
||||
* num of \t before log string.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private static $indentation = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
private static $microtimeStart = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var callable
|
||||
*/
|
||||
private static $postprocessCallback;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private static $afterFatalErrorCallback;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var null|resource
|
||||
*/
|
||||
private static $logHandle;
|
||||
|
||||
/**
|
||||
* set log level from param manager
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setLogLevel(): void
|
||||
{
|
||||
self::$logLevel = PrmMng::getInstance()->getValue(PrmMng::PARAM_LOGGING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to write debug info to the text log file
|
||||
*
|
||||
* @param string $msg Any text data
|
||||
* @param int $logging Log level
|
||||
* @param bool $flush if true flush file log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function info($msg, $logging = self::LV_DEFAULT, $flush = false): void
|
||||
{
|
||||
if ($logging > self::$logLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$preLog = '';
|
||||
if (self::$indentation) {
|
||||
$preLog .= str_repeat("\t", self::$indentation);
|
||||
}
|
||||
if (self::$logLevel >= self::LV_DETAILED) {
|
||||
$preLog .= sprintf('[DELTA:%10.5f] ', microtime(true) - self::$microtimeStart);
|
||||
}
|
||||
if (is_callable(self::$postprocessCallback)) {
|
||||
$msg = call_user_func(self::$postprocessCallback, $msg);
|
||||
}
|
||||
|
||||
@fwrite(self::getFileHandle(), $preLog . $msg . "\n", self::MAX_LENGTH_FWRITE);
|
||||
|
||||
if ($flush) {
|
||||
self::flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool Returns true on success or false on failure.
|
||||
*/
|
||||
public static function clearLog()
|
||||
{
|
||||
self::close();
|
||||
if (file_exists(self::getLogFilePath())) {
|
||||
return unlink(self::getLogFilePath());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function getLogFileName(): string
|
||||
{
|
||||
return 'dup-installer-log__' . Security::getInstance()->getSecondaryPackageHash() . '.txt';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLogFilePath(): string
|
||||
{
|
||||
return DUPX_INIT . '/' . self::getLogFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLogFileUrl(): string
|
||||
{
|
||||
return Resources::getAssetsBaseUrl() . '/' . self::getLogFileName() . '?now=' . $GLOBALS['NOW_TIME'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trace string
|
||||
*
|
||||
* @param array<int, mixed> $callers result of debug_backtrace
|
||||
* @param int $fromLevel level to start
|
||||
* @param int<0,max> $tabs num tabs on each line
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function traceToString($callers, $fromLevel = 0, $tabs = 0): string
|
||||
{
|
||||
$result = '';
|
||||
for ($i = $fromLevel; $i < count($callers); $i++) {
|
||||
$trace = $callers[$i];
|
||||
$result .= str_repeat("\t", $tabs);
|
||||
if (!empty($trace['class'])) {
|
||||
$result .= str_pad('TRACE[' . $i . '] CLASS___: ' . $trace['class'] . $trace['type'] . $trace['function'], 45, ' ');
|
||||
} else {
|
||||
$result .= str_pad('TRACE[' . $i . '] FUNCTION: ' . $trace['function'], 45, ' ');
|
||||
}
|
||||
if (isset($trace['file'])) {
|
||||
$result .= ' FILE: ' . $trace['file'] . '[' . $trace['line'] . ']';
|
||||
} else {
|
||||
$result .= ' NO FILE';
|
||||
}
|
||||
$result .= "\n";
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set post process callback
|
||||
*
|
||||
* @param callable $callback callback function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setPostProcessCallback($callback): void
|
||||
{
|
||||
self::$postprocessCallback = is_callable($callback) ? $callback : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set after fatal error callback
|
||||
*
|
||||
* @param callable $callback callback function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setAfterFatalErrorCallback($callback): void
|
||||
{
|
||||
self::$afterFatalErrorCallback = is_callable($callback) ? $callback : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset time counter
|
||||
*
|
||||
* @param int $logging log level
|
||||
* @param boolean $fileInfo Log file info (file, line)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resetTime($logging = self::LV_DEFAULT, $fileInfo = true): void
|
||||
{
|
||||
self::$microtimeStart = microtime(true);
|
||||
if ($logging > self::$logLevel) {
|
||||
return;
|
||||
}
|
||||
// phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
||||
$callers = debug_backtrace();
|
||||
$file = $callers[0]['file'];
|
||||
$line = $callers[0]['line'];
|
||||
Log::info('LOG-TIME' . ($fileInfo ? '[' . $file . ':' . $line . ']' : '') . ' RESET TIME', $logging);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log time delta from last resetTime call
|
||||
*
|
||||
* @param string $msg message
|
||||
* @param int $logging log level
|
||||
* @param bool $fileInfo if strue write file info (file, line)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function logTime($msg = '', $logging = self::LV_DEFAULT, $fileInfo = true): void
|
||||
{
|
||||
if ($logging > self::$logLevel) {
|
||||
return;
|
||||
}
|
||||
// phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
||||
$callers = debug_backtrace();
|
||||
$file = $callers[0]['file'];
|
||||
$line = $callers[0]['line'];
|
||||
|
||||
if ($fileInfo) {
|
||||
Log::info(
|
||||
sprintf('LOG-TIME[%s:%s][DELTA:%10.5f] ', $file, $line, microtime(true) - self::$microtimeStart) . (empty($msg) ? '' : ' MESSAGE:' . $msg),
|
||||
$logging
|
||||
);
|
||||
} else {
|
||||
Log::info(sprintf('LOG-TIME[DELTA:%10.5f] ', microtime(true) - self::$microtimeStart) . (empty($msg) ? '' : ' MESSAGE:' . $msg), $logging);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment indentation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function incIndent(): void
|
||||
{
|
||||
self::$indentation++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrease indentation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function decIndent(): void
|
||||
{
|
||||
if (self::$indentation > 0) {
|
||||
self::$indentation--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset indentation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resetIndent(): void
|
||||
{
|
||||
self::$indentation = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if log level is >= of loggin level
|
||||
*
|
||||
* @param int $logging log level
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isLevel($logging): bool
|
||||
{
|
||||
return $logging <= self::$logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log passed object
|
||||
*
|
||||
* @param string $msg log message
|
||||
* @param mixed $object object to log
|
||||
* @param int $logging log level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function infoObject($msg, $object, $logging = self::LV_DEFAULT): void
|
||||
{
|
||||
$msg = $msg . "\n" . print_r($object, true);
|
||||
self::info($msg, $logging);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush log file
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function flush(): void
|
||||
{
|
||||
if (is_resource(self::$logHandle)) {
|
||||
fflush(self::$logHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close log file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function close(): bool
|
||||
{
|
||||
if (is_resource(self::$logHandle)) {
|
||||
fclose(self::$logHandle);
|
||||
}
|
||||
self::$logHandle = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get log file stream
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public static function getFileHandle()
|
||||
{
|
||||
if (is_resource(self::$logHandle)) {
|
||||
return self::$logHandle;
|
||||
}
|
||||
|
||||
if (!is_writable(dirname(self::getLogFilePath()))) {
|
||||
throw new \Exception('Can\'t write in dup-installer folder, please check the dup-installer permission folder');
|
||||
}
|
||||
|
||||
if (file_exists(self::getLogFilePath())) {
|
||||
SnapIO::chmod(self::getLogFilePath(), 'u+rw');
|
||||
}
|
||||
|
||||
if ((self::$logHandle = fopen(self::getLogFilePath(), "a+")) === false) {
|
||||
self::$logHandle = null;
|
||||
throw new \Exception('Can\'t open the log file, please check the dup-installer permission folder');
|
||||
}
|
||||
|
||||
SnapIO::chmod(self::getLogFilePath(), 'u+rw');
|
||||
|
||||
return self::$logHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error and die or thore exception if self::$thowExceptionOnError is true
|
||||
*
|
||||
* @param string $errorMessage error message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function error($errorMessage): void
|
||||
{
|
||||
$breaks = [
|
||||
"<br />",
|
||||
"<br>",
|
||||
"<br/>",
|
||||
];
|
||||
$spaces = [" "];
|
||||
$log_msg = str_ireplace($breaks, "\r\n", $errorMessage);
|
||||
$log_msg = str_ireplace($spaces, " ", $log_msg);
|
||||
$log_msg = strip_tags($log_msg);
|
||||
|
||||
self::info("\nINSTALLER ERROR:\n{$log_msg}\n");
|
||||
|
||||
if (is_callable(self::$afterFatalErrorCallback)) {
|
||||
call_user_func(self::$afterFatalErrorCallback);
|
||||
}
|
||||
|
||||
if (self::$thowExceptionOnError) {
|
||||
throw new \Exception($errorMessage);
|
||||
} else {
|
||||
self::close();
|
||||
die("<div class='dupx-ui-error'><hr size='1' /><b style='color:#B80000;'>INSTALL ERROR!</b><br/><pre>{$errorMessage}</pre></div>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get log exception string
|
||||
*
|
||||
* @param Exception $e exception object
|
||||
* @param string $title log message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLogException($e, $title = 'EXCEPTION ERROR: '): string
|
||||
{
|
||||
return $title . ' ' . $e->getMessage() . "[CODE:" . $e->getCode() . "]\n" .
|
||||
"\tFILE:" . $e->getFile() . '[' . $e->getLIne() . "]\n" .
|
||||
"\tTRACE:\n" . $e->getTraceAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log exception
|
||||
*
|
||||
* @param Error|Exception $e exception object
|
||||
* @param int $logging log level
|
||||
* @param string $title log message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function logException($e, $logging = self::LV_DEFAULT, $title = 'EXCEPTION ERROR: '): void
|
||||
{
|
||||
if ($logging <= self::$logLevel) {
|
||||
Log::info("\n" . self::getLogException($e, $title) . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If set true error function thorw exception instead die
|
||||
*
|
||||
* @param boolean $set enable/disable thorw exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setThrowExceptionOnError($set): void
|
||||
{
|
||||
self::$thowExceptionOnError = (bool) $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string from generic value
|
||||
*
|
||||
* @param mixed $var value
|
||||
* @param bool $checkCallable if true check if var is callable and display it
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function v2str($var, $checkCallable = false): string
|
||||
{
|
||||
if ($checkCallable && is_callable($var)) {
|
||||
return '(callable) ' . print_r($var, true);
|
||||
}
|
||||
switch (gettype($var)) {
|
||||
case "boolean":
|
||||
return $var ? 'true' : 'false';
|
||||
case "integer":
|
||||
case "double":
|
||||
return (string) $var;
|
||||
case "string":
|
||||
return '"' . $var . '"';
|
||||
case "array":
|
||||
case "object":
|
||||
return print_r($var, true);
|
||||
case "resource":
|
||||
case "resource (closed)":
|
||||
case "NULL":
|
||||
case "unknown type":
|
||||
default:
|
||||
return gettype($var);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Error Hadler logging
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
||||
*
|
||||
* @package SC\DUPX\Log
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils\Log;
|
||||
|
||||
use Duplicator\Installer\Core\Bootstrap;
|
||||
|
||||
class LogHandler
|
||||
{
|
||||
const MODE_OFF = 0; // don't write in log
|
||||
const MODE_LOG = 1; // write errors in log file
|
||||
const MODE_VAR = 2; // put php errors in $varModeLog static var
|
||||
const SHUTDOWN_TIMEOUT = 'tm';
|
||||
const ERRNO_EXCEPTION = 1073741824; // 31 pos of bit mask
|
||||
|
||||
/**
|
||||
* Set error handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function initErrorHandler(): void
|
||||
{
|
||||
Bootstrap::disableBootShutdownFunction();
|
||||
|
||||
set_error_handler([self::class, 'error']);
|
||||
register_shutdown_function([self::class, 'shutdown']);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private static $shutdownReturns = ['tm' => 'timeout'];
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private static $handlerMode = self::MODE_LOG;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool // print code reference and errno at end of php error line [CODE:10|FILE:test.php|LINE:100]
|
||||
*/
|
||||
private static $codeReference = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool // print prefix in php error line [PHP ERR][WARN] MSG: .....
|
||||
*/
|
||||
private static $errPrefix = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string PHP errors in MODE_VAR
|
||||
*/
|
||||
private static $varModeLog = '';
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*
|
||||
* @param integer $errno Error level
|
||||
* @param string $errstr Error message
|
||||
* @param string $errfile Error file
|
||||
* @param integer $errline Error line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function error($errno, $errstr, $errfile, $errline): bool
|
||||
{
|
||||
switch (self::$handlerMode) {
|
||||
case self::MODE_OFF:
|
||||
if ($errno == E_ERROR) {
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
Log::error($log_message);
|
||||
}
|
||||
break;
|
||||
case self::MODE_VAR:
|
||||
self::$varModeLog .= self::getMessage($errno, $errstr, $errfile, $errline) . "\n";
|
||||
break;
|
||||
case self::MODE_LOG:
|
||||
default:
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
Log::error($log_message);
|
||||
break;
|
||||
case E_NOTICE:
|
||||
case E_WARNING:
|
||||
default:
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
Log::info($log_message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error message from erro data
|
||||
*
|
||||
* @param int $errno error code
|
||||
* @param string $errstr error message
|
||||
* @param string $errfile file
|
||||
* @param int $errline line
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getMessage($errno, $errstr, $errfile, $errline): string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
if (self::$errPrefix) {
|
||||
$result = '[PHP ERR][' . self::errnoToString($errno) . '] MSG:';
|
||||
}
|
||||
|
||||
$result .= $errstr;
|
||||
|
||||
if (self::$codeReference) {
|
||||
$result .= ' [CODE:' . $errno . '|FILE:' . $errfile . '|LINE:' . $errline . ']';
|
||||
if (Log::isLevel(Log::LV_DEBUG)) {
|
||||
// phpcs:ignore PHPCompatibility.FunctionUse.ArgumentFunctionsReportCurrentValue.NeedsInspection
|
||||
$result .= "\n" . Log::traceToString(debug_backtrace(), 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Errno code to string
|
||||
*
|
||||
* @param int $errno error code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function errnoToString($errno): string
|
||||
{
|
||||
switch ($errno) {
|
||||
case E_PARSE:
|
||||
return 'E_PARSE';
|
||||
case E_ERROR:
|
||||
return 'E_ERROR';
|
||||
case E_CORE_ERROR:
|
||||
return 'E_CORE_ERROR';
|
||||
case E_COMPILE_ERROR:
|
||||
return 'E_COMPILE_ERROR';
|
||||
case E_USER_ERROR:
|
||||
return 'E_USER_ERROR';
|
||||
case E_WARNING:
|
||||
return 'E_WARNING';
|
||||
case E_USER_WARNING:
|
||||
return 'E_USER_WARNING';
|
||||
case E_COMPILE_WARNING:
|
||||
return 'E_COMPILE_WARNING';
|
||||
case E_NOTICE:
|
||||
return 'E_NOTICE';
|
||||
case E_USER_NOTICE:
|
||||
return 'E_USER_NOTICE';
|
||||
case self::ERRNO_EXCEPTION:
|
||||
return 'EXCEPTION';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (defined('E_RECOVERABLE_ERROR') && $errno === E_RECOVERABLE_ERROR) {
|
||||
return 'E_RECOVERABLE_ERROR';
|
||||
}
|
||||
if (defined('E_DEPRECATED') && $errno === E_DEPRECATED) {
|
||||
return 'E_DEPRECATED';
|
||||
}
|
||||
if (defined('E_USER_DEPRECATED') && $errno === E_USER_DEPRECATED) {
|
||||
return 'E_USER_DEPRECATED';
|
||||
}
|
||||
return 'E_UNKNOWN CODE: ' . $errno;
|
||||
}
|
||||
|
||||
/**
|
||||
* if setMode is called without params set as default
|
||||
*
|
||||
* @param int $mode log mode
|
||||
* @param bool $errPrefix print prefix in php error line [PHP ERR][WARN] MSG: .....
|
||||
* @param bool $codeReference print code reference and errno at end of php error line [CODE:10|FILE:test.php|LINE:100]
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setMode($mode = self::MODE_LOG, $errPrefix = true, $codeReference = true): void
|
||||
{
|
||||
switch ($mode) {
|
||||
case self::MODE_OFF:
|
||||
case self::MODE_VAR:
|
||||
self::$handlerMode = $mode;
|
||||
break;
|
||||
case self::MODE_LOG:
|
||||
default:
|
||||
self::$handlerMode = self::MODE_LOG;
|
||||
}
|
||||
|
||||
self::$varModeLog = '';
|
||||
self::$errPrefix = $errPrefix;
|
||||
self::$codeReference = $codeReference;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string // return var log string in MODE_VAR
|
||||
*/
|
||||
public static function getVarLog()
|
||||
{
|
||||
return self::$varModeLog;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string // return var log string in MODE_VAR and clean var
|
||||
*/
|
||||
public static function getVarLogClean()
|
||||
{
|
||||
$result = self::$varModeLog;
|
||||
self::$varModeLog = '';
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set shutdown print string
|
||||
*
|
||||
* @param string $status status type
|
||||
* @param string $str string to print if is shouddown status
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setShutdownReturn($status, $str): void
|
||||
{
|
||||
self::$shutdownReturns[$status] = $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function shutdown(): void
|
||||
{
|
||||
if (($error = error_get_last())) {
|
||||
if (preg_match('/^Maximum execution time (?:.+) exceeded$/i', $error['message'])) {
|
||||
echo self::$shutdownReturns[self::SHUTDOWN_TIMEOUT];
|
||||
}
|
||||
self::error($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Utils\ReplaceEngine;
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
/**
|
||||
* search and replace item use in manager to creat the search and replace list.
|
||||
*/
|
||||
class ReplaceItem
|
||||
{
|
||||
const PATH_SEPARATOR_REGEX_NORMAL = '[\/\\\\]';
|
||||
const PATH_SEPARATOR_REGEX_JSON = '(?:\\\\\/|\\\\\\\\)';
|
||||
const PATH_END_REGEX_MATCH_NORMAL = '([\/\\\\"\'\n\r]|$)';
|
||||
const PATH_END_REGEX_MATCH_JSON = '(\\\\\/|\\\\\\\\|["\'\n\r]|$)';
|
||||
const URL_END_REGEX_MATCH_NORMAL = '([\/?"\'\n\r]|$)';
|
||||
const URL_END_REGEX_MATCH_JSON = '(\\\\\/|[?"\'\n\r]|$)';
|
||||
const URL_END_REGEX_MATCH_URLENCODE = '(%2F|%3F|["\'\n\r]|$)';
|
||||
|
||||
const TYPE_STRING = 'str';
|
||||
const TYPE_URL = 'url';
|
||||
const TYPE_URL_NORMALIZE_DOMAIN = 'urlnd';
|
||||
const TYPE_PATH = 'path';
|
||||
|
||||
/** @var int */
|
||||
private static $uniqueIdCount = 0;
|
||||
|
||||
/** @var int */
|
||||
private $id = 0;
|
||||
/** @var int prority lower first */
|
||||
public $prority = 10;
|
||||
/** @var string[] scope list */
|
||||
public $scope = [];
|
||||
/** @var string type of string ENUM self::TYPE_* */
|
||||
public $type = self::TYPE_STRING;
|
||||
/** @var string search string */
|
||||
public $search = '';
|
||||
/** @var string replace string */
|
||||
public $replace = '';
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $search search string
|
||||
* @param string $replace replace string
|
||||
* @param string $type type of string
|
||||
* @param int $prority lower first
|
||||
* @param string|string[] $scope if empty never used
|
||||
*/
|
||||
public function __construct($search, $replace, $type = self::TYPE_STRING, $prority = 10, $scope = [])
|
||||
{
|
||||
if (!is_array($scope)) {
|
||||
$this->scope = empty($scope) ? [] : [(string) $scope];
|
||||
} else {
|
||||
$this->scope = $scope;
|
||||
}
|
||||
$this->prority = (int) $prority;
|
||||
switch ($type) {
|
||||
case self::TYPE_URL:
|
||||
case self::TYPE_URL_NORMALIZE_DOMAIN:
|
||||
$this->search = rtrim($search, '/');
|
||||
$this->replace = rtrim($replace, '/');
|
||||
break;
|
||||
case self::TYPE_PATH:
|
||||
$this->search = SnapIO::safePathUntrailingslashit($search);
|
||||
$this->replace = SnapIO::safePathUntrailingslashit($replace);
|
||||
break;
|
||||
case self::TYPE_STRING:
|
||||
default:
|
||||
$this->search = (string) $search;
|
||||
$this->replace = (string) $replace;
|
||||
break;
|
||||
}
|
||||
$this->type = $type;
|
||||
$this->id = self::$uniqueIdCount;
|
||||
self::$uniqueIdCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array
|
||||
*
|
||||
* @return array{id:int,search:string,replace:string,type:string,prority:int,scope:string[]}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'prority' => $this->prority,
|
||||
'scope' => $this->scope,
|
||||
'type' => $this->type,
|
||||
'search' => $this->search,
|
||||
'replace' => $this->replace,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return item from array
|
||||
*
|
||||
* @param array{search:string,replace:string,type:string,prority:int,scope:string[]} $array Array data
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getItemFromArray($array): self
|
||||
{
|
||||
return new self($array['search'], $array['replace'], $array['type'], $array['prority'], $array['scope']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return search an replace string
|
||||
*
|
||||
* result
|
||||
* [
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ]
|
||||
*
|
||||
* @return array<array{search:string,replace:string}>
|
||||
*/
|
||||
public function getPairsSearchReplace(): array
|
||||
{
|
||||
switch ($this->type) {
|
||||
case self::TYPE_URL:
|
||||
return self::searchReplaceUrl($this->search, $this->replace);
|
||||
case self::TYPE_URL_NORMALIZE_DOMAIN:
|
||||
return self::searchReplaceUrl($this->search, $this->replace, true, true);
|
||||
case self::TYPE_PATH:
|
||||
return self::searchReplacePath($this->search, $this->replace);
|
||||
case self::TYPE_STRING:
|
||||
default:
|
||||
return self::searchReplaceWithEncodings($this->search, $this->replace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search and replace strings with encodings
|
||||
* prevents unnecessary substitution like when search and reaplace are the same.
|
||||
*
|
||||
* result
|
||||
* [
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ]
|
||||
*
|
||||
* @param string $search search string
|
||||
* @param string $replace replace string
|
||||
* @param bool $json add json encode string
|
||||
* @param bool $urlencode add urlencode string
|
||||
*
|
||||
* @return array<array{search:string,replace:string}> pairs search and replace
|
||||
*/
|
||||
public static function searchReplaceWithEncodings($search, $replace, $json = true, $urlencode = true): array
|
||||
{
|
||||
$result = [];
|
||||
if ($search != $replace) {
|
||||
$result[] = [
|
||||
'search' => '/' . preg_quote($search, '/') . '/m',
|
||||
'replace' => addcslashes($replace, '\\$'),
|
||||
];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
// JSON ENCODE
|
||||
if ($json) {
|
||||
$search_json = SnapJson::getJsonWithoutQuotes($search);
|
||||
$replace_json = SnapJson::getJsonWithoutQuotes($replace);
|
||||
|
||||
if ($search != $search_json && $search_json != $replace_json) {
|
||||
$result[] = [
|
||||
'search' => '/' . preg_quote($search_json, '/') . '/m',
|
||||
'replace' => addcslashes($replace_json, '\\$'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// URL ENCODE
|
||||
if ($urlencode) {
|
||||
$search_urlencode = urlencode($search);
|
||||
$replace_urlencode = urlencode($replace);
|
||||
|
||||
if ($search != $search_urlencode && $search_urlencode != $replace_urlencode) {
|
||||
$result[] = [
|
||||
'search' => '/' . preg_quote($search_urlencode, '/') . '/m',
|
||||
'replace' => addcslashes($replace_urlencode, '\\$'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add replace strings to substitute old url to new url
|
||||
* 1) no protocol old url to no protocol new url (es. //www.hold.url => //www.new.url)
|
||||
* 2) wrong protocol new url to right protocol new url (es. http://www.new.url => https://www.new.url)
|
||||
*
|
||||
* result
|
||||
* [
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ]
|
||||
*
|
||||
* @param string $search_url old url
|
||||
* @param string $replace_url new url
|
||||
* @param bool $force_new_protocol if true force http or https protocol (work only if replace url have http or https scheme)
|
||||
* @param bool $normalizeWww if true normalize www
|
||||
*
|
||||
* @return array<array{search:string,replace:string}> pairs search and replace
|
||||
*/
|
||||
public static function searchReplaceUrl($search_url, $replace_url, $force_new_protocol = true, $normalizeWww = false): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if (($parse_search_url = parse_url($search_url)) !== false && isset($parse_search_url['scheme'])) {
|
||||
$search_url_raw = substr($search_url, strlen($parse_search_url['scheme']) + 1);
|
||||
} else {
|
||||
$search_url_raw = $search_url;
|
||||
}
|
||||
$search_url_raw = trim($search_url_raw, '/');
|
||||
|
||||
if (($parse_replace_url = parse_url($replace_url)) !== false && isset($parse_replace_url['scheme'])) {
|
||||
$replace_url_raw = substr($replace_url, strlen($parse_replace_url['scheme']) + 1);
|
||||
} else {
|
||||
$replace_url_raw = $replace_url;
|
||||
}
|
||||
$replace_url_raw = trim($replace_url_raw, '/');
|
||||
|
||||
// (?<!https:|http:)\/\/(?:www\.|)aaaa\.it([?\/'"]|$)
|
||||
if ($normalizeWww && self::domainCanNormalized($search_url)) {
|
||||
$baseSearchUrl = self::isWww($search_url_raw) ? substr($search_url_raw, strlen('www.')) : $search_url_raw;
|
||||
|
||||
$regExSearchUrlNormal = '\/\/(?:www\.)?' . preg_quote($baseSearchUrl, '/');
|
||||
$regExSearchUrlJson = '\\\\\/\\\\\/(?:www\.)?' . preg_quote(SnapJson::getJsonWithoutQuotes($baseSearchUrl), '/');
|
||||
$regExSearchUrlEncode = '%2F%2F(?:www\.)?' . preg_quote(urlencode($baseSearchUrl), '/');
|
||||
//'/https?:\/\/(?:www\.|)aaaa\.it(?<end>[?\/\'"]|$)/m'
|
||||
//$searchRawRegEx = '/(?<!https:|http:)\/\/(?:www\.|)'.preg_quote($baseSearchUrl, '/').'([?\/\'"]|$)/m';
|
||||
} else {
|
||||
$regExSearchUrlNormal = '\/\/' . preg_quote($search_url_raw, '/');
|
||||
$regExSearchUrlJson = '\\\\\/\\\\\/' . preg_quote(SnapJson::getJsonWithoutQuotes($search_url_raw), '/');
|
||||
$regExSearchUrlEncode = '%2F%2F' . preg_quote(urlencode($search_url_raw), '/');
|
||||
}
|
||||
|
||||
// NORMALIZE source protocol
|
||||
if ($force_new_protocol && $parse_replace_url !== false && isset($parse_replace_url['scheme'])) {
|
||||
$result[] = [
|
||||
'search' => '/(?<!https:|http:)' . $regExSearchUrlNormal . self::URL_END_REGEX_MATCH_NORMAL . '/m',
|
||||
'replace' => addcslashes('//' . $replace_url_raw, '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/https?:' . $regExSearchUrlNormal . self::URL_END_REGEX_MATCH_NORMAL . '/m',
|
||||
'replace' => addcslashes($replace_url, '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/(?<!https:|http:)' . $regExSearchUrlJson . self::URL_END_REGEX_MATCH_JSON . '/m',
|
||||
'replace' => addcslashes(SnapJson::getJsonWithoutQuotes('//' . $replace_url_raw), '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/https?:' . $regExSearchUrlJson . self::URL_END_REGEX_MATCH_JSON . '/m',
|
||||
'replace' => addcslashes(SnapJson::getJsonWithoutQuotes($replace_url), '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/(?<!https%3A|http%3A)' . $regExSearchUrlEncode . self::URL_END_REGEX_MATCH_URLENCODE . '/m',
|
||||
'replace' => addcslashes(urlencode('//' . $replace_url_raw), '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/https?%3A' . $regExSearchUrlEncode . self::URL_END_REGEX_MATCH_URLENCODE . '/m',
|
||||
'replace' => addcslashes(urlencode($replace_url), '\\$') . '$1',
|
||||
];
|
||||
} else {
|
||||
$result[] = [
|
||||
'search' => '/' . $regExSearchUrlNormal . self::URL_END_REGEX_MATCH_NORMAL . '/m',
|
||||
'replace' => addcslashes('//' . $replace_url_raw, '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/' . $regExSearchUrlJson . self::URL_END_REGEX_MATCH_JSON . '/m',
|
||||
'replace' => addcslashes(SnapJson::getJsonWithoutQuotes('//' . $replace_url_raw), '\\$') . '$1',
|
||||
];
|
||||
|
||||
$result[] = [
|
||||
'search' => '/' . $regExSearchUrlEncode . self::URL_END_REGEX_MATCH_URLENCODE . '/m',
|
||||
'replace' => addcslashes(urlencode('//' . $replace_url_raw), '\\$') . '$1',
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* result
|
||||
* [
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ]
|
||||
*
|
||||
* @param string $search_path search path
|
||||
* @param string $replace_path replace path
|
||||
*
|
||||
* @return array<array{search:string,replace:string}> pairs search and replace
|
||||
*/
|
||||
public static function searchReplacePath($search_path, $replace_path): array
|
||||
{
|
||||
$result = [];
|
||||
if ($search_path == $replace_path) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$explodeSearch = explode('/', $search_path);
|
||||
|
||||
$normaSearchArray = array_map(fn($val): string => preg_quote(SnapJson::getJsonWithoutQuotes($val), '/'), $explodeSearch);
|
||||
$normalPathSearch = '/' . implode(self::PATH_SEPARATOR_REGEX_NORMAL, $normaSearchArray) . self::PATH_END_REGEX_MATCH_NORMAL . '/m';
|
||||
$result[] = [
|
||||
'search' => $normalPathSearch,
|
||||
'replace' => addcslashes($replace_path, '\\$') . '$1',
|
||||
];
|
||||
|
||||
$jsonSearchArray = array_map(fn($val): string => preg_quote(SnapJson::getJsonWithoutQuotes($val), '/'), $explodeSearch);
|
||||
$jsonPathSearch = '/' . implode(self::PATH_SEPARATOR_REGEX_JSON, $jsonSearchArray) . self::PATH_END_REGEX_MATCH_JSON . '/m';
|
||||
$result[] = [
|
||||
'search' => $jsonPathSearch,
|
||||
'replace' => addcslashes(SnapJson::getJsonWithoutQuotes($replace_path), '\\$') . '$1',
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* get unique item id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url string The URL whichs domain you want to get
|
||||
*
|
||||
* @return string The domain part of the given URL
|
||||
* www.myurl.co.uk => myurl.co.uk
|
||||
* www.google.com => google.com
|
||||
* my.test.myurl.co.uk => myurl.co.uk
|
||||
* www.myurl.localweb => myurl.localweb
|
||||
*/
|
||||
public static function getDomain($url)
|
||||
{
|
||||
$pieces = parse_url($url);
|
||||
$domain = $pieces['host'] ?? '';
|
||||
$regs = null;
|
||||
if (strpos($domain, ".") !== false) {
|
||||
if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
|
||||
return $regs['domain'];
|
||||
} else {
|
||||
$exDomain = explode('.', $domain);
|
||||
return implode('.', array_slice($exDomain, -2, 2));
|
||||
}
|
||||
} else {
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if domain can be normalized
|
||||
*
|
||||
* @param string $url The URL whichs domain you want to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function domainCanNormalized($url)
|
||||
{
|
||||
$pieces = parse_url($url);
|
||||
|
||||
if (!isset($pieces['host'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($pieces['host'], ".") === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dLevels = explode('.', $pieces['host']);
|
||||
if ($dLevels[0] == 'www') {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (count($dLevels)) {
|
||||
case 1:
|
||||
return false;
|
||||
case 2:
|
||||
return true;
|
||||
case 3:
|
||||
if (preg_match('/(?P<domain>[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $pieces['host'], $regs)) {
|
||||
return $regs['domain'] == $pieces['host'];
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if domain is www
|
||||
*
|
||||
* @param string $url The URL whichs domain you want to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isWww($url)
|
||||
{
|
||||
$pieces = parse_url($url);
|
||||
if (!isset($pieces['host'])) {
|
||||
return false;
|
||||
} else {
|
||||
return strpos($pieces['host'], 'www.') === 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils\ReplaceEngine;
|
||||
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
|
||||
/**
|
||||
* Search and replace manager
|
||||
* singleton class
|
||||
*/
|
||||
final class ReplaceMng
|
||||
{
|
||||
const GLOBAL_SCOPE_KEY = '___!GLOBAL!___!SCOPE!___';
|
||||
|
||||
/** @var ?self */
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* full list items not sorted
|
||||
*
|
||||
* @var ReplaceItem[]
|
||||
*/
|
||||
private $items = [];
|
||||
|
||||
/**
|
||||
* items sorted by priority and scope
|
||||
* [
|
||||
* 10 => [
|
||||
* '___!GLOBAL!___!SCOPE!___' => [
|
||||
* SearchReplaceItem
|
||||
* SearchReplaceItem
|
||||
* SearchReplaceItem
|
||||
* ],
|
||||
* 'scope_one' => [
|
||||
* SearchReplaceItem
|
||||
* SearchReplaceItem
|
||||
* ]
|
||||
* ],
|
||||
* 20 => [
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @var array<int,array<string,ReplaceItem[]>>
|
||||
*/
|
||||
private $prorityScopeItems = [];
|
||||
|
||||
/**
|
||||
*
|
||||
* @return ReplaceMng
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array<array{id:int,search:string,replace:string,type:string,prority:int,scope:string[]}>
|
||||
*/
|
||||
public function getArrayData(): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
$data[] = $item->toArray();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data from array
|
||||
*
|
||||
* @param array<array{search:string,replace:string,type:string,prority:int,scope:string[]}> $data Array of data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFromArrayData($data): void
|
||||
{
|
||||
|
||||
foreach ($data as $itemArray) {
|
||||
$new_item = ReplaceItem::getItemFromArray($itemArray);
|
||||
$this->setNewItem($new_item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $search search string
|
||||
* @param string $replace replace string
|
||||
* @param string $type item type ReplaceItem::[TYPE_STRING|TYPE_URL|TYPE_URL_NORMALIZE_DOMAIN|TYPE_PATH]
|
||||
* @param int $prority lower first
|
||||
* @param bool|string|string[] $scope true = global scope | false = never | string signle scope | string[] scope list
|
||||
*
|
||||
* @return boolean|ReplaceItem false if fail
|
||||
*/
|
||||
public function addItem($search, $replace, $type = ReplaceItem::TYPE_STRING, $prority = 10, $scope = true)
|
||||
{
|
||||
$search = (string) $search;
|
||||
$replace = (string) $replace;
|
||||
|
||||
if (strlen($search) == 0 || $search === $replace) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_bool($scope)) {
|
||||
$scope = $scope ? self::GLOBAL_SCOPE_KEY : '';
|
||||
}
|
||||
|
||||
if (is_array($scope)) {
|
||||
$scopeStr = implode(',', $scope);
|
||||
$scopeStr = (strlen($scopeStr) > 50 ? substr($scopeStr, 0, 50) . "..." : $scopeStr);
|
||||
} else {
|
||||
$scopeStr = 'ALL';
|
||||
}
|
||||
|
||||
Log::info(
|
||||
"SEARCH ITEM[T:" . str_pad($type, 5) . "|P:" . str_pad((string) $prority, 2) . "]" .
|
||||
" SEARCH: " . $search .
|
||||
" REPLACE: " . $replace . " [SCOPE: " . $scopeStr . "]"
|
||||
);
|
||||
|
||||
$new_item = new ReplaceItem($search, $replace, $type, $prority, $scope);
|
||||
|
||||
return $this->setNewItem($new_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new item
|
||||
*
|
||||
* @param ReplaceItem $new_item new item
|
||||
*
|
||||
* @return ReplaceItem
|
||||
*/
|
||||
private function setNewItem(ReplaceItem $new_item): ReplaceItem
|
||||
{
|
||||
$this->items[$new_item->getId()] = $new_item;
|
||||
|
||||
// create priority array
|
||||
if (!isset($this->prorityScopeItems[$new_item->prority])) {
|
||||
$this->prorityScopeItems[$new_item->prority] = [];
|
||||
|
||||
// sort by priority
|
||||
ksort($this->prorityScopeItems);
|
||||
}
|
||||
|
||||
// create scope list
|
||||
foreach ($new_item->scope as $scope) {
|
||||
if (!isset($this->prorityScopeItems[$new_item->prority][$scope])) {
|
||||
$this->prorityScopeItems[$new_item->prority][$scope] = [];
|
||||
}
|
||||
$this->prorityScopeItems[$new_item->prority][$scope][] = $new_item;
|
||||
}
|
||||
|
||||
return $new_item;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all search and reaple items by scpoe
|
||||
*
|
||||
* @param null|string $scope if scope is empty get only global scope
|
||||
* @param bool $globalScope if true get global scope
|
||||
*
|
||||
* @return ReplaceItem[]
|
||||
*/
|
||||
private function getSearchReplaceItems($scope = null, $globalScope = true): array
|
||||
{
|
||||
$items_list = [];
|
||||
foreach ($this->prorityScopeItems as $priority => $priority_list) {
|
||||
// get scope list
|
||||
if (!empty($scope) && isset($priority_list[$scope])) {
|
||||
foreach ($priority_list[$scope] as $item) {
|
||||
$items_list[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
// get global scope
|
||||
if ($globalScope && isset($priority_list[self::GLOBAL_SCOPE_KEY])) {
|
||||
foreach ($priority_list[self::GLOBAL_SCOPE_KEY] as $item) {
|
||||
$items_list[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $items_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* get replace list by scope
|
||||
* result
|
||||
* [
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ['search' => ...,'replace' => ...]
|
||||
* ]
|
||||
*
|
||||
* @param null|string $scope if scope is empty get only global scope
|
||||
* @param bool $unique_search If true it eliminates the double searches leaving the one with lower priority.
|
||||
* @param bool $globalScope if true get global scope
|
||||
*
|
||||
* @return array<array{search:string,replace:string}> pairs search and replace
|
||||
*/
|
||||
public function getSearchReplaceList($scope = null, $unique_search = true, $globalScope = true): array
|
||||
{
|
||||
Log::info('-- SEARCH LIST -- SCOPE: ' . Log::v2str($scope), Log::LV_DEBUG);
|
||||
|
||||
$items_list = $this->getSearchReplaceItems($scope, $globalScope);
|
||||
if (Log::isLevel(Log::LV_HARD_DEBUG)) {
|
||||
Log::info('-- SEARCH LIST ITEMS --' . "\n" . print_r($items_list, true), Log::LV_HARD_DEBUG);
|
||||
}
|
||||
|
||||
if ($unique_search) {
|
||||
$items_list = self::uniqueSearchListItem($items_list);
|
||||
if (Log::isLevel(Log::LV_HARD_DEBUG)) {
|
||||
Log::info('-- UNIQUE LIST ITEMS --' . "\n" . print_r($items_list, true), Log::LV_HARD_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('--- BASE STRINGS ---');
|
||||
foreach ($items_list as $index => $item) {
|
||||
Log::info(
|
||||
'SEARCH[' . str_pad($item->type, 5, ' ', STR_PAD_RIGHT) . ']' . str_pad((string) ($index + 1), 3, ' ', STR_PAD_LEFT) . ":" .
|
||||
str_pad(Log::v2str($item->search) . " ", 50, '=', STR_PAD_RIGHT) .
|
||||
"=> " .
|
||||
Log::v2str($item->replace)
|
||||
);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($items_list as $item) {
|
||||
$result = array_merge($result, $item->getPairsSearchReplace());
|
||||
}
|
||||
|
||||
// remove empty search strings
|
||||
$result = array_filter($result, function ($val): bool {
|
||||
if (!empty($val['search'])) {
|
||||
return true;
|
||||
} else {
|
||||
Log::info('Empty search string remove, replace: ' . Log::v2str($val['replace']), Log::LV_DETAILED);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (Log::isLevel(Log::LV_DEBUG)) {
|
||||
Log::info('--- REXEXES ---');
|
||||
foreach ($result as $index => $c_sr) {
|
||||
Log::info(
|
||||
'SEARCH' . str_pad((string) ($index + 1), 3, ' ', STR_PAD_LEFT) . ":" .
|
||||
str_pad(Log::v2str($c_sr['search']) . " ", 50, '=', STR_PAD_RIGHT) .
|
||||
"=> " .
|
||||
Log::v2str($c_sr['replace'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove duplicated search strings.
|
||||
* Leave the object at lower priority
|
||||
*
|
||||
* @param ReplaceItem[] $list list of SearchReplaceItem
|
||||
*
|
||||
* @return ReplaceItem[]
|
||||
*/
|
||||
private static function uniqueSearchListItem(array $list): array
|
||||
{
|
||||
$search_strings = [];
|
||||
$result = [];
|
||||
|
||||
foreach ($list as $item) {
|
||||
if (!in_array($item->search, $search_strings)) {
|
||||
$result[] = $item;
|
||||
$search_strings[] = $item->search;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SecureCsrf
|
||||
{
|
||||
/**
|
||||
* Session var name prefix
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PREFIX = '_DUPX_CSRF';
|
||||
|
||||
/** @var string */
|
||||
private static $packagHash = '';
|
||||
/** @var string */
|
||||
private static $mainFolder = '';
|
||||
|
||||
/**
|
||||
* Stores all CSRF values: Key as CSRF name and Val as CRF value
|
||||
*
|
||||
* @var ?mixed[]
|
||||
*/
|
||||
private static $CSRFVars;
|
||||
|
||||
/**
|
||||
* Init CSRF
|
||||
*
|
||||
* @param string $mainFolderm folter to store CSRF file
|
||||
* @param string $packageHash package has
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init($mainFolderm, $packageHash): void
|
||||
{
|
||||
self::$mainFolder = $mainFolderm;
|
||||
self::$packagHash = $packageHash;
|
||||
self::$CSRFVars = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new CSRF
|
||||
*
|
||||
* @param string $key CSRF Key
|
||||
* @param mixed $val CSRF Val
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setKeyVal($key, $val): void
|
||||
{
|
||||
self::getCSRFVars();
|
||||
self::$CSRFVars[$key] = $val;
|
||||
self::saveCSRFVars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove CSRF if exists
|
||||
*
|
||||
* @param string $key CSRF Key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function removeKeyVal($key): void
|
||||
{
|
||||
self::getCSRFVars();
|
||||
if (isset(self::$CSRFVars[$key])) {
|
||||
unset(self::$CSRFVars[$key]);
|
||||
self::saveCSRFVars();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get CSRF value by passing CSRF key
|
||||
*
|
||||
* @param string $key CSRF key
|
||||
*
|
||||
* @return string|boolean If CSRF value set for give n Key, It returns CRF value otherise returns false
|
||||
*/
|
||||
public static function getVal($key)
|
||||
{
|
||||
self::getCSRFVars();
|
||||
if (isset(self::$CSRFVars[$key])) {
|
||||
return self::$CSRFVars[$key];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SecureCsrf value for form
|
||||
*
|
||||
* @param string $form // Form name as session key
|
||||
*
|
||||
* @return string // token
|
||||
*/
|
||||
public static function generate($form = null)
|
||||
{
|
||||
$keyName = self::getKeyName($form);
|
||||
$existingToken = self::getVal($keyName);
|
||||
$token = false !== $existingToken ? $existingToken : self::token() . self::fingerprint();
|
||||
|
||||
self::setKeyVal($keyName, $token);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check SecureCsrf value of form
|
||||
*
|
||||
* @param string $token - Token
|
||||
* @param string $form - Form name as session key
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check($token, $form = null)
|
||||
{
|
||||
if (empty($form)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$keyName = self::getKeyName($form);
|
||||
self::getCSRFVars();
|
||||
return (isset(self::$CSRFVars[$keyName]) && self::$CSRFVars[$keyName] == $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function token(): string
|
||||
{
|
||||
$microtime = (int) (microtime(true) * 10000);
|
||||
mt_srand($microtime);
|
||||
$charid = strtoupper(md5(uniqid((string) random_int(0, mt_getrandmax()), true)));
|
||||
return substr($charid, 0, 8) . substr($charid, 8, 4) . substr($charid, 12, 4) . substr($charid, 16, 4) . substr($charid, 20, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "digital fingerprint" of user
|
||||
*
|
||||
* @return string - MD5 hashed data
|
||||
*/
|
||||
protected static function fingerprint(): string
|
||||
{
|
||||
return strtoupper(md5(implode('|', [$_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']])));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CSRF Key name
|
||||
*
|
||||
* @param string $form the form name for which CSRF key need to generate
|
||||
*
|
||||
* @return string CSRF key
|
||||
*/
|
||||
private static function getKeyName($form): string
|
||||
{
|
||||
return self::PREFIX . '_' . $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Package hash
|
||||
*
|
||||
* @return string Package hash
|
||||
*/
|
||||
private static function getPackageHash()
|
||||
{
|
||||
if (strlen(self::$packagHash) == 0) {
|
||||
throw new Exception('Not init CSFR CLASS');
|
||||
}
|
||||
return self::$packagHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file path where CSRF tokens are stored in JSON encoded format
|
||||
*
|
||||
* @return string file path where CSRF token stored
|
||||
*/
|
||||
public static function getFilePath(): string
|
||||
{
|
||||
if (strlen(self::$mainFolder) == 0) {
|
||||
throw new Exception('Not init CSFR CLASS');
|
||||
}
|
||||
$dupInstallerfolderPath = self::$mainFolder;
|
||||
$packageHash = self::getPackageHash();
|
||||
//Can't use the descriptor manager because it is not initialized yet
|
||||
$fileName = 'dup_descriptors_' . $packageHash . '/installer-csrf.txt';
|
||||
return $dupInstallerfolderPath . '/' . $fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all CSRF vars in array format
|
||||
*
|
||||
* @return mixed[] Key as CSRF name and value as CSRF value
|
||||
*/
|
||||
private static function getCSRFVars()
|
||||
{
|
||||
if (is_null(self::$CSRFVars)) {
|
||||
$filePath = self::getFilePath();
|
||||
if (file_exists($filePath)) {
|
||||
$contents = file_get_contents($filePath);
|
||||
if (empty($contents)) {
|
||||
self::$CSRFVars = [];
|
||||
} else {
|
||||
$CSRFobjs = json_decode($contents);
|
||||
foreach ($CSRFobjs as $key => $value) {
|
||||
self::$CSRFVars[$key] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self::$CSRFVars = [];
|
||||
}
|
||||
}
|
||||
return self::$CSRFVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores all CSRF vars
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function saveCSRFVars(): void
|
||||
{
|
||||
$contents = json_encode(self::$CSRFVars);
|
||||
$filePath = self::getFilePath();
|
||||
file_put_contents($filePath, $contents);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Customizes final report error messages
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
||||
*
|
||||
* @package SC\DUPX\U
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils\Tests;
|
||||
|
||||
use Duplicator\Installer\Utils\InstDescMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
class MessageCustomizer
|
||||
{
|
||||
const CONTEXT_SHORT_MESSAGE = 'short';
|
||||
const CONTEXT_LONG_MESSAGE = 'long';
|
||||
const CONTEXT_NOTICE_ID = 'notice-id';
|
||||
|
||||
/**
|
||||
* Tries to apply each customization until one of them works
|
||||
*
|
||||
* @param string $shortMessage short message of notice to be customized
|
||||
* @param string $longMessage long message of notice to be customized
|
||||
* @param string $noticeId notice ID
|
||||
*
|
||||
* @return bool true if any of the customizations were applied, false otherwise
|
||||
*/
|
||||
public static function applyAllNoticeCustomizations(&$shortMessage, &$longMessage, &$noticeId): bool
|
||||
{
|
||||
foreach (self::getCustomizationItems() as $item) {
|
||||
if ($item->conditionSatisfied($longMessage)) {
|
||||
$shortMessage = $item->apply($shortMessage, self::CONTEXT_SHORT_MESSAGE);
|
||||
$longMessage = $item->apply($longMessage, self::CONTEXT_LONG_MESSAGE);
|
||||
$noticeId = $item->apply($noticeId, self::CONTEXT_NOTICE_ID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customization to apply at error messages
|
||||
*
|
||||
* @return MessageCustomizerItem[] customizations list
|
||||
*/
|
||||
protected static function getCustomizationItems(): array
|
||||
{
|
||||
$items = [];
|
||||
$items[] = new MessageCustomizerItem(
|
||||
function ($string): bool {
|
||||
if (self::getArchiveConfigData() == false) {
|
||||
return false;
|
||||
}
|
||||
return preg_match("/undefined.*create_function/", $string) &&
|
||||
version_compare(phpversion(), "8") >= 0 &&
|
||||
version_compare(self::getArchiveConfigData()->version_php, "8") < 0;
|
||||
},
|
||||
function ($string, $context) {
|
||||
if (self::getArchiveConfigData() == false) {
|
||||
return $string;
|
||||
}
|
||||
$phpVersionNew = self::getTwoLevelVersion(phpversion());
|
||||
$phpVersionOld = self::getTwoLevelVersion(self::getArchiveConfigData()->version_php);
|
||||
$longMsgPrefix = "There is code in this site that is not compatible with PHP " . $phpVersionNew . ". " .
|
||||
"To make the install work you will either have to\ninstall on PHP " .
|
||||
$phpVersionOld . " or ";
|
||||
|
||||
switch ($context) {
|
||||
case self::CONTEXT_SHORT_MESSAGE:
|
||||
return "Source site or plugins are incompatible with PHP " . $phpVersionNew;
|
||||
case self::CONTEXT_LONG_MESSAGE:
|
||||
if (($plugin = self::getProblematicPluginFromError($string)) !== false) {
|
||||
return $longMsgPrefix . "disable the plugin '{$plugin->name}' (slug: $plugin->slug) using a " .
|
||||
"file manager of your choice.\nSee full error message below: \n\n" . $string;
|
||||
} elseif (($theme = self::getProblematicThemeFromError($string)) !== false) {
|
||||
return $longMsgPrefix . "disable the theme '{$theme->themeName}' (slug: $theme->slug) using a " .
|
||||
"file manager of your choice.\nSee full error message below: \n\n" . $string;
|
||||
} else {
|
||||
return $longMsgPrefix . "manually modify the affected files mentioned in error trace below: \n\n" .
|
||||
$string;
|
||||
}
|
||||
case self::CONTEXT_NOTICE_ID:
|
||||
return $string . '_php8';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plugin that is causing the error message if present
|
||||
*
|
||||
* @param string $longMessage the long error message containing the error trace
|
||||
*
|
||||
* @return false|object object containing plugin info or false on failure
|
||||
*/
|
||||
protected static function getProblematicPluginFromError($longMessage)
|
||||
{
|
||||
if (($archiveConfig = self::getArchiveConfigData()) === false) {
|
||||
return false;
|
||||
}
|
||||
$oldMain = $archiveConfig->wpInfo->targetRoot;
|
||||
$oldMuPlugins = $archiveConfig->wpInfo->configs->realValues->originalPaths->muplugins;
|
||||
$oldPlugins = $archiveConfig->wpInfo->configs->realValues->originalPaths->plugins;
|
||||
$relativeMuPlugins = SnapIO::getRelativePath($oldMuPlugins, $oldMain);
|
||||
$relativePlugins = SnapIO::getRelativePath($oldPlugins, $oldMain);
|
||||
$regex = "/(?:" . preg_quote($relativePlugins, "/") . "\/|" . preg_quote($relativeMuPlugins, "/") . "\/)(.*?)(\/|\.php).*$/m";
|
||||
if (!preg_match($regex, $longMessage, $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//matches the first part of the slug related to the plugin directory
|
||||
$slug = $matches[1];
|
||||
foreach ($archiveConfig->wpInfo->plugins as $plugin) {
|
||||
if (strpos($plugin->slug, $slug) === 0) {
|
||||
return $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the theme that is causing the error message if present
|
||||
*
|
||||
* @param string $longMessage the long error message containing the error trace
|
||||
*
|
||||
* @return false|object object containing theme info or false
|
||||
*/
|
||||
protected static function getProblematicThemeFromError($longMessage)
|
||||
{
|
||||
if (($archiveConfig = self::getArchiveConfigData()) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldMain = $archiveConfig->wpInfo->targetRoot;
|
||||
$oldThemes = $archiveConfig->wpInfo->configs->realValues->originalPaths->themes;
|
||||
$relativeThemes = SnapIO::getRelativePath($oldThemes, $oldMain);
|
||||
|
||||
file_put_contents(
|
||||
DUPX_INIT . "/my_log.txt",
|
||||
"OLD THEMES: {$oldThemes} \n" .
|
||||
"Relative themes: {$relativeThemes} \n" .
|
||||
"regex: " . "/(" . preg_quote($relativeThemes, "/") . "\/)(.*?)(\/|\.php).*$/m"
|
||||
);
|
||||
|
||||
if (!preg_match("/(?:" . preg_quote($relativeThemes, "/") . "\/)(.*?)(?:\/|\.php).*$/m", $longMessage, $matches)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$slug = $matches[1];
|
||||
foreach ($archiveConfig->wpInfo->themes as $theme) {
|
||||
if ($theme->slug == $slug) {
|
||||
return $theme;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get package config data
|
||||
*
|
||||
* @return false|object package config data or false on failure
|
||||
*/
|
||||
protected static function getArchiveConfigData()
|
||||
{
|
||||
static $archiveConfig = null;
|
||||
if (is_null($archiveConfig)) {
|
||||
$archiveConfigPath = DUPX_INIT . "/" . InstDescMng::getInstance()->getName(InstDescMng::TYPE_ARCHIVE_CONFIG);
|
||||
|
||||
if (!file_exists($archiveConfigPath)) {
|
||||
$archiveConfig = false;
|
||||
return $archiveConfig;
|
||||
}
|
||||
|
||||
if (($json = file_get_contents($archiveConfigPath)) === false) {
|
||||
$archiveConfig = false;
|
||||
return $archiveConfig;
|
||||
}
|
||||
|
||||
$archiveConfig = json_decode($json);
|
||||
if (!is_object($archiveConfig)) {
|
||||
$archiveConfig = false;
|
||||
}
|
||||
}
|
||||
return $archiveConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $version a version number
|
||||
*
|
||||
* @return string returns only the first 2 levels of the version numbers
|
||||
*/
|
||||
private static function getTwoLevelVersion($version): string
|
||||
{
|
||||
$arr = explode(".", $version);
|
||||
return $arr[0] . "." . $arr[1];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Utils\Tests;
|
||||
|
||||
class MessageCustomizerItem
|
||||
{
|
||||
/** @var callable|bool */
|
||||
private $checkCallback;
|
||||
/** @var callable */
|
||||
private $applyCallback;
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*
|
||||
* @param callable|bool $checkCallback callback or bool whether to apply customization
|
||||
* @param callable $applyCallback the customizations to be applied
|
||||
*/
|
||||
public function __construct($checkCallback, $applyCallback)
|
||||
{
|
||||
if (!is_bool($checkCallback) && !is_callable($checkCallback)) {
|
||||
throw new \Exception("check callback must be either bool or callable");
|
||||
}
|
||||
$this->checkCallback = $checkCallback;
|
||||
|
||||
if (!is_callable($applyCallback)) {
|
||||
throw new \Exception("customization callback must be callable");
|
||||
}
|
||||
$this->applyCallback = $applyCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $input necessary input to check condition
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function conditionSatisfied($input): bool
|
||||
{
|
||||
return (is_bool($this->checkCallback) && $this->checkCallback) || call_user_func($this->checkCallback, $input);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string string to be customized
|
||||
* @param mixed $context context about what to apply
|
||||
*
|
||||
* @return false|mixed
|
||||
*/
|
||||
public function apply($string, $context)
|
||||
{
|
||||
return call_user_func($this->applyCallback, $string, $context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Utils\Tests;
|
||||
|
||||
interface TestInterface
|
||||
{
|
||||
/**
|
||||
* @return bool true on success
|
||||
*/
|
||||
public static function preTestPrepare();
|
||||
|
||||
/**
|
||||
* @return bool true on success
|
||||
*/
|
||||
public static function afterTestClean();
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Error handler for test scripts
|
||||
* *******************
|
||||
* IMPORTANT
|
||||
* Don\'t use snap lib functions o other duplicator functions
|
||||
* *******************
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
||||
*
|
||||
* @package SC\DUPX\U
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils\Tests\WP;
|
||||
|
||||
use Error;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class TestsErrorHandler
|
||||
{
|
||||
const ERR_TYPE_ERROR = 'error';
|
||||
const ERR_TYPE_WARNING = 'warning';
|
||||
const ERR_TYPE_NOTICE = 'notice';
|
||||
const ERR_TYPE_DEPRECATED = 'deprecated';
|
||||
const ERRNO_EXCEPTION = 1073741824; // 31 pos of bit mask
|
||||
|
||||
/** @var array<int, array<int, array<string, mixed>>> */
|
||||
protected static $errors = [];
|
||||
|
||||
/**
|
||||
* If it is null a json is displayed otherwise the callback function is executed in the shutd
|
||||
*
|
||||
* @var ?callable
|
||||
*/
|
||||
protected static $shutdownCallback;
|
||||
|
||||
/**
|
||||
* Register error handlers
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register(): void
|
||||
{
|
||||
@register_shutdown_function([self::class, 'shutdown']);
|
||||
@set_error_handler([self::class, 'error']);
|
||||
@set_exception_handler([self::class, 'exception']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback shutdown callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setShutdownCallabck($callback): void
|
||||
{
|
||||
self::$shutdownCallback = is_callable($callback) ? $callback : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add error on list
|
||||
*
|
||||
* @param int $errno error number
|
||||
* @param string $errstr error string
|
||||
* @param string $errfile error file
|
||||
* @param int $errline error line
|
||||
* @param array<int, array<string, mixed>> $trace error trace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function addError($errno, $errstr, $errfile, $errline, $trace)
|
||||
{
|
||||
$newError = [
|
||||
'error_cat' => self::getErrorCategoryFromErrno($errno),
|
||||
'errno' => $errno,
|
||||
'errno_str' => self::errnoToString($errno),
|
||||
'errstr' => $errstr,
|
||||
'errfile' => $errfile,
|
||||
'errline' => $errline,
|
||||
'trace' => array_map([self::class, 'normalizeTraceElement'], $trace),
|
||||
];
|
||||
|
||||
self::$errors[] = $newError;
|
||||
|
||||
if (function_exists('error_clear_last')) {
|
||||
error_clear_last(); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.error_clear_lastFound
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $error the error array
|
||||
*
|
||||
* @return string human-readable error message with trace
|
||||
*/
|
||||
public static function errorToString($error): string
|
||||
{
|
||||
$result = $error['errno_str'] . ' ' . $error['errstr'] . "\n";
|
||||
$result .= "\tFILE: " . $error['errfile'] . '[' . $error['errline'] . ']' . "\n";
|
||||
$result .= "\t--- TRACE ---\n";
|
||||
foreach ($error['trace'] as $trace) {
|
||||
$result .= "\t";
|
||||
if (!empty($trace['class'])) {
|
||||
$result .= str_pad('CLASS___: ' . $trace['class'] . $trace['type'] . $trace['function'], 40, ' ');
|
||||
} else {
|
||||
$result .= str_pad('FUNCTION: ' . $trace['function'], 40, ' ');
|
||||
}
|
||||
$result .= 'FILE: ' . $trace['file'] . '[' . $trace['line'] . ']' . "\n";
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*
|
||||
* @param integer $errno Error level
|
||||
* @param string $errstr Error message
|
||||
* @param string $errfile Error file
|
||||
* @param integer $errline Error line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function error($errno, $errstr, $errfile, $errline): bool
|
||||
{
|
||||
$trace = debug_backtrace();
|
||||
array_shift($trace);
|
||||
self::addError($errno, $errstr, $errfile, $errline, $trace);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception handler
|
||||
*
|
||||
* @param Exception|Error|Throwable $e Throwable in php 7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function exception($e): void
|
||||
{
|
||||
self::addError(self::ERRNO_EXCEPTION, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTrace());
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function shutdown(): void
|
||||
{
|
||||
self::obCleanAll();
|
||||
|
||||
if (($error = error_get_last())) {
|
||||
self::error($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
}
|
||||
ob_end_clean();
|
||||
|
||||
if (is_callable(self::$shutdownCallback)) {
|
||||
call_user_func(self::$shutdownCallback, self::$errors);
|
||||
} else {
|
||||
echo json_encode(self::$errors);
|
||||
}
|
||||
|
||||
// prevent other shutdown functions
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all buffers and return content
|
||||
*
|
||||
* @param bool $getContent If true it returns buffer content, otherwise it is discarded
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function obCleanAll($getContent = true): string
|
||||
{
|
||||
$result = '';
|
||||
for ($i = 0; $i < ob_get_level(); $i++) {
|
||||
if ($getContent) {
|
||||
$result .= ob_get_contents();
|
||||
}
|
||||
ob_clean();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, scalar> $elem normalize error element
|
||||
*
|
||||
* @return array{file: string, line: int, function: string, class: string, type: string}
|
||||
*/
|
||||
public static function normalizeTraceElement($elem): array
|
||||
{
|
||||
if (!is_array($elem)) {
|
||||
$elem = [];
|
||||
}
|
||||
|
||||
unset($elem['args']);
|
||||
unset($elem['object']);
|
||||
|
||||
return array_merge([
|
||||
'file' => '',
|
||||
'line' => -1,
|
||||
'function' => '',
|
||||
'class' => '',
|
||||
'type' => '',
|
||||
], $elem);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $errno error number
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getErrorCategoryFromErrno($errno): string
|
||||
{
|
||||
switch ($errno) {
|
||||
case E_PARSE:
|
||||
case E_ERROR:
|
||||
case E_CORE_ERROR:
|
||||
case E_COMPILE_ERROR:
|
||||
case E_USER_ERROR:
|
||||
case self::ERRNO_EXCEPTION:
|
||||
return self::ERR_TYPE_ERROR;
|
||||
case E_WARNING:
|
||||
case E_USER_WARNING:
|
||||
case E_COMPILE_WARNING:
|
||||
return self::ERR_TYPE_WARNING;
|
||||
case E_NOTICE:
|
||||
case E_USER_NOTICE:
|
||||
return self::ERR_TYPE_NOTICE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (defined('E_RECOVERABLE_ERROR') && $errno === E_RECOVERABLE_ERROR) {
|
||||
return self::ERR_TYPE_WARNING;
|
||||
}
|
||||
if (defined('E_DEPRECATED') && $errno === E_DEPRECATED) {
|
||||
return self::ERR_TYPE_DEPRECATED;
|
||||
}
|
||||
if (defined('E_USER_DEPRECATED') && $errno === E_USER_DEPRECATED) {
|
||||
return self::ERR_TYPE_DEPRECATED;
|
||||
}
|
||||
return self::ERR_TYPE_WARNING;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $errno error number
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function errnoToString($errno): string
|
||||
{
|
||||
switch ($errno) {
|
||||
case E_PARSE:
|
||||
return 'E_PARSE';
|
||||
case E_ERROR:
|
||||
return 'E_ERROR';
|
||||
case E_CORE_ERROR:
|
||||
return 'E_CORE_ERROR';
|
||||
case E_COMPILE_ERROR:
|
||||
return 'E_COMPILE_ERROR';
|
||||
case E_USER_ERROR:
|
||||
return 'E_USER_ERROR';
|
||||
case E_WARNING:
|
||||
return 'E_WARNING';
|
||||
case E_USER_WARNING:
|
||||
return 'E_USER_WARNING';
|
||||
case E_COMPILE_WARNING:
|
||||
return 'E_COMPILE_WARNING';
|
||||
case E_NOTICE:
|
||||
return 'E_NOTICE';
|
||||
case E_USER_NOTICE:
|
||||
return 'E_USER_NOTICE';
|
||||
case self::ERRNO_EXCEPTION:
|
||||
return 'EXCEPTION';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (defined('E_RECOVERABLE_ERROR') && $errno === E_RECOVERABLE_ERROR) {
|
||||
return 'E_RECOVERABLE_ERROR';
|
||||
}
|
||||
if (defined('E_DEPRECATED') && $errno === E_DEPRECATED) {
|
||||
return 'E_DEPRECATED';
|
||||
}
|
||||
if (defined('E_USER_DEPRECATED') && $errno === E_USER_DEPRECATED) {
|
||||
return 'E_USER_DEPRECATED';
|
||||
}
|
||||
return 'E_UNKNOWN CODE: ' . $errno;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* plugin custom actions
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
||||
*
|
||||
* @package SC\DUPX\U
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\Utils\Tests\WP;
|
||||
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Utils\Log\Log;
|
||||
use Duplicator\Installer\Core\Params\PrmMng;
|
||||
use Duplicator\Installer\Utils\Tests\TestInterface;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
|
||||
class TestsExecuter implements TestInterface
|
||||
{
|
||||
const SCRIPT_NAME_HTTP_PARAM = 'dupli_test_script_name';
|
||||
|
||||
/**
|
||||
* @return bool true on success
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function preTestPrepare(): bool
|
||||
{
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
$scriptFilePath = self::getScriptTestPath();
|
||||
Log::info('PREPARE FILE BEFORE TEST: ' . $scriptFilePath, Log::LV_DETAILED);
|
||||
if (file_put_contents($scriptFilePath, self::getExecFileContent()) === false) {
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t create final text script file',
|
||||
'longMsg' => 'Can\'t create file ' . $scriptFilePath,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'sections' => ['general'],
|
||||
]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true on success
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function afterTestClean(): bool
|
||||
{
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
$scriptFilePath = self::getScriptTestPath();
|
||||
Log::info('DELETE FILE AFTER TEST: ' . $scriptFilePath, Log::LV_DETAILED);
|
||||
if (file_exists($scriptFilePath)) {
|
||||
if (unlink($scriptFilePath) == false) {
|
||||
$nManager->addFinalReportNotice([
|
||||
'shortMsg' => 'Can\'t deleta final text script file',
|
||||
'longMsg' => 'Can\'t delete file ' . $scriptFilePath . '. Remove it manually',
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_DEFAULT,
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'sections' => ['general'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string url of WP front-end
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getFrontendUrl(): string
|
||||
{
|
||||
$indexPath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW) . '/index.php';
|
||||
$data = [self::SCRIPT_NAME_HTTP_PARAM => $indexPath];
|
||||
|
||||
return self::getScriptTestUrl() . '?' . http_build_query($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string url of WP back-end
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getBackendUrl(): string
|
||||
{
|
||||
$indexPath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW) . '/wp-login.php';
|
||||
$data = [self::SCRIPT_NAME_HTTP_PARAM => $indexPath];
|
||||
|
||||
return self::getScriptTestUrl() . '?' . http_build_query($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string test script name
|
||||
*/
|
||||
protected static function getScriptTestName(): string
|
||||
{
|
||||
return 'wp_test_script_' . Security::getInstance()->getSecondaryPackageHash() . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string test script path
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getScriptTestPath(): string
|
||||
{
|
||||
// use wp-content path and not root path
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_CONTENT_NEW) . '/' . self::getScriptTestName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string test script url
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getScriptTestUrl(): string
|
||||
{
|
||||
// use wp-content path and not root path
|
||||
return PrmMng::getInstance()->getValue(PrmMng::PARAM_URL_CONTENT_NEW) . '/' . self::getScriptTestName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string contents to be added to the test script file
|
||||
*/
|
||||
public static function getExecFileContent(): string
|
||||
{
|
||||
$result = file_get_contents(__DIR__ . '/tests_template.php');
|
||||
$result = preg_replace('/^.*\[REMOVE LINE BY SCRIPT].*\n/m', '', $result); // remove first line with die
|
||||
return str_replace(
|
||||
[
|
||||
'$_$_NOTICES_FILE_PATH_$_$',
|
||||
'$_$_DUPX_INIT_$_$',
|
||||
],
|
||||
[
|
||||
$GLOBALS["NOTICES_FILE_PATH"],
|
||||
DUPX_INIT,
|
||||
],
|
||||
$result
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Duplicator\Installer\Utils\Tests\WP;
|
||||
|
||||
use Duplicator\Installer\Utils\Autoloader;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Installer\Utils\Tests\MessageCustomizer;
|
||||
use DUPX_NOTICE_ITEM;
|
||||
use DUPX_NOTICE_MANAGER;
|
||||
use Exception;
|
||||
|
||||
// phpcs:disable
|
||||
die(); // [REMOVE LINE BY SCRIPT] don't remove/change this *********************************
|
||||
|
||||
if (!defined('DUPXABSPATH')) { // @phpstan-ignore-line
|
||||
define('DUPXABSPATH', __DIR__);
|
||||
}
|
||||
|
||||
if (!defined('DUPX_INIT')) {
|
||||
define('DUPX_INIT', '$_$_DUPX_INIT_$_$');
|
||||
}
|
||||
// phpcs:enable
|
||||
|
||||
require_once(DUPX_INIT . '/src/Utils/Autoloader.php');
|
||||
Autoloader::register();
|
||||
|
||||
require_once(DUPX_INIT . '/classes/utilities/class.u.notices.manager.php');
|
||||
$GLOBALS["NOTICES_FILE_PATH"] = '$_$_NOTICES_FILE_PATH_$_$';
|
||||
|
||||
$GLOBALS["TEST_SCRIPT"] = SnapUtil::sanitizeDefaultInput(INPUT_GET, 'dupli_test_script_name');
|
||||
ob_start();
|
||||
TestsErrorHandler::register();
|
||||
TestsErrorHandler::setShutdownCallabck(function ($errors): void {
|
||||
|
||||
$nManager = DUPX_NOTICE_MANAGER::getInstance();
|
||||
$scriptName = basename($GLOBALS["TEST_SCRIPT"]);
|
||||
|
||||
if (!file_exists($GLOBALS["TEST_SCRIPT"])) {
|
||||
$longMessage = "- The file " . $GLOBALS["TEST_SCRIPT"] . " doesn't exist.\n";
|
||||
$data = [
|
||||
'shortMsg' => "Some files required for the final tests were not found.",
|
||||
'level' => DUPX_NOTICE_ITEM::HARD_WARNING,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
|
||||
'longMsg' => $longMessage,
|
||||
'sections' => 'general',
|
||||
];
|
||||
|
||||
$nManager->addBothNextAndFinalReportNotice($data, DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, 'test_file_not_found');
|
||||
|
||||
if ($nManager->saveNotices()) {
|
||||
echo json_encode(true);
|
||||
} else {
|
||||
echo json_encode(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$scriptNameId = str_replace(['.', '-', '#'], '_', $scriptName);
|
||||
$firstFatal = true;
|
||||
$firstNotice = true;
|
||||
|
||||
switch ($scriptName) {
|
||||
case 'index.php':
|
||||
$shortMessageFatal = 'Fatal error on WordPress front-end tests!';
|
||||
$shortMessageNotice = 'Warnings or notices on WordPress front-end tests!';
|
||||
$fatalErrorLevel = DUPX_NOTICE_ITEM::CRITICAL;
|
||||
break;
|
||||
case 'wp-login.php':
|
||||
$shortMessageFatal = 'Fatal error on WordPress login tests!';
|
||||
$shortMessageNotice = 'Warnings or notices on WordPress backend tests!';
|
||||
$fatalErrorLevel = DUPX_NOTICE_ITEM::FATAL;
|
||||
break;
|
||||
default:
|
||||
$shortMessageFatal = 'Fatal error on php script ' . $scriptName;
|
||||
$shortMessageNotice = 'Warnings or notices on php script ' . $scriptName;
|
||||
$fatalErrorLevel = DUPX_NOTICE_ITEM::CRITICAL;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($errors as $error) {
|
||||
$addBeforeNotice = false;
|
||||
switch ($error['error_cat']) {
|
||||
case TestsErrorHandler::ERR_TYPE_ERROR:
|
||||
$noticeId = 'wptest_fatal_error_' . $scriptNameId;
|
||||
$errorLevel = $fatalErrorLevel;
|
||||
$shortMessage = $shortMessageFatal;
|
||||
if ($firstFatal) {
|
||||
$addBeforeNotice = true;
|
||||
$firstFatal = false;
|
||||
}
|
||||
|
||||
break;
|
||||
case TestsErrorHandler::ERR_TYPE_NOTICE:
|
||||
case TestsErrorHandler::ERR_TYPE_DEPRECATED:
|
||||
case TestsErrorHandler::ERR_TYPE_WARNING:
|
||||
default:
|
||||
$noticeId = 'wptest_notice_' . $scriptNameId;
|
||||
$errorLevel = DUPX_NOTICE_ITEM::NOTICE;
|
||||
$shortMessage = $shortMessageNotice;
|
||||
if ($firstNotice) {
|
||||
$addBeforeNotice = true;
|
||||
$firstNotice = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$longMessage = $addBeforeNotice ? 'SCRIPT FILE TEST: ' . $GLOBALS["TEST_SCRIPT"] . "\n\n" : '';
|
||||
$longMessage .= TestsErrorHandler::errorToString($error) . "\n-----\n\n";
|
||||
$longMessage .= "For solutions to these issues see the online FAQs \nhttps://duplicator.com/knowledge-base \n\n";
|
||||
|
||||
MessageCustomizer::applyAllNoticeCustomizations($shortMessage, $longMessage, $noticeId);
|
||||
$data = [
|
||||
'shortMsg' => $shortMessage,
|
||||
'level' => $errorLevel,
|
||||
'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_PRE,
|
||||
'longMsg' => $longMessage,
|
||||
'sections' => 'general',
|
||||
];
|
||||
if ($errorLevel == DUPX_NOTICE_ITEM::FATAL) {
|
||||
$nManager->addBothNextAndFinalReportNotice($data, DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, $noticeId);
|
||||
} else {
|
||||
$nManager->addFinalReportNotice($data, DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, $noticeId);
|
||||
}
|
||||
}
|
||||
|
||||
if ($nManager->saveNotices()) {
|
||||
echo json_encode(true);
|
||||
} else {
|
||||
echo json_encode(false);
|
||||
}
|
||||
});
|
||||
|
||||
$_SERVER['REQUEST_URI'] = '/';
|
||||
if (file_exists($GLOBALS["TEST_SCRIPT"])) {
|
||||
require_once($GLOBALS["TEST_SCRIPT"]);
|
||||
} else {
|
||||
throw new Exception('test script file ' . $GLOBALS["TEST_SCRIPT"] . ' doesn\'t exist');
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator\Installer
|
||||
*/
|
||||
|
||||
namespace Duplicator\Installer\ViewHelpers;
|
||||
|
||||
use Duplicator\Installer\Core\Security;
|
||||
use Duplicator\Installer\Core\InstState;
|
||||
|
||||
class Resources
|
||||
{
|
||||
/**
|
||||
* Return assets base URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getAssetsBaseUrl()
|
||||
{
|
||||
if (InstState::isBridgeInstall()) {
|
||||
return Security::getInstance()->getOriginalInstallerUrl();
|
||||
} else {
|
||||
return DUPX_INIT_URL;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user