Skip to content

Commit

Permalink
Don't cache blog post previews (#360)
Browse files Browse the repository at this point in the history
It's been cached when the `BlogPost` object was created before it was sent to `sendPreview()` (where caching was disabled a little bit too late), and also in `onValidate` where we still want the data to be validated (so can't call `setValidationScope([])`). The original `BlogPost` object is still cached in `actionEdit()`, but that's the original one. not the previewed one.
  • Loading branch information
spaze authored Jul 5, 2024
2 parents 6d2f8ae + 238abd1 commit da0975b
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 33 deletions.
7 changes: 3 additions & 4 deletions site/app/Articles/Articles.php
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,16 @@ private function enrichArticles(array $articles): array
}


public function buildArticle(Row $row): ArticlePublishedElsewhere
private function buildArticle(Row $row): ArticlePublishedElsewhere
{
$texy = $this->texyFormatter->getTexy();
$this->texyFormatter->setTopHeading(2);
return new ArticlePublishedElsewhere(
$row->id,
$this->texyFormatter->format($row->titleTexy, $texy),
$this->texyFormatter->format($row->titleTexy),
$row->titleTexy,
$row->href,
$row->published,
$this->texyFormatter->format($row->leadTexy, $texy),
$this->texyFormatter->format($row->leadTexy),
$row->leadTexy,
$row->sourceName,
$row->sourceHref,
Expand Down
12 changes: 7 additions & 5 deletions site/app/Articles/Blog/BlogPostFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,16 @@ public function create(
bool $omitExports,
): BlogPost {
$texy = $this->texyFormatter->getTexy();
$texyFormatter = $this->texyFormatter->withTexy($texy);

if ($allowedTagsGroups) {
$allowedTags = [];
foreach ($allowedTagsGroups as $allowedTagsGroup) {
$allowedTags = array_merge($allowedTags, $this->allowedTags[$allowedTagsGroup]);
}
$texy->allowedTags = $allowedTags;
}
$this->texyFormatter->setTopHeading(2);
$texyFormatter->setTopHeading(2);

$needsPreviewKey = $published === null || $published > new DateTime();
return new BlogPost(
Expand All @@ -87,16 +89,16 @@ public function create(
$localeId,
$locale,
$translationGroupId,
$this->texyFormatter->format($titleTexy, $texy),
$texyFormatter->format($titleTexy),
$titleTexy,
$leadTexy !== null ? $this->texyFormatter->formatBlock($leadTexy, $texy) : null,
$leadTexy !== null ? $texyFormatter->formatBlock($leadTexy) : null,
$leadTexy,
$this->texyFormatter->formatBlock($textTexy, $texy),
$texyFormatter->formatBlock($textTexy),
$textTexy,
$published,
$needsPreviewKey,
$previewKey,
$originallyTexy !== null ? $this->texyFormatter->formatBlock($originallyTexy, $texy) : null,
$originallyTexy !== null ? $texyFormatter->formatBlock($originallyTexy) : null,
$originallyTexy,
$ogImage,
$tags,
Expand Down
4 changes: 3 additions & 1 deletion site/app/Articles/Blog/BlogPostPreview.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ public function __construct(


/**
* @param callable(): BlogPost $createPost
* @param callable(?DefaultTemplate): void $sendTemplate
*/
public function sendPreview(BlogPost $post, DefaultTemplate $template, callable $sendTemplate): void
public function sendPreview(callable $createPost, DefaultTemplate $template, callable $sendTemplate): void
{
$this->texyFormatter->disableCache();
$post = $createPost();
$template->setFile(__DIR__ . '/../../Www/Presenters/templates/Post/default.latte');
$template->post = $post;
$template->edits = $post->hasId() ? $post->getEdits() : [];
Expand Down
23 changes: 15 additions & 8 deletions site/app/Form/PostFormFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,25 @@ public function create(callable $onSuccessAdd, callable $onSuccessEdit, DefaultT

$submitButton = $form->addSubmit('submit', 'Přidat');
$caption = $this->translator->translate('messages.label.preview');
$form->addSubmit('preview', $caption)
->setHtmlAttribute('data-loading-value', 'Moment…')
$previewButton = $form->addSubmit('preview', $caption);
$previewButton->setHtmlAttribute('data-loading-value', 'Moment…')
->setHtmlAttribute('data-original-value', $caption)
->onClick[] = function () use ($form, $post, $template, $sendTemplate): void {
$newPost = $this->buildPost($form->getFormValues(), $post?->getId());
$this->blogPostPreview->sendPreview($newPost, $template, $sendTemplate);
$this->blogPostPreview->sendPreview(
function () use ($form, $post): BlogPost {
return $this->buildPost($form->getFormValues(), $post?->getId());
},
$template,
$sendTemplate,
);
};

$form->onValidate[] = function (UiForm $form) use ($post, $previewKeyInput): void {
$newPost = $this->buildPost($form->getFormValues(), $post?->getId());
if ($newPost->needsPreviewKey() && $newPost->getPreviewKey() === null) {
$previewKeyInput->addError(sprintf('Tento %s příspěvek vyžaduje klíč pro náhled', $newPost->getPublishTime() === null ? 'nepublikovaný' : 'budoucí'));
$form->onValidate[] = function (UiForm $form) use ($previewButton, $post, $previewKeyInput): void {
if ($form->isSubmitted() !== $previewButton) {
$newPost = $this->buildPost($form->getFormValues(), $post?->getId());
if ($newPost->needsPreviewKey() && $newPost->getPreviewKey() === null) {
$previewKeyInput->addError(sprintf('Tento %s příspěvek vyžaduje klíč pro náhled', $newPost->getPublishTime() === null ? 'nepublikovaný' : 'budoucí'));
}
}
};
$form->onSuccess[] = function (UiForm $form) use ($onSuccessAdd, $onSuccessEdit, $post): void {
Expand Down
33 changes: 23 additions & 10 deletions site/app/Formatter/TexyFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
class TexyFormatter
{

private const string CACHE_KEY_DELIMITER = '|';

private ?Texy $texy = null;

private bool $cacheResult = true;
Expand Down Expand Up @@ -144,32 +146,34 @@ public function translate(string $message, array $replacements = []): Html
*
* Suitable for "inline" strings like headers.
*/
public function format(string $text, ?Texy $texy = null): Html
public function format(string $text): Html
{
return $this->replace("{$text}|" . __FUNCTION__, function () use ($text, $texy): string {
return Strings::replace(($texy ?? $this->getTexy())->process($text), '~^\s*<p[^>]*>(.*)</p>\s*$~s', '$1');
$texy = $this->texy ?? $this->getTexy();
return $this->replace($text . self::CACHE_KEY_DELIMITER . __FUNCTION__, $texy, function () use ($texy, $text): string {
return Strings::replace($texy->process($text), '~^\s*<p[^>]*>(.*)</p>\s*$~s', '$1');
});
}


/**
* Format string.
*/
public function formatBlock(string $text, ?Texy $texy = null): Html
public function formatBlock(string $text): Html
{
return $this->replace("{$text}|" . __FUNCTION__, function () use ($text, $texy): string {
return ($texy ?? $this->getTexy())->process($text);
$texy = $this->texy ?? $this->getTexy();
return $this->replace($text . self::CACHE_KEY_DELIMITER . __FUNCTION__, $texy, function () use ($texy, $text): string {
return $texy->process($text);
});
}


/**
* @param callable(): string $callback
*/
private function replace(string $key, callable $callback): Html
private function replace(string $key, Texy $texy, callable $callback): Html
{
if ($this->cacheResult) {
$result = $this->cache->get($this->getCacheKey($key), function (ItemInterface $item, bool &$save) use ($callback): string {
$result = $this->cache->get($this->getCacheKey($key, $texy), function (ItemInterface $item, bool &$save) use ($callback): string {
$item->expiresAt(null);
$save = true;
return $callback();
Expand All @@ -194,11 +198,12 @@ function (array $matches) use ($replacements): string {
}


public function getCacheKey(string $text): string
public function getCacheKey(string $text, Texy $texy): string
{
$key = "{$text}|" . serialize($texy->allowedTags);
// Make the key shorter because Symfony Cache stores it in comments in cache files
// Don't hash the locale to make it visible inside cache files
return Hash::nonCryptographic($text) . '.' . $this->translator->getDefaultLocale();
return Hash::nonCryptographic($key) . '.' . $this->translator->getDefaultLocale();
}


Expand All @@ -208,4 +213,12 @@ public function disableCache(): self
return $this;
}


public function withTexy(Texy $texy): self
{
$texyFormatter = clone $this;
$texyFormatter->texy = $texy;
return $texyFormatter;
}

}
3 changes: 2 additions & 1 deletion site/tests/Articles/ArticlesTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class ArticlesTest extends TestCase
'slug' => null,
'href' => 'https://example.com/article-1',
'published' => new DateTime('3 years ago'),
'leadTexy' => 'Excerpt 1',
'leadTexy' => "Excerpt 1\n#########\nFoo",
'textTexy' => null,
'sourceName' => 'Source 1',
'sourceHref' => 'https://source1.example/',
Expand Down Expand Up @@ -155,6 +155,7 @@ class ArticlesTest extends TestCase
$this->database->addFetchAllResult($fetchResult);
$articles = $this->articles->getAll();
Assert::type(ArticlePublishedElsewhere::class, $articles[0]);
Assert::same("<h2 id=\"excerpt-1\">Excerpt 1</h2>\n\n<p>Foo</p>\n", $articles[0]->getSummary()?->render());
Assert::type(BlogPost::class, $articles[1]);
Assert::type(ArticlePublishedElsewhere::class, $articles[2]);
Assert::type(BlogPost::class, $articles[3]);
Expand Down
12 changes: 9 additions & 3 deletions site/tests/Articles/Blog/BlogPostPreviewTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,15 @@ class BlogPostPreviewTest extends TestCase
$template = $this->templateFactory->createTemplate($presenter);
$rendered = '';
Assert::noError(function () use ($post, $template, &$rendered): void {
$this->blogPostPreview->sendPreview($post, $template, function (?DefaultTemplate $template) use (&$rendered): void {
$rendered = $template?->renderToString() ?? '';
});
$this->blogPostPreview->sendPreview(
function () use ($post): BlogPost {
return $post;
},
$template,
function (?DefaultTemplate $template) use (&$rendered): void {
$rendered = $template?->renderToString() ?? '';
},
);
});
Assert::contains('<p>Text <strong>something</strong></p>', $rendered);
}
Expand Down
2 changes: 1 addition & 1 deletion site/tests/Formatter/TexyFormatterTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class TexyFormatterTest extends TestCase
$text = '**anoff**';
$expected = '<strong>anoff</strong>';

$key = $this->texyFormatter->getCacheKey("{$text}|format");
$key = $this->texyFormatter->getCacheKey("{$text}|format", $this->texyFormatter->getTexy());
Assert::same($expected, $this->texyFormatter->format($text)->toHtml());
Assert::true($this->cacheInterface->getItem($key)->isHit());

Expand Down

0 comments on commit da0975b

Please sign in to comment.