From c1ba8b1f99c897b567552d2ba0d14f22a65fe384 Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Mon, 20 Nov 2023 20:24:44 +0800 Subject: [PATCH 01/12] `$field->toResolvedUrls()` --- config/methods.php | 31 +++++++++++++++++++++++ src/Uuid/Uuid.php | 34 +++++++++++++++++-------- tests/Content/FieldMethodsTest.php | 35 +++++++++++++++++++++++++- tests/Uuid/UuidTest.php | 40 ++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 11 deletions(-) diff --git a/config/methods.php b/config/methods.php index f768a128e3..be4f741b54 100644 --- a/config/methods.php +++ b/config/methods.php @@ -19,9 +19,12 @@ use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\NotFoundException; use Kirby\Image\QrCode; +use Kirby\Toolkit\A; +use Kirby\Toolkit\Dom; use Kirby\Toolkit\Str; use Kirby\Toolkit\V; use Kirby\Toolkit\Xml; +use Kirby\Uuid\Uuid; /** * Field method setup @@ -247,6 +250,34 @@ return $field->isNotEmpty() ? new QrCode($field->value) : null; }, + /** + * Parses the field value as DOM and replaces + * any permalinks in href/src attributes with + * the regular url + */ + 'toResolvedUrls' => function (Field $field): Field { + if ($field->isNotEmpty() === true) { + $dom = new Dom($field->value); + $attributes = ['href', 'src']; + $elements = $dom->query('//*[' . implode(' | ', A::map($attributes, fn ($attribute) => '@' . $attribute)) . ']'); + + foreach ($elements as $element) { + foreach ($attributes as $attribute) { + if ($url = $element->getAttribute($attribute)) { + if ($uuid = Uuid::for($url)) { + $url = $uuid->model()->url(); + $element->setAttribute($attribute, $url); + } + } + } + } + + $field->value = $dom->toString(); + } + + return $field; + }, + /** * Converts a yaml field to a Structure object */ diff --git a/src/Uuid/Uuid.php b/src/Uuid/Uuid.php index 2dd266b671..b8f310a24b 100644 --- a/src/Uuid/Uuid.php +++ b/src/Uuid/Uuid.php @@ -162,16 +162,30 @@ final public static function for( // for UUID string if (is_string($seed) === true) { - return match (Str::before($seed, '://')) { - 'page' => new PageUuid(uuid: $seed, context: $context), - 'file' => new FileUuid(uuid: $seed, context: $context), - 'site' => new SiteUuid(uuid: $seed, context: $context), - 'user' => new UserUuid(uuid: $seed, context: $context), - // TODO: activate for uuid-block-structure-support - // 'block' => new BlockUuid(uuid: $seed, context: $context), - // 'struct' => new StructureUuid(uuid: $seed, context: $context), - default => throw new InvalidArgumentException('Invalid UUID URI: ' . $seed) - }; + if ($uri = Str::before($seed, '://')) { + return match ($uri) { + 'page' => new PageUuid(uuid: $seed, context: $context), + 'file' => new FileUuid(uuid: $seed, context: $context), + 'site' => new SiteUuid(uuid: $seed, context: $context), + 'user' => new UserUuid(uuid: $seed, context: $context), + // TODO: activate for uuid-block-structure-support + // 'block' => new BlockUuid(uuid: $seed, context: $context), + // 'struct' => new StructureUuid(uuid: $seed, context: $context), + default => throw new InvalidArgumentException('Invalid UUID URI: ' . $seed) + }; + } + + // permalinks + if ($url = Str::after($seed, '/@/')) { + $parts = explode('/', $url); + + return static::for( + $parts[0] . '://' . $parts[1], + $context + ); + } + + throw new InvalidArgumentException('Invalid UUID string: ' . $seed); } // for model object diff --git a/tests/Content/FieldMethodsTest.php b/tests/Content/FieldMethodsTest.php index ae156dd780..7ac3f4b182 100644 --- a/tests/Content/FieldMethodsTest.php +++ b/tests/Content/FieldMethodsTest.php @@ -389,6 +389,39 @@ public function testToQrCode() $this->assertNull($this->field()->toQrCode()); } + public function testToResolvedUrls() + { + $app = new App([ + 'roots' => [ + 'index' => $this->tmp + ], + 'site' => [ + 'children' => [ + [ + 'slug' => 'a', + 'content' => [ + 'uuid' => 'my-page' + ], + 'files' => [ + [ + 'filename' => 'test.jpg', + 'content' => [ + 'uuid' => 'my-file', + ] + ] + ] + ], + ] + ] + ]); + + $field = $this->field('

This is a test

'); + $result = $field->toResolvedUrls(); + $hash = $app->file('a/test.jpg')->mediaHash(); + + $this->assertSame('

This is a test

', (string)$result); + } + public function testToStructure() { $data = [ @@ -440,7 +473,7 @@ public function testToDefaultUrl() $this->assertSame($expected, $field->toUrl()); } - public function testToCustomUrl() + public function testToUrlCustom() { $app = new App([ 'roots' => [ diff --git a/tests/Uuid/UuidTest.php b/tests/Uuid/UuidTest.php index d41c996485..8d1f118297 100644 --- a/tests/Uuid/UuidTest.php +++ b/tests/Uuid/UuidTest.php @@ -3,6 +3,7 @@ namespace Kirby\Uuid; use Generator; +use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\LogicException; use Kirby\Toolkit\Str; @@ -161,6 +162,45 @@ public function testForUuidString() // $this->assertInstanceOf(StructureUuid::class, Uuid::for('struct://my-id')); } + /** + * @covers ::for + */ + public function testForUuidStringInvalid() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid UUID URI: foo://my-id'); + Uuid::for('foo://my-id'); + } + + /** + * @covers ::for + */ + public function testForPermalinkString() + { + $this->assertInstanceOf(PageUuid::class, Uuid::for('/@/page/my-id')); + $this->assertInstanceOf(FileUuid::class, Uuid::for('/@/file/my-id')); + } + + /** + * @covers ::for + */ + public function testForPermalinkStringInvalid() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid UUID URI: foo://my-id'); + Uuid::for('/@/foo/my-id'); + } + + /** + * @covers ::for + */ + public function testForStringInvalid() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid UUID string: foo˜bar'); + Uuid::for('foo˜bar'); + } + /** * @covers ::for */ From f22b952601d84343df61a2d0bdf9e7034c9a59fd Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Tue, 21 Nov 2023 12:24:25 +0800 Subject: [PATCH 02/12] Phase out `k-bar` --- .../components/collections/1_list/index.vue | 19 ++++++++++++++++- .../components/collections/2_cards/index.vue | 21 ++++++++++++++++++- .../collections/3_cardlets/index.vue | 21 ++++++++++++++++++- panel/lab/components/collections/index.php | 8 ++++++- .../src/components/Collection/Collection.vue | 8 +++++-- panel/src/components/Forms/Field.vue | 5 ++++- .../components/Forms/Field/BlocksField.vue | 8 +++---- .../components/Forms/Field/LayoutField.vue | 4 +++- .../components/Forms/Field/StructureField.vue | 4 +++- .../src/components/Navigation/Pagination.vue | 3 +++ panel/src/components/Sections/Section.vue | 5 ++++- 11 files changed, 91 insertions(+), 15 deletions(-) diff --git a/panel/lab/components/collections/1_list/index.vue b/panel/lab/components/collections/1_list/index.vue index 994b717116..2fadb504a6 100644 --- a/panel/lab/components/collections/1_list/index.vue +++ b/panel/lab/components/collections/1_list/index.vue @@ -6,6 +6,21 @@ + + + + + + @@ -13,7 +28,9 @@ export default { props: { empty: Object, + help: String, items: Array, - }, + pagination: Object + } }; diff --git a/panel/lab/components/collections/2_cards/index.vue b/panel/lab/components/collections/2_cards/index.vue index b66b9af53e..af3e4b7adf 100644 --- a/panel/lab/components/collections/2_cards/index.vue +++ b/panel/lab/components/collections/2_cards/index.vue @@ -6,6 +6,23 @@ + + + + + + @@ -13,7 +30,9 @@ export default { props: { empty: Object, + help: String, items: Array, - }, + pagination: Object + } }; diff --git a/panel/lab/components/collections/3_cardlets/index.vue b/panel/lab/components/collections/3_cardlets/index.vue index be4a5b76e6..5855fb1b1f 100644 --- a/panel/lab/components/collections/3_cardlets/index.vue +++ b/panel/lab/components/collections/3_cardlets/index.vue @@ -6,6 +6,23 @@ + + + + + + @@ -13,7 +30,9 @@ export default { props: { empty: Object, + help: String, items: Array, - }, + pagination: Object + } }; diff --git a/panel/lab/components/collections/index.php b/panel/lab/components/collections/index.php index 4326df185f..478e78ad90 100644 --- a/panel/lab/components/collections/index.php +++ b/panel/lab/components/collections/index.php @@ -9,6 +9,7 @@ 'icon' => 'box', 'text' => 'No items yet' ], + 'help' => 'This is a help text that can be used to give some context to the section. It can be rather short or very long and can contain links and other HTML.', 'items' => A::map(range(0, 20), function ($item) { return [ 'text' => 'This is item ' . $item, @@ -17,5 +18,10 @@ 'src' => 'https://picsum.photos/800/600/?v=' . Str::random() ] ]; - }) + }), + 'pagination' => [ + 'page' => 3, + 'limit' => 8, + 'total' => 40 + ] ]; diff --git a/panel/src/components/Collection/Collection.vue b/panel/src/components/Collection/Collection.vue index d75dbb4612..d9d743dcf7 100644 --- a/panel/src/components/Collection/Collection.vue +++ b/panel/src/components/Collection/Collection.vue @@ -25,7 +25,7 @@ -