Coder Social home page Coder Social logo

Comments (13)

sbaghdadi avatar sbaghdadi commented on August 22, 2024

+1

from aimeos-typo3.

aimeos avatar aimeos commented on August 22, 2024

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.

AndreasA avatar AndreasA commented on August 22, 2024

There are actually mulltiple issues here.

  1. 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
  2. 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.

aimeos avatar aimeos commented on August 22, 2024

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.

AndreasA avatar AndreasA commented on August 22, 2024
  1. 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.

  2. 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.

aimeos avatar aimeos commented on August 22, 2024

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.

aimeos avatar aimeos commented on August 22, 2024

Proposed fix at the moment: 93d2122

from aimeos-typo3.

AndreasA avatar AndreasA commented on August 22, 2024

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.

aimeos avatar aimeos commented on August 22, 2024

Your last suggestion might be a valid option. Can you propose a patch for this?

from aimeos-typo3.

AndreasA avatar AndreasA commented on August 22, 2024

I should be able to. But probably not before Thursday or so :)
We are talking about the config option correct?

from aimeos-typo3.

jfastnacht avatar jfastnacht commented on August 22, 2024

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.

aimeos avatar aimeos commented on August 22, 2024

@jfastnacht Can you please create a pull request with the necessary changes?

from aimeos-typo3.

aimeos avatar aimeos commented on August 22, 2024

Fixed in 3e740cb

from aimeos-typo3.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.