Release 2.25 (#5109)

This commit is contained in:
Kevin Papst
2024-11-21 22:44:49 +01:00
committed by GitHub
parent 49eb7068c9
commit 0c26a2678e
261 changed files with 6431 additions and 7426 deletions

View File

@@ -15,11 +15,11 @@ use App\Plugin\Plugin;
use App\Plugin\PluginManager;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\PhpSubprocess;
#[AsCommand(name: 'kimai:plugins', description: 'Manage Kimai plugins')]
final class PluginCommand extends Command
@@ -69,14 +69,28 @@ final class PluginCommand extends Command
}
}
$command = $this->getApplication()?->find('doctrine:migrations:migrate');
if ($command === null) {
throw new \RuntimeException('Failed finding doctrine migrations command');
}
$cmdInput = new ArrayInput(['--allow-no-migration' => true, '--configuration' => $config]);
$cmdInput->setInteractive(false);
if (0 !== $command->run($cmdInput, $output)) {
$io->error('Failed to install bundle database: ' . $config);
// using getApplication()->find('doctrine:migrations:migrate') does NOT work here
// because the Doctrine command can only be executed once
// if run more than once it fails with a "Container is frozen" exception
$process = new PhpSubprocess([
'bin/console',
'doctrine:migrations:migrate',
'--allow-no-migration',
'--no-interaction',
'--configuration=' . $config
]);
$process->run();
if (!$process->isSuccessful()) {
$io->error('Failed to install bundle database: ' . PHP_EOL . $config);
$io->error($process->getErrorOutput());
} else {
if ($io->isVerbose()) {
$io->write($process->getOutput());
} else {
$io->success('Successfully installed: ' . $plugin->getName());
}
}
}

View File

@@ -136,7 +136,15 @@ final class RegenerateLocalesCommand extends Command
$shortTime = new \IntlDateFormatter($locale, \IntlDateFormatter::NONE, \IntlDateFormatter::SHORT);
$settings['date'] = $shortDate->getPattern();
if ($settings['date'] === false) {
$io->error('Invalid date pattern for locale: ' . $locale);
continue;
}
$settings['time'] = $shortTime->getPattern();
if ($settings['time'] === false) {
$io->error('Invalid time pattern for locale: ' . $locale);
continue;
}
// see https://github.com/kimai/kimai/issues/4402 - Korean time format failed parsing
// special case when time pattern starts with A / a => this will lead to an error

View File

@@ -48,7 +48,9 @@ final class TranslationCommand extends Command
->addOption('fill-empty', null, InputOption::VALUE_NONE, 'Pre-fills empty translations with the english version')
->addOption('delete-empty', null, InputOption::VALUE_NONE, 'Delete all empty keys and files which have no translated key at all')
->addOption('move-resname', null, InputOption::VALUE_REQUIRED, 'Move a resname from one file to another (needs "source" and "target" options)')
->addOption('move-all', null, InputOption::VALUE_NONE, 'Move all keys from one file to another (needs "source" and "target" options)')
->addOption('source', null, InputOption::VALUE_REQUIRED, 'Single source file to use')
->addOption('only-core', null, InputOption::VALUE_NONE, 'Do not include plugin and theme directories')
->addOption('target', null, InputOption::VALUE_REQUIRED, 'Single target file to use')
// DEEPL TRANSLATION FEATURE - UNTESTED
->addOption('translate-locale', null, InputOption::VALUE_REQUIRED, 'Translate into the given locale with Deepl')
@@ -68,10 +70,13 @@ final class TranslationCommand extends Command
$bases = [
'core' => $this->projectDirectory . '/translations/*.xlf',
'plugins' => $this->projectDirectory . Kernel::PLUGIN_DIRECTORY . '/*/Resources/translations/*.xlf',
'theme' => $this->projectDirectory . '/vendor/kevinpapst/tabler-bundle/translations/*.xlf',
];
if (!$input->getOption('only-core')) {
$bases['plugins'] = $this->projectDirectory . Kernel::PLUGIN_DIRECTORY . '/*/Resources/translations/*.xlf';
$bases['theme'] = $this->projectDirectory . '/vendor/kevinpapst/tabler-bundle/translations/*.xlf';
}
$sources = [];
if ($input->getOption('source') !== null) {
/** @var string $tmp */
@@ -146,6 +151,19 @@ final class TranslationCommand extends Command
return $this->moveResname($io, $moveResname, $sources, $targets);
}
// ==========================================================================
// Move all keys from source to target
// ==========================================================================
if ($input->getOption('move-all')) {
if (\count($sources) === 0 || \count($targets) === 0) {
$io->error('Moving all keys only works with one source and one target file');
return Command::FAILURE;
}
return $this->moveAll($io, $sources, $targets);
}
// ==========================================================================
// Fill empty translations with english version
// ==========================================================================
@@ -634,4 +652,66 @@ final class TranslationCommand extends Command
return Command::SUCCESS;
}
/**
* @param array<string> $sources
* @param array<string> $targets
*/
private function moveAll(SymfonyStyle $io, array $sources, array $targets): int
{
foreach ($sources as $source) {
$tmp = basename($source);
$pos = strpos($tmp, '.');
if ($pos === false) {
$io->error('Unexpected filename: ' . $source);
return Command::FAILURE;
}
$suffix = substr($tmp, $pos);
$target = null;
foreach ($targets as $t) {
if (str_ends_with($t, $suffix)) {
$target = $t;
}
}
if ($target === null) {
$io->error('Cannot find translation target file for source: ' . $source);
return Command::FAILURE;
}
$sourceDocument = new \DOMDocument('1.0');
$sourceDocument->load($source);
$targetDocument = new \DOMDocument('1.0');
$targetDocument->load($target);
$removeNodes = [];
/** @var \DOMElement $element */
foreach ($sourceDocument->getElementsByTagName('trans-unit') as $element) {
$newNode = $targetDocument->importNode($element, true);
$targetDocument->documentElement->firstElementChild->firstElementChild->appendChild($newNode); // @phpstan-ignore-line
$removeNodes[] = $element;
}
foreach ($removeNodes as $node) {
$sourceDocument->documentElement->firstElementChild->firstElementChild->removeChild($node); // @phpstan-ignore-line
}
$xmlDocument = new \DOMDocument('1.0');
$xmlDocument->preserveWhiteSpace = false;
$xmlDocument->formatOutput = true;
$xmlDocument->loadXML($sourceDocument->saveXML()); // @phpstan-ignore-line
file_put_contents($source, $xmlDocument->saveXML());
$xmlDocument = new \DOMDocument('1.0');
$xmlDocument->preserveWhiteSpace = false;
$xmlDocument->formatOutput = true;
$xmlDocument->loadXML($targetDocument->saveXML()); // @phpstan-ignore-line
file_put_contents($target, $xmlDocument->saveXML());
}
return Command::SUCCESS;
}
}