Mercurial > hg > rc1
comparison vendor/roundcube/plugin-installer/src/Roundcube/Composer/PluginInstaller.php @ 0:1e000243b222
vanilla 1.3.3 distro, I hope
| author | Charlie Root |
|---|---|
| date | Thu, 04 Jan 2018 15:50:29 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1e000243b222 |
|---|---|
| 1 <?php | |
| 2 | |
| 3 namespace Roundcube\Composer; | |
| 4 | |
| 5 use Composer\Installer\LibraryInstaller; | |
| 6 use Composer\Package\Version\VersionParser; | |
| 7 use Composer\Semver\Constraint\Constraint; | |
| 8 use Composer\Package\PackageInterface; | |
| 9 use Composer\Repository\InstalledRepositoryInterface; | |
| 10 use Composer\Util\ProcessExecutor; | |
| 11 | |
| 12 /** | |
| 13 * @category Plugins | |
| 14 * @package PluginInstaller | |
| 15 * @author Till Klampaeckel <till@php.net> | |
| 16 * @author Thomas Bruederli <thomas@roundcube.net> | |
| 17 * @license GPL-3.0+ | |
| 18 * @version GIT: <git_id> | |
| 19 * @link http://github.com/roundcube/plugin-installer | |
| 20 */ | |
| 21 class PluginInstaller extends LibraryInstaller | |
| 22 { | |
| 23 const INSTALLER_TYPE = 'roundcube-plugin'; | |
| 24 | |
| 25 /** | |
| 26 * {@inheritDoc} | |
| 27 */ | |
| 28 public function getInstallPath(PackageInterface $package) | |
| 29 { | |
| 30 static $vendorDir; | |
| 31 if ($vendorDir === null) { | |
| 32 $vendorDir = $this->getVendorDir(); | |
| 33 } | |
| 34 | |
| 35 return sprintf('%s/%s', $vendorDir, $this->getPluginName($package)); | |
| 36 } | |
| 37 | |
| 38 /** | |
| 39 * {@inheritDoc} | |
| 40 */ | |
| 41 public function install(InstalledRepositoryInterface $repo, PackageInterface $package) | |
| 42 { | |
| 43 $this->rcubeVersionCheck($package); | |
| 44 parent::install($repo, $package); | |
| 45 | |
| 46 // post-install: activate plugin in Roundcube config | |
| 47 $config_file = $this->rcubeConfigFile(); | |
| 48 $plugin_name = $this->getPluginName($package); | |
| 49 $plugin_dir = $this->getVendorDir() . DIRECTORY_SEPARATOR . $plugin_name; | |
| 50 $extra = $package->getExtra(); | |
| 51 $plugin_name = $this->getPluginName($package); | |
| 52 | |
| 53 if (is_writeable($config_file) && php_sapi_name() == 'cli') { | |
| 54 $answer = $this->io->askConfirmation("Do you want to activate the plugin $plugin_name? [N|y] ", false); | |
| 55 if (true === $answer) { | |
| 56 $this->rcubeAlterConfig($plugin_name, true); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 // copy config.inc.php.dist -> config.inc.php | |
| 61 if (is_file($plugin_dir . DIRECTORY_SEPARATOR . 'config.inc.php.dist') && !is_file($plugin_dir . DIRECTORY_SEPARATOR . 'config.inc.php') && is_writeable($plugin_dir)) { | |
| 62 $this->io->write("<info>Creating plugin config file</info>"); | |
| 63 copy($plugin_dir . DIRECTORY_SEPARATOR . 'config.inc.php.dist', $plugin_dir . DIRECTORY_SEPARATOR . 'config.inc.php'); | |
| 64 } | |
| 65 | |
| 66 // initialize database schema | |
| 67 if (!empty($extra['roundcube']['sql-dir'])) { | |
| 68 if ($sqldir = realpath($plugin_dir . DIRECTORY_SEPARATOR . $extra['roundcube']['sql-dir'])) { | |
| 69 $this->io->write("<info>Running database initialization script for $plugin_name</info>"); | |
| 70 system(getcwd() . "/vendor/bin/rcubeinitdb.sh --package=$plugin_name --dir=$sqldir"); | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 // run post-install script | |
| 75 if (!empty($extra['roundcube']['post-install-script'])) { | |
| 76 $this->rcubeRunScript($extra['roundcube']['post-install-script'], $package); | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 /** | |
| 81 * {@inheritDoc} | |
| 82 */ | |
| 83 public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) | |
| 84 { | |
| 85 $this->rcubeVersionCheck($target); | |
| 86 parent::update($repo, $initial, $target); | |
| 87 | |
| 88 $extra = $target->getExtra(); | |
| 89 | |
| 90 // trigger updatedb.sh | |
| 91 if (!empty($extra['roundcube']['sql-dir'])) { | |
| 92 $plugin_name = $this->getPluginName($target); | |
| 93 $plugin_dir = $this->getVendorDir() . DIRECTORY_SEPARATOR . $plugin_name; | |
| 94 | |
| 95 if ($sqldir = realpath($plugin_dir . DIRECTORY_SEPARATOR . $extra['roundcube']['sql-dir'])) { | |
| 96 $this->io->write("<info>Updating database schema for $plugin_name</info>"); | |
| 97 system(getcwd() . "/bin/updatedb.sh --package=$plugin_name --dir=$sqldir", $res); | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 // run post-update script | |
| 102 if (!empty($extra['roundcube']['post-update-script'])) { | |
| 103 $this->rcubeRunScript($extra['roundcube']['post-update-script'], $target); | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 /** | |
| 108 * {@inheritDoc} | |
| 109 */ | |
| 110 public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) | |
| 111 { | |
| 112 parent::uninstall($repo, $package); | |
| 113 | |
| 114 // post-uninstall: deactivate plugin | |
| 115 $plugin_name = $this->getPluginName($package); | |
| 116 $this->rcubeAlterConfig($plugin_name, false); | |
| 117 | |
| 118 // run post-uninstall script | |
| 119 $extra = $package->getExtra(); | |
| 120 if (!empty($extra['roundcube']['post-uninstall-script'])) { | |
| 121 $this->rcubeRunScript($extra['roundcube']['post-uninstall-script'], $package); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 /** | |
| 126 * {@inheritDoc} | |
| 127 */ | |
| 128 public function supports($packageType) | |
| 129 { | |
| 130 return $packageType === self::INSTALLER_TYPE; | |
| 131 } | |
| 132 | |
| 133 /** | |
| 134 * Setup vendor directory to one of these two: | |
| 135 * ./plugins | |
| 136 * | |
| 137 * @return string | |
| 138 */ | |
| 139 public function getVendorDir() | |
| 140 { | |
| 141 $pluginDir = getcwd(); | |
| 142 $pluginDir .= '/plugins'; | |
| 143 | |
| 144 return $pluginDir; | |
| 145 } | |
| 146 | |
| 147 /** | |
| 148 * Extract the (valid) plugin name from the package object | |
| 149 */ | |
| 150 private function getPluginName(PackageInterface $package) | |
| 151 { | |
| 152 @list($vendor, $pluginName) = explode('/', $package->getPrettyName()); | |
| 153 | |
| 154 return strtr($pluginName, '-', '_'); | |
| 155 } | |
| 156 | |
| 157 /** | |
| 158 * Check version requirements from the "extra" block of a package | |
| 159 * against the local Roundcube version | |
| 160 */ | |
| 161 private function rcubeVersionCheck($package) | |
| 162 { | |
| 163 $parser = new VersionParser; | |
| 164 | |
| 165 // read rcube version from iniset | |
| 166 $rootdir = getcwd(); | |
| 167 $iniset = @file_get_contents($rootdir . '/program/include/iniset.php'); | |
| 168 if (preg_match('/define\(.RCMAIL_VERSION.,\s*.([0-9.]+[a-z-]*)?/', $iniset, $m)) { | |
| 169 $rcubeVersion = $parser->normalize(str_replace('-git', '.999', $m[1])); | |
| 170 } else { | |
| 171 throw new \Exception("Unable to find a Roundcube installation in $rootdir"); | |
| 172 } | |
| 173 | |
| 174 $extra = $package->getExtra(); | |
| 175 | |
| 176 if (!empty($extra['roundcube'])) { | |
| 177 foreach (array('min-version' => '>=', 'max-version' => '<=') as $key => $operator) { | |
| 178 if (!empty($extra['roundcube'][$key])) { | |
| 179 $version = $parser->normalize(str_replace('-git', '.999', $extra['roundcube'][$key])); | |
| 180 $constraint = new Constraint($operator, $version); | |
| 181 if (!$constraint->versionCompare($rcubeVersion, $version, $operator)) { | |
| 182 throw new \Exception("Version check failed! " . $package->getName() . " requires Roundcube version $operator $version, $rcubeVersion was detected."); | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 /** | |
| 190 * Add or remove the given plugin to the list of active plugins in the Roundcube config. | |
| 191 */ | |
| 192 private function rcubeAlterConfig($plugin_name, $add) | |
| 193 { | |
| 194 $config_file = $this->rcubeConfigFile(); | |
| 195 @include($config_file); | |
| 196 $success = false; | |
| 197 $varname = '$config'; | |
| 198 | |
| 199 if (empty($config) && !empty($rcmail_config)) { | |
| 200 $config = $rcmail_config; | |
| 201 $varname = '$rcmail_config'; | |
| 202 } | |
| 203 | |
| 204 if (is_array($config) && is_writeable($config_file)) { | |
| 205 $config_templ = @file_get_contents($config_file) ?: ''; | |
| 206 $config_plugins = !empty($config['plugins']) ? ((array) $config['plugins']) : array(); | |
| 207 $active_plugins = $config_plugins; | |
| 208 | |
| 209 if ($add && !in_array($plugin_name, $active_plugins)) { | |
| 210 $active_plugins[] = $plugin_name; | |
| 211 } elseif (!$add && ($i = array_search($plugin_name, $active_plugins)) !== false) { | |
| 212 unset($active_plugins[$i]); | |
| 213 } | |
| 214 | |
| 215 if ($active_plugins != $config_plugins) { | |
| 216 $count = 0; | |
| 217 $var_export = "array(\n\t'" . join("',\n\t'", $active_plugins) . "',\n);"; | |
| 218 $new_config = preg_replace( | |
| 219 "/(\\$varname\['plugins'\])\s+=\s+(.+);/Uims", | |
| 220 "\\1 = " . $var_export, | |
| 221 $config_templ, -1, $count); | |
| 222 | |
| 223 // 'plugins' option does not exist yet, add it... | |
| 224 if (!$count) { | |
| 225 $var_txt = "\n{$varname}['plugins'] = $var_export;\n"; | |
| 226 $new_config = str_replace('?>', $var_txt . '?>', $config_templ, $count); | |
| 227 | |
| 228 if (!$count) { | |
| 229 $new_config = $config_templ . $var_txt; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 $success = file_put_contents($config_file, $new_config); | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 if ($success && php_sapi_name() == 'cli') { | |
| 238 $this->io->write("<info>Updated local config at $config_file</info>"); | |
| 239 } | |
| 240 | |
| 241 return $success; | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * Helper method to get an absolute path to the local Roundcube config file | |
| 246 */ | |
| 247 private function rcubeConfigFile() | |
| 248 { | |
| 249 return realpath(getcwd() . '/config/config.inc.php'); | |
| 250 } | |
| 251 | |
| 252 /** | |
| 253 * Run the given script file | |
| 254 */ | |
| 255 private function rcubeRunScript($script, PackageInterface $package) | |
| 256 { | |
| 257 $plugin_name = $this->getPluginName($package); | |
| 258 $plugin_dir = $this->getVendorDir() . DIRECTORY_SEPARATOR . $plugin_name; | |
| 259 | |
| 260 // check for executable shell script | |
| 261 if (($scriptfile = realpath($plugin_dir . DIRECTORY_SEPARATOR . $script)) && is_executable($scriptfile)) { | |
| 262 $script = $scriptfile; | |
| 263 } | |
| 264 | |
| 265 // run PHP script in Roundcube context | |
| 266 if ($scriptfile && preg_match('/\.php$/', $scriptfile)) { | |
| 267 $incdir = realpath(getcwd() . '/program/include'); | |
| 268 include_once($incdir . '/iniset.php'); | |
| 269 include($scriptfile); | |
| 270 } | |
| 271 // attempt to execute the given string as shell commands | |
| 272 else { | |
| 273 $process = new ProcessExecutor($this->io); | |
| 274 $exitCode = $process->execute($script, null, $plugin_dir); | |
| 275 if ($exitCode !== 0) { | |
| 276 throw new \RuntimeException('Error executing script: '. $process->getErrorOutput(), $exitCode); | |
| 277 } | |
| 278 } | |
| 279 } | |
| 280 } |
