PageTemplate 2.1.3 → 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/Changes CHANGED
@@ -1,4 +1,4 @@
1
- 2.1.3:
1
+ CVS:
2
2
  Bugfixes:
3
3
  HTGlossary didn't survive the change from 2.0 to 2.1. I've
4
4
  added unit tests and fixed the HTGlossary bugs. Whoops.
data/Rakefile CHANGED
@@ -54,7 +54,7 @@ end
54
54
 
55
55
  spec = Gem::Specification.new do |s|
56
56
  s.name = "PageTemplate"
57
- s.version = "2.1.3"
57
+ s.version = "2.1.5"
58
58
  s.author = "Brian Wisti"
59
59
  s.email = "brianwisti@rubyforge.org"
60
60
  s.homepage = "http://coolnamehere.com/products/pagetemplate"
@@ -176,11 +176,20 @@ class PageTemplate
176
176
  class FileSource < Source
177
177
  attr_reader :paths
178
178
 
179
+ # initialize looks for the following in the args Hash:
180
+ # * include_paths = a list of file paths
181
+ # * include_path = a single file path string
182
+ # (kept for backwards compatibility)
179
183
  def initialize(args = {})
180
184
  @args = args
181
- @paths = @args['include_paths']
182
- @paths ||= [@args['include_path']].compact
183
- @paths = [Dir.getwd,'/'] if @paths.empty?
185
+ if @args['include_paths']
186
+ @paths = @args['include_paths']
187
+ elsif @args['include_path']
188
+ @paths = [ @args['include_path'] ]
189
+ else
190
+ @paths = [Dir.getwd,'/']
191
+ end
192
+ @paths = @paths.compact
184
193
  @cache = Hash.new(nil)
185
194
  @mtime = Hash.new(0)
186
195
  end
@@ -204,20 +213,16 @@ class PageTemplate
204
213
  return "[ Unable to open file #{fn} because of #{er.message} ]"
205
214
  end
206
215
  end
207
-
208
216
  def cache(name,cmds)
209
217
  fn = get_filename(name)
210
218
  @cache[fn] = cmds.dup
211
219
  @mtime[fn] = Time.now.to_i
212
220
  end
213
-
214
- # Return the absolute pathname of the first name +file+
215
- # found in the include_paths.
216
221
  def get_filename(file)
217
222
  file = file.gsub(/\.\.\//,'')
218
- file.untaint
219
223
  @paths.each do |path|
220
224
  fn = File.join(path,file)
225
+ fn.untaint
221
226
  return fn if File.exists?(fn)
222
227
  end
223
228
  return nil
@@ -4,7 +4,7 @@ Installing Programs with setup.rb
4
4
  Quick Start
5
5
  -----------
6
6
 
7
- Type this (You might needs super user privilege):
7
+ Type this (You might needs super user previledge):
8
8
 
9
9
  ($ su)
10
10
  # ruby setup.rb
@@ -0,0 +1,15 @@
1
+ all: site css
2
+ css:
3
+ cp base.css html/
4
+ site:
5
+ ruby Site.rb
6
+ images: site
7
+ mkdir html/images
8
+ cp extras/images/* html/images/
9
+ doc:
10
+ cp -R extras/doc/PageTemplate html/doc
11
+ clean:
12
+ rm -rf html
13
+
14
+ test-install:
15
+ sudo cp -Rf html/* /var/www/html
@@ -0,0 +1,43 @@
1
+ require 'ZenWeb/GenericRenderer'
2
+
3
+ =begin
4
+
5
+ = Class SubpageRenderer
6
+
7
+ Generates a list of known subpages in a format compatible with
8
+ TextToHtmlRenderer.
9
+
10
+ === Methods
11
+
12
+ =end
13
+
14
+ class MySubpageRenderer < GenericRenderer
15
+
16
+ =begin
17
+
18
+ --- SubpageRenderer#render(content)
19
+
20
+ Renders a list of known subpages in a format compatible with
21
+ TextToHtmlRenderer. Adds the list to the end of the content.
22
+
23
+ =end
24
+
25
+ def render(content)
26
+ subpages = @document.subpages.clone
27
+ if (subpages.length > 0) then
28
+ push("\n\n")
29
+ push("<h2>Subpages:</h2>\n\n")
30
+ subpages.each_index { | index |
31
+ url = subpages[index]
32
+ doc = @website[url]
33
+ title = doc.fulltitle
34
+
35
+ push("* <A HREF=\"#{url}\">#{title}</A>\n")
36
+ }
37
+ push("\n")
38
+ end
39
+
40
+ return content + self.result
41
+ end
42
+ end
43
+
@@ -0,0 +1,37 @@
1
+ require 'ZenWeb/GenericRenderer'
2
+
3
+ class PageNavRenderer < GenericRenderer
4
+ def render(content)
5
+ # I know there's a quicker way to do this, but I'm in a hurry.
6
+ this_url = @document.url
7
+ doc_order = @sitemap.doc_order
8
+ this_index = doc_order.index(this_url)
9
+ forward = backward = " "
10
+
11
+ last_url = doc_order[this_index - 1]
12
+ last_page = @sitemap.documents[last_url]
13
+ if last_page and File.dirname(last_url) == File.dirname(this_url)
14
+ title = last_page.title
15
+ url = File.basename(last_url)
16
+ backward = "<p>&lt;- <a href='#{url}'>#{title}</a></p>\n"
17
+ end
18
+
19
+ next_url = doc_order[this_index + 1]
20
+ next_page = @sitemap.documents[next_url]
21
+ if next_page and File.dirname(next_url) == File.dirname(this_url)
22
+ title = next_page.title
23
+ url = File.basename(next_url)
24
+ forward = "<p><a href='#{url}'>#{title}</a>- &gt;</p>\n"
25
+ end
26
+
27
+ push("<table width='100%' border='0'><tr>\n")
28
+ push("<td>#{backward}</td>\n")
29
+ push("<td align='right'>#{forward}</td>\n")
30
+ push("</tr></table>\n")
31
+
32
+ push(content)
33
+
34
+ return self.result
35
+ end
36
+ end
37
+
@@ -0,0 +1,20 @@
1
+ require 'ZenWeb/GenericRenderer'
2
+
3
+ # Hands content off to the RedCloth text formatter for rendering.
4
+ #
5
+ # + Set Attributes in the 'RedClothAttributes' metadata.
6
+ # + filter_html -- HTML not created by RedCloth is escaped
7
+ # + filter_styles -- style markup specifier is disabled
8
+ # + See http://www.whytheluckystiff.net/ruby/redcloth/
9
+ class RedClothRenderer < GenericRenderer
10
+ require 'redcloth'
11
+
12
+ def render(content)
13
+ attributes = []
14
+ if @document.metadata.has_key?('RedClothAttributes')
15
+ attributes = @document['RedClothAttributes']
16
+ end
17
+ rc = RedCloth::new(content, attributes)
18
+ return rc.to_html(:textile)
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'ZenWeb'
4
+
5
+ data_dir = "src"
6
+ html_dir = "html"
7
+ sitemap = "/SiteMap.html"
8
+ url = "/"
9
+
10
+ website = ZenWebsite.new(sitemap, data_dir, html_dir)
11
+ website.renderSite
@@ -0,0 +1,23 @@
1
+ require 'ZenWeb/GenericRenderer'
2
+
3
+ class SiteNewsRenderer < GenericRenderer
4
+ require 'yaml'
5
+
6
+ def render(content)
7
+ newsfile = 'news.yml'
8
+ newsitems = YAML.load(File.open(newsfile).read())
9
+ limit = @document['NewsLimit']
10
+
11
+ if newsitems
12
+ newsitems.each do |item|
13
+ push("\n<div class='newsitem'>\n\nh3. #{item['title']}\n\np(date). #{item['date']}\n\n#{item['content']}\n</div>\n")
14
+ if limit
15
+ limit -= 1
16
+ break if limit == 0
17
+ end
18
+ end
19
+ end
20
+
21
+ return self.result
22
+ end
23
+ end
@@ -0,0 +1,141 @@
1
+ require 'ZenWeb/HtmlRenderer'
2
+
3
+ =begin
4
+
5
+ = Class HtmlTemplateRenderer
6
+
7
+ Generates a consistant HTML page header and footer, including a
8
+ navigation bar, title, subtitle, and appropriate META tags.
9
+
10
+ === Methods
11
+
12
+ =end
13
+
14
+ class XhtmlTemplateRenderer < HtmlRenderer
15
+
16
+ =begin
17
+
18
+ --- HtmlTemplateRenderer#render(content)
19
+
20
+ Renders a standardized HTML header and footer. This currently also
21
+ includes a navigation bar and a list of subpages, which will
22
+ probably be broken out to their own renderers soon.
23
+
24
+ Metadata variables used:
25
+
26
+ + author
27
+ + banner - graphic at the top of the page, usually a logo
28
+ + bgcolor - defaults to not being defined
29
+ + copyright
30
+ + description
31
+ + dtd (default: 'DTD HTML 4.0 Transitional')
32
+ + email - used in a mailto in metadata
33
+ + keywords
34
+ + rating (default: 'general')
35
+ + stylesheet - reference to a CSS file
36
+ + style - CSS code directly (for smaller snippets)
37
+ + subtitle
38
+ + title (default: 'Unknown Title')
39
+ + icbm - longitude and latitude for geourl.org
40
+ + icbm_title - defaults to the page title
41
+
42
+ =end
43
+
44
+ def render(content)
45
+ author = @document['author']
46
+ banner = @document['banner']
47
+ bgcolor = @document['bgcolor']
48
+ dtd = @document['dtd'] || 'DTD HTML 4.0 Transitional'
49
+ copyright = @document['copyright']
50
+ description = @document['description']
51
+ email = @document['email']
52
+ keywords = @document['keywords']
53
+ rating = @document['rating'] || 'general'
54
+ stylesheet = @document['stylesheet']
55
+ subtitle = @document['subtitle']
56
+ title = @document['title'] || 'Unknown Title'
57
+ icbm = @document['icbm']
58
+ icbm_title = @document['icbm_title'] || @document['title']
59
+ charset = @document['charset']
60
+ style = @document['style']
61
+
62
+ titletext = @document.fulltitle
63
+
64
+ # TODO: iterate over a list of metas and add them in one nicely organized block
65
+
66
+ # header
67
+ push([
68
+ '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">',
69
+ "<head>\n",
70
+ "<title>#{titletext}</title>\n",
71
+ stylesheet ? "<link rel=\"STYLESHEET\" href=\"#{stylesheet}\" type=\"text/css\" title=\"#{stylesheet}\">\n" : [],
72
+ style ? "<style>\n#{style}\n</style>" : [],
73
+ "</head>\n",
74
+ "<body>\n"
75
+ ])
76
+
77
+ self.navbar
78
+
79
+ if banner then
80
+ push("<img src=\"#{banner}\" /><br />\n")
81
+ unless (subtitle) then
82
+ push("<h3>#{title}</h3>\n")
83
+ end
84
+ else
85
+ push("<h1>#{title}</h1>\n")
86
+ end
87
+
88
+ push([
89
+ subtitle ? "<h2>#{subtitle}</h2>\n" : [],
90
+ "<hr />\n\n",
91
+ content,
92
+ "<hr />\n\n",
93
+ ])
94
+
95
+ self.navbar
96
+
97
+ push("\n</body>\n</html>\n")
98
+
99
+ return self.result
100
+ end
101
+
102
+ =begin
103
+
104
+ --- HtmlTemplateRenderer#navbar
105
+
106
+ Generates a navbar that contains a link to the sitemap, search
107
+ page (if any), and a fake "breadcrumbs" trail which is really just
108
+ a list of all of the parent titles up the chain to the top.
109
+
110
+ =end
111
+
112
+ def navbar
113
+
114
+ sep = " / "
115
+ search = @website["/Search.html"]
116
+
117
+ push([
118
+ "<p class=\"navbar\">\n",
119
+ "<a href=\"#{@sitemap.url}\">Sitemap</a>",
120
+ search ? " | <a href=\"#{search.url}\"><em>Search</em></a>" : [],
121
+ " || ",
122
+ ])
123
+
124
+ path = []
125
+ current = @document
126
+ while current and current != current.parent do
127
+ current = current.parent
128
+ path.unshift(current) if current
129
+ end
130
+
131
+ push([
132
+ path.map{|doc| ["<a href=\"#{doc.url}\">#{doc['title']}</a>\n", sep]},
133
+ @document['title'],
134
+ "</p>\n",
135
+ ])
136
+
137
+ return []
138
+ end
139
+
140
+ end
141
+
@@ -0,0 +1,4 @@
1
+ body {
2
+ background-color: white;
3
+ color: black;
4
+ }
@@ -0,0 +1,2 @@
1
+ <hr />
2
+ <p>Copyright 2002 - 2005 Brian Wisti and Greg Millam</p>
@@ -0,0 +1,2 @@
1
+ <h1>PageTemplate</h1>
2
+ <hr />
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2
+ <HTML>
3
+ <HEAD>
4
+ <TITLE>SiteMap: There are 6 pages in this website.</TITLE>
5
+ <LINK REL="STYLESHEET" HREF="base.css" type="text/css" title="base.css">
6
+ <META NAME="rating" CONTENT="general">
7
+ <META NAME="GENERATOR" CONTENT="ZenWeb v. 2.17.0 http://www.zenspider.com/ZSS/Products/ZenWeb/">
8
+ <META NAME="keywords" CONTENT="sitemap, website">
9
+ <META NAME="description" CONTENT="This page links to every page in the website.">
10
+ <link rel="up" href="index.html" title="PageTemplate">
11
+ <link rel="contents" href="" title="SiteMap">
12
+ <link rel="top" href="index.html" title="PageTemplate">
13
+ </HEAD>
14
+ <BODY>
15
+ <h1>Yo!</h1>
16
+ <hr />
17
+ <P class="navbar">
18
+ <A HREF="">Sitemap</A> || <A HREF="index.html">PageTemplate</A>
19
+ / SiteMap</P>
20
+ <H1>SiteMap</H1>
21
+ <H2>There are 6 pages in this website.</H2>
22
+ <HR SIZE="3" NOSHADE>
23
+
24
+ <UL>
25
+ <LI><A HREF="index.html">PageTemplate</A></LI>
26
+ <LI><A HREF="install.html">Getting It</A></LI>
27
+ <LI><A HREF="designer.html">The Designer's Perspective</A></LI>
28
+ <LI><A HREF="programmer.html">The Programmer's Perspective</A></LI>
29
+ <LI><A HREF="version2.html">PageTemplate Version 2: What's New?</A></LI>
30
+ <LI><A HREF="">SiteMap: There are 6 pages in this website.</A></LI>
31
+ </UL>
32
+ <HR SIZE="3" NOSHADE>
33
+
34
+ <P class="navbar">
35
+ <A HREF="">Sitemap</A> || <A HREF="index.html">PageTemplate</A>
36
+ / SiteMap</P>
37
+
38
+ <h1>Yo!</h1>
39
+ <hr />
40
+ <h1>Yo!</h1>
41
+ <hr />
42
+ </BODY>
43
+ </HTML>
@@ -0,0 +1,4 @@
1
+ body {
2
+ background-color: white;
3
+ color: black;
4
+ }
@@ -0,0 +1,524 @@
1
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
2
+ <title>The Designer's Perspective</title>
3
+ <link rel="STYLESHEET" href="base.css" type="text/css" title="base.css">
4
+ </head>
5
+ <body>
6
+ <h1>Yo!</h1>
7
+ <hr />
8
+ <p class="navbar">
9
+ <a href="SiteMap.html">Sitemap</a> || <a href="index.html">PageTemplate</a>
10
+ / The Designer's Perspective</p>
11
+ <h1>The Designer's Perspective</h1>
12
+ <hr />
13
+
14
+ <table width='100%' border='0'><tr>
15
+ <td><p>&lt;- <a href='install.html'>Getting It</a></p>
16
+ </td>
17
+ <td align='right'><p><a href='programmer.html'>The Programmer&#8217;s Perspective</a>- &gt;</p>
18
+ </td>
19
+ </tr></table>
20
+
21
+ <h2>Who Are You?</h2>
22
+
23
+
24
+ <p>You are the esteemed Web Designer, aesthetically talented and
25
+ perhaps artistically inclined. You know what makes a good Web page.
26
+ You are not a programmer, though. It&#8217;s horrible when you have to go
27
+ down to the caves where they keep the developers to explain
28
+ where a simple login form belongs. You also don&#8217;t want to
29
+ remember where their odd-looking programming code is supposed to go
30
+ in your beautiful page. You want a simple, clean way of describing
31
+ the dynamic elements of site pages.</p>
32
+
33
+
34
+ <p>Okay, I&#8217;ve had too much coffee. This page explains how templating
35
+ works, and how to put PageTemplate to use when laying out the <span class="caps">HTML</span>
36
+ of your page.</p>
37
+
38
+
39
+ <h2>What&#8217;s a Template?</h2>
40
+
41
+
42
+ <p>When you are designing pages for a dynamic site or Web application,
43
+ there are a lot of details you won&#8217;t know in advance. Some examples
44
+ might include login information, the contents of a shopping cart,
45
+ or maybe even the contents of the page! Templates
46
+ allow you to put placeholders within your <span class="caps">HTML</span> to show where that login
47
+ information is displayed and how it is formatted. A good template
48
+ system does not require you to remember code while you&#8217;re designing:
49
+ you just make the page, and let programmers worry about filling it with
50
+ data.</p>
51
+
52
+
53
+ <p>In PageTemplate, those placeholders are referred to as <em>directives</em>.</p>
54
+
55
+
56
+ <h2>How Do I Use PageTemplate In My Pages?</h2>
57
+
58
+
59
+ <p>PageTemplate uses a simple language which you can embed
60
+ in your page. You should be able to use your favorite design tools to
61
+ create an attractive template. My favorite design tool happens to be
62
+ <a href="geekery/editors/emacs/index.html">GNU/Emacs</a>, but the odds are that
63
+ the designers out there lean towards something a little friendlier, like
64
+ <a href="http://www.dreamweaver.com/">Macromedia Dreamweaver</a>. With the default syntax,
65
+ all of us can be happy.</p>
66
+
67
+
68
+ <p>PageTemplate directives are indicated by being wrapped in between
69
+ <code>[%</code> and <code>%]</code> characters. If any of those characters are missing, PageTemplate
70
+ decides it is not a directive, and leaves it alone.</p>
71
+
72
+
73
+ <h3>Variables</h3>
74
+
75
+
76
+ <p>The major directives require <em>variables</em>, which are just
77
+ names for the value your want inserted, checked, or otherwise
78
+ accessed. It&#8217;s a good idea to use variable names that make
79
+ sense (<code>name</code> for a person&#8217;s name, <code>title</code> for the title of the page, etc.).</p>
80
+
81
+
82
+ <h3>Value Substitution</h3>
83
+
84
+
85
+ <p>Substitution is the easiest concept to master. When PageTemplate
86
+ comes across a value directive, it replaces that directive with some text.</p>
87
+
88
+
89
+ <h4>Syntax</h4>
90
+
91
+
92
+ <pre>
93
+ [%var variable %]
94
+ </pre>
95
+
96
+ <h4>Example</h4>
97
+
98
+
99
+ <pre>
100
+ &lt;h1&gt;Hello, [%var name %]&lt;/h1&gt;
101
+ </pre>
102
+
103
+ <p>Every time that PageTemplate sees <code>[%var name %]</code> in
104
+ your template, it will replace that directive with the text
105
+ associated with <code>name</code>.</p>
106
+
107
+
108
+ <p>The programmer works his magic, and the visitor &#8220;Frank&#8221; sees this
109
+ greeting:</p>
110
+
111
+
112
+ <pre>&lt;h1&gt;Hello, Frank&lt;/h1&gt;</pre>
113
+
114
+ <p>If <code>name</code> is not set, nothing is inserted. The greeting header would end up
115
+ looking like this:</p>
116
+
117
+
118
+ <pre>&lt;h1&gt;Hello, &lt;/h1&gt;</pre>
119
+
120
+ <h3>If</h3>
121
+
122
+
123
+ <p>The <code>if</code> directive tells PageTemplate to only display a chunk of content when
124
+ some condition is true. If the condition is not true, PageTemplate skips the
125
+ block and moves on.</p>
126
+
127
+
128
+ <h4>Syntax</h4>
129
+
130
+
131
+ <pre>
132
+ [%if condition %]
133
+ chunk
134
+ [%endif%]
135
+ </pre>
136
+
137
+ <h4>Example</h4>
138
+
139
+
140
+ <pre>
141
+ [%if pageowner%]
142
+ &lt;a href="admin.cgi"&gt;Admin&lt;/a&gt;
143
+ [%endif%]
144
+ </pre>
145
+
146
+ <p>In this example, if the application tells PageTemplate that
147
+ <code>pageowner</code> is true, PageTemplate inserts a link to
148
+ an administrative page. Otherwise, nothing happens here.</p>
149
+
150
+
151
+ <h3>If/Else</h3>
152
+
153
+
154
+ <p>The <code>else</code> directive adds extra power to <code>if</code>, by indicating a chunk of
155
+ content to use when a condition is not true.</p>
156
+
157
+
158
+ <h4>Syntax</h4>
159
+
160
+
161
+ <pre>
162
+ [%if value%]
163
+ chunk
164
+ [%else%]
165
+ alternate chunk
166
+ [%endif%]
167
+ </pre>
168
+
169
+ <h4>Example</h4>
170
+
171
+
172
+ <pre>
173
+ [%if login%]
174
+ &lt;p&gt;Welcome back, [%var login%]!&lt;/p&gt;
175
+ &lt;p&gt;&lt;a href="logout.cgi"&gt;Log Out&lt;/a&gt;&lt;/p&gt;
176
+ [%else%]
177
+ &lt;form name="login" method="post"&gt;
178
+ Login: &lt;input type="text" name="login" /&gt;&lt;br /&gt;
179
+ Password: &lt;input type="password" name="passwd" /&gt;&lt;br /&gt;
180
+ &lt;input type="submit" value="Login" /&gt;
181
+ [%endif%]
182
+ </pre>
183
+
184
+ <p>This is the situation where I use <code>else</code> directives the
185
+ most. If the visitor is logged in to a Web application, she
186
+ is shown a brief welcoming message. If not, then she will see a
187
+ login form.</p>
188
+
189
+
190
+ <p>This example also shows a convenient approach to <code>if</code>
191
+ conditions. We <em>could</em> make up a special <code>logged_in</code>
192
+ variable, but since all we care about here is the presence of a login,
193
+ we have PageTemplate test that as if it were a regular condition.</p>
194
+
195
+
196
+ <h3>Unless</h3>
197
+
198
+
199
+ <p>Sometimes you only want to display a chunk if something is false, but
200
+ nothing if the condition is true. That description twisted my brain
201
+ a little bit. Think of it this way. Sometimes it&#8217;s just easier to say
202
+ &#8220;unless something&#8221; instead of &#8220;if not_something&#8221;. That&#8217;s all. Templates
203
+ should be easy to read, you know? That&#8217;s what this directive is for:
204
+ making some of the logic in your template easier to read.</p>
205
+
206
+
207
+ <h4>Syntax</h4>
208
+
209
+
210
+ <pre>
211
+ [%unless condition %]
212
+ chunk
213
+ [%end unless%]
214
+ </pre>
215
+
216
+ <h4>Example</h4>
217
+
218
+
219
+ <pre>
220
+ [%unless launched%]
221
+ Product Launch Coming Soon!
222
+ [%end unless%]
223
+ </pre>
224
+
225
+ <h3>In</h3>
226
+
227
+
228
+ <p>Use the <code>in</code> directive when you want PageTempate to insert the same chunk
229
+ repeatedly for a list of items. It can grab values from the item to be
230
+ inserted in <code>value</code> directives within the chunk. If there is no list of items,
231
+ the <code>in</code> chunk is skipped.</p>
232
+
233
+
234
+ <p>The <code>in</code> directive is the most complex, and requires more explanation of its
235
+ details.</p>
236
+
237
+
238
+ <h4>Syntax</h4>
239
+
240
+
241
+ <pre>
242
+ [%in list%]
243
+ some text and [%var value%]
244
+ [%end in%]
245
+ </pre>
246
+
247
+ <h4>Example</h4>
248
+
249
+
250
+ <pre>
251
+ &lt;ul&gt;
252
+ [%in books%]
253
+ &lt;li&gt;"[%var title%]" by [%var author%]&lt;/li&gt;
254
+ [%end in%]
255
+ &lt;/ul&gt;
256
+ </pre>
257
+
258
+ <p>It presents a list of favorite albums, along with the band that produced it.
259
+ If there was no such list, then PageTemplate would not insert anything, and
260
+ we would be left with <code>&lt;ul&gt;&lt;/ul&gt;</code> all by itself.
261
+ That&#8217;s more than a little awkward from a designer&#8217;s point of view, but
262
+ the <code>no</code> directive presentd in a few moments helps us work around that.</p>
263
+
264
+
265
+ <h4>&#8220;Local&#8221; Values</h4>
266
+
267
+
268
+ <p>When stepping through a list, PageTemplate works a little magic with
269
+ <code>value</code> directives. First it examines the list item to see
270
+ if it has a value for the variable named. If it can&#8217;t find one there,
271
+ it checks its main variable listing and tries to insert that. If it
272
+ can&#8217;t find a value in the main variable listing, it inserts nothing for
273
+ that <code>value</code> directive.</p>
274
+
275
+
276
+ This logic works for nested lists, too. If you have an
277
+ <code>in</code> directive embedded in another <code>in</code> directive,
278
+ (say, a list of books written by each one of a list of your favorite
279
+ authors), PageTemplate first looks in the innermost list (the books) for
280
+ a name, then the next list out (the authors), and finally the main
281
+ variable listing.
282
+
283
+ <h4>Loop Metavariables</h4>
284
+
285
+
286
+ <p><em>Metavariables</em> were added in 1.2 so that you can fine-tune your lists, loops, and tables. They are special
287
+ flags that are set during certain times in a loop: the first time through, the last time through, during
288
+ odd trips through the loop, etcetera. Metavariables are uppercased and wrapped in double underscores to
289
+ emphasize that they are not normal variables.</p>
290
+
291
+
292
+ <p>Here are all of the metavariables that are defined in <code>in</code> blocks.</p>
293
+
294
+
295
+ <table>
296
+ <tr>
297
+ <th>variable </th>
298
+ <th>description </th>
299
+ </tr>
300
+ <tr>
301
+ <td> <code>__FIRST__</code> </td>
302
+ <td> true if this is the first time through the loop. </td>
303
+ </tr>
304
+ <tr>
305
+ <td> <code>__LAST__</code> </td>
306
+ <td> true if this is the last time through the loop. </td>
307
+ </tr>
308
+ <tr>
309
+ <td> <code>__ODD__</code> </td>
310
+ <td> true on odd-numbered steps through the loop (1st, 3rd, 5th) </td>
311
+ </tr>
312
+ </table>
313
+
314
+
315
+
316
+
317
+ <p>And to give a little idea of metavariables in action, here&#8217;s a little example:</p>
318
+
319
+
320
+ <pre>
321
+ &lt;table&gt;
322
+ &lt;tr&gt;&lt;th&gt;#&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;th&gt;Amount&lt;/th&gt;&lt;/tr&gt;
323
+ [%in transactions %]
324
+ [%if __ODD__ %]
325
+ &lt;tr class="odd"&gt;
326
+ [%else%]
327
+ &lt;tr&gt;
328
+ [%endif%]
329
+ &lt;td&gt;[%var num%]&lt;/td&gt;
330
+ &lt;td&gt;[%var description%]&lt;/td&gt;
331
+ &lt;td&gt;[%var amount%]
332
+ &lt;/tr&gt;
333
+ [%endin%]
334
+ &lt;/table&gt;
335
+ </pre>
336
+
337
+ <h3>In/No</h3>
338
+
339
+
340
+ <p>The <code>no</code> directive allows you to use an alternate chunk
341
+ for an <code>in</code> if there is no list available.</p>
342
+
343
+
344
+ <h4>Syntax</h4>
345
+
346
+
347
+ <pre>
348
+ [%in list%]
349
+ [%var value%]
350
+ [%no%]
351
+ Nothing in the list
352
+ [%endin%]
353
+ </pre>
354
+
355
+ <h4>Example</h4>
356
+
357
+
358
+ <pre>
359
+ &lt;ul&gt;
360
+ [%in books%]
361
+ &lt;li&gt;"[%var title%]" by [%var author%]&lt;/li&gt;
362
+ [%no%]
363
+ &lt;li&gt;I have no favorites today.&lt;/li&gt;
364
+ [%endin%]
365
+ &lt;/ul&gt;
366
+ </pre>
367
+
368
+ <p>This extends the earlier example by providing an alternate chunk
369
+ to use if there is no list named <code>books</code>. Now
370
+ the <acronym title="Hypertext Markup Language"><span class="caps">HTML</span></acronym>
371
+ formatting is a little more appropriate. Instead of displaying
372
+ an empty list, it shows a list item declaring that there is no list.</p>
373
+
374
+
375
+ <p>I think I hurt my head with that last sentence.</p>
376
+
377
+
378
+ <h4>Lists and <span class="caps">WYSIWYG </span>Editors</h4>
379
+
380
+
381
+ <p>Here&#8217;s a specific problem that might pop up when you are using
382
+ a <acronym title="What You See Is What You Get"><span class="caps">WYSIWYG</span></acronym>
383
+ editor. Let&#8217;s say you&#8217;re embedding a list into a table, so that each
384
+ item in the list gets one table row. Dreamweaver is probably not
385
+ going to like this style:</p>
386
+
387
+
388
+ <pre>
389
+ &lt;table&gt;
390
+ [%in listing%]
391
+ &lt;tr&gt;
392
+ &lt;td&gt;[%var item%]&lt;/td&gt;
393
+ &lt;/tr&gt;
394
+ [%no%]
395
+ &lt;tr&gt;
396
+ &lt;td&gt;Listing is empty&lt;/td&gt;
397
+ &lt;/tr&gt;
398
+ [%endin%]
399
+ &lt;/table&gt;
400
+ </pre>
401
+
402
+ <p>The problem is that the <code>in listing</code>, <code>no</code>,
403
+ and <code>endin</code> directives are in locations that make for
404
+ invalid <span class="caps">HTML</span>, and might not be allowed by your editor.</p>
405
+
406
+
407
+ <p>It turns out that the solution is simple, though maybe a little
408
+ awkward. Wrap the offending directives in <span class="caps">HTML</span> comments, like this
409
+ example shows:</p>
410
+
411
+
412
+ <pre>
413
+ &lt;table&gt;
414
+ &lt;!-- [%in listing%] --&gt;
415
+ &lt;tr&gt;
416
+ &lt;td&gt;[%var item%]&lt;/td&gt;
417
+ &lt;/tr&gt;
418
+ &lt;!-- [%no%] --&gt;
419
+ &lt;tr&gt;
420
+ &lt;td&gt;Listing is empty&lt;/td&gt;
421
+ &lt;/tr&gt;
422
+ &lt;!-- [%endin%] --&gt;
423
+ &lt;/table&gt;
424
+ </pre>
425
+
426
+ <p>Your fancy editor should be able to handle this, and PageTemplate
427
+ should be able to understand it just fine. The only side effect
428
+ is that you will have some empty comments in your final page.</p>
429
+
430
+
431
+ <p>The other solution is to use <a href="geekery/editors/vim/index.html">Vim</a>,
432
+ <a href="http://www.xemacs.org/">XEmacs</a>, or some other effective
433
+ non-<acronym title="What You See Is What You Get"><span class="caps">WYSIWYG</span></acronym>
434
+ editor. Personal preference, of course. I know that Dreamweaver and GoLive
435
+ cost good money, and you aren&#8217;t going to toss them aside just because I say
436
+ so. Hopefully this workaround will suit your needs.</p>
437
+
438
+
439
+ <h3>Include</h3>
440
+
441
+
442
+ <p>The <code>include</code> directive is very simple to use, but it gives you a whole lot of
443
+ extra power. It allows you to include content that comes from another
444
+ files, including templates. This can be useful when building a page out of
445
+ smaller component templates.</p>
446
+
447
+
448
+ <h4>Syntax</h4>
449
+
450
+
451
+ <pre>[%include filename %]</pre>
452
+
453
+ <p><code>filename</code> can point to a real file in a particular directory, or it could be a
454
+ placeholder variable name. If PageTemplate can&#8217;t find that file, it displays a simple message stating
455
+ that the file was not found. The idea for this is to make it easier for developers to debug problems during
456
+ testing, rather than for you to wonder why there&#8217;s a blank space where your menu text should be.</p>
457
+
458
+
459
+ <h4>Example</h4>
460
+
461
+
462
+ <pre>
463
+ [%include menu %]
464
+ </pre>
465
+
466
+ <p>Meanwhile, we have something like this in <em>menu.txt</em></p>
467
+
468
+
469
+ <pre>
470
+ &lt;table&gt;
471
+ [%in photos%]
472
+ &lt;tr&gt;
473
+ &lt;td&gt;[%var title%]&lt;/td&gt;
474
+ &lt;td&gt;[%var thumbnail %]&lt;/td&gt;
475
+ &lt;/tr&gt;
476
+ [%endin%]
477
+ &lt;/table&gt;
478
+ </pre>
479
+
480
+ <p>Do you see what I meant when I said &#8220;simple&#8221;? If not, then please let me know.
481
+ I&#8217;m still working on this part of the manual.</p>
482
+
483
+
484
+ <h3>Comments</h3>
485
+
486
+
487
+ <p>Every once in a while you want to make a comment about your template, but not have it show up the
488
+ <span class="caps">HTML</span> markup that goes to the visitor. Comments are perfect for that. They are eaten up and spit aside
489
+ by PageTemplate when reading your template file, and the visitor never sees them.</p>
490
+
491
+
492
+ <h4>Syntax</h4>
493
+
494
+
495
+ <pre>
496
+ [%-- comment --%]
497
+ </pre>
498
+
499
+ <h2>Customized Syntax</h2>
500
+
501
+
502
+ <p>One of PageTemplate&#8217;s features is the ability to come up with your
503
+ own directive syntax. If you feel that the default syntax is
504
+ less than ideal, discuss a new system with your developers. If
505
+ you are the lone designer/developer, talk to yourself for a bit. We
506
+ all need some quality time to ourselves occasionally. Working together,
507
+ you and the developers <em>(or you and your split personalities)</em>
508
+ can come up with a syntax that is much more comfortable.</p>
509
+
510
+
511
+ <h2>Conclusion</h2>
512
+
513
+
514
+ <p>Yes, we&#8217;ve already reached the end of this designer&#8217;s tutorial.
515
+ Now go make with the pretty pages!</p><hr />
516
+
517
+ <p class="navbar">
518
+ <a href="SiteMap.html">Sitemap</a> || <a href="index.html">PageTemplate</a>
519
+ / The Designer's Perspective</p>
520
+
521
+ <h1>Yo!</h1>
522
+ <hr />
523
+ </body>
524
+ </html>