65 lines
2.5 KiB
PHP
65 lines
2.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace DoctrineMigrations;
|
|
|
|
use Doctrine\DBAL\Schema\Schema;
|
|
use Doctrine\Migrations\AbstractMigration;
|
|
|
|
/**
|
|
* Admin-edited mail bodies used to be plain text in which a single *asterisk* made a word
|
|
* bold. They are Markdown now (App\Email\MailBodyRenderer), where that is italic and bold
|
|
* is written **twice**, so every saved body is rewritten once.
|
|
*
|
|
* Only `body` is touched: `subject` never took markers, and `headline` is plain text under
|
|
* the new renderer, so a marker left in one is meant to stay the character it is.
|
|
*/
|
|
final class Version20260820000000 extends AbstractMigration
|
|
{
|
|
/**
|
|
* A single asterisk pair on one line. The lookarounds keep three things out: an already
|
|
* doubled **bold**, an escaped \* an admin wrote to get a literal asterisk, and a marker
|
|
* with whitespace next to it - "a * b * c" and "5 * 3" are arithmetic, not emphasis.
|
|
* That last rule is CommonMark's own, and slightly stricter than the renderer this
|
|
* replaces: a text that leant on the old, laxer matching loses its bold here rather
|
|
* than turning into something nobody wrote.
|
|
*/
|
|
private const SINGLE_EMPHASIS = '/(?<![\\\\*])\*(?!\*)(?!\s)([^*\n]+?)(?<![\s\\\\*])\*(?!\*)/u';
|
|
private const DOUBLE_EMPHASIS = '/(?<![\\\\*])\*\*(?!\*)(?!\s)([^*\n]+?)(?<![\s\\\\*])\*\*(?!\*)/u';
|
|
|
|
public function getDescription(): string
|
|
{
|
|
return 'Rewrite *bold* to **bold** in saved email texts, which are Markdown now';
|
|
}
|
|
|
|
public function up(Schema $schema): void
|
|
{
|
|
$this->rewriteBodies(self::SINGLE_EMPHASIS, '**$1**');
|
|
}
|
|
|
|
public function down(Schema $schema): void
|
|
{
|
|
$this->rewriteBodies(self::DOUBLE_EMPHASIS, '*$1*');
|
|
}
|
|
|
|
/**
|
|
* The rewrite happens in PHP, one row at a time: MySQL's REGEXP_REPLACE has no
|
|
* lookbehind, and getting this wrong would quietly mangle wording that goes out to
|
|
* every teamer. Only the read is done here and now - the writes go through addSql, so
|
|
* they are logged and rolled back like any other migration statement.
|
|
*/
|
|
private function rewriteBodies(string $pattern, string $replacement): void
|
|
{
|
|
foreach ($this->connection->fetchAllAssociative('SELECT id, body FROM email_text') as $row) {
|
|
$body = preg_replace($pattern, $replacement, (string) $row['body']);
|
|
|
|
if (null === $body || $body === $row['body']) {
|
|
continue;
|
|
}
|
|
|
|
$this->addSql('UPDATE email_text SET body = ? WHERE id = ?', [$body, $row['id']]);
|
|
}
|
|
}
|
|
}
|