Up and Running with Frontier Web Site Management
by Matt Neuburg
If your page is an outline, including the situation where it is an outline being Included in another page, it can have a different sort of "private" glossary. This is accomplished by means of the #define directive.It is easiest to explain this simply by giving an example. Suppose, at the start of your outline, you have lines that go:
#define "jefferson" The God who gave us life gave us liberty at the same time. The hand of force may destroy but cannot disjoin them.Then, every time in that outline you use what looks like a macro call, "{jefferson}" (without the quotes), the entire phrase will be substituted: "The God who gave us life gave us liberty at the same time. The hand of force may destroy but cannot disjoin them."
What the #define evaluates to is everything subordinate to its definition line. This can be as many lines of material as you like, which will all be run together into a single string without any HTML formatting being introduced into it.
(Putting it another way, lineation may be meaningful in an outline, depending on what renderer you're using, but the material in a #define is not considered to have any lineation.)
But of course the main part of the outline does have lineation, and this lineation generates HTML (depending on our renderer). So, if we are using the default renderer, which puts the appropriate <li> tag before every line, a line consisting of
{jefferson}will end up bulleted, at the appropriate level, a single item consisting of the entire phrase.
One use of this is to circumvent restrictions imposed by outlines and the outline renderers. Suppose, for example, we are rendering our outline with the default renderer, and we wish to have a bulleted item of considerable length: say, 600 characters.
However, Frontier outline lines are limited to 255 characters. What are we to do? If we split our material into several outline lines, Frontier will render that as several different bulleted items.
Okay, you could write your own renderer. But another way is to use the #define mechanism as a workaround.
A similar trick allows you to use the default outline renderer, yet have a bulleted item consisting of multiple paragraphs. (Do you see how to do it? It's left as an exercise for the reader!)
Although a #define call can appear only in an outline, it may be referenced in any type of document. That's right: you can use the #define call to send information from one document to another!For instance, let's suppose your template is an outline. Then that outline could contain a #define directive. If what is defined is called "jefferson", then a {jefferson} call will work in any page which uses that template, even if the page itself is not an outline!
So we've used a #define to pass information down the hierarchy.
More common in practical usage is to take advantage of the #define mechanism to pass information up the hierarchy. In particular, it is often used to hand a template a piece of information which will change for every page rendered with that template.
Let us suppose, for example, that every page is to have a title, possibly different from its titlebar #title, which is to appear in large colored letters at the beginning of the page -- that is, before the <bodytext>.
Since this is to be true of every page, and since it happens outside the <bodytext>, it makes sense to give the job of inserting this large colored title to the template. That way, if we later decide we want a small non-colored title instead, we have but to change the HTML in the template, once, and render our pages again.
So, we might have a line in the template just before the <bodytext> call, that looks like this:
<center> <h1><font size="7" color="#cc9900">{myTitle}</font></h1> </center>Then, in every (outline) page that is to be rendered with that template, we have a line
#define "myTitle"to which is subordinated a line with the desired title (not in quotes). For instance, it might be:
#define "myTitle" This Is A Cool TitleAs each page is rendered, the {myTitle} call in the template is replaced by its value as given in the body of the page. We have handed the value upwards from the page to the template that contains it. Try this yourself with fourthPage, if you like.
Now, you might say: "But the #define mechanism works only for outlines! What if I want to use this method when my pages are not outlines?" The solution is to use a custom directive.It turns out that you are always free to make up your own directive. If you have a line in your page that says:
#myTitle "This Is A Cool Title"then Frontier will not complain on the grounds that it knows of no directive "#myTitle". On the contrary, it will do with "#myTitle" what it does with every directive.
Just what does Frontier do with directives? As it prepares to render a page, it sucks out the directives in the site table and in the page itself and stores them in a special table at suites.html.data.page.
So, in this case, Frontier will just create an entry in suites.html.data.page called "myTitle" and give it the value you said to give it, in this case the string "This Is A Cool Title".
The only difference between this directive and one of the pre-defined directives is that Frontier is not set up to do anything with this information -- that's up to you.
One way to do something with the value of a custom directive is to call it with the same macro-like syntax we used with #define. In this case, any occurrence of {myTitle} in your page would be replaced by "This Is A Cool Title" (without the quotes).
Why does this work? It's because (take a deep breath, now) a macro call is just a line of UserTalk, and a line of UserTalk can be the name of a database entry, and one of the places Frontier searches for database entries mentioned in macro calls is suites.html.data.page (we talked about this under "Includes and Macros", remember?). So Frontier finds "myTitle" as an entry in suites.html.data.page, and substitutes its value, which is "This Is A Cool Title."
So it would also be possible to say {suites.html.data.page.myTitle} -- but why bother?
And #define works the same way: it, too, places an entry into suites.html.data.page, which will be available to these macro-like calls up and down the hierarchy as the page is rendered.
Indeed, you could just as well use a custom directive in your outline instead of a #define, except that since outline lines are limited to 255 characters you wouldn't be able to define a longer string that way.
At this point I can picture you drooling at the thought of how you'll use custom directives to help generate HTML. But watch out! HTML tags (things in angle brackets) are protected from macro processing, remember? So you have to be clever to get the HTML you want.One reader wrote me describing a problem along these lines. His Web pages are tables. There are a few cells at the top and bottom of each table that are common to each page, so obviously that's template material. The start and end of the table HTML is in the template; just the middle of the table is in each individual page.
But there's a problem. Different pages have different numbers of columns. The stuff in the template needs to span all those columns, with a tag like this:
<td colspan=??? height=36>The question is: how will the template know what to put for ??? for any particular page?
The answer: a custom directive! In each page, he can say how many columns its table has, like this:
#numColumns "3"That information is handed up to the template, which can use it to generate the HTML, like this, right?
<td colspan={numColumns} height=36>Wrong! A macro call inside angle brackets won't be seen.
Fortunately there are lots of solutions. Here's one; just remove the protection:
\<td colspan={numColumns} height=36>Here's another; generate the whole tag:
{"<td colspan=" + numColumns + " height=36>"}Or, generate each piece of the tag separately:
{"<td colspan="}\{numColumns} \{"height=36>"}
This business of being able to get at the value of a directive by naming it in curly braces is very cool. But things can get rather complicated because of the way pages are embedded within one another: a page in a template, perhaps an Included page in that page, and so on.A directive high up in the hierarchy, say in a template, is visible all the way down the hierarchy, into a page rendered with that template, then into an object rendered inside that page with {renderObject}, and so on. That's because all directives are gathered before any macro calls are interpreted.
But a custom directive inside an Included page is visible outside its own page only in what physically comes after the {renderObject} call that Includes it!
Suppose, for example, you have no definition of #myTitle in mySecondPage, but you do have a definition for it in myThirdPage, which is Included in mySecondPage. And suppose, in your template, you have the phrase "{myTitle}" (without the quotes) somewhere before the <bodytext> call and also somewhere after the <bodytext> call.
What do you think will happen when you view mySecondPage in the browser? Try it! The instance of "{myTitle}" at the top has generated an error, but the instance of "{myTitle}" at the bottom has been successfully replaced by your value for #myTitle!
This happened because {renderObject} is a macro call, and so is {myTitle}. When the first {myTitle} was first evaluated, we hadn't come to {renderObject} yet, so no entry for "myTitle" had gone into suites.html.data.page, and this gave an error.
But when {renderObject} was encountered, Frontier started working on myThirdPage, and added its directives to suites.html.data.page -- where they remained, so that when the later instance of {myTitle} was reached, there was a value to replace it with.
You're probably thinking about now: "I bet this is another of those problems that can be solved by asking for a second round of macro processing." But no: when we do the second round, the first {myTitle} is gone -- it's been replaced by the error message!
But there is a solution...
Like any directive, custom directives can be defined as entries in your site table (not just within particular templates or pages), and the value at the lowest level will be the one that is actually used.One use for this is to provide a default value for a custom directive. If you make a macro call to evaluate a custom directive and no value has been provided for it, an error message appears in the rendered page, as we have seen. But a default value, located for instance in the site table, can prevent this from happening.
A directive's value can be an empty string (signified, when you type it, by ""); this will effectively cause any "macro" call to it to be deleted with no further effect. That's a useful trick right there.
But here's a better one: make the directive's value a macro call to that directive. For instance, the value of #myTitle would be "{myTitle}". Do you see what this does? Remember how the first {myTitle} call caused an error, because we hadn't Included thirdPage yet, so #myTitle had no value?
Now it does have a value, so that value is substituted: and that value is {myTitle}. But that's a macro call. And on the second round of macro processing, it is evaluated -- correctly!
If you figured this out before I told it to you, you are definitely reaching Frontier Nirvana!
All text is by Matt Neuburg, phd, matt@tidbits.com. This page created with Frontier, 7/8/97; 9:13:41 AM.