Fix lost form submissions on archives, the unlocked update finish and six more

Update Symfony to 7.4.23 (12 advisories in 8 packages).

- A form can be sent from /beitraege, /kategorie/... and /schlagwort/...;
  it answered 404 before anything was stored or mailed.
- Booleans reach the database as integers, so MySQL in strict mode
  accepts form submissions, task switches and plugin states.
- A stepped form with a required multiple choice shows its message
  instead of stopping on a TypeError.
- The second update step runs under the update lock; a concurrent
  finish no longer rolls a successful update back.
- Accordion entries and gallery images can be selected from the
  editor panel again.
- Category and tag names are escaped in the archive heading.
- Restoring a published page from the bin needs the publisher role.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Fable 5.2 committed 2026-11-10 11:15:49 +01:00
1 parent 441e629e80
commit d464c82ac8
17 files changed
+324 -168

No files matched your search

+24 -1
View File
@@ -23,6 +23,8 @@ namespace App\Ops;
*/
final class ArchiveUpdate
{
public const LAEUFT_BEREITS = 'Der Abschluss läuft bereits in einer anderen Anfrage. Diese Seite in einem Moment neu laden.';
/** Was aus dem Archiv übernommen wird. Alles andere im Projektstamm bleibt unberührt. */
private const ERSETZEN = ['bin', 'config', 'migrations', 'src', 'templates', 'vendor', 'composer.json', 'composer.lock', 'migrations.php', 'cli-config.php', 'VERSION'];
@@ -363,7 +365,28 @@ final class ArchiveUpdate
* gibt null zurück oder den Grund, warum sie nicht
* läuft - dann wird zurückgeschaltet
*/
public function finish(Installation $installation, ?callable $pruefen = null): array
public function finish(Installation $installation, ?callable $pruefen = null, bool $gesperrt = false): array
{
// Nur ein Abschluss zur selben Zeit. Die Zwischenseite ruft ihn von selbst auf und bietet
// daneben einen Link an: Zwei Abschlüsse nebeneinander ließen den zweiten an der bereits
// laufenden Migration scheitern - und der schaltete dann zurück, Code und Datenbank,
// obwohl der erste gelungen war. $gesperrt: der Aufrufer hält die Sperre schon.
$sperre = $gesperrt ? null : $this->updater->lock();
if (!$gesperrt && null === $sperre) {
return ['ok' => false, 'laeuft' => true, 'steps' => [['step' => 'Abschluss', 'ok' => true, 'detail' => self::LAEUFT_BEREITS]]];
}
try {
return $this->abschliessen($installation, $pruefen);
} finally {
if (null !== $sperre) {
flock($sperre, LOCK_UN);
fclose($sperre);
}
}
}
/** @param (callable(): ?string)|null $pruefen */
private function abschliessen(Installation $installation, ?callable $pruefen): array
{
$schritte = [];
$offen = $this->pending();
+2 -2
View File
@@ -291,7 +291,7 @@ final class Scheduler
$this->db->insert('scheduled_task', [
'name' => $name,
'interval_seconds' => $task->interval(),
'enabled' => true,
'enabled' => 1,
'next_run_at' => $now->modify('+120 seconds')->format('Y-m-d H:i:s'),
'last_message' => '',
'last_duration_ms' => 0,
@@ -357,7 +357,7 @@ final class Scheduler
public function enable(string $name, bool $on): void
{
$this->db->executeStatement('UPDATE scheduled_task SET enabled = ? WHERE name = ?', [$on, $name]);
$this->db->executeStatement('UPDATE scheduled_task SET enabled = ? WHERE name = ?', [(int) $on, $name]);
$this->refreshState();
}