type="text/css"href="styles.css"/>
</head>
<body>(->html doc)</body>
</html>

Fans of hand-coded CSS, I trust you to take it from here: put a "styles.css" file in your project directory, and enjoy the results.

But folks who paid attention during the first tutorial might be wondering “Can we link to a dynamically generated "styles.css.pp" file?”

Yes, of course. Here’s the rule of thumb: when you’re making links between files — whether CSS, HTML, or anything else — Pollen doesn’t care whether the file is static or dynamic. You just refer to it by its ultimate name, in this case "styles.css". If a static "styles.css" file exists, Pollen will use that. If it doesn’t, Pollen will look for a source file it can use to make "styles.css", and generate it on the spot. (You can also start with a static file, and change it to be dynamic later, and Pollen will do the right thing.)

So to use a dynamic CSS file, we don’t need to make any changes to "template.html.p". We just need to add "styles.css.pp" to the project directory:

"styles.css.pp"
 
(define h1-color"blue")
(define strong-color"green")
 
body{padding:3em;font-size:20px;}
h1{background:◊|h1-color|;color:white;}
strong{color:◊|strong-color|;}

This time, when you refresh "article.html" in the project server, Pollen will run "styles.css.pp" to get the "styles.css" file it needs, and you’ll see the new colors in the page. As usual, if you update "styles.css.pp", Pollen will notice and regenerate the CSS file when you refresh the page.

Can you add multiple dynamic style sheets? Yes.
Can you mix dynamic and static style sheets? Yes.
Can you add a dynamic JavaScript file? Yes.

You’ve got the general idea, right? So let’s move on.

6.5IntermissionπŸ”— i

If you only need one page for your article, you can stop here. You now know everything necessary to publish a single-page article using authoring mode. You know how to create the mandatory ingredients — a source file and a template — and you also know how to link to an optional CSS file, which can be dynamically generated.

If you want to create a multi-page article, however, you need to get through one more big idea. This might be a good time to take a break.

6.6PagetreesπŸ”— i

A pagetree is a hierarchical list of Pollen pages. When you have multiple pages in your project, the pagetree establishes relationships among those pages. At its most basic, a pagetree establishes a linear sequence for the pages. But pagetrees can also establish hierarchical relationships — for instance, a book-length project can be organized into chapters, the chapters into sections, and so on. The pagetree doesn’t impose any semantics on the organization of your project. It’s just a tree, and it’s up to you how many layers to establish, what those layers mean, and so on.

6.6.1Pagetree navigationπŸ”— i

An obvious use for a pagetree is to add navigational links to your pages. Obviously, in a multi-page article, readers need a way of getting from one page to the next. In this part of the tutorial, we’ll expand our sample article from one page to three, and see how to create “previous page” and “next page” links in our template that are dynamically generated relative to the current page.

6.6.2Using the automatic pagetreeπŸ”— i

You’ve actually already been exposed to pagetrees (though I didn’t tell you about it at the time). Recall that the dashboard of the project server is located at http://localhost:8080/index.ptree. The list of files you see in the dashboard is a pagetree that Pollen generates by getting a list of files in the current directory and arranging them in alphabetical order.

Thus, if the multiple pages in your project are already ordered alphabetically by filename, then you can rely on this automatic pagetree. (More commonly, you’ll make a separate pagetree file for the navigation — but we’ll cover that later in this tutorial.)

From earlier in the tutorial, you have a Markdown source file called "article.html.pmd" that looks like this:

"article.html.pmd"
 
DeepThought
============
 
Iam**so**happytobewritingthis.

Let’s supplement this source file by creating two others for the project:

"barticle.html.pmd"
 
BarticleTitle
==============
 
Thewonderfulsecondpartofthearticle.
"carticle.html.pmd"
 
CarticleTitle
==============
 
Theterrificthirdpart.

As before, you can fill these source files with any sample Markdown content you like. Moreover, you don’t have to use the filenames "barticle.html.pmd" and "carticle.html.pmd" — the point is that the intended sequence needs to match the alphabetic sorting of the filenames.

We’ll reuse the "template.html.p" and "styles.css.pp" files from earlier in the tutorial. Move or delete the other tutorial files so that your dashboard in the project server shows only these five files:

If you click on any of the three Markdown sources, you’ll see it converted into HTML using "template.html.p", with styles from the generated "styles.css".

The automatic pagetree for this project is exactly what you see in the dashboard: a list of the three article files, followed by "styles.css" and "template.html.p".

6.6.3Adding navigation links to the template with hereπŸ”— i

Recall from earlier in the tutorial that the content of your source file is made available in the template through the special variable doc. Likewise, the output name of the current source file is made available through the special variable here.

To make any navigation link — up, down, sideways — the general idea is that we use here as input to a pagetree-navigation function, which then looks up the answer in the current pagetree.

First, let’s just see here on its own. Update your template as follows:

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
</body>
</html>

If you refresh "article.html", you’ll now see the line “The current page is called article.html.” Switch to "barticle.html", and you’ll see “The current page is called barticle.html.” Makes sense, right?

Notice that here is always an output filename, on the idea that navigation naturally makes connections among output files, not source files. In this case, this conversion to output name means we lose the "pmd" extension. (As we’ll see below, we’ll also use these output filenames when we’re Making a pagetree file.)

Now let’s use pagetree functions to show the names of the previous and next pages. Consistent with the usual Pollen policy of obviousness, these functions are called previous and next :

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
Thepreviousis◊|(previous here)|.
Thenextis◊|(next here)|.
</body>
</html>

Refresh "barticle.html". You’ll now see that “The current page is called barticle.html. The previous is article.html. The next is carticle.html.” So far, so good: we’re correctly deriving the previous and next pages from the automatic pagetree.

All that’s left is adding the hyperlinks:

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
Thepreviousis<ahref="◊|(previous here)|">◊|(previous here)|</a>.
Thenextis<ahref="◊|(next here)|">◊|(next here)|</a>.
</body>
</html>

Refresh "barticle.html", and you’ll see that the names of the previous and next pages are now hyperlinks to those pages. Click through and convince yourself that it works.

The documentation for pagetree Navigation will tell you about the other functions available for generating navigation links.

6.6.4Handling navigation boundaries with conditionalsπŸ”— i

If you clicked through to "article.html" or "carticle.html", you might’ve noticed a couple problems. Because "article.html" is the first page in the automatic pagetree, it doesn’t have any previous page it can link to. And the next-page link for "carticle.html" is "styles.css", which is strictly correct — it is, in fact, the next file in the automatic pagetree — but it’s not part of our article, so we’d rather stop the navigation there.

One way to fix the problem would be to have three separate template files — the standard one with both previous- and next-page links, one with only a next-page link, and one with only a previous-page link.

But since we have a whole programming language available in Pollen, that’s a weak solution. The better way is to add conditionals to the template to selectively change the navigation. That keeps things simple, because we’ll still have only one "template.html.p" to deal with.

To handle "article.html", we want to hide the previous-page navigation link when there’s no previous page. As it turns out, if the previous function can’t find a previous page, it will return false. So we just need to wrap our previous-page navigation in the when/splice command like so:

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
when/splice [(previous here)]{Thepreviousis
<ahref="◊|(previous here)|">◊|(previous here)|</a>.}
Thenextis<ahref="◊|(next here)|">◊|(next here)|</a>.
</body>
</html>

The basic form of when/splice is

◊when/splice[test-condition]{content-to-insert}

We’re writing this command in Pollen style — note the square braces around the condition, and the curly braces around the text. Using (previous here) as the condition is shorthand for “when (previous here) does not return false...”

Programmers in the audience might be getting anxious about the repeated use of (previous here) — you’re welcome to store that value in a variable, and everything will work the same way:

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
(define prev-page(previous here))
when/splice [prev-page]{Thepreviousis
<ahref="◊|prev-page|">◊|prev-page|</a>.}
Thenextis<ahref="◊|(next here)|">◊|(next here)|</a>.
</body>
</html>

We need a different technique for handling the end of the next-page navigation, because we’re not reaching the actual end of the pagetree. We’re just reaching the end of the pages we care about navigating through.

What condition will help us detect this? This time, we might notice that the names of our article pages all contain the string article. In a real project, you’d probably want a more meaningful test condition. But in this tutorial, what we’ll do is hide the next-page navigation if the name of the next page doesn’t contain "article". As we did before, we wrap our navigation line in the when/splice function:

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
(define prev-page(previous here))
when/splice [prev-page]{Thepreviousis
<ahref="◊|prev-page|">◊|prev-page|</a>.}
when/splice [(regexp-match "article"(symbol->string (next here)))]{
Thenextis<ahref="◊|(next here)|">◊|(next here)|</a>.}
</body>
</html>

This time, the condition is (regexp-match "article"(symbol->string (next here))). How were you supposed to know this? You weren’t. That’s why this is a tutorial. Without going on a lengthy detour, the regexp-match function is Racket’s regular-expression matcher. It returns true if the first string (in this case, "article") is found inside the second string (in this case, we convert (next here) to a string by wrapping it in symbol->string ).

Even if some of the programmy bits went over your head just now, relax and paste the code into your template. What you’ll see when you refresh "carticle.html" is that the next-page link is gone. So now our template lets us navigate among the pages of our article, and the conditionals handle the end pages correctly.

6.6.5Making a pagetree fileπŸ”— i

I didn’t want to dwell on programming complications in the previous section. Why? Because the extra programming was necessary only because we made life somewhat difficult for ourselves by relying on the automatic pagetree.

A better way to solve the problem is to avoid it altogether by making a pagetree file.

Pagetree files have a different syntax and status than other Pollen source files, so they’re compiled using their own Pollen dialect. To invoke this dialect, you just start the file with #lang pollen and name the file with the "ptree" extension, for instance "my-project.ptree". While you can have as many pagetrees in your project as you want, Pollen will first look for one named "index.ptree".

A pagetree file only gets one file extension, because unlike other Pollen source files, it’s never converted into an output file.

So let’s make an "index.ptree" file. At its simplest, a pagetree file can just be a list of files in the intended order. In DrRacket, create a new file in your project directory as follows:

"index.ptree"
 
carticle.html
article.html
barticle.html

Now run the file. The result will be:

'(pagetree-root carticle.html article.html barticle.html)

Pretty boring, I know. But behind the scenes, Pollen’s pagetree compiler is making sure your tree is valid (e.g., no duplicate or malformed names). Today it’s boring, but on the day you have a long and complicated pagetree, you’ll be grateful.

Notice that the names in this pagetree are the names of output files, not source files. This is deliberate. Neither you nor Pollen has to care which files are static vs. dynamic. This next pagetree wouldn’t be wrong in the sense of bad syntax — the pagetree compiler won’t complain — but it would be wrong in the sense of not-what-you-want, because it refers to source names rather than output names:

"bad-index.ptree"
 
carticle.html.pmd
article.html.pmd
barticle.html.pmd

You also probably noticed that the files are in a different order than they were in the automatic pagetree: "carticle.html" is first, followed by "article.html" and then "barticle.html". This too is deliberate, so we can see what happens with a differently ordered pagetree.

Pagetrees don’t change as often as other source files, so as a performance optimization, the project server ignores them when deciding whether to refresh a file. Thus, after updating a pagetree, you have to manually reset your project. Go to your terminal window and stop the project server with ctrl+C.

Then clear Pollen’s cache of rendered pages:

> raco pollen reset

And restart the project server:

> raco pollen start

Now click on "carticle.html" again. You’ll notice that the navigation links are different. You won’t see a previous-page link — because "carticle.html" is now the first page in the pagetree — and the next page will show up as "article.html". Click through to "article.html", and you’ll see the navigation likewise updated. Click through to "barticle.html", and you’ll see ...

BAM! An error page that says

symbol->string: contract violation
expected: symbol?
given: #f

What happened? We switched to using our own pagetree file but we didn’t update the conditions in our template. Once we reach "barticle.html", the value of (next here) is false (#f). But the (symbol->string (next here)) command in the template needs a symbol as input. Hence the error.

So let’s go back and fix that. Because we don’t have extraneous files in our pagetree anymore, we can change the second conditional in the template to work the same way as the first:

"template.html.p"
<html>
<head>
<metacharset="UTF-8">
<title>(select ' h1doc),byMB</title>
<linkrel="stylesheet"type="text/css"href="styles.css"/>
</head>
<body>(->html doc)
Thecurrentpageiscalled◊|here|.
(define prev-page(previous here))
when/splice [prev-page]{Thepreviousis<ahref="◊|prev-page|">◊|prev-page|</a>.}
(define next-page(next here))
when/splice [next-page]{Thenextis<ahref="◊|next-page|">◊|next-page|</a>.}
</body>
</html>

Refresh "barticle.html" — because you’re updating the template, you don’t need to restart the project server — and you’ll see the right result. The previous-page link goes to "article.html", and the next-page link is hidden.

6.6.6index.ptree & the project serverπŸ”— i

One more thing before we wrap up this tutorial. Remember that the dashboard of the project server is at http://localhost:8080/index.ptree? By default, the project server will generate a pagetree from an alphabetical directory listing.

But if you put your own "index.ptree" file in that directory, the project server will use that for the dashboard instead. In fact, visit http://localhost:8080/index.ptree now and you’ll see what I mean. Consistent with the "index.ptree" you made, you’ll now see "carticle.html", "article.html", and "barticle.html", but not "template.html.p" nor "styles.css" (even though they’re still in the project directory).

6.7Second tutorial completeπŸ”— i

That was a big tutorial. I commend you for your tenacity and patience. But you made a giant leap forward. Despite the silly examples, you now know everything you need to make multi-page articles — books, even — using Markdown authoring mode in Pollen. If this is all you ever use Pollen for, it’ll be a big improvement over ordinary Markdown.

But there’s more. We haven’t even gotten into the more elaborate automation that’s possible with Pollen, nor Pollen’s own markup language. We’ll cover that in the third tutorial.

top
up

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /