From e3f1e66fd1511ef0011e2a30e68a0353b8f76d3a Mon Sep 17 00:00:00 2001 From: Ahmet Bora Date: Tue, 9 May 2023 16:26:16 +0300 Subject: [PATCH 01/88] Fix number field blur issue --- config/fields/number.php | 6 +++--- .../src/components/Forms/Input/NumberInput.vue | 2 +- tests/Form/Fields/NumberFieldTest.php | 17 ++++++----------- tests/Form/Fields/RangeFieldTest.php | 6 +++--- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/config/fields/number.php b/config/fields/number.php index 62470ed476..11334e878a 100644 --- a/config/fields/number.php +++ b/config/fields/number.php @@ -8,7 +8,7 @@ * Default number that will be saved when a new page/user/file is created */ 'default' => function ($default = null) { - return $this->toNumber($default); + return $this->toNumber($default) ?? ''; }, /** * The lowest allowed number @@ -26,10 +26,10 @@ * Allowed incremental steps between numbers (i.e `0.5`) */ 'step' => function ($step = null) { - return $this->toNumber($step); + return $this->toNumber($step) ?? ''; }, 'value' => function ($value = null) { - return $this->toNumber($value); + return $this->toNumber($value) ?? ''; } ], 'methods' => [ diff --git a/panel/src/components/Forms/Input/NumberInput.vue b/panel/src/components/Forms/Input/NumberInput.vue index 6e8ec8c2fc..2a958a9347 100644 --- a/panel/src/components/Forms/Input/NumberInput.vue +++ b/panel/src/components/Forms/Input/NumberInput.vue @@ -44,7 +44,7 @@ export const props = { step: Number, value: { type: [Number, String], - default: null + default: "" } } }; diff --git a/tests/Form/Fields/NumberFieldTest.php b/tests/Form/Fields/NumberFieldTest.php index a8ff400725..e5194e3049 100644 --- a/tests/Form/Fields/NumberFieldTest.php +++ b/tests/Form/Fields/NumberFieldTest.php @@ -10,19 +10,19 @@ public function testDefaultProps() $this->assertSame('number', $field->type()); $this->assertSame('number', $field->name()); - $this->assertNull($field->value()); - $this->assertNull($field->default()); + $this->assertSame('', $field->value()); + $this->assertSame('', $field->default()); $this->assertNull($field->min()); $this->assertNull($field->max()); - $this->assertNull($field->step()); + $this->assertSame('', $field->step()); $this->assertTrue($field->save()); } public function valueProvider() { return [ - [null, null], - ['', null], + [null, ''], + ['', ''], [false, (float)0], [0, (float)0], ['0', (float)0], @@ -49,12 +49,7 @@ public function testValue($input, $expected) $this->assertSame($expected, $field->value()); $this->assertSame($expected, $field->default()); - - if ($input === null) { - $this->assertNull($field->step()); - } else { - $this->assertSame($expected, $field->step()); - } + $this->assertSame($expected, $field->step()); } public function testMin() diff --git a/tests/Form/Fields/RangeFieldTest.php b/tests/Form/Fields/RangeFieldTest.php index 4ac27197e4..fea86e624b 100644 --- a/tests/Form/Fields/RangeFieldTest.php +++ b/tests/Form/Fields/RangeFieldTest.php @@ -10,11 +10,11 @@ public function testDefaultProps() $this->assertSame('range', $field->type()); $this->assertSame('range', $field->name()); - $this->assertNull($field->value()); - $this->assertNull($field->default()); + $this->assertSame('', $field->value()); + $this->assertSame('', $field->default()); $this->assertNull($field->min()); $this->assertSame(100.0, $field->max()); - $this->assertNull($field->step()); + $this->assertSame('', $field->step()); $this->assertTrue($field->tooltip()); $this->assertTrue($field->save()); } From 29bf41e91e70748b3cde365671766e3d9b37b2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= <34400296+soerenengels@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:46:57 +0200 Subject: [PATCH 02/88] fix typo in Grid.vue --- panel/src/components/Layout/Grid.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/src/components/Layout/Grid.vue b/panel/src/components/Layout/Grid.vue index d24c4386bb..42183671f8 100644 --- a/panel/src/components/Layout/Grid.vue +++ b/panel/src/components/Layout/Grid.vue @@ -6,7 +6,7 @@ + + diff --git a/panel/src/components/Forms/Previews/index.js b/panel/src/components/Forms/Previews/index.js index 77b17bd559..8a59302fbe 100644 --- a/panel/src/components/Forms/Previews/index.js +++ b/panel/src/components/Forms/Previews/index.js @@ -1,5 +1,6 @@ import ArrayFieldPreview from "./ArrayFieldPreview.vue"; import BubblesFieldPreview from "./BubblesFieldPreview.vue"; +import ColorFieldPreview from "./ColorFieldPreview.vue"; import DateFieldPreview from "./DateFieldPreview.vue"; import EmailFieldPreview from "./EmailFieldPreview.vue"; import FilesFieldPreview from "./FilesFieldPreview.vue"; @@ -18,6 +19,7 @@ export default { install(app) { app.component("k-array-field-preview", ArrayFieldPreview); app.component("k-bubbles-field-preview", BubblesFieldPreview); + app.component("k-color-field-preview", ColorFieldPreview); app.component("k-date-field-preview", DateFieldPreview); app.component("k-email-field-preview", EmailFieldPreview); app.component("k-files-field-preview", FilesFieldPreview); diff --git a/panel/src/components/Layout/Bubble.vue b/panel/src/components/Layout/Bubble.vue index 72188d2f51..0aa8aa27cd 100644 --- a/panel/src/components/Layout/Bubble.vue +++ b/panel/src/components/Layout/Bubble.vue @@ -9,8 +9,11 @@ class="k-bubble" @click.native.stop > - - {{ text }} + + + + + {{ text }} @@ -32,23 +35,29 @@ export default { From 7bb2552abd1ae18bcc19394a4559bef1d932e81b Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 15:23:08 +0200 Subject: [PATCH 25/88] FileBrowser: fix for unselected page Fixes #5280 --- panel/src/components/Navigation/FileBrowser.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panel/src/components/Navigation/FileBrowser.vue b/panel/src/components/Navigation/FileBrowser.vue index 9c96c22b55..66db189b3c 100644 --- a/panel/src/components/Navigation/FileBrowser.vue +++ b/panel/src/components/Navigation/FileBrowser.vue @@ -12,7 +12,7 @@ Date: Sun, 18 Jun 2023 17:10:29 +0200 Subject: [PATCH 26/88] Files field better error for invalid upload.parent #5276 --- config/fields/files.php | 6 +++++- config/fields/mixins/upload.php | 7 ++++++- src/Query/Query.php | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/config/fields/files.php b/config/fields/files.php index e8d4686da8..4f3776549d 100644 --- a/config/fields/files.php +++ b/config/fields/files.php @@ -1,5 +1,6 @@ function () { if ( is_string($this->parent) === true && - $model = $this->model()->query($this->parent, 'Kirby\Cms\ModelWithContent') + $model = $this->model()->query( + $this->parent, + ModelWithContent::class + ) ) { return $model; } diff --git a/config/fields/mixins/upload.php b/config/fields/mixins/upload.php index 00f35dc2ce..700cef9ccb 100644 --- a/config/fields/mixins/upload.php +++ b/config/fields/mixins/upload.php @@ -3,6 +3,7 @@ use Kirby\Cms\Api; use Kirby\Cms\File; use Kirby\Exception\Exception; +use Kirby\Exception\InvalidArgumentException; return [ 'props' => [ @@ -28,7 +29,11 @@ // get parent object for upload target $parent = $this->uploadParent($uploads['parent'] ?? null); - $file = new File([ + if ($parent === null) { + throw new InvalidArgumentException('"' . $uploads['parent'] . '" could not be resolved as a valid parent for the upload'); + } + + $file = new File([ 'filename' => 'tmp', 'parent' => $parent, 'template' => $template diff --git a/src/Query/Query.php b/src/Query/Query.php index 4dd590a53b..54691165c3 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -117,7 +117,7 @@ public function resolve(array|object $data = []): mixed }; Query::$entries['page'] = function (string $id): Page|null { - return App::instance()->site()->find($id); + return App::instance()->page($id); }; Query::$entries['site'] = function (): Site { From 51ac4ca06bf8b017ad688dbd90c341a5bf79dcbe Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 18:44:38 +0200 Subject: [PATCH 27/88] Query: backport fix for floats --- src/Query/Argument.php | 4 ++++ tests/Query/ArgumentTest.php | 8 +++++--- tests/Query/ArgumentsTest.php | 14 +++++++------- tests/Query/ExpressionTest.php | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Query/Argument.php b/src/Query/Argument.php index 29a1b8f4a9..d9ca49389b 100644 --- a/src/Query/Argument.php +++ b/src/Query/Argument.php @@ -70,6 +70,10 @@ public static function factory(string $argument): static // numeric if (is_numeric($argument) === true) { + if (strpos($argument, '.') === false) { + return new static((int)$argument); + } + return new static((float)$argument); } diff --git a/tests/Query/ArgumentTest.php b/tests/Query/ArgumentTest.php index 96719cd72d..85b7b0a17c 100644 --- a/tests/Query/ArgumentTest.php +++ b/tests/Query/ArgumentTest.php @@ -43,7 +43,9 @@ public function testFactory() // numbers $argument = Argument::factory(' 23 '); - $this->assertSame(23.0, $argument->value); + $this->assertSame(23, $argument->value); + $argument = Argument::factory(' 23.3 '); + $this->assertSame(23.3, $argument->value); // null $argument = Argument::factory(' null '); @@ -70,8 +72,8 @@ public function testResolve() $this->assertSame(' 23 ', $argument); // arrays - $argument = Argument::factory('[1, "a", 3]')->resolve(); - $this->assertSame([1.0, 'a', 3.0], $argument); + $argument = Argument::factory('[1, "a", 3.3]')->resolve(); + $this->assertSame([1, 'a', 3.3], $argument); // nested query $argument = Argument::factory('foo')->resolve(['foo' => 'bar']); diff --git a/tests/Query/ArgumentsTest.php b/tests/Query/ArgumentsTest.php index 4029d3b018..685b0110f0 100644 --- a/tests/Query/ArgumentsTest.php +++ b/tests/Query/ArgumentsTest.php @@ -33,19 +33,19 @@ public function testFactory() */ public function testResolve() { - $arguments = Arguments::factory('1, 2, 3'); - $this->assertSame([1.0 , 2.0, 3.0], $arguments->resolve()); + $arguments = Arguments::factory('1, 2.3, 3'); + $this->assertSame([1, 2.3, 3], $arguments->resolve()); - $arguments = Arguments::factory('1, 2, [3, 4]'); - $this->assertSame([1.0 , 2.0, [3.0, 4.0]], $arguments->resolve()); + $arguments = Arguments::factory('1, 2, [3.3, 4]'); + $this->assertSame([1, 2, [3.3, 4]], $arguments->resolve()); $arguments = Arguments::factory('1, 2, \'3, 4\''); - $this->assertSame([1.0 , 2.0, '3, 4'], $arguments->resolve()); + $this->assertSame([1, 2, '3, 4'], $arguments->resolve()); $arguments = Arguments::factory('1, 2, "3, 4"'); - $this->assertSame([1.0 , 2.0, '3, 4'], $arguments->resolve()); + $this->assertSame([1, 2, '3, 4'], $arguments->resolve()); $arguments = Arguments::factory('1, 2, \'(3, 4)\''); - $this->assertSame([1.0 , 2.0, '(3, 4)'], $arguments->resolve()); + $this->assertSame([1, 2, '(3, 4)'], $arguments->resolve()); } } diff --git a/tests/Query/ExpressionTest.php b/tests/Query/ExpressionTest.php index feb1aa0b3e..28590ea3d9 100644 --- a/tests/Query/ExpressionTest.php +++ b/tests/Query/ExpressionTest.php @@ -91,7 +91,7 @@ public function providerResolve(): array ['0 ?: "no"', 'no'], ['null ?? null ?? null ?? "yes"', 'yes'], ['"yes" ?? "no"', 'yes'], - ['null ?? (null ?? null ?? (false ? "what" : 42)) ?? "no"', 42.0], + ['null ?? (null ?? null ?? (false ? "what" : 42)) ?? "no"', 42], ]; } From 80fd73c6e57f4f3ef998510eb221fb54a38a164d Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 18:46:33 +0200 Subject: [PATCH 28/88] Fix overwriting of `Query::$entries` Fixes #5276 --- src/Query/Query.php | 2 +- src/Query/Segment.php | 62 ++++++++++++++++------- tests/Query/QueryDefaultFunctionsTest.php | 19 +++++-- tests/Query/SegmentsTest.php | 14 ++--- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/src/Query/Query.php b/src/Query/Query.php index 4dd590a53b..54691165c3 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -117,7 +117,7 @@ public function resolve(array|object $data = []): mixed }; Query::$entries['page'] = function (string $id): Page|null { - return App::instance()->site()->find($id); + return App::instance()->page($id); }; Query::$entries['site'] = function (): Site { diff --git a/src/Query/Segment.php b/src/Query/Segment.php index f417f1a74a..89e75744c3 100644 --- a/src/Query/Segment.php +++ b/src/Query/Segment.php @@ -77,19 +77,17 @@ public static function factory( /** * Automatically resolves the segment depending on the * segment position and the type of the base + * + * @param mixed $base Current value of the query chain */ public function resolve(mixed $base = null, array|object $data = []): mixed { // resolve arguments to array $args = $this->arguments?->resolve($data) ?? []; - // 1st segment, start from $data array + // 1st segment, use $data as base if ($this->position === 0) { - if (is_array($data) == true) { - return $this->resolveArray($data, $args); - } - - return $this->resolveObject($data, $args); + $base = $data; } if (is_array($base) === true) { @@ -109,26 +107,55 @@ public function resolve(mixed $base = null, array|object $data = []): mixed */ protected function resolveArray(array $array, array $args): mixed { - if (array_key_exists($this->method, $array) === false) { - static::error($array, $this->method, 'property'); - } + // the directly provided array takes precedence + // to look up a matching entry + if (array_key_exists($this->method, $array) === true) { + $value = $array[$this->method]; + + // if this is a Closure we can directly use it, as + // Closures from the $array should always have priority + // over the Query::$entries Closures + if ($value instanceof Closure) { + return $value(...$args); + } - $value = $array[$this->method]; + // if we have no arguments to pass, we also can directly + // use the value from the $array as it must not be different + // to the one from Query::$entries with the same name + if ($args === []) { + return $value; + } + } - if ($value instanceof Closure) { - return $value(...$args); + // fallback time: only if we are handling the first segment, + // we can also try to resolve the segment with an entry from the + // default Query::$entries + if ($this->position === 0) { + if (array_key_exists($this->method, Query::$entries) === true) { + return Query::$entries[$this->method](...$args); + } } - if ($args !== []) { + // if we have not been able to return anything so far, + // we just need to differntiate between two different error messages + + // this one is in case the original array contained the key, + // but was not a Closure while the segment had arguments + if ( + array_key_exists($this->method, $array) && + $args !== [] + ) { throw new InvalidArgumentException('Cannot access array element "' . $this->method . '" with arguments'); } - return $value; + // last, the standard error for trying to access something + // that does not exist + static::error($array, $this->method, 'property'); } /** - * Resolves segment by calling the method/accessing the property - * on the base object + * Resolves segment by calling the method/ + * accessing the property on the base object */ protected function resolveObject(object $object, array $args): mixed { @@ -140,7 +167,8 @@ protected function resolveObject(object $object, array $args): mixed } if ( - $args === [] && ( + $args === [] && + ( property_exists($object, $this->method) === true || method_exists($object, '__get') === true ) diff --git a/tests/Query/QueryDefaultFunctionsTest.php b/tests/Query/QueryDefaultFunctionsTest.php index dbe946ac39..7cc5f71f42 100644 --- a/tests/Query/QueryDefaultFunctionsTest.php +++ b/tests/Query/QueryDefaultFunctionsTest.php @@ -75,20 +75,33 @@ public function testFile() public function testPage() { - new App([ + $app = new App([ 'site' => [ 'children' => [ [ 'slug' => 'a', + ], + [ + 'slug' => 'b', + 'content' => ['uuid' => 'test'] ] ] ] ]); + $a = $app->page('a'); + $b = $app->page('b'); + $query = new Query('page("a")'); - $this->assertInstanceOf(Page::class, $query->resolve()); + $this->assertSame($a, $query->resolve()); + + $query = new Query('page("a").slug'); + $this->assertSame('a', $query->resolve(['slug' => 'foo'])); + + $query = new Query('page("page://test")'); + $this->assertSame($b, $query->resolve(['page' => $b])); - $query = new Query('page("b")'); + $query = new Query('page("c")'); $this->assertNull($query->resolve()); } diff --git a/tests/Query/SegmentsTest.php b/tests/Query/SegmentsTest.php index 1df37835e6..4c2f3dde64 100644 --- a/tests/Query/SegmentsTest.php +++ b/tests/Query/SegmentsTest.php @@ -220,10 +220,10 @@ public function testResolveWithArrayCallClosure() public function testResolveWithArrayCallError() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Cannot access array element "user" with arguments'); + $this->expectExceptionMessage('Cannot access array element "editor" with arguments'); - $segments = Segments::factory('user("test")'); - $data = ['user' => new TestUser()]; + $segments = Segments::factory('editor("test")'); + $data = ['editor' => new TestUser()]; $segments->resolve($data); } @@ -233,9 +233,9 @@ public function testResolveWithArrayCallError() public function testResolveWithArrayMissingKey1() { $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage('Access to non-existing property "user" on array'); + $this->expectExceptionMessage('Access to non-existing property "editor" on array'); - $segments = Segments::factory('user'); + $segments = Segments::factory('editor'); $segments->resolve(); } @@ -245,9 +245,9 @@ public function testResolveWithArrayMissingKey1() public function testResolveWithArrayMissingKey2() { $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessage('Access to non-existing property "user" on array'); + $this->expectExceptionMessage('Access to non-existing property "editor" on array'); - $segments = Segments::factory('user.username'); + $segments = Segments::factory('editor.username'); $segments->resolve(); } From e87a0415e50e26a579c5f296019dac96f42b7d9e Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 19:56:37 +0200 Subject: [PATCH 29/88] Fix block keydown propagation Fixes #5272 --- panel/src/components/Forms/Blocks/Block.vue | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/panel/src/components/Forms/Blocks/Block.vue b/panel/src/components/Forms/Blocks/Block.vue index edc26b222a..c5eee043ca 100644 --- a/panel/src/components/Forms/Blocks/Block.vue +++ b/panel/src/components/Forms/Blocks/Block.vue @@ -11,24 +11,24 @@ :data-translate="fieldset.translate" class="k-block-container" tabindex="0" - @keydown.meta.j.prevent="$emit('merge')" - @keydown.ctrl.j.prevent="$emit('merge')" - @keydown.meta.up.exact.prevent="$emit('focusPrev')" - @keydown.ctrl.up.exact.prevent="$emit('focusPrev')" - @keydown.meta.down.exact.prevent="$emit('focusNext')" - @keydown.ctrl.down.exact.prevent="$emit('focusNext')" - @keydown.meta.alt.down.prevent="$emit('selectDown')" - @keydown.ctrl.alt.down.prevent="$emit('selectDown')" - @keydown.meta.alt.up.prevent="$emit('selectUp')" - @keydown.ctrl.alt.up.prevent="$emit('selectUp')" - @keydown.meta.shift.down.prevent="$emit('sortDown')" - @keydown.ctrl.shift.down.prevent="$emit('sortDown')" - @keydown.meta.shift.up.prevent="$emit('sortUp')" - @keydown.ctrl.shift.up.prevent="$emit('sortUp')" - @keydown.meta.backspace.prevent="remove" - @keydown.ctrl.backspace.prevent="remove" - @focus="$emit('focus')" - @focusin="onFocusIn" + @keydown.meta.j.prevent.stop="$emit('merge')" + @keydown.ctrl.j.prevent.stop="$emit('merge')" + @keydown.meta.up.exact.prevent.stop="$emit('focusPrev')" + @keydown.ctrl.up.exact.prevent.stop="$emit('focusPrev')" + @keydown.meta.down.exact.prevent.stop="$emit('focusNext')" + @keydown.ctrl.down.exact.prevent.stop="$emit('focusNext')" + @keydown.meta.alt.down.prevent.stop="$emit('selectDown')" + @keydown.ctrl.alt.down.prevent.stop="$emit('selectDown')" + @keydown.meta.alt.up.prevent.stop="$emit('selectUp')" + @keydown.ctrl.alt.up.prevent.stop="$emit('selectUp')" + @keydown.meta.shift.down.prevent.stop="$emit('sortDown')" + @keydown.ctrl.shift.down.prevent.stop="$emit('sortDown')" + @keydown.meta.shift.up.prevent.stop="$emit('sortUp')" + @keydown.ctrl.shift.up.prevent.stop="$emit('sortUp')" + @keydown.meta.backspace.prevent.stop="remove" + @keydown.ctrl.backspace.prevent.stop="remove" + @focus.stop="$emit('focus')" + @focusin.stop="onFocusIn" >
Date: Sun, 18 Jun 2023 19:20:39 +0200 Subject: [PATCH 30/88] `Find::parent()`: fix for file with `files` parent --- src/Cms/Find.php | 5 ++++- tests/Cms/FindTest.php | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Cms/Find.php b/src/Cms/Find.php index 7b57f407c3..9c3ad63b94 100644 --- a/src/Cms/Find.php +++ b/src/Cms/Find.php @@ -121,7 +121,10 @@ public static function parent(string $path) 'site' => $kirby->site(), 'account' => static::user(), 'page' => static::page(basename($path)), - 'file' => static::file(...explode('/files/', $path)), + // regular expression to split the path at the last + // occurrence of /files/ which separates parent path + // and filename + 'file' => static::file(...preg_split('$.*\K(/files/)$', $path)), 'user' => $kirby->user(basename($path)), default => throw new InvalidArgumentException('Invalid model type: ' . $modelType) }; diff --git a/tests/Cms/FindTest.php b/tests/Cms/FindTest.php index ad561a77e8..b0c7f7f22a 100644 --- a/tests/Cms/FindTest.php +++ b/tests/Cms/FindTest.php @@ -245,6 +245,12 @@ public function testParent() 'files' => [ ['filename' => 'a-regular-file.jpg'] ] + ], + [ + 'slug' => 'files', + 'files' => [ + ['filename' => 'file-in-files-page.jpg'] + ] ] ], 'files' => [ @@ -279,6 +285,7 @@ public function testParent() $this->assertInstanceOf(Page::class, Find::parent('pages/a aa')); $this->assertInstanceOf(File::class, Find::parent('site/files/sitefile.jpg')); $this->assertInstanceOf(File::class, Find::parent('pages/a/files/a-regular-file.jpg')); + $this->assertInstanceOf(File::class, Find::parent('pages/files/files/file-in-files-page.jpg')); $this->assertInstanceOf(File::class, Find::parent('users/test@getkirby.com/files/userfile.jpg')); } From 2301f889c7bdb5ec26e37157afc6e3b04b8fdae8 Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 20:40:45 +0200 Subject: [PATCH 31/88] Stats section: add more i18n support Fixes #5256 --- config/sections/stats.php | 21 ++++++++++++--------- tests/Cms/Sections/StatsSectionTest.php | 8 ++++---- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/config/sections/stats.php b/config/sections/stats.php index e73e7dceba..e18eba0acd 100644 --- a/config/sections/stats.php +++ b/config/sections/stats.php @@ -34,9 +34,9 @@ ], 'computed' => [ 'reports' => function () { - $reports = []; - $model = $this->model(); - $value = fn ($value) => $value === null ? null : $model->toString($value); + $reports = []; + $model = $this->model(); + $toString = fn ($value) => $value === null ? null : $model->toString($value); foreach ($this->reports as $report) { if (is_string($report) === true) { @@ -47,14 +47,17 @@ continue; } - $info = $report['info'] ?? null; + $info = $report['info'] ?? null; + $label = $report['label'] ?? null; + $link = $report['link'] ?? null; + $value = $report['value'] ?? null; $reports[] = [ - 'label' => I18n::translate($report['label'], $report['label']), - 'value' => $value($report['value'] ?? null), - 'info' => $value(I18n::translate($info, $info)), - 'link' => $value($report['link'] ?? null), - 'theme' => $value($report['theme'] ?? null) + 'info' => $toString(I18n::translate($info, $info)), + 'label' => $toString(I18n::translate($label, $label)), + 'link' => $toString(I18n::translate($link, $link)), + 'theme' => $toString($report['theme'] ?? null), + 'value' => $toString(I18n::translate($value, $value)) ]; } diff --git a/tests/Cms/Sections/StatsSectionTest.php b/tests/Cms/Sections/StatsSectionTest.php index 0d4a390a26..1e3374f424 100644 --- a/tests/Cms/Sections/StatsSectionTest.php +++ b/tests/Cms/Sections/StatsSectionTest.php @@ -15,18 +15,18 @@ public function reports() { return [ [ - 'label' => 'A', - 'value' => 'Value A', 'info' => 'Info A', + 'label' => 'A', 'link' => 'https://getkirby.com', 'theme' => null, + 'value' => 'Value A', ], [ - 'label' => 'B', - 'value' => 'Value B', 'info' => null, + 'label' => 'B', 'link' => null, 'theme' => null, + 'value' => 'Value B', ] ]; } From f79249051f14e671b95b1e0e676258ca2c9f877b Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 21:27:52 +0200 Subject: [PATCH 32/88] First LanguageVariable unit tests #5229 --- src/Cms/LanguageVariable.php | 47 +++++++------- tests/Cms/Languages/LanguageTest.php | 36 +++++------ tests/Cms/Languages/LanguageVariableTest.php | 65 ++++++++++++++++++++ tests/Cms/Languages/LanguagesTest.php | 8 +-- 4 files changed, 112 insertions(+), 44 deletions(-) create mode 100644 tests/Cms/Languages/LanguageVariableTest.php diff --git a/src/Cms/LanguageVariable.php b/src/Cms/LanguageVariable.php index 9296510be9..96c83bd2a3 100644 --- a/src/Cms/LanguageVariable.php +++ b/src/Cms/LanguageVariable.php @@ -2,8 +2,8 @@ namespace Kirby\Cms; -use InvalidArgumentException; use Kirby\Exception\DuplicateException; +use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\Str; /** @@ -21,8 +21,10 @@ class LanguageVariable { protected App $kirby; - public function __construct(protected Language $language, protected string $key) - { + public function __construct( + protected Language $language, + protected string $key + ) { $this->kirby = App::instance(); } @@ -31,13 +33,11 @@ public function __construct(protected Language $language, protected string $key) * be added to the default language first and * can then be translated in other languages. */ - public static function create(string $key, string|null $value = null): static - { - $key = Str::slug($key, null, 'a-z0-9_-'); - $value = trim($value ?? ''); - $kirby = App::instance(); - $language = $kirby->defaultLanguage(); - $translations = $language->translations(); + public static function create( + string $key, + string|null $value = null + ): static { + $key = Str::slug($key, null, 'a-z0-9_-'); if (is_numeric($key) === true) { throw new InvalidArgumentException('The variable key must not be numeric'); @@ -47,19 +47,21 @@ public static function create(string $key, string|null $value = null): static throw new InvalidArgumentException('The variable needs a valid key'); } + $kirby = App::instance(); + $language = $kirby->defaultLanguage(); + $translations = $language->translations(); + if ($kirby->translation()->get($key) !== null) { if (isset($translations[$key]) === true) { throw new DuplicateException('The variable already exists'); - } else { - throw new DuplicateException('The variable is part of the core translation and cannot be overwritten'); } + + throw new DuplicateException('The variable is part of the core translation and cannot be overwritten'); } - $translations[$key] = $value; + $translations[$key] = trim($value ?? ''); - $language->update([ - 'translations' => $translations - ]); + $language->update(['translations' => $translations]); return $language->variable($key); } @@ -77,9 +79,7 @@ public function delete(): bool unset($variables[$this->key]); - $language->update([ - 'translations' => $variables - ]); + $language->update(['translations' => $variables]); } return true; @@ -90,7 +90,8 @@ public function delete(): bool */ public function exists(): bool { - return isset($this->kirby->defaultLanguage()->translations()[$this->key]) === true; + $language = $this->kirby->defaultLanguage(); + return isset($language->translations()[$this->key]) === true; } /** @@ -109,13 +110,15 @@ public function update(string $value): static $translations = $this->language->translations(); $translations[$this->key] = $value; - return $this->language->update(['translations' => $translations])->variable($this->key); + $language = $this->language->update(['translations' => $translations]); + + return $language->variable($this->key); } /** * Returns the value if the variable has been translated. */ - public function value(): ?string + public function value(): string|null { return $this->language->translations()[$this->key] ?? null; } diff --git a/tests/Cms/Languages/LanguageTest.php b/tests/Cms/Languages/LanguageTest.php index 83e434efad..81c615b8b3 100644 --- a/tests/Cms/Languages/LanguageTest.php +++ b/tests/Cms/Languages/LanguageTest.php @@ -11,22 +11,22 @@ class LanguageTest extends TestCase { protected $app; - protected $fixtures; + protected $tmp; public function setUp(): void { $this->app = new App([ 'roots' => [ - 'index' => $this->fixtures = __DIR__ . '/fixtures/LanguageTest', + 'index' => $this->tmp = __DIR__ . '/tmp/LanguageTest', ] ]); - Dir::make($this->fixtures); + Dir::make($this->tmp); } public function tearDown(): void { - Dir::remove($this->fixtures); + Dir::remove($this->tmp); } public function testConstructNoCode() @@ -248,12 +248,12 @@ public function testSave() { $app = new App([ 'roots' => [ - 'index' => $fixtures = __DIR__ . '/fixtures/LanguageTest', - 'languages' => $fixtures + 'index' => $tmp = __DIR__ . '/tmp/LanguageTest', + 'languages' => $tmp ] ]); - $file = $fixtures . '/de.php'; + $file = $tmp . '/de.php'; // default $language = new Language([ @@ -314,7 +314,7 @@ public function testSave() $this->assertSame('test', $data['custom']); - Dir::remove($fixtures); + Dir::remove($tmp); } public function testRouter() @@ -352,7 +352,7 @@ public function testExists() { $app = new App([ 'roots' => [ - 'index' => __DIR__ . '/fixtures' + 'index' => __DIR__ . '/tmp' ] ]); @@ -366,14 +366,14 @@ public function testExists() $this->assertTrue($language->exists()); - Dir::remove(__DIR__ . '/fixtures'); + Dir::remove(__DIR__ . '/tmp'); } public function testRoot() { $app = new App([ 'roots' => [ - 'index' => $fixtures = __DIR__ . '/fixtures' + 'index' => $tmp = __DIR__ . '/tmp' ] ]); @@ -381,7 +381,7 @@ public function testRoot() 'code' => 'de' ]); - $this->assertSame($fixtures . '/site/languages/de.php', $language->root()); + $this->assertSame($tmp . '/site/languages/de.php', $language->root()); } public function pathProvider() @@ -501,7 +501,7 @@ public function testDelete() public function testUpdate() { - Dir::make($contentDir = $this->fixtures . '/content'); + Dir::make($contentDir = $this->tmp . '/content'); $language = Language::create([ 'code' => 'en' @@ -519,7 +519,7 @@ public function testCreateHooks() new App([ 'roots' => [ - 'index' => $this->fixtures = __DIR__ . '/fixtures/CreateHooksTest', + 'index' => $this->tmp = __DIR__ . '/tmp/CreateHooksTest', ], 'hooks' => [ 'language.create:before' => function (Language $language, array $input) use ($phpunit, &$calls) { @@ -549,12 +549,12 @@ public function testUpdateHooks() $calls = 0; $phpunit = $this; - $this->fixtures = __DIR__ . '/fixtures/UpdateHooksTest'; - Dir::make($this->fixtures . '/content'); + $this->tmp = __DIR__ . '/tmp/UpdateHooksTest'; + Dir::make($this->tmp . '/content'); new App([ 'roots' => [ - 'index' => $this->fixtures, + 'index' => $this->tmp, ], 'hooks' => [ 'language.update:before' => function (Language $language, array $input) use ($phpunit, &$calls) { @@ -593,7 +593,7 @@ public function testDeleteHooks() new App([ 'roots' => [ - 'index' => $this->fixtures = __DIR__ . '/fixtures/DeleteHooksTest', + 'index' => $this->tmp = __DIR__ . '/tmp/DeleteHooksTest', ], 'hooks' => [ 'language.delete:before' => function (Language $language) use ($phpunit, &$calls) { diff --git a/tests/Cms/Languages/LanguageVariableTest.php b/tests/Cms/Languages/LanguageVariableTest.php new file mode 100644 index 0000000000..881e36f757 --- /dev/null +++ b/tests/Cms/Languages/LanguageVariableTest.php @@ -0,0 +1,65 @@ +app = new App([ + 'roots' => [ + 'index' => $this->tmp = __DIR__ . '/tmp/LanguageVariableTest', + ] + ]); + + Dir::make($this->tmp); + } + + public function tearDown(): void + { + Dir::remove($this->tmp); + } + + /** + * @covers ::create + */ + public function testCreateEmptyKey() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The variable needs a valid key'); + + LanguageVariable::create(''); + } + + /** + * @covers ::create + */ + public function testCreateInvalidKey() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The variable key must not be numeric'); + + LanguageVariable::create('0'); + } + + /** + * @covers ::__construct + * @covers ::key + */ + public function testKey() + { + $language = new Language(['code' => 'test']); + $variable = new LanguageVariable($language, 'foo'); + $this->assertSame('foo', $variable->key()); + } +} diff --git a/tests/Cms/Languages/LanguagesTest.php b/tests/Cms/Languages/LanguagesTest.php index 31a129aaf9..f8f32d1459 100644 --- a/tests/Cms/Languages/LanguagesTest.php +++ b/tests/Cms/Languages/LanguagesTest.php @@ -11,13 +11,13 @@ class LanguagesTest extends TestCase { protected $app; protected $languages; - protected $fixtures; + protected $tmp; public function setUp(): void { $this->app = new App([ 'roots' => [ - 'index' => $this->fixtures = __DIR__ . '/fixtures/LanguagesTest', + 'index' => $this->tmp = __DIR__ . '/tmp/LanguagesTest', ], 'languages' => [ [ @@ -41,7 +41,7 @@ public function setUp(): void public function tearDown(): void { - Dir::remove($this->fixtures); + Dir::remove($this->tmp); } public function testLoad() @@ -55,7 +55,7 @@ public function testLoadFromFiles() { $this->app->clone([ 'roots' => [ - 'languages' => $root = __DIR__ . '/fixtures/LanguagesTest' + 'languages' => $root = __DIR__ . '/tmp/LanguagesTest' ] ]); From af388724f38950e177756ed2b0918502e8994df3 Mon Sep 17 00:00:00 2001 From: Ahmet Bora Date: Mon, 19 Jun 2023 12:37:04 +0300 Subject: [PATCH 33/88] Fix panel url redirect issue when running on subfolder #5266 --- src/Panel/Home.php | 9 +++++++++ src/Panel/Panel.php | 2 +- tests/Panel/HomeTest.php | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Panel/Home.php b/src/Panel/Home.php index 95ba708b96..a0995ab130 100644 --- a/src/Panel/Home.php +++ b/src/Panel/Home.php @@ -152,6 +152,15 @@ public static function panelPath(string $url): string|null return trim($after, '/'); } + /** + * Returns the path after /panel/ for the home url + */ + public static function path(): string|null + { + $url = Home::url(); + return Home::panelPath($url); + } + /** * Returns the Url that has been stored in the session * before the last logout. We take this Url if possible diff --git a/src/Panel/Panel.php b/src/Panel/Panel.php index 0f937b0ec6..086dbf3d76 100644 --- a/src/Panel/Panel.php +++ b/src/Panel/Panel.php @@ -354,7 +354,7 @@ public static function routes(array $areas): array 'installation', 'login', ], - 'action' => fn () => Panel::go(Home::url()), + 'action' => fn () => Panel::go(Home::path()), 'auth' => false ]; diff --git a/tests/Panel/HomeTest.php b/tests/Panel/HomeTest.php index 89bbb7a6ff..55047b9db8 100644 --- a/tests/Panel/HomeTest.php +++ b/tests/Panel/HomeTest.php @@ -288,6 +288,22 @@ public function testPanelPath() $this->assertSame('', Home::panelPath('/test/page')); } + /** + * @covers ::path + */ + public function testPath() + { + $this->app = $this->app->clone([ + 'users' => [ + ['email' => 'test@getkirby.com', 'role' => 'admin'] + ] + ]); + + $this->app->impersonate('test@getkirby.com'); + + $this->assertSame('site', Home::path()); + } + /** * @covers ::remembered */ From 67626bb1b462291b5a279a8972ac58e43989c1c5 Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 13:33:54 +0200 Subject: [PATCH 34/88] Fix users field default Fixes #5284 --- config/fields/users.php | 34 +++++++++++++++------------- tests/Form/Fields/UsersFieldTest.php | 12 ++++++++++ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/config/fields/users.php b/config/fields/users.php index b962a36fc1..7ddda9c6f5 100644 --- a/config/fields/users.php +++ b/config/fields/users.php @@ -24,29 +24,31 @@ /** * Default selected user(s) when a new page/file/user is created */ - 'default' => function ($default = null) { - if ($default === false) { + 'default' => function (string|array|bool $default = null) { + return $default; + }, + + 'value' => function ($value = null) { + return $this->toUsers($value); + }, + ], + 'computed' => [ + 'default' => function (): array { + if ($this->default === false) { return []; } - if ($default === null && $user = $this->kirby()->user()) { + if ( + $this->default === true && + $user = $this->kirby()->user() + ) { return [ $this->userResponse($user) ]; } - return $this->toUsers($default); - }, - - 'value' => function ($value = null) { - return $this->toUsers($value); - }, - ], - 'computed' => [ - /** - * Unset inherited computed - */ - 'default' => null + return $this->toUsers($this->default); + } ], 'methods' => [ 'userResponse' => function ($user) { @@ -57,7 +59,7 @@ 'text' => $this->text, ]); }, - 'toUsers' => function ($value = null) { + 'toUsers' => function ($value = null): array { $users = []; $kirby = App::instance(); diff --git a/tests/Form/Fields/UsersFieldTest.php b/tests/Form/Fields/UsersFieldTest.php index e98ed18847..1d58412be9 100644 --- a/tests/Form/Fields/UsersFieldTest.php +++ b/tests/Form/Fields/UsersFieldTest.php @@ -55,6 +55,18 @@ public function testDefaultUser() 'model' => new Page(['slug' => 'test']) ]); + $this->assertSame([], $field->default()); + } + + public function testCurrentDefaultUser() + { + $this->app->impersonate('raphael@getkirby.com'); + + $field = $this->field('users', [ + 'model' => new Page(['slug' => 'test']), + 'default' => true + ]); + $this->assertSame('raphael@getkirby.com', $field->default()[0]['email']); } From de56185809f33f880559735a291877d55265aa4e Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Tue, 20 Jun 2023 20:55:50 +0200 Subject: [PATCH 35/88] Upgrade npm dependencies --- panel/package-lock.json | 379 +++++++++++++++++++++------------------- panel/package.json | 24 +-- 2 files changed, 209 insertions(+), 194 deletions(-) diff --git a/panel/package-lock.json b/panel/package-lock.json index 8e55823432..90fd4fba99 100644 --- a/panel/package-lock.json +++ b/panel/package-lock.json @@ -8,7 +8,7 @@ "dependencies": { "container-query-polyfill": "^1.0.2", "css-has-pseudo": "^5.0.2", - "dayjs": "^1.11.7", + "dayjs": "^1.11.8", "mitt": "^3.0.0", "portal-vue": "^2.1.7", "prosemirror-commands": "^1.5.2", @@ -16,8 +16,8 @@ "prosemirror-inputrules": "^1.2.1", "prosemirror-keymap": "^1.2.2", "prosemirror-model": "^1.19.2", - "prosemirror-schema-list": "^1.2.3", - "prosemirror-view": "^1.31.3", + "prosemirror-schema-list": "^1.3.0", + "prosemirror-view": "^1.31.5", "vue": "^2.7.14", "vuedraggable": "^2.24.3", "vuelidate": "^0.7.7", @@ -25,26 +25,26 @@ }, "devDependencies": { "@vitejs/plugin-vue2": "^2.2.0", - "@vitest/coverage-c8": "^0.31.1", - "@vitest/ui": "^0.31.1", + "@vitest/coverage-c8": "^0.32.2", + "@vitest/ui": "^0.32.2", "@vue/test-utils": "^1.3.0", "autoprefixer": "^10.4.14", "cssnano": "^6.0.1", - "cypress": "^12.13.0", - "eslint": "^8.41.0", + "cypress": "^12.15.0", + "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-cypress": "^2.13.3", - "eslint-plugin-vue": "^9.14.1", + "eslint-plugin-vue": "^9.15.0", "jsdom": "^22.1.0", "postcss-dir-pseudo-class": "^7.0.2", "postcss-logical": "^6.2.0", "prettier": "^2.8.8", "rollup-plugin-external-globals": "^0.8.0", - "terser": "^5.17.6", + "terser": "^5.18.1", "vite": "^4.3.9", - "vite-plugin-static-copy": "^0.15.0", - "vitest": "^0.31.1", - "vue-docgen-api": "^4.72.3", + "vite-plugin-static-copy": "^0.16.0", + "vitest": "^0.32.2", + "vue-docgen-api": "^4.73.0", "vue-template-compiler": "^2.7.14" } }, @@ -247,18 +247,18 @@ } }, "node_modules/@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -330,9 +330,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -505,9 +505,9 @@ } }, "node_modules/@vitest/coverage-c8": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.31.1.tgz", - "integrity": "sha512-6TkjQpmgYez7e3dbAUoYdRXxWN81BojCmUILJwgCy39uZFG33DsQ0rSRSZC9beAEdCZTpxR63nOvd9hxDQcJ0g==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.32.2.tgz", + "integrity": "sha512-z07kMTN6e4t1jDY4XXU6W1LxCb3V5Rw7KAZId4VM6BCIGLGz1QqwH9UWYWv7LemqQVnARl5CwaDDwVrkcYgwPg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -536,13 +536,13 @@ } }, "node_modules/@vitest/expect": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.31.1.tgz", - "integrity": "sha512-BV1LyNvhnX+eNYzJxlHIGPWZpwJFZaCcOIzp2CNG0P+bbetenTupk6EO0LANm4QFt0TTit+yqx7Rxd1qxi/SQA==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.2.tgz", + "integrity": "sha512-6q5yzweLnyEv5Zz1fqK5u5E83LU+gOMVBDuxBl2d2Jfx1BAp5M+rZgc5mlyqdnxquyoiOXpXmFNkcGcfFnFH3Q==", "dev": true, "dependencies": { - "@vitest/spy": "0.31.1", - "@vitest/utils": "0.31.1", + "@vitest/spy": "0.32.2", + "@vitest/utils": "0.32.2", "chai": "^4.3.7" }, "funding": { @@ -550,12 +550,12 @@ } }, "node_modules/@vitest/runner": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.31.1.tgz", - "integrity": "sha512-imWuc82ngOtxdCUpXwtEzZIuc1KMr+VlQ3Ondph45VhWoQWit5yvG/fFcldbnCi8DUuFi+NmNx5ehMUw/cGLUw==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.2.tgz", + "integrity": "sha512-06vEL0C1pomOEktGoLjzZw+1Fb+7RBRhmw/06WkDrd1akkT9i12su0ku+R/0QM69dfkIL/rAIDTG+CSuQVDcKw==", "dev": true, "dependencies": { - "@vitest/utils": "0.31.1", + "@vitest/utils": "0.32.2", "concordance": "^5.0.4", "p-limit": "^4.0.0", "pathe": "^1.1.0" @@ -592,9 +592,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.31.1.tgz", - "integrity": "sha512-L3w5uU9bMe6asrNzJ8WZzN+jUTX4KSgCinEJPXyny0o90fG4FPQMV0OWsq7vrCWfQlAilMjDnOF9nP8lidsJ+g==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.2.tgz", + "integrity": "sha512-JwhpeH/PPc7GJX38vEfCy9LtRzf9F4er7i4OsAJyV7sjPwjj+AIR8cUgpMTWK4S3TiamzopcTyLsZDMuldoi5A==", "dev": true, "dependencies": { "magic-string": "^0.30.0", @@ -618,9 +618,9 @@ } }, "node_modules/@vitest/spy": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.31.1.tgz", - "integrity": "sha512-1cTpt2m9mdo3hRLDyCG2hDQvRrePTDgEJBFQQNz1ydHHZy03EiA6EpFxY+7ODaY7vMRCie+WlFZBZ0/dQWyssQ==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.2.tgz", + "integrity": "sha512-Q/ZNILJ4ca/VzQbRM8ur3Si5Sardsh1HofatG9wsJY1RfEaw0XKP8IVax2lI1qnrk9YPuG9LA2LkZ0EI/3d4ug==", "dev": true, "dependencies": { "tinyspy": "^2.1.0" @@ -630,12 +630,12 @@ } }, "node_modules/@vitest/ui": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.31.1.tgz", - "integrity": "sha512-+JJ2+rvRPAVxFLNE+WJOMzOjxqYPn7V2hl00uNwid6kquD+UHTa716Z7szfNeZMLnHOHv+fxq1UgLCymvVpE5w==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.32.2.tgz", + "integrity": "sha512-N5JKftnB8qzKFtpQC5OcUGxYTLo6wiB/95Lgyk6MF52t74Y7BJOWbf6EFYhXqt9J0MSbhOR2kapq+WKKUGDW0g==", "dev": true, "dependencies": { - "@vitest/utils": "0.31.1", + "@vitest/utils": "0.32.2", "fast-glob": "^3.2.12", "fflate": "^0.7.4", "flatted": "^3.2.7", @@ -651,12 +651,12 @@ } }, "node_modules/@vitest/utils": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.31.1.tgz", - "integrity": "sha512-yFyRD5ilwojsZfo3E0BnH72pSVSuLg2356cN1tCEe/0RtDzxTPYwOomIC+eQbot7m6DRy4tPZw+09mB7NkbMmA==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.2.tgz", + "integrity": "sha512-lnJ0T5i03j0IJaeW73hxe2AuVnZ/y1BhhCOuIcl9LIzXnbpXJT9Lrt6brwKHXLOiA7MZ6N5hSJjt0xE1dGNCzQ==", "dev": true, "dependencies": { - "concordance": "^5.0.4", + "diff-sequences": "^29.4.3", "loupe": "^2.3.6", "pretty-format": "^27.5.1" }, @@ -1879,9 +1879,9 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/cypress": { - "version": "12.13.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.13.0.tgz", - "integrity": "sha512-QJlSmdPk+53Zhy69woJMySZQJoWfEWun3X5OOenGsXjRPVfByVTHorxNehbzhZrEzH9RDUDqVcck0ahtlS+N/Q==", + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.15.0.tgz", + "integrity": "sha512-FqGbxsH+QgjStuTO9onXMIeF44eOrgVwPvlcvuzLIaePQMkl72YgBvpuHlBGRcrw3Q4SvqKfajN8iV5XWShAiQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -1974,9 +1974,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" }, "node_modules/de-indent": { "version": "1.0.2", @@ -2050,6 +2050,15 @@ "node": ">=0.4.0" } }, + "node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2291,16 +2300,16 @@ } }, "node_modules/eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -2380,9 +2389,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.14.1.tgz", - "integrity": "sha512-LQazDB1qkNEKejLe/b5a9VfEbtbczcOaui5lQ4Qw0tbRBbQYREyxxOV5BQgNDTqGPs9pxqiEpbMi9ywuIaF7vw==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.15.0.tgz", + "integrity": "sha512-XYzpK6e2REli100+6iCeBA69v6Sm0D/yK2FZP+fCeNt0yH/m82qZQq+ztseyV0JsKdhFysuSEzeE1yCmSC92BA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.3.0", @@ -5170,13 +5179,13 @@ } }, "node_modules/prosemirror-schema-list": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.3.tgz", - "integrity": "sha512-HD8yjDOusz7JB3oBFCaMOpEN9Z9DZttLr6tcASjnvKMc0qTyX5xgAN8YiMFFEcwyhF7WZrZ2YQkAwzsn8ICVbQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz", + "integrity": "sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==", "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.0.0" + "prosemirror-transform": "^1.7.3" } }, "node_modules/prosemirror-state": { @@ -5190,17 +5199,17 @@ } }, "node_modules/prosemirror-transform": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz", - "integrity": "sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.3.tgz", + "integrity": "sha512-qDapyx5lqYfxVeUWEw0xTGgeP2S8346QtE7DxkalsXlX89lpzkY6GZfulgfHyk1n4tf74sZ7CcXgcaCcGjsUtA==", "dependencies": { "prosemirror-model": "^1.0.0" } }, "node_modules/prosemirror-view": { - "version": "1.31.3", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.31.3.tgz", - "integrity": "sha512-UYDa8WxRFZm0xQLXiPJUVTl6H08Fn0IUVDootA7ZlQwzooqVWnBOXLovJyyTKgws1nprfsPhhlvWgt2jo4ZA6g==", + "version": "1.31.5", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.31.5.tgz", + "integrity": "sha512-tobRCDeCp61elR1d97XE/JTL9FDIfswZpWeNs7GKJjAJvWyMGHWYFCq29850p6bbG2bckP+i9n1vT56RifosbA==", "dependencies": { "prosemirror-model": "^1.16.0", "prosemirror-state": "^1.0.0", @@ -6007,13 +6016,13 @@ "dev": true }, "node_modules/terser": { - "version": "5.17.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", - "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", + "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -6107,9 +6116,9 @@ } }, "node_modules/tinyspy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.0.tgz", - "integrity": "sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", + "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", "dev": true, "engines": { "node": ">=14.0.0" @@ -6428,9 +6437,9 @@ } }, "node_modules/vite-node": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.31.1.tgz", - "integrity": "sha512-BajE/IsNQ6JyizPzu9zRgHrBwczkAs0erQf/JRpgTIESpKvNj9/Gd0vxX905klLkb0I0SJVCKbdrl5c6FnqYKA==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.32.2.tgz", + "integrity": "sha512-dTQ1DCLwl2aEseov7cfQ+kDMNJpM1ebpyMMMwWzBvLbis8Nla/6c9WQcqpPssTwS6Rp/+U6KwlIj8Eapw4bLdA==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -6451,9 +6460,9 @@ } }, "node_modules/vite-plugin-static-copy": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.15.0.tgz", - "integrity": "sha512-Ww+/Ug9guV45oIfIi/lA2z8v3K+lLHV9zCJqTVO4FTdqrJoZBj68VgGBSH1fi0N4q/EHW32RsL3ympi4Wlsq5w==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.16.0.tgz", + "integrity": "sha512-dMVEg5Z2SwYRgQnHZaeokvSKB4p/TOTf65JU4sP3U6ccSBsukqdtDOjpmT+xzTFHAA8WJjcS31RMLjUdWQCBzw==", "dev": true, "dependencies": { "chokidar": "^3.5.3", @@ -6483,19 +6492,19 @@ } }, "node_modules/vitest": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.31.1.tgz", - "integrity": "sha512-/dOoOgzoFk/5pTvg1E65WVaobknWREN15+HF+0ucudo3dDG/vCZoXTQrjIfEaWvQXmqScwkRodrTbM/ScMpRcQ==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.2.tgz", + "integrity": "sha512-hU8GNNuQfwuQmqTLfiKcqEhZY72Zxb7nnN07koCUNmntNxbKQnVbeIS6sqUgR3eXSlbOpit8+/gr1KpqoMgWCQ==", "dev": true, "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.31.1", - "@vitest/runner": "0.31.1", - "@vitest/snapshot": "0.31.1", - "@vitest/spy": "0.31.1", - "@vitest/utils": "0.31.1", + "@vitest/expect": "0.32.2", + "@vitest/runner": "0.32.2", + "@vitest/snapshot": "0.32.2", + "@vitest/spy": "0.32.2", + "@vitest/utils": "0.32.2", "acorn": "^8.8.2", "acorn-walk": "^8.2.0", "cac": "^6.7.14", @@ -6511,7 +6520,7 @@ "tinybench": "^2.5.0", "tinypool": "^0.5.0", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.31.1", + "vite-node": "0.32.2", "why-is-node-running": "^2.2.2" }, "bin": { @@ -6591,9 +6600,9 @@ } }, "node_modules/vue-docgen-api": { - "version": "4.72.3", - "resolved": "https://registry.npmjs.org/vue-docgen-api/-/vue-docgen-api-4.72.3.tgz", - "integrity": "sha512-76IpEW/pnY0Lfi+NaKkjYZGpEIxqVnDbCbzfZldLt0ldaKz2wvjepvCN1lhxWYtuucziXspFEEfvOY2shrrE3w==", + "version": "4.73.0", + "resolved": "https://registry.npmjs.org/vue-docgen-api/-/vue-docgen-api-4.73.0.tgz", + "integrity": "sha512-Hx3VZNi630q/JRhHsVo7qeLJdIDkz3JEp/LBITUZQoZBruGyQa7m80uxbEo4JGBpHMxWWHFO9AXrVy7jH4vzFA==", "dev": true, "dependencies": { "@babel/parser": "^7.21.4", @@ -7117,15 +7126,15 @@ } }, "@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", "dev": true }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -7175,9 +7184,9 @@ "dev": true }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "requires": { "@jridgewell/gen-mapping": "^0.3.0", @@ -7318,9 +7327,9 @@ "requires": {} }, "@vitest/coverage-c8": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.31.1.tgz", - "integrity": "sha512-6TkjQpmgYez7e3dbAUoYdRXxWN81BojCmUILJwgCy39uZFG33DsQ0rSRSZC9beAEdCZTpxR63nOvd9hxDQcJ0g==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.32.2.tgz", + "integrity": "sha512-z07kMTN6e4t1jDY4XXU6W1LxCb3V5Rw7KAZId4VM6BCIGLGz1QqwH9UWYWv7LemqQVnARl5CwaDDwVrkcYgwPg==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.1", @@ -7342,23 +7351,23 @@ } }, "@vitest/expect": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.31.1.tgz", - "integrity": "sha512-BV1LyNvhnX+eNYzJxlHIGPWZpwJFZaCcOIzp2CNG0P+bbetenTupk6EO0LANm4QFt0TTit+yqx7Rxd1qxi/SQA==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.2.tgz", + "integrity": "sha512-6q5yzweLnyEv5Zz1fqK5u5E83LU+gOMVBDuxBl2d2Jfx1BAp5M+rZgc5mlyqdnxquyoiOXpXmFNkcGcfFnFH3Q==", "dev": true, "requires": { - "@vitest/spy": "0.31.1", - "@vitest/utils": "0.31.1", + "@vitest/spy": "0.32.2", + "@vitest/utils": "0.32.2", "chai": "^4.3.7" } }, "@vitest/runner": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.31.1.tgz", - "integrity": "sha512-imWuc82ngOtxdCUpXwtEzZIuc1KMr+VlQ3Ondph45VhWoQWit5yvG/fFcldbnCi8DUuFi+NmNx5ehMUw/cGLUw==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.2.tgz", + "integrity": "sha512-06vEL0C1pomOEktGoLjzZw+1Fb+7RBRhmw/06WkDrd1akkT9i12su0ku+R/0QM69dfkIL/rAIDTG+CSuQVDcKw==", "dev": true, "requires": { - "@vitest/utils": "0.31.1", + "@vitest/utils": "0.32.2", "concordance": "^5.0.4", "p-limit": "^4.0.0", "pathe": "^1.1.0" @@ -7382,9 +7391,9 @@ } }, "@vitest/snapshot": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.31.1.tgz", - "integrity": "sha512-L3w5uU9bMe6asrNzJ8WZzN+jUTX4KSgCinEJPXyny0o90fG4FPQMV0OWsq7vrCWfQlAilMjDnOF9nP8lidsJ+g==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.2.tgz", + "integrity": "sha512-JwhpeH/PPc7GJX38vEfCy9LtRzf9F4er7i4OsAJyV7sjPwjj+AIR8cUgpMTWK4S3TiamzopcTyLsZDMuldoi5A==", "dev": true, "requires": { "magic-string": "^0.30.0", @@ -7404,21 +7413,21 @@ } }, "@vitest/spy": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.31.1.tgz", - "integrity": "sha512-1cTpt2m9mdo3hRLDyCG2hDQvRrePTDgEJBFQQNz1ydHHZy03EiA6EpFxY+7ODaY7vMRCie+WlFZBZ0/dQWyssQ==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.2.tgz", + "integrity": "sha512-Q/ZNILJ4ca/VzQbRM8ur3Si5Sardsh1HofatG9wsJY1RfEaw0XKP8IVax2lI1qnrk9YPuG9LA2LkZ0EI/3d4ug==", "dev": true, "requires": { "tinyspy": "^2.1.0" } }, "@vitest/ui": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.31.1.tgz", - "integrity": "sha512-+JJ2+rvRPAVxFLNE+WJOMzOjxqYPn7V2hl00uNwid6kquD+UHTa716Z7szfNeZMLnHOHv+fxq1UgLCymvVpE5w==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.32.2.tgz", + "integrity": "sha512-N5JKftnB8qzKFtpQC5OcUGxYTLo6wiB/95Lgyk6MF52t74Y7BJOWbf6EFYhXqt9J0MSbhOR2kapq+WKKUGDW0g==", "dev": true, "requires": { - "@vitest/utils": "0.31.1", + "@vitest/utils": "0.32.2", "fast-glob": "^3.2.12", "fflate": "^0.7.4", "flatted": "^3.2.7", @@ -7428,12 +7437,12 @@ } }, "@vitest/utils": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.31.1.tgz", - "integrity": "sha512-yFyRD5ilwojsZfo3E0BnH72pSVSuLg2356cN1tCEe/0RtDzxTPYwOomIC+eQbot7m6DRy4tPZw+09mB7NkbMmA==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.2.tgz", + "integrity": "sha512-lnJ0T5i03j0IJaeW73hxe2AuVnZ/y1BhhCOuIcl9LIzXnbpXJT9Lrt6brwKHXLOiA7MZ6N5hSJjt0xE1dGNCzQ==", "dev": true, "requires": { - "concordance": "^5.0.4", + "diff-sequences": "^29.4.3", "loupe": "^2.3.6", "pretty-format": "^27.5.1" } @@ -8329,9 +8338,9 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "cypress": { - "version": "12.13.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.13.0.tgz", - "integrity": "sha512-QJlSmdPk+53Zhy69woJMySZQJoWfEWun3X5OOenGsXjRPVfByVTHorxNehbzhZrEzH9RDUDqVcck0ahtlS+N/Q==", + "version": "12.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.15.0.tgz", + "integrity": "sha512-FqGbxsH+QgjStuTO9onXMIeF44eOrgVwPvlcvuzLIaePQMkl72YgBvpuHlBGRcrw3Q4SvqKfajN8iV5XWShAiQ==", "dev": true, "requires": { "@cypress/request": "^2.88.10", @@ -8408,9 +8417,9 @@ } }, "dayjs": { - "version": "1.11.7", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", - "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" }, "de-indent": { "version": "1.0.2", @@ -8464,6 +8473,12 @@ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, + "diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -8652,16 +8667,16 @@ "dev": true }, "eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -8723,9 +8738,9 @@ } }, "eslint-plugin-vue": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.14.1.tgz", - "integrity": "sha512-LQazDB1qkNEKejLe/b5a9VfEbtbczcOaui5lQ4Qw0tbRBbQYREyxxOV5BQgNDTqGPs9pxqiEpbMi9ywuIaF7vw==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.15.0.tgz", + "integrity": "sha512-XYzpK6e2REli100+6iCeBA69v6Sm0D/yK2FZP+fCeNt0yH/m82qZQq+ztseyV0JsKdhFysuSEzeE1yCmSC92BA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.3.0", @@ -10729,13 +10744,13 @@ } }, "prosemirror-schema-list": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.3.tgz", - "integrity": "sha512-HD8yjDOusz7JB3oBFCaMOpEN9Z9DZttLr6tcASjnvKMc0qTyX5xgAN8YiMFFEcwyhF7WZrZ2YQkAwzsn8ICVbQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz", + "integrity": "sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==", "requires": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", - "prosemirror-transform": "^1.0.0" + "prosemirror-transform": "^1.7.3" } }, "prosemirror-state": { @@ -10749,17 +10764,17 @@ } }, "prosemirror-transform": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz", - "integrity": "sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.7.3.tgz", + "integrity": "sha512-qDapyx5lqYfxVeUWEw0xTGgeP2S8346QtE7DxkalsXlX89lpzkY6GZfulgfHyk1n4tf74sZ7CcXgcaCcGjsUtA==", "requires": { "prosemirror-model": "^1.0.0" } }, "prosemirror-view": { - "version": "1.31.3", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.31.3.tgz", - "integrity": "sha512-UYDa8WxRFZm0xQLXiPJUVTl6H08Fn0IUVDootA7ZlQwzooqVWnBOXLovJyyTKgws1nprfsPhhlvWgt2jo4ZA6g==", + "version": "1.31.5", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.31.5.tgz", + "integrity": "sha512-tobRCDeCp61elR1d97XE/JTL9FDIfswZpWeNs7GKJjAJvWyMGHWYFCq29850p6bbG2bckP+i9n1vT56RifosbA==", "requires": { "prosemirror-model": "^1.16.0", "prosemirror-state": "^1.0.0", @@ -11388,13 +11403,13 @@ "dev": true }, "terser": { - "version": "5.17.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", - "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.1.tgz", + "integrity": "sha512-j1n0Ao919h/Ai5r43VAnfV/7azUYW43GPxK7qSATzrsERfW7+y2QW9Cp9ufnRF5CQUWbnLSo7UJokSWCqg4tsQ==", "dev": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -11471,9 +11486,9 @@ "dev": true }, "tinyspy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.0.tgz", - "integrity": "sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", + "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", "dev": true }, "tmp": { @@ -11685,9 +11700,9 @@ } }, "vite-node": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.31.1.tgz", - "integrity": "sha512-BajE/IsNQ6JyizPzu9zRgHrBwczkAs0erQf/JRpgTIESpKvNj9/Gd0vxX905klLkb0I0SJVCKbdrl5c6FnqYKA==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.32.2.tgz", + "integrity": "sha512-dTQ1DCLwl2aEseov7cfQ+kDMNJpM1ebpyMMMwWzBvLbis8Nla/6c9WQcqpPssTwS6Rp/+U6KwlIj8Eapw4bLdA==", "dev": true, "requires": { "cac": "^6.7.14", @@ -11699,9 +11714,9 @@ } }, "vite-plugin-static-copy": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.15.0.tgz", - "integrity": "sha512-Ww+/Ug9guV45oIfIi/lA2z8v3K+lLHV9zCJqTVO4FTdqrJoZBj68VgGBSH1fi0N4q/EHW32RsL3ympi4Wlsq5w==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-0.16.0.tgz", + "integrity": "sha512-dMVEg5Z2SwYRgQnHZaeokvSKB4p/TOTf65JU4sP3U6ccSBsukqdtDOjpmT+xzTFHAA8WJjcS31RMLjUdWQCBzw==", "dev": true, "requires": { "chokidar": "^3.5.3", @@ -11724,19 +11739,19 @@ } }, "vitest": { - "version": "0.31.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.31.1.tgz", - "integrity": "sha512-/dOoOgzoFk/5pTvg1E65WVaobknWREN15+HF+0ucudo3dDG/vCZoXTQrjIfEaWvQXmqScwkRodrTbM/ScMpRcQ==", + "version": "0.32.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.2.tgz", + "integrity": "sha512-hU8GNNuQfwuQmqTLfiKcqEhZY72Zxb7nnN07koCUNmntNxbKQnVbeIS6sqUgR3eXSlbOpit8+/gr1KpqoMgWCQ==", "dev": true, "requires": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.31.1", - "@vitest/runner": "0.31.1", - "@vitest/snapshot": "0.31.1", - "@vitest/spy": "0.31.1", - "@vitest/utils": "0.31.1", + "@vitest/expect": "0.32.2", + "@vitest/runner": "0.32.2", + "@vitest/snapshot": "0.32.2", + "@vitest/spy": "0.32.2", + "@vitest/utils": "0.32.2", "acorn": "^8.8.2", "acorn-walk": "^8.2.0", "cac": "^6.7.14", @@ -11752,7 +11767,7 @@ "tinybench": "^2.5.0", "tinypool": "^0.5.0", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.31.1", + "vite-node": "0.32.2", "why-is-node-running": "^2.2.2" }, "dependencies": { @@ -11783,9 +11798,9 @@ } }, "vue-docgen-api": { - "version": "4.72.3", - "resolved": "https://registry.npmjs.org/vue-docgen-api/-/vue-docgen-api-4.72.3.tgz", - "integrity": "sha512-76IpEW/pnY0Lfi+NaKkjYZGpEIxqVnDbCbzfZldLt0ldaKz2wvjepvCN1lhxWYtuucziXspFEEfvOY2shrrE3w==", + "version": "4.73.0", + "resolved": "https://registry.npmjs.org/vue-docgen-api/-/vue-docgen-api-4.73.0.tgz", + "integrity": "sha512-Hx3VZNi630q/JRhHsVo7qeLJdIDkz3JEp/LBITUZQoZBruGyQa7m80uxbEo4JGBpHMxWWHFO9AXrVy7jH4vzFA==", "dev": true, "requires": { "@babel/parser": "^7.21.4", diff --git a/panel/package.json b/panel/package.json index 8d53b69a88..0d66d06b63 100644 --- a/panel/package.json +++ b/panel/package.json @@ -19,7 +19,7 @@ "dependencies": { "container-query-polyfill": "^1.0.2", "css-has-pseudo": "^5.0.2", - "dayjs": "^1.11.7", + "dayjs": "^1.11.8", "mitt": "^3.0.0", "portal-vue": "^2.1.7", "prosemirror-commands": "^1.5.2", @@ -27,8 +27,8 @@ "prosemirror-inputrules": "^1.2.1", "prosemirror-keymap": "^1.2.2", "prosemirror-model": "^1.19.2", - "prosemirror-schema-list": "^1.2.3", - "prosemirror-view": "^1.31.3", + "prosemirror-schema-list": "^1.3.0", + "prosemirror-view": "^1.31.5", "vue": "^2.7.14", "vuedraggable": "^2.24.3", "vuelidate": "^0.7.7", @@ -36,26 +36,26 @@ }, "devDependencies": { "@vitejs/plugin-vue2": "^2.2.0", - "@vitest/coverage-c8": "^0.31.1", - "@vitest/ui": "^0.31.1", + "@vitest/coverage-c8": "^0.32.2", + "@vitest/ui": "^0.32.2", "@vue/test-utils": "^1.3.0", "autoprefixer": "^10.4.14", "cssnano": "^6.0.1", - "cypress": "^12.13.0", - "eslint": "^8.41.0", + "cypress": "^12.15.0", + "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-cypress": "^2.13.3", - "eslint-plugin-vue": "^9.14.1", + "eslint-plugin-vue": "^9.15.0", "jsdom": "^22.1.0", "postcss-dir-pseudo-class": "^7.0.2", "postcss-logical": "^6.2.0", "prettier": "^2.8.8", "rollup-plugin-external-globals": "^0.8.0", - "terser": "^5.17.6", + "terser": "^5.18.1", "vite": "^4.3.9", - "vite-plugin-static-copy": "^0.15.0", - "vitest": "^0.31.1", - "vue-docgen-api": "^4.72.3", + "vite-plugin-static-copy": "^0.16.0", + "vitest": "^0.32.2", + "vue-docgen-api": "^4.73.0", "vue-template-compiler": "^2.7.14" }, "browserslist": [ From 33c83f99b1067c44788d79efdb692199b9d41979 Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Tue, 20 Jun 2023 21:02:37 +0200 Subject: [PATCH 36/88] Upload: filter duplicates --- panel/src/panel/upload.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/panel/src/panel/upload.js b/panel/src/panel/upload.js index f799fa8702..22031b1513 100644 --- a/panel/src/panel/upload.js +++ b/panel/src/panel/upload.js @@ -172,6 +172,19 @@ export default (panel) => { // merge the new files with already selected files this.files = [...this.files, ...files]; + // remove duplicates by comparing crucial src attributes, + // preserving the newer file + this.files = this.files.filter( + (file, index) => + this.files.findLastIndex( + (x) => + x.src.name === file.src.name && + x.src.type === file.src.type && + x.src.size === file.src.size && + x.src.lastModified === file.src.lastModified + ) === index + ); + // apply the max limit to the list of files if (this.max !== null) { // slice from the end to keep the latest files From 282fc13556596a3cedd921b34bf86adb37ecbb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hoffmann=20=20=E0=B7=B4?= Date: Wed, 21 Jun 2023 13:07:36 +0200 Subject: [PATCH 37/88] Update config/fields/users.php Co-authored-by: Bastian Allgeier --- config/fields/users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/fields/users.php b/config/fields/users.php index 7ddda9c6f5..f30f6ab894 100644 --- a/config/fields/users.php +++ b/config/fields/users.php @@ -24,7 +24,7 @@ /** * Default selected user(s) when a new page/file/user is created */ - 'default' => function (string|array|bool $default = null) { + 'default' => function (string|array|bool|null $default = null) { return $default; }, From 074bf96c6f581840cb855608866d8fbd947e279a Mon Sep 17 00:00:00 2001 From: Ahmet Bora Date: Thu, 22 Jun 2023 20:35:36 +0300 Subject: [PATCH 38/88] New `templatesIgnore` for pages section Implements https://kirby.nolt.io/130 --- config/sections/pages.php | 32 +++++++++- tests/Cms/Sections/PagesSectionTest.php | 85 +++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/config/sections/pages.php b/config/sections/pages.php index 88e76daf49..7d8b92e664 100644 --- a/config/sections/pages.php +++ b/config/sections/pages.php @@ -50,11 +50,23 @@ return $status; }, + /** + * Filters the list by single template. + */ + 'template' => function (string $template = null) { + return $template; + }, /** * Filters the list by templates and sets template options when adding new pages to the section. */ 'templates' => function ($templates = null) { return A::wrap($templates ?? $this->template); + }, + /** + * Excludes the selected templates. + */ + 'templatesIgnore' => function ($templates = null) { + return A::wrap($templates); } ], 'computed' => [ @@ -94,8 +106,21 @@ return false; } + $intendedTemplate = $page->intendedTemplate()->name(); + // filter by all set templates - if ($this->templates && in_array($page->intendedTemplate()->name(), $this->templates) === false) { + if ( + $this->templates && + in_array($intendedTemplate, $this->templates) === false + ) { + return false; + } + + // exclude by all ignored templates + if ( + $this->templatesIgnore && + in_array($intendedTemplate, $this->templatesIgnore) === true + ) { return false; } @@ -227,6 +252,11 @@ $templates = $this->kirby()->blueprints(); } + // excludes ignored templates + if ($templatesIgnore = $this->templatesIgnore) { + $templates = array_diff($templates, $templatesIgnore); + } + // convert every template to a usable option array // for the template select box foreach ($templates as $template) { diff --git a/tests/Cms/Sections/PagesSectionTest.php b/tests/Cms/Sections/PagesSectionTest.php index 4863d78fde..82140d0968 100644 --- a/tests/Cms/Sections/PagesSectionTest.php +++ b/tests/Cms/Sections/PagesSectionTest.php @@ -849,4 +849,89 @@ public function testQuery() $this->assertCount(1, $section->pages()); } + + public function testTemplatesIgnore() + { + $parent = new Page([ + 'slug' => 'test', + 'children' => [ + ['slug' => 'a', 'template' => 'foo'], + ['slug' => 'b', 'template' => 'bar'], + ['slug' => 'c', 'template' => 'baz'] + ] + ]); + + // test 1 + $section = new Section('pages', [ + 'name' => 'test', + 'model' => $parent, + 'templatesIgnore' => $expected = [ + 'foo', + 'baz' + ] + ]); + + $this->assertSame($expected, $section->templatesIgnore()); + $this->assertCount(1, $section->pages()); + + // test 2 + $section = new Section('pages', [ + 'name' => 'test', + 'model' => $parent, + 'templatesIgnore' => $expected = [ + 'bar' + ] + ]); + + $this->assertSame($expected, $section->templatesIgnore()); + $this->assertCount(2, $section->pages()); + + // test 3 + $section = new Section('pages', [ + 'name' => 'test', + 'model' => $parent, + 'templatesIgnore' => $expected = [ + 'not-exists' + ] + ]); + + $this->assertSame($expected, $section->templatesIgnore()); + $this->assertCount(3, $section->pages()); + } + + public function testBlueprints() + { + $app = $this->app->clone([ + 'blueprints' => [ + 'pages/default' => ['title' => 'Default'], + 'pages/section-a' => ['title' => 'Section A'], + 'pages/section-b' => ['title' => 'Section B'], + 'pages/section-c' => ['title' => 'Section C'], + ], + ]); + + $app->impersonate('kirby'); + + $parent = new Page([ + 'slug' => 'test', + 'children' => [ + ['slug' => 'a', 'template' => 'section-a'], + ['slug' => 'b', 'template' => 'section-b'], + ['slug' => 'c', 'template' => 'section-c'] + ] + ]); + + $section = new Section('pages', [ + 'name' => 'test', + 'model' => $parent, + 'templatesIgnore' => $expected = [ + 'section-a', + 'section-c' + ] + ]); + + $this->assertSame($expected, $section->templatesIgnore()); + $this->assertCount(1, $section->pages()); + $this->assertCount(2, $section->blueprints()); + } } From e413b719eab33f341b6be77c13449df31e8a9751 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 09:52:15 +0200 Subject: [PATCH 39/88] Disable the toggle in the link field if the field is disabled --- panel/src/components/Forms/Field/LinkField.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index e1b7347c91..bf3a3bc871 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -6,6 +6,7 @@ From cac27a1a6a63b12ceac272dac4b1aa5e43acc380 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 09:52:58 +0200 Subject: [PATCH 40/88] Remove unsupported attributes --- config/fields/link.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/fields/link.php b/config/fields/link.php index 2cc9775c7f..2c4d86f5e9 100644 --- a/config/fields/link.php +++ b/config/fields/link.php @@ -7,6 +7,11 @@ return [ 'props' => [ + 'after' => null, + 'before' => null, + 'icon' => null, + 'placeholder' => null, + 'value' => function (string|null $value = null) { return $value ?? ''; } From d20194e106595c1ea596b34a5fc3d5ff26b520f4 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 10:01:29 +0200 Subject: [PATCH 41/88] Use Page::isAccessible instead of isReadable --- src/Cms/Api.php | 2 +- tests/Cms/Api/ApiTest.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Cms/Api.php b/src/Cms/Api.php index b6c872d801..844cd80fde 100644 --- a/src/Cms/Api.php +++ b/src/Cms/Api.php @@ -158,7 +158,7 @@ public function pages(string|null $parentId = null, string|null $status = null): default => $parent->children() }; - return $pages->filter('isReadable', true); + return $pages->filter('isAccessible', true); } /** diff --git a/tests/Cms/Api/ApiTest.php b/tests/Cms/Api/ApiTest.php index 669a16b136..94ba488f6a 100644 --- a/tests/Cms/Api/ApiTest.php +++ b/tests/Cms/Api/ApiTest.php @@ -373,6 +373,11 @@ public function testPage() $this->api->page('does-not-exist'); } + public function testPages() + { + $this->assertSame(['a/aa', 'a/ab'], $this->api->pages('a')->keys()); + } + public function testUser() { $app = $this->app->clone([ From 8494ddcbbea40523cecf2f510a40c1ac470c28eb Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 12:27:45 +0200 Subject: [PATCH 42/88] Label and placeholder for tel field and tel type in link field --- i18n/translations/en.json | 4 +++- panel/src/components/Forms/Field/LinkField.vue | 4 ++-- panel/src/components/Forms/Input/TelInput.vue | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/i18n/translations/en.json b/i18n/translations/en.json index c3554e1cdb..f70c08e04a 100644 --- a/i18n/translations/en.json +++ b/i18n/translations/en.json @@ -554,8 +554,10 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Title", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Template", + "title": "Title", "today": "Today", "toolbar.button.clear": "Clear formatting", diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index bf3a3bc871..51ba0b2a0f 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -154,9 +154,9 @@ export default { }, tel: { icon: "phone", - label: "Phone", + label: this.$t("tel"), pattern: "[+]{0,1}[0-9]+", - placeholder: "Enter a phone number …", + placeholder: this.$t("tel.placeholder"), input: "tel", value: (value) => "tel:" + value } diff --git a/panel/src/components/Forms/Input/TelInput.vue b/panel/src/components/Forms/Input/TelInput.vue index 2b528bf950..36def4b781 100644 --- a/panel/src/components/Forms/Input/TelInput.vue +++ b/panel/src/components/Forms/Input/TelInput.vue @@ -6,11 +6,12 @@ export const props = { mixins: [TextInputProps], props: { autocomplete: { - type: String, default: "tel" }, + placeholder: { + default: () => window.panel.$t("tel.placeholder") + }, type: { - type: String, default: "tel" } } From a095e5f38842b723e62492cd2668537123003593 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 13:07:48 +0200 Subject: [PATCH 43/88] New anchor icon --- panel/public/img/icons.svg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/panel/public/img/icons.svg b/panel/public/img/icons.svg index 437d89501f..5591c8861c 100644 --- a/panel/public/img/icons.svg +++ b/panel/public/img/icons.svg @@ -12,6 +12,9 @@ + + + From 283de097b3967e39b7892abef05c0f7c603fe24b Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 13:08:01 +0200 Subject: [PATCH 44/88] Only validate URLs if they start with http or https --- config/fields/link.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/fields/link.php b/config/fields/link.php index 2c4d86f5e9..6f17756817 100644 --- a/config/fields/link.php +++ b/config/fields/link.php @@ -50,7 +50,7 @@ return true; } - if (Url::isAbsolute($value) === true) { + if (Str::startsWith($value, 'http://') === true || Str::startsWith($value, 'https://') === true) { if (V::url($value) === false) { throw new InvalidArgumentException([ 'key' => 'validation.url' From e58cb38d5c01c4b768fd56d868b6ba2f47b82ea8 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 13:08:25 +0200 Subject: [PATCH 45/88] Link type detection per type and new link types --- .../src/components/Forms/Field/LinkField.vue | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index 51ba0b2a0f..3ff079c821 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -126,39 +126,79 @@ export default { types() { return { url: { + detect: (value) => { + return /^(http|https):\/\//.test(value); + }, icon: "url", label: this.$t("url"), + link: (value) => value, placeholder: this.$t("url.placeholder"), input: "url", value: (value) => value }, page: { + detect: (value) => { + return this.isPageUUID(value) === true; + }, icon: "page", label: this.$t("page"), + link: (value) => value, placeholder: this.$t("select") + " …", input: "text", value: (value) => value }, file: { + detect: (value) => { + return this.isFileUUID(value) === true; + }, icon: "file", label: this.$t("file"), + link: (value) => value, placeholder: this.$t("select") + " …", value: (value) => value }, email: { + detect: (value) => { + return value.startsWith("mailto:"); + }, icon: "email", label: this.$t("email"), + link: (value) => value.replace(/^mailto:/, ""), placeholder: this.$t("email.placeholder"), input: "email", value: (value) => "mailto:" + value }, tel: { + detect: (value) => { + return value.startsWith("tel:"); + }, icon: "phone", label: this.$t("tel"), + link: (value) => value.replace(/^tel:/, ""), pattern: "[+]{0,1}[0-9]+", placeholder: this.$t("tel.placeholder"), input: "tel", value: (value) => "tel:" + value + }, + anchor: { + detect: (value) => { + return value.startsWith("#"); + }, + icon: "anchor", + label: "Anchor", + link: (value) => value, + pattern: "^#.+", + placeholder: "#element", + input: "text", + value: (value) => value + }, + custom: { + detect: (value) => true, + icon: "title", + label: "Custom", + link: (value) => value, + input: "text", + value: (value) => value } }; } @@ -166,6 +206,10 @@ export default { watch: { value: { handler(value, old) { + if (value === old) { + return; + } + const parts = this.detect(value); this.linkType = this.linkType ?? parts.type; @@ -186,38 +230,21 @@ export default { detect(value) { value = value ?? ""; - if (this.isPageUUID(value) === true) { - return { - type: "page", - link: value - }; - } - - if (this.isFileUUID(value) === true) { - return { - type: "file", - link: value - }; - } - - if (value.startsWith("tel:")) { + if (value.length === 0) { return { - type: "tel", - link: value.replace(/^tel:/, "") + type: "url", + link: "" }; } - if (value.startsWith("mailto:")) { - return { - type: "email", - link: value.replace(/^mailto:/, "") - }; + for (const type in this.types) { + if (this.types[type].detect(value) === true) { + return { + type: type, + link: this.types[type].link(value) + }; + } } - - return { - type: "url", - link: value - }; }, focus() { this.$refs.input?.focus(); From 96408d997f049b89724a769d4e2bb28f93d8b906 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 13:26:47 +0200 Subject: [PATCH 46/88] Collapse link field browsers on outside click --- panel/src/components/Forms/Field/LinkField.vue | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index 3ff079c821..e2ddcb5f22 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -110,6 +110,12 @@ export default { type: String } }, + created() { + this.$events.on("click", this.onOutsideClick); + }, + destroyed() { + this.$events.off("click", this.onOutsideClick); + }, data() { return { model: null, @@ -279,6 +285,11 @@ export default { onInvalid(invalid) { this.isInvalid = invalid; }, + onOutsideClick(event) { + if (this.$el.contains(event.target) === false) { + this.expanded = false; + } + }, async preview() { if (this.linkType === "page" && this.linkValue) { this.model = await this.previewForPage(this.linkValue); From 1eb1f7002e2358c4311b6bd69f4e20595f2a2b36 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 13:34:14 +0200 Subject: [PATCH 47/88] Fix linting issues --- panel/src/components/Forms/Field/LinkField.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index e2ddcb5f22..ef9d51f6c2 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -110,12 +110,6 @@ export default { type: String } }, - created() { - this.$events.on("click", this.onOutsideClick); - }, - destroyed() { - this.$events.off("click", this.onOutsideClick); - }, data() { return { model: null, @@ -199,7 +193,7 @@ export default { value: (value) => value }, custom: { - detect: (value) => true, + detect: () => true, icon: "title", label: "Custom", link: (value) => value, @@ -228,6 +222,12 @@ export default { immediate: true } }, + created() { + this.$events.on("click", this.onOutsideClick); + }, + destroyed() { + this.$events.off("click", this.onOutsideClick); + }, methods: { clear() { this.$emit("input", ""); From 3f27a62712735a760be50ec0bae08d15d4f11784 Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Sun, 18 Jun 2023 15:40:30 +0200 Subject: [PATCH 48/88] Link field: select available type --- config/fields/link.php | 10 +++++-- .../src/components/Forms/Field/LinkField.vue | 26 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/config/fields/link.php b/config/fields/link.php index 6f17756817..340a94eaff 100644 --- a/config/fields/link.php +++ b/config/fields/link.php @@ -12,6 +12,12 @@ 'icon' => null, 'placeholder' => null, + /** + * @values 'url, 'page, 'file', 'email', 'tel', 'custom' + */ + 'options' => function (array $options = null) { + return $options; + }, 'value' => function (string|null $value = null) { return $value ?? ''; } @@ -56,9 +62,9 @@ 'key' => 'validation.url' ]); } - - return true; } + + return true; }, ] ]; diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index ef9d51f6c2..00b5e8739f 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -14,7 +14,7 @@ { @@ -201,6 +202,19 @@ export default { value: (value) => value } }; + }, + activeTypes() { + if (!this.options) { + return this.availableTypes; + } + + const available = {}; + + for (const type of this.options) { + available[type] = this.availableTypes[type]; + } + + return available; } }, watch: { @@ -243,11 +257,11 @@ export default { }; } - for (const type in this.types) { - if (this.types[type].detect(value) === true) { + for (const type in this.availableTypes) { + if (this.availableTypes[type].detect(value) === true) { return { type: type, - link: this.types[type].link(value) + link: this.availableTypes[type].link(value) }; } } From bb3b1c6554b1e41d2b81874a65d2bf28370a1dd5 Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 14:16:00 +0200 Subject: [PATCH 49/88] Validate link types on the backend Validate link type on the backend --- config/fields/link.php | 146 ++++++++++++++++++++++++++++++-------- i18n/translations/en.json | 3 + 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/config/fields/link.php b/config/fields/link.php index 340a94eaff..b86cf9fa95 100644 --- a/config/fields/link.php +++ b/config/fields/link.php @@ -13,55 +13,141 @@ 'placeholder' => null, /** - * @values 'url, 'page, 'file', 'email', 'tel', 'custom' + * @values 'anchor', 'url, 'page, 'file', 'email', 'tel', 'custom' */ - 'options' => function (array $options = null) { - return $options; + 'options' => function (array|null $options = null): array { + return $options ?? [ + 'url', + 'page', + 'file', + 'email', + 'tel', + 'anchor', + 'custom' + ]; }, 'value' => function (string|null $value = null) { return $value ?? ''; } ], + 'methods' => [ + 'activeTypes' => function () { + return array_filter($this->availableTypes(), function (string $type) { + return in_array($type, $this->props['options']) === true; + }, ARRAY_FILTER_USE_KEY); + }, + 'availableTypes' => function () { + return [ + 'anchor' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, '#') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return Str::startsWith($value, '#') === true; + }, + ], + 'email' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'mailto:') === true; + }, + 'link' => function (string $value): string { + return str_replace('mailto:', '', $value); + }, + 'validate' => function (string $value): bool { + return V::email($value); + }, + ], + 'file' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'file://') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return V::uuid($value, 'file'); + }, + ], + 'page' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'page://') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return V::uuid($value, 'page'); + }, + ], + 'tel' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'tel:') === true; + }, + 'link' => function (string $value): string { + return str_replace('tel:', '', $value); + }, + 'validate' => function (string $value): bool { + return V::tel($value); + }, + ], + 'url' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'http://') === true || Str::startsWith($value, 'https://') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return V::url($value); + }, + ], + + // needs to come last + 'custom' => [ + 'detect' => function (string $value): bool { + return true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (): bool { + return true; + }, + ] + ]; + }, + ], 'validations' => [ - 'value' => function ($value) { - if (V::uuid($value) === true) { + 'value' => function (string|null $value) { + if (empty($value) === true) { return true; } - if (Str::startsWith($value, 'mailto:') === true) { - // get the plain email address - $email = str_replace('mailto:', '', $value); + $detected = false; - // validate the email address - if (V::email($email) === false) { - throw new InvalidArgumentException([ - 'key' => 'validation.email' - ]); + foreach ($this->activeTypes() as $type => $options) { + if ($options['detect']($value) !== true) { + continue; } - return true; - } - - if (Str::startsWith($value, 'tel:') === true) { - // get the plain phone number - $tel = str_replace('tel:', '', $value); + $link = $options['link']($value); + $detected = true; - // validate the phone address - if (V::tel($tel) === false) { + if ($options['validate']($link) === false) { throw new InvalidArgumentException([ - 'key' => 'validation.tel' + 'key' => 'validation.' . $type ]); } - - return true; } - if (Str::startsWith($value, 'http://') === true || Str::startsWith($value, 'https://') === true) { - if (V::url($value) === false) { - throw new InvalidArgumentException([ - 'key' => 'validation.url' - ]); - } + // none of the configured types has been detected + if ($detected === false) { + throw new InvalidArgumentException([ + 'key' => 'validation.linkType' + ]); } return true; diff --git a/i18n/translations/en.json b/i18n/translations/en.json index f70c08e04a..b3da374a9a 100644 --- a/i18n/translations/en.json +++ b/i18n/translations/en.json @@ -211,6 +211,7 @@ "error.validation.accepted": "Please confirm", "error.validation.alpha": "Please only enter characters between a-z", "error.validation.alphanum": "Please only enter characters between a-z or numerals 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Please enter a value between \"{min}\" and \"{max}\"", "error.validation.boolean": "Please confirm or deny", "error.validation.contains": "Please enter a value that contains \"{needle}\"", @@ -227,6 +228,7 @@ "error.validation.integer": "Please enter a valid integer", "error.validation.ip": "Please enter a valid IP address", "error.validation.less": "Please enter a value lower than {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "The value does not match the expected pattern", "error.validation.max": "Please enter a value equal to or lower than {max}", "error.validation.maxlength": "Please enter a shorter value. (max. {max} characters)", @@ -248,6 +250,7 @@ "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Please enter a valid URL", "expand": "Expand", From b81413137e088402985153398dd6cc473ad80d6e Mon Sep 17 00:00:00 2001 From: Bastian Allgeier Date: Fri, 23 Jun 2023 15:08:16 +0200 Subject: [PATCH 50/88] Translate custom --- i18n/translations/en.json | 1 + panel/src/components/Forms/Field/LinkField.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/i18n/translations/en.json b/i18n/translations/en.json index b3da374a9a..acd4ddeddd 100644 --- a/i18n/translations/en.json +++ b/i18n/translations/en.json @@ -17,6 +17,7 @@ "copy.all": "Copy all", "copy.success": "{count} copied!", "create": "Create", + "custom": "Custom", "date": "Date", "date.select": "Select a date", diff --git a/panel/src/components/Forms/Field/LinkField.vue b/panel/src/components/Forms/Field/LinkField.vue index 00b5e8739f..66e01d61e2 100644 --- a/panel/src/components/Forms/Field/LinkField.vue +++ b/panel/src/components/Forms/Field/LinkField.vue @@ -196,7 +196,7 @@ export default { custom: { detect: () => true, icon: "title", - label: "Custom", + label: this.$t("custom"), link: (value) => value, input: "text", value: (value) => value From 5cdd4c8fc8687bf3f3168096b3b3724d7cd26882 Mon Sep 17 00:00:00 2001 From: Ahmet Bora Date: Fri, 23 Jun 2023 16:42:12 +0300 Subject: [PATCH 51/88] Add more unit test for api `isAccessible` part --- tests/Cms/Api/ApiTest.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Cms/Api/ApiTest.php b/tests/Cms/Api/ApiTest.php index 94ba488f6a..e6fbf94565 100644 --- a/tests/Cms/Api/ApiTest.php +++ b/tests/Cms/Api/ApiTest.php @@ -378,6 +378,34 @@ public function testPages() $this->assertSame(['a/aa', 'a/ab'], $this->api->pages('a')->keys()); } + public function testPagesNotAccessible() + { + $app = $this->app->clone([ + 'blueprints' => [ + 'pages/api-protected' => [ + 'options' => ['access' => false] + ] + ], + 'site' => [ + 'children' => [ + [ + 'slug' => 'a' + ], + [ + 'slug' => 'b', + 'template' => 'api-protected' + ], + [ + 'slug' => 'c' + ] + ] + ] + ]); + $app->impersonate('kirby'); + + $this->assertSame(['a', 'c'], $app->api()->pages()->keys()); + } + public function testUser() { $app = $this->app->clone([ From b341a4389f8fedd519d0ebfc4292c317ff976708 Mon Sep 17 00:00:00 2001 From: Ahmet Bora Date: Fri, 23 Jun 2023 18:16:08 +0300 Subject: [PATCH 52/88] Fix language detect with custom locale #5320 --- src/Cms/App.php | 9 ++++++++- tests/Cms/App/AppLanguagesTest.php | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Cms/App.php b/src/Cms/App.php index 83b3e74684..d5b2ed2c98 100644 --- a/src/Cms/App.php +++ b/src/Cms/App.php @@ -580,7 +580,14 @@ public function detectedLanguage(): Language|null $visitor = $this->visitor(); foreach ($visitor->acceptedLanguages() as $acceptedLang) { - $closure = fn ($language) => $language->locale(LC_ALL) === $acceptedLang->locale(); + $closure = function ($language) use ($acceptedLang) { + $languageLocale = $language->locale(LC_ALL); + $acceptedLocale = $acceptedLang->locale(); + + return $languageLocale === $acceptedLocale || + $acceptedLocale === Str::substr($languageLocale, 0, 2); + }; + if ($language = $languages->filter($closure)?->first()) { return $language; } diff --git a/tests/Cms/App/AppLanguagesTest.php b/tests/Cms/App/AppLanguagesTest.php index 738d1dd11b..5effd239c8 100644 --- a/tests/Cms/App/AppLanguagesTest.php +++ b/tests/Cms/App/AppLanguagesTest.php @@ -59,6 +59,7 @@ public function detectedLanguageProvider(): array ['fr', 'en'], ['en-US, en;q=0.5', 'us'], ['en-US;q=0.5, de;q=0.8, fr;q=0.9', 'de'], + ['tr, en-US;q=0.9, en;q=0.8', 'tr'] ]; } @@ -86,6 +87,11 @@ public function testDetectedLanguage($accept, $expected) 'code' => 'us', 'name' => 'English (US)', 'locale' => 'en_US' + ], + [ + 'code' => 'tr', + 'name' => 'Turkish (TR)', + 'locale' => 'tr_TR.utf-8' ] ], 'options' => [ From e6c45c57518e6400c1ec1900e8122a1cf8916ff0 Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Tue, 27 Jun 2023 21:33:54 +0200 Subject: [PATCH 53/88] Fix vitest code coverage provider --- panel/package-lock.json | 247 ++++++++-------------------------------- panel/package.json | 2 +- 2 files changed, 51 insertions(+), 198 deletions(-) diff --git a/panel/package-lock.json b/panel/package-lock.json index 90fd4fba99..5e8943c437 100644 --- a/panel/package-lock.json +++ b/panel/package-lock.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@vitejs/plugin-vue2": "^2.2.0", - "@vitest/coverage-c8": "^0.32.2", + "@vitest/coverage-v8": "^0.32.2", "@vitest/ui": "^0.32.2", "@vue/test-utils": "^1.3.0", "autoprefixer": "^10.4.14", @@ -504,26 +504,32 @@ "vue": "^2.7.0-0" } }, - "node_modules/@vitest/coverage-c8": { + "node_modules/@vitest/coverage-v8": { "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.32.2.tgz", - "integrity": "sha512-z07kMTN6e4t1jDY4XXU6W1LxCb3V5Rw7KAZId4VM6BCIGLGz1QqwH9UWYWv7LemqQVnARl5CwaDDwVrkcYgwPg==", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-0.32.2.tgz", + "integrity": "sha512-/+V3nB3fyeuuSeKxCfi6XmWjDIxpky7AWSkGVfaMjAk7di8igBwRsThLjultwIZdTDH1RAxpjmCXEfSqsMFZOA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", - "c8": "^7.13.0", + "@bcoe/v8-coverage": "^0.2.3", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.5", "magic-string": "^0.30.0", "picocolors": "^1.0.0", - "std-env": "^3.3.2" + "std-env": "^3.3.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": ">=0.30.0 <1" + "vitest": ">=0.32.0 <1" } }, - "node_modules/@vitest/coverage-c8/node_modules/magic-string": { + "node_modules/@vitest/coverage-v8/node_modules/magic-string": { "version": "0.30.0", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", @@ -1244,32 +1250,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/c8": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.13.0.tgz", - "integrity": "sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1521,17 +1501,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2771,19 +2740,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -2861,15 +2817,6 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -3613,6 +3560,20 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/istanbul-reports": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", @@ -5461,15 +5422,6 @@ "throttleit": "^1.0.0" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -6916,48 +6868,12 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", @@ -7326,17 +7242,23 @@ "dev": true, "requires": {} }, - "@vitest/coverage-c8": { + "@vitest/coverage-v8": { "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-c8/-/coverage-c8-0.32.2.tgz", - "integrity": "sha512-z07kMTN6e4t1jDY4XXU6W1LxCb3V5Rw7KAZId4VM6BCIGLGz1QqwH9UWYWv7LemqQVnARl5CwaDDwVrkcYgwPg==", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-0.32.2.tgz", + "integrity": "sha512-/+V3nB3fyeuuSeKxCfi6XmWjDIxpky7AWSkGVfaMjAk7di8igBwRsThLjultwIZdTDH1RAxpjmCXEfSqsMFZOA==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.1", - "c8": "^7.13.0", + "@bcoe/v8-coverage": "^0.2.3", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.5", "magic-string": "^0.30.0", "picocolors": "^1.0.0", - "std-env": "^3.3.2" + "std-env": "^3.3.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.1.0" }, "dependencies": { "magic-string": { @@ -7864,26 +7786,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "c8": { - "version": "7.13.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.13.0.tgz", - "integrity": "sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^2.0.0", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", - "rimraf": "^3.0.2", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" - } - }, "cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -8061,17 +7963,6 @@ "string-width": "^4.2.0" } }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -9028,16 +8919,6 @@ "is-callable": "^1.1.3" } }, - "foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -9092,12 +8973,6 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", @@ -9635,6 +9510,17 @@ } } }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, "istanbul-reports": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", @@ -11000,12 +10886,6 @@ "throttleit": "^1.0.0" } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -12031,39 +11911,12 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/panel/package.json b/panel/package.json index 0d66d06b63..248c2fc5f7 100644 --- a/panel/package.json +++ b/panel/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@vitejs/plugin-vue2": "^2.2.0", - "@vitest/coverage-c8": "^0.32.2", + "@vitest/coverage-v8": "^0.32.2", "@vitest/ui": "^0.32.2", "@vue/test-utils": "^1.3.0", "autoprefixer": "^10.4.14", From 954485ef6df6c638336bc286d071156f29c6752a Mon Sep 17 00:00:00 2001 From: Nico Hoffmann Date: Tue, 27 Jun 2023 21:25:12 +0200 Subject: [PATCH 54/88] File blueprint `focus` option Fixes #5316 --- panel/src/components/Layout/FilePreview.vue | 12 ++++-- panel/src/components/Views/FileView.vue | 13 +----- src/Panel/File.php | 48 ++++++++++++++++----- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/panel/src/components/Layout/FilePreview.vue b/panel/src/components/Layout/FilePreview.vue index 1897b4a7bf..8cce0e168a 100644 --- a/panel/src/components/Layout/FilePreview.vue +++ b/panel/src/components/Layout/FilePreview.vue @@ -6,7 +6,7 @@ -