Commit c1d4294e authored by Helmut Hummel's avatar Helmut Hummel Committed by Andreas Fernandez
Browse files

[TASK] Use absolute paths when installing extensions

In order to prepare for further refactorings and streamlining
extension manager code, resolving extensions' path is now
always handled with a full path.

Releases: master
Resolves: #93054
Change-Id: I2d853c3f5d4a6ae4861dcd174d380a855764d995
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/64873

Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
parent 4a584abd
......@@ -171,7 +171,7 @@ class ActionController extends AbstractController
protected function reloadExtensionDataAction($extension)
{
$extension = $this->installUtility->enrichExtensionWithDetails($extension, false);
$registryKey = $extension['siteRelPath'] . 'ext_tables_static+adt.sql';
$registryKey = PathUtility::stripPathSitePrefix($extension['packagePath']) . 'ext_tables_static+adt.sql';
$registry = GeneralUtility::makeInstance(Registry::class);
$registry->remove('extensionDataImport', $registryKey);
......
......@@ -425,7 +425,7 @@ class DependencyUtility implements SingletonInterface
$this->setAvailableExtensions();
$extensionData = $this->emConfUtility->includeEmConf(
$dependency->getIdentifier(),
$this->availableExtensions[$dependency->getIdentifier()]
$this->availableExtensions[$dependency->getIdentifier()]['packagePath'] ?? ''
);
return $dependency->isVersionCompatible($extensionData['version']);
}
......
......@@ -15,7 +15,6 @@
namespace TYPO3\CMS\Extensionmanager\Utility;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Extensionmanager\Domain\Model\Extension;
......@@ -29,19 +28,15 @@ class EmConfUtility implements SingletonInterface
* Returns the $EM_CONF array from an extensions ext_emconf.php file
*
* @param string $extensionKey the extension name
* @param array $extension Extension information array
* @param string $absolutePath path to the ext_emconf.php
* @return array|bool EMconf array values or false if no ext_emconf.php found.
*/
public function includeEmConf(string $extensionKey, array $extension)
public function includeEmConf(string $extensionKey, string $absolutePath)
{
$_EXTKEY = $extensionKey;
if (!empty($extension['packagePath'])) {
$path = $extension['packagePath'] . 'ext_emconf.php';
} else {
$path = Environment::getPublicPath() . '/' . $extension['siteRelPath'] . 'ext_emconf.php';
}
$path = rtrim($absolutePath, '/') . '/ext_emconf.php';
$EM_CONF = null;
if (file_exists($path)) {
if (!empty($absolutePath) && file_exists($path)) {
include $path;
if (is_array($EM_CONF[$_EXTKEY])) {
return $EM_CONF[$_EXTKEY];
......
......@@ -271,9 +271,7 @@ class FileHandlingUtility implements SingletonInterface, LoggerAwareInterface
if (file_exists($rootPath . 'ext_emconf.php')) {
$emConfFileData = $this->emConfUtility->includeEmConf(
$extensionData['extKey'],
[
'packagePath' => $rootPath
]
$rootPath
);
$emConfFileData = is_array($emConfFileData) ? $emConfFileData : [];
}
......
......@@ -35,6 +35,7 @@ use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionDatabaseContentHasBeenImportedEvent;
use TYPO3\CMS\Extensionmanager\Event\AfterExtensionFilesHaveBeenImportedEvent;
......@@ -187,10 +188,10 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
public function processExtensionSetup(string $extensionKey): void
{
$extension = $this->enrichExtensionWithDetails($extensionKey, false);
$this->importInitialFiles($extension['siteRelPath'] ?? '', $extensionKey);
$this->importStaticSqlFile($extensionKey, $extension['siteRelPath']);
$import = $this->importT3DFile($extensionKey, $extension['siteRelPath']);
$this->importSiteConfiguration($extension['siteRelPath'], $import);
$this->importInitialFiles($extension['packagePath'], $extensionKey);
$this->importStaticSqlFile($extensionKey, $extension['packagePath']);
$import = $this->importT3DFile($extensionKey, $extension['packagePath']);
$this->importSiteConfiguration($extensionKey, $extension['packagePath'], $import);
}
/**
......@@ -439,11 +440,12 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
* Execution state is saved in the this->registry, so it only happens once
*
* @param string $extensionKey
* @param string $extensionSiteRelPath
* @param string $packagePath
* @return Import|null
*/
protected function importT3DFile($extensionKey, $extensionSiteRelPath): ?Import
protected function importT3DFile($extensionKey, $packagePath): ?Import
{
$extensionSiteRelPath = PathUtility::stripPathSitePrefix($packagePath);
$registryKeysToCheck = [
$extensionSiteRelPath . 'Initialisation/data.t3d',
$extensionSiteRelPath . 'Initialisation/dataImported',
......@@ -456,11 +458,11 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
}
$importFileToUse = null;
$possibleImportFiles = [
$extensionSiteRelPath . 'Initialisation/data.t3d',
$extensionSiteRelPath . 'Initialisation/data.xml'
$packagePath . 'Initialisation/data.t3d',
$packagePath . 'Initialisation/data.xml'
];
foreach ($possibleImportFiles as $possibleImportFile) {
if (!file_exists(Environment::getPublicPath() . '/' . $possibleImportFile)) {
if (!file_exists($possibleImportFile)) {
continue;
}
$importFileToUse = $possibleImportFile;
......@@ -468,7 +470,7 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
if ($importFileToUse !== null) {
$importExportUtility = GeneralUtility::makeInstance(ImportExportUtility::class);
try {
$importResult = $importExportUtility->importT3DFile(Environment::getPublicPath() . '/' . $importFileToUse, 0);
$importResult = $importExportUtility->importT3DFile($importFileToUse, 0);
$this->registry->set('extensionDataImport', $extensionSiteRelPath . 'Initialisation/dataImported', 1);
$this->eventDispatcher->dispatch(new AfterExtensionDatabaseContentHasBeenImportedEvent($extensionKey, $importFileToUse, $importResult, $this));
return $importExportUtility->getImport();
......@@ -484,13 +486,13 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
* Execution state is saved in the this->registry, so it only happens once
*
* @param string $extensionKey
* @param string $extensionSiteRelPath
* @param string $packagePath
*/
protected function importStaticSqlFile(string $extensionKey, $extensionSiteRelPath)
protected function importStaticSqlFile(string $extensionKey, $packagePath)
{
$extTablesStaticSqlRelFile = $extensionSiteRelPath . 'ext_tables_static+adt.sql';
$extTablesStaticSqlFile = $packagePath . 'ext_tables_static+adt.sql';
$extTablesStaticSqlRelFile = PathUtility::stripPathSitePrefix($extTablesStaticSqlFile);
if (!$this->registry->get('extensionDataImport', $extTablesStaticSqlRelFile)) {
$extTablesStaticSqlFile = Environment::getPublicPath() . '/' . $extTablesStaticSqlRelFile;
$shortFileHash = '';
if (file_exists($extTablesStaticSqlFile)) {
$extTablesStaticSqlContent = (string)file_get_contents($extTablesStaticSqlFile);
......@@ -498,7 +500,7 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
$this->importStaticSql($extTablesStaticSqlContent);
}
$this->registry->set('extensionDataImport', $extTablesStaticSqlRelFile, $shortFileHash);
$this->eventDispatcher->dispatch(new AfterExtensionStaticDatabaseContentHasBeenImportedEvent($extensionKey, $extTablesStaticSqlRelFile, $this));
$this->eventDispatcher->dispatch(new AfterExtensionStaticDatabaseContentHasBeenImportedEvent($extensionKey, $extTablesStaticSqlFile, $this));
}
}
......@@ -506,23 +508,22 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
* Imports files from Initialisation/Files to fileadmin
* via lowlevel copy directory method
*
* @param string $extensionSiteRelPath relative path to extension dir
* @param string $packagePath absolute path to extension dir
* @param string $extensionKey
*/
protected function importInitialFiles($extensionSiteRelPath, $extensionKey)
protected function importInitialFiles($packagePath, $extensionKey)
{
$importRelFolder = $extensionSiteRelPath . 'Initialisation/Files';
$importFolder = $packagePath . 'Initialisation/Files';
$importRelFolder = PathUtility::stripPathSitePrefix($importFolder);
if (!$this->registry->get('extensionDataImport', $importRelFolder)) {
$importFolder = Environment::getPublicPath() . '/' . $importRelFolder;
if (file_exists($importFolder)) {
$destinationRelPath = $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey;
$destinationAbsolutePath = Environment::getPublicPath() . '/' . $destinationRelPath;
$destinationAbsolutePath = GeneralUtility::getFileAbsFileName($GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . $extensionKey);
if (!file_exists($destinationAbsolutePath) &&
GeneralUtility::isAllowedAbsPath($destinationAbsolutePath)
) {
GeneralUtility::mkdir($destinationAbsolutePath);
}
GeneralUtility::copyDirectory($importRelFolder, $destinationRelPath);
GeneralUtility::copyDirectory($importFolder, $destinationAbsolutePath);
$this->registry->set('extensionDataImport', $importRelFolder, 1);
$this->eventDispatcher->dispatch(new AfterExtensionFilesHaveBeenImportedEvent($extensionKey, $destinationAbsolutePath, $this));
}
......@@ -530,13 +531,13 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
}
/**
* @param string $extensionSiteRelPath
* @param string $extensionKey
* @param string $packagePath
* @param Import|null $import
*/
protected function importSiteConfiguration(string $extensionSiteRelPath, Import $import = null): void
protected function importSiteConfiguration(string $extensionKey, string $packagePath, Import $import = null): void
{
$importRelFolder = $extensionSiteRelPath . 'Initialisation/Site';
$importAbsFolder = Environment::getPublicPath() . '/' . $importRelFolder;
$importAbsFolder = $packagePath . 'Initialisation/Site';
$destinationFolder = Environment::getConfigPath() . '/sites';
if (!is_dir($importAbsFolder)) {
......@@ -556,7 +557,7 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface
$this->logger->warning(
sprintf(
'Skipped importing site configuration from %s due to existing site identifier %s',
$extensionSiteRelPath,
$extensionKey,
$siteIdentifier
)
);
......
......@@ -16,7 +16,6 @@
namespace TYPO3\CMS\Extensionmanager\Utility;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Package\Event\PackagesMayHaveChangedEvent;
use TYPO3\CMS\Core\Package\PackageInterface;
use TYPO3\CMS\Core\Package\PackageManager;
......@@ -121,7 +120,6 @@ class ListUtility implements SingletonInterface
$installationType = $this->getInstallTypeForPackage($package);
if ($filter === '' || $filter === $installationType) {
$this->availableExtensions[$package->getPackageKey()] = [
'siteRelPath' => str_replace(Environment::getPublicPath() . '/', '', $package->getPackagePath()),
'packagePath' => $package->getPackagePath(),
'type' => $installationType,
'key' => $package->getPackageKey(),
......@@ -195,7 +193,7 @@ class ListUtility implements SingletonInterface
public function enrichExtensionsWithEmConfInformation(array $extensions)
{
foreach ($extensions as $extensionKey => $properties) {
$emconf = $this->emConfUtility->includeEmConf($extensionKey, $properties);
$emconf = $this->emConfUtility->includeEmConf($extensionKey, $properties['packagePath'] ?? '');
if (is_array($emconf)) {
$extensions[$extensionKey] = array_merge($emconf, $properties);
} else {
......
......@@ -15,11 +15,11 @@
namespace TYPO3\CMS\Extensionmanager\ViewHelpers;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Registry;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Fluid\ViewHelpers\Link\ActionViewHelper;
......@@ -51,19 +51,19 @@ class ReloadSqlDataViewHelper extends ActionViewHelper
$extension = $this->arguments['extension'];
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$staticSqlDataFile = $extension['siteRelPath'] . 'ext_tables_static+adt.sql';
if (!file_exists(Environment::getPublicPath() . '/' . $staticSqlDataFile)) {
$staticSqlDataFile = $extension['packagePath'] . 'ext_tables_static+adt.sql';
if (!file_exists($staticSqlDataFile)) {
return '<span class="btn btn-default disabled">' . $iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
}
$registry = GeneralUtility::makeInstance(Registry::class);
$oldMd5Hash = $registry->get(static::$registryNamespace, $staticSqlDataFile);
$oldMd5Hash = $registry->get(static::$registryNamespace, PathUtility::stripPathSitePrefix($staticSqlDataFile));
$md5HashIsEqual = true;
// We used to only store "1" in the database when data was imported
// No need to compare file content here and just show the reload icon
if (!empty($oldMd5Hash) && $oldMd5Hash !== 1) {
$currentMd5Hash = md5_file(Environment::getPublicPath() . '/' . $staticSqlDataFile);
$currentMd5Hash = md5_file($staticSqlDataFile);
$md5HashIsEqual = $oldMd5Hash === $currentMd5Hash;
}
......
......@@ -47,10 +47,8 @@ class FileHandlingUtilityTest extends UnitTestCase
{
$extKey = strtolower(StringUtility::getUniqueId('testing'));
$absExtPath = Environment::getVarPath() . '/tests/ext-' . $extKey . '/';
$relPath = 'typo3temp/var/tests/ext-' . $extKey . '/';
$this->fakedExtensions[$extKey] = [
'siteRelPath' => $relPath,
'siteAbsPath' => $absExtPath
'packagePath' => $absExtPath
];
if ($extkeyOnly === true) {
return $extKey;
......@@ -297,7 +295,7 @@ class FileHandlingUtilityTest extends UnitTestCase
'content' => 'FEEL FREE TO ADD SOME DOCUMENTATION HERE'
]
];
$rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath']);
$rootPath = ($extDirPath = $this->fakedExtensions[$this->createFakeExtension()]['packagePath']);
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['makeAndClearExtensionDir']);
$fileHandlerMock->_call('writeExtensionFiles', $files, $rootPath);
self::assertTrue(file_exists($rootPath . 'ChangeLog'));
......@@ -359,7 +357,7 @@ class FileHandlingUtilityTest extends UnitTestCase
*/
public function createDirectoriesForExtensionFilesCreatesDirectories()
{
$rootPath = $this->fakedExtensions[$this->createFakeExtension()]['siteAbsPath'];
$rootPath = $this->fakedExtensions[$this->createFakeExtension()]['packagePath'];
$directories = [
'doc/',
'mod/doc/'
......@@ -386,7 +384,7 @@ class FileHandlingUtilityTest extends UnitTestCase
'category' => 'Frontend',
]
];
$rootPath = $this->fakedExtensions[$extKey]['siteAbsPath'];
$rootPath = $this->fakedExtensions[$extKey]['packagePath'];
$emConfUtilityMock = $this->getAccessibleMock(EmConfUtility::class, ['constructEmConf']);
$emConfUtilityMock->expects(self::once())->method('constructEmConf')->with($extensionData)->willReturn(var_export($extensionData['EM_CONF'], true));
$fileHandlerMock = $this->getAccessibleMock(FileHandlingUtility::class, ['makeAndClearExtensionDir']);
......
......@@ -27,6 +27,7 @@ use TYPO3\CMS\Core\Configuration\SiteConfiguration;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Registry;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Extensionmanager\Utility\DependencyUtility;
use TYPO3\CMS\Extensionmanager\Utility\InstallUtility;
......@@ -69,7 +70,7 @@ class InstallUtilityTest extends UnitTestCase
$this->extensionKey = 'dummy';
$this->extensionData = [
'key' => $this->extensionKey,
'siteRelPath' => '',
'packagePath' => '',
];
$this->installMock = $this->getAccessibleMock(
InstallUtility::class,
......@@ -139,11 +140,9 @@ class InstallUtilityTest extends UnitTestCase
{
$extKey = strtolower(StringUtility::getUniqueId('testing'));
$absExtPath = Environment::getVarPath() . '/tests/' . $extKey;
$relativeVarPath = ltrim(str_replace(Environment::getProjectPath(), '', Environment::getVarPath()), '/');
$relPath = $relativeVarPath . '/tests/' . $extKey . '/';
GeneralUtility::mkdir($absExtPath);
$this->fakedExtensions[$extKey] = [
'siteRelPath' => $relPath
'packagePath' => $absExtPath
];
return $extKey;
}
......@@ -260,7 +259,8 @@ class InstallUtilityTest extends UnitTestCase
public function importT3DFileDoesNotImportFileIfAlreadyImported($fileName, $registryNameReturnsFalse, $registryNameReturnsTrue)
{
$extKey = $this->createFakeExtension();
$absPath = Environment::getProjectPath() . '/' . $this->fakedExtensions[$extKey]['siteRelPath'];
$absPath = $this->fakedExtensions[$extKey]['packagePath'];
$relPath = PathUtility::stripPathSitePrefix($absPath);
GeneralUtility::mkdir($absPath . 'Initialisation');
file_put_contents($absPath . 'Initialisation/' . $fileName, 'DUMMY');
$registryMock = $this->getMockBuilder(Registry::class)
......@@ -271,8 +271,8 @@ class InstallUtilityTest extends UnitTestCase
->method('get')
->willReturnMap(
[
['extensionDataImport', $this->fakedExtensions[$extKey]['siteRelPath'] . 'Initialisation/' . $registryNameReturnsFalse, null, false],
['extensionDataImport', $this->fakedExtensions[$extKey]['siteRelPath'] . 'Initialisation/' . $registryNameReturnsTrue, null, true],
['extensionDataImport', $relPath . 'Initialisation/' . $registryNameReturnsFalse, null, false],
['extensionDataImport', $relPath . 'Initialisation/' . $registryNameReturnsTrue, null, true],
]
);
$installMock = $this->getAccessibleMock(
......@@ -286,7 +286,7 @@ class InstallUtilityTest extends UnitTestCase
$installMock->_set('dependencyUtility', $dependencyUtility);
$installMock->_set('registry', $registryMock);
$installMock->expects(self::never())->method('getImportExportUtility');
$installMock->_call('importT3DFile', $extKey, $this->fakedExtensions[$extKey]['siteRelPath']);
$installMock->_call('importT3DFile', $extKey, $this->fakedExtensions[$extKey]['packagePath']);
}
/**
......@@ -296,7 +296,7 @@ class InstallUtilityTest extends UnitTestCase
{
// prepare an extension with a shipped site config
$extKey = $this->createFakeExtension();
$absPath = Environment::getProjectPath() . '/' . $this->fakedExtensions[$extKey]['siteRelPath'];
$absPath = $this->fakedExtensions[$extKey]['packagePath'];
$config = Yaml::dump(['dummy' => true]);
$siteIdentifier = 'site_identifier';
GeneralUtility::mkdir_deep($absPath . 'Initialisation/Site/' . $siteIdentifier);
......@@ -312,7 +312,7 @@ class InstallUtilityTest extends UnitTestCase
$availableExtensions = [
$extKey => [
'siteRelPath' => $this->fakedExtensions[$extKey]['siteRelPath'],
'packagePath' => $this->fakedExtensions[$extKey]['packagePath'],
],
];
$listUtility->enrichExtensionsWithEmConfInformation($availableExtensions)->willReturn($availableExtensions);
......@@ -355,7 +355,7 @@ class InstallUtilityTest extends UnitTestCase
{
// prepare an extension with a shipped site config
$extKey = $this->createFakeExtension();
$absPath = Environment::getProjectPath() . '/' . $this->fakedExtensions[$extKey]['siteRelPath'];
$absPath = $this->fakedExtensions[$extKey]['packagePath'];
$siteIdentifier = 'site_identifier';
GeneralUtility::mkdir_deep($absPath . 'Initialisation/Site/' . $siteIdentifier);
file_put_contents($absPath . 'Initialisation/Site/' . $siteIdentifier . '/config.yaml', Yaml::dump(['dummy' => true]));
......@@ -380,7 +380,7 @@ class InstallUtilityTest extends UnitTestCase
$availableExtensions = [
$extKey => [
'siteRelPath' => $this->fakedExtensions[$extKey]['siteRelPath'],
'packagePath' => $this->fakedExtensions[$extKey]['packagePath'],
],
];
$listUtility->enrichExtensionsWithEmConfInformation($availableExtensions)->willReturn($availableExtensions);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment