diff --git a/resources/js/components/fieldtypes/bard/LinkToolbar.vue b/resources/js/components/fieldtypes/bard/LinkToolbar.vue
index 2753706489a..72ceabdc006 100644
--- a/resources/js/components/fieldtypes/bard/LinkToolbar.vue
+++ b/resources/js/components/fieldtypes/bard/LinkToolbar.vue
@@ -80,6 +80,15 @@
+
+
+
after('statamic://')->before('?')->before('#')->toString();
[$type, $id] = explode('::', $ref, 2);
$data = null;
diff --git a/src/Fieldtypes/Bard/LinkMark.php b/src/Fieldtypes/Bard/LinkMark.php
index 955011d86f8..1277d60c8f1 100644
--- a/src/Fieldtypes/Bard/LinkMark.php
+++ b/src/Fieldtypes/Bard/LinkMark.php
@@ -62,7 +62,7 @@ protected function convertHref($href)
return $href;
}
- $ref = Str::after($href, 'statamic://');
+ $ref = str($href)->after('statamic://')->before('?')->before('#')->toString();
if (! $item = Data::find($ref)) {
return '';
@@ -70,11 +70,13 @@ protected function convertHref($href)
$selectAcrossSites = Augmentor::$currentBardConfig['select_across_sites'] ?? false;
+ $extras = Str::after($href, $ref);
+
if (! $selectAcrossSites && ! $this->isApi() && $item instanceof Entry) {
- return ($item->in(Site::current()->handle()) ?? $item)->url();
+ return ($item->in(Site::current()->handle()) ?? $item)->url().$extras;
}
- return $selectAcrossSites ? $item->absoluteUrl() : $item->url();
+ return $selectAcrossSites ? $item->absoluteUrl().$extras : $item->url().$extras;
}
private function isApi()
diff --git a/tests/Fieldtypes/BardTest.php b/tests/Fieldtypes/BardTest.php
index 7eca35f1d5a..01b907697e3 100644
--- a/tests/Fieldtypes/BardTest.php
+++ b/tests/Fieldtypes/BardTest.php
@@ -1351,6 +1351,116 @@ public function it_doesnt_localize_when_select_across_sites_setting_is_enabled()
$this->assertEquals('The One', $augmented);
}
+ #[Test]
+ public function it_preserves_query_params_on_entry_links()
+ {
+ tap(Facades\Collection::make('blog')->routes('blog/{slug}'))->save();
+ EntryFactory::collection('blog')->id('123')->slug('my-post')->data(['title' => 'My Post'])->create();
+
+ $field = (new Bard)->setField(new Field('test', ['type' => 'bard']));
+
+ $augmented = $field->augment([
+ ['type' => 'text', 'marks' => [['type' => 'link', 'attrs' => ['href' => 'statamic://entry::123?foo=bar']]], 'text' => 'Link'],
+ ]);
+
+ $this->assertEquals('Link', $augmented);
+ }
+
+ #[Test]
+ public function it_preserves_anchors_on_entry_links()
+ {
+ tap(Facades\Collection::make('blog')->routes('blog/{slug}'))->save();
+ EntryFactory::collection('blog')->id('123')->slug('my-post')->data(['title' => 'My Post'])->create();
+
+ $field = (new Bard)->setField(new Field('test', ['type' => 'bard']));
+
+ $augmented = $field->augment([
+ ['type' => 'text', 'marks' => [['type' => 'link', 'attrs' => ['href' => 'statamic://entry::123#section']]], 'text' => 'Link'],
+ ]);
+
+ $this->assertEquals('Link', $augmented);
+ }
+
+ #[Test]
+ public function it_preserves_query_params_and_anchors_on_entry_links()
+ {
+ tap(Facades\Collection::make('blog')->routes('blog/{slug}'))->save();
+ EntryFactory::collection('blog')->id('123')->slug('my-post')->data(['title' => 'My Post'])->create();
+
+ $field = (new Bard)->setField(new Field('test', ['type' => 'bard']));
+
+ $augmented = $field->augment([
+ ['type' => 'text', 'marks' => [['type' => 'link', 'attrs' => ['href' => 'statamic://entry::123?foo=bar#section']]], 'text' => 'Link'],
+ ]);
+
+ $this->assertEquals('Link', $augmented);
+ }
+
+ #[Test]
+ public function it_preserves_appends_on_localized_entry_links()
+ {
+ $this->setSites([
+ 'en' => ['url' => 'http://localhost/', 'locale' => 'en'],
+ 'fr' => ['url' => 'http://localhost/fr/', 'locale' => 'fr'],
+ ]);
+
+ Facades\Site::setCurrent('fr');
+
+ tap(Facades\Collection::make('blog')->routes('blog/{slug}'))->sites(['en', 'fr'])->save();
+
+ EntryFactory::id('parent')->collection('blog')->slug('theparent')->id(123)->locale('en')->create();
+ EntryFactory::id('123-fr')->origin('123')->locale('fr')->collection('blog')->slug('one-fr')->data(['title' => 'Le One'])->create();
+
+ $field = (new Bard)->setField(new Field('test', array_merge(['type' => 'bard'], ['select_across_sites' => false])));
+
+ $augmented = $field->augment([
+ ['type' => 'text', 'marks' => [['type' => 'link', 'attrs' => ['href' => 'statamic://entry::123-fr?foo=bar#section']]], 'text' => 'The One'],
+ ]);
+
+ $this->assertEquals('The One', $augmented);
+ }
+
+ #[Test]
+ public function it_preserves_appends_on_entry_links_with_select_across_sites()
+ {
+ $this->setSites([
+ 'en' => ['url' => 'http://localhost/', 'locale' => 'en'],
+ 'fr' => ['url' => 'http://localhost/fr/', 'locale' => 'fr'],
+ ]);
+
+ Facades\Site::setCurrent('en');
+
+ tap(Facades\Collection::make('blog')->routes('blog/{slug}'))->sites(['en', 'fr'])->save();
+
+ EntryFactory::id('parent')->collection('blog')->slug('theparent')->id(123)->locale('en')->create();
+ EntryFactory::id('123-fr')->origin('123')->locale('fr')->collection('blog')->slug('one-fr')->data(['title' => 'Le One'])->create();
+
+ $field = (new Bard)->setField(new Field('test', array_merge(['type' => 'bard'], ['select_across_sites' => true])));
+
+ $augmented = $field->augment([
+ ['type' => 'text', 'marks' => [['type' => 'link', 'attrs' => ['href' => 'statamic://entry::123-fr?foo=bar#section']]], 'text' => 'The One'],
+ ]);
+
+ $this->assertEquals('The One', $augmented);
+ }
+
+ #[Test]
+ public function it_gets_link_data_with_appends()
+ {
+ tap(Facades\Collection::make('pages')->routes('/{slug}'))->save();
+ EntryFactory::collection('pages')->id('1')->slug('about')->data(['title' => 'About'])->create();
+
+ $bard = $this->bard(['save_html' => true, 'sets' => null]);
+
+ $html = 'Link with appends
';
+
+ $prosemirror = (new Augmentor($this))->renderHtmlToProsemirror($html)['content'];
+
+ $this->assertEquals([
+ 'entry::1' => ['title' => 'About', 'permalink' => 'http://localhost/about'],
+ ], $bard->getLinkData($prosemirror));
+ }
+
private function bard($config = [])
{
return (new Bard)->setField(new Field('test', array_merge(['type' => 'bard', 'sets' => ['one' => []]], $config)));