Fix #475 - Add deletion warning on upgrade

This commit is contained in:
y.yerli 2025-01-20 12:52:57 +03:00
parent 31a20e84d0
commit 78eb20ccca
5 changed files with 490 additions and 0 deletions

View file

@ -66,6 +66,16 @@ class FolderComparator implements FolderComparatorInterface
*/
protected $toExpandKeys;
/**
* @var string[]
*/
protected $toReAdd = [];
/**
* @var bool[]
*/
protected $toReAddKeys;
/**
* FolderCompare constructor.
*/
@ -305,6 +315,38 @@ class FolderComparator implements FolderComparatorInterface
return $this->toKeepIgnoreKeys;
}
/**
* Set paths to re-add
* @param array $toReAdd
*/
public function setToReAdd(array $toReAdd): void
{
$this->toReAdd = $toReAdd;
$this->toReAddKeys = [];
foreach ($this->toReAdd as $item) {
$this->toReAddKeys[$item] = true;
}
}
/**
* Get paths to re-add
* @return array
*/
public function getToReAdd(): array
{
return $this->toReAdd;
}
/**
* Get paths to re-add keys
* @return array
*/
public function getToReAddKeys(): array
{
return $this->toReAddKeys;
}
/**
* Add folder directories
* @param string $originPath

View file

@ -107,6 +107,11 @@ abstract class BaseStepExecutorCommand extends BaseCommand
$alerts = $this->getHandler()->getAlerts($position, $context);
foreach ($alerts as $alert) {
$messages = $alert->getMessages();
if (empty($messages)) {
continue;
}
$title = $alert->getTile() ?? 'Alert';
$output->writeln($this->colorMessage('comment', $title));

View file

@ -0,0 +1,195 @@
<?php
/**
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
* Copyright (C) 2024 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Supercharged by SuiteCRM".
*/
namespace App\Install\Service\Upgrade\Steps;
use App\Engine\Model\Feedback;
use App\Engine\Model\ProcessStepTrait;
use App\Engine\Service\FolderSync\FolderComparatorInterface;
use App\Engine\Service\ProcessSteps\ProcessStepAlert;
use App\Install\Service\Upgrade\UpgradePackageHandler;
use App\Install\Service\Upgrade\UpgradeStepInterface;
/**
* Class FilesToDeleteCheck
* @package App\Install\Service\LegacyMigration\Steps;
*/
class FilesToDeleteCheck implements UpgradeStepInterface
{
use ProcessStepTrait;
public const HANDLER_KEY = 'custom-files-deletion-warning';
public const POSITION = 550;
public const STAGE = 'upgrade-install';
/**
* @var UpgradePackageHandler
*/
private $handler;
/**
* @var string
*/
protected $projectDir;
/**
* @var string
*/
protected $upgradePackageDir;
/**
* @var FolderComparatorInterface
*/
protected $compare;
/**
* ExternalFilesCheck constructor.
* @param UpgradePackageHandler $handler
* @param string $projectDir
* @param string $upgradePackageDir
* @param FolderComparatorInterface $compare
* @param array $upgradeConfig
*/
public function __construct(
UpgradePackageHandler $handler,
string $projectDir,
string $upgradePackageDir,
FolderComparatorInterface $compare,
array $upgradeConfig
) {
$this->handler = $handler;
$this->projectDir = $projectDir;
$this->upgradePackageDir = $upgradePackageDir;
$this->compare = $compare;
$this->compare->setToKeep($upgradeConfig['toKeep']);
$this->compare->setToKeepIgnore($upgradeConfig['toKeepIgnore']);
$this->compare->setPathsToExpand($upgradeConfig['toExpand']);
}
/**
* @inheritDoc
*/
public function getKey(): string
{
return self::HANDLER_KEY;
}
/**
* @inheritDoc
*/
public function getOrder(): int
{
return self::POSITION;
}
/**
* @inheritDoc
*/
public function getStage(): string
{
return self::STAGE;
}
/**
* Get Alert
* @param array $context
* @return ProcessStepAlert
*/
public function getAlert(array &$context): ProcessStepAlert
{
$version = $context['version'];
$extractPath = $this->getPackageExtractPath($version);
$manifest = $this->runCompare($extractPath);
$deletionList = [];
if (!empty($manifest)) {
foreach ($manifest as $path => $entry) {
if ($entry->action === 'delete') {
$deletionList[] = $path;
}
}
}
$alert = new ProcessStepAlert();
if (!empty($deletionList)) {
$alert->setTile("Warning: Potential Custom Files");
$messages = [
"*********************************************************************",
"** \e[31m !!! WARNING !!! \e[0m**",
"*********************************************************************",
"** The following files/directories will be permanently DELETED: **",
];
foreach ($deletionList as $item) {
$messages[] = "** - $item";
}
$messages[] = "** **";
$messages[] = "** Move any important files to a safe location BEFORE proceeding. **";
$messages[] = "*********************************************************************";
$alert->setMessages($messages);
} else {
$alert->setMessages([]);
}
return $alert;
}
/**
* Get package extract output path
* @param string $version
* @return string
*/
public function getPackageExtractPath(string $version): string
{
return $this->upgradePackageDir . '/' . $version . '-extracted';
}
/**
* @param string $extractPath
* @return array
*/
public function runCompare(string $extractPath): array
{
return $this->compare->run($extractPath, $this->projectDir);
}
/**
* @inheritDoc
*/
public function execute(array &$context): Feedback
{
$targetVersion = $context['version'] ?? '';
return $this->handler->checkFilesToDelete($targetVersion);
}
}

View file

@ -95,6 +95,7 @@ class UpgradePackageHandler extends PackageHandler
$this->compare->setToKeep($upgradeConfig['toKeep']);
$this->compare->setToKeepIgnore($upgradeConfig['toKeepIgnore']);
$this->compare->setPathsToExpand($upgradeConfig['toExpand']);
$this->compare->setToReAdd($upgradeConfig['toReAdd']);
$this->upgradeLogger = $upgradeLogger;
}
@ -142,6 +143,21 @@ class UpgradePackageHandler extends PackageHandler
return $feedback;
}
/**
* Run compare and check files to delete
* @param string $version
* @return Feedback
*/
public function checkFilesToDelete(string $version): Feedback
{
$feedback = new Feedback();
$feedback->setSuccess(true);
$messages = ['Files to delete checked'];
$feedback->setMessages($messages);
return $feedback;
}
/**
* Run compare and check permissions
* @param string $version
@ -215,13 +231,117 @@ class UpgradePackageHandler extends PackageHandler
'sync manifest generated: ' . json_encode($manifest, JSON_THROW_ON_ERROR)
]);
$tmpFolder = $this->projectDir . '/tmp/toReAdd';
$toReAddItems = $this->compare->getToReAdd();
$this->copyFilesToReAddToTmpFolder($tmpFolder, $toReAddItems);
$this->sync->run($extractPath, $this->projectDir, $manifest);
$this->restoreFilesFromTmpFolder($tmpFolder, $toReAddItems);
$feedback->setSuccess(true)->setMessages(['Successfully installed package']);
return $feedback;
}
/**
* Copy files to be re-added to a temporary folder
*
* @param string $tmpFolder
* @param array $toReAddItems
* @return void
*/
protected function copyFilesToReAddToTmpFolder(string $tmpFolder, array $toReAddItems): void
{
$filesystem = new Filesystem();
if (!$filesystem->exists($tmpFolder)) {
$filesystem->mkdir($tmpFolder);
}
foreach ($toReAddItems as $item) {
$sourcePath = $this->projectDir . '/' . $item;
$destinationPath = $tmpFolder . '/' . $item;
if (is_dir($sourcePath)) {
$dirContents = scandir($sourcePath);
foreach ($dirContents as $file) {
if ($file === '.' || $file === '..') {
continue;
}
if ($file !== 'en_us.lang.php') {
$fileSourcePath = $sourcePath . '/' . $file;
$fileDestinationPath = $destinationPath . '/' . $file;
if (file_exists($fileSourcePath)) {
if (!is_dir($destinationPath)) {
mkdir($destinationPath, 0755, true);
}
$filesystem->copy($fileSourcePath, $fileDestinationPath);
}
}
}
continue;
}
if (file_exists($sourcePath)) {
$filesystem->mirror($sourcePath, $destinationPath);
}
}
}
/**
* Restore files from the temporary folder to their original location
*
* @param string $tmpFolder
* @param array $toReAddItems
* @return void
*/
protected function restoreFilesFromTmpFolder(string $tmpFolder, array $toReAddItems): void
{
$filesystem = new Filesystem();
foreach ($toReAddItems as $item) {
$tmpPath = $tmpFolder . '/' . $item;
$finalDestinationPath = $this->projectDir . '/' . $item;
if (file_exists($tmpPath)) {
if (is_dir($tmpPath)) {
$dirContents = scandir($tmpPath);
foreach ($dirContents as $file) {
if ($file === '.' || $file === '..') {
continue;
}
$fileTmpPath = $tmpPath . '/' . $file;
$fileFinalPath = $finalDestinationPath . '/' . $file;
if ($file === 'en_us.lang.php' && file_exists($fileFinalPath)) {
continue;
}
if (is_dir($fileTmpPath)) {
$filesystem->mirror($fileTmpPath, $fileFinalPath);
} else {
$filesystem->copy($fileTmpPath, $fileFinalPath);
}
}
} else {
if (basename($item) !== 'en_us.lang.php' || !file_exists($finalDestinationPath)) {
$filesystem->copy($tmpPath, $finalDestinationPath);
}
}
}
}
$filesystem->remove($tmpFolder);
}
/**
* Run compare and install package
* @param string $version