Skip to content

Commit

Permalink
Support preview tokens for custom blueprint config
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasbestle committed Dec 5, 2024
1 parent c452ffc commit cef90a8
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 52 deletions.
60 changes: 49 additions & 11 deletions src/Content/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Kirby\Exception\LogicException;
use Kirby\Exception\NotFoundException;
use Kirby\Form\Form;
use Kirby\Http\Query;
use Kirby\Http\Uri;
use Kirby\Toolkit\Str;

Expand Down Expand Up @@ -431,8 +432,13 @@ protected function previewTokenFromUrl(string $url): string|null
return null;
}

// get rid of all modifiers after the path
$uri = new Uri($url);
$uri->fragment = null;
$uri->query = new Query([]);

$data = [
'uri' => Str::after($url, $localPrefix),
'uri' => Str::after($uri->toString(), $localPrefix),
'versionId' => $this->id->value()
];

Expand Down Expand Up @@ -590,24 +596,56 @@ public function url(): string|null

$url = $this->model->blueprint()->preview();

// preview was disabled
if ($url === false) {
return null;
}

$url = match ($url) {
true, null => $this->model->url(),
default => $url
};

$uri = new Uri($url);

// we only need to add a token for draft and changes previews
if (
($this->model instanceof Page && $this->model->isDraft() === true) ||
$this->id->is('changes') === true
($this->model instanceof Site || $this->model->isDraft() === false) &&
$this->id->is('changes') === false
) {
$uri->query->_token = $this->previewToken();
return match (true) {
is_string($url) => $url,
default => $this->model->url()
};
}

// check if the URL was customized
if (is_string($url) === true) {
return $this->urlFromOption($url);
}

// it wasn't, use the safer/more reliable model-based preview token
return $this->urlParams($this->model->url(), $this->previewToken());
}

/**
* Returns the preview URL based on an arbitrary URL from
* the blueprint option
*/
protected function urlFromOption(string $url): string
{
// try to determine a token for a local preview
// (we cannot determine the token for external previews)
if ($token = $this->previewTokenFromUrl($url)) {
return $this->urlParams($url, $token);
}

// fall back to the URL as defined in the blueprint
return $url;
}

/**
* Assembles the preview URL with the added `_token` and `_version`
* query params, no matter if the base URL already contains query params
*/
protected function urlParams(string $baseUrl, string $token): string
{
$uri = new Uri($baseUrl);
$uri->query->_token = $token;

if ($this->id->is('changes') === true) {
$uri->query->_version = 'changes';
}
Expand Down
27 changes: 14 additions & 13 deletions tests/Cms/Pages/PageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -607,18 +607,18 @@ public function testPreviewUrlUnauthenticated()
public static function previewUrlProvider(): array
{
return [
[null, '/test', false],
[null, '/test?{token}', true],
[true, '/test', false],
[true, '/test?{token}', true],
['/something/different', '/something/different', false],
['/something/different', '/something/different?{token}', true],
['{{ site.url }}#{{ page.slug }}', '/#test', false],
['{{ site.url }}#{{ page.slug }}', '/?{token}#test', true],
['{{ page.url }}?preview=true', '/test?preview=true&{token}', true],
[false, null, false],
[false, null, true],
[null, null, false, false],
[null, '/test', null, false],
[null, '/test?{token}', 'test', true],
[true, '/test', null, false],
[true, '/test?{token}', 'test', true],
['/something/different', '/something/different', null, false],
['/something/different', '/something/different?{token}', 'something\/different', true],
['{{ site.url }}#{{ page.slug }}', '/#test', null, false],
['{{ site.url }}#{{ page.slug }}', '/?{token}#test', '', true],
['{{ page.url }}?preview=true', '/test?preview=true&{token}', 'test', true],
[false, null, null, false],
[false, null, null, true],
[null, null, null, false, false],
];
}

Expand All @@ -628,6 +628,7 @@ public static function previewUrlProvider(): array
public function testCustomPreviewUrl(
$input,
$expected,
$expectedUri,
bool $draft,
bool $authenticated = true
): void {
Expand Down Expand Up @@ -677,7 +678,7 @@ public function testCustomPreviewUrl(
]);

if ($draft === true && $expected !== null) {
$expectedToken = substr(hash_hmac('sha1', '{"uri":"' . $page->uri() . '","versionId":"latest"}', $page->kirby()->root('content')), 0, 10);
$expectedToken = substr(hash_hmac('sha1', '{"uri":"' . $expectedUri . '","versionId":"latest"}', $page->kirby()->root('content')), 0, 10);
$expected = str_replace(
'{token}',
'_token=' . $expectedToken,
Expand Down
61 changes: 33 additions & 28 deletions tests/Content/VersionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1511,34 +1511,38 @@ public static function pageUrlProvider(): array
{
return [
// latest version
[null, '/test', false, 'latest'],
[null, '/test?{token}', true, 'latest'],
[true, '/test', false, 'latest'],
[true, '/test?{token}', true, 'latest'],
['/something/different', '/something/different', false, 'latest'],
['/something/different', '/something/different?{token}', true, 'latest'],
['{{ site.url }}#{{ page.slug }}', '/#test', false, 'latest'],
['{{ site.url }}#{{ page.slug }}', '/?{token}#test', true, 'latest'],
['{{ page.url }}?preview=true', '/test?preview=true', false, 'latest'],
['{{ page.url }}?preview=true', '/test?preview=true&{token}', true, 'latest'],
[false, null, false, 'latest'],
[false, null, true, 'latest'],
[null, null, false, 'latest', false],
[null, '/test', null, false, 'latest'],
[null, '/test?{token}', 'test', true, 'latest'],
[true, '/test', null, false, 'latest'],
[true, '/test?{token}', 'test', true, 'latest'],
['https://test.com', 'https://test.com', null, false, 'latest'],
['https://test.com', 'https://test.com', null, true, 'latest'],
['/something/different', '/something/different', 'something\/different', false, 'latest'],
['/something/different', '/something/different?{token}', 'something\/different', true, 'latest'],
['{{ site.url }}#{{ page.slug }}', '/#test', null, false, 'latest'],
['{{ site.url }}#{{ page.slug }}', '/?{token}#test', '', true, 'latest'],
['{{ page.url }}?preview=true', '/test?preview=true', null, false, 'latest'],
['{{ page.url }}?preview=true', '/test?preview=true&{token}', 'test', true, 'latest'],
[false, null, null, false, 'latest'],
[false, null, null, true, 'latest'],
[null, null, null, false, 'latest', false],

// changes version
[null, '/test?{token}&_version=changes', false, 'changes'],
[null, '/test?{token}&_version=changes', true, 'changes'],
[true, '/test?{token}&_version=changes', false, 'changes'],
[true, '/test?{token}&_version=changes', true, 'changes'],
['/something/different', '/something/different?{token}&_version=changes', false, 'changes'],
['/something/different', '/something/different?{token}&_version=changes', true, 'changes'],
['{{ site.url }}#{{ page.slug }}', '/?{token}&_version=changes#test', false, 'changes'],
['{{ site.url }}#{{ page.slug }}', '/?{token}&_version=changes#test', true, 'changes'],
['{{ page.url }}?preview=true', '/test?preview=true&{token}&_version=changes', false, 'changes'],
['{{ page.url }}?preview=true', '/test?preview=true&{token}&_version=changes', true, 'changes'],
[false, null, false, 'changes'],
[false, null, true, 'changes'],
[null, null, false, 'changes', false],
[null, '/test?{token}&_version=changes', 'test', false, 'changes'],
[null, '/test?{token}&_version=changes', 'test', true, 'changes'],
[true, '/test?{token}&_version=changes', 'test', false, 'changes'],
[true, '/test?{token}&_version=changes', 'test', true, 'changes'],
['https://test.com', 'https://test.com', null, false, 'changes'],
['https://test.com', 'https://test.com', null, true, 'changes'],
['/something/different', '/something/different?{token}&_version=changes', 'something\/different', false, 'changes'],
['/something/different', '/something/different?{token}&_version=changes', 'something\/different', true, 'changes'],
['{{ site.url }}#{{ page.slug }}', '/?{token}&_version=changes#test', '', false, 'changes'],
['{{ site.url }}#{{ page.slug }}', '/?{token}&_version=changes#test', '', true, 'changes'],
['{{ page.url }}?preview=true', '/test?preview=true&{token}&_version=changes', 'test', false, 'changes'],
['{{ page.url }}?preview=true', '/test?preview=true&{token}&_version=changes', 'test', true, 'changes'],
[false, null, null, false, 'changes'],
[false, null, null, true, 'changes'],
[null, null, null, false, 'changes', false],
];
}

Expand All @@ -1549,6 +1553,7 @@ public static function pageUrlProvider(): array
public function testUrlPageCustom(
$input,
$expected,
$expectedUri,
bool $draft,
string $versionId,
bool $authenticated = true
Expand Down Expand Up @@ -1597,7 +1602,7 @@ public function testUrlPageCustom(
$expectedToken = substr(
hash_hmac(
'sha1',
'{"uri":"' . $page->uri() . '","versionId":"' . $versionId . '"}',
'{"uri":"' . $expectedUri . '","versionId":"' . $versionId . '"}',
$page->kirby()->root('content')
),
0,
Expand Down Expand Up @@ -1663,7 +1668,7 @@ public static function siteUrlProvider(): array

// changes version
[null, '/?{token}&_version=changes', 'changes'],
['https://test.com', 'https://test.com?{token}&_version=changes', 'changes'],
['https://test.com', 'https://test.com', 'changes'],
['{{ site.url }}#test', '/?{token}&_version=changes#test', 'changes'],
[false, null, 'changes'],
[null, null, 'changes', false],
Expand Down

0 comments on commit cef90a8

Please sign in to comment.