Create a customized myers diff based system for snip content
This commit is contained in:
parent
a18eda6748
commit
2db9c5f1d9
77
deploy.php
77
deploy.php
@ -3,6 +3,7 @@
|
||||
namespace Deployer;
|
||||
|
||||
require_once 'recipe/common.php';
|
||||
require_once 'deploy/git.php';
|
||||
|
||||
// Project name
|
||||
set('application', 'snips');
|
||||
@ -13,82 +14,48 @@ set('repository', 'git@git.loken.nl:ardent/Snips.git');
|
||||
// [Optional] Allocate tty for git clone. Default value is false.
|
||||
set('git_tty', true);
|
||||
|
||||
// Shared files/dirs between deploys
|
||||
// Shared files/dirs between deploys
|
||||
set('shared_dirs', ['var/log', 'var/sessions']);
|
||||
set('shared_files', ['.env.local']);
|
||||
//set('writable_dirs', ['var']);
|
||||
|
||||
set('migrations_config', '');
|
||||
set('allow_anonymous_stats', false);
|
||||
|
||||
set('console_options', fn() => '--no-interaction');
|
||||
set('bin/console', fn() => parse('{{release_path}}/bin/console'));
|
||||
|
||||
// Hosts
|
||||
host('snips.loken.nl')
|
||||
->setRemoteUser('www-data')
|
||||
->set('branch', function () {
|
||||
return input()->getOption('branch') ?: 'master';
|
||||
})
|
||||
->set('deploy_path', '~/snips.loken.nl');
|
||||
|
||||
set('bin/console', function () {
|
||||
return parse('{{release_path}}/bin/console');
|
||||
});
|
||||
|
||||
set('console_options', function () {
|
||||
return '--no-interaction';
|
||||
});
|
||||
->set('deploy_path', '~/snips.loken.nl')
|
||||
;
|
||||
|
||||
desc('Clear cache');
|
||||
task('cache:clear', function () {
|
||||
run('{{bin/php}} {{bin/console}} cache:clear {{console_options}} --no-warmup');
|
||||
});
|
||||
task('cache:clear', fn() => run('{{bin/php}} {{bin/console}} cache:clear {{console_options}} --no-warmup'));
|
||||
|
||||
desc('Warm up cache');
|
||||
task('cache:warmup', function () {
|
||||
run('{{bin/php}} {{bin/console}} cache:warmup {{console_options}}');
|
||||
});
|
||||
task('cache:warmup', fn() => run('{{bin/php}} {{bin/console}} cache:warmup {{console_options}}'));
|
||||
|
||||
desc('Migrate database');
|
||||
task('database:migrate', function () {
|
||||
// $options = '--allow-no-migration';
|
||||
// if (get('migrations_config') !== '') {
|
||||
// $options = sprintf('%s --configuration={{release_path}}/{{migrations_config}}', $options);
|
||||
// }
|
||||
//
|
||||
// run(sprintf('{{bin/php}} {{bin/console}} doctrine:migrations:migrate %s {{console_options}}', $options));
|
||||
run('{{bin/php}} {{bin/console}} doctrine:schema:update --force');
|
||||
$options = '--allow-no-migration';
|
||||
if (get('migrations_config') !== '') {
|
||||
$options = sprintf('%s --configuration={{release_path}}/{{migrations_config}}', $options);
|
||||
}
|
||||
|
||||
run(sprintf('{{bin/php}} {{bin/console}} doctrine:migrations:migrate %s {{console_options}}', $options));
|
||||
});
|
||||
|
||||
task('deployment:log', function () { //https://stackoverflow.com/questions/59686270/how-to-log-deployments-in-deployer
|
||||
$branch = parse('{{branch}}');
|
||||
$date = date('Y-m-d H:i:s');
|
||||
$commitHashShort = runLocally('git rev-parse --short HEAD');
|
||||
// $commitHash = runLocally('git rev-parse HEAD');
|
||||
$commit = explode(PHP_EOL, runLocally('git log -1 --pretty="%H%n%ci"'));
|
||||
$commitHash = $commit[0];
|
||||
$commitDate = $commit[1];
|
||||
|
||||
// $line = sprintf('%s %s branch="%s" hash="%s"', $date, $commitHashShort, $branch, $commitHash);
|
||||
$projectUrlBase = 'https://git.loken.nl/ardent/Snips';
|
||||
$array = [
|
||||
'branch' => $branch,
|
||||
'branchUrl' => sprintf('%s/src/branch/%s', $projectUrlBase, $branch),
|
||||
'date' => $date,
|
||||
'commitHashShort' => $commitHashShort,
|
||||
'commitHashLong' => $commitHash,
|
||||
'commitDate' => $commitDate,
|
||||
'commitUrl' => sprintf('%s/commit/%s', $projectUrlBase, $commitHash),
|
||||
'projectUrl' => $projectUrlBase,
|
||||
];
|
||||
$json = json_encode($array, JSON_PRETTY_PRINT);
|
||||
|
||||
runLocally("echo '$json' > release.json");
|
||||
upload('release.json', '{{release_path}}/release.json');
|
||||
desc('Shows current deployed version');
|
||||
task('deploy:current', function () {
|
||||
$current = run('readlink {{deploy_path}}/current');
|
||||
writeln("Current deployed version: $current");
|
||||
});
|
||||
|
||||
//desc('Deploy project');
|
||||
//task('deploy', [
|
||||
// 'deployment:log',
|
||||
//]);
|
||||
|
||||
desc('Deploy project');
|
||||
task('deploy', [
|
||||
'deploy:prepare',
|
||||
@ -100,9 +67,9 @@ task('deploy', [
|
||||
'deploy:symlink',
|
||||
'deploy:unlock',
|
||||
'deploy:cleanup',
|
||||
'deploy:current',
|
||||
]);
|
||||
|
||||
after('deploy', 'deploy:success');
|
||||
|
||||
// [Optional] if deploy fails automatically unlock.
|
||||
after('deploy:failed', 'deploy:unlock');
|
||||
after('deploy:failed', 'deploy:unlock');
|
27
deploy/git.php
Normal file
27
deploy/git.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Deployer;
|
||||
|
||||
desc('Transfers information about current git commit to server');
|
||||
task('deployment:log', function () { //https://stackoverflow.com/questions/59686270/how-to-log-deployments-in-deployer
|
||||
$branch = parse('{{branch}}');
|
||||
$date = date('Y-m-d H:i:s');
|
||||
$commitHashShort = runLocally('git rev-parse --short HEAD');
|
||||
// $commitHash = runLocally('git rev-parse HEAD');
|
||||
$commit = explode(PHP_EOL, runLocally('git log -1 --pretty="%H%n%ci"'));
|
||||
$commitHash = $commit[0];
|
||||
$commitDate = $commit[1];
|
||||
|
||||
// $line = sprintf('%s %s branch="%s" hash="%s"', $date, $commitHashShort, $branch, $commitHash);
|
||||
$array = [
|
||||
'branch' => $branch,
|
||||
'date' => $date,
|
||||
'commitHashShort' => $commitHashShort,
|
||||
'commitHashLong' => $commitHash,
|
||||
'commitDate' => $commitDate,
|
||||
];
|
||||
$json = json_encode($array, JSON_PRETTY_PRINT);
|
||||
|
||||
runLocally("echo '$json' > release.json");
|
||||
upload('release.json', '{{release_path}}/release.json');
|
||||
});
|
@ -7,8 +7,8 @@ use App\Form\ConfirmationType;
|
||||
use App\Form\SnipType;
|
||||
use App\Repository\SnipRepository;
|
||||
use App\Security\Voter\SnipVoter;
|
||||
use App\Service\SnipContent\SnipContentService;
|
||||
use App\Service\SnipParser\Pipeline;
|
||||
use App\Service\SnipServiceFactory;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -20,10 +20,8 @@ class SnipController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SnipRepository $repository,
|
||||
private readonly SnipServiceFactory $snipServiceFactory,
|
||||
)
|
||||
{
|
||||
}
|
||||
private readonly SnipContentService $contentService,
|
||||
) {}
|
||||
|
||||
#[Route('/', name: '_index')]
|
||||
public function index(): Response
|
||||
@ -48,11 +46,9 @@ class SnipController extends AbstractController
|
||||
{
|
||||
$this->denyAccessUnlessGranted(SnipVoter::VIEW, $snip);
|
||||
|
||||
$snipService = $this->snipServiceFactory->create($snip);
|
||||
|
||||
return $this->render('snip/single.html.twig', [
|
||||
'snip' => $snip,
|
||||
'content' => $pl->parse($snipService->getActiveText()),
|
||||
'content' => $pl->parse($this->contentService->getActiveText($snip)),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -62,7 +58,7 @@ class SnipController extends AbstractController
|
||||
$this->denyAccessUnlessGranted(SnipVoter::VIEW, $snip);
|
||||
|
||||
$response = new Response(
|
||||
$pl->clean($this->snipServiceFactory->create($snip)->getActiveText()),
|
||||
$pl->clean($this->contentService->getActiveText($snip)),
|
||||
Response::HTTP_OK,
|
||||
['Content-Type' => 'text/html']
|
||||
);
|
||||
@ -70,7 +66,8 @@ class SnipController extends AbstractController
|
||||
->setVary(['Accept', 'Accept-Encoding'])
|
||||
->setEtag(md5($response->getContent()))
|
||||
->setTtl(3600)
|
||||
->setClientTtl(300);
|
||||
->setClientTtl(300)
|
||||
;
|
||||
|
||||
if (!$request->isNoCache()) {
|
||||
$response->isNotModified($request);
|
||||
@ -87,13 +84,13 @@ class SnipController extends AbstractController
|
||||
$form = $this->createForm(SnipType::class, $snip);
|
||||
$form->add('Save', SubmitType::class);
|
||||
if ($snip->getId()) {
|
||||
$form->get('content')->setData($this->snipServiceFactory->create($snip)->getActiveText());
|
||||
$form->get('content')->setData($this->contentService->getActiveText($snip));
|
||||
}
|
||||
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->repository->save($snip);
|
||||
$this->snipServiceFactory->create($snip)->update($form->get('content')->getData());
|
||||
$this->contentService->update($snip, $form->get('content')->getData());
|
||||
|
||||
$this->addFlash('success', sprintf('Snip "%s" saved', $snip));
|
||||
|
||||
@ -113,7 +110,8 @@ class SnipController extends AbstractController
|
||||
{
|
||||
$snip = new Snip();
|
||||
$snip->setCreatedAtNow()
|
||||
->setCreatedBy($this->getUser());
|
||||
->setCreatedBy($this->getUser())
|
||||
;
|
||||
|
||||
return $this->edit($snip, $request);
|
||||
}
|
||||
@ -126,7 +124,7 @@ class SnipController extends AbstractController
|
||||
$form = $this->createForm(ConfirmationType::class);
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->snipServiceFactory->create($snip)->delete();
|
||||
$this->contentService->delete($snip);
|
||||
$this->repository->remove($snip);
|
||||
$this->addFlash('success', sprintf('Snip "%s" deleted', $snip));
|
||||
return $this->redirectToRoute('snip_index');
|
||||
|
@ -5,7 +5,7 @@ namespace App\Controller;
|
||||
use App\Entity\Snip;
|
||||
use App\Entity\SnipContent;
|
||||
use App\Security\Voter\SnipVoter;
|
||||
use App\Service\SnipServiceFactory;
|
||||
use App\Service\SnipContent\SnipContentService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@ -14,7 +14,7 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
class VersionController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly SnipServiceFactory $snipServiceFactory,
|
||||
private readonly SnipContentService $contentService,
|
||||
) {}
|
||||
|
||||
#[Route('/', name: '_index')]
|
||||
@ -32,7 +32,7 @@ class VersionController extends AbstractController
|
||||
{
|
||||
$this->denyAccessUnlessGranted(SnipVoter::EDIT, $snip);
|
||||
|
||||
$this->snipServiceFactory->create($snip)->setVersion($version);
|
||||
$this->contentService->setVersion($snip, $version);
|
||||
$this->addFlash('success', 'Snip version set to ' . $version->getId());
|
||||
return $this->redirectToRoute('snip_single', ['snip' => $snip->getId()]);
|
||||
}
|
||||
|
@ -32,6 +32,9 @@ class SnipContent
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
private ?string $text = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?array $diff = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->children = new ArrayCollection();
|
||||
@ -107,4 +110,16 @@ class SnipContent
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDiff(): ?array
|
||||
{
|
||||
return $this->diff;
|
||||
}
|
||||
|
||||
public function setDiff(?array $diff): static
|
||||
{
|
||||
$this->diff = $diff;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
171
src/Service/SnipContent/MyersDiff.php
Normal file
171
src/Service/SnipContent/MyersDiff.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\SnipContent;
|
||||
|
||||
class MyersDiff
|
||||
{
|
||||
private const string NEWLINE = "\r\n";
|
||||
|
||||
/**
|
||||
* Backtrack through the intermediate results to extract the "snakes" that
|
||||
* are visited on the chosen "D-path".
|
||||
*
|
||||
* @param string[] $v_save Intermediate results
|
||||
* @param int $x End position
|
||||
* @param int $y End position
|
||||
*
|
||||
* @return int[][]
|
||||
*/
|
||||
private static function extractSnakes(array $v_save, int $x, int $y): array
|
||||
{
|
||||
$snakes = [];
|
||||
for ($d = count($v_save) - 1; $x >= 0 && $y >= 0; $d--) {
|
||||
array_unshift($snakes, [$x, $y]);
|
||||
|
||||
$v = $v_save[$d];
|
||||
$k = $x - $y;
|
||||
|
||||
if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) {
|
||||
$k_prev = $k + 1;
|
||||
} else {
|
||||
$k_prev = $k - 1;
|
||||
}
|
||||
|
||||
$x = $v[$k_prev];
|
||||
$y = $x - $k_prev;
|
||||
}
|
||||
|
||||
return $snakes;
|
||||
}
|
||||
|
||||
private static function formatCompact(array $snakes, array $b): array
|
||||
{
|
||||
$solution = [];
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
|
||||
foreach ($snakes as $snake) {
|
||||
// Deletions
|
||||
while ($snake[0] - $snake[1] > $x - $y) {
|
||||
$count = 0;
|
||||
while ($snake[0] - $snake[1] > $x - $y) {
|
||||
$x++;
|
||||
$count++;
|
||||
}
|
||||
$solution[] = ['D', $count];
|
||||
}
|
||||
|
||||
// Insertions
|
||||
while ($snake[0] - $snake[1] < $x - $y) {
|
||||
$values = [];
|
||||
while ($snake[0] - $snake[1] < $x - $y) {
|
||||
$values[] = $b[$y];
|
||||
$y++;
|
||||
}
|
||||
if ($solution[count($solution) - 1][0] === 'I') {
|
||||
$solution[count($solution) - 1][1] = array_merge($solution[count($solution) - 1][1], $values);
|
||||
} else {
|
||||
$solution[] = ['I', $values];
|
||||
}
|
||||
}
|
||||
|
||||
// Keeps (snake diagonals)
|
||||
$count = 0;
|
||||
while ($x < $snake[0]) {
|
||||
$x++;
|
||||
$y++;
|
||||
$count++;
|
||||
}
|
||||
if ($count > 0) {
|
||||
$solution[] = ['K', $count];
|
||||
}
|
||||
}
|
||||
|
||||
return $solution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the shortest edit sequence to convert $x into $y.
|
||||
*
|
||||
* @param string $textFrom - tokens (characters, words or lines)
|
||||
* @param string $textTo - tokens (characters, words or lines)
|
||||
* @param ?callable $compare - comparison function for tokens. Signature is compare($x, $y):bool. If null, === is used.
|
||||
*
|
||||
* @return array[] - pairs of token and edit (-1 for delete, 0 for keep, +1 for insert)
|
||||
*/
|
||||
public static function calculate(string $textFrom, string $textTo, ?callable $compare = null): array
|
||||
{
|
||||
$a = self::explode($textFrom);
|
||||
$b = self::explode($textTo);
|
||||
|
||||
if ($compare === null) {
|
||||
$compare = function ($x, $y) {
|
||||
return $x === $y;
|
||||
};
|
||||
}
|
||||
|
||||
$n = count($a);
|
||||
$m = count($b);
|
||||
$a = array_values($a);
|
||||
$b = array_values($b);
|
||||
$max = $m + $n;
|
||||
|
||||
$v_save = [];
|
||||
|
||||
$v = [1 => 0];
|
||||
for ($d = 0; $d <= $max; $d++) {
|
||||
for ($k = -$d; $k <= $d; $k += 2) {
|
||||
if ($k === -$d || $k !== $d && $v[$k - 1] < $v[$k + 1]) {
|
||||
$x = $v[$k + 1];
|
||||
} else {
|
||||
$x = $v[$k - 1] + 1;
|
||||
}
|
||||
$y = $x - $k;
|
||||
while ($x < $n && $y < $m && $compare($a[$x], $b[$y])) {
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
$v[$k] = $x;
|
||||
$v_save[$d] = $v;
|
||||
if ($x === $n && $y === $m) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::formatCompact(self::extractSnakes($v_save, $n, $m), $b);
|
||||
}
|
||||
|
||||
public static function rebuildBFromCompact(string $textFrom, array $diff): string
|
||||
{
|
||||
$a = self::explode($textFrom);
|
||||
$b = [];
|
||||
$x = 0;
|
||||
|
||||
foreach ($diff as [$op, $data]) {
|
||||
if ($op === 'K') {
|
||||
for ($i = 0; $i < $data; $i++) {
|
||||
$b[] = $a[$x++];
|
||||
}
|
||||
} elseif ($op === 'D') {
|
||||
$x += $data; // skip deleted
|
||||
} elseif ($op === 'I') {
|
||||
foreach ($data as $v) {
|
||||
$b[] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::implode($b);
|
||||
}
|
||||
|
||||
private static function explode(string $text): array
|
||||
{
|
||||
return explode(self::NEWLINE, $text);
|
||||
}
|
||||
|
||||
private static function implode(array $text): string
|
||||
{
|
||||
return implode(self::NEWLINE, $text);
|
||||
}
|
||||
}
|
@ -8,49 +8,96 @@ use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
readonly class SnipContentService
|
||||
{
|
||||
|
||||
public function __construct(
|
||||
private Snip $snip,
|
||||
private EntityManagerInterface $em,
|
||||
) {}
|
||||
|
||||
public function update(string $snipContents): void
|
||||
public function update(Snip $snip, string $snipContents): void
|
||||
{
|
||||
if ($this->snip->getActiveVersion()?->getText() === $snipContents) {
|
||||
$parentContent = $snip->getActiveVersion();
|
||||
if ($this->rebuildText($parentContent) === $snipContents) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new snipContent entity with previous one as parent
|
||||
$content = new SnipContent();
|
||||
$content
|
||||
->setText($snipContents)
|
||||
->setSnip($this->snip)
|
||||
->setSnip($snip)
|
||||
;
|
||||
if ($this->snip->getActiveVersion() !== null) {
|
||||
$content->setParent($this->snip->getActiveVersion());
|
||||
if ($parentContent !== null) {
|
||||
$content->setParent($parentContent);
|
||||
$this->contentToRelative($parentContent);
|
||||
}
|
||||
|
||||
$this->em->persist($content);
|
||||
$this->em->flush();
|
||||
|
||||
$this->snip->setActiveVersion($content);
|
||||
$this->em->persist($this->snip);
|
||||
$snip->setActiveVersion($content);
|
||||
$this->em->persist($snip);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
// Shortcut to get the active text
|
||||
public function getActiveText(): string
|
||||
public function getActiveText(Snip $snip): string
|
||||
{
|
||||
$contentRepo = $this->em->getRepository(SnipContent::class);
|
||||
return $contentRepo->find($this->snip->getActiveVersion())->getText();
|
||||
return $this->rebuildText($contentRepo->find($snip->getActiveVersion()));
|
||||
}
|
||||
|
||||
public function setVersion(SnipContent $version): void
|
||||
public function rebuildText(SnipContent $snipContent): string
|
||||
{
|
||||
$this->snip->setActiveVersion($version);
|
||||
$this->em->persist($this->snip);
|
||||
if ($snipContent->getText()) {
|
||||
return $snipContent->getText();
|
||||
}
|
||||
|
||||
$parentContent = $snipContent->getParent();
|
||||
if ($parentContent === null && $snipContent->getDiff() === null) {
|
||||
return '---Something went very wrong, cant rebuild the text---';
|
||||
}
|
||||
|
||||
return MyersDiff::rebuildBFromCompact(
|
||||
$this->rebuildText($parentContent), $snipContent->getDiff()
|
||||
);
|
||||
}
|
||||
|
||||
public function setVersion(Snip $snip, SnipContent $version): void
|
||||
{
|
||||
$activeVersion = $snip->getActiveVersion();
|
||||
$this->contentToAbsolute($version);
|
||||
$this->contentToRelative($activeVersion);
|
||||
|
||||
$snip->setActiveVersion($version);
|
||||
$this->em->persist($snip);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
public function contentToRelative(SnipContent $content): void
|
||||
{
|
||||
if ($content->getText() === null || $content->getParent() === null) {
|
||||
return;
|
||||
}
|
||||
$contentText = $content->getText();
|
||||
$parentText = $this->rebuildText($content->getParent());
|
||||
$diff = MyersDiff::calculate($parentText, $contentText);
|
||||
$content->setDiff($diff);
|
||||
$content->setText(null);
|
||||
$this->em->persist($content);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function contentToAbsolute(SnipContent $content): void
|
||||
{
|
||||
if ($content->getDiff() === null) {
|
||||
return;
|
||||
}
|
||||
$content->setText($this->rebuildText($content));
|
||||
$content->setDiff(null);
|
||||
$this->em->persist($content);
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
public function delete(Snip $snip): void
|
||||
{
|
||||
// Cleanup the versions
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ namespace App\Service\SnipParser\Stages;
|
||||
use App\Repository\SnipContentRepository;
|
||||
use App\Repository\SnipRepository;
|
||||
use App\Security\Voter\SnipVoter;
|
||||
use App\Service\SnipContent\SnipContentService;
|
||||
use App\Service\SnipParser\Pipeline;
|
||||
use League\Pipeline\StageInterface;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
@ -17,6 +18,7 @@ class IncludeReferenceStage implements StageInterface
|
||||
#[Autowire(lazy: true)] private readonly SnipRepository $snipRepository,
|
||||
#[Autowire(lazy: true)] private readonly SnipContentRepository $snipContentRepository,
|
||||
#[Autowire(lazy: true)] private readonly Pipeline $pipeline,
|
||||
#[Autowire(lazy: true)] private readonly SnipContentService $snipContentService,
|
||||
) {}
|
||||
|
||||
public function __invoke(mixed $payload): string
|
||||
@ -51,7 +53,9 @@ class IncludeReferenceStage implements StageInterface
|
||||
return sprintf('<span title="access denied">%s</span>', $matches[0]);
|
||||
}
|
||||
|
||||
return $this->pipeline->parse($content->getText());
|
||||
return $this->pipeline->parse(
|
||||
$this->snipContentService->rebuildText($content)
|
||||
);
|
||||
}, $payload);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user