Fields: Custom blocks type internal kirby link

I just followed the guide on custom blocks here for creating a button block type. Now, the example uses url as type which I thought replicates the functionality of normal Kirby link tags. Instead, this uses URL and marks an internal Kirby link (just the page name) as invalid.

How would I create a component that allows for internal Kirby links as well as external URLs? Is it possible?

Thanks,
Anselm

One option would be to use a radion field which let’s you select if it is an internal or external link and then show a pages field or a url field depending on the selection, similar to how the image block works.

See blueprint image block: image | Kirby CMS

Or here: Block factory: Creating your own blocks collection | Kirby CMS

1 Like

Hm, while that would certainly work I hoped for a better solution like the normally used link handler in Kirby that can just auto-detect which type of link is entered… it’s not very user friendly with the radio option handler.

Hm, but there is no such thing as a link field that has this functionality. You could, of course, create a custom field.

I’m not so sure it’s not more user friendly. While it’s one more decision to make, you get URL validation and a pages selector instead of having to enter the page id manually.

In any textarea or writer field, you can click to add a link. This opens the Kirby field that writes a kirbytext link where you can either enter a full valid URL or a page slug to a Kirby page.

The user is used to this behaviour already so in my opinion this would be the convenient, already known way of dealing with it.

Yes, I know, but honestly, from my point of view it’s a pain having to enter a page id manually, particularly a long one of a deeply nested page.

The dialog where you can enter URL or page id is just a simple text field, which you can of course also use instead of url/pages field in your block.

But it doesn’t behave like one as it creates an actual Kirbytext link from my input? When I’m just using a text field, it doesn’t create URLs and link tags from it.

Maybe I’m missing something, but why would you have to store a link tag? You can create the link from the stored value in your preview/frontend snippet?

I don’t need to store it necessarily, I don’t mind to be honest. I hoped for a solution that exists in Kirby already instead of replicating Kirby’s own behaviour to get the exact same functionality in Kirby Blocks that already exists elsewhere (any textarea, any writer field).

I understand where you are coming from, but I would argue that these are different use cases. In your blocks field, you would ideally just store the value, i.e., either a URL or a page id in your field, not a rendered link or a kirbytag. Then in your frontend snippet, you would then output either the URL in an anker tag directly, or in case of a page or even file id, convert the value to a page object and then render the link with the page/file URL (or even just the id as relative link).

1 Like

Thanks, so there’s simply no solution in Kirby to reuse the internal logic of it, right @texnixe? Then I’ll recreate it in my snippet, shouldn’t be too much of work. Just hoped to not have to duplicate the functionality, hence the question initially.

In regards to the “how to store the data” discussion: In a context of a WYSIWG page (and all blocks pages are such ones), I would disagree because data integrity and abstraction of data and content doesn’t matter much anymore.

Here’s the button.php blocks snippet. The yml field in button.yml is of type text now.

<?php $url = null;
if ( filter_var($block->link(), FILTER_VALIDATE_URL) || strpos($block->link(), 'mailto:') === 0 ) {
    $url = $block->link();
} else {
    $url = $site->find($block->link())->url();
} ?>
<a href="<?= $url ?>" class="button-action">
  <?= $block->text() ?>
</a>

It currently just decides between a real HTTP URL, a mailto: URL, and an internal Kirby reference.
If you have PHP8, you can even use a more convenient function for the mailto action:

<?php $url = null;
if ( filter_var($block->link(), FILTER_VALIDATE_URL) || str_starts_with($block->link(), 'mailto:') ) {
    $url = $block->link();
} else {
    $url = ($page =  $site->find($block->link())) ? $page->url() : null;
} ?>
<a href="<?= $url ?>" class="button-action">
  <?= $block->text() ?>
</a>

Hope it helps others if they land on this topic.

Keep in mind that this code will throw an error if the page doesn’t exist.

$url = ($page =  $site->find($block->link())) ? $page->url() : null;
1 Like