Change page context of a custom tag

I am enjoying kirby and the incredible dedication of the author and the community for some time.
What I am trying to follow is the “one-pager” site example.
So what I am trying to solve is:
I have some page and in the content I want to have a relative link to a file, attached to this page. But if I output the content from a snippet then the context page for the link would be the single container page rather than the page to which the file is attached so the link would not be resolved and so the result would be no output from kirbytext for that link.
So the question is is there a possibility to switch context when outputting content from a snippet? Or some other solution?
Thanks again for a nice piece of software!

I’m not sure, but maybe you could just work with absolute links to your files by prepending them with

<?php echo $site->url() ... ?>

Just to get this right: So you are talking about a link to a file you add via Kirbytext into a content file like

(image: image.jpg)

? If so, the link should be rendered correctly automatically.

If it is something else, could you please post your code?

Let’s say I have a page with a PDF file. I can put somewhere in the page text a relative link to the PDF:
(link:MyDocument.PDF text:Here is the document)
If I output the page itself, using it’s own template:

<?php echo kirbytext( $page->content() ) ?>

the link would be resolved relatively to the page and would render.
If I follow the one pager tutorial I would build one big page by outputting the contents of each subpage using a snippet.
But then the context page would be the one-pager, the link would not resolve and there would be no output (I tried this).
I am just using an absolute link right now, but if I decide to reshuffle the pages then the link would not be valid and I would have to change it.

If you use

(file: yourdocument.pdf text: file)

it will work.

Actually it does not.
(link) or (file) would work relatively for a file attached to the page if they were used from the page’s template. Any snippet runs in the context of the containing page, which IS NOT the page that the file is attached to.
Let me explain again. Let’s say I have an about page which contains a PDF - OurPortfolio.PDF.
If I create a one-pager, in the home template I would call snippets rendering any of the site pages, including about.

So in home I have:

<?php snippet( "about" ) ?>

And if in the snippet I have:


The link would not be resolved.

I think what you’re asking can be framed like this:

(file: my-document.pdf page: another/page text: Download PDF)

The normal file Kirbytag doesn’t support querying other pages for files— only the current page context.

However— I wrote a similar thing to reference a document that might be in a Resources page, elsewhere on the site. You can see the source here, and a use case here.

I’ve included the source, below, to save you a click. :wink:

Please be aware that I wrote this not for public release, and it’s largely untested and undocumented. Use or adapt at your own risk!, and let me know how it goes!

kirbytext::$tags['resource'] = array(
  'attr' => array('page', 'text'),
  'html' => function($tag) {
    if ( is_object( $page = page($tag->attr('page')) ) && is_object( $file = $page->file($tag->attr('resource')) ) ) {
      # If we're good to go, return (not print) the HTML for a link.
      return html::a($file->url(), $tag->attr('text', $file->filename()), array(
        'class' => 'file'
    } else {
      # Otherwise, output just the text that was provided in the short-tag
      return $tag->attr('text', 'Unknown Resource');

You use it like this:

(resource: html-cheatsheet.pdf page: resources text: syntax of HTML)

Following this thread with quite some interest! Let me explain what I need:

I have a site where I have a list of Instructors. In a blueprint, I’ve set up a custom select query field nested within a structure. Here’s the code for that specific field:

			label: Select an Instructor
			type: select
			options: query
				page: calendar/instructors
				fetch: children
			required: true

That’s the code that outputs the {{instructor}} tag. I use that in my entry, allowing me to reference {{instructor}}. But the {{instructor}} tag outputs the page URL of that field, not the text version of the page title. So instead of getting “John Doe” as an instructor, I get “john-doe” as an output. Needless to say, that isn’t ideal. I’d like to have the {{instructor}} tag allow me to output John Doe with a link directly to that instructor page, instead of just having john-doe appear everywhere {{instructor}} is used.

Any ideas? I feel like this resource snippet is pretty darn close but need help climbing this hill. Thanks for any nuggets of code you can provide!

I don’t quite see the relation of you question to the thread above. So I hope I understand what you are trying to achieve.

In your blueprint, you can define the text that is displayed to the user and the value that is entered into the text file like this:

  label: Select an Instructor
  type: select
  options: query
    page: calendar/instructors
    fetch: children
    text: '{{title}}'
    value: '{{title}}'
  required: true

The default value is always the uid, so here we change this to title.

So if you output $page->instructor(or the yaml syntax alternativ for your structure field), you’ll get John Doe instead of john-doe.

That’s fantastic, @texnixe. Answers my question perfectly. The first few refreshes removed the description field right above it, but now the description field appears above it and works like a charm. Thanks, @texnixe!

Oops, I corrected the typo above

And I removed the reference to a typo… so there never was a typo! Your reputation remains intact, @texnixe!

One more follow-up question that is in the same vein. In a structure field, I have this field “Courses,” that I want to output the default value (the uid) for, but display the text to the user. Here’s how the field looks:

			label: Course or Activity Title
			type: text
			text: '{{title}}'
			required: true

This code should theoretically do two things: output text for the user and output the default value of uid since I haven’t defined the value. Right now, it outputs only the text that the user types in as the course title for both the text displayed to the user and uid, but I need an uid to be able to link to the course as a page as well using this:

<a href='/calendar/events/<?php echo $event->getField(course); ?>'><?php echo '<br>'.$event->getField(course); ?></a>

It displays the text perfectly, but the uid isn’t there- it links to the text so it appears as /Vinyasa%20Yoga instead of vinyasa-yoga, the uid. How do I get both the text and UID? Thanks for helping out!

The field in your blueprint is of type text, so this won’t work. You would have to use a select field in the first place and that’s probably what you want to do?

If you use a select field in your blueprint like this:

  label: Course or Activity Title
  type: select
  text: '{{title}}'
  required: true

Then the form would show the title to the user in the select field and write the uid into the text file.

But maybe you want to achieve something else, like create a new course?

1 Like

That’s right- I want the user to be able to create a new course (and an associated page, actually).

I’m using the mzur kirby calendar plugin to create a listing of events, specifically the teaser snippet. So when an user goes to the page menu and selects “Events”, they will see a teaser of upcoming courses.

Current Setup:
A folder contains all courses, like calendar/events/coursepage. A structure field is also deployed at calendar that the user must populate so that the teaser snippet can pull fields and display them. This sucks, because the user has to fill out two separate portions to display events as either a subpage or a section in the structure field of calendar.

I don’t know how to modify the below snippet (a modified version of mzur’s teaser snippet) to reference the subpages of calendar/events.

<?php $calendar = calendar($page->calendar()->yaml()); ?>  
	foreach ($calendar->getEvents() as $event):
		if (--$items < 0) break;

On the front end, the user should see “Vinyasa Yoga” as a link that leads to the actual course subpage.

-fix snippet to reference children (course pages) of calendar/events. Then the text field for “Course or Activity Title” won’t need to output a title and UID, as the user just creates subpages each time he/she needs to create a new course, bypassing the structure field in calendar that the snippet references.
-enable user to type text for the {{course}} tag in the events folder that does two things: it creates a subpage for that course in calendar/events AND outputs an UID that allows the title for that event to be linked with its actual course subpage.

I would love this because then in the events page, the administrator can see a list of all current events at calendar/events vs going into each page individually to find out course / time. I’m OK with reversing this- having the event page pull data from each subpage that the administrator creates is fine as well. Thoughts?

The first solution is certainly the easiest because it works out of the box.

For the second implementation, I’m not sure what would be the best way. Either custom fields or even better the brand new hooks that come with Kirby 2.1.0 might be your friend.

Thanks for the reply- know that was a complicated question. Do you know how I can modify:

<?php $calendar = calendar($page->calendar()->yaml()); ?>  
	foreach ($calendar->getEvents() as $event):
		if (--$items < 0) break;

to reference all the children of calendar/events?

I posted my question re: hooks in a new topic- I certainly agree with you that the Kirby 2.1 hooks are exactly what I’m looking for!


I have never used this plugin, if it expects the yaml data, then you probably need to modify the calendar class. But I’d rather leave that to be answered by our more advanced devs here …