From fbb85e2f4f4550dcdf9a0b8f28855959a5a53a47 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Wed, 9 Sep 2015 19:56:52 +0200 Subject: [PATCH 01/85] [ticket/11150] Add ability to manage extensions through composer PHPBB3-11150 --- phpBB/composer.json | 3 +- phpBB/composer.lock | 563 ++++++++++++++++-- phpBB/config/default/container/services.yml | 13 +- .../default/container/services_console.yml | 32 + .../default/container/services_extensions.yml | 39 ++ .../composer/exception/runtime_exception.php | 37 ++ phpBB/phpbb/composer/installer.php | 392 ++++++++++++ phpBB/phpbb/composer/manager.php | 190 ++++++ .../console/command/extension/install.php | 72 +++ .../command/extension/list_available.php | 75 +++ .../console/command/extension/remove.php | 72 +++ .../console/command/extension/update.php | 72 +++ .../data/v320/extensions_composer.php | 27 + 13 files changed, 1523 insertions(+), 64 deletions(-) create mode 100644 phpBB/config/default/container/services_extensions.yml create mode 100644 phpBB/phpbb/composer/exception/runtime_exception.php create mode 100644 phpBB/phpbb/composer/installer.php create mode 100644 phpBB/phpbb/composer/manager.php create mode 100644 phpBB/phpbb/console/command/extension/install.php create mode 100644 phpBB/phpbb/console/command/extension/list_available.php create mode 100644 phpBB/phpbb/console/command/extension/remove.php create mode 100644 phpBB/phpbb/console/command/extension/update.php create mode 100644 phpBB/phpbb/db/migration/data/v320/extensions_composer.php diff --git a/phpBB/composer.json b/phpBB/composer.json index 2207d10b9d..324ef32a1f 100644 --- a/phpBB/composer.json +++ b/phpBB/composer.json @@ -47,7 +47,8 @@ "symfony/routing": "~3.1", "symfony/twig-bridge": "~3.1", "symfony/yaml": "~3.1", - "twig/twig": "^1.0,<1.25" + "twig/twig": "^1.0,<1.25", + "composer/composer": "^1.0" }, "require-dev": { "fabpot/goutte": "~3.1", diff --git a/phpBB/composer.lock b/phpBB/composer.lock index 0d258bc677..6ad23abf13 100644 --- a/phpBB/composer.lock +++ b/phpBB/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "c53f2fa544168309d695bb1855c98c24", - "content-hash": "4bc93e90a4852f936c13986c3823831b", + "hash": "80752e19604f3bd6ac0af14d488a748b", + "content-hash": "60c4fa2116744111294d5c3a5c21afdd", "packages": [ { "name": "bantu/ini-get-wrapper", @@ -37,6 +37,263 @@ "description": "Convenience wrapper around ini_get()", "time": "2014-09-15 13:12:35" }, + { + "name": "composer/ca-bundle", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "5df9ed0ed0c9506ea6404a23450854e5df15cc12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/5df9ed0ed0c9506ea6404a23450854e5df15cc12", + "reference": "5df9ed0ed0c9506ea6404a23450854e5df15cc12", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "symfony/process": "^2.5 || ^3.0" + }, + "suggest": { + "symfony/process": "This is necessary to reliably check whether openssl_x509_parse is vulnerable on older php versions, but can be ignored on PHP 5.5.6+" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "time": "2016-07-18 23:07:53" + }, + { + "name": "composer/composer", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "b49a006748a460f8dae6500ec80ed021501ce969" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/b49a006748a460f8dae6500ec80ed021501ce969", + "reference": "b49a006748a460f8dae6500ec80ed021501ce969", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", + "composer/semver": "^1.0", + "composer/spdx-licenses": "^1.0", + "justinrainbow/json-schema": "^1.6 || ^2.0", + "php": "^5.3.2 || ^7.0", + "psr/log": "^1.0", + "seld/cli-prompt": "^1.0", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.0", + "symfony/console": "^2.5 || ^3.0", + "symfony/filesystem": "^2.5 || ^3.0", + "symfony/finder": "^2.2 || ^3.0", + "symfony/process": "^2.1 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "^2.3 || ^3.0" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "time": "2016-07-18 23:28:52" + }, + { + "name": "composer/semver", + "version": "1.4.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", + "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "time": "2016-08-30 16:08:34" + }, + { + "name": "composer/spdx-licenses", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "88c26372b1afac36d8db601cdf04ad8716f53d88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/88c26372b1afac36d8db601cdf04ad8716f53d88", + "reference": "88c26372b1afac36d8db601cdf04ad8716f53d88", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.5 || ^5.0.5", + "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "time": "2016-05-04 12:27:30" + }, { "name": "google/recaptcha", "version": "1.1.2", @@ -253,6 +510,72 @@ ], "time": "2016-06-24 23:00:38" }, + { + "name": "justinrainbow/json-schema", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "6b2a33e6a768f96bdc2ead5600af0822eed17d67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/6b2a33e6a768f96bdc2ead5600af0822eed17d67", + "reference": "6b2a33e6a768f96bdc2ead5600af0822eed17d67", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "json-schema/json-schema-test-suite": "1.2.0", + "phpdocumentor/phpdocumentor": "~2", + "phpunit/phpunit": "^4.8.22" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2016-06-02 10:59:52" + }, { "name": "lusitanian/oauth", "version": "v0.8.10", @@ -703,6 +1026,144 @@ ], "time": "2017-01-22 17:12:21" }, + { + "name": "seld/cli-prompt", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/cli-prompt.git", + "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/cli-prompt/zipball/8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", + "reference": "8cbe10923cae5bcd7c5a713f6703fc4727c8c1b4", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\CliPrompt\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Allows you to prompt for user input on the command line, and optionally hide the characters they type", + "keywords": [ + "cli", + "console", + "hidden", + "input", + "prompt" + ], + "time": "2016-04-18 09:31:41" + }, + { + "name": "seld/jsonlint", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "66834d3e3566bb5798db7294619388786ae99394" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", + "reference": "66834d3e3566bb5798db7294619388786ae99394", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "time": "2015-11-21 02:21:41" + }, + { + "name": "seld/phar-utils", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/7009b5139491975ef6486545a39f3e6dad5ac30a", + "reference": "7009b5139491975ef6486545a39f3e6dad5ac30a", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phra" + ], + "time": "2015-10-13 18:44:15" + }, { "name": "symfony/config", "version": "v3.2.0", @@ -1294,6 +1755,55 @@ ], "time": "2016-11-14 01:06:16" }, + { + "name": "symfony/process", + "version": "v3.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "04c2dfaae4ec56a5c677b0c69fac34637d815758" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/04c2dfaae4ec56a5c677b0c69fac34637d815758", + "reference": "04c2dfaae4ec56a5c677b0c69fac34637d815758", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2016-07-28 11:13:48" + }, { "name": "symfony/proxy-manager-bridge", "version": "v3.2.0", @@ -3231,55 +3741,6 @@ "homepage": "https://symfony.com", "time": "2016-11-25 12:32:42" }, - { - "name": "symfony/process", - "version": "v3.2.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "02ea84847aad71be7e32056408bb19f3a616cdd3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/02ea84847aad71be7e32056408bb19f3a616cdd3", - "reference": "02ea84847aad71be7e32056408bb19f3a616cdd3", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "https://symfony.com", - "time": "2016-11-24 10:40:28" - }, { "name": "webmozart/assert", "version": "1.2.0", diff --git a/phpBB/config/default/container/services.yml b/phpBB/config/default/container/services.yml index 9bb1d673f4..17fa223dda 100644 --- a/phpBB/config/default/container/services.yml +++ b/phpBB/config/default/container/services.yml @@ -8,6 +8,7 @@ imports: - { resource: services_cron.yml } - { resource: services_db.yml } - { resource: services_event.yml } + - { resource: services_extensions.yml } - { resource: services_feed.yml } - { resource: services_files.yml } - { resource: services_filesystem.yml } @@ -98,18 +99,6 @@ services: - '%core.root_path%' - '@template' - ext.manager: - class: phpbb\extension\manager - arguments: - - '@service_container' - - '@dbal.conn' - - '@config' - - '@filesystem' - - '%tables.ext%' - - '%core.root_path%' - - '%core.php_ext%' - - '@cache' - file_downloader: class: phpbb\file_downloader diff --git a/phpBB/config/default/container/services_console.yml b/phpBB/config/default/container/services_console.yml index e25ab4f03f..3305ede490 100644 --- a/phpBB/config/default/container/services_console.yml +++ b/phpBB/config/default/container/services_console.yml @@ -141,6 +141,22 @@ services: tags: - { name: console.command } + console.command.extension.install: + class: phpbb\console\command\extension\install + arguments: + - @user + - @ext.composer.manager + tags: + - { name: console.command } + + console.command.extension.list_available: + class: phpbb\console\command\extension\list_available + arguments: + - @user + - @ext.composer.manager + tags: + - { name: console.command } + console.command.extension.purge: class: phpbb\console\command\extension\purge arguments: @@ -150,6 +166,14 @@ services: tags: - { name: console.command } + console.command.extension.remove: + class: phpbb\console\command\extension\remove + arguments: + - @user + - @ext.composer.manager + tags: + - { name: console.command } + console.command.extension.show: class: phpbb\console\command\extension\show arguments: @@ -159,6 +183,14 @@ services: tags: - { name: console.command } + console.command.extension.update: + class: phpbb\console\command\extension\update + arguments: + - @user + - @ext.composer.manager + tags: + - { name: console.command } + console.command.fixup.recalculate_email_hash: class: phpbb\console\command\fixup\recalculate_email_hash arguments: diff --git a/phpBB/config/default/container/services_extensions.yml b/phpBB/config/default/container/services_extensions.yml new file mode 100644 index 0000000000..3a2e83f73a --- /dev/null +++ b/phpBB/config/default/container/services_extensions.yml @@ -0,0 +1,39 @@ +services: + ext.manager: + class: phpbb\extension\manager + arguments: + - @service_container + - @dbal.conn + - @config + - @filesystem + - %tables.ext% + - %core.root_path% + - %core.php_ext% + - @cache + + ext.composer.installer: + class: phpbb\composer\installer + arguments: + - %core.root_path% + - @config + + ext.composer.manager: + class: phpbb\composer\manager + arguments: + - @ext.composer.installer + - phpbb-extension + - EXTENSIONS_ + + style.composer.manager: + class: phpbb\composer\manager + arguments: + - @ext.composer.installer + - phpbb-style + - STYLES_ + + lang.composer.manager: + class: phpbb\composer\manager + arguments: + - @ext.composer.installer + - phpbb-language + - LANGUAGES_ diff --git a/phpBB/phpbb/composer/exception/runtime_exception.php b/phpBB/phpbb/composer/exception/runtime_exception.php new file mode 100644 index 0000000000..eb92759318 --- /dev/null +++ b/phpBB/phpbb/composer/exception/runtime_exception.php @@ -0,0 +1,37 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +use phpbb\exception\runtime_exception as base; + +/** + * Base class for exceptions thrown when managing packages through composer + */ +class runtime_exception extends base +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php new file mode 100644 index 0000000000..05c3ef2d68 --- /dev/null +++ b/phpBB/phpbb/composer/installer.php @@ -0,0 +1,392 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use Composer\Composer; +use Composer\Factory; +use Composer\IO\BufferIO; +use Composer\IO\NullIO; +use Composer\Json\JsonFile; +use Composer\Package\CompletePackage; +use Composer\Package\PackageInterface; +use Composer\Repository\ComposerRepository; +use Composer\Repository\RepositoryInterface; +use Composer\Util\RemoteFilesystem; +use phpbb\config\config; +use phpbb\exception\runtime_exception; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Class to install packages through composer while freezing core dependencies. + */ +class installer +{ + /** + * @var array Repositories to look packages from + */ + protected $repositories = []; + + /** + * @var bool Indicates whether packagist usage is allowed or not + */ + protected $packagist = false; + + /** + * @var string Composer filename used to manage the packages + */ + protected $composer_filename = 'composer-ext.json'; + + /** + * @var string Directory where to install packages vendors + */ + protected $packages_vendor_dir = 'vendor-ext/'; + + /** + * @var string phpBB root path + */ + protected $root_path; + + /** + * @param \phpbb\config\config $config Config object + * @param string $root_path phpBB root path + */ + public function __construct($root_path, config $config = null) + { + if ($config) + { + $this->repositories = (array) unserialize($config['exts_composer_repositories']); + $this->packagist = (bool) $config['exts_composer_packagist']; + $this->composer_filename = $config['exts_composer_json_file']; + $this->packages_vendor_dir = $config['exts_composer_vendor_dir']; + } + + $this->root_path = $root_path; + } + + /** + * Update the current installed set of packages + * + * @param array $packages Packages to install. + * Each entry may be a name or an array associating a version constraint to a name + * @param array $whitelist White-listed packages (packages that can be installed/updated/removed) + * @throws runtime_exception + */ + public function install(array $packages, $whitelist) + { + $this->generate_ext_json_file($packages); + + putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . '/' . $this->packages_vendor_dir); + + $io = new BufferIO('', OutputInterface::VERBOSITY_DEBUG); + $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + $install = \Composer\Installer::create($io, $composer); + + $install + ->setVerbose(true) + ->setPreferSource(false) + ->setPreferDist(true) + ->setDevMode(false) + ->setUpdate(true) + ->setUpdateWhitelist($whitelist) + ->setWhitelistDependencies(false) + ->setIgnorePlatformRequirements(false) + ->setDumpAutoloader(false) + ->setPreferStable(true) + ->setRunScripts(false) + ->setDryRun(false); + + try + { + $install->run(); + $output = $io->getOutput(); + $error_pos = strpos($output, 'Your requirements could not be resolved to an installable set of packages.'); + + if ($error_pos) + { + // TODO Extract the precise error and use language string + throw new \RuntimeException(substr($output, $error_pos)); + } + + } + catch (\Exception $e) + { + throw new runtime_exception('Cannot install packages', [], $e); + } + } + + /** + * Returns the list of currently installed packages + * + * @param string $type Returns only the packages with the given type + * + * @return array The installed packages associated to their version. + */ + public function get_installed_packages($type) + { + try + { + $io = new NullIO(); + putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . '/' . $this->packages_vendor_dir); + $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + + $installed = []; + $packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + + foreach ($packages as $package) + { + if ($package->getType() === $type) + { + $installed[$package->getName()] = $package->getPrettyVersion(); + } + } + + return $installed; + } + catch (\Exception $e) + { + return []; + } + } + + /** + * Gets the list of the available packages of the configured type in the configured repositories + * + * @param string $type Returns only the packages with the given type + * + * @return array The name of the available packages, associated to their definition. Ordered by name. + */ + public function get_available_packages($type) + { + try + { + $io = new NullIO(); + + $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + + $available = []; + $repositories = $composer->getRepositoryManager()->getRepositories(); + + /** @var RepositoryInterface $repository */ + foreach ($repositories as $repository) + { + if ($repository instanceof ComposerRepository && $repository->hasProviders()) + { + $r = new \ReflectionObject($repository); + $repo_url = $r->getProperty('url'); + $repo_url->setAccessible(true); + + if ($repo_url->getValue($repository) === 'http://packagist.org') + { + $url = 'https://packagist.org/packages/list.json?type=' . $type; + $rfs = new RemoteFilesystem($io); + $hostname = parse_url($url, PHP_URL_HOST) ?: $url; + $json = $rfs->getContents($hostname, $url, false); + + /** @var PackageInterface $package */ + foreach (JsonFile::parseJson($json, $url)['packageNames'] as $package) + { + $packages = $repository->findPackages($package); + $package = array_pop($packages); + $available[$package->getName()] = ['name' => $package->getPrettyName()]; + + if ($package instanceof CompletePackage) + { + $available[$package->getName()]['description'] = $package->getDescription(); + $available[$package->getName()]['url'] = $package->getHomepage(); + } + } + } + } + else + { + /** @var PackageInterface $package */ + foreach ($repository->getPackages() as $package) + { + if ($package->getType() === $type) + { + $available[$package->getName()] = ['name' => $package]; + + if ($package instanceof CompletePackage) + { + $available[$package->getName()]['description'] = $package->getDescription(); + $available[$package->getName()]['url'] = $package->getHomepage(); + } + } + } + } + } + + ksort($available); + + return $available; + } + catch (\Exception $e) + { + return []; + } + } + + /** + * Generates and write the json file used to install the set of packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + */ + protected function generate_ext_json_file(array $packages) + { + $io = new NullIO(); + $composer = Factory::create($io, null, false); + + $core_packages = $this->get_core_packages($composer); + $core_json_data = [ + 'require' => array_merge( + ['php' => $this->get_core_php_requirement($composer)], + $core_packages, + $this->get_extra_dependencies(), + $packages), + 'replace' => $core_packages, + 'repositories' => $this->get_composer_repositories(), + ]; + + $json_file = new JsonFile($this->get_composer_ext_json_filename()); + $json_file->write($core_json_data); + } + + /** + * Get the core installed packages + * + * @param Composer $composer Composer object to load the dependencies + * @return array The core packages with their version + */ + protected function get_core_packages(Composer $composer) + { + $core_deps = []; + $packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + + foreach ($packages as $package) + { + $core_deps[$package->getName()] = $package->getPrettyVersion(); + } + + $core_deps['phpbb/phpbb'] = $composer->getPackage()->getPrettyVersion(); + + return $core_deps; + } + + /** + * Get the PHP version required by the core + * + * @param Composer $composer Composer object to load the dependencies + * @return string The PHP version required by the core + */ + protected function get_core_php_requirement(Composer $composer) + { + return $composer->getLocker()->getLockData()['platform']['php']; + } + + /** + * Generate the repositories entry of the packages json file + * + * @return array repositories entry + */ + protected function get_composer_repositories() + { + $repositories = []; + + if (!$this->packagist) + { + $repositories[]['packagist'] = false; + } + + foreach ($this->repositories as $repository) + { + $repositories[] = [ + 'type' => 'composer', + 'url' => $repository, + ]; + } + + return $repositories; + } + + /** + * Get the name of the json file used for the packages. + * + * @return string The json filename + */ + protected function get_composer_ext_json_filename() + { + return $this->root_path . $this->composer_filename; + } + + /** + * Get extra dependencies required to install the packages + * + * @return array Array of composer dependencies + */ + protected function get_extra_dependencies() + { + return []; + } + + /** + * Sets the customs repositories + * + * @param array $repositories An array of composer repositories to use + */ + public function set_repositories($repositories) + { + $this->repositories = $repositories; + } + + /** + * Allow or disallow packagist + * + * @param boolean $packagist + */ + public function set_packagist($packagist) + { + $this->packagist = $packagist; + } + + /** + * Sets the name of the managed packages' json file + * + * @param string $composer_filename + */ + public function set_composer_filename($composer_filename) + { + $this->composer_filename = $composer_filename; + } + + /** + * Sets the location of the managed packages' vendors + * + * @param string $packages_vendor_dir + */ + public function set_packages_vendor_dir($packages_vendor_dir) + { + $this->packages_vendor_dir = $packages_vendor_dir; + } + + /** + * Sets the phpBB root path + * + * @param string $root_path + */ + public function set_root_path($root_path) + { + $this->root_path = $root_path; + } +} diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php new file mode 100644 index 0000000000..836f39b509 --- /dev/null +++ b/phpBB/phpbb/composer/manager.php @@ -0,0 +1,190 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use phpbb\composer\exception\runtime_exception; + +/** + * Class to manage packages through composer. + */ +class manager +{ + /** + * @var installer Composer packages installer + */ + protected $installer; + + /** + * @var string Type of packages (phpbb-packages per example) + */ + protected $package_type; + + /** + * @var string Prefix used for the exception's language string + */ + protected $exception_prefix; + + /** + * @var array Caches the managed packages list + */ + private $managed_packages; + + /** + * @var array Caches the available packages list + */ + private $available_packages; + + /** + * @param installer $installer Installer object + * @param string $package_type Composer type of managed packages + * @param string $exception_prefix Exception prefix to use + */ + public function __construct(installer $installer, $package_type, $exception_prefix) + { + $this->installer = $installer; + $this->package_type = $package_type; + $this->exception_prefix = $exception_prefix; + } + + /** + * Installs (if necessary) a set of packages + * + * @param array $packages Packages to install. + * Each entry may be a name or an array associating a version constraint to a name + * @throws runtime_exception + */ + public function install(array $packages) + { + $packages = $this->normalize_version($packages); + + $already_managed = array_intersect(array_keys($this->get_managed_packages()), array_keys($packages)); + if (count($already_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); + } + + $managed_packages = array_merge($this->get_managed_packages(), $packages); + ksort($managed_packages); + + $this->installer->install($managed_packages, array_keys($packages)); + + $this->managed_packages = null; + } + + /** + * Updates or installs a set of packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @throws runtime_exception + */ + public function update(array $packages) + { + $packages = $this->normalize_version($packages); + + // TODO: if the extension is already enabled, we should disabled and re-enable it + $not_managed = array_diff_key($packages, $this->get_managed_packages()); + if (count($not_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); + } + + $managed_packages = array_merge($this->get_managed_packages(), $packages); + ksort($managed_packages); + + $this->installer->install($managed_packages, array_keys($packages)); + } + + /** + * Removes a set of packages + * + * @param array $packages Packages to remove. + * Each entry may be a name or an array associating a version constraint to a name + * @throws runtime_exception + */ + public function remove(array $packages) + { + $packages = $this->normalize_version($packages); + + // TODO: if the extension is already enabled, we should disabled (with an option for purge) + $not_managed = array_diff_key($packages, $this->get_managed_packages()); + if (count($not_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); + } + + $managed_packages = array_diff_key($this->get_managed_packages(), $packages); + ksort($managed_packages); + + $this->installer->install($managed_packages, array_keys($packages)); + + $this->managed_packages = null; + } + + /** + * Tells whether or not a package is managed by Composer. + * + * @param string $packages Package name + * @return bool + */ + public function is_managed($packages) + { + return array_key_exists($packages, $this->get_managed_packages()); + } + + /** + * Returns the list of managed packages + * + * @return array The managed packages associated to their version. + */ + public function get_managed_packages() + { + if ($this->managed_packages === null) + { + $this->managed_packages = $this->installer->get_installed_packages($this->package_type); + } + + return $this->managed_packages; + } + + /** + * Returns the list of available packages + * + * @return array The name of the available packages, associated to their definition. Ordered by name. + */ + public function get_available_packages() + { + if ($this->available_packages === null) + { + $this->available_packages = $this->installer->get_available_packages($this->package_type); + } + + return $this->available_packages; + } + + protected function normalize_version($packages) + { + $normalized_packages = []; + + foreach ($packages as $package) + { + if (!is_array($package)) + { + $normalized_packages[$package] = '*'; + } + } + + return $normalized_packages; + } +} diff --git a/phpBB/phpbb/console/command/extension/install.php b/phpBB/phpbb/console/command/extension/install.php new file mode 100644 index 0000000000..f2358e7e4a --- /dev/null +++ b/phpBB/phpbb/console/command/extension/install.php @@ -0,0 +1,72 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\console\command\extension; + +use phpbb\composer\manager; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class install extends \phpbb\console\command\command +{ + /** + * @var \phpbb\composer\manager Composer extensions manager + */ + protected $manager; + + public function __construct(\phpbb\user $user, manager $manager) + { + $this->manager = $manager; + + parent::__construct($user); + } + + /** + * Sets the command name and description + * + * @return null + */ + protected function configure() + { + $this + ->setName('extension:install') + ->setDescription($this->user->lang('CLI_DESCRIPTION_EXTENSION_INSTALL')) + ->addArgument( + 'extensions', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + $this->user->lang('CLI_DESCRIPTION_EXTENSION_INSTALL')) + ; + } + + /** + * Executes the command extension:install + * + * @param InputInterface $input + * @param OutputInterface $output + * @return integer + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $extensions = $input->getArgument('extensions'); + + $this->manager->install($extensions); + + $io->success('All extensions installed'); + + return 0; + } +} diff --git a/phpBB/phpbb/console/command/extension/list_available.php b/phpBB/phpbb/console/command/extension/list_available.php new file mode 100644 index 0000000000..0b20ce2d5e --- /dev/null +++ b/phpBB/phpbb/console/command/extension/list_available.php @@ -0,0 +1,75 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\console\command\extension; + +use Composer\Package\CompletePackage; +use Composer\Package\PackageInterface; +use phpbb\composer\installer; +use phpbb\composer\manager; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class list_available extends \phpbb\console\command\command +{ + /** + * @var \phpbb\composer\manager Composer extensions manager + */ + protected $manager; + + public function __construct(\phpbb\user $user, manager $manager) + { + $this->manager = $manager; + + parent::__construct($user); + } + + /** + * Sets the command name and description + * + * @return null + */ + protected function configure() + { + $this + ->setName('extension:list-available') + ->setDescription($this->user->lang('CLI_DESCRIPTION_EXTENSION_LIST_AVAILABLE')) + ; + } + + /** + * Executes the command extension:install + * + * @param InputInterface $input + * @param OutputInterface $output + * @return integer + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $extensions = []; + + foreach ($this->manager->get_available_packages() as $package) + { + $extensions[] = '' . $package['name'] . ' ' . $package['url'] . "\n" . $package['description']; + } + + $io->listing($extensions); + + return 0; + } +} diff --git a/phpBB/phpbb/console/command/extension/remove.php b/phpBB/phpbb/console/command/extension/remove.php new file mode 100644 index 0000000000..a668322cdf --- /dev/null +++ b/phpBB/phpbb/console/command/extension/remove.php @@ -0,0 +1,72 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\console\command\extension; + +use phpbb\composer\manager; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class remove extends \phpbb\console\command\command +{ + /** + * @var \phpbb\composer\manager Composer extensions manager + */ + protected $manager; + + public function __construct(\phpbb\user $user, manager $manager) + { + $this->manager = $manager; + + parent::__construct($user); + } + + /** + * Sets the command name and description + * + * @return null + */ + protected function configure() + { + $this + ->setName('extension:remove') + ->setDescription($this->user->lang('CLI_DESCRIPTION_EXTENSION_REMOVE')) + ->addArgument( + 'extensions', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + $this->user->lang('CLI_DESCRIPTION_EXTENSION_REMOVE')) + ; + } + + /** + * Executes the command extension:install + * + * @param InputInterface $input + * @param OutputInterface $output + * @return integer + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $extensions = $input->getArgument('extensions'); + + $this->manager->remove($extensions); + + $io->success('All extensions removed'); + + return 0; + } +} diff --git a/phpBB/phpbb/console/command/extension/update.php b/phpBB/phpbb/console/command/extension/update.php new file mode 100644 index 0000000000..01c9db0c28 --- /dev/null +++ b/phpBB/phpbb/console/command/extension/update.php @@ -0,0 +1,72 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\console\command\extension; + +use phpbb\composer\manager; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class update extends \phpbb\console\command\command +{ + /** + * @var \phpbb\composer\manager Composer extensions manager + */ + protected $manager; + + public function __construct(\phpbb\user $user, manager $manager) + { + $this->manager = $manager; + + parent::__construct($user); + } + + /** + * Sets the command name and description + * + * @return null + */ + protected function configure() + { + $this + ->setName('extension:update') + ->setDescription($this->user->lang('CLI_DESCRIPTION_EXTENSION_UPDATE')) + ->addArgument( + 'extensions', + InputArgument::IS_ARRAY | InputArgument::REQUIRED, + $this->user->lang('CLI_DESCRIPTION_EXTENSION_UPDATE')) + ; + } + + /** + * Executes the command extension:install + * + * @param InputInterface $input + * @param OutputInterface $output + * @return integer + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $extensions = $input->getArgument('extensions'); + + $this->manager->update($extensions); + + $io->success('All extensions updated'); + + return 0; + } +} diff --git a/phpBB/phpbb/db/migration/data/v320/extensions_composer.php b/phpBB/phpbb/db/migration/data/v320/extensions_composer.php new file mode 100644 index 0000000000..09771f1797 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/extensions_composer.php @@ -0,0 +1,27 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v320; + +class extensions_composer extends \phpbb\db\migration\migration +{ + public function update_data() + { + return array( + array('config.add', array('exts_composer_repositories', serialize([]))), + array('config.add', array('exts_composer_packagist', true)), + array('config.add', array('exts_composer_json_file', 'composer-ext.json')), + array('config.add', array('exts_composer_vendor_dir', 'vendor-ext/')), + ); + } +} From ac129f34d3b455c9148964c5cc8e60670faa1e62 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Wed, 9 Sep 2015 23:35:14 +0200 Subject: [PATCH 02/85] [ticket/11150] Do not override extensions installed manually PHPBB3-11150 --- .../default/container/services_extensions.yml | 3 +- phpBB/phpbb/composer/extension_manager.php | 63 +++++++++++++++++ phpBB/phpbb/composer/installer.php | 15 +++- phpBB/phpbb/composer/manager.php | 28 ++++++-- phpBB/phpbb/composer/manager_interface.php | 69 +++++++++++++++++++ .../console/command/extension/install.php | 5 +- .../command/extension/list_available.php | 5 +- .../console/command/extension/remove.php | 5 +- .../console/command/extension/update.php | 5 +- 9 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 phpBB/phpbb/composer/extension_manager.php create mode 100644 phpBB/phpbb/composer/manager_interface.php diff --git a/phpBB/config/default/container/services_extensions.yml b/phpBB/config/default/container/services_extensions.yml index 3a2e83f73a..46953ae4a2 100644 --- a/phpBB/config/default/container/services_extensions.yml +++ b/phpBB/config/default/container/services_extensions.yml @@ -18,9 +18,10 @@ services: - @config ext.composer.manager: - class: phpbb\composer\manager + class: phpbb\composer\extension_manager arguments: - @ext.composer.installer + - @ext.manager - phpbb-extension - EXTENSIONS_ diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php new file mode 100644 index 0000000000..ee3ac1aeaf --- /dev/null +++ b/phpBB/phpbb/composer/extension_manager.php @@ -0,0 +1,63 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer; + +use phpbb\composer\exception\runtime_exception; +use phpbb\extension\manager as ext_manager; + +/** + * Class to safely manage extensions through composer. + */ +class extension_manager extends manager +{ + /** + * @var \phpbb\extension\manager + */ + protected $extension_manager; + + /** + * @param installer $installer Installer object + * @param ext_manager $extension_manager phpBB extension manager + * @param string $package_type Composer type of managed packages + * @param string $exception_prefix Exception prefix to use + */ + public function __construct(installer $installer, ext_manager $extension_manager, $package_type, $exception_prefix) + { + $this->extension_manager = $extension_manager; + + parent::__construct($installer, $package_type, $exception_prefix); + } + + /** + * {@inheritdoc} + */ + public function install(array $packages) + { + $packages = $this->normalize_version($packages); + + $already_managed = array_intersect(array_keys($this->get_managed_packages()), array_keys($packages)); + if (count($already_managed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); + } + + $installed_manually = array_intersect(array_keys($this->extension_manager->all_available()), array_keys($packages)); + if (count($installed_manually) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED_MANUALLY', [implode('|', array_keys($installed_manually))]); + } + + $this->do_install($packages); + } +} diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 05c3ef2d68..7256e82d3a 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -86,7 +86,8 @@ class installer { $this->generate_ext_json_file($packages); - putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . '/' . $this->packages_vendor_dir); + $original_vendor_dir = getenv('COMPOSER_VENDOR_DIR'); + putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . $this->packages_vendor_dir); $io = new BufferIO('', OutputInterface::VERBOSITY_DEBUG); $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); @@ -123,6 +124,10 @@ class installer { throw new runtime_exception('Cannot install packages', [], $e); } + finally + { + putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); + } } /** @@ -134,10 +139,12 @@ class installer */ public function get_installed_packages($type) { + $original_vendor_dir = getenv('COMPOSER_VENDOR_DIR'); + try { $io = new NullIO(); - putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . '/' . $this->packages_vendor_dir); + putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . $this->packages_vendor_dir); $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); $installed = []; @@ -157,6 +164,10 @@ class installer { return []; } + finally + { + putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); + } } /** diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 836f39b509..4ade6e24a0 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -18,7 +18,7 @@ use phpbb\composer\exception\runtime_exception; /** * Class to manage packages through composer. */ -class manager +class manager implements manager_interface { /** * @var installer Composer packages installer @@ -74,6 +74,16 @@ class manager throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); } + $this->do_install($packages); + } + + /** + * Really install the packages. + * + * @param array $packages Packages to install. + */ + protected function do_install($packages) + { $managed_packages = array_merge($this->get_managed_packages(), $packages); ksort($managed_packages); @@ -135,12 +145,12 @@ class manager /** * Tells whether or not a package is managed by Composer. * - * @param string $packages Package name + * @param string $package Package name * @return bool */ - public function is_managed($packages) + public function is_managed($package) { - return array_key_exists($packages, $this->get_managed_packages()); + return array_key_exists($package, $this->get_managed_packages()); } /** @@ -177,11 +187,15 @@ class manager { $normalized_packages = []; - foreach ($packages as $package) + foreach ($packages as $package => $version) { - if (!is_array($package)) + if (is_numeric($package)) { - $normalized_packages[$package] = '*'; + $normalized_packages[$version] = '*'; + } + else + { + $normalized_packages[$package] = $version; } } diff --git a/phpBB/phpbb/composer/manager_interface.php b/phpBB/phpbb/composer/manager_interface.php new file mode 100644 index 0000000000..d4c2b9eb51 --- /dev/null +++ b/phpBB/phpbb/composer/manager_interface.php @@ -0,0 +1,69 @@ +manager = $manager; diff --git a/phpBB/phpbb/console/command/extension/list_available.php b/phpBB/phpbb/console/command/extension/list_available.php index 0b20ce2d5e..107aca3410 100644 --- a/phpBB/phpbb/console/command/extension/list_available.php +++ b/phpBB/phpbb/console/command/extension/list_available.php @@ -17,6 +17,7 @@ use Composer\Package\CompletePackage; use Composer\Package\PackageInterface; use phpbb\composer\installer; use phpbb\composer\manager; +use phpbb\composer\manager_interface; use Symfony\Component\Console\Formatter\OutputFormatter; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -26,11 +27,11 @@ use Symfony\Component\Console\Style\SymfonyStyle; class list_available extends \phpbb\console\command\command { /** - * @var \phpbb\composer\manager Composer extensions manager + * @var manager_interface Composer extensions manager */ protected $manager; - public function __construct(\phpbb\user $user, manager $manager) + public function __construct(\phpbb\user $user, manager_interface $manager) { $this->manager = $manager; diff --git a/phpBB/phpbb/console/command/extension/remove.php b/phpBB/phpbb/console/command/extension/remove.php index a668322cdf..16a3ad263f 100644 --- a/phpBB/phpbb/console/command/extension/remove.php +++ b/phpBB/phpbb/console/command/extension/remove.php @@ -14,6 +14,7 @@ namespace phpbb\console\command\extension; use phpbb\composer\manager; +use phpbb\composer\manager_interface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -22,11 +23,11 @@ use Symfony\Component\Console\Style\SymfonyStyle; class remove extends \phpbb\console\command\command { /** - * @var \phpbb\composer\manager Composer extensions manager + * @var manager_interface Composer extensions manager */ protected $manager; - public function __construct(\phpbb\user $user, manager $manager) + public function __construct(\phpbb\user $user, manager_interface $manager) { $this->manager = $manager; diff --git a/phpBB/phpbb/console/command/extension/update.php b/phpBB/phpbb/console/command/extension/update.php index 01c9db0c28..e3f8935985 100644 --- a/phpBB/phpbb/console/command/extension/update.php +++ b/phpBB/phpbb/console/command/extension/update.php @@ -14,6 +14,7 @@ namespace phpbb\console\command\extension; use phpbb\composer\manager; +use phpbb\composer\manager_interface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -22,11 +23,11 @@ use Symfony\Component\Console\Style\SymfonyStyle; class update extends \phpbb\console\command\command { /** - * @var \phpbb\composer\manager Composer extensions manager + * @var manager_interface Composer extensions manager */ protected $manager; - public function __construct(\phpbb\user $user, manager $manager) + public function __construct(\phpbb\user $user, manager_interface $manager) { $this->manager = $manager; From 540bac3ba425196d914dce1b74df7eb2880ea00c Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Thu, 10 Sep 2015 13:11:55 +0200 Subject: [PATCH 03/85] [ticket/11150] Better version normalization PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 1 - phpBB/phpbb/composer/manager.php | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 7256e82d3a..5c3bf712e7 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -118,7 +118,6 @@ class installer // TODO Extract the precise error and use language string throw new \RuntimeException(substr($output, $error_pos)); } - } catch (\Exception $e) { diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 4ade6e24a0..6f087ea19d 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -191,7 +191,15 @@ class manager implements manager_interface { if (is_numeric($package)) { - $normalized_packages[$version] = '*'; + if (strpos($version, ':') !== false) + { + $parts = explode(':', $version); + $normalized_packages[$parts[0]] = $parts[1]; + } + else + { + $normalized_packages[$version] = '*'; + } } else { From 779c9c8552ed5bb863561a24b539ad3495018798 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Fri, 11 Sep 2015 12:11:29 +0200 Subject: [PATCH 04/85] [ticket/11150] Preserve every packages of every phpbb types in composer-ext.json PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 12 +++++++++--- phpBB/phpbb/composer/manager.php | 30 +++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 5c3bf712e7..8be6194497 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -32,6 +32,8 @@ use Symfony\Component\Console\Output\OutputInterface; */ class installer { + const PHPBB_TYPES = ['phpbb-extension', 'phpbb-style', 'phpbb-language']; + /** * @var array Repositories to look packages from */ @@ -132,12 +134,14 @@ class installer /** * Returns the list of currently installed packages * - * @param string $type Returns only the packages with the given type + * @param string|array $types Returns only the packages with the given type(s) * * @return array The installed packages associated to their version. */ - public function get_installed_packages($type) + public function get_installed_packages($types) { + $types = (array) $types; + $original_vendor_dir = getenv('COMPOSER_VENDOR_DIR'); try @@ -151,7 +155,7 @@ class installer foreach ($packages as $package) { - if ($package->getType() === $type) + if (in_array($package->getType(), $types, true)) { $installed[$package->getName()] = $package->getPrettyVersion(); } @@ -180,6 +184,8 @@ class installer { try { + $this->generate_ext_json_file($this->get_installed_packages(self::PHPBB_TYPES)); + $io = new NullIO(); $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 6f087ea19d..02ca33ecb8 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -36,10 +36,15 @@ class manager implements manager_interface protected $exception_prefix; /** - * @var array Caches the managed packages list + * @var array Caches the managed packages list (for the current type) */ private $managed_packages; + /** + * @var array Caches the managed packages list (for all phpBB types) + */ + private $all_managed_packages; + /** * @var array Caches the available packages list */ @@ -84,7 +89,7 @@ class manager implements manager_interface */ protected function do_install($packages) { - $managed_packages = array_merge($this->get_managed_packages(), $packages); + $managed_packages = array_merge($this->get_all_managed_packages(), $packages); ksort($managed_packages); $this->installer->install($managed_packages, array_keys($packages)); @@ -110,7 +115,7 @@ class manager implements manager_interface throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); } - $managed_packages = array_merge($this->get_managed_packages(), $packages); + $managed_packages = array_merge($this->get_all_managed_packages(), $packages); ksort($managed_packages); $this->installer->install($managed_packages, array_keys($packages)); @@ -134,7 +139,7 @@ class manager implements manager_interface throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); } - $managed_packages = array_diff_key($this->get_managed_packages(), $packages); + $managed_packages = array_diff_key($this->get_all_managed_packages(), $packages); ksort($managed_packages); $this->installer->install($managed_packages, array_keys($packages)); @@ -154,7 +159,7 @@ class manager implements manager_interface } /** - * Returns the list of managed packages + * Returns the list of managed packages for the current type * * @return array The managed packages associated to their version. */ @@ -168,6 +173,21 @@ class manager implements manager_interface return $this->managed_packages; } + /** + * Returns the list of managed packages for all phpBB types + * + * @return array The managed packages associated to their version. + */ + public function get_all_managed_packages() + { + if ($this->all_managed_packages === null) + { + $this->all_managed_packages = $this->installer->get_installed_packages(installer::PHPBB_TYPES); + } + + return $this->all_managed_packages; + } + /** * Returns the list of available packages * From 00229c20f00a5503ab59826ce35bc15242536696 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Fri, 11 Sep 2015 18:01:56 +0200 Subject: [PATCH 05/85] [ticket/11150] Add extension:start-managing command PHPBB3-11150 --- .../default/container/services_console.yml | 8 ++ .../default/container/services_extensions.yml | 1 + phpBB/language/en/acp/common.php | 3 + phpBB/language/en/acp/extensions.php | 10 +++ ...d_with_clean_error_exception_exception.php | 35 ++++++++ .../managed_with_enable_error_exception.php | 35 ++++++++ .../managed_with_error_exception.php | 35 ++++++++ phpBB/phpbb/composer/extension_manager.php | 76 ++++++++++++++++- phpBB/phpbb/composer/installer.php | 13 ++- phpBB/phpbb/composer/manager.php | 43 ++++------ phpBB/phpbb/composer/manager_interface.php | 20 ++++- .../command/extension/start_managing.php | 84 +++++++++++++++++++ 12 files changed, 331 insertions(+), 32 deletions(-) create mode 100644 phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php create mode 100644 phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php create mode 100644 phpBB/phpbb/composer/exception/managed_with_error_exception.php create mode 100644 phpBB/phpbb/console/command/extension/start_managing.php diff --git a/phpBB/config/default/container/services_console.yml b/phpBB/config/default/container/services_console.yml index 3305ede490..a1cc599aa7 100644 --- a/phpBB/config/default/container/services_console.yml +++ b/phpBB/config/default/container/services_console.yml @@ -183,6 +183,14 @@ services: tags: - { name: console.command } + console.command.extension.start_managing: + class: phpbb\console\command\extension\start_managing + arguments: + - @user + - @ext.composer.manager + tags: + - { name: console.command } + console.command.extension.update: class: phpbb\console\command\extension\update arguments: diff --git a/phpBB/config/default/container/services_extensions.yml b/phpBB/config/default/container/services_extensions.yml index 46953ae4a2..9e6a9fc0ec 100644 --- a/phpBB/config/default/container/services_extensions.yml +++ b/phpBB/config/default/container/services_extensions.yml @@ -22,6 +22,7 @@ services: arguments: - @ext.composer.installer - @ext.manager + - @filesystem - phpbb-extension - EXTENSIONS_ diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 9be3cc9676..537cc1a5d1 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -240,6 +240,9 @@ $lang = array_merge($lang, array( 'EXCEPTION' => 'Exception', 'COLOUR_SWATCH' => 'Web-safe colour swatch', + + 'COMPOSER_UNSUPPORTED_OPERATION' => 'Operation unsupported for the package type “%s”.', + 'CONFIG_UPDATED' => 'Configuration updated successfully.', 'CRON_LOCK_ERROR' => 'Could not obtain cron lock.', 'CRON_NO_SUCH_TASK' => 'Could not find cron task “%s”.', diff --git a/phpBB/language/en/acp/extensions.php b/phpBB/language/en/acp/extensions.php index a96a7a2a2b..a94b767111 100644 --- a/phpBB/language/en/acp/extensions.php +++ b/phpBB/language/en/acp/extensions.php @@ -35,6 +35,16 @@ if (empty($lang) || !is_array($lang)) // in a url you again do not need to specify an order e.g., 'Click %sHERE%s' is fine $lang = array_merge($lang, array( + + 'EXTENSION_ALREADY_INSTALLED' => 'The “%s” extension has already been installed.', + 'EXTENSION_ALREADY_INSTALLED_MANUALLY' => 'The “%s” extension has already been installed manually.', + 'EXTENSION_ALREADY_MANAGED' => 'The “%s” extension has already been installed manually.', + 'EXTENSION_CANNOT_MANAGE_FILESYSTEM_ERROR' => 'The “%s” extension cannot be managed because the old files cannot be removed.', + 'EXTENSION_CANNOT_MANAGE_INSTALL_ERROR' => 'The “%s” extension cannot be installed. The old extension have been restored.', + 'EXTENSION_MANAGED_WITH_CLEAN_ERROR' => 'The “%1$s” extension has been installed but an error occurred and the old files have not been removed. You might want to delete the “%2$s” files manually.', + 'EXTENSION_MANAGED_WITH_ENABLE_ERROR' => 'The “%s” extension has been installed but an error occurred when re-enabling it.', + 'EXTENSION_NOT_INSTALLED' => 'The “%s” extension is not installed.', + 'EXTENSION' => 'Extension', 'EXTENSIONS' => 'Extensions', 'EXTENSIONS_ADMIN' => 'Extensions Manager', diff --git a/phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php b/phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php new file mode 100644 index 0000000000..2339fa4096 --- /dev/null +++ b/phpBB/phpbb/composer/exception/managed_with_clean_error_exception_exception.php @@ -0,0 +1,35 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +/** + * Packaged managed with success but error occurred when cleaning the filesystem + */ +class managed_with_clean_error_exception extends managed_with_error_exception +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php b/phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php new file mode 100644 index 0000000000..7ef7a42df3 --- /dev/null +++ b/phpBB/phpbb/composer/exception/managed_with_enable_error_exception.php @@ -0,0 +1,35 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +/** + * Packaged managed with success but error occurred when re-enabling the extension + */ +class managed_with_enable_error_exception extends managed_with_error_exception +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/exception/managed_with_error_exception.php b/phpBB/phpbb/composer/exception/managed_with_error_exception.php new file mode 100644 index 0000000000..9e7c67580e --- /dev/null +++ b/phpBB/phpbb/composer/exception/managed_with_error_exception.php @@ -0,0 +1,35 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\exception; + +/** + * Packaged managed with success but errored at some point + */ +class managed_with_error_exception extends runtime_exception +{ + /** + * Constructor + * + * @param string $prefix The language string prefix + * @param string $message The Exception message to throw (must be a language variable). + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($prefix, $message = '', array $parameters = [], \Exception $previous = null, $code = 0) + { + parent::__construct($prefix . $message, $parameters, $previous, $code); + } + +} diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php index ee3ac1aeaf..360e2fca5b 100644 --- a/phpBB/phpbb/composer/extension_manager.php +++ b/phpBB/phpbb/composer/extension_manager.php @@ -13,8 +13,13 @@ namespace phpbb\composer; +use phpbb\composer\exception\managed_with_clean_error_exception; +use phpbb\composer\exception\managed_with_enable_error_exception; +use phpbb\composer\exception\managed_with_error_exception; use phpbb\composer\exception\runtime_exception; use phpbb\extension\manager as ext_manager; +use phpbb\filesystem\exception\filesystem_exception; +use phpbb\filesystem\filesystem; /** * Class to safely manage extensions through composer. @@ -26,15 +31,22 @@ class extension_manager extends manager */ protected $extension_manager; + /** + * @var \phpbb\filesystem\filesystem + */ + protected $filesystem; + /** * @param installer $installer Installer object * @param ext_manager $extension_manager phpBB extension manager + * @param filesystem $filesystem Filesystem object * @param string $package_type Composer type of managed packages * @param string $exception_prefix Exception prefix to use */ - public function __construct(installer $installer, ext_manager $extension_manager, $package_type, $exception_prefix) + public function __construct(installer $installer, ext_manager $extension_manager, filesystem $filesystem, $package_type, $exception_prefix) { $this->extension_manager = $extension_manager; + $this->filesystem = $filesystem; parent::__construct($installer, $package_type, $exception_prefix); } @@ -60,4 +72,66 @@ class extension_manager extends manager $this->do_install($packages); } + + /** + * {@inheritdoc} + */ + public function start_managing($package) + { + if (!$this->extension_manager->is_available($package)) + { + throw new runtime_exception($this->exception_prefix, 'NOT_INSTALLED', [$package]); + } + + if ($this->is_managed($package)) + { + throw new runtime_exception($this->exception_prefix, 'ALREADY_MANAGED', [$package]); + } + + $enabled = false; + if ($this->extension_manager->is_enabled($package)) + { + $enabled = true; + $this->extension_manager->disable($package); + } + + $ext_path = $this->extension_manager->get_extension_path($package); + $backup_path = rtrim($ext_path, '/') . '__backup__'; + + try + { + $this->filesystem->rename($ext_path, $backup_path); + } + catch (filesystem_exception $e) + { + throw new runtime_exception($this->exception_prefix, 'CANNOT_MANAGE_FILESYSTEM_ERROR', [$package], $e); + } + + try + { + $this->install((array) $package); + $this->filesystem->remove($backup_path); + } + catch (runtime_exception $e) + { + $this->filesystem->rename($backup_path, $ext_path); + throw new runtime_exception($this->exception_prefix, 'CANNOT_MANAGE_INSTALL_ERROR', [$package], $e); + } + catch (filesystem_exception $e) + { + throw new managed_with_clean_error_exception($this->exception_prefix, 'MANAGED_WITH_CLEAN_ERROR', [$package, $backup_path], $e); + } + + if ($enabled) + { + try + { + $this->extension_manager->enable($package); + } + catch (\Exception $e) + { + throw new managed_with_enable_error_exception($this->exception_prefix, 'MANAGED_WITH_ENABLE_ERROR', [$package], $e); + } + } + } } diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 8be6194497..94cf0c634c 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -73,6 +73,9 @@ class installer $this->packages_vendor_dir = $config['exts_composer_vendor_dir']; } + $this->repositories = ['http://phpbb.local/ext/phpbb/titania/composer/']; + $this->packagist = true; + $this->root_path = $root_path; } @@ -214,12 +217,18 @@ class installer { $packages = $repository->findPackages($package); $package = array_pop($packages); + + if (isset($available[$package->getName()])) + { + continue; + } + $available[$package->getName()] = ['name' => $package->getPrettyName()]; if ($package instanceof CompletePackage) { $available[$package->getName()]['description'] = $package->getDescription(); - $available[$package->getName()]['url'] = $package->getHomepage(); + $available[$package->getName()]['url'] = $package->getDistUrl();//getHomepage(); } } } @@ -236,7 +245,7 @@ class installer if ($package instanceof CompletePackage) { $available[$package->getName()]['description'] = $package->getDescription(); - $available[$package->getName()]['url'] = $package->getHomepage(); + $available[$package->getName()]['url'] = $package->getDistUrl();//getHomepage(); } } } diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 02ca33ecb8..ed4ce6528b 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -63,11 +63,7 @@ class manager implements manager_interface } /** - * Installs (if necessary) a set of packages - * - * @param array $packages Packages to install. - * Each entry may be a name or an array associating a version constraint to a name - * @throws runtime_exception + * {@inheritdoc} */ public function install(array $packages) { @@ -98,11 +94,7 @@ class manager implements manager_interface } /** - * Updates or installs a set of packages - * - * @param array $packages Packages to update. - * Each entry may be a name or an array associating a version constraint to a name - * @throws runtime_exception + * {@inheritdoc} */ public function update(array $packages) { @@ -122,11 +114,7 @@ class manager implements manager_interface } /** - * Removes a set of packages - * - * @param array $packages Packages to remove. - * Each entry may be a name or an array associating a version constraint to a name - * @throws runtime_exception + * {@inheritdoc} */ public function remove(array $packages) { @@ -148,10 +136,7 @@ class manager implements manager_interface } /** - * Tells whether or not a package is managed by Composer. - * - * @param string $package Package name - * @return bool + * {@inheritdoc} */ public function is_managed($package) { @@ -159,9 +144,7 @@ class manager implements manager_interface } /** - * Returns the list of managed packages for the current type - * - * @return array The managed packages associated to their version. + * {@inheritdoc} */ public function get_managed_packages() { @@ -174,9 +157,7 @@ class manager implements manager_interface } /** - * Returns the list of managed packages for all phpBB types - * - * @return array The managed packages associated to their version. + * {@inheritdoc} */ public function get_all_managed_packages() { @@ -189,9 +170,7 @@ class manager implements manager_interface } /** - * Returns the list of available packages - * - * @return array The name of the available packages, associated to their definition. Ordered by name. + * {@inheritdoc} */ public function get_available_packages() { @@ -203,6 +182,14 @@ class manager implements manager_interface return $this->available_packages; } + /** + * {@inheritdoc} + */ + public function start_managing($package) + { + throw new \phpbb\exception\runtime_exception('COMPOSER_UNSUPPORTED_OPERATION', (array) $this->package_type); + } + protected function normalize_version($packages) { $normalized_packages = []; diff --git a/phpBB/phpbb/composer/manager_interface.php b/phpBB/phpbb/composer/manager_interface.php index d4c2b9eb51..13f9498b35 100644 --- a/phpBB/phpbb/composer/manager_interface.php +++ b/phpBB/phpbb/composer/manager_interface.php @@ -54,16 +54,34 @@ interface manager_interface public function is_managed($packages); /** - * Returns the list of managed packages + * Returns the list of managed packages for the current type * * @return array The managed packages associated to their version. */ public function get_managed_packages(); + /** + * Returns the list of managed packages for all phpBB types + * + * @return array The managed packages associated to their version. + */ + public function get_all_managed_packages(); + /** * Returns the list of available packages * * @return array The name of the available packages, associated to their definition. Ordered by name. */ public function get_available_packages(); + + /** + * Start managing a manually installed package + * + * Remove a package installed manually and reinstall it using composer. + * + * @param string $package Package to manage + * + * @throws runtime_exception + */ + public function start_managing($package); } diff --git a/phpBB/phpbb/console/command/extension/start_managing.php b/phpBB/phpbb/console/command/extension/start_managing.php new file mode 100644 index 0000000000..ba21f44a70 --- /dev/null +++ b/phpBB/phpbb/console/command/extension/start_managing.php @@ -0,0 +1,84 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\console\command\extension; + +use phpbb\composer\exception\managed_with_error_exception; +use phpbb\composer\manager; +use phpbb\composer\manager_interface; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class start_managing extends \phpbb\console\command\command +{ + /** + * @var manager_interface Composer extensions manager + */ + protected $manager; + + public function __construct(\phpbb\user $user, manager_interface $manager) + { + $this->manager = $manager; + + $user->add_lang('acp/extensions'); + + parent::__construct($user); + } + + /** + * Sets the command name and description + * + * @return null + */ + protected function configure() + { + $this + ->setName('extension:start-managing') + ->setDescription($this->user->lang('CLI_DESCRIPTION_EXTENSION_START_MANAGING')) + ->addArgument( + 'extension', + InputArgument::REQUIRED, + $this->user->lang('CLI_DESCRIPTION_EXTENSION_START_MANAGING')) + ; + } + + /** + * Executes the command extension:install + * + * @param InputInterface $input + * @param OutputInterface $output + * @return integer + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $extension = $input->getArgument('extension'); + + try + { + $this->manager->start_managing($extension); + } + catch (managed_with_error_exception $e) + { + $io->warning(call_user_func_array([$this->user, 'lang'], [$e->getMessage(), $e->get_parameters()])); + return 1; + } + + $io->success('The extension ' . $extension . ' is now managed automatically.'); + + return 0; + } +} From e3fadc1fc73ae65d6503f97182455624fe5b3499 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sat, 12 Sep 2015 11:05:35 +0200 Subject: [PATCH 06/85] [ticket/11150] Display and translate composer output PHPBB3-11150 --- .../default/container/services_console.yml | 3 + phpBB/language/en/acp/common.php | 7 + phpBB/phpbb/composer/extension_manager.php | 5 +- phpBB/phpbb/composer/installer.php | 36 +-- phpBB/phpbb/composer/io/console_io.php | 217 ++++++++++++++++++ phpBB/phpbb/composer/manager.php | 18 +- phpBB/phpbb/composer/manager_interface.php | 10 +- .../console/command/extension/install.php | 18 +- .../console/command/extension/remove.php | 18 +- .../console/command/extension/update.php | 18 +- 10 files changed, 311 insertions(+), 39 deletions(-) create mode 100644 phpBB/phpbb/composer/io/console_io.php diff --git a/phpBB/config/default/container/services_console.yml b/phpBB/config/default/container/services_console.yml index a1cc599aa7..3b05ef9d46 100644 --- a/phpBB/config/default/container/services_console.yml +++ b/phpBB/config/default/container/services_console.yml @@ -154,6 +154,7 @@ services: arguments: - @user - @ext.composer.manager + - @language tags: - { name: console.command } @@ -171,6 +172,7 @@ services: arguments: - @user - @ext.composer.manager + - @language tags: - { name: console.command } @@ -188,6 +190,7 @@ services: arguments: - @user - @ext.composer.manager + - @language tags: - { name: console.command } diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 537cc1a5d1..7f341abd07 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -242,6 +242,13 @@ $lang = array_merge($lang, array( 'COLOUR_SWATCH' => 'Web-safe colour swatch', 'COMPOSER_UNSUPPORTED_OPERATION' => 'Operation unsupported for the package type “%s”.', + 'COMPOSER_UPDATING_DEPENDENCIES' => 'Updating packages', + 'COMPOSER_LOADING_REPOSITORIES' => 'Loading remote packages information', + 'COMPOSER_ERROR_CONFLICT' => 'Your requirements could not be resolved to an installable set of packages.', + 'COMPOSER_REPOSITORY_UNAVAILABLE' => 'An error occurred when fetching the repository %s.', + 'COMPOSER_INSTALLING_PACKAGE' => ' - Installing %1$s (%2$s)', + 'COMPOSER_DELETING' => ' - Deleting %s', + 'COMPOSER_UPDATE_NOTHING' => 'Nothing to update', 'CONFIG_UPDATED' => 'Configuration updated successfully.', 'CRON_LOCK_ERROR' => 'Could not obtain cron lock.', diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php index 360e2fca5b..a86b91fffa 100644 --- a/phpBB/phpbb/composer/extension_manager.php +++ b/phpBB/phpbb/composer/extension_manager.php @@ -13,6 +13,7 @@ namespace phpbb\composer; +use Composer\IO\IOInterface; use phpbb\composer\exception\managed_with_clean_error_exception; use phpbb\composer\exception\managed_with_enable_error_exception; use phpbb\composer\exception\managed_with_error_exception; @@ -54,7 +55,7 @@ class extension_manager extends manager /** * {@inheritdoc} */ - public function install(array $packages) + public function install(array $packages, IOInterface $io = null) { $packages = $this->normalize_version($packages); @@ -70,7 +71,7 @@ class extension_manager extends manager throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED_MANUALLY', [implode('|', array_keys($installed_manually))]); } - $this->do_install($packages); + $this->do_install($packages, $io); } /** diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 94cf0c634c..498fb6cdcb 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -15,7 +15,7 @@ namespace phpbb\composer; use Composer\Composer; use Composer\Factory; -use Composer\IO\BufferIO; +use Composer\IO\IOInterface; use Composer\IO\NullIO; use Composer\Json\JsonFile; use Composer\Package\CompletePackage; @@ -25,7 +25,6 @@ use Composer\Repository\RepositoryInterface; use Composer\Util\RemoteFilesystem; use phpbb\config\config; use phpbb\exception\runtime_exception; -use Symfony\Component\Console\Output\OutputInterface; /** * Class to install packages through composer while freezing core dependencies. @@ -85,19 +84,27 @@ class installer * @param array $packages Packages to install. * Each entry may be a name or an array associating a version constraint to a name * @param array $whitelist White-listed packages (packages that can be installed/updated/removed) + * @param IOInterface $io IO object used for the output + * * @throws runtime_exception */ - public function install(array $packages, $whitelist) + public function install(array $packages, $whitelist, IOInterface $io = null) { + if (!$io) + { + $io = new NullIO(); + } + $this->generate_ext_json_file($packages); $original_vendor_dir = getenv('COMPOSER_VENDOR_DIR'); putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . $this->packages_vendor_dir); - $io = new BufferIO('', OutputInterface::VERBOSITY_DEBUG); $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); $install = \Composer\Installer::create($io, $composer); + $composer->getDownloadManager()->setOutputProgress(false); + $install ->setVerbose(true) ->setPreferSource(false) @@ -112,17 +119,12 @@ class installer ->setRunScripts(false) ->setDryRun(false); + $result = 0; try { - $install->run(); - $output = $io->getOutput(); - $error_pos = strpos($output, 'Your requirements could not be resolved to an installable set of packages.'); - - if ($error_pos) - { - // TODO Extract the precise error and use language string - throw new \RuntimeException(substr($output, $error_pos)); - } + $result = $install->run(); + //$output = $io->getOutput(); + //$error_pos = strpos($output, 'Your requirements could not be resolved to an installable set of packages.'); } catch (\Exception $e) { @@ -132,6 +134,11 @@ class installer { putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); } + + if ($result !== 0) + { + throw new runtime_exception($io->get_composer_error(), []); + } } /** @@ -154,7 +161,7 @@ class installer $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); $installed = []; - $packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + $packages = $composer->getPackage()->getRequires();//$composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); foreach ($packages as $package) { @@ -282,6 +289,7 @@ class installer $packages), 'replace' => $core_packages, 'repositories' => $this->get_composer_repositories(), + 'minimum-stability' => 'dev', ]; $json_file = new JsonFile($this->get_composer_ext_json_filename()); diff --git a/phpBB/phpbb/composer/io/console_io.php b/phpBB/phpbb/composer/io/console_io.php new file mode 100644 index 0000000000..bf1754d4c0 --- /dev/null +++ b/phpBB/phpbb/composer/io/console_io.php @@ -0,0 +1,217 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Composer\IO\ConsoleIO; +use phpbb\language\language; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class console_io extends ConsoleIO +{ + /** + * @var language + */ + protected $language; + + /** + * @var array + */ + protected $composer_error; + + /** + * Constructor. + * + * @param InputInterface $input The input instance + * @param OutputInterface $output The output instance + * @param HelperSet $helperSet The helperSet instance + */ + public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet, language $language) + { + $this->language = $language; + + parent::__construct($input, $output, $helperSet); + } + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = true) + { + $messages = (array) $messages; + $translated_messages = []; + + foreach ($messages as $message) + { + $lang_key = $message; + $parameters = []; + $level = 0; + + $message = trim(strip_tags($message), "\n\r"); + + if (strpos($message, 'Deleting ') === 0) + { + $elements = explode(' ', $message); + $lang_key = 'COMPOSER_DELETING'; + $parameters = [$elements[1]]; + } + + //$translated_message = $this->language->lang_array($lang_key, $parameters); + $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); + + switch ($level) + { + case 1: + $translated_message = '' . $translated_message . ''; + break; + case 2: + $translated_message = '' . $translated_message . ''; + break; + case 3: + $translated_message = '' . $translated_message . ''; + break; + case 4: + $translated_message = '' . $translated_message . ''; + break; + } + + $translated_messages[] = $translated_message; + } + + parent::write($translated_messages, $newline); + } + + /** + * {@inheritdoc} + */ + public function writeError($messages, $newline = true) + { + $messages = (array) $messages; + $translated_messages = []; + + foreach ($messages as $message) + { + $lang_key = $message; + $parameters = []; + $level = 0; + + $message = trim(strip_tags($message), "\n\r"); + + if (strpos($message, ' Problem ') === 0) + { + if ($this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) + { + continue; + } + + $lang_key = "\n" . $message . "\n"; + $level = 4; + } + else if ($message === 'Updating dependencies') + { + $lang_key = 'COMPOSER_UPDATING_DEPENDENCIES'; + $level = 1; + } + else if ($message === 'Loading composer repositories with package information') + { + $lang_key = 'COMPOSER_LOADING_REPOSITORIES'; + $level = 1; + } + else if ($message === 'Your requirements could not be resolved to an installable set of packages.') + { + $this->composer_error[] = ['COMPOSER_ERROR_CONFLICT', []]; + continue; + } + else if (strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date') !== false) + { + $end_repo = strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date'); + $repo = substr($message, 0, $end_repo - 1); + + $lang_key = 'COMPOSER_REPOSITORY_UNAVAILABLE'; + $parameters = [$repo]; + $level = 3; + } + else if (strpos($message, 'file could not be downloaded') !== false) + { + continue; + } + else if (strpos($message, ' - Installing ') === 0) + { + $elements = explode(' ', $message); + $lang_key = 'COMPOSER_INSTALLING_PACKAGE'; + $parameters = [$elements[4], trim($elements[5], '()')]; + } + else if ($message === 'Nothing to install or update') + { + $lang_key = 'COMPOSER_UPDATE_NOTHING'; + $level = 3; + } + else if ($message === ' Downloading') + { + continue; + } + else if ($message === ' Loading from cache') + { + continue; + } + else if ($message === 'Writing lock file') + { + continue; + } + else if (empty($message)) + { + continue; + } + + //$translated_message = $this->language->lang_array($lang_key, $parameters); + $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); + + switch ($level) + { + case 1: + $translated_message = '' . $translated_message . ''; + break; + case 2: + $translated_message = '' . $translated_message . ''; + break; + case 3: + $translated_message = '' . $translated_message . ''; + break; + case 4: + $translated_message = '' . $translated_message . ''; + break; + } + + $translated_messages[] = $translated_message; + } + + parent::writeError($translated_messages, $newline); + } + + public function get_composer_error() + { + $error = ''; + foreach ($this->composer_error as $error_line) + { + // $error .= $this->language->lang_array($error_line[0], $error_line[1]); + $error .= call_user_func_array([$this->language, 'lang'], array_merge((array)$error_line[0], $error_line[1])); + $error .= "\n"; + } + + $this->composer_error = []; + + return $error; + } +} diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index ed4ce6528b..5d942c11d0 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -13,6 +13,7 @@ namespace phpbb\composer; +use Composer\IO\IOInterface; use phpbb\composer\exception\runtime_exception; /** @@ -65,7 +66,7 @@ class manager implements manager_interface /** * {@inheritdoc} */ - public function install(array $packages) + public function install(array $packages, IOInterface $io = null) { $packages = $this->normalize_version($packages); @@ -75,20 +76,21 @@ class manager implements manager_interface throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); } - $this->do_install($packages); + $this->do_install($packages, $io); } /** * Really install the packages. * * @param array $packages Packages to install. + * @param IOInterface $io IO object used for the output */ - protected function do_install($packages) + protected function do_install($packages, IOInterface $io = null) { $managed_packages = array_merge($this->get_all_managed_packages(), $packages); ksort($managed_packages); - $this->installer->install($managed_packages, array_keys($packages)); + $this->installer->install($managed_packages, array_keys($packages), $io); $this->managed_packages = null; } @@ -96,7 +98,7 @@ class manager implements manager_interface /** * {@inheritdoc} */ - public function update(array $packages) + public function update(array $packages, IOInterface $io = null) { $packages = $this->normalize_version($packages); @@ -110,13 +112,13 @@ class manager implements manager_interface $managed_packages = array_merge($this->get_all_managed_packages(), $packages); ksort($managed_packages); - $this->installer->install($managed_packages, array_keys($packages)); + $this->installer->install($managed_packages, array_keys($packages), $io); } /** * {@inheritdoc} */ - public function remove(array $packages) + public function remove(array $packages, IOInterface $io = null) { $packages = $this->normalize_version($packages); @@ -130,7 +132,7 @@ class manager implements manager_interface $managed_packages = array_diff_key($this->get_all_managed_packages(), $packages); ksort($managed_packages); - $this->installer->install($managed_packages, array_keys($packages)); + $this->installer->install($managed_packages, array_keys($packages), $io); $this->managed_packages = null; } diff --git a/phpBB/phpbb/composer/manager_interface.php b/phpBB/phpbb/composer/manager_interface.php index 13f9498b35..b5d0177342 100644 --- a/phpBB/phpbb/composer/manager_interface.php +++ b/phpBB/phpbb/composer/manager_interface.php @@ -6,6 +6,7 @@ * Time: 23:12 */ namespace phpbb\composer; +use Composer\IO\IOInterface; use phpbb\composer\exception\runtime_exception; @@ -19,30 +20,33 @@ interface manager_interface * * @param array $packages Packages to install. * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output * * @throws runtime_exception */ - public function install(array $packages); + public function install(array $packages, IOInterface $io = null); /** * Updates or installs a set of packages * * @param array $packages Packages to update. * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output * * @throws runtime_exception */ - public function update(array $packages); + public function update(array $packages, IOInterface $io = null); /** * Removes a set of packages * * @param array $packages Packages to remove. * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output * * @throws runtime_exception */ - public function remove(array $packages); + public function remove(array $packages, IOInterface $io = null); /** * Tells whether or not a package is managed by Composer. diff --git a/phpBB/phpbb/console/command/extension/install.php b/phpBB/phpbb/console/command/extension/install.php index 4d83fe2cef..aa9d5a6867 100644 --- a/phpBB/phpbb/console/command/extension/install.php +++ b/phpBB/phpbb/console/command/extension/install.php @@ -13,8 +13,10 @@ namespace phpbb\console\command\extension; -use phpbb\composer\manager; +use phpbb\composer\io\console_io; use phpbb\composer\manager_interface; +use phpbb\language\language; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -27,9 +29,15 @@ class install extends \phpbb\console\command\command */ protected $manager; - public function __construct(\phpbb\user $user, manager_interface $manager) + /** + * @var \phpbb\language\language + */ + protected $language; + + public function __construct(\phpbb\user $user, manager_interface $manager, language $language) { $this->manager = $manager; + $this->language = $language; parent::__construct($user); } @@ -60,11 +68,13 @@ class install extends \phpbb\console\command\command */ protected function execute(InputInterface $input, OutputInterface $output) { - $io = new SymfonyStyle($input, $output); + $output->getFormatter()->setStyle('warning', new OutputFormatterStyle('black', 'yellow')); + $io = new SymfonyStyle($input, $output); + $composer_io = new console_io($input, $output, $this->getHelperSet(), $this->language); $extensions = $input->getArgument('extensions'); - $this->manager->install($extensions); + $this->manager->install($extensions, $composer_io); $io->success('All extensions installed'); diff --git a/phpBB/phpbb/console/command/extension/remove.php b/phpBB/phpbb/console/command/extension/remove.php index 16a3ad263f..d1f59dec5c 100644 --- a/phpBB/phpbb/console/command/extension/remove.php +++ b/phpBB/phpbb/console/command/extension/remove.php @@ -13,8 +13,10 @@ namespace phpbb\console\command\extension; -use phpbb\composer\manager; +use phpbb\composer\io\console_io; use phpbb\composer\manager_interface; +use phpbb\language\language; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -27,9 +29,15 @@ class remove extends \phpbb\console\command\command */ protected $manager; - public function __construct(\phpbb\user $user, manager_interface $manager) + /** + * @var \phpbb\language\language + */ + protected $language; + + public function __construct(\phpbb\user $user, manager_interface $manager, language $language) { $this->manager = $manager; + $this->language = $language; parent::__construct($user); } @@ -60,11 +68,13 @@ class remove extends \phpbb\console\command\command */ protected function execute(InputInterface $input, OutputInterface $output) { - $io = new SymfonyStyle($input, $output); + $output->getFormatter()->setStyle('warning', new OutputFormatterStyle('black', 'yellow')); + $io = new SymfonyStyle($input, $output); + $composer_io = new console_io($input, $output, $this->getHelperSet(), $this->language); $extensions = $input->getArgument('extensions'); - $this->manager->remove($extensions); + $this->manager->remove($extensions, $composer_io); $io->success('All extensions removed'); diff --git a/phpBB/phpbb/console/command/extension/update.php b/phpBB/phpbb/console/command/extension/update.php index e3f8935985..7a38294ed4 100644 --- a/phpBB/phpbb/console/command/extension/update.php +++ b/phpBB/phpbb/console/command/extension/update.php @@ -13,8 +13,10 @@ namespace phpbb\console\command\extension; -use phpbb\composer\manager; +use phpbb\composer\io\console_io; use phpbb\composer\manager_interface; +use phpbb\language\language; +use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -27,9 +29,15 @@ class update extends \phpbb\console\command\command */ protected $manager; - public function __construct(\phpbb\user $user, manager_interface $manager) + /** + * @var \phpbb\language\language + */ + protected $language; + + public function __construct(\phpbb\user $user, manager_interface $manager, language $language) { $this->manager = $manager; + $this->language = $language; parent::__construct($user); } @@ -60,11 +68,13 @@ class update extends \phpbb\console\command\command */ protected function execute(InputInterface $input, OutputInterface $output) { - $io = new SymfonyStyle($input, $output); + $output->getFormatter()->setStyle('warning', new OutputFormatterStyle('black', 'yellow')); + $io = new SymfonyStyle($input, $output); + $composer_io = new console_io($input, $output, $this->getHelperSet(), $this->language); $extensions = $input->getArgument('extensions'); - $this->manager->update($extensions); + $this->manager->update($extensions, $composer_io); $io->success('All extensions updated'); From 8a1ca4375e752ca764e8dbcb156f744efecafcf2 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sat, 12 Sep 2015 16:05:19 +0200 Subject: [PATCH 07/85] [ticket/11150] Preserve original requirements and avoid finally PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 498fb6cdcb..72378cfa73 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -119,20 +119,19 @@ class installer ->setRunScripts(false) ->setDryRun(false); - $result = 0; try { $result = $install->run(); + + putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); //$output = $io->getOutput(); //$error_pos = strpos($output, 'Your requirements could not be resolved to an installable set of packages.'); } catch (\Exception $e) { - throw new runtime_exception('Cannot install packages', [], $e); - } - finally - { + putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); + throw new runtime_exception('Cannot install packages', [], $e); } if ($result !== 0) @@ -161,25 +160,24 @@ class installer $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); $installed = []; - $packages = $composer->getPackage()->getRequires();//$composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + $required_links = $composer->getPackage()->getRequires(); + $installed_packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); - foreach ($packages as $package) + foreach ($installed_packages as $package) { - if (in_array($package->getType(), $types, true)) + if (array_key_exists($package->getName(), $required_links) && in_array($package->getType(), $types, true)) { - $installed[$package->getName()] = $package->getPrettyVersion(); + $installed[$package->getName()] = $required_links[$package->getName()]->getPrettyConstraint(); } } + putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); return $installed; } catch (\Exception $e) - { - return []; - } - finally { putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); + return []; } } From 8b1284594ba8fac8e9247d3eb1af100c4b06e881 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sat, 12 Sep 2015 16:55:03 +0200 Subject: [PATCH 08/85] [ticket/11150] Use a trait to translate composer and introduce web_io PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 1 - phpBB/phpbb/composer/io/console_io.php | 180 +--------------- .../composer/io/translate_composer_trait.php | 200 ++++++++++++++++++ phpBB/phpbb/composer/io/web_io.php | 39 ++++ 4 files changed, 240 insertions(+), 180 deletions(-) create mode 100644 phpBB/phpbb/composer/io/translate_composer_trait.php create mode 100644 phpBB/phpbb/composer/io/web_io.php diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 72378cfa73..7731d5c074 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -287,7 +287,6 @@ class installer $packages), 'replace' => $core_packages, 'repositories' => $this->get_composer_repositories(), - 'minimum-stability' => 'dev', ]; $json_file = new JsonFile($this->get_composer_ext_json_filename()); diff --git a/phpBB/phpbb/composer/io/console_io.php b/phpBB/phpbb/composer/io/console_io.php index bf1754d4c0..33ac77ba4e 100644 --- a/phpBB/phpbb/composer/io/console_io.php +++ b/phpBB/phpbb/composer/io/console_io.php @@ -21,15 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface; class console_io extends ConsoleIO { - /** - * @var language - */ - protected $language; - - /** - * @var array - */ - protected $composer_error; + use translate_composer_trait; /** * Constructor. @@ -44,174 +36,4 @@ class console_io extends ConsoleIO parent::__construct($input, $output, $helperSet); } - - /** - * {@inheritdoc} - */ - public function write($messages, $newline = true) - { - $messages = (array) $messages; - $translated_messages = []; - - foreach ($messages as $message) - { - $lang_key = $message; - $parameters = []; - $level = 0; - - $message = trim(strip_tags($message), "\n\r"); - - if (strpos($message, 'Deleting ') === 0) - { - $elements = explode(' ', $message); - $lang_key = 'COMPOSER_DELETING'; - $parameters = [$elements[1]]; - } - - //$translated_message = $this->language->lang_array($lang_key, $parameters); - $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); - - switch ($level) - { - case 1: - $translated_message = '' . $translated_message . ''; - break; - case 2: - $translated_message = '' . $translated_message . ''; - break; - case 3: - $translated_message = '' . $translated_message . ''; - break; - case 4: - $translated_message = '' . $translated_message . ''; - break; - } - - $translated_messages[] = $translated_message; - } - - parent::write($translated_messages, $newline); - } - - /** - * {@inheritdoc} - */ - public function writeError($messages, $newline = true) - { - $messages = (array) $messages; - $translated_messages = []; - - foreach ($messages as $message) - { - $lang_key = $message; - $parameters = []; - $level = 0; - - $message = trim(strip_tags($message), "\n\r"); - - if (strpos($message, ' Problem ') === 0) - { - if ($this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) - { - continue; - } - - $lang_key = "\n" . $message . "\n"; - $level = 4; - } - else if ($message === 'Updating dependencies') - { - $lang_key = 'COMPOSER_UPDATING_DEPENDENCIES'; - $level = 1; - } - else if ($message === 'Loading composer repositories with package information') - { - $lang_key = 'COMPOSER_LOADING_REPOSITORIES'; - $level = 1; - } - else if ($message === 'Your requirements could not be resolved to an installable set of packages.') - { - $this->composer_error[] = ['COMPOSER_ERROR_CONFLICT', []]; - continue; - } - else if (strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date') !== false) - { - $end_repo = strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date'); - $repo = substr($message, 0, $end_repo - 1); - - $lang_key = 'COMPOSER_REPOSITORY_UNAVAILABLE'; - $parameters = [$repo]; - $level = 3; - } - else if (strpos($message, 'file could not be downloaded') !== false) - { - continue; - } - else if (strpos($message, ' - Installing ') === 0) - { - $elements = explode(' ', $message); - $lang_key = 'COMPOSER_INSTALLING_PACKAGE'; - $parameters = [$elements[4], trim($elements[5], '()')]; - } - else if ($message === 'Nothing to install or update') - { - $lang_key = 'COMPOSER_UPDATE_NOTHING'; - $level = 3; - } - else if ($message === ' Downloading') - { - continue; - } - else if ($message === ' Loading from cache') - { - continue; - } - else if ($message === 'Writing lock file') - { - continue; - } - else if (empty($message)) - { - continue; - } - - //$translated_message = $this->language->lang_array($lang_key, $parameters); - $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); - - switch ($level) - { - case 1: - $translated_message = '' . $translated_message . ''; - break; - case 2: - $translated_message = '' . $translated_message . ''; - break; - case 3: - $translated_message = '' . $translated_message . ''; - break; - case 4: - $translated_message = '' . $translated_message . ''; - break; - } - - $translated_messages[] = $translated_message; - } - - parent::writeError($translated_messages, $newline); - } - - public function get_composer_error() - { - $error = ''; - foreach ($this->composer_error as $error_line) - { - // $error .= $this->language->lang_array($error_line[0], $error_line[1]); - $error .= call_user_func_array([$this->language, 'lang'], array_merge((array)$error_line[0], $error_line[1])); - $error .= "\n"; - } - - $this->composer_error = []; - - return $error; - } } diff --git a/phpBB/phpbb/composer/io/translate_composer_trait.php b/phpBB/phpbb/composer/io/translate_composer_trait.php new file mode 100644 index 0000000000..376ef28a80 --- /dev/null +++ b/phpBB/phpbb/composer/io/translate_composer_trait.php @@ -0,0 +1,200 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use phpbb\language\language; +use Symfony\Component\Console\Output\OutputInterface; + +trait translate_composer_trait +{ + /** + * @var language + */ + protected $language; + + /** + * @var array + */ + protected $composer_error; + + /** + * {@inheritdoc} + */ + public function write($messages, $newline = true) + { + $messages = (array) $messages; + $translated_messages = []; + + foreach ($messages as $message) + { + $lang_key = $message; + $parameters = []; + $level = 0; + + $message = trim(strip_tags($message), "\n\r"); + + if (strpos($message, 'Deleting ') === 0) + { + $elements = explode(' ', $message); + $lang_key = 'COMPOSER_DELETING'; + $parameters = [$elements[1]]; + } + + //$translated_message = $this->language->lang_array($lang_key, $parameters); + $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); + + switch ($level) + { + case 1: + $translated_message = '' . $translated_message . ''; + break; + case 2: + $translated_message = '' . $translated_message . ''; + break; + case 3: + $translated_message = '' . $translated_message . ''; + break; + case 4: + $translated_message = '' . $translated_message . ''; + break; + } + + $translated_messages[] = $translated_message; + } + + parent::write($translated_messages, $newline); + } + + /** + * {@inheritdoc} + */ + public function writeError($messages, $newline = true) + { + $messages = (array) $messages; + $translated_messages = []; + + foreach ($messages as $message) + { + $lang_key = $message; + $parameters = []; + $level = 0; + + $message = trim(strip_tags($message), "\n\r"); + + if (strpos($message, ' Problem ') === 0) + { + if ($this->output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) + { + continue; + } + + $lang_key = "\n" . $message . "\n"; + $level = 4; + } + else if ($message === 'Updating dependencies') + { + $lang_key = 'COMPOSER_UPDATING_DEPENDENCIES'; + $level = 1; + } + else if ($message === 'Loading composer repositories with package information') + { + $lang_key = 'COMPOSER_LOADING_REPOSITORIES'; + $level = 1; + } + else if ($message === 'Your requirements could not be resolved to an installable set of packages.') + { + $this->composer_error[] = ['COMPOSER_ERROR_CONFLICT', []]; + continue; + } + else if (strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date') !== false) + { + $end_repo = strpos($message, 'could not be fully loaded, package information was loaded from the local cache and may be out of date'); + $repo = substr($message, 0, $end_repo - 1); + + $lang_key = 'COMPOSER_REPOSITORY_UNAVAILABLE'; + $parameters = [$repo]; + $level = 3; + } + else if (strpos($message, 'file could not be downloaded') !== false) + { + continue; + } + else if (strpos($message, ' - Installing ') === 0) + { + $elements = explode(' ', $message); + $lang_key = 'COMPOSER_INSTALLING_PACKAGE'; + $parameters = [$elements[4], trim($elements[5], '()')]; + } + else if ($message === 'Nothing to install or update') + { + $lang_key = 'COMPOSER_UPDATE_NOTHING'; + $level = 3; + } + else if ($message === ' Downloading') + { + continue; + } + else if ($message === ' Loading from cache') + { + continue; + } + else if ($message === 'Writing lock file') + { + continue; + } + else if (empty($message)) + { + continue; + } + + //$translated_message = $this->language->lang_array($lang_key, $parameters); + $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); + + switch ($level) + { + case 1: + $translated_message = '' . $translated_message . ''; + break; + case 2: + $translated_message = '' . $translated_message . ''; + break; + case 3: + $translated_message = '' . $translated_message . ''; + break; + case 4: + $translated_message = '' . $translated_message . ''; + break; + } + + $translated_messages[] = $translated_message; + } + + parent::writeError($translated_messages, $newline); + } + + public function get_composer_error() + { + $error = ''; + foreach ($this->composer_error as $error_line) + { + // $error .= $this->language->lang_array($error_line[0], $error_line[1]); + $error .= call_user_func_array([$this->language, 'lang'], array_merge((array)$error_line[0], $error_line[1])); + $error .= "\n"; + } + + $this->composer_error = []; + + return $error; + } +} diff --git a/phpBB/phpbb/composer/io/web_io.php b/phpBB/phpbb/composer/io/web_io.php new file mode 100644 index 0000000000..9e8aa6ec76 --- /dev/null +++ b/phpBB/phpbb/composer/io/web_io.php @@ -0,0 +1,39 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\composer\io; + +use Composer\IO\BufferIO; +use phpbb\language\language; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class web_io extends BufferIO +{ + use translate_composer_trait; + + /** + * Constructor. + * + * @param InputInterface $input The input instance + * @param OutputInterface $output The output instance + * @param HelperSet $helperSet The helperSet instance + */ + public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet, language $language) + { + $this->language = $language; + + parent::__construct($input, $output, $helperSet); + } +} From c49cd29e969364ea0dfeeb5c78c131562830cbe9 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sat, 12 Sep 2015 17:07:28 +0200 Subject: [PATCH 09/85] [ticket/11150] Cache the list of available packages for 24 hours PHPBB3-11150 --- .../default/container/services_extensions.yml | 3 +++ phpBB/phpbb/composer/extension_manager.php | 16 +++++++------ phpBB/phpbb/composer/installer.php | 6 ++--- phpBB/phpbb/composer/manager.php | 23 +++++++++++++++---- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/phpBB/config/default/container/services_extensions.yml b/phpBB/config/default/container/services_extensions.yml index 9e6a9fc0ec..77ff0d2ab9 100644 --- a/phpBB/config/default/container/services_extensions.yml +++ b/phpBB/config/default/container/services_extensions.yml @@ -21,6 +21,7 @@ services: class: phpbb\composer\extension_manager arguments: - @ext.composer.installer + - @cache.driver - @ext.manager - @filesystem - phpbb-extension @@ -30,6 +31,7 @@ services: class: phpbb\composer\manager arguments: - @ext.composer.installer + - @cache.driver - phpbb-style - STYLES_ @@ -37,5 +39,6 @@ services: class: phpbb\composer\manager arguments: - @ext.composer.installer + - @cache.driver - phpbb-language - LANGUAGES_ diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php index a86b91fffa..04c0af666f 100644 --- a/phpBB/phpbb/composer/extension_manager.php +++ b/phpBB/phpbb/composer/extension_manager.php @@ -14,6 +14,7 @@ namespace phpbb\composer; use Composer\IO\IOInterface; +use phpbb\cache\driver\driver_interface; use phpbb\composer\exception\managed_with_clean_error_exception; use phpbb\composer\exception\managed_with_enable_error_exception; use phpbb\composer\exception\managed_with_error_exception; @@ -38,18 +39,19 @@ class extension_manager extends manager protected $filesystem; /** - * @param installer $installer Installer object - * @param ext_manager $extension_manager phpBB extension manager - * @param filesystem $filesystem Filesystem object - * @param string $package_type Composer type of managed packages - * @param string $exception_prefix Exception prefix to use + * @param installer $installer Installer object + * @param driver_interface $cache Cache object + * @param ext_manager $extension_manager phpBB extension manager + * @param filesystem $filesystem Filesystem object + * @param string $package_type Composer type of managed packages + * @param string $exception_prefix Exception prefix to use */ - public function __construct(installer $installer, ext_manager $extension_manager, filesystem $filesystem, $package_type, $exception_prefix) + public function __construct(installer $installer, driver_interface $cache, ext_manager $extension_manager, filesystem $filesystem, $package_type, $exception_prefix) { $this->extension_manager = $extension_manager; $this->filesystem = $filesystem; - parent::__construct($installer, $package_type, $exception_prefix); + parent::__construct($installer, $cache, $package_type, $exception_prefix); } /** diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 7731d5c074..73d90c6658 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -233,7 +233,7 @@ class installer if ($package instanceof CompletePackage) { $available[$package->getName()]['description'] = $package->getDescription(); - $available[$package->getName()]['url'] = $package->getDistUrl();//getHomepage(); + $available[$package->getName()]['url'] = $package->getHomepage(); } } } @@ -245,12 +245,12 @@ class installer { if ($package->getType() === $type) { - $available[$package->getName()] = ['name' => $package]; + $available[$package->getName()] = ['name' => $package->getPrettyName()]; if ($package instanceof CompletePackage) { $available[$package->getName()]['description'] = $package->getDescription(); - $available[$package->getName()]['url'] = $package->getDistUrl();//getHomepage(); + $available[$package->getName()]['url'] = $package->getHomepage(); } } } diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 5d942c11d0..f05fec4957 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -14,6 +14,7 @@ namespace phpbb\composer; use Composer\IO\IOInterface; +use phpbb\cache\driver\driver_interface; use phpbb\composer\exception\runtime_exception; /** @@ -26,6 +27,11 @@ class manager implements manager_interface */ protected $installer; + /** + * @var driver_interface Cache instance + */ + protected $cache; + /** * @var string Type of packages (phpbb-packages per example) */ @@ -52,13 +58,15 @@ class manager implements manager_interface private $available_packages; /** - * @param installer $installer Installer object - * @param string $package_type Composer type of managed packages - * @param string $exception_prefix Exception prefix to use + * @param installer $installer Installer object + * @param driver_interface $cache Cache object + * @param string $package_type Composer type of managed packages + * @param string $exception_prefix Exception prefix to use */ - public function __construct(installer $installer, $package_type, $exception_prefix) + public function __construct(installer $installer, driver_interface $cache, $package_type, $exception_prefix) { $this->installer = $installer; + $this->cache = $cache; $this->package_type = $package_type; $this->exception_prefix = $exception_prefix; } @@ -178,7 +186,12 @@ class manager implements manager_interface { if ($this->available_packages === null) { - $this->available_packages = $this->installer->get_available_packages($this->package_type); + $this->available_packages = $this->cache->get('_composer_' . $this->package_type . '_available'); + if ($this->available_packages === false) + { + $this->available_packages = $this->installer->get_available_packages($this->package_type); + $this->cache->put('_composer_' . $this->package_type . '_available', $this->available_packages, 24*60*60); + } } return $this->available_packages; From adb8d30d98d56d123692066cbd9f0ba63326f0d7 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sun, 13 Sep 2015 10:48:30 +0200 Subject: [PATCH 10/85] [ticket/11150] Add ACP gallery PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 57 ++++++ phpBB/includes/acp/acp_extensions.php | 38 +++- phpBB/includes/acp/info/acp_extensions.php | 1 + phpBB/language/en/acp/common.php | 1 + phpBB/phpbb/composer/installer.php | 168 ++++++++++++++---- phpBB/phpbb/composer/manager.php | 2 +- .../command/extension/list_available.php | 3 +- .../data/v320/extensions_composer.php | 8 + 8 files changed, 237 insertions(+), 41 deletions(-) create mode 100644 phpBB/adm/style/acp_ext_gallery.html diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html new file mode 100644 index 0000000000..9be59429f6 --- /dev/null +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -0,0 +1,57 @@ + + + + +

{{lang( 'EXTENSIONS_ADMIN') }}

+ +

{{lang( 'EXTENSIONS_EXPLAIN') }}

+ +
+ {{ lang('BROWSE_EXTENSIONS_DATABASE') }}{{ lang('SETTINGS') }} +
+ + + + + + + + + + + + + + {% for extension in extensions %} + + + + + + {% endfor %} + +
{{ lang("EXTENSION_NAME") }}{{ lang("VERSION") }}{{ lang("DESCRIPTION") }}
+ {{ extension.name }}
+ {{ lang('DETAILS') }}{{ lang('INSTALL') }} +
{{ extension.version }}{{ extension.description }}
+ + diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index c08674841c..01e0b5e245 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -28,10 +28,13 @@ class acp_extensions var $tpl_name; var $page_title; + private $db; private $config; private $template; private $user; private $log; + + /** @var \phpbb\request\request */ private $request; private $phpbb_dispatcher; private $ext_manager; @@ -39,8 +42,10 @@ class acp_extensions function main() { // Start the page - global $config, $user, $template, $request, $phpbb_extension_manager, $phpbb_root_path, $phpbb_log, $phpbb_dispatcher; + global $config, $user, $template, $request, $phpbb_extension_manager, $db, $phpbb_root_path, $phpbb_log, $phpbb_dispatcher; + + $this->db = $db; $this->config = $config; $this->template = $template; $this->user = $user; @@ -49,7 +54,22 @@ class acp_extensions $this->phpbb_dispatcher = $phpbb_dispatcher; $this->ext_manager = $phpbb_extension_manager; - $this->user->add_lang(array('install', 'acp/extensions', 'migrator')); + $this->user->add_lang(['install', 'acp/extensions', 'migrator']); + + switch ($mode) + { + case 'gallery': + $this->gallery_mode(); + break; + default: + $this->main_mode(); + break; + } + } + + public function main_mode() + { + global $phpbb_extension_manager, $phpbb_root_path; $this->page_title = 'ACP_EXTENSIONS'; @@ -381,6 +401,20 @@ class acp_extensions $this->tpl_name = $tpl_name; } + public function gallery_mode() + { + global $phpbb_container; + + /** @var \phpbb\composer\extension_manager $manager */ + $manager = $phpbb_container->get('ext.composer.manager'); + $this->page_title = 'ACP_EXTENSIONS_GALLERY'; + $this->tpl_name = 'acp_ext_gallery'; + + $this->request->enable_super_globals(); + $this->template->assign_var('extensions', $manager->get_available_packages()); + $this->request->disable_super_globals(); + } + /** * Lists all the enabled extensions and dumps to the template * diff --git a/phpBB/includes/acp/info/acp_extensions.php b/phpBB/includes/acp/info/acp_extensions.php index 9adcd543b9..5d9e420fda 100644 --- a/phpBB/includes/acp/info/acp_extensions.php +++ b/phpBB/includes/acp/info/acp_extensions.php @@ -20,6 +20,7 @@ class acp_extensions_info 'title' => 'ACP_EXTENSION_MANAGEMENT', 'modes' => array( 'main' => array('title' => 'ACP_EXTENSIONS', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSION_MANAGEMENT')), + 'gallery' => array('title' => 'ACP_EXTENSIONS_GALLERY', 'auth' => 'acl_a_extensions', 'cat' => array('ACP_EXTENSION_MANAGEMENT')), ), ); } diff --git a/phpBB/language/en/acp/common.php b/phpBB/language/en/acp/common.php index 7f341abd07..40b4a5d9d4 100644 --- a/phpBB/language/en/acp/common.php +++ b/phpBB/language/en/acp/common.php @@ -88,6 +88,7 @@ $lang = array_merge($lang, array( 'ACP_EXTENSION_GROUPS' => 'Manage attachment extension groups', 'ACP_EXTENSION_MANAGEMENT' => 'Extension management', 'ACP_EXTENSIONS' => 'Manage extensions', + 'ACP_EXTENSIONS_GALLERY' => 'Extensions gallery', 'ACP_FORUM_BASED_PERMISSIONS' => 'Forum based permissions', 'ACP_FORUM_LOGS' => 'Forum logs', diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 73d90c6658..1135e13c8e 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -19,6 +19,8 @@ use Composer\IO\IOInterface; use Composer\IO\NullIO; use Composer\Json\JsonFile; use Composer\Package\CompletePackage; +use Composer\Package\LinkConstraint\LinkConstraintInterface; +use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\PackageInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\RepositoryInterface; @@ -198,62 +200,101 @@ class installer $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); + /** @var LinkConstraintInterface $core_constraint */ + $core_constraint = $composer->getPackage()->getRequires()['phpbb/phpbb']->getConstraint(); + $available = []; + + $compatible_packages = []; $repositories = $composer->getRepositoryManager()->getRepositories(); /** @var RepositoryInterface $repository */ foreach ($repositories as $repository) { - if ($repository instanceof ComposerRepository && $repository->hasProviders()) + try { - $r = new \ReflectionObject($repository); - $repo_url = $r->getProperty('url'); - $repo_url->setAccessible(true); - - if ($repo_url->getValue($repository) === 'http://packagist.org') + if ($repository instanceof ComposerRepository && $repository->hasProviders()) { - $url = 'https://packagist.org/packages/list.json?type=' . $type; - $rfs = new RemoteFilesystem($io); - $hostname = parse_url($url, PHP_URL_HOST) ?: $url; - $json = $rfs->getContents($hostname, $url, false); + // Special case for packagist which exposes an api to retrieve all packages of a given type. + // For the others composer repositories with providers we can't do anything. It would be too slow. - /** @var PackageInterface $package */ - foreach (JsonFile::parseJson($json, $url)['packageNames'] as $package) + $r = new \ReflectionObject($repository); + $repo_url = $r->getProperty('url'); + $repo_url->setAccessible(true); + + if ($repo_url->getValue($repository) === 'http://packagist.org') { - $packages = $repository->findPackages($package); - $package = array_pop($packages); + $url = 'https://packagist.org/packages/list.json?type=' . $type; + $rfs = new RemoteFilesystem($io); + $hostname = parse_url($url, PHP_URL_HOST) ?: $url; + $json = $rfs->getContents($hostname, $url, false); - if (isset($available[$package->getName()])) + /** @var PackageInterface $package */ + foreach (JsonFile::parseJson($json, $url)['packageNames'] as $package) { - continue; + $versions = $repository->findPackages($package); + $compatible_packages = $this->get_compatible_versions($compatible_packages, $core_constraint, $package, $versions); } - - $available[$package->getName()] = ['name' => $package->getPrettyName()]; - - if ($package instanceof CompletePackage) + } + } + else + { + // Pre-filter repo packages by their type + $packages = []; + /** @var PackageInterface $package */ + foreach ($repository->getPackages() as $package) + { + if ($package->getType() === $type) { - $available[$package->getName()]['description'] = $package->getDescription(); - $available[$package->getName()]['url'] = $package->getHomepage(); + $packages[$package->getName()][] = $package; } } + + // Filter the compatibles versions + foreach ($packages as $package => $versions) + { + $compatible_packages = $this->get_compatible_versions($compatible_packages, $core_constraint, $package, $versions); + } } } + catch (\Exception $e) + { + // If a repo fails, just skip it. + continue; + } + } + + foreach ($compatible_packages as $name => $versions) + { + // Determine the highest version of the package + /** @var CompletePackage $highest_version */ + $highest_version = null; + + /** @var CompletePackage $version */ + foreach ($versions as $version) + { + if (!$highest_version || version_compare($version->getVersion(), $highest_version->getVersion(), '>')) + { + $highest_version = $version; + } + } + + // Generates the entry + $available[$name] = []; + $available[$name]['name'] = $highest_version->getPrettyName(); + $available[$name]['version'] = $highest_version->getPrettyVersion(); + + if ($version instanceof CompletePackage) + { + $available[$name]['description'] = $highest_version->getDescription(); + $available[$name]['url'] = $highest_version->getHomepage(); + $available[$name]['authors'] = $highest_version->getAuthors(); + } else { - /** @var PackageInterface $package */ - foreach ($repository->getPackages() as $package) - { - if ($package->getType() === $type) - { - $available[$package->getName()] = ['name' => $package->getPrettyName()]; - - if ($package instanceof CompletePackage) - { - $available[$package->getName()]['description'] = $package->getDescription(); - $available[$package->getName()]['url'] = $package->getHomepage(); - } - } - } + $available[$name]['description'] = ''; + $available[$name]['url'] = ''; + $available[$name]['authors'] = []; } } @@ -267,6 +308,38 @@ class installer } } + /** + * Updates $compatible_packages with the versions of $versions compatibles with the $core_constraint + * + * @param array $compatible_packages List of compatibles versions + * @param LinkConstraintInterface $core_constraint Constraint against the phpBB version + * @param string $package_name Considered package + * @param array $versions List of available versions + * + * @return array + */ + private function get_compatible_versions(array $compatible_packages, LinkConstraintInterface $core_constraint, $package_name, array $versions) + { + /** @var PackageInterface $version */ + foreach ($versions as $version) + { + if (array_key_exists('phpbb/phpbb', $version->getRequires())) + { + /** @var LinkConstraintInterface $package_constraint */ + $package_constraint = $version->getRequires()['phpbb/phpbb']->getConstraint(); + + if (!$package_constraint->matches($core_constraint)) + { + continue; + } + } + + $compatible_packages[$package_name][] = $version; + } + + return $compatible_packages; + } + /** * Generates and write the json file used to install the set of packages * @@ -276,7 +349,7 @@ class installer protected function generate_ext_json_file(array $packages) { $io = new NullIO(); - $composer = Factory::create($io, null, false); + $composer = Factory::create($io, $this->root_path . 'composer.json', false); $core_packages = $this->get_core_packages($composer); $core_json_data = [ @@ -314,6 +387,27 @@ class installer return $core_deps; } + /** + * Get the core installed packages + * + * @param Composer $composer Composer object to load the dependencies + * @return array The core packages with their version + */ + protected function get_core_version(Composer $composer) + { + $core_deps = []; + $packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages(); + + foreach ($packages as $package) + { + $core_deps[$package->getName()] = $package->getPrettyVersion(); + } + + $core_deps['phpbb/phpbb'] = $composer->getPackage()->getPrettyVersion(); + + return $core_deps; + } + /** * Get the PHP version required by the core * diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index f05fec4957..5f7f2e28d9 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -187,7 +187,7 @@ class manager implements manager_interface if ($this->available_packages === null) { $this->available_packages = $this->cache->get('_composer_' . $this->package_type . '_available'); - if ($this->available_packages === false) + if (!$this->available_packages) { $this->available_packages = $this->installer->get_available_packages($this->package_type); $this->cache->put('_composer_' . $this->package_type . '_available', $this->available_packages, 24*60*60); diff --git a/phpBB/phpbb/console/command/extension/list_available.php b/phpBB/phpbb/console/command/extension/list_available.php index 107aca3410..c844201fd3 100644 --- a/phpBB/phpbb/console/command/extension/list_available.php +++ b/phpBB/phpbb/console/command/extension/list_available.php @@ -66,7 +66,8 @@ class list_available extends \phpbb\console\command\command foreach ($this->manager->get_available_packages() as $package) { - $extensions[] = '' . $package['name'] . ' ' . $package['url'] . "\n" . $package['description']; + $extensions[] = '' . $package['name'] . ' (' . $package['version'] . ') ' . $package['url'] . + ($package['description'] ? "\n" . $package['description'] : ''); } $io->listing($extensions); diff --git a/phpBB/phpbb/db/migration/data/v320/extensions_composer.php b/phpBB/phpbb/db/migration/data/v320/extensions_composer.php index 09771f1797..1271adb358 100644 --- a/phpBB/phpbb/db/migration/data/v320/extensions_composer.php +++ b/phpBB/phpbb/db/migration/data/v320/extensions_composer.php @@ -22,6 +22,14 @@ class extensions_composer extends \phpbb\db\migration\migration array('config.add', array('exts_composer_packagist', true)), array('config.add', array('exts_composer_json_file', 'composer-ext.json')), array('config.add', array('exts_composer_vendor_dir', 'vendor-ext/')), + array('module.add', array( + 'acp', + 'ACP_EXTENSION_MANAGEMENT', + array( + 'module_basename' => 'acp_extensions', + 'modes' => array('gallery'), + ), + )), ); } } From 6a15ad1368e42cd1525e2344b608f9eda8eb996f Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sun, 13 Sep 2015 16:21:25 +0200 Subject: [PATCH 11/85] [ticket/11150] Implement install extension link in ACP PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 2 +- phpBB/ext/index.htm | 0 phpBB/includes/acp/acp_extensions.php | 56 ++++++++++++++++++++++---- phpBB/phpbb/composer/installer.php | 3 +- phpBB/phpbb/composer/io/console_io.php | 1 + phpBB/phpbb/composer/io/web_io.php | 18 ++++----- 6 files changed, 61 insertions(+), 19 deletions(-) mode change 100644 => 100755 phpBB/ext/index.htm diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 9be59429f6..f9cdad34ff 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -45,7 +45,7 @@ {{ extension.name }}
- {{ lang('DETAILS') }}{{ lang('INSTALL') }} + {{ lang('DETAILS') }}{{ lang('INSTALL') }} {{ extension.version }} {{ extension.description }} diff --git a/phpBB/ext/index.htm b/phpBB/ext/index.htm old mode 100644 new mode 100755 diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index 01e0b5e245..9506a168a4 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -405,14 +405,56 @@ class acp_extensions { global $phpbb_container; - /** @var \phpbb\composer\extension_manager $manager */ - $manager = $phpbb_container->get('ext.composer.manager'); - $this->page_title = 'ACP_EXTENSIONS_GALLERY'; - $this->tpl_name = 'acp_ext_gallery'; + $action = $this->request->variable('action', 'list'); - $this->request->enable_super_globals(); - $this->template->assign_var('extensions', $manager->get_available_packages()); - $this->request->disable_super_globals(); + /** @var \phpbb\language\language $language */ + $language = $phpbb_container->get('language'); + + /** @var \phpbb\composer\manager $composer_manager */ + $composer_manager = $phpbb_container->get('ext.composer.manager'); + + switch ($action) + { + case 'install': + $extension = $this->request->variable('extension', ''); + + if (empty($extension)) + { + redirect($this->u_action); + } + + $formatter = new \Composer\Console\HtmlOutputFormatter([ + 'warning' => new \Symfony\Component\Console\Formatter\OutputFormatterStyle('black', 'yellow') + ]); + + $composer_io = new \phpbb\composer\io\web_io($language, '', \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL, $formatter); + + try + { + $composer_manager->install((array) $extension, $composer_io); + } + catch (\phpbb\exception\runtime_exception $e) + { + trigger_error($language->lang_array($e->getMessage(), $e->get_parameters()) . '

' . $composer_io->getOutput() . adm_back_link($this->u_action), E_USER_WARNING); + } + + trigger_error($language->lang('EXTENSIONS_INSTALLED') . '

' . $composer_io->getOutput() . adm_back_link($this->u_action)); + + break; + case 'remove': + break; + case 'list': + default: + /** @var \phpbb\composer\extension_manager $manager */ + $manager = $phpbb_container->get('ext.composer.manager'); + $this->page_title = 'ACP_EXTENSIONS_GALLERY'; + $this->tpl_name = 'acp_ext_gallery'; + + $this->request->enable_super_globals(); + $this->template->assign_var('extensions', $manager->get_available_packages()); + $this->request->disable_super_globals(); + break; + } } /** diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 1135e13c8e..442dcc3f43 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -119,7 +119,7 @@ class installer ->setDumpAutoloader(false) ->setPreferStable(true) ->setRunScripts(false) - ->setDryRun(false); + ->setDryRun(true); try { @@ -282,6 +282,7 @@ class installer // Generates the entry $available[$name] = []; $available[$name]['name'] = $highest_version->getPrettyName(); + $available[$name]['composer_name'] = $highest_version->getName(); $available[$name]['version'] = $highest_version->getPrettyVersion(); if ($version instanceof CompletePackage) diff --git a/phpBB/phpbb/composer/io/console_io.php b/phpBB/phpbb/composer/io/console_io.php index 33ac77ba4e..14c1ef29e9 100644 --- a/phpBB/phpbb/composer/io/console_io.php +++ b/phpBB/phpbb/composer/io/console_io.php @@ -29,6 +29,7 @@ class console_io extends ConsoleIO * @param InputInterface $input The input instance * @param OutputInterface $output The output instance * @param HelperSet $helperSet The helperSet instance + * @param language $language Language object */ public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet, language $language) { diff --git a/phpBB/phpbb/composer/io/web_io.php b/phpBB/phpbb/composer/io/web_io.php index 9e8aa6ec76..e7927c7233 100644 --- a/phpBB/phpbb/composer/io/web_io.php +++ b/phpBB/phpbb/composer/io/web_io.php @@ -15,25 +15,23 @@ namespace phpbb\composer\io; use Composer\IO\BufferIO; use phpbb\language\language; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Output\StreamOutput; class web_io extends BufferIO { use translate_composer_trait; /** - * Constructor. - * - * @param InputInterface $input The input instance - * @param OutputInterface $output The output instance - * @param HelperSet $helperSet The helperSet instance + * @param language $language Language object + * @param string $input Input string + * @param int $verbosity Verbosity level + * @param OutputFormatterInterface $formatter Output formatter */ - public function __construct(InputInterface $input, OutputInterface $output, HelperSet $helperSet, language $language) + public function __construct(language $language, $input = '', $verbosity = StreamOutput::VERBOSITY_NORMAL, OutputFormatterInterface $formatter = null) { $this->language = $language; - parent::__construct($input, $output, $helperSet); + parent::__construct($input, $verbosity, $formatter); } } From d5672303a359be027c403e329d97193884161897 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sun, 13 Sep 2015 18:44:50 +0200 Subject: [PATCH 12/85] [ticket/11150] Install from ACP PHPBB3-11150 --- phpBB/adm/style/detailled_message_body.html | 14 ++ phpBB/includes/acp/acp_extensions.php | 56 +++++++- phpBB/phpbb/composer/installer.php | 126 ++++++++++++++---- .../composer/io/translate_composer_trait.php | 12 ++ 4 files changed, 180 insertions(+), 28 deletions(-) create mode 100644 phpBB/adm/style/detailled_message_body.html diff --git a/phpBB/adm/style/detailled_message_body.html b/phpBB/adm/style/detailled_message_body.html new file mode 100644 index 0000000000..001fe921e1 --- /dev/null +++ b/phpBB/adm/style/detailled_message_body.html @@ -0,0 +1,14 @@ + + +
+ {%- if MESSAGE_DETAIL_LEGEND -%} + {{ MESSAGE_DETAIL_LEGEND|nl2br }} + {%- endif -%} +
{{ MESSAGE_DETAIL }}
+
+
+

{{ MESSAGE_TITLE }}

+

{{ MESSAGE_TEXT }}

+
+ + diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index 9506a168a4..a23ced9daa 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -427,18 +427,65 @@ class acp_extensions 'warning' => new \Symfony\Component\Console\Formatter\OutputFormatterStyle('black', 'yellow') ]); - $composer_io = new \phpbb\composer\io\web_io($language, '', \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL, $formatter); + $composer_io = new \phpbb\composer\io\web_io($language, '', \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERBOSE, $formatter); try { + $this->request->enable_super_globals(); $composer_manager->install((array) $extension, $composer_io); + $this->request->disable_super_globals(); } catch (\phpbb\exception\runtime_exception $e) { - trigger_error($language->lang_array($e->getMessage(), $e->get_parameters()) . '

' . $composer_io->getOutput() . adm_back_link($this->u_action), E_USER_WARNING); - } + $this->tpl_name = 'detailled_message_body'; - trigger_error($language->lang('EXTENSIONS_INSTALLED') . '

' . $composer_io->getOutput() . adm_back_link($this->u_action)); + if ($e->getPrevious()) + { + $message_title = $language->lang_array($e->getMessage(), $e->get_parameters()); + + if ($e->getPrevious() instanceof \phpbb\exception\exception_interface) + { + $message_text = $language->lang_array($e->getPrevious()->getMessage(), $e->getPrevious()->get_parameters()) . adm_back_link($this->u_action); + } + else + { + $message_text = $e->getPrevious()->getMessage(); + if (strpos($message_text, 'ext/') === 0 && strpos($message_text, 'does not exist and could not be created.') !== false) + {dump($e->getPrevious()->getTraceAsString()); + $message_text = $language->lang('EXTENSIONS_DIR_NOT_WRITABLE'); + } + $message_text .= adm_back_link($this->u_action); + } + } + else + { + $message_title = $language->lang('INFORMATION'); + $message_text = $language->lang_array($e->getMessage(), $e->get_parameters()) . adm_back_link($this->u_action); + } + + $this->template->assign_vars(array( + 'MESSAGE_TITLE' => $message_title, + 'MESSAGE_TEXT' => $message_text, + 'MESSAGE_DETAIL' => $composer_io->getOutput(), + 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), + 'S_USER_ERROR' => true, + ) + ); + + return; + } + $this->tpl_name = 'detailled_message_body'; + + $this->template->assign_vars(array( + 'MESSAGE_TITLE' => $language->lang('ACP_EXTENSIONS_INSTALL'), + 'MESSAGE_TEXT' => $language->lang('EXTENSIONS_INSTALLED') . adm_back_link($this->u_action), + 'MESSAGE_DETAIL' => $composer_io->getOutput(), + 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), + 'S_USER_NOTICE' => true, + ) + ); + + return; break; case 'remove': @@ -452,6 +499,7 @@ class acp_extensions $this->request->enable_super_globals(); $this->template->assign_var('extensions', $manager->get_available_packages()); + $this->template->assign_var('U_ACTION', $this->u_action); $this->request->disable_super_globals(); break; } diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 442dcc3f43..856ef2f6eb 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -74,7 +74,7 @@ class installer $this->packages_vendor_dir = $config['exts_composer_vendor_dir']; } - $this->repositories = ['http://phpbb.local/ext/phpbb/titania/composer/']; + $this->repositories = [/*'http://phpbb.local/ext/phpbb/titania/composer/'*/]; $this->packagist = true; $this->root_path = $root_path; @@ -91,6 +91,36 @@ class installer * @throws runtime_exception */ public function install(array $packages, $whitelist, IOInterface $io = null) + { + // The composer installers works with a path relative to the current directory + $original_working_dir = getcwd(); + chdir($this->root_path); + + try + { + $this->do_install($packages, $whitelist, $io); + chdir($original_working_dir); + } + catch (\Exception $e) + { + chdir($original_working_dir); + throw $e; + } + } + + /** + * Update the current installed set of packages + * + * /!\ Doesn't change the current working directory + * + * @param array $packages Packages to install. + * Each entry may be a name or an array associating a version constraint to a name + * @param array $whitelist White-listed packages (packages that can be installed/updated/removed) + * @param IOInterface $io IO object used for the output + * + * @throws runtime_exception + */ + protected function do_install(array $packages, $whitelist, IOInterface $io = null) { if (!$io) { @@ -99,9 +129,6 @@ class installer $this->generate_ext_json_file($packages); - $original_vendor_dir = getenv('COMPOSER_VENDOR_DIR'); - putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . $this->packages_vendor_dir); - $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); $install = \Composer\Installer::create($io, $composer); @@ -119,21 +146,15 @@ class installer ->setDumpAutoloader(false) ->setPreferStable(true) ->setRunScripts(false) - ->setDryRun(true); + ->setDryRun(false); try { $result = $install->run(); - - putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); - //$output = $io->getOutput(); - //$error_pos = strpos($output, 'Your requirements could not be resolved to an installable set of packages.'); } catch (\Exception $e) { - - putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); - throw new runtime_exception('Cannot install packages', [], $e); + throw new runtime_exception('COMPOSER_CANNOT_INSTALL', [], $e); } if ($result !== 0) @@ -151,14 +172,40 @@ class installer */ public function get_installed_packages($types) { - $types = (array) $types; + // The composer installers works with a path relative to the current directory + $original_working_dir = getcwd(); + chdir($this->root_path); - $original_vendor_dir = getenv('COMPOSER_VENDOR_DIR'); + try + { + $result = $this->do_get_installed_packages($types); + chdir($original_working_dir); + } + catch (\Exception $e) + { + chdir($original_working_dir); + throw $e; + } + + return $result; + } + + /** + * Returns the list of currently installed packages + * + * /!\ Doesn't change the current working directory + * + * @param string|array $types Returns only the packages with the given type(s) + * + * @return array The installed packages associated to their version. + */ + protected function do_get_installed_packages($types) + { + $types = (array) $types; try { $io = new NullIO(); - putenv('COMPOSER_VENDOR_DIR=' . $this->root_path . $this->packages_vendor_dir); $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); $installed = []; @@ -173,16 +220,43 @@ class installer } } - putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); return $installed; } catch (\Exception $e) { - putenv('COMPOSER_VENDOR_DIR=' . $original_vendor_dir); return []; } } + /** + * Gets the list of the available packages of the configured type in the configured repositories + * + * /!\ Doesn't change the current working directory + * + * @param string $type Returns only the packages with the given type + * + * @return array The name of the available packages, associated to their definition. Ordered by name. + */ + public function get_available_packages($type) + { + // The composer installers works with a path relative to the current directory + $original_working_dir = getcwd(); + chdir($this->root_path); + + try + { + $result = $this->do_get_available_packages($type); + chdir($original_working_dir); + } + catch (\Exception $e) + { + chdir($original_working_dir); + throw $e; + } + + return $result; + } + /** * Gets the list of the available packages of the configured type in the configured repositories * @@ -190,14 +264,13 @@ class installer * * @return array The name of the available packages, associated to their definition. Ordered by name. */ - public function get_available_packages($type) + protected function do_get_available_packages($type) { try { - $this->generate_ext_json_file($this->get_installed_packages(self::PHPBB_TYPES)); + $this->generate_ext_json_file($this->do_get_installed_packages(self::PHPBB_TYPES)); $io = new NullIO(); - $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); /** @var LinkConstraintInterface $core_constraint */ @@ -350,10 +423,11 @@ class installer protected function generate_ext_json_file(array $packages) { $io = new NullIO(); - $composer = Factory::create($io, $this->root_path . 'composer.json', false); + + $composer = Factory::create($io, null, false); $core_packages = $this->get_core_packages($composer); - $core_json_data = [ + $ext_json_data = [ 'require' => array_merge( ['php' => $this->get_core_php_requirement($composer)], $core_packages, @@ -361,10 +435,14 @@ class installer $packages), 'replace' => $core_packages, 'repositories' => $this->get_composer_repositories(), + 'config' => [ + 'cache-dir' => 'store/composer', + 'vendor-dir'=> $this->packages_vendor_dir, + ], ]; $json_file = new JsonFile($this->get_composer_ext_json_filename()); - $json_file->write($core_json_data); + $json_file->write($ext_json_data); } /** @@ -452,7 +530,7 @@ class installer */ protected function get_composer_ext_json_filename() { - return $this->root_path . $this->composer_filename; + return $this->composer_filename; } /** diff --git a/phpBB/phpbb/composer/io/translate_composer_trait.php b/phpBB/phpbb/composer/io/translate_composer_trait.php index 376ef28a80..e76244b977 100644 --- a/phpBB/phpbb/composer/io/translate_composer_trait.php +++ b/phpBB/phpbb/composer/io/translate_composer_trait.php @@ -50,6 +50,10 @@ trait translate_composer_trait $lang_key = 'COMPOSER_DELETING'; $parameters = [$elements[1]]; } + else + { + dump('WRITE | ' . $message); + } //$translated_message = $this->language->lang_array($lang_key, $parameters); $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); @@ -153,10 +157,18 @@ trait translate_composer_trait { continue; } + else if ($message === ' Extracting archive') + { + continue; + } else if (empty($message)) { continue; } + else + { + dump('WRITE ERROR | ' . $message); + } //$translated_message = $this->language->lang_array($lang_key, $parameters); $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); From 50b2989cf01fb9a5bb54d198290e23793dff59cf Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sun, 13 Sep 2015 18:51:33 +0200 Subject: [PATCH 13/85] [ticket/11150] PHP 5.4 compatibility PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 4 ++-- phpBB/phpbb/composer/manager.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 856ef2f6eb..61289b7135 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -33,7 +33,7 @@ use phpbb\exception\runtime_exception; */ class installer { - const PHPBB_TYPES = ['phpbb-extension', 'phpbb-style', 'phpbb-language']; + const PHPBB_TYPES = 'phpbb-extension,phpbb-style,phpbb-language'; /** * @var array Repositories to look packages from @@ -268,7 +268,7 @@ class installer { try { - $this->generate_ext_json_file($this->do_get_installed_packages(self::PHPBB_TYPES)); + $this->generate_ext_json_file($this->do_get_installed_packages(explode(',', self::PHPBB_TYPES))); $io = new NullIO(); $composer = Factory::create($io, $this->get_composer_ext_json_filename(), false); diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 5f7f2e28d9..64ab0ccb7c 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -173,7 +173,7 @@ class manager implements manager_interface { if ($this->all_managed_packages === null) { - $this->all_managed_packages = $this->installer->get_installed_packages(installer::PHPBB_TYPES); + $this->all_managed_packages = $this->installer->get_installed_packages(explode(',', installer::PHPBB_TYPES)); } return $this->all_managed_packages; From a32d429f1eb60c487404a4e244010384fa550f4c Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Sun, 13 Sep 2015 21:04:03 +0200 Subject: [PATCH 14/85] [ticket/11150] Restore the cwd before using the autoloader in case of error PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 53 ++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 61289b7135..57e9673049 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -20,7 +20,6 @@ use Composer\IO\NullIO; use Composer\Json\JsonFile; use Composer\Package\CompletePackage; use Composer\Package\LinkConstraint\LinkConstraintInterface; -use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\PackageInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\RepositoryInterface; @@ -60,6 +59,11 @@ class installer */ protected $root_path; + /** + * @var string Store the original working directory in case it has been changed through move_to_root() + */ + private $original_cwd; + /** * @param \phpbb\config\config $config Config object * @param string $root_path phpBB root path @@ -93,17 +97,16 @@ class installer public function install(array $packages, $whitelist, IOInterface $io = null) { // The composer installers works with a path relative to the current directory - $original_working_dir = getcwd(); - chdir($this->root_path); + $this->move_to_root(); try { $this->do_install($packages, $whitelist, $io); - chdir($original_working_dir); + $this->restore_cwd(); } catch (\Exception $e) { - chdir($original_working_dir); + $this->restore_cwd(); throw $e; } } @@ -154,11 +157,13 @@ class installer } catch (\Exception $e) { + $this->restore_cwd(); throw new runtime_exception('COMPOSER_CANNOT_INSTALL', [], $e); } if ($result !== 0) { + $this->restore_cwd(); throw new runtime_exception($io->get_composer_error(), []); } } @@ -173,17 +178,16 @@ class installer public function get_installed_packages($types) { // The composer installers works with a path relative to the current directory - $original_working_dir = getcwd(); - chdir($this->root_path); + $this->move_to_root(); try { $result = $this->do_get_installed_packages($types); - chdir($original_working_dir); + $this->restore_cwd(); } catch (\Exception $e) { - chdir($original_working_dir); + $this->restore_cwd(); throw $e; } @@ -240,17 +244,16 @@ class installer public function get_available_packages($type) { // The composer installers works with a path relative to the current directory - $original_working_dir = getcwd(); - chdir($this->root_path); + $this->move_to_root(); try { $result = $this->do_get_available_packages($type); - chdir($original_working_dir); + $this->restore_cwd(); } catch (\Exception $e) { - chdir($original_working_dir); + $this->restore_cwd(); throw $e; } @@ -592,4 +595,28 @@ class installer { $this->root_path = $root_path; } + + /** + * Change the current directory to phpBB root + */ + protected function move_to_root() + { + if ($this->original_cwd === null) + { + $this->original_cwd = getcwd(); + chdir($this->root_path); + } + } + + /** + * Restore the current working directory if move_to_root() have been called + */ + protected function restore_cwd() + { + if ($this->original_cwd) + { + chdir($this->original_cwd); + $this->original_cwd = null; + } + } } From 51916def9c9bc7521dea624932ca92883c751195 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 13:55:21 +0200 Subject: [PATCH 15/85] [ticket/11150] Display extension status in "gallery" PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 11 ++++++++++- phpBB/includes/acp/acp_extensions.php | 7 ++++++- phpBB/language/en/acp/extensions.php | 3 +++ .../composer/io/translate_composer_trait.php | 16 +++++++++++++--- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index f9cdad34ff..9ae42e2e85 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -45,7 +45,16 @@ {{ extension.name }}
- {{ lang('DETAILS') }}{{ lang('INSTALL') }} + + {{ lang('DETAILS') }} • + {% if extension.name in managed_extensions %} + {{ lang('INSTALLED') }} + {% elseif extension.name in installed_extensions -%} + {{ lang('INSTALLED_MANUALLY') }} ({{ lang('MANAGE') }}) + {% else -%} + {{ lang('INSTALL') }} + {%- endif -%} + {{ extension.version }} {{ extension.description }} diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index a23ced9daa..08a7b36f2e 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -413,6 +413,9 @@ class acp_extensions /** @var \phpbb\composer\manager $composer_manager */ $composer_manager = $phpbb_container->get('ext.composer.manager'); + /** @var \phpbb\extension\manager $extensions_manager */ + $extensions_manager = $phpbb_container->get('ext.manager'); + switch ($action) { case 'install': @@ -451,7 +454,7 @@ class acp_extensions { $message_text = $e->getPrevious()->getMessage(); if (strpos($message_text, 'ext/') === 0 && strpos($message_text, 'does not exist and could not be created.') !== false) - {dump($e->getPrevious()->getTraceAsString()); + { $message_text = $language->lang('EXTENSIONS_DIR_NOT_WRITABLE'); } $message_text .= adm_back_link($this->u_action); @@ -499,6 +502,8 @@ class acp_extensions $this->request->enable_super_globals(); $this->template->assign_var('extensions', $manager->get_available_packages()); + $this->template->assign_var('managed_extensions', array_keys($manager->get_managed_packages())); + $this->template->assign_var('installed_extensions', array_keys($extensions_manager->all_available())); $this->template->assign_var('U_ACTION', $this->u_action); $this->request->disable_super_globals(); break; diff --git a/phpBB/language/en/acp/extensions.php b/phpBB/language/en/acp/extensions.php index a94b767111..30d20839fd 100644 --- a/phpBB/language/en/acp/extensions.php +++ b/phpBB/language/en/acp/extensions.php @@ -104,6 +104,9 @@ $lang = array_merge($lang, array( 'EXTENSION_ENABLE_CONFIRM' => 'Are you sure that you wish to enable the “%s” extension?', 'EXTENSION_FORCE_UNSTABLE_CONFIRM' => 'Are you sure that you wish to force the use of unstable version?', + 'INSTALLED' => 'Installed', + 'INSTALLED_MANUALLY' => 'Installed manually', + 'RETURN_TO_EXTENSION_LIST' => 'Return to the extension list', 'EXT_DETAILS' => 'Extension Details', diff --git a/phpBB/phpbb/composer/io/translate_composer_trait.php b/phpBB/phpbb/composer/io/translate_composer_trait.php index e76244b977..b322accafb 100644 --- a/phpBB/phpbb/composer/io/translate_composer_trait.php +++ b/phpBB/phpbb/composer/io/translate_composer_trait.php @@ -42,7 +42,7 @@ trait translate_composer_trait $parameters = []; $level = 0; - $message = trim(strip_tags($message), "\n\r"); + $message = trim($this->strip_format($message), "\n\r"); if (strpos($message, 'Deleting ') === 0) { @@ -94,7 +94,7 @@ trait translate_composer_trait $parameters = []; $level = 0; - $message = trim(strip_tags($message), "\n\r"); + $message = trim($this->strip_format($message), "\n\r"); if (strpos($message, ' Problem ') === 0) { @@ -103,7 +103,7 @@ trait translate_composer_trait continue; } - $lang_key = "\n" . $message . "\n"; + $lang_key = "\n" . htmlentities($message) . "\n"; $level = 4; } else if ($message === 'Updating dependencies') @@ -209,4 +209,14 @@ trait translate_composer_trait return $error; } + + protected function strip_format($message) + { + return str_replace([ + '', '', + '', '', + '', '', + '', '', + ], '', $message); + } } From d6618397bf6a9c2a9ed71f7d1bf7944d27871f49 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 16:20:33 +0200 Subject: [PATCH 16/85] [ticket/11150] Better pre/post action handling, restore ext.json in case of err PHPBB3-11150 --- phpBB/phpbb/composer/extension_manager.php | 116 ++++++++++++++++-- phpBB/phpbb/composer/installer.php | 32 ++++- .../composer/io/translate_composer_trait.php | 43 ++++--- phpBB/phpbb/composer/manager.php | 90 ++++++++++++-- phpBB/phpbb/composer/manager_interface.php | 3 +- 5 files changed, 243 insertions(+), 41 deletions(-) diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php index 04c0af666f..49f27762c2 100644 --- a/phpBB/phpbb/composer/extension_manager.php +++ b/phpBB/phpbb/composer/extension_manager.php @@ -38,6 +38,11 @@ class extension_manager extends manager */ protected $filesystem; + /** + * @var array + */ + private $enabled_extensions; + /** * @param installer $installer Installer object * @param driver_interface $cache Cache object @@ -57,29 +62,112 @@ class extension_manager extends manager /** * {@inheritdoc} */ - public function install(array $packages, IOInterface $io = null) + public function pre_install(array $packages, IOInterface $io = null) { - $packages = $this->normalize_version($packages); - - $already_managed = array_intersect(array_keys($this->get_managed_packages()), array_keys($packages)); - if (count($already_managed) !== 0) - { - throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); - } - $installed_manually = array_intersect(array_keys($this->extension_manager->all_available()), array_keys($packages)); if (count($installed_manually) !== 0) { throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED_MANUALLY', [implode('|', array_keys($installed_manually))]); } - - $this->do_install($packages, $io); } /** * {@inheritdoc} */ - public function start_managing($package) + protected function pre_update(array $packages, IOInterface $io = null) + { + $io->writeError('DISABLING_EXTENSIONS', true, 1); + $this->enabled_extensions = []; + foreach ($packages as $package) + { + try + { + if ($this->extension_manager->is_enabled($package)) + { + $this->enabled_extensions[] = $package; + $this->extension_manager->disable($package); + } + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([$e->getMessage(), $e->get_parameters()], true, 4); + } + catch (\Exception $e) + { + $io->writeError($e->getMessage(), true, 4); + } + } + } + + /** + * {@inheritdoc} + */ + protected function post_update(array $packages, IOInterface $io = null) + { + $io->writeError('ENABLING_EXTENSIONS', true, 1); + foreach ($this->enabled_extensions as $package) + { + try + { + $this->extension_manager->enable($package); + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([$e->getMessage(), $e->get_parameters()], true, 4); + } + catch (\Exception $e) + { + $io->writeError($e->getMessage(), true, 4); + } + } + } + + /** + * {@inheritdoc} + */ + public function remove(array $packages, IOInterface $io = null) + { + $packages = $this->normalize_version($packages); + + $not_installed = array_diff(array_keys($packages), array_keys($this->extension_manager->all_available())); + if (count($not_installed) !== 0) + { + throw new runtime_exception($this->exception_prefix, 'NOT_INSTALLED', [implode('|', array_keys($not_installed))]); + } + + parent::remove($packages, $io); + } + + /** + * {@inheritdoc} + */ + public function pre_remove(array $packages, IOInterface $io = null) + { + $io->writeError('DISABLING_EXTENSIONS', true, 1); + foreach ($packages as $package) + { + try + { + if ($this->extension_manager->is_enabled($package)) + { + $this->extension_manager->disable($package); + } + } + catch (\phpbb\exception\runtime_exception $e) + { + $io->writeError([$e->getMessage(), $e->get_parameters()], true, 4); + } + catch (\Exception $e) + { + $io->writeError($e->getMessage(), true, 4); + } + } + } + + /** + * {@inheritdoc} + */ + public function start_managing($package, $io) { if (!$this->extension_manager->is_available($package)) { @@ -95,6 +183,7 @@ class extension_manager extends manager if ($this->extension_manager->is_enabled($package)) { $enabled = true; + $io->writeError('DISABLING_EXTENSION', true, 1); $this->extension_manager->disable($package); } @@ -129,7 +218,8 @@ class extension_manager extends manager { try { - $this->extension_manager->enable($package); + $io->writeError('ENABLING_EXTENSION', true, 1); + $this->extension_manager->enabling($package); } catch (\Exception $e) { diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 57e9673049..18e1b6e83b 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -60,10 +60,15 @@ class installer protected $root_path; /** - * @var string Store the original working directory in case it has been changed through move_to_root() + * @var string Stores the original working directory in case it has been changed through move_to_root() */ private $original_cwd; + /** + * @var array Stores the content of the ext json file before generate_ext_json_file() overrides it + */ + private $ext_json_file_backup; + /** * @param \phpbb\config\config $config Config object * @param string $root_path phpBB root path @@ -157,12 +162,14 @@ class installer } catch (\Exception $e) { + $this->restore_ext_json_file(); $this->restore_cwd(); throw new runtime_exception('COMPOSER_CANNOT_INSTALL', [], $e); } if ($result !== 0) { + $this->restore_ext_json_file(); $this->restore_cwd(); throw new runtime_exception($io->get_composer_error(), []); } @@ -444,8 +451,31 @@ class installer ], ]; + $this->ext_json_file_backup = null; $json_file = new JsonFile($this->get_composer_ext_json_filename()); + $ext_json_file_backup = $json_file->read(); $json_file->write($ext_json_data); + $this->ext_json_file_backup = $ext_json_file_backup; + } + + /** + * Restore the json file overridden by generate_ext_json_file() + */ + protected function restore_ext_json_file() + { + if ($this->ext_json_file_backup) + { + try + { + $json_file = new JsonFile($this->get_composer_ext_json_filename()); + $json_file->write($this->ext_json_file_backup); + } + catch (\Exception $e) + { + } + + $this->ext_json_file_backup = null; + } } /** diff --git a/phpBB/phpbb/composer/io/translate_composer_trait.php b/phpBB/phpbb/composer/io/translate_composer_trait.php index b322accafb..0f2d6f8e64 100644 --- a/phpBB/phpbb/composer/io/translate_composer_trait.php +++ b/phpBB/phpbb/composer/io/translate_composer_trait.php @@ -38,9 +38,21 @@ trait translate_composer_trait foreach ($messages as $message) { - $lang_key = $message; - $parameters = []; $level = 0; + if (is_array($message)) + { + $lang_key = $message[0]; + $parameters = $message[1]; + if (count($message) > 2) + { + $level = $message[2]; + } + } + else + { + $lang_key = $message; + $parameters = []; + } $message = trim($this->strip_format($message), "\n\r"); @@ -50,10 +62,6 @@ trait translate_composer_trait $lang_key = 'COMPOSER_DELETING'; $parameters = [$elements[1]]; } - else - { - dump('WRITE | ' . $message); - } //$translated_message = $this->language->lang_array($lang_key, $parameters); $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); @@ -90,9 +98,21 @@ trait translate_composer_trait foreach ($messages as $message) { - $lang_key = $message; - $parameters = []; $level = 0; + if (is_array($message)) + { + $lang_key = $message[0]; + $parameters = $message[1]; + if (count($message) > 2) + { + $level = $message[2]; + } + } + else + { + $lang_key = $message; + $parameters = []; + } $message = trim($this->strip_format($message), "\n\r"); @@ -165,13 +185,8 @@ trait translate_composer_trait { continue; } - else - { - dump('WRITE ERROR | ' . $message); - } - //$translated_message = $this->language->lang_array($lang_key, $parameters); - $translated_message = call_user_func_array([$this->language, 'lang'], array_merge((array)$lang_key, $parameters)); + $translated_message = $this->language->lang_array($lang_key, $parameters); switch ($level) { diff --git a/phpBB/phpbb/composer/manager.php b/phpBB/phpbb/composer/manager.php index 64ab0ccb7c..1941e79fff 100644 --- a/phpBB/phpbb/composer/manager.php +++ b/phpBB/phpbb/composer/manager.php @@ -84,25 +84,40 @@ class manager implements manager_interface throw new runtime_exception($this->exception_prefix, 'ALREADY_INSTALLED', [implode('|', $already_managed)]); } - $this->do_install($packages, $io); - } + $this->pre_install($packages, $io); - /** - * Really install the packages. - * - * @param array $packages Packages to install. - * @param IOInterface $io IO object used for the output - */ - protected function do_install($packages, IOInterface $io = null) - { $managed_packages = array_merge($this->get_all_managed_packages(), $packages); ksort($managed_packages); $this->installer->install($managed_packages, array_keys($packages), $io); + $this->post_install($packages, $io); + $this->managed_packages = null; } + /** + * Hook called before installing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function pre_install(array $packages, IOInterface $io = null) + { + } + + /** + * Hook called after installing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function post_install(array $packages, IOInterface $io = null) + { + } + /** * {@inheritdoc} */ @@ -110,17 +125,42 @@ class manager implements manager_interface { $packages = $this->normalize_version($packages); - // TODO: if the extension is already enabled, we should disabled and re-enable it $not_managed = array_diff_key($packages, $this->get_managed_packages()); if (count($not_managed) !== 0) { throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); } + $this->pre_update($packages, $io); + $managed_packages = array_merge($this->get_all_managed_packages(), $packages); ksort($managed_packages); $this->installer->install($managed_packages, array_keys($packages), $io); + + $this->post_update($packages, $io); + } + + /** + * Hook called before updating the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function pre_update(array $packages, IOInterface $io = null) + { + } + + /** + * Hook called after updating the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function post_update(array $packages, IOInterface $io = null) + { } /** @@ -137,14 +177,40 @@ class manager implements manager_interface throw new runtime_exception($this->exception_prefix, 'NOT_MANAGED', [implode('|', array_keys($not_managed))]); } + $this->pre_remove($packages, $io); + $managed_packages = array_diff_key($this->get_all_managed_packages(), $packages); ksort($managed_packages); $this->installer->install($managed_packages, array_keys($packages), $io); + $this->post_remove($packages, $io); + $this->managed_packages = null; } + /** + * Hook called before removing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function pre_remove(array $packages, IOInterface $io = null) + { + } + + /** + * Hook called after removing the packages + * + * @param array $packages Packages to update. + * Each entry may be a name or an array associating a version constraint to a name + * @param IOInterface $io IO object used for the output + */ + protected function post_remove(array $packages, IOInterface $io = null) + { + } + /** * {@inheritdoc} */ @@ -200,7 +266,7 @@ class manager implements manager_interface /** * {@inheritdoc} */ - public function start_managing($package) + public function start_managing($package, $io) { throw new \phpbb\exception\runtime_exception('COMPOSER_UNSUPPORTED_OPERATION', (array) $this->package_type); } diff --git a/phpBB/phpbb/composer/manager_interface.php b/phpBB/phpbb/composer/manager_interface.php index b5d0177342..2572ba0831 100644 --- a/phpBB/phpbb/composer/manager_interface.php +++ b/phpBB/phpbb/composer/manager_interface.php @@ -84,8 +84,9 @@ interface manager_interface * Remove a package installed manually and reinstall it using composer. * * @param string $package Package to manage + * @param IOInterface $io IO object used for the output * * @throws runtime_exception */ - public function start_managing($package); + public function start_managing($package, $io); } From 88b32580dc1fcc44871b8a27128b086703503383 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 18:43:58 +0200 Subject: [PATCH 17/85] [ticket/11150] Add Remove && Update buttons PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 4 +- phpBB/adm/style/acp_ext_list.html | 11 ++ phpBB/includes/acp/acp_extensions.php | 211 +++++++++++++++++---- phpBB/language/en/common.php | 1 + phpBB/phpbb/composer/extension_manager.php | 4 +- 5 files changed, 185 insertions(+), 46 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 9ae42e2e85..b8d940935e 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -35,7 +35,7 @@ - {{ lang("EXTENSION_NAME") }} + {{ lang("EXTENSION_NAME") }} {{ lang("VERSION") }} {{ lang("DESCRIPTION") }} @@ -46,7 +46,7 @@ {{ extension.name }}
- {{ lang('DETAILS') }} • + {{ lang('HOMEPAGE') }} • {% if extension.name in managed_extensions %} {{ lang('INSTALLED') }} {% elseif extension.name in installed_extensions -%} diff --git a/phpBB/adm/style/acp_ext_list.html b/phpBB/adm/style/acp_ext_list.html index af9e00a614..80163684c1 100644 --- a/phpBB/adm/style/acp_ext_list.html +++ b/phpBB/adm/style/acp_ext_list.html @@ -62,6 +62,11 @@ title="{enabled.actions.L_ACTION_EXPLAIN}">{enabled.actions.L_ACTION}  |  + + +  |  + {L_UPDATE} + @@ -89,6 +94,12 @@ title="{disabled.actions.L_ACTION_EXPLAIN}">{disabled.actions.L_ACTION}  |  + + +  |  + {L_UPDATE} +  | {L_REMOVE} + diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index 08a7b36f2e..bcb057ddb9 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -59,17 +59,17 @@ class acp_extensions switch ($mode) { case 'gallery': - $this->gallery_mode(); + $this->gallery_mode($id, $mode); break; default: - $this->main_mode(); + $this->main_mode($id, $mode); break; } } - public function main_mode() + public function main_mode($id, $mode) { - global $phpbb_extension_manager, $phpbb_root_path; + global $phpbb_extension_manager, $phpbb_root_path, $phpbb_container, $phpbb_admin_path, $phpEx; $this->page_title = 'ACP_EXTENSIONS'; @@ -167,11 +167,17 @@ class acp_extensions $this->list_disabled_exts(); $this->list_available_exts(); + $composer_manager = $phpbb_container->get('ext.composer.manager'); + + $this->request->enable_super_globals(); $this->template->assign_vars(array( 'U_VERSIONCHECK_FORCE' => $this->u_action . '&action=list&versioncheck_force=1', 'FORCE_UNSTABLE' => $this->config['extension_force_unstable'], 'U_ACTION' => $this->u_action, + 'MANAGED_EXTENSIONS' => array_keys($composer_manager->get_managed_packages()), + 'U_GALLERY_ACTION' => append_sid("{$phpbb_admin_path}index.$phpEx", "i=$id&mode=gallery"), )); + $this->request->disable_super_globals(); $this->tpl_name = 'acp_ext_list'; break; @@ -401,7 +407,7 @@ class acp_extensions $this->tpl_name = $tpl_name; } - public function gallery_mode() + public function gallery_mode($id, $mode) { global $phpbb_container; @@ -440,41 +446,7 @@ class acp_extensions } catch (\phpbb\exception\runtime_exception $e) { - $this->tpl_name = 'detailled_message_body'; - - if ($e->getPrevious()) - { - $message_title = $language->lang_array($e->getMessage(), $e->get_parameters()); - - if ($e->getPrevious() instanceof \phpbb\exception\exception_interface) - { - $message_text = $language->lang_array($e->getPrevious()->getMessage(), $e->getPrevious()->get_parameters()) . adm_back_link($this->u_action); - } - else - { - $message_text = $e->getPrevious()->getMessage(); - if (strpos($message_text, 'ext/') === 0 && strpos($message_text, 'does not exist and could not be created.') !== false) - { - $message_text = $language->lang('EXTENSIONS_DIR_NOT_WRITABLE'); - } - $message_text .= adm_back_link($this->u_action); - } - } - else - { - $message_title = $language->lang('INFORMATION'); - $message_text = $language->lang_array($e->getMessage(), $e->get_parameters()) . adm_back_link($this->u_action); - } - - $this->template->assign_vars(array( - 'MESSAGE_TITLE' => $message_title, - 'MESSAGE_TEXT' => $message_text, - 'MESSAGE_DETAIL' => $composer_io->getOutput(), - 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), - 'S_USER_ERROR' => true, - ) - ); - + $this->display_composer_exception($language, $e, $composer_io); return; } $this->tpl_name = 'detailled_message_body'; @@ -488,10 +460,117 @@ class acp_extensions ) ); - return; - break; case 'remove': + $extension = $this->request->variable('extension', ''); + + if (empty($extension)) + { + redirect($this->u_action); + } + + $formatter = new \Composer\Console\HtmlOutputFormatter([ + 'warning' => new \Symfony\Component\Console\Formatter\OutputFormatterStyle('black', 'yellow') + ]); + + $composer_io = new \phpbb\composer\io\web_io($language, '', \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERBOSE, $formatter); + + try + { + $this->request->enable_super_globals(); + $composer_manager->remove((array) $extension, $composer_io); + $this->request->disable_super_globals(); + } + catch (\phpbb\exception\runtime_exception $e) + { + $this->display_composer_exception($language, $e, $composer_io); + return; + } + $this->tpl_name = 'detailled_message_body'; + + $this->template->assign_vars(array( + 'MESSAGE_TITLE' => $language->lang('ACP_EXTENSIONS_REMOVE'), + 'MESSAGE_TEXT' => $language->lang('EXTENSIONS_REMOVED') . adm_back_link($this->u_action), + 'MESSAGE_DETAIL' => $composer_io->getOutput(), + 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), + 'S_USER_NOTICE' => true, + ) + ); + + break; + case 'update': + $extension = $this->request->variable('extension', ''); + + if (empty($extension)) + { + redirect($this->u_action); + } + + $formatter = new \Composer\Console\HtmlOutputFormatter([ + 'warning' => new \Symfony\Component\Console\Formatter\OutputFormatterStyle('black', 'yellow') + ]); + + $composer_io = new \phpbb\composer\io\web_io($language, '', \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERBOSE, $formatter); + + try + { + $this->request->enable_super_globals(); + $composer_manager->update((array) $extension, $composer_io); + $this->request->disable_super_globals(); + } + catch (\phpbb\exception\runtime_exception $e) + { + $this->display_composer_exception($language, $e, $composer_io); + return; + } + $this->tpl_name = 'detailled_message_body'; + + $this->template->assign_vars(array( + 'MESSAGE_TITLE' => $language->lang('ACP_EXTENSIONS_UPDATE'), + 'MESSAGE_TEXT' => $language->lang('EXTENSIONS_UPDATED') . adm_back_link($this->u_action), + 'MESSAGE_DETAIL' => $composer_io->getOutput(), + 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), + 'S_USER_NOTICE' => true, + ) + ); + + break; + case 'manage': + $extension = $this->request->variable('extension', ''); + + if (empty($extension)) + { + redirect($this->u_action); + } + + $formatter = new \Composer\Console\HtmlOutputFormatter([ + 'warning' => new \Symfony\Component\Console\Formatter\OutputFormatterStyle('black', 'yellow') + ]); + + $composer_io = new \phpbb\composer\io\web_io($language, '', \Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERBOSE, $formatter); + + try + { + $this->request->enable_super_globals(); + $composer_manager->start_managing($extension, $composer_io); + $this->request->disable_super_globals(); + } + catch (\phpbb\exception\runtime_exception $e) + { + $this->display_composer_exception($language, $e, $composer_io); + return; + } + $this->tpl_name = 'detailled_message_body'; + + $this->template->assign_vars(array( + 'MESSAGE_TITLE' => $language->lang('ACP_EXTENSIONS_MANAGE'), + 'MESSAGE_TEXT' => $language->lang('EXTENSION_MANAGED') . adm_back_link($this->u_action), + 'MESSAGE_DETAIL' => $composer_io->getOutput(), + 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), + 'S_USER_NOTICE' => true, + ) + ); + break; case 'list': default: @@ -510,6 +589,51 @@ class acp_extensions } } + /** + * Display an exception raised by the composer manager + * + * @param \phpbb\language\language $language + * @param \phpbb\exception\runtime_exception $e + * @param \phpbb\composer\io\web_io $composer_io + */ + private function display_composer_exception(\phpbb\language\language $language, \phpbb\exception\runtime_exception $e, \phpbb\composer\io\web_io $composer_io) + { + $this->tpl_name = 'detailled_message_body'; + + if ($e->getPrevious()) + { + $message_title = $language->lang_array($e->getMessage(), $e->get_parameters()); + + if ($e->getPrevious() instanceof \phpbb\exception\exception_interface) + { + $message_text = $language->lang_array($e->getPrevious()->getMessage(), $e->getPrevious()->get_parameters()) . adm_back_link($this->u_action); + } + else + { + $message_text = $e->getPrevious()->getMessage(); + if (strpos($message_text, 'ext/') === 0 && strpos($message_text, 'does not exist and could not be created.') !== false) + { + $message_text = $language->lang('EXTENSIONS_DIR_NOT_WRITABLE'); + } + $message_text .= adm_back_link($this->u_action); + } + } + else + { + $message_title = $language->lang('INFORMATION'); + $message_text = $language->lang_array($e->getMessage(), $e->get_parameters()) . adm_back_link($this->u_action); + } + + $this->template->assign_vars(array( + 'MESSAGE_TITLE' => $message_title, + 'MESSAGE_TEXT' => $message_text, + 'MESSAGE_DETAIL' => $composer_io->getOutput(), + 'MESSAGE_DETAIL_LEGEND' => $language->lang('COMPOSER_OUTPUT'), + 'S_USER_ERROR' => true, + ) + ); + } + /** * Lists all the enabled extensions and dumps to the template * @@ -529,6 +653,7 @@ class acp_extensions $enabled_extension_meta_data[$name] = array( 'META_DISPLAY_NAME' => $md_manager->get_metadata('display-name'), 'META_VERSION' => $meta['version'], + 'META_NAME' => $md_manager->get_metadata('name'), ); if (isset($meta['extra']['version-check'])) @@ -600,6 +725,7 @@ class acp_extensions $disabled_extension_meta_data[$name] = array( 'META_DISPLAY_NAME' => $md_manager->get_metadata('display-name'), 'META_VERSION' => $meta['version'], + 'META_NAME' => $md_manager->get_metadata('name'), ); if (isset($meta['extra']['version-check'])) @@ -671,6 +797,7 @@ class acp_extensions $available_extension_meta_data[$name] = array( 'META_DISPLAY_NAME' => $md_manager->get_metadata('display-name'), 'META_VERSION' => $meta['version'], + 'META_NAME' => $md_manager->get_metadata('name'), ); if (isset($meta['extra']['version-check'])) diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php index 709b7d9731..e2198655c9 100644 --- a/phpBB/language/en/common.php +++ b/phpBB/language/en/common.php @@ -153,6 +153,7 @@ $lang = array_merge($lang, array( 'BYTES_SHORT' => 'B', 'CANCEL' => 'Cancel', + 'CANNOT_RENAME_FILE' => 'Cannot rename %s', 'CHANGE' => 'Change', 'CHANGE_FONT_SIZE' => 'Change font size', 'CHANGING_PREFERENCES' => 'Changing board preferences', diff --git a/phpBB/phpbb/composer/extension_manager.php b/phpBB/phpbb/composer/extension_manager.php index 49f27762c2..d877d28676 100644 --- a/phpBB/phpbb/composer/extension_manager.php +++ b/phpBB/phpbb/composer/extension_manager.php @@ -187,7 +187,7 @@ class extension_manager extends manager $this->extension_manager->disable($package); } - $ext_path = $this->extension_manager->get_extension_path($package); + $ext_path = $this->extension_manager->get_extension_path($package, true); $backup_path = rtrim($ext_path, '/') . '__backup__'; try @@ -201,7 +201,7 @@ class extension_manager extends manager try { - $this->install((array) $package); + $this->install((array) $package, $io); $this->filesystem->remove($backup_path); } catch (runtime_exception $e) From 76a9bb22754c1335aae7c46c745101a0c95398aa Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 18:54:16 +0200 Subject: [PATCH 18/85] [ticket/11150] Display extensions with the display_name in the gallery PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 2 +- phpBB/phpbb/composer/installer.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index b8d940935e..5b9916bf7c 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -44,7 +44,7 @@ {% for extension in extensions %} - {{ extension.name }}
+ {{ extension.display_name }}
{{ lang('HOMEPAGE') }} • {% if extension.name in managed_extensions %} diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 18e1b6e83b..1ba3ed42a2 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -365,6 +365,7 @@ class installer // Generates the entry $available[$name] = []; $available[$name]['name'] = $highest_version->getPrettyName(); + $available[$name]['display_name'] = $highest_version->getExtra()['display-name']; $available[$name]['composer_name'] = $highest_version->getName(); $available[$name]['version'] = $highest_version->getPrettyVersion(); From c12cfdad9d20b1c4f5ee7b1a71ac6643c966ada1 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 18:59:00 +0200 Subject: [PATCH 19/85] [ticket/11150] Move actions to a dedicated collumn in the gallery PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 5b9916bf7c..50057f1c4f 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -32,12 +32,13 @@ - + - + + @@ -46,18 +47,20 @@ - + + {% endfor %} From f6a867af5f205a25e70068aa7e6573d332ff818b Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 19:05:04 +0200 Subject: [PATCH 20/85] [ticket/11150] Move homepage and display package name PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 10 +++++----- phpBB/adm/style/acp_ext_list.html | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 50057f1c4f..6003875f5e 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -46,15 +46,15 @@ - + From bf5462e7083339dbbf9e085289c277e31d7a1c82 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 19:06:49 +0200 Subject: [PATCH 21/85] [ticket/11150] Resizes some columns PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 6003875f5e..5bf3e295c8 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -35,10 +35,10 @@ - + - + From 8db408a21c2b1a0eb466ba8a7bfc251be8c3d8c4 Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 19:11:52 +0200 Subject: [PATCH 22/85] [ticket/11150] Resize actions column PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 5bf3e295c8..eaf57ac284 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -38,7 +38,7 @@ - + @@ -46,7 +46,7 @@ From 3a1f2ad3ff8a0aa5fcdfba43d8c963d29c77d3ed Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 19:49:15 +0200 Subject: [PATCH 23/85] [ticket/11150] Sorts extensions by display name PHPBB3-11150 --- phpBB/phpbb/composer/installer.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/phpBB/phpbb/composer/installer.php b/phpBB/phpbb/composer/installer.php index 1ba3ed42a2..ab1af35525 100644 --- a/phpBB/phpbb/composer/installer.php +++ b/phpBB/phpbb/composer/installer.php @@ -383,7 +383,10 @@ class installer } } - ksort($available); + usort($available, function($a, $b) + { + return strcmp($a['display_name'], $b['display_name']); + }); return $available; } From 3d363a7bb3ae8f76b352a095e1cdbff83dfd3d4f Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Mon, 14 Sep 2015 23:27:10 +0200 Subject: [PATCH 24/85] [ticket/11150] Gallery pagination PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 99 +++++++++++++++------------ phpBB/adm/style/admin.css | 10 +++ phpBB/includes/acp/acp_extensions.php | 14 +++- 3 files changed, 78 insertions(+), 45 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index eaf57ac284..4a435f4838 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -1,18 +1,23 @@ - +{% include('overall_header.html') %} -

{{lang( 'EXTENSIONS_ADMIN') }}

+

{{lang( 'EXTENSIONS_ADMIN') }}

-

{{lang( 'EXTENSIONS_EXPLAIN') }}

+

{{lang( 'EXTENSIONS_EXPLAIN') }}

-
- {{ lang('BROWSE_EXTENSIONS_DATABASE') }}{{ lang('SETTINGS') }} -
+
+ {{ lang('BROWSE_EXTENSIONS_DATABASE') }}{{ lang('SETTINGS') }} +
- +{% if pagination is defined %} + +{% endif %} -
+ +
{{ lang('EXTENSIONS_GALLERY_SETTINGS') }}
@@ -29,41 +34,47 @@ {{ S_FORM_TOKEN }}

- + -
{{ lang("EXTENSION_NAME") }}{{ lang("VERSION") }}{{ lang("VERSION") }} {{ lang("DESCRIPTION") }}{{ lang("EXTENSION_ACTIONS") }}
{{ extension.display_name }}
- {{ lang('HOMEPAGE') }} • - {% if extension.name in managed_extensions %} - {{ lang('INSTALLED') }} - {% elseif extension.name in installed_extensions -%} - {{ lang('INSTALLED_MANUALLY') }} ({{ lang('MANAGE') }}) - {% else -%} - {{ lang('INSTALL') }} - {%- endif -%} + {{ lang('HOMEPAGE') }}
{{ extension.version }}{{ extension.version }} {{ extension.description }} + {% if extension.name in managed_extensions %} + {{ lang('INSTALLED') }} + {% elseif extension.name in installed_extensions -%} + {{ lang('INSTALLED_MANUALLY') }} ({{ lang('MANAGE') }}) + {% else -%} + {{ lang('INSTALL') }} + {%- endif -%} +
{{ extension.display_name }}
- - {{ lang('HOMEPAGE') }} - + {{ extension.name }}
{{ extension.version }}{{ extension.description }}{{ extension.description }} • {{ lang('HOMEPAGE') }} {% if extension.name in managed_extensions %} - {{ lang('INSTALLED') }} + {{ lang('INSTALLED') }}
+ ({{ lang('UPDATE') }} + • {{ lang('REMOVE') }}) {% elseif extension.name in installed_extensions -%} {{ lang('INSTALLED_MANUALLY') }} ({{ lang('MANAGE') }}) {% else -%} diff --git a/phpBB/adm/style/acp_ext_list.html b/phpBB/adm/style/acp_ext_list.html index 80163684c1..da7c58591f 100644 --- a/phpBB/adm/style/acp_ext_list.html +++ b/phpBB/adm/style/acp_ext_list.html @@ -66,6 +66,7 @@  |  {L_UPDATE} +  | {L_REMOVE}
{{ lang("EXTENSION_NAME") }}{{ lang("EXTENSION_NAME") }} {{ lang("VERSION") }} {{ lang("DESCRIPTION") }}{{ lang("EXTENSION_ACTIONS") }}{{ lang("EXTENSION_ACTIONS") }}
{{ lang("EXTENSION_NAME") }} {{ lang("VERSION") }} {{ lang("DESCRIPTION") }}{{ lang("EXTENSION_ACTIONS") }}{{ lang("EXTENSION_ACTIONS") }}
{{ extension.display_name }}
- {{ extension.name }} + {{ extension.name }}
{{ extension.version }} {{ extension.description }} • {{ lang('HOMEPAGE') }}
- - - - - - - - - - - {% for extension in extensions %} - - - - - - - {% endfor %} - -
{{ lang("EXTENSION_NAME") }}{{ lang("VERSION") }}{{ lang("DESCRIPTION") }}{{ lang("EXTENSION_ACTIONS") }}
- {{ extension.display_name }}
- {{ extension.name }} -
{{ extension.version }}{{ extension.description }} • {{ lang('HOMEPAGE') }} - {% if extension.name in managed_extensions %} - {{ lang('INSTALLED') }}
- ({{ lang('UPDATE') }} - • {{ lang('REMOVE') }}) - {% elseif extension.name in installed_extensions -%} - {{ lang('INSTALLED_MANUALLY') }} ({{ lang('MANAGE') }}) - {% else -%} - {{ lang('INSTALL') }} - {%- endif -%} -
+ + + + + + + + + + + +{% for extension in extensions %} + + + + + + +{% endfor %} + +
{{ lang("EXTENSION_NAME") }}{{ lang("VERSION") }}{{ lang("DESCRIPTION") }}{{ lang("EXTENSION_ACTIONS") }}
+ {{ extension.display_name }}
+ {{ extension.name }} +
{{ extension.version }}{{ extension.description }} • {{ lang('HOMEPAGE') }} + {% if extension.name in managed_extensions %} + {{ lang('INSTALLED') }}
+ ({{ lang('UPDATE') }} + • {{ lang('REMOVE') }}) + {% elseif extension.name in installed_extensions -%} + {{ lang('INSTALLED_MANUALLY') }} ({{ lang('MANAGE') }}) + {% else -%} + {{ lang('INSTALL') }} + {%- endif -%} +
- +{% if pagination is defined %} + +{% endif %} + +{% include('overall_footer.html') %} diff --git a/phpBB/adm/style/admin.css b/phpBB/adm/style/admin.css index f81dd35531..290fee2983 100644 --- a/phpBB/adm/style/admin.css +++ b/phpBB/adm/style/admin.css @@ -1333,6 +1333,16 @@ p.quick { text-align: left; } +fieldset.quick-left, p.quick-left { + margin: 15px 0 5px 0; + padding: 0 0px 0; + float: left; +} + +.rtl fieldset.quick-left, .rtl p.quick-left { + float: right; +} + fieldset.quick legend { display: none; } diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index bcb057ddb9..8c144b09fc 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -576,11 +576,23 @@ class acp_extensions default: /** @var \phpbb\composer\extension_manager $manager */ $manager = $phpbb_container->get('ext.composer.manager'); + + /** @var \phpbb\pagination $pagination */ + $pagination = $phpbb_container->get('pagination'); + + $start = $this->request->variable('start', 0); + $base_url = $this->u_action; + + $available_extensions = $manager->get_available_packages(); + $extensions = array_slice($available_extensions, $start, 20); + + $pagination->generate_template_pagination($base_url, 'pagination', 'start', count($available_extensions), 20, $start); + $this->page_title = 'ACP_EXTENSIONS_GALLERY'; $this->tpl_name = 'acp_ext_gallery'; $this->request->enable_super_globals(); - $this->template->assign_var('extensions', $manager->get_available_packages()); + $this->template->assign_var('extensions', $extensions); $this->template->assign_var('managed_extensions', array_keys($manager->get_managed_packages())); $this->template->assign_var('installed_extensions', array_keys($extensions_manager->all_available())); $this->template->assign_var('U_ACTION', $this->u_action); From 3640302afd27f1e7dab08592f28ded507c1d7cbe Mon Sep 17 00:00:00 2001 From: Tristan Darricau Date: Tue, 15 Sep 2015 10:54:38 +0200 Subject: [PATCH 25/85] [ticket/11150] Set settings PHPBB3-11150 --- phpBB/adm/style/acp_ext_gallery.html | 24 ++++++++-- phpBB/adm/style/acp_ext_list.html | 2 +- phpBB/includes/acp/acp_extensions.php | 47 +++++++++++++++++++ phpBB/language/en/acp/extensions.php | 6 +++ .../data/v320/extensions_composer.php | 2 + 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/phpBB/adm/style/acp_ext_gallery.html b/phpBB/adm/style/acp_ext_gallery.html index 4a435f4838..72ec92785c 100644 --- a/phpBB/adm/style/acp_ext_gallery.html +++ b/phpBB/adm/style/acp_ext_gallery.html @@ -20,10 +20,28 @@
{{ lang('EXTENSIONS_GALLERY_SETTINGS') }}
-
+
- - + + +
+
+
+
+
+ + +
+
+
+
+
+ + {{ lang('WARNING') }}{{ lang('COLON') }} {{ lang('ENABLE_PACKAGIST_DETAIL') }} +
+
+ +
diff --git a/phpBB/adm/style/acp_ext_list.html b/phpBB/adm/style/acp_ext_list.html index da7c58591f..0ab620b864 100644 --- a/phpBB/adm/style/acp_ext_list.html +++ b/phpBB/adm/style/acp_ext_list.html @@ -7,7 +7,7 @@

{L_EXTENSIONS_EXPLAIN}

- {L_BROWSE_EXTENSIONS_DATABASE}{L_VERSIONCHECK_FORCE_UPDATE_ALL}{L_SETTINGS} + {L_VERSIONCHECK_FORCE_UPDATE_ALL}{L_SETTINGS}