55use Composer \Installer \LibraryInstaller ;
66use Composer \Package \PackageInterface ;
77use Composer \Package \Version \VersionParser ;
8+ use Composer \Repository \InstalledRepository ;
89use Composer \Repository \InstalledRepositoryInterface ;
10+ use Composer \Repository \RootPackageRepository ;
911use Composer \Util \Filesystem ;
1012use Composer \Util \ProcessExecutor ;
1113use React \Promise \PromiseInterface ;
1214
1315abstract class ExtensionInstaller extends LibraryInstaller
1416{
17+ /** @var string|null */
18+ private $ roundcubemailInstallPath ;
19+
20+ /** @var string */
1521 protected $ composer_type ;
1622
17- protected function getRoundcubemailInstallPath ( ): string
23+ protected function setRoundcubemailInstallPath ( InstalledRepositoryInterface $ installedRepo ): void
1824 {
19- $ rootPackage = $ this ->composer ->getPackage ();
20- if ($ rootPackage ->getName () === 'roundcube/roundcubemail ' ) {
25+ // https://github.com/composer/composer/discussions/11927#discussioncomment-9116893
26+ $ rootPackage = clone $ this ->composer ->getPackage ();
27+ $ installedRepo = new InstalledRepository ([
28+ $ installedRepo ,
29+ new RootPackageRepository ($ rootPackage ),
30+ ]);
31+
32+ $ roundcubemailPackages = $ installedRepo ->findPackagesWithReplacersAndProviders ('roundcube/roundcubemail ' );
33+ assert (count ($ roundcubemailPackages ) === 1 );
34+ $ roundcubemailPackage = $ roundcubemailPackages [0 ];
35+
36+ if ($ roundcubemailPackage === $ rootPackage ) { // $this->getInstallPath($package) does not work for root package
2137 $ this ->initializeVendorDir ();
22-
23- return dirname ($ this ->vendorDir );
38+ $ this ->roundcubemailInstallPath = dirname ($ this ->vendorDir );
39+ } else {
40+ $ this ->roundcubemailInstallPath = $ this ->getInstallPath ($ roundcubemailPackage );
2441 }
42+ }
2543
26- $ roundcubemailPackage = $ this ->composer
27- ->getRepositoryManager ()
28- ->findPackage ('roundcube/roundcubemail ' , '* ' );
29-
30- return $ this ->getInstallPath ($ roundcubemailPackage );
44+ protected function getRoundcubemailInstallPath (): string
45+ {
46+ return $ this ->roundcubemailInstallPath ;
3147 }
3248
3349 public function getInstallPath (PackageInterface $ package )
3450 {
35- if (!$ this ->supports ($ package ->getType ())) {
51+ if (
52+ !$ this ->supports ($ package ->getType ())
53+ || $ this ->roundcubemailInstallPath === null // install path is not known at download phase
54+ ) {
3655 return parent ::getInstallPath ($ package );
3756 }
3857
3958 $ vendorDir = $ this ->getVendorDir ();
4059
41- return sprintf ('%s/%s ' , $ vendorDir , $ this ->getPackageName ($ package ));
60+ return $ vendorDir . \DIRECTORY_SEPARATOR
61+ . str_replace ('/ ' , \DIRECTORY_SEPARATOR , $ this ->getPackageName ($ package ));
4262 }
4363
44- public function install ( InstalledRepositoryInterface $ repo , PackageInterface $ package )
64+ private function initializeRoundcubemailEnvironment (): void
4565 {
4666 // initialize Roundcube environment
4767 if (!defined ('INSTALL_PATH ' )) {
4868 define ('INSTALL_PATH ' , $ this ->getRoundcubemailInstallPath () . '/ ' );
4969 }
5070 require_once INSTALL_PATH . 'program/include/iniset.php ' ;
71+ }
5172
73+ public function install (InstalledRepositoryInterface $ repo , PackageInterface $ package )
74+ {
75+ $ this ->setRoundcubemailInstallPath ($ repo );
76+ $ this ->initializeRoundcubemailEnvironment ();
5277 $ this ->rcubeVersionCheck ($ package );
5378
5479 $ postInstall = function () use ($ package ) {
5580 $ config_file = $ this ->rcubeConfigFile ();
5681 $ package_name = $ this ->getPackageName ($ package );
57- $ package_dir = $ this ->getVendorDir () . \ DIRECTORY_SEPARATOR . $ package_name ;
82+ $ package_dir = $ this ->getInstallPath ( $ package ) ;
5883 $ extra = $ package ->getExtra ();
5984
6085 if (is_writable ($ config_file ) && \PHP_SAPI === 'cli ' && $ this ->confirmInstall ($ package_name )) {
@@ -83,12 +108,7 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa
83108 if ($ sqldir = realpath ($ package_dir . \DIRECTORY_SEPARATOR . $ extra ['roundcube ' ]['sql-dir ' ])) {
84109 $ this ->io ->write ("<info>Running database initialization script for {$ package_name }</info> " );
85110
86- $ roundcube_version = self ::versionNormalize (RCMAIL_VERSION );
87- if (self ::versionCompare ($ roundcube_version , '1.2.0 ' , '>= ' )) {
88- \rcmail_utils::db_init ($ sqldir );
89- } else {
90- throw new \Exception ('Database initialization failed. Roundcube 1.2.0 or above required. ' );
91- }
111+ \rcmail_utils::db_init ($ sqldir );
92112 }
93113 }
94114
@@ -113,31 +133,28 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa
113133
114134 public function update (InstalledRepositoryInterface $ repo , PackageInterface $ initial , PackageInterface $ target )
115135 {
116- // initialize Roundcube environment
117- if (!defined ('INSTALL_PATH ' )) {
118- define ('INSTALL_PATH ' , $ this ->getRoundcubemailInstallPath () . '/ ' );
119- }
120- require_once INSTALL_PATH . 'program/include/iniset.php ' ;
121-
136+ $ this ->setRoundcubemailInstallPath ($ repo );
137+ $ this ->initializeRoundcubemailEnvironment ();
122138 $ this ->rcubeVersionCheck ($ target );
123139
124140 $ extra = $ target ->getExtra ();
125141 $ fs = new Filesystem ();
126142
127143 // backup persistent files e.g. config.inc.php
128- $ package_name = $ this ->getPackageName ($ initial );
129- $ package_dir = $ this ->getVendorDir () . \DIRECTORY_SEPARATOR . $ package_name ;
144+ $ package_dir = $ this ->getInstallPath ($ initial );
130145 $ temp_dir = $ package_dir . '- ' . sprintf ('%010d%010d ' , mt_rand (), mt_rand ());
131146
132147 // make a backup of existing files (for restoring persistent files)
133148 $ fs ->copy ($ package_dir , $ temp_dir );
134149
135150 $ postUpdate = function () use ($ target , $ extra , $ fs , $ temp_dir ) {
136151 $ package_name = $ this ->getPackageName ($ target );
137- $ package_dir = $ this ->getVendorDir () . \ DIRECTORY_SEPARATOR . $ package_name ;
152+ $ package_dir = $ this ->getInstallPath ( $ target ) ;
138153
139154 // restore persistent files
140- $ persistent_files = !empty ($ extra ['roundcube ' ]['persistent-files ' ]) ? $ extra ['roundcube ' ]['persistent-files ' ] : ['config.inc.php ' ];
155+ $ persistent_files = !empty ($ extra ['roundcube ' ]['persistent-files ' ])
156+ ? $ extra ['roundcube ' ]['persistent-files ' ]
157+ : ['config.inc.php ' ];
141158 foreach ($ persistent_files as $ file ) {
142159 $ path = $ temp_dir . \DIRECTORY_SEPARATOR . $ file ;
143160 if (is_readable ($ path )) {
@@ -156,12 +173,7 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini
156173 if ($ sqldir = realpath ($ package_dir . \DIRECTORY_SEPARATOR . $ extra ['roundcube ' ]['sql-dir ' ])) {
157174 $ this ->io ->write ("<info>Updating database schema for {$ package_name }</info> " );
158175
159- $ roundcube_version = self ::versionNormalize (RCMAIL_VERSION );
160- if (self ::versionCompare ($ roundcube_version , '1.2.0 ' , '>= ' )) {
161- \rcmail_utils::db_update ($ sqldir , $ package_name );
162- } else {
163- throw new \Exception ('Database update failed. Roundcube 1.2.0 or above required. ' );
164- }
176+ \rcmail_utils::db_update ($ sqldir , $ package_name );
165177 }
166178 }
167179
@@ -186,18 +198,15 @@ public function update(InstalledRepositoryInterface $repo, PackageInterface $ini
186198
187199 public function uninstall (InstalledRepositoryInterface $ repo , PackageInterface $ package )
188200 {
189- // initialize Roundcube environment
190- if (!defined ('INSTALL_PATH ' )) {
191- define ('INSTALL_PATH ' , $ this ->getRoundcubemailInstallPath () . '/ ' );
192- }
193- require_once INSTALL_PATH . 'program/include/iniset.php ' ;
201+ $ this ->setRoundcubemailInstallPath ($ repo );
202+ $ this ->initializeRoundcubemailEnvironment ();
194203
195204 $ config = $ this ->composer ->getConfig ()->get ('roundcube ' );
196205
197206 $ postUninstall = function () use ($ package , $ config ) {
198207 // post-uninstall: deactivate package
199208 $ package_name = $ this ->getPackageName ($ package );
200- $ package_dir = $ this ->getVendorDir () . \ DIRECTORY_SEPARATOR . $ package_name ;
209+ $ package_dir = $ this ->getInstallPath ( $ package ) ;
201210
202211 $ this ->rcubeAlterConfig ($ package_name , false );
203212
@@ -380,7 +389,7 @@ private function rcubeRunScript($script, PackageInterface $package)
380389 {
381390 $ package_name = $ this ->getPackageName ($ package );
382391 $ package_type = $ package ->getType ();
383- $ package_dir = $ this ->getVendorDir () . \ DIRECTORY_SEPARATOR . $ package_name ;
392+ $ package_dir = $ this ->getInstallPath ( $ package ) ;
384393
385394 // check for executable shell script
386395 if (($ scriptfile = realpath ($ package_dir . \DIRECTORY_SEPARATOR . $ script )) && is_executable ($ scriptfile )) {
@@ -404,7 +413,7 @@ private function rcubeRunScript($script, PackageInterface $package)
404413 }
405414
406415 /**
407- * normalize Roundcube version string.
416+ * Normalize Roundcube version string.
408417 */
409418 private static function versionNormalize (string $ version ): string
410419 {
0 commit comments