Comments (13)
+1
from aimeos-typo3.
We had absolute links before. The main reason why we use relative links is due to installations with staging/live environments that are copied. Is there another way to fix that instead of switching back to absolute paths?
from aimeos-typo3.
There are actually mulltiple issues here.
- The relative path might differ depending on composer.json settings, like web-dir and vendor-dir and therefore could also fail with non Windows OS
- Not sure why (didn't dig too deep into it) but composer sometimes seems to return relative and sometimes absolute directories, e.g. the package path for typo3-ter/aimeos is absolute but for aimeos/ai-typo3 is relative to current working directory for me.
To fix those issues I would do it like this (see code part at the end of comment) - copied some stuff regarding relative directory from stackoverflow and other stuff from TYPO3 to determine OS, not sure if it would also need an exec for a Linux / Mac based setup or not but I don't think so. Maybe it would require a chdir() call before though?
The reason for this to fail in Windows is I think the check regarding which type of link to create.
Btw.: Why do you actually need a link of the extension directory? Wouldn't it make more sense to just copy / link the resources but not to care where those resources are or store the path (relative) in some config file.
EDIT: Added a check to ensure mklink was executed successfully.
<?php
/**
* @license GPLv3, http://www.gnu.org/copyleft/gpl.html
* @copyright Aimeos (aimeos.org), 2016
* @package TYPO3
*/
namespace Aimeos\Aimeos\Custom;
/**
* Performs setup and installation steps during composer installs
*
* @package TYPO3
*/
class Composer
{
/**
* Installs the shop files.
*
* @param \Composer\Script\Event $event Event instance
* @throws \RuntimeException If an error occured
*/
public static function install( \Composer\Script\Event $event )
{
$repository = $event->getComposer()->getRepositoryManager();
$t3package = $repository->findPackage( 'typo3-ter/aimeos', '*' );
if( $t3package !== null )
{
$installer = $event->getComposer()->getInstallationManager();
$t3path = $installer->getInstallPath( $t3package );
if (!self::isAbsolutePath($t3path)) {
$t3path = getcwd() . '/' . $t3path;
}
if( ( $package = $repository->findPackage( 'aimeos/ai-client-html', '*' ) ) !== null )
{
$event->getIO()->write( 'Installing Aimeos public files from HTML client' );
$path = $installer->getInstallPath( $package );
self::copyRecursive( $path . '/client/html/themes', $t3path . '/Resources/Public/Themes' );
}
if( ( $package = $repository->findPackage( 'aimeos/ai-typo3', '*' ) ) !== null )
{
$event->getIO()->write( 'Creating symlink to Aimeos extension directory' );
if( file_exists( $t3path . '/Resources/Private/Extensions' ) === false )
{
$path = dirname($installer->getInstallPath( $package ));
if (!self::isAbsolutePath($path)) {
$path = getcwd() . '/' . $path;
}
$relativePath = rtrim(self::getRelativePath($t3path . '/Resources/Private/', $path), '/');
if (self::isWindows()) {
$output = [];
$returnStatus = 0;
exec('mklink /D ' . escapeshellarg(str_replace('/', '\\', $t3path . '/Resources/Private/Extensions')) . ' ' . escapeshellarg(str_replace('/', '\\', $relativePath)), $output, $returnStatus);
if ($returnStatus !== 0) {
throw new \RuntimeException('Could not create symlink');
}
} else {
symlink($relativePath, $t3path . '/Resources/Private/Extensions');
}
}
}
}
}
protected static function isAbsolutePath($path)
{
// On Windows also a path starting with a drive letter is absolute: X:/
if (static::isWindows() && (substr($path, 1, 2) === ':/' || substr($path, 1, 2) === ':\\')) {
return true;
}
// Path starting with a / is always absolute, on every system
return $path[0] === '/';
}
protected static function isWindows()
{
if (!stristr(PHP_OS, 'darwin') && !stristr(PHP_OS, 'cygwin') && stristr(PHP_OS, 'win')) {
return true;
}
return false;
}
protected static function getRelativePath($from, $to)
{
// some compatibility fixes for Windows paths
$from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
$to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
$from = str_replace('\\', '/', $from);
$to = str_replace('\\', '/', $to);
$from = explode('/', $from);
$to = explode('/', $to);
$relPath = $to;
foreach($from as $depth => $dir) {
// find first non-matching dir
if($dir === $to[$depth]) {
// ignore this directory
array_shift($relPath);
} else {
// get number of remaining dirs to $from
$remaining = count($from) - $depth;
if($remaining > 1) {
// add traversals up to first matching dir
$padLength = (count($relPath) + $remaining - 1) * -1;
$relPath = array_pad($relPath, $padLength, '..');
break;
} else {
$relPath[0] = './' . $relPath[0];
}
}
}
return implode('/', $relPath);
}
/**
* Copies the source directory recursively to the destination
*
* @param string $src Source directory path
* @param string $dest Target directory path
* @throws \RuntimeException If an error occured
*/
protected static function copyRecursive( $src, $dest )
{
self::createDirectory( $dest );
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator( $src, \RecursiveDirectoryIterator::SKIP_DOTS ),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach( $iterator as $item )
{
$target = $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName();
if( $item->isDir() === false )
{
if( copy( $item, $target ) === false ) {
throw new \RuntimeException( sprintf( 'Unable to copy file "%1$s"', $item ) );
}
}
else
{
self::createDirectory( $target );
}
}
}
/**
* Creates a new directory if it doesn't exist yet
*
* @param string $dir Absolute path of the new directory
* @throws \RuntimeException If directory couldn't be created
*/
protected static function createDirectory( $dir )
{
$perm = 0755;
if( is_dir( $dir ) === false && mkdir( $dir, $perm, true ) === false )
{
$msg = 'Unable to create directory "%1$s" with permission "%2$s"';
throw new \RuntimeException( sprintf( $msg, $dir, $perm ) );
}
}
/**
* Returns the available options defined in the composer file.
*
* @param CommandEvent $event Command event object
* @return array Associative list of option keys and values
*/
protected static function getOptions( CommandEvent $event )
{
return $event->getComposer()->getPackage()->getExtra();
}
}
from aimeos-typo3.
We need to create a link for the extensions to the ./Resources/Private/Extensions/ folder because the Aimeos TYPO3 extension expects them there but composer stores it relative to the directory where the composer.json is located.
Regarding creating the link, wouldn't it be easier to use something like this?
$extPath = '/../../../../../../' . $path;
$t3extPath = $t3path . '/Resources/Private/Extensions';
if( symlink( $extPath, $t3extPath ) === false )
{
$extPath = realpath( __DIR__ . $extPath);
if( symlink( $extPath, $t3extPath ) === false ) {
throw new \RuntimeException( sprintf( 'Failed to create symlink for "%1$s" to "%2$s"', $extPath, $t3extPath ) );
}
}
The composer.json settings for web-dir and vendor-dir doesn't matter. The links are always correct.
Yes, for typo3-ter/aimeos the returned path is absolute but for aimeos/ai-typo3 composer will return a path relative to current working directory. That's an inconsistent behavior of composer (full packages vs. extension packages) but the current code knows that.
from aimeos-typo3.
-
You wanted a solution that works with relative paths which the above one does.
As you mentioned copying the whole project folder will fail with absolute links. My solution creates a relative link. But maybe you could use the move simplistic approach and just differentiate between Windows and other OS. -
However, the proposed $extPath is to simplistic.
For instance, if I set TYPO3 web-path to e.g. to "other-folder/htdocs" it will fails as you need to go back one more folder. If the web-path is "." then you are going back one folder too many.
Even worse, would be something like "../other-folder/htdocs" as that would even go outside the composer.json path BUT that one is unlikely to happen / be used.
Maybe you could retrieve the TYPO3 web path config to find out how many folders you need to go back instead of the more complex solution.
from aimeos-typo3.
Windows is used as development environment but rarely for production in our case. Therefore, it's OK if the link is absolute on Windows.
You are right, the solution is simple and won't catch all possibilities but your approach is extremely complex. I haven't found a simple way to match ".", "other-folder/htdocs" and "../other-folder/htdocs" so at the moment we rely on the TYPO3 composer default values.
from aimeos-typo3.
Proposed fix at the moment: 93d2122
from aimeos-typo3.
Yes, I agree my solution is quite complex and I would avoid it too if possible :)
And you are also correct Windows is most likely only used in development so absolute links should be fine :)
Maybe you could retrieve the web-path of the composer.json and count the "amount" of directories to determine the correct backpath? Still not perfect but should be way easier than my proposed solution which probably isn't perfect either :)
Or maybe even easier, provide an option to override the symlink path (relative to EXT root dir)? That way if the default doesn't work they can just override the setting.
from aimeos-typo3.
Your last suggestion might be a valid option. Can you propose a patch for this?
from aimeos-typo3.
I should be able to. But probably not before Thursday or so :)
We are talking about the config option correct?
from aimeos-typo3.
symlink
still fails for me now in line 115
[ErrorException]
symlink(): Could not fetch file information(error 2)
Exception trace:
() at
`...\aimeos\Classes\Custom\Composer.php:115`
which is
if( file_exists( $target ) === false && symlink( $source, $target ) === false )
There's also a /
missing in line 117:
$source = realpath( __DIR__ . '/' . $source );
Same workaround for everyone interested in a quick copy-paste-solution:
protected static function createLink( $source, $target )
{
if( file_exists( $target ) === false && symlink( realpath( __DIR__ . '/' . $source ), $target ) === false )
{
$source = realpath( __DIR__ . '/' . $source );
if( symlink( $source, $target ) === false ) {
throw new \RuntimeException( sprintf( 'Failed to create symlink for "%1$s" to "%2$s"', $source, $target ) );
}
}
}
from aimeos-typo3.
@jfastnacht Can you please create a pull request with the necessary changes?
from aimeos-typo3.
Fixed in 3e740cb
from aimeos-typo3.
Related Issues (20)
- TYPO3 is stuck to v10.4.24 with aimeos/aimeos-typo3 v21.10.4 HOT 2
- Your Composer dependencies require a PHP version ">= 8.0.0". HOT 2
- Scheduler not working (Undefined array key) HOT 17
- Aimeos Shop advanced scheduler not saving catalog page id HOT 7
- Aimeos Advanced Scheduler - Forgot field 'aimeos_pageid_catalog' HOT 1
- Don't break TYPO3 database compare HOT 7
- Renaming "Supplier" via page.tsConfig has no impact HOT 8
- Exception when using postgresql database HOT 19
- Update Script Composer.php has wrong path HOT 4
- Typo3 12.4.5 - Bootstrap autoload causes error in backend module HOT 2
- HTML not nested correctly HOT 2
- A Warning is showing up in Database Upgrade Wizard / Reports module after successful database migration HOT 1
- Cannot install `v23.10.1` in PHP 8.2 due to `nyholm/psr7 ` HOT 3
- Incompatibility with Symfony/Console^7 (^TYPO3 12.4.10) HOT 2
- [update] - Update the broken Image Link HOT 2
- Usage of anonymous function in ext_localconf.php leads to memoryleak in SystemBackendModule HOT 5
- Upgrade Wizard overwrites existing extension configuration HOT 2
- [feature request] Send a 500'er when the json api answers with a 'Oops, an error occurred!' HOT 6
- Adding decorator with new key in Plugin Typoscript configuration appends the item to the list instead of respecting the array key HOT 5
- ContentSecutiryPolicy for Deepl HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from aimeos-typo3.