diff --git a/app/Cli/Commands/CompilePoFiles.php b/app/Cli/Commands/CompilePoFiles.php new file mode 100644 index 00000000000..0bc92472c29 --- /dev/null +++ b/app/Cli/Commands/CompilePoFiles.php @@ -0,0 +1,80 @@ +. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Cli\Commands; + +use Fisharebest\Localization\Translation; +use Fisharebest\Webtrees\Webtrees; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +use function basename; +use function count; +use function dirname; +use function file_put_contents; +use function glob; +use function realpath; +use function var_export; + +class CompilePoFiles extends Command +{ + private const PO_FILE_PATTERN = Webtrees::ROOT_DIR . 'resources/lang/*/*.po'; + + protected function configure(): void + { + $this + ->setName(name: 'compile-po-files') + ->setDescription(description: 'Convert the PO files into PHP files'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle(input: $input, output: $output); + + $po_files = glob(pattern: self::PO_FILE_PATTERN); + + if ($po_files === false || $po_files === []) { + $io->error('Failed to find any PO files matching ' . self::PO_FILE_PATTERN); + + return Command::FAILURE; + } + + $error = false; + + foreach ($po_files as $po_file) { + $po_file = realpath($po_file); + $translation = new Translation(filename: $po_file); + $translations = $translation->asArray(); + $php_file = dirname(path: $po_file) . '/' . basename(path: $po_file, suffix: '.po') . '.php'; + $php_code = "error('Failed to write to ' . $php_file); + $error = true; + } else { + $io->success('Created ' . $php_file . ' with ' . count(value: $translations) . ' translations'); + } + } + + return $error ? Command::FAILURE : Command::SUCCESS; + } +} diff --git a/app/Cli/Commands/UserCreate.php b/app/Cli/Commands/UserCreate.php new file mode 100644 index 00000000000..5f1ee72ba24 --- /dev/null +++ b/app/Cli/Commands/UserCreate.php @@ -0,0 +1,120 @@ +. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Cli\Commands; + +use Composer\Console\Input\InputOption; +use Fisharebest\Webtrees\Contracts\UserInterface; +use Fisharebest\Webtrees\DB; +use Fisharebest\Webtrees\Services\UserService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +use function bin2hex; +use function random_bytes; + +class UserCreate extends Command +{ + public function __construct(private readonly UserService $user_service) + { + parent::__construct(); + } + + protected function configure(): void + { + $this + ->setName(name: 'user-create') + ->setDescription(description: 'Create a new user account') + ->addOption(name: 'admin', shortcut: 'a', mode: InputOption::VALUE_NONE, description: 'Make the new user an administrator') + ->addOption(name: 'username', shortcut: 'u', mode: InputOption::VALUE_REQUIRED, description: 'The username of the new user') + ->addOption(name: 'realname', shortcut: 'r', mode: InputOption::VALUE_REQUIRED, description: 'The real name of the new user') + ->addOption(name: 'email', shortcut: 'e', mode: InputOption::VALUE_REQUIRED, description: 'The email of the new user') + ->addOption(name: 'password', shortcut: 'p', mode: InputOption::VALUE_OPTIONAL, description: 'The password of the new user'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle(input: $input, output: $output); + + $username = $input->getOption(name: 'username'); + $realname = $input->getOption(name: 'realname'); + $email = $input->getOption(name: 'email'); + $password = $input->getOption(name: 'password'); + $admin = (bool) $input->getOption(name: 'admin'); + + $missing = false; + + if ($username === null) { + $io->error(message: 'Missing required option: --username'); + $missing = true; + } + + if ($realname === null) { + $io->error(message: 'Missing required option: --realname'); + $missing = true; + } + + if ($email === null) { + $io->error(message: 'Missing required option: --email'); + $missing = true; + } + + if ($missing) { + return Command::FAILURE; + } + + $user = $this->user_service->findByUserName(user_name: $username); + + if ($user !== null) { + $io->error(message: 'A user with the username "' . $username . '" already exists.'); + + return Command::FAILURE; + } + + $user = $this->user_service->findByEmail(email: $email); + + if ($user !== null) { + $io->error(message: 'A user with the email "' . $email . '" already exists'); + + return Command::FAILURE; + } + + if ($password === null) { + $password = bin2hex(string: random_bytes(length: 8)); + $io->info(message: 'Generated password: ' . $password); + } + + $user = $this->user_service->create(user_name: $username, real_name:$realname, email: $email, password: $password); + $user->setPreference(setting_name: UserInterface::PREF_IS_ACCOUNT_APPROVED, setting_value: '1'); + $user->setPreference(setting_name: UserInterface::PREF_IS_EMAIL_VERIFIED, setting_value: '1'); + $io->success('User ' . $user->id() . ' created.'); + + if ($admin) { + $user->setPreference(setting_name: UserInterface::PREF_IS_ADMINISTRATOR, setting_value: '1'); + $io->success(message: 'User granted administrator role.'); + } + + DB::exec(sql: 'COMMIT'); + + return Command::SUCCESS; + } +} diff --git a/app/Cli/Commands/UserList.php b/app/Cli/Commands/UserList.php new file mode 100644 index 00000000000..33ac0d0e5d5 --- /dev/null +++ b/app/Cli/Commands/UserList.php @@ -0,0 +1,97 @@ +. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Cli\Commands; + +use Composer\Console\Input\InputOption; +use Fisharebest\Webtrees\Auth; +use Fisharebest\Webtrees\Contracts\UserInterface; +use Fisharebest\Webtrees\DB; +use Fisharebest\Webtrees\Registry; +use Fisharebest\Webtrees\Services\UserService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +use function bin2hex; +use function random_bytes; + +class UserList extends Command +{ + public function __construct(private readonly UserService $user_service) + { + parent::__construct(); + } + + protected function configure(): void + { + $this + ->setName(name: 'user-list') + ->setDescription(description: 'List users'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle(input: $input, output: $output); + + $users = $this->user_service->all()->sort(fn($a, $b) => $a->id() <=> $b->id()); + + $table = new Table(output: $output); + + $table->setHeaders(headers: ['ID', 'Username', 'Real Name', 'Email', 'Admin', 'Approved', 'Verified', 'Language', 'Time zone', 'Registered', 'Last login']); + + foreach ($users as $user) { + $registered = (int) $user->getPreference(setting_name: UserInterface::PREF_TIMESTAMP_REGISTERED); + $last_login = (int) $user->getPreference(setting_name: UserInterface::PREF_TIMESTAMP_ACTIVE); + + if ($registered === 0) { + $registered = 'Never'; + } else { + $registered = Registry::timestampFactory()->make(timestamp: $registered)->format(format: 'Y-m-d H:i:s'); + } + + if ($last_login === 0) { + $last_login = 'Never'; + } else { + $last_login = Registry::timestampFactory()->make(timestamp: $last_login)->format(format: 'Y-m-d H:i:s'); + } + + $table->addRow(row: [ + $user->id(), + $user->userName(), + $user->realName(), + $user->email(), + Auth::isAdmin(user: $user) ? 'Yes' : 'No', + $user->getPreference(setting_name: UserInterface::PREF_IS_ACCOUNT_APPROVED) ? 'Yes' : 'No', + $user->getPreference(setting_name: UserInterface::PREF_IS_EMAIL_VERIFIED) ? 'Yes' : 'No', + $user->getPreference(setting_name: UserInterface::PREF_LANGUAGE), + $user->getPreference(setting_name: UserInterface::PREF_TIME_ZONE), + $registered, + $last_login, + ]); + } + + $table->render(); + + return Command::SUCCESS; + } +} diff --git a/app/Cli/Console.php b/app/Cli/Console.php new file mode 100644 index 00000000000..c93fed33c5c --- /dev/null +++ b/app/Cli/Console.php @@ -0,0 +1,73 @@ +. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Cli; + +use Fisharebest\Webtrees\DB; +use Fisharebest\Webtrees\I18N; +use Fisharebest\Webtrees\Registry; +use Fisharebest\Webtrees\Webtrees; +use Symfony\Component\Console\Application; + +use function parse_ini_file; + +final class Console extends Application +{ + public function loadCommands(): self + { + $commands = glob(pattern: __DIR__ . '/Commands/*.php') ?: []; + + foreach ($commands as $command) { + $class = __NAMESPACE__ . '\\Commands\\' . basename(path: $command, suffix: '.php'); + + $this->add(Registry::container()->get($class)); + } + + return $this; + } + + public function bootstrap(): self + { + I18N::init(code: 'en-US', setup: true); + + $config = parse_ini_file(filename: Webtrees::CONFIG_FILE); + + if ($config === false) { + return $this; + } + + DB::connect( + driver: $config['dbtype'] ?? DB::MYSQL, + host: $config['dbhost'], + port: $config['dbport'], + database: $config['dbname'], + username: $config['dbuser'], + password: $config['dbpass'], + prefix: $config['tblpfx'], + key: $config['dbkey'] ?? '', + certificate: $config['dbcert'] ?? '', + ca: $config['dbca'] ?? '', + verify_certificate: (bool) ($config['dbverify'] ?? ''), + ); + + DB::exec('START TRANSACTION'); + + return $this; + } +} diff --git a/app/Console/ComposerScripts.php b/app/Console/ComposerScripts.php deleted file mode 100644 index d7017f57ca5..00000000000 --- a/app/Console/ComposerScripts.php +++ /dev/null @@ -1,63 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace Fisharebest\Webtrees\Console; - -use Composer\Script\Event; -use Fisharebest\Localization\Translation; - -use function basename; -use function count; -use function dirname; -use function file_put_contents; -use function glob; -use function var_export; - -/** - * Command-line utilities. - */ -class ComposerScripts -{ - // Location of our translation files. - private const PO_FILE_PATTERN = 'resources/lang/*/*.po'; - - /** - * Rebuild the .POT, .PO and .PHP file. - * - * @param Event $event - */ - public static function languageFiles(Event $event): void - { - require $event->getComposer()->getConfig()->get('vendor-dir') . '/autoload.php'; - - $io = $event->getIO(); - - $po_files = glob(self::PO_FILE_PATTERN) ?: []; - - foreach ($po_files as $po_file) { - $translation = new Translation($po_file); - $translations = $translation->asArray(); - $io->write($po_file . ': ' . count($translations)); - $php_file = dirname($po_file) . '/' . basename($po_file, '.po') . '.php'; - $php_code = "cliRequest(); - } else { - $this->httpRequest(); - }; + if ($php_sapi === 'cli') { + return $this->bootstrap()->cliRequest(); + } + + return $this->bootstrap()->httpRequest(); } /** * Respond to a CLI request. - * - * @return void */ - public function cliRequest(): void + public function cliRequest(): int { - // CLI handler will go here. + $console = new Console(); + + return $console->loadCommands()->bootstrap()->run(); } /** * Respond to an HTTP request. - * - * @return ResponseInterface */ public function httpRequest(): ResponseInterface { diff --git a/composer.json b/composer.json index 9099e73e837..af51698b0d0 100644 --- a/composer.json +++ b/composer.json @@ -71,6 +71,7 @@ "ramsey/uuid": "4.7.5", "sabre/vobject": "4.5.4", "symfony/cache": "7.0.4", + "symfony/console": "7.0.4", "symfony/expression-language": "7.0.3", "symfony/mailer": "7.0.4", "symfony/polyfill-mbstring": "1.29.0", @@ -81,7 +82,6 @@ "ext-libxml": "*", "ext-pdo_sqlite": "*", "ext-sqlite3": "*", - "composer/composer": "2.7.2", "league/flysystem-memory": "3.25.1", "php-coveralls/php-coveralls": "2.7.0", "phpunit/phpunit": "11.0.8" @@ -101,7 +101,7 @@ "git archive --prefix=webtrees/ HEAD --format=tar | tar -x", "@composer install --no-dev --quiet", "cp -r vendor/ webtrees/vendor/", - "@composer webtrees:lang", + "php index.php compile-po-files", "for FILE in resources/lang/*/messages.php; do cp $FILE webtrees/$FILE; done", "zip --quiet --recurse-paths --move -9 webtrees-`git describe`.zip webtrees" ], @@ -154,9 +154,6 @@ "vendor/bin/phpunit --coverage-html=tests/coverage", "@composer install --no-dev --quiet" ], - "webtrees:lang": [ - "Fisharebest\\Webtrees\\Console\\ComposerScripts::languageFiles" - ], "webtrees:po": [ "sed -i.bak -e 's/\\(I18N::[^)]*[)]\\)//g' resources/xml/reports/*.xml", "git grep -I --name-only --fixed-strings -e I18N:: -- '*.php' '*.phtml' '*.xml' | xargs xgettext --package-name=webtrees --package-version=1.0 --output=resources/lang/webtrees.pot --no-wrap --language=PHP --add-comments=I18N --from-code=utf-8 --keyword --keyword=translate:1 --keyword=translateContext:1c,2 --keyword=plural:1,2", diff --git a/composer.lock b/composer.lock index a149c1fbb07..c7a0bccece7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5ce1341a95776c8eacfd4417856fdc36", + "content-hash": "8466db88d92706bec77c5364dacecb11", "packages": [ { "name": "aura/router", @@ -4166,6 +4166,99 @@ ], "time": "2024-03-02T12:46:12+00:00" }, + { + "name": "symfony/console", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^6.4|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "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": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-22T20:27:20+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.4.0", @@ -4615,6 +4708,163 @@ ], "time": "2024-01-30T08:34:29+00:00" }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, { "name": "symfony/polyfill-intl-idn", "version": "v1.29.0", @@ -5173,12 +5423,98 @@ "time": "2023-12-26T14:02:43+00:00" }, { - "name": "symfony/translation", + "name": "symfony/string", "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "5b75e872f7d135d7abb4613809fadc8d9f3d30a0" + "url": "https://github.com/symfony/string.git", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.0.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-01T13:17:36+00:00" + }, + { + "name": "symfony/translation", + "version": "v7.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "5b75e872f7d135d7abb4613809fadc8d9f3d30a0" }, "dist": { "type": "zip", @@ -8309,99 +8645,6 @@ ], "time": "2024-02-26T07:52:39+00:00" }, - { - "name": "symfony/console", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "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": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-22T20:27:20+00:00" - }, { "name": "symfony/filesystem", "version": "v7.0.3", @@ -8529,163 +8772,6 @@ ], "time": "2023-10-31T17:59:56+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-01-29T20:11:03+00:00" - }, { "name": "symfony/polyfill-php73", "version": "v1.29.0", @@ -8961,92 +9047,6 @@ ], "time": "2024-01-23T15:02:46+00:00" }, - { - "name": "symfony/string", - "version": "v7.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v7.0.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-02-01T13:17:36+00:00" - }, { "name": "symfony/yaml", "version": "v7.0.3", diff --git a/index.php b/index.php index 25abb2ba9d1..1e3765c7713 100644 --- a/index.php +++ b/index.php @@ -21,5 +21,4 @@ require __DIR__ . '/vendor/autoload.php'; -$webtrees = new Webtrees(); -$webtrees->bootstrap()->run(); +return Webtrees::new()->run(PHP_SAPI);