diff --git a/CHANGELOG.md b/CHANGELOG.md index c2623655ad..a984282ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ Contao change log Version 4.0.0-RC1 (2014-05-XX) ------------------------------ +### Fixed +Create absolute symlinks if relative symlinks are not supported (see #208). + ### Removed The "postFlushData" hook has been removed (see #196). diff --git a/src/Analyzer/HtaccessAnalyzer.php b/src/Analyzer/HtaccessAnalyzer.php index f1f5c9a4e0..32903ed66b 100644 --- a/src/Analyzer/HtaccessAnalyzer.php +++ b/src/Analyzer/HtaccessAnalyzer.php @@ -38,6 +38,18 @@ public function __construct(\SplFileInfo $file) $this->file = $file; } + /** + * Creates a new object instance. + * + * @param \SplFileInfo $file The file object + * + * @return static The object instance + */ + public static function create(\SplFileInfo $file) + { + return new static($file); + } + /** * Checks whether the .htaccess file grants access via HTTP. * diff --git a/src/Command/SymlinksCommand.php b/src/Command/SymlinksCommand.php index c70c67a106..fd751d5bd9 100644 --- a/src/Command/SymlinksCommand.php +++ b/src/Command/SymlinksCommand.php @@ -15,6 +15,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; @@ -81,9 +82,9 @@ public function generateSymlinks($rootDir, OutputInterface $output) $this->symlinkThemes($rootDir, $output); // Symlink the assets and themes directory - $this->symlink('../assets', 'web/assets', $rootDir, $output); - $this->symlink('../../system/themes', 'web/system/themes', $rootDir, $output); - $this->symlink('../app/logs', 'system/logs', $rootDir, $output); + $this->symlink('assets', 'web/assets', $rootDir, $output); + $this->symlink('system/themes', 'web/system/themes', $rootDir, $output); + $this->symlink('app/logs', 'system/logs', $rootDir, $output); } /** @@ -95,7 +96,7 @@ public function generateSymlinks($rootDir, OutputInterface $output) */ private function symlinkFiles($uploadPath, $rootDir, OutputInterface $output) { - $this->relativeSymlink( + $this->createSymlinksFromFinder( $this->findIn("$rootDir/$uploadPath")->files()->name('.public'), $uploadPath, $rootDir, @@ -112,12 +113,10 @@ private function symlinkFiles($uploadPath, $rootDir, OutputInterface $output) private function symlinkModules($rootDir, OutputInterface $output) { $filter = function (SplFileInfo $file) { - $htaccess = new HtaccessAnalyzer($file); - - return $htaccess->grantsAccess(); + return HtaccessAnalyzer::create($file)->grantsAccess(); }; - $this->relativeSymlink( + $this->createSymlinksFromFinder( $this->findIn("$rootDir/system/modules")->files()->filter($filter)->name('.htaccess'), 'system/modules', $rootDir, @@ -143,30 +142,33 @@ private function symlinkThemes($rootDir, OutputInterface $output) continue; } - $this->symlink("../../$path", 'system/themes/' . basename($path), $rootDir, $output); + $this->symlink($path, 'system/themes/' . basename($path), $rootDir, $output); } } /** - * Generates a symlink relative to the given path. + * Generates symlinks from a Finder object. * * @param Finder $finder The finder object * @param string $prepend The path to prepend * @param string $rootDir The root directory * @param OutputInterface $output The output object */ - private function relativeSymlink(Finder $finder, $prepend, $rootDir, OutputInterface $output) + private function createSymlinksFromFinder(Finder $finder, $prepend, $rootDir, OutputInterface $output) { /** @var SplFileInfo $file */ foreach ($finder as $file) { $path = rtrim($prepend . '/' . $file->getRelativePath(), '/'); - $this->symlink(str_repeat('../', substr_count($path, '/') + 1) . $path, "web/$path", $rootDir, $output); + $this->symlink($path, "web/$path", $rootDir, $output); } } /** * Generates a symlink. * + * The method will try to generate relative symlinks and fall back to generating + * absolute symlinks if relative symlinks are not supported (see #208). + * * @param string $source The symlink name * @param string $target The symlink target * @param string $rootDir The root directory @@ -177,19 +179,14 @@ private function symlink($source, $target, $rootDir, OutputInterface $output) $this->validateSymlink($source, $target, $rootDir); $fs = new Filesystem(); - $fs->symlink($source, "$rootDir/$target"); - - $stat = lstat("$rootDir/$target"); - // Try to fix the UID - if (function_exists('lchown') && $stat['uid'] !== getmyuid()) { - lchown("$rootDir/$target", getmyuid()); + try { + $fs->symlink(rtrim($fs->makePathRelative($source, dirname($target)), '/'), "$rootDir/$target"); + } catch (IOException $e) { + $fs->symlink("$rootDir/$source", "$rootDir/$target"); } - // Try to fix the GID - if (function_exists('lchgrp') && $stat['gid'] !== getmygid()) { - lchgrp("$rootDir/$target", getmygid()); - } + $this->fixSymlinkPermissions($target, $rootDir); $output->writeln("Added $target as symlink to $source."); } @@ -225,6 +222,27 @@ private function validateSymlink($source, $target, $rootDir) } } + /** + * Fixes the symlink permissions. + * + * @param string $target The symlink target + * @param string $rootDir The root directory + */ + private function fixSymlinkPermissions($target, $rootDir) + { + $stat = lstat("$rootDir/$target"); + + // Try to fix the UID + if (function_exists('lchown') && $stat['uid'] !== getmyuid()) { + lchown("$rootDir/$target", getmyuid()); + } + + // Try to fix the GID + if (function_exists('lchgrp') && $stat['gid'] !== getmygid()) { + lchgrp("$rootDir/$target", getmygid()); + } + } + /** * Returns a finder instance to find files in the given path. *