mirror of
https://github.com/OlafvdSpek/ctemplate.git
synced 2025-09-28 19:05:49 +08:00
docs/ directory is now called doc/, to keep consistent with the p4 tree
This commit is contained in:
parent
4a61bf4e95
commit
1c6d2fd4cb
|
@ -1,119 +0,0 @@
|
||||||
/* This file is duplicated at http://www.corp.google.com/css/designstyle.css
|
|
||||||
* If you change this file, copy it to ~web/html/css/designstyle.css
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: black;
|
|
||||||
margin-right: 1in;
|
|
||||||
margin-left: 1in;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
color: #3366ff;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
@media print {
|
|
||||||
/* Darker version for printing */
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
color: #000080;
|
|
||||||
font-family: helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 18pt;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
margin-left: -0.5in;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin-left: -0.25in;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
margin-left: -0.125in;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
margin-left: -1in;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Definition lists: definition term bold */
|
|
||||||
dt {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
address {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
/* Use the <code> tag for bits of code and <var> for variables and objects. */
|
|
||||||
code,pre,samp,var {
|
|
||||||
color: #006000;
|
|
||||||
}
|
|
||||||
/* Use the <file> tag for file and directory paths and names. */
|
|
||||||
file {
|
|
||||||
color: #905050;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
/* Use the <kbd> tag for stuff the user should type. */
|
|
||||||
kbd {
|
|
||||||
color: #600000;
|
|
||||||
}
|
|
||||||
div.note p {
|
|
||||||
float: right;
|
|
||||||
width: 3in;
|
|
||||||
margin-right: 0%;
|
|
||||||
padding: 1px;
|
|
||||||
border: 2px solid #6060a0;
|
|
||||||
background-color: #fffff0;
|
|
||||||
}
|
|
||||||
|
|
||||||
UL.nobullets {
|
|
||||||
list-style-type: none;
|
|
||||||
list-style-image: none;
|
|
||||||
margin-left: -1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
body:after {
|
|
||||||
content: "Google Confidential";
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* pretty printing styles. See prettify.js */
|
|
||||||
.str { color: #080; }
|
|
||||||
.kwd { color: #008; }
|
|
||||||
.com { color: #800; }
|
|
||||||
.typ { color: #606; }
|
|
||||||
.lit { color: #066; }
|
|
||||||
.pun { color: #660; }
|
|
||||||
.pln { color: #000; }
|
|
||||||
.tag { color: #008; }
|
|
||||||
.atn { color: #606; }
|
|
||||||
.atv { color: #080; }
|
|
||||||
pre.prettyprint { padding: 2px; border: 1px solid #888; }
|
|
||||||
|
|
||||||
.embsrc { background: #eee; }
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
.str { color: #060; }
|
|
||||||
.kwd { color: #006; font-weight: bold; }
|
|
||||||
.com { color: #600; font-style: italic; }
|
|
||||||
.typ { color: #404; font-weight: bold; }
|
|
||||||
.lit { color: #044; }
|
|
||||||
.pun { color: #440; }
|
|
||||||
.pln { color: #000; }
|
|
||||||
.tag { color: #006; font-weight: bold; }
|
|
||||||
.atn { color: #404; }
|
|
||||||
.atv { color: #060; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table Column Headers */
|
|
||||||
.hdr {
|
|
||||||
color: #006;
|
|
||||||
font-weight: bold;
|
|
||||||
background-color: #dddddd; }
|
|
||||||
.hdr2 {
|
|
||||||
color: #006;
|
|
||||||
background-color: #eeeeee; }
|
|
|
@ -1,262 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Template Examples</title>
|
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link href="http://www.google.com/favicon.ico" type="image/x-icon"
|
|
||||||
rel="shortcut icon">
|
|
||||||
<link href="designstyle.css" type="text/css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
<!--
|
|
||||||
ol.bluelist li {
|
|
||||||
color: #3366ff;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
ol.bluelist li p {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
ul.blacklist li {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1>Template Examples</h1>
|
|
||||||
<small>(as of 27 February 2006)</small></center>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Search Results Page </h2>
|
|
||||||
|
|
||||||
<p>Here is an example template that could be used to format a Google
|
|
||||||
search results page:</p>
|
|
||||||
|
|
||||||
<pre class=example>
|
|
||||||
|
|
||||||
{{>HEADER}}
|
|
||||||
<body bgcolor=white>
|
|
||||||
|
|
||||||
{{>PAGE_HEADING}}{{!The following div must be on the same line}}<div>
|
|
||||||
|
|
||||||
{{!The ONE_RESULT section displays a single search item}}
|
|
||||||
{{#ONE_RESULT}}
|
|
||||||
{{! Note: there are two SUBITEM_SECTIONs. They both show or hide together}}
|
|
||||||
{{#SUBITEM_SECTION}}<blockquote>{{/SUBITEM_SECTION}}
|
|
||||||
<p><a href={{JUMP_TO_URL:html_escape}} target=nw>{{LEAD_LINE}}</a><font size=-1>
|
|
||||||
|
|
||||||
{{#SNIPPET1_SECTION}}
|
|
||||||
<br>{{SNIPPET1}}
|
|
||||||
{{/SNIPPET1_SECTION}}
|
|
||||||
|
|
||||||
{{#SNIPPET2_SECTION}}
|
|
||||||
<br>{{SNIPPET2}}
|
|
||||||
{{/SNIPPET2_SECTION}}
|
|
||||||
|
|
||||||
{{#DESCRIPTION_SECTION}}
|
|
||||||
<br><span class=f>Description:</span> {{DESC}}
|
|
||||||
{{/DESCRIPTION_SECTION}}
|
|
||||||
|
|
||||||
{{#CATEGORY_SECTION}}
|
|
||||||
<br><span class=f>Category:</span> <a href={{CAT_URL:html_escape}} class=f>
|
|
||||||
{{CATEGORY}}</a>
|
|
||||||
{{/CATEGORY_SECTION}}
|
|
||||||
|
|
||||||
{{#LASTLINE_SECTION}}
|
|
||||||
<br><font color={{ALT_TEXT_COLOR}}>{{URL}}
|
|
||||||
{{#KS_SECTION}}} - {{KSIZE}}{{/KS_SECTION}}}
|
|
||||||
{{#CACHE_SECTION}}} - <a href={{CACHE_URL:html_escape}} class=f>Cached</A>
|
|
||||||
{{/CACHE_SECTION}}}
|
|
||||||
{{#SIM_SECTION}}} - <a href={{SIM_PAGES_URL:html_escape}} class=f>Similar pages</A>
|
|
||||||
{{/SIM_SECTION}}}
|
|
||||||
|
|
||||||
{{#STOCK_SECTION}}
|
|
||||||
- <a href={{STOCK_URL:html_escape}} class=f>Stock quotes: {{STOCK_SYMBOL}}</a>
|
|
||||||
{{/STOCK_SECTION}}
|
|
||||||
</font>
|
|
||||||
{{/LASTLINE_SECTION}}
|
|
||||||
|
|
||||||
{{#MORE_SECTION}}
|
|
||||||
<br>[ <a href={{MORE_URL:html_escape}} class=f>More results from {{MORE_LABEL}}</a> ]
|
|
||||||
{{/MORE_SECTION}}
|
|
||||||
|
|
||||||
</font><br>
|
|
||||||
{{! Note: there are two SUBITEM_SECTIONs. They both show or hide together}}
|
|
||||||
{{#SUBITEM_SECTION}}</blockquote>{{/SUBITEM_SECTION}}
|
|
||||||
{{/ONE_RESULT}}
|
|
||||||
</div> {{! this /div closes the div at the top of this file}}
|
|
||||||
{{>PAGE_FOOTING}}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p> Here is a sample procedure that could populate a dictionary for
|
|
||||||
expanding that template. The "one procedure" entry point is
|
|
||||||
<code>fill_search_results_dictionary</code>. The
|
|
||||||
<code>SetTemplateValues</code> function is a separate entry point for
|
|
||||||
initializing each top-level template with some standard values.</p>
|
|
||||||
|
|
||||||
<pre class=example>
|
|
||||||
#include "template.h"
|
|
||||||
|
|
||||||
RegisterTemplateFilename(SEARCH_RESULTS_FN, "search_results.tpl");
|
|
||||||
#include "search_results.tpl.varnames.h" // defines ksr_HEADER, etc.
|
|
||||||
|
|
||||||
using google::Template;
|
|
||||||
using google::TemplateDictionary;
|
|
||||||
using google::STRIP_WHITESPACE;
|
|
||||||
|
|
||||||
// IsEmpty
|
|
||||||
// A simple utility function
|
|
||||||
static bool IsEmpty(const string &str) {
|
|
||||||
return str.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTemplateValues
|
|
||||||
// Use the TemplateDictionary object to set template-wide values that
|
|
||||||
// may be used in the top-level template and all its sub-sections
|
|
||||||
// and included templates. The template-wide values are all
|
|
||||||
// colors from the Palette object
|
|
||||||
void SetTemplateValues(TemplateDictionary *dictionary, const Palette* colors) {
|
|
||||||
// better would be to use ksr_LINK_COLOR, etc, assuming those are
|
|
||||||
// defined in search_results.tpl.varnames.h. But using literal
|
|
||||||
// text, as here, is legal as well.
|
|
||||||
dictionary->SetValue("LINK_COLOR", colors->link_color);
|
|
||||||
dictionary->SetValue("BAR_TEXT_COLOR", colors->bar_text_color);
|
|
||||||
dictionary->SetValue("TEXT_COLOR", colors->text_color);
|
|
||||||
dictionary->SetValue("FAINT_COLOR", colors->faint_color);
|
|
||||||
dictionary->SetValue("IMPORTANT_COLOR", colors->important_color);
|
|
||||||
dictionary->SetValue("BAR_COLOR", colors->bar_color);
|
|
||||||
dictionary->SetValue("ALT_TEXT_COLOR", colors->alt_text_color);
|
|
||||||
dictionary->SetValue("ALINK_COLOR", colors->alink_color);
|
|
||||||
dictionary->SetValue("VLINK_COLOR", colors->vlink_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill_search_results_dictionary
|
|
||||||
// Iterates through all the QueryResults contained in the Query object.
|
|
||||||
// For each one, it sets corresponding template dictionary values
|
|
||||||
// (or hides sections containing their variables, if appropriate) in
|
|
||||||
// a sub-dictionary and then adds that dictionary to the parent
|
|
||||||
void fill_search_results_dictionary(TemplateDictionary *dictionary,
|
|
||||||
const Query *query) {
|
|
||||||
dictionary->SetFilename(SEARCH_RESULTS_FN);
|
|
||||||
|
|
||||||
// These two functions are defined elsewhere
|
|
||||||
fill_header_dictionary(dictionary->AddIncludeDictionary(ksr_HEADER));
|
|
||||||
fill_page_heading_dictionary(dictionary->AddIncludeDictionary(ksr_PAGE_HEADING),
|
|
||||||
query);
|
|
||||||
|
|
||||||
ResultsList *results = query->GetResults();
|
|
||||||
int resCount = 0;
|
|
||||||
|
|
||||||
for (ResultsList::const_iterator iter = results->begin();
|
|
||||||
iter != results->end();
|
|
||||||
++iter) {
|
|
||||||
QueryResult *qr = (*iter);
|
|
||||||
|
|
||||||
// Create a new sub-dictionary named "Result Dict <n>" for this entry
|
|
||||||
|
|
||||||
++resCount;
|
|
||||||
|
|
||||||
TemplateDictionary *result_dictionary =
|
|
||||||
dictionary->AddSectionDictionary(ksr_ONE_RESULT);
|
|
||||||
|
|
||||||
result_dictionary->SetValue(ksr_JUMP_TO_URL, qr->GetUrl());
|
|
||||||
|
|
||||||
if (qr->IsSubItem()) {
|
|
||||||
result_dictionary->ShowSection(ksr_SUBITEM_SECTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
result_dictionary->SetValue(ksr_LEAD_LINE, qr->GetLeadLine());
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_SNIPPET1, qr->GetSnippet1(),
|
|
||||||
ksr_SNIPPET1_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_SNIPPET2, qr->GetSnippet2(),
|
|
||||||
ksr_SNIPPET2_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_DESC, qr->GetDescription(),
|
|
||||||
ksr_DESCRIPTION_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_CAT_URL, qr->GetCategoryUrl(),
|
|
||||||
ksr_CATEGORY_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection("CATEGORY", qr->GetCategoryName(),
|
|
||||||
"CATEGORY_SECTION");
|
|
||||||
|
|
||||||
|
|
||||||
if (IsEmpty(qr->GetDisplayUrl()) &&
|
|
||||||
IsEmpty(qr->GetPageSize()) &&
|
|
||||||
IsEmpty(qr->GetCachedUrl()) &&
|
|
||||||
IsEmpty(qr->GetSimilarPagesUrl()) &&
|
|
||||||
(IsEmpty(qr->GetStockUrl()) ||
|
|
||||||
IsEmpty(qr->GetStockSymbol())) ) {
|
|
||||||
// there is nothing on the last line, so hide it altogether
|
|
||||||
} else {
|
|
||||||
result_dictionary->ShowSection("LASTLINE_SECTION");
|
|
||||||
|
|
||||||
result_dictionary->SetValue(ksr_URL, qr->GetDisplayUrl());
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_KSIZE, qr->GetPageSize(),
|
|
||||||
ksr_KS_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_CACHE_URL, qr->GetCachedUrl(),
|
|
||||||
ksr_CACHE_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_SIM_PAGES_URL,
|
|
||||||
qr->GetSimilarPagesUrl(),
|
|
||||||
ksr_SIM_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_STOCK_URL, qr->GetStockUrl(),
|
|
||||||
ksr_STOCK_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_STOCK_SYMBOL,
|
|
||||||
qr->GetStockSymbol(),
|
|
||||||
ksr_STOCK_SECTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_MORE_URL, qr->GetMoreUrl(),
|
|
||||||
ksr_MORE_SECTION);
|
|
||||||
|
|
||||||
result_dictionary->SetValueAndShowSection(ksr_MORE_LABEL, qr->GetMoreLabel(),
|
|
||||||
ksr_MORE_SECTION);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fill_page_footing_dictionary(dictionary->AddIncludeDictionary(ksr_PAGE_FOOTING),
|
|
||||||
query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_page(const Query* query) {
|
|
||||||
Template* tpl = Template::GetTemplate(SEARCH_RESULTS_FN, STRIP_WHITESPACE);
|
|
||||||
TemplateDictionary dict("search-results dict");
|
|
||||||
string output;
|
|
||||||
fill_search_results_dictionary(&dict, query);
|
|
||||||
tpl->Expand(&output, &dict);
|
|
||||||
// output now holds the expanded template
|
|
||||||
}
|
|
||||||
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<ul>
|
|
||||||
<li> <A HREF="howto.html">Howto</A> </li>
|
|
||||||
<li> <A HREF="tips.html">Tips</A> </li>
|
|
||||||
<!--
|
|
||||||
<li> <A HREF="example.html">Example</A> </li>
|
|
||||||
-->
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<address>
|
|
||||||
Craig Silverstein<br>
|
|
||||||
27 February 2006
|
|
||||||
</address>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
946
docs/howto.html
946
docs/howto.html
|
@ -1,946 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>How To Use the Google Template System</title>
|
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link href="http://www.google.com/favicon.ico" type="image/x-icon"
|
|
||||||
rel="shortcut icon">
|
|
||||||
<link href="designstyle.css" type="text/css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
<!--
|
|
||||||
ol.bluelist li {
|
|
||||||
color: #3366ff;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
ol.bluelist li p {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
ul.blacklist li {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1>How To Use the Google Template System</h1>
|
|
||||||
<small>(as of 27 February 2006)</small></center>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Motivation </h2>
|
|
||||||
|
|
||||||
<p>A template system can be used to separate output formatting
|
|
||||||
specifications, which govern the appearance and location of output
|
|
||||||
text and data elements, from the executable logic which prepares the
|
|
||||||
data and makes decisions about what appears in the output.</p>
|
|
||||||
|
|
||||||
<p>Template systems lie along a continuum of power versus separation.
|
|
||||||
"Powerful" constructs like variable assignment or conditional
|
|
||||||
statements make it easy to modify the look of an application within
|
|
||||||
the template system exclusively, without having to modify any of the
|
|
||||||
underlying "application logic". They do so, however, at the cost of
|
|
||||||
separation, turning the templates themselves into part of the
|
|
||||||
application logic.</p>
|
|
||||||
|
|
||||||
<p>This template system leans strongly towards preserving the
|
|
||||||
separation of logic and presentation. It is intentionally constrained
|
|
||||||
in the features it supports and, as a result, applications tend to
|
|
||||||
require quite a bit of code to instantiate a template. This may not
|
|
||||||
be to everybody's tastes. However, while this design limits the power
|
|
||||||
of the template <i>language</i>, it does not limit the power or
|
|
||||||
flexibility of the template <i>system</i>. This system supports
|
|
||||||
arbitrarily complex text formatting. Many Google applications,
|
|
||||||
including the "main" Google web search, use this system exclusively
|
|
||||||
for formatting output.</p>
|
|
||||||
|
|
||||||
<p>Finally, this system is designed with an eye towards efficiency.
|
|
||||||
Template instantiation is very quick, with an eye towards minimizing
|
|
||||||
both memory use and memory fragmentation.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Overview </h2>
|
|
||||||
|
|
||||||
<p>There are two parts to the Google Template System:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> Templates
|
|
||||||
<li> Data dictionaries
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>The templates are text files that contain the format specification
|
|
||||||
for the formatted output, i.e, the template language. The data
|
|
||||||
dictionaries contain the mappings from the template elements (markers)
|
|
||||||
embedded in the templates to the data that they will format. Here's
|
|
||||||
a simple template:</p>
|
|
||||||
<pre>
|
|
||||||
<html><head><title>{{TITLE}}</title>{{META_TAGS}}</head>
|
|
||||||
<body>{{BODY}}</body></html>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>Here's a dictionary that one could use to instantiate the template:</p>
|
|
||||||
<pre>
|
|
||||||
{"TITLE": "Template example",
|
|
||||||
"BODY": "This is a simple template example.\nIt's boring",
|
|
||||||
"DATE": "11/20/2005"}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>If we instantiated the template with this dictionary, here's the
|
|
||||||
output we would get:</p>
|
|
||||||
<pre>
|
|
||||||
<html><head><title>Template example</title></head>
|
|
||||||
<body>This is a simple template example.
|
|
||||||
It's boring</body></html>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p><code>{{TITLE}}</code> and <code>{{{BODY}}</code> are <b>template
|
|
||||||
elements</b>, also called <b>markers</b>. In the dictionary,
|
|
||||||
<code>TITLE</code>, <code>BODY</code>, and <code>DATE</code> are
|
|
||||||
<b>dictionary names</b>, and the values associated with each one, such
|
|
||||||
as <code>11/20/2005</code>, are <b>dictionary values</b>.</p>
|
|
||||||
|
|
||||||
<p>A few points are clear even from this simple example:</p>
|
|
||||||
<ol>
|
|
||||||
<li> Dictionary keys and values are strings; the Google template
|
|
||||||
system is not typed. </li>
|
|
||||||
<li> Dictionary values come already formatted. It was up to the
|
|
||||||
application code to decide how to format the value for
|
|
||||||
<code>DATE</code>, and to insert the date into the dictionary
|
|
||||||
already formatted. </li>
|
|
||||||
<li> Not all dictionary values must be used by a templete.
|
|
||||||
<code>DATE</code> is entirely ignored. </li>
|
|
||||||
<li> Not all template elements may exist in the dictionary. In this
|
|
||||||
example, <code>{{META_TAGS}}</code> is not found in the
|
|
||||||
dictionary. This is perfectly legal; missing variable markers
|
|
||||||
evaluate to the empty string. </li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Templates </h3>
|
|
||||||
|
|
||||||
<p> The template language has four types of markers:</p>
|
|
||||||
<ol>
|
|
||||||
<li> VARIABLE markers, which are replaced by text based on
|
|
||||||
dictionary values. All markers in the above example are
|
|
||||||
variable markers. Variable markers look like this:
|
|
||||||
<code>{{VARIABLE}}</code></li>
|
|
||||||
|
|
||||||
<li> START SECTION and END SECTION markers, which delimit
|
|
||||||
sections which may appear zero, one, or N times in
|
|
||||||
the output. The number of times a section appears is
|
|
||||||
determined by the data dictionaries, as explained below.
|
|
||||||
Each time a section is expanded, it uses a different
|
|
||||||
dictionary, so that the output values may be different from one
|
|
||||||
iteration of a section expansion to another. Note that the
|
|
||||||
specification of how sections expand is entirely dependent on
|
|
||||||
the dictionary, as set up by the application; there is no way
|
|
||||||
to specify a repeat rate in the template language itself.
|
|
||||||
Section markers look like this:
|
|
||||||
<code>{{#SECTION_NAME}}...{{/SECTION_NAME}}</code></li>
|
|
||||||
|
|
||||||
<li> TEMPLATE-INCLUDE markers, which designate other templates to be
|
|
||||||
expanded and inserted at the location where the marker appears.
|
|
||||||
These are treated much like sections -- one may think of them
|
|
||||||
as sections whose content is specified in a
|
|
||||||
different file instead of inline -- and just like sections, can
|
|
||||||
be expanded zero, one or N times in the output, each with a
|
|
||||||
different dictionary. Template-include markers look like this:
|
|
||||||
<code>{{>FILENAME}}</code></li>
|
|
||||||
|
|
||||||
<li> COMMENT markers, which may annotate the template
|
|
||||||
structure but drop completely out of the expanded
|
|
||||||
output. Comment markers look like this:
|
|
||||||
<code>{{! comment lives here -- cool, no?}}</code></li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<p>These marker types each have their own namespace. For readability,
|
|
||||||
however, it is best to not overuse a single name.</p>
|
|
||||||
|
|
||||||
<p>Anything found in a template of the form <code>{{...}}</code> is
|
|
||||||
interpreted as a template marker. All other text is considered
|
|
||||||
formatting text and is output verbatim at template expansion time.
|
|
||||||
Formatting text may consist of HTML tags, XML tags, linefeeds and
|
|
||||||
other spacing characters, constant text, etc.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Data Dictionaries </h3>
|
|
||||||
|
|
||||||
<p>A data dictionary is a map from keys to values. The keys are
|
|
||||||
always strings, each string representing either a variable, a section,
|
|
||||||
or a template-include file. (Comments are not stored in the data
|
|
||||||
dictionary!) These values correspond to the name of the associated
|
|
||||||
template marker: a section <code>{{#FOO}}</code> in the template text
|
|
||||||
is matched to the key <code>"FOO"</code> in the dictionary, if it
|
|
||||||
exists. Note the case must match as well.</p>
|
|
||||||
|
|
||||||
<p>The value associated with a key differs according to key type. The
|
|
||||||
value associated with a <i>variable</i> is simple: it's the value for
|
|
||||||
that variable. (Niggly implementation note: this value may not have
|
|
||||||
internal NUL -- that is, \0 -- characters. It's a TODO to allow this
|
|
||||||
one day.)</p>
|
|
||||||
|
|
||||||
<p>The value associated with a <i>section</i> is more complicated, and
|
|
||||||
somewhat recursive: it's a list of data dictionaries. Come
|
|
||||||
template-expansion time, the section is expanded once for each
|
|
||||||
dictionary in the list, so if there are two dictionaries in the list,
|
|
||||||
then the section text will occur in the output twice. The first time,
|
|
||||||
all variables/etc. in the section will be evaluated taking into
|
|
||||||
account the first dictionary. The second time, all
|
|
||||||
variables/etc. will be evaluated taking into account the second
|
|
||||||
dictionary. (See <A HREF="#inheritence">below</A> for a definition of
|
|
||||||
"taking into account.")</p>
|
|
||||||
|
|
||||||
<p>A <i>template-include</i> is a special type of section, so the
|
|
||||||
associated value is the same: a list of dictionaries.
|
|
||||||
Template-includes also have one other, mandatory associated piece of
|
|
||||||
information: the filename of the template to include. This filename
|
|
||||||
may be specified either as an absolute path, or as a relative path.
|
|
||||||
(In the latter case, the path is taken relative to the
|
|
||||||
<i>template_root</i>, as <A HREF="#managing">set by the
|
|
||||||
application</A>.)</p>
|
|
||||||
|
|
||||||
<p>The application program is responsible for building this data
|
|
||||||
dictionary, including all nesting. It then applies this dictionary to
|
|
||||||
a single template to produce formatted output.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3>Expanding a Template</h3>
|
|
||||||
|
|
||||||
<p>A program using Google Templates typically reads in templates at
|
|
||||||
load time. During the course of program execution, the program will
|
|
||||||
repeatedly perform the following two steps: first, instantiate a data
|
|
||||||
dictionary, and second, apply the dictionary to the template to
|
|
||||||
produce output.</p>
|
|
||||||
|
|
||||||
<p>The template system applies a dictionary to a template by finding
|
|
||||||
all template markers in the template, and replacing them with the
|
|
||||||
appropriate dictionary values. It matches template markers to
|
|
||||||
dictionary keys in the obvious way. For instance, a template marker
|
|
||||||
<code>{{FOO}}</code> matches the dictionary key <code>FOO</code>.
|
|
||||||
<code>{{FOO:html_escape}}</code> matches <code>FOO</code> as well. The
|
|
||||||
marker <code>{{#BAR}}</code> matches the dictionary key
|
|
||||||
<code>BAR</code>, as does the marker <code>{{/BAR}}</code>. The
|
|
||||||
marker <code>{{>BAZ}}</code> matches the dictionary key
|
|
||||||
<code>BAZ</code>. (And of course, the marker <code>{{!
|
|
||||||
comment}}</code> doesn't match any dictionary key at all.)</p>
|
|
||||||
|
|
||||||
<p>Template-variables can also have <A
|
|
||||||
HREF="#modifiers">modifiers</A>. In that case, the template-system
|
|
||||||
starts by finding the appropriate value for that variable in the
|
|
||||||
dictionary, just like normal. Then it applies each modifier to the
|
|
||||||
variable, left to right. Finally, it emits the modified value to the
|
|
||||||
output.</p>
|
|
||||||
|
|
||||||
<p>If no dictionary key is found for a given template marker, then the
|
|
||||||
template marker is ignored: if a variable, it expands to the empty
|
|
||||||
string; if a section or include-template, the section or
|
|
||||||
include-template is expanded zero times.</p>
|
|
||||||
|
|
||||||
<p>All names are case sensitive. Names -- that is, variable keys and,
|
|
||||||
as a result, template markers -- must be made of (7-bit ascii)
|
|
||||||
alphanumeric characters and the underscore. The commment marker,
|
|
||||||
which does not map to dictionary keys, may contain any chararacters
|
|
||||||
whatsoever except <code>}</code>, the close-curly brace. It's a
|
|
||||||
syntax error for any template marker to violate this rule.</p>
|
|
||||||
|
|
||||||
<p>Outside of the template markers, templates may contain any text
|
|
||||||
whatsoever, including (single) curly braces and NUL characters.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3><A NAME="modifiers">Variable Modifiers</A></h3>
|
|
||||||
|
|
||||||
<p>Recall that variables look like this: <code>{{VARNAME}}</code>. We
|
|
||||||
actually allow a more generic form: the variable name may be followed
|
|
||||||
by one or more <b>modifiers</b>. A modifier is a filter that's
|
|
||||||
applied at template-expand time, that munges the value of the variable
|
|
||||||
before it's output. For instance, consider a template that looks like
|
|
||||||
this:</p>
|
|
||||||
<pre>
|
|
||||||
<html><body>{{NAME:html_escape}}</body></html>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>This asks the template system to apply the built-in
|
|
||||||
<code>html_escape</code> modifier when expanding
|
|
||||||
<code>{{NAME}}</code>. If you set <code>NAME</code> in your
|
|
||||||
dictionary to be <code>Jim & Bob</code>, what will actually be
|
|
||||||
emitted in the template is <code>Jim &amp; Bob</code>.</p>
|
|
||||||
|
|
||||||
<p>You can chain modifiers together. This template first html-escapes
|
|
||||||
<code>NAME</code>, and then javascript-escapes that result:</p>
|
|
||||||
<pre>
|
|
||||||
<html><body>{{NAME:html_escape:javascript_escape}}</body></html>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>Modifiers typically have a long, descriptive name and also a
|
|
||||||
one-letter abbreviation. So this example is equivalent to the
|
|
||||||
previous one:</p>
|
|
||||||
<pre>
|
|
||||||
<html><body>{{NAME:h:j}}</body></html>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>Only the modifiers built in to the template system may be used:
|
|
||||||
It is not possible at the time for users to register modifiers. Here
|
|
||||||
are the modifiers that are supported:</p>
|
|
||||||
<table border=1 cellpadding=3>
|
|
||||||
<tr><th>long name</th><th>short name</th><th>description</th></tr>
|
|
||||||
|
|
||||||
<tr><td><code>:html_escape<code></td><td><code>:h</code></td>
|
|
||||||
<td>html-escapes the variable before output
|
|
||||||
(eg <code>&</code> -> <code>&amp</code>)</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr><td><code>:javascript_escape<code></td><td><code>:j</code></td>
|
|
||||||
<td>javascript-escapes the variable before output
|
|
||||||
(eg <code>"</code> -> <code>\"</code>)</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> <A NAME="inheritence">Details on Dictionary Lookup</A> </h3>
|
|
||||||
|
|
||||||
<p>The dictionary structure is a tree: there's a 'main' dictionary,
|
|
||||||
and then sub-dictionaries for each section or include-template. Even
|
|
||||||
with all this complexity, the lookup rules are mostly straightforward:
|
|
||||||
when looking up a marker -- be it a variable, section, or
|
|
||||||
include-template marker -- the system looks in the currently
|
|
||||||
applicable dictionary. If it's found there, great. If not, and the
|
|
||||||
parent dictionary is not an include-template, it continues the look in
|
|
||||||
the parent dictionary, and possibly the grandparent, etc. That is,
|
|
||||||
lookup has <i>static scoping</i>: you look in your dictionary and any
|
|
||||||
parent dictionary that is associated with the same template-file. As
|
|
||||||
soon as continuing the lookup would require you to jump to a new
|
|
||||||
template-file (which is what include-template would do), we stop the
|
|
||||||
lookup.</p>
|
|
||||||
|
|
||||||
<p>For instance, for a template that says
|
|
||||||
<code>{{#RESULTS}}{{RESULTNUM}}. {{>ONE_RESULT}}{{#RESULTS}}</code>,
|
|
||||||
<code>"ONE_RESULT"</code> is looked for in the "RESULTS" dictionary,
|
|
||||||
and if not found there, is looked for in the main, top-level
|
|
||||||
dictionary. Likewise, the variable <code>"RESULTNUM"</code> is looked
|
|
||||||
for first in the "RESULTS" dictionary, then in the main dictionary if
|
|
||||||
necessary. However, "ONE_RESULT" will not do equivalent cascading
|
|
||||||
lookups. In fact, it will have no parent dictionaries at all, because
|
|
||||||
it's a different template file and thus in a different scope.</p>
|
|
||||||
|
|
||||||
<p>Because of these scoping rules, it's perfectly reasonable to set
|
|
||||||
all variables that are needed in a given template file, in the
|
|
||||||
top-level dictionary for that template. In fact, the <code><A
|
|
||||||
HREF="#sections">ShowSection()</A></code> function is provided to
|
|
||||||
support just this idiom. To avoid confusion in such a usage mode,
|
|
||||||
it's strongly encouraged that you give unique names to all sections
|
|
||||||
and include-templates in a single template file. (It's no problem,
|
|
||||||
given the template scoping rules, for a single section or
|
|
||||||
include-template name to be repeated across different template
|
|
||||||
files.)</p>
|
|
||||||
|
|
||||||
<p>There's a single special case: the <b>global variable
|
|
||||||
dictionary</b>. Every dictionary inherits its initial set of values
|
|
||||||
from the global dictionary. Clients can <A HREF="#variables">set
|
|
||||||
variables in the global dictionary</A> just like they can in normal
|
|
||||||
template dictionaries they create.</p>
|
|
||||||
|
|
||||||
<p>The system initializes the global dictionary with a few useful
|
|
||||||
values for your convenience. All system variables are prefixed with
|
|
||||||
<code>BI</code>, to emphasize they are "built in" variables.</p>
|
|
||||||
<ul>
|
|
||||||
<li> <code>BI_SPACE</code>, which has the value
|
|
||||||
<code><space></code>. It is used to force a space
|
|
||||||
at the beginning or end of a line in the template,
|
|
||||||
where it would normally be suppressed. (See below.) </li>
|
|
||||||
|
|
||||||
<li><code>BI_NEWLINE</code>, which has the value
|
|
||||||
<code><newline></code> It is used to force a
|
|
||||||
newline at the end of a line, where it would normally
|
|
||||||
be suppressed. (See below.) </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>As is usual for inheritence, if a user explicitly assigns a value
|
|
||||||
to these variable-names in its own dictionary, this overrides the
|
|
||||||
inherited value. So, <code>dict->SetValue("BI_SPACE",
|
|
||||||
"&nbsp;")</code> causes <code>BI_SPACE</code> to have the value
|
|
||||||
<code>&nbsp;</code>, rather than <code><space></code>, when
|
|
||||||
expanding <code>dict</code>.</p>
|
|
||||||
|
|
||||||
<p>Note that only variables can be inherited from the global
|
|
||||||
dictionary, not section dictionaries or include-file dictionaries.</p>
|
|
||||||
|
|
||||||
<p>A couple of small implementation notes: global inheritence is "last
|
|
||||||
chance", so if a section's parent dictionary redefined
|
|
||||||
<code>BI_SPACE</code>, say, the section dictionary inherits the
|
|
||||||
parent-dict value, not the global-dict value. Second, variable
|
|
||||||
inheritence happens at expand time, not at dictionary-create time. So
|
|
||||||
if you create a section dictionary, and then afterwards set a variable
|
|
||||||
in its parent dictionary (or in the global dictionary), the section
|
|
||||||
<i>will</i> inherit that variable value, if it doesn't define the
|
|
||||||
value itself.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Writing Application Code To Use Templates </h2>
|
|
||||||
|
|
||||||
<p>Most application code concerns filling a template dictionary, but
|
|
||||||
there is also code for loading templates themselves from disk. A
|
|
||||||
final category of code lets you inspect and control the template
|
|
||||||
system.</p>
|
|
||||||
|
|
||||||
<p>The code below assumes the default configuration option of putting
|
|
||||||
all template code in namespace <code>google</code>.
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Loading A Template </h3>
|
|
||||||
|
|
||||||
<p>The main routine to load a template is
|
|
||||||
<code>google::Template::GetTemplate()</code>, defined in
|
|
||||||
<code>template.h</code>. This is a static, factory method, that loads
|
|
||||||
a template from either disk or from an internal template cache, and
|
|
||||||
returns a pointer to a <code>Template</code> object. Besides a
|
|
||||||
filename to load from, this routine takes a 'strip' argument which
|
|
||||||
defines how to expand whitespace found in a template file. It can
|
|
||||||
have one of the following values:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> <code>google::DO_NOT_STRIP</code>: do nothing. This expands the
|
|
||||||
template file verbatim.
|
|
||||||
|
|
||||||
<li> <code>google::STRIP_BLANK_LINES</code>: remove all blank
|
|
||||||
lines when expanding. This ignores any blank lines found in
|
|
||||||
the template file when expanding. When the template is html,
|
|
||||||
this reduces the size of the output text without requiring a
|
|
||||||
sacrifice of readability for the input file.
|
|
||||||
|
|
||||||
<li> <code>google::STRIP_WHITESPACE</code>: remove not only blank lines when
|
|
||||||
expanding, but also whitespace at the beginning and end of each
|
|
||||||
line. It also removes any linefeed (possibly following
|
|
||||||
whitespace) that follows a closing '}}' of any kind of template
|
|
||||||
marker <i>except</i> a template variable. (This means a
|
|
||||||
linefeed may be removed anywhere by simply placing a comment
|
|
||||||
marker as the last element on the line.) When the template is
|
|
||||||
html, this reduces the size of the output html without changing
|
|
||||||
the way it renders (except in a few special cases). When using
|
|
||||||
this flag, the built-in template variables
|
|
||||||
<code>BI_NEWLINE</code> and <code>BI_SPACE</code> can be useful
|
|
||||||
to force a space or newline in a particular situation.
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>This factory method returns NULL if the template cannot be found,
|
|
||||||
or if there is a syntax error trying to load it.</p>
|
|
||||||
|
|
||||||
<p>Besides loading templates, the application can also ask the
|
|
||||||
template system to <i>reload</i> a template, via
|
|
||||||
<code>template->ReloadIfChanged()</code>. (You can also reload all
|
|
||||||
templates at once via <code>google::Template::ReloadAllIfChanged()</code>.)
|
|
||||||
<code>ReloadIfChanged()</code> looks on disk, and if it notices the
|
|
||||||
template file has changed since the last load, it will reload the
|
|
||||||
template from disk, replacing the old contents. Actually, the reload
|
|
||||||
is done lazily: <code>ReloadIfChanged</code> just sets a bit that
|
|
||||||
causes the template to be reloaded next time <code>GetTemplate</code>
|
|
||||||
is called.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Creating A Template Dictionary </h3>
|
|
||||||
|
|
||||||
<p>The class <code>google::TemplateDictionary</code> is used for all template
|
|
||||||
dictionary operations. <code>new google::TemplateDictionary(name)</code> is
|
|
||||||
used to create a new top-level dictionary.
|
|
||||||
<code>dict->AddSectionDictionary(name)</code> and
|
|
||||||
<code>dict->AddIncludeDictionary(name)</code> are used to create
|
|
||||||
sub-dictionaries for sections or include-files. After
|
|
||||||
creating a dictionary, the application should call one or more
|
|
||||||
functions for each marker in the template. As an example, consider
|
|
||||||
the following template:
|
|
||||||
<pre>
|
|
||||||
<html><body> {{! This page has no head section.}}
|
|
||||||
{{#CHANGE_USER}}
|
|
||||||
<A HREF="/login">Click here</A> if you are not {{USERNAME}}<br>
|
|
||||||
{{/CHANGE_USER}}
|
|
||||||
|
|
||||||
Last five searches:<ol>
|
|
||||||
{{#PREV_SEARCHES}
|
|
||||||
<li> {{PREV_SEARCH}}
|
|
||||||
{{/PREV_SEARCH}}
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
{{>RESULT_TEMPLATE}}
|
|
||||||
|
|
||||||
{{FOOTER}}
|
|
||||||
</body></html>
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>To instantiate the template, the user should call a function to set
|
|
||||||
up <code>FOOTER</code>, and a function to say what to do for the
|
|
||||||
sections <code>CHANGE_USER</code> and <code>PREV_SEARCHES</code>, and
|
|
||||||
for the include-template <code>RESULT_TEMPLATE</code>. Quite likely,
|
|
||||||
the application will also want to create a sub-dictionary for
|
|
||||||
<code>CHANGE_USER</code>, and in that sub-dictionary call a function
|
|
||||||
to set up <code>USERNAME</code>. There will also be sub-dictionaries
|
|
||||||
for <code>PREV_SEARCHES</code>, each of which will need to set
|
|
||||||
<code>PREV_SEARCH</code>. Only when this is all set up will the
|
|
||||||
application be able to apply the dictionary to the template to get
|
|
||||||
output.</p>
|
|
||||||
|
|
||||||
<p>The appropriate function to call for a given template marker
|
|
||||||
depends on its type.</p>
|
|
||||||
|
|
||||||
<h4> <A NAME="variables">Variables</A> </h4>
|
|
||||||
|
|
||||||
<p>For variables, the only interesting action is to set the variable's
|
|
||||||
value. For most variables, the right method to call is
|
|
||||||
<code>dict->SetValue(name, value)</code>. (The name and value
|
|
||||||
can be specified as strings in a variety of ways: C++ strings, char
|
|
||||||
*'s, or char *'s plus length.) You can also call
|
|
||||||
<code>google::TemplateDictionary::SetGlobalValue(name, value)</code> -- no
|
|
||||||
<code>TemplateDictionary</code> instance needed here -- to set a
|
|
||||||
variable that can be used by all templates in an applications. This is
|
|
||||||
quite rare.</p>
|
|
||||||
|
|
||||||
<p>In addition to <code>SetValue()</code>, there are a few helper
|
|
||||||
routines to help setting values of a few special forms.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> <code>SetIntValue(name, int)</code>: takes an int as the value. </li>
|
|
||||||
<li> <code>SetEscapedValue(name, value, escape_functor)</code>:
|
|
||||||
escapes the value, using the escape-functor, which takes a
|
|
||||||
string as input and gives a "munged" string as output.
|
|
||||||
<code>TemplateDictionary</code> has a few escape-functors built
|
|
||||||
in, including <code>html_escape</code>, which replaces
|
|
||||||
<code><</code>, <code>></code>, <code>&</code>, and
|
|
||||||
<code>"</code> with the appropriate html entity;
|
|
||||||
<code>xml_escape</code>, which deals with the
|
|
||||||
<code>&nbsp;</code> entity; and
|
|
||||||
<code>javascript_escape</code>, which escapes quotes and other
|
|
||||||
characters that are meaningful to javascript. These are
|
|
||||||
helpful in avoiding security holes when the template is
|
|
||||||
html/xml/javascript. You can also define your own functor; see
|
|
||||||
the example below. </li>
|
|
||||||
<li> <code>SetFormattedValue(name, fmt, ...)</code>: the
|
|
||||||
<code>fmt</code> and <code>...</code> work just like in
|
|
||||||
<code>printf</code>: <code>SetFormattedValue("HOMEPAGE",
|
|
||||||
"http://%s/", hostname)</code>. </li>
|
|
||||||
<li> <code>SetEscapedFormattedValue(name, escape_functor, fmt,
|
|
||||||
...)</code>: formats the value just like <code>printf</code>,
|
|
||||||
and then escapes the result using the given functor. </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>Example:</p>
|
|
||||||
<pre>
|
|
||||||
google::TemplateDictionary* dict = new google::TemplateDictionary("var example");
|
|
||||||
dict->SetValue("FOOTER", "Aren't these great results?");
|
|
||||||
class StarEscape { string operator()(const string& in) const { return string("*") + in + string("*"); } };
|
|
||||||
dict->SetEscapedValue("USERNAME", username, StarEscape());
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>Note that the template itself can also specify escaping via <A
|
|
||||||
HREF="#modifiers">variable modifiers</A>! It's very possible for you
|
|
||||||
to escape the value when setting it in the dictionary, and then have
|
|
||||||
the template escape it again when outputting, so be careful you escape
|
|
||||||
only as much as you need to.</p>
|
|
||||||
|
|
||||||
<h4> <A NAME="sections">Sections</A> </h4>
|
|
||||||
|
|
||||||
<p>Sections are used in two ways in templates. One is to expand some
|
|
||||||
text multiple times. This is how <code>PREV_SEARCHES</code> is used
|
|
||||||
in the example above. In this case we'll have one small
|
|
||||||
sub-dictionary for each of the five previous searches the user did.
|
|
||||||
To do this, call <code>AddSectionDictionary(section_name)</code>
|
|
||||||
to create the sub-dictionary. It returns a
|
|
||||||
<code>TemplateDictionary*</code> that you can use to fill the
|
|
||||||
sub-dictionary.
|
|
||||||
|
|
||||||
<p>The other use of sections is to conditionally show or hide a block
|
|
||||||
of text at template-expand time. This is how <code>CHANGE_USER</code>
|
|
||||||
is used in the example template: if the user is logged in, we show the
|
|
||||||
section with the user's username, otherwise we choose not to show the
|
|
||||||
section.</p>
|
|
||||||
|
|
||||||
<p>This second case is a special case of the first, and the "standard"
|
|
||||||
way to show a section is to expand it exactly one time, by calling
|
|
||||||
<code>AddSectionDictionary()</code> once, and then setting
|
|
||||||
<code>USERNAME</code> in the sub-dictionary.</p>
|
|
||||||
|
|
||||||
<p>However, the hide/show idiom is so common there are a few
|
|
||||||
convenience methods to make it simpler. The first takes advantage of
|
|
||||||
the fact sections inherit variables from their parent: you set
|
|
||||||
<code>USERNAME</code> in the parent dictionary, rather than a section
|
|
||||||
sub-dictionary, and then call <code>ShowSection()</code>, which adds a
|
|
||||||
single, empty dictionary for that section. This causes the section to
|
|
||||||
be shown once, and to inherit <i>all</i> its variable values from its
|
|
||||||
parent.</p>
|
|
||||||
|
|
||||||
<p>A second convenience method is written for the particular case we
|
|
||||||
have with <code>USERNAME</code>: if the user's username is non-empty,
|
|
||||||
we wish to
|
|
||||||
show the section with <code>USERNAME</code> set to the username,
|
|
||||||
otherwise we wish to hide the section and show neither
|
|
||||||
<code>USERNAME</code> nor the text around it. The method
|
|
||||||
<code>SetValueAndShowSection(name, value, section_name)</code> does
|
|
||||||
exactly that: if value is non-empty, add a single single dictionary to
|
|
||||||
<code>section_name</code> and call <code>section_dict->AddValue(name,
|
|
||||||
value)</code>. There's also <code>SetEscapedValueAndShowSection(name,
|
|
||||||
value, escape_functor, section_name)</code>, which lets you escape
|
|
||||||
<code>value</code>.</p>
|
|
||||||
|
|
||||||
<p>Example:</p>
|
|
||||||
<pre>
|
|
||||||
using google::TemplateDictionary;
|
|
||||||
TemplateDictionary* dict = new TemplateDictionary("section example");
|
|
||||||
const char* username = GetUsername(); // returns "" for no user
|
|
||||||
if (username[0] != '\0') {
|
|
||||||
TemplateDictionary* sub_dict = dict->AddSectionDictionary("CHANGE_USER");
|
|
||||||
sub_dict->SetValue("USERNAME", username);
|
|
||||||
} else {
|
|
||||||
// don't need to do anything; we want a hidden section, which is the default
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of the above 'if' statement, we could have done this:
|
|
||||||
if (username[0] != '\0') {
|
|
||||||
dict->ShowSection("CHANGE_USER"); // adds a single, empty dictionary
|
|
||||||
dict->SetValue("USERNAME", username); // take advantage of inheritence
|
|
||||||
} else {
|
|
||||||
// don't need to do anything; we want a hidden section, which is the default
|
|
||||||
}
|
|
||||||
|
|
||||||
// Or we could have done this:
|
|
||||||
dict->SetValueAndShowSection("USERNAME", username, "CHANGE_USER");
|
|
||||||
|
|
||||||
// Moving on...
|
|
||||||
GetPrevSearches(prev_searches, &num_prev_searches);
|
|
||||||
if (num_prev_searches > 0) {
|
|
||||||
for (int i = 0; i < num_prev_searches; ++i) {
|
|
||||||
TemplateDictionary* sub_dict = dict->AddSectionDictionary("CHANGE_USER");
|
|
||||||
sub_dict->SetEscapedValue("PREV_SEARCH", prev_searches[i],
|
|
||||||
TemplateDictionary::html_escape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h4> Template-includes </h4>
|
|
||||||
|
|
||||||
<p>Template-include markers are much like section markers, so
|
|
||||||
<code>SetIncludeDictionary(name)</code> acts, not surprisingly,
|
|
||||||
exactly like <code>SetSectionDictionary(name)</code>. However, since
|
|
||||||
variable inheritence doesn't work across include boundaries, there is
|
|
||||||
no template-include equivalent to <code>ShowSection()</code> or
|
|
||||||
<code>SetValueAndShowSection()</code>.<p>
|
|
||||||
|
|
||||||
<p>One difference bewteen template-includes and sections is that for a
|
|
||||||
sub-dictionary that you create via
|
|
||||||
<code>SetIncludeDictionary()</code>, you <i>must</i> call
|
|
||||||
<code>subdict->SetFilename()</code> to indicate the name of the
|
|
||||||
template to include. If you do not set this, the sub-dictionary will
|
|
||||||
be ignored. The filename may be absolute, or relative, in which case
|
|
||||||
it's relative to <A HREF="#managing">template_root</A>.</p>
|
|
||||||
|
|
||||||
<p>Example:</p>
|
|
||||||
<pre>
|
|
||||||
using google::TemplateDictionary;
|
|
||||||
TemplateDictionary* dict = new TemplateDictionary("include example");
|
|
||||||
GetResults(results, &num_results);
|
|
||||||
for (int i = 0; i < num_results; ++i) {
|
|
||||||
TemplateDictionary* sub_dict = dict->AddIncludeDictionary("RESULT_TEMPLATE");
|
|
||||||
sub_dict->SetFilename("results.tpl");
|
|
||||||
FillResultsTemplate(sub_dict, results[i]);
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>In practice, it's much more likely that
|
|
||||||
<code>FillResultsTemplate()</code> will be the one to call
|
|
||||||
<code>SetFilename()</code>. Note that it's not an error to call
|
|
||||||
<code>SetFilename()</code> on a dictionary even if the dictionary is
|
|
||||||
not being used for a template-include; in that case, the function is a
|
|
||||||
no-op, but is perhaps still useful as self-documenting code.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> <A name="expand">Expanding a Template</A> </h3>
|
|
||||||
|
|
||||||
<p>Once you have a template and a template dictionary, it's simplicity
|
|
||||||
itself to expand the template with those dictionary values, putting
|
|
||||||
the output in a string:</p>
|
|
||||||
<pre>
|
|
||||||
google::Template* tpl = google::Template::GetTemplate(<filename>, google::STRIP_WHITESPACE);
|
|
||||||
google::TemplateDictionary dict("debug-name");
|
|
||||||
FillDictionary(&dict, ...);
|
|
||||||
string output;
|
|
||||||
<font color=red>tpl->Expand(&output, &dict);</font>
|
|
||||||
// output now holds the expanded template
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>The expanded template is written to the string <code>output</code>.
|
|
||||||
If <code>output</code> was not empty before calling
|
|
||||||
<code>Expand()</code>, the expanded template is appended to the end of
|
|
||||||
<code>output</code>.
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Getting a Template From a String Rather Than a File </h3>
|
|
||||||
|
|
||||||
<p>The <code>TemplateFromString</code> class, in
|
|
||||||
<code>template_from_string.h</code>, is an alternative to the
|
|
||||||
<code>Template</code> class when you really want your template to be
|
|
||||||
built in to the executable rather than read from a file. It's a
|
|
||||||
drop-in replacement, that takes an extra argument which is the
|
|
||||||
template contents.</p>
|
|
||||||
|
|
||||||
<p>Prefer <code>Template</code> to <code>TemplateFromString</code>,
|
|
||||||
for several reasons. For one, updating the template requires merely a
|
|
||||||
data push, rather than pushing the new executable. Also, you can load
|
|
||||||
the new template without needing to restart the binary. It also makes
|
|
||||||
it easier for non-programmers to modify the template. Finally,
|
|
||||||
string-templates cannot be included by other
|
|
||||||
templates, since <code>{{>include}}</code> takes a filename.</p>
|
|
||||||
|
|
||||||
<p>One reason to use <code>TemplateFromString</code> is if you are in
|
|
||||||
an environment where having data files could be dangerous -- for
|
|
||||||
instance, you work on a disk that is usually full, or need the
|
|
||||||
template to work even in the face of disk I/O errors.</p>
|
|
||||||
|
|
||||||
<p>This package comes with a script, <A
|
|
||||||
HREF="#converter">template-converter</A>, that takes a template file
|
|
||||||
as input and emits a C++ code snippet (an .h file) that defines a
|
|
||||||
string with those template contents. This makes it easy to start by
|
|
||||||
using a normal, file-based template, and then switch to
|
|
||||||
template-from-string later if you so desire.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Working Effectively with Templates </h2>
|
|
||||||
|
|
||||||
<h3> <A name="register">Registering Template Strings</A> </h3>
|
|
||||||
|
|
||||||
<p>Both dictionary keys and template filenames are strings. Instead
|
|
||||||
of using raw strings, we encourage you to use a bit of machinery to
|
|
||||||
help protect against various types of errors.</p>
|
|
||||||
|
|
||||||
<p>For dictionary keys, you can use the <A
|
|
||||||
HREF="#make_tpl_varnames_h">make_tpl_varnames_h</A> tool to create
|
|
||||||
static string variables to use instead of a string constant. This
|
|
||||||
will protect against typos, as the <A
|
|
||||||
HREF="#make_tpl_varnames_h">make_tpl_varnames_h</A> documentation
|
|
||||||
describes.</p>
|
|
||||||
|
|
||||||
<p>For template filenames that a program uses -- including
|
|
||||||
sub-templates -- we suggest the following idiom:</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
#include "example.tpl.varnames.h" // defines 1 string per dictionary key
|
|
||||||
RegisterTemplateFilename(EXAMPLE_FN, "example.tpl"); // defines template
|
|
||||||
...
|
|
||||||
google::Template* tpl = google::Template::GetTemplate(EXAMPLE_FN, ...);
|
|
||||||
...
|
|
||||||
include_dict->SetFilename(EXAMPLE_FN);
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>By registering the filename, you can <A HREF="#managing">query</A>
|
|
||||||
the template system to detect syntax errors, reload-status, and so
|
|
||||||
forth.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> <A NAME="managing">Managing Templates</A> </h3>
|
|
||||||
|
|
||||||
<p>The following functions affect the global state of the template
|
|
||||||
system.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> <code>google::Template::SetTemplateRootDirectory(root)</code>: when
|
|
||||||
<code>GetTemplate()</code> is called with a relative filename,
|
|
||||||
the template system will try to load the template from
|
|
||||||
<code>root/file</code>. This defaults to <code>./</code>. </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>There are some administrative tools that can help with tweaking
|
|
||||||
template performance and debugging template problems. The following
|
|
||||||
functions work on <A HREF="#register">registered</A> templates.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> <code>google::TemplateNameList::GetMissingList()</code>: returns a list
|
|
||||||
of all registered templates where the file could not be found
|
|
||||||
on disk. </li>
|
|
||||||
<li> <code>google::TemplateNameList::AllDoExist()</code>: true iff the
|
|
||||||
missing-list is empty. </li>
|
|
||||||
<li> <code>google::TemplateNameList::GetBadSyntaxList()</code>: returns a
|
|
||||||
list of all registered templates where the template contains a
|
|
||||||
syntax error, and thus cannot be used. </li>
|
|
||||||
<li> <code>google::TemplateNameList::IsAllSyntaxOkay()</code>: true iff the
|
|
||||||
bad-syntax list is emtpy. </li>
|
|
||||||
<li> <code>google::TemplateNameList::GetLastmodTime()</code>: the latest
|
|
||||||
last-modified time for any registered template-file. </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>The following functions help with debugging, by allowing you to
|
|
||||||
examine the template dictionaries and expanded templates in
|
|
||||||
more detail.</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> <code>dict->Dump()</code>: dumps the contents of the dictionary
|
|
||||||
(and any sub-dictionaries) to stderr. </li>
|
|
||||||
<li> <code>dict->DumpToString()</code>: dumps the contents of the
|
|
||||||
dictionary (and sub-dictionaries) to the given string. </li>
|
|
||||||
<li> <code>dict->SetAnnotateOutput()</code>: when applying this
|
|
||||||
dictionary to a template, add marker-strings to the output to
|
|
||||||
indicate what template-substitutions the system was making.
|
|
||||||
This takes a string argument which can be used to shorten the
|
|
||||||
filenames printed in the annotations: any filename that starts
|
|
||||||
with the string you give, that string is elided from the
|
|
||||||
filename before printing. It's confusing, but fear not: it's
|
|
||||||
safe to just always pass in the empty string. </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>Finally, <code>ClearCache()</code> removes all template objects
|
|
||||||
from the cache used by <code>GetTemplate()</code>. Typically, this is
|
|
||||||
only used in environments that check for memory leaks: calling this at
|
|
||||||
the end of the program will clean up all memory that the template
|
|
||||||
system uses.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Template and Threads </h3>
|
|
||||||
|
|
||||||
<p>All static methods on <code>Template</code> and
|
|
||||||
<code>TemplateDictionary</code> objects are threadsafe: you can safely
|
|
||||||
call <code>google::TemplateDictionary::SetGlobalValue()</code> without needing
|
|
||||||
to worry about locking.</p>
|
|
||||||
|
|
||||||
<p>Non-static methods are not thread-safe. It is not safe for two
|
|
||||||
threads to assign values to the same template-dictionary without doing
|
|
||||||
their own locking. Note that this is expected to be quite rare:
|
|
||||||
usually only one thread will care about a given
|
|
||||||
template-dictionary.</p>
|
|
||||||
|
|
||||||
<p>For <code>Template</code> objects, the most common idiom is that a
|
|
||||||
template is loaded via <code>GetTemplate()</code>, and after that only
|
|
||||||
const methods like <code>Expand()</code> are called on the template.
|
|
||||||
With such usage, it's safe to use the same <code>Template</code>
|
|
||||||
object in multiple threads without locking. Be careful, however, if
|
|
||||||
you also call functions like <code>ReloadIfChanged()</code>.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Development Tools </h2>
|
|
||||||
|
|
||||||
<p>This package includes several tools to make it easier to use write
|
|
||||||
and use templates.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> <A name="make_tpl_varnames_h">make_tpl_varnames_h:
|
|
||||||
Template Syntax Checker and Header File Generator</A> </h3>
|
|
||||||
|
|
||||||
<p><code>make_tpl_varnames_h</code> is a "lint" style syntax checker
|
|
||||||
and header file generator. It takes the names of template files as
|
|
||||||
command line arguments and loads each file into a Template object by
|
|
||||||
retrieving the file via the Template factory method. The loading of
|
|
||||||
the file does pure syntax checking and reports such errors as
|
|
||||||
mis-matched section start/end markers, mis-matched open/close
|
|
||||||
double-curly braces, such as <code>"{{VAR}"</code>, or invalid characters
|
|
||||||
in template variables/names/comments.</p>
|
|
||||||
|
|
||||||
<p>If the template passes the syntax check, by default the utility
|
|
||||||
then creates a header file for use in the executable code that fills
|
|
||||||
the dictionary for the template. If the developer includes this
|
|
||||||
header file, then constants in the header file may be referenced in
|
|
||||||
the dictionary building function, rather than hard-coding strings as
|
|
||||||
variable and section names. By using these constants, the compiler
|
|
||||||
can notify the developer of spelling errors and mismatched names.
|
|
||||||
Here's an example of how this is used, and how it helps prevent
|
|
||||||
errors:</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
const char * const kosr_RESULT_NUMBER = "RESULT_NUMBER"; // script output
|
|
||||||
dict.SetValue("RESSULT_NUMBER", "4"); // typo is silently missed
|
|
||||||
dict.SetValue(kosr_RESSULT_NUMBER, "4"); // compiler catches typo
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>Each constant is named as follows:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> The initial letter 'k', indicating a defined constant. </li>
|
|
||||||
|
|
||||||
<li> One or more prefix letters which are derived from the
|
|
||||||
template file name. These prefix letters consist of the first
|
|
||||||
letter of the file name, followed by the first letter following
|
|
||||||
each underscore in the name, with the exception of the letter
|
|
||||||
'p' when it is followed by the letters "ost", as is a <A
|
|
||||||
HREF="tips.html#versioning">recommended convention</A> for
|
|
||||||
template versioning. For example, the prefix letters for the
|
|
||||||
file <code>one_search_result_post20020815.tpl</code> are
|
|
||||||
<code>osr</code>. </li>
|
|
||||||
|
|
||||||
<li> An underscore. </li>
|
|
||||||
|
|
||||||
<li> The varible or section name itself, same casing. </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>As an example, the section name "RESULT_NUMBER" in the file
|
|
||||||
one_search_result_post20020815.tpl would be given the constant name
|
|
||||||
<code>kosr_RESULT_NUMBER</code> and would appear in the header file as
|
|
||||||
<code>const char * const kosr_RESULT_NUMBER = "RESULT_NUMBER";</code>
|
|
||||||
-- as in the example above.</p>
|
|
||||||
|
|
||||||
<p>By default, the header file is produced in the current directory.
|
|
||||||
An alternate output directory may be specified
|
|
||||||
by the command line flag <code>--header_dir</code>.
|
|
||||||
|
|
||||||
<p>The name of the generated header file is the same as the name of
|
|
||||||
the template file with an extension added to the name. By default,
|
|
||||||
that extension is <code>.varnames.h</code>. In the above example, the
|
|
||||||
header file containing the constant declarations would be named
|
|
||||||
<code>one_search_result_post20020815.tpl.varnames.h</code>. An
|
|
||||||
alternate extension may be provided via the command line flag
|
|
||||||
<code>--outputfile_suffix</code>.
|
|
||||||
|
|
||||||
<p>Important command line flags:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> <code>--noheader</code> -- Indicates that a header file
|
|
||||||
should not be generated; only syntax checking should be done. </li>
|
|
||||||
|
|
||||||
<li> <code>--header_dir</code> -- sets the directory where the header
|
|
||||||
is written. Default: "./" </li>
|
|
||||||
|
|
||||||
<li> <code>--template_dir</code> -- sets the template root
|
|
||||||
directory. Default: <code>./</code> which is the correct
|
|
||||||
specification when it is run from the directory where the templates
|
|
||||||
are located. This is only used if the input template filenames
|
|
||||||
are specified as relative paths rather than absolute
|
|
||||||
paths. </li>
|
|
||||||
|
|
||||||
<li> <code>--outputfile_suffix</code> -- the extension added to the
|
|
||||||
name of the template file to create the name of the generated
|
|
||||||
header file. Default: <code>.varnames.h</code>.
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>For a full list of command line flags, run
|
|
||||||
<code>make_tpl_varnames_h --help</code>.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> <A name="converter">template-converter: convert a template to a C++ string</A> </h3>
|
|
||||||
|
|
||||||
<p>The <code>TemplateFromString</code> class lets you load a template
|
|
||||||
from a string instead of a file. Applications may prefer this option
|
|
||||||
to reduce the dependencies of the executable, or use it in
|
|
||||||
environments where data files are not practical. In such cases,
|
|
||||||
<code>template-converter</code> can be used as a template "compiler",
|
|
||||||
letting the developer write a template file as a data file in the
|
|
||||||
normal way, and then "compiling" it to a C++ string to be included in
|
|
||||||
the executable.</p>
|
|
||||||
|
|
||||||
<p>Usage is <code>template-converter <template filename></code>.
|
|
||||||
C++ code is output is to stdout; it can be stored in a .h file or
|
|
||||||
included directly into a C++ file. Perl must be installed to use this
|
|
||||||
script.<p>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<ul>
|
|
||||||
<!--
|
|
||||||
<li> <A HREF="howto.html">Howto</A> </li>
|
|
||||||
-->
|
|
||||||
<li> <A HREF="tips.html">Tips</A> </li>
|
|
||||||
<li> <A HREF="example.html">Example</A> </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<address>
|
|
||||||
Craig Silverstein<br>
|
|
||||||
27 February 2006
|
|
||||||
</address>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,94 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Google Template System</title>
|
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link href="http://www.google.com/favicon.ico" type="image/x-icon"
|
|
||||||
rel="shortcut icon">
|
|
||||||
<link href="designstyle.css" type="text/css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
<!--
|
|
||||||
ol.bluelist li {
|
|
||||||
color: #3366ff;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
ol.bluelist li p {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
ul.blacklist li {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1> <a name="Google_Template_System"></a>Google Template System </h1>
|
|
||||||
<center><strong>Status: Current</strong>
|
|
||||||
<small>(as of 16 February 2006)</small></center>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
Welcome to the Google C++ template system! As a quick start, here's a
|
|
||||||
small but complete program that uses this template library. For more
|
|
||||||
details see, the links below.
|
|
||||||
|
|
||||||
<h3>Template file <code>example.tpl</code></h3>
|
|
||||||
<pre>
|
|
||||||
Hello {{NAME}},
|
|
||||||
You have just won ${{VALUE}}!
|
|
||||||
{{#IN_CA}}Well, ${{TAXED_VALUE}}, after taxes.{{/IN_CA}}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h3>C++ program <code>example.cc</code></h3>
|
|
||||||
<pre>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <google/template.h>
|
|
||||||
int main(int argc, char** argv) {
|
|
||||||
google::TemplateDictionary dict("example");
|
|
||||||
dict.SetValue("NAME", "John Smith");
|
|
||||||
int winnings = random() % 100000;
|
|
||||||
dict.SetIntValue("VALUE", winnings);
|
|
||||||
dict.SetFormattedValue("TAXED_VALUE", "%.2f", winnings * 0.83);
|
|
||||||
// For now, assume everyone lives in CA.
|
|
||||||
// (Try running the program with a 0 here instead!)
|
|
||||||
if (1) {
|
|
||||||
dict.ShowSection("IN_CA");
|
|
||||||
}
|
|
||||||
|
|
||||||
google::Template* tpl = google::Template::GetTemplate("example.tpl",
|
|
||||||
google::DO_NOT_STRIP);
|
|
||||||
std::string output;
|
|
||||||
tpl->Expand(&output, &dict);
|
|
||||||
std::cout << output;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h2>In-depth Documentation</h2>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li> <A HREF="howto.html">Howto</A>: Introduction to the Google
|
|
||||||
Template system, and a tutorial for using it. </li>
|
|
||||||
|
|
||||||
<li> <A HREF="tips.html">Tips</A>: Advice, tips, and recommendations
|
|
||||||
for best practices with templates, to make them easier to write
|
|
||||||
and maintain, and to avoid common template mistakes. </li>
|
|
||||||
|
|
||||||
<li> <A HREF="example.html">Examples</A>: Some example templates and
|
|
||||||
application code that uses them. These are taken from actual
|
|
||||||
Google applications. </li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<address>
|
|
||||||
Craig Silverstein<br>
|
|
||||||
Last modified: Wed Feb 15 23:21:42 PST 2006
|
|
||||||
</address>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
468
docs/tips.html
468
docs/tips.html
|
@ -1,468 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Tips and Guidelines for Using the Google Template System</title>
|
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<link href="http://www.google.com/favicon.ico" type="image/x-icon"
|
|
||||||
rel="shortcut icon">
|
|
||||||
<link href="designstyle.css" type="text/css" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
<!--
|
|
||||||
ol.bluelist li {
|
|
||||||
color: #3366ff;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
ol.bluelist li p {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
ul.blacklist li {
|
|
||||||
color: #000;
|
|
||||||
font-family: "Times Roman", times, serif;
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<h1>Tips and Guidelines for Using the Google Template System</h1>
|
|
||||||
<small>(as of 27 February 2006)</small></center>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<p>The <A HREF="howto.html">basic rules</A> of the template system are
|
|
||||||
enough to use it, but over time, we at Google have developed some
|
|
||||||
tips, guidelines, and best practices that make it easier to use
|
|
||||||
templates effectively, and to avoid common template errors.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h2> Program Design Considerations </h2>
|
|
||||||
|
|
||||||
<h3> <a name=versioning>Template naming and versioning</a> </h3>
|
|
||||||
|
|
||||||
<p> Early in Google's use of templates, we noticed a problem: if a
|
|
||||||
binary that uses a template and its corresponding template were both
|
|
||||||
modified, particularly if the change were such that the old binary
|
|
||||||
could not work with the new template or the new binary cannot work
|
|
||||||
with the old template, then somehow they both had to be deployed at
|
|
||||||
the same instant to not present errors to our users. This was hard to
|
|
||||||
do. The solution was to adopt a template naming and versioning
|
|
||||||
convention. The procedure to use it follows:</p>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li> Each template name ends with <code>_postYYYYMMDD.tpl</code>,
|
|
||||||
where YYYMMDD is the date of this version's initial
|
|
||||||
creation. </li>
|
|
||||||
|
|
||||||
<li> Before making (non-backward-compatible) modifications to a
|
|
||||||
template, copy the template to a new name, incorporating a
|
|
||||||
later date than the original one being copied. </li>
|
|
||||||
|
|
||||||
<li> Edit the new file, and push it to the production server. </li>
|
|
||||||
|
|
||||||
<li> Finally, update the code to refer to the new template-name
|
|
||||||
(ideally, using the <A
|
|
||||||
HREF="howto.html#register"><code>RegisterTemplateFilename</code>
|
|
||||||
idiom</A>), and push the new executable to the production
|
|
||||||
server. </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<p>When this convention is followed, the new template file does not
|
|
||||||
overwrite the old one when it is deployed, because it is a new file
|
|
||||||
with a new name. The old template file is still there to be used as
|
|
||||||
long as the old binary is still in production and the new template
|
|
||||||
file just sits there being ignored. Then when the new binary finally
|
|
||||||
gets deployed, it immediately starts using the new template file,
|
|
||||||
because it is coded (in <code>RegisterTemplateFilename</code>) to do
|
|
||||||
so. After that, it is the old template file that continues to sit
|
|
||||||
there ignored.</p>
|
|
||||||
|
|
||||||
<p>The <A
|
|
||||||
HREF="howto.html#make_tpl_varnames_h"><code>make_tpl_varnames_h</code>
|
|
||||||
utility</A> knows about the "_postYYYYMMDD" naming convention, so it
|
|
||||||
is important that you use that convention exactly if you use the
|
|
||||||
<code>make_tpl_varnames_h</code>.</p>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Processing Phases </h3>
|
|
||||||
|
|
||||||
<p>Typically a program using the Google Template System will
|
|
||||||
perform the following phases, usually in this order:</p>
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li> Retrieve and prepare the data used to fill a dictionary. </li>
|
|
||||||
|
|
||||||
<li> Build the data dictionary, including all its
|
|
||||||
sub-dictionaries, that will supply the values to the
|
|
||||||
designated template object, its sections, and its
|
|
||||||
included templates. </li>
|
|
||||||
|
|
||||||
<li> Retrieve the top-level template object required to
|
|
||||||
format the data. (This may or may
|
|
||||||
not involve reading and parsing a template file,
|
|
||||||
depending on whether the requested file has already
|
|
||||||
been read and parsed by the running program or
|
|
||||||
whether that file has been marked "reload if changed"
|
|
||||||
and was in fact changed.) </li>
|
|
||||||
|
|
||||||
<li> Expand the template object into an output buffer
|
|
||||||
using the completed data dictionary. </li>
|
|
||||||
|
|
||||||
<li> Output the buffer. </li>
|
|
||||||
|
|
||||||
<li> Clean up: Destroy the top-level data dictionary
|
|
||||||
whenever it is no longer needed. </li>
|
|
||||||
|
|
||||||
<li> Optionally, clear the cache at the end of program
|
|
||||||
execution. </li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
|
|
||||||
<h3> <A NAME="oneone">One template / One procedure call</A> </h3>
|
|
||||||
|
|
||||||
<p> Most of the code of the program will be in Phases 1 and
|
|
||||||
2. Clearly, Phase 1 is outside the scope of the template system. But
|
|
||||||
in designing the code for Phase 2 (building the data dictionary), it
|
|
||||||
is wise to have the structure of the program reflect the structure of
|
|
||||||
the templates being used. Specifically, there should be a single
|
|
||||||
procedure call to build the dictionary for a single template. That
|
|
||||||
procedure call should take parameters that include all the data
|
|
||||||
required to populate the data dictionary for that template and all the
|
|
||||||
templates it includes. Following this "one template/one procedure
|
|
||||||
call" guideline further, for each included template, another procedure
|
|
||||||
should be called to populate the (or <i>each</i>) data dictionary for
|
|
||||||
that included template. This maintains the "one template/one procedure
|
|
||||||
call" principle in a nested fashion that reflects the nesting of the
|
|
||||||
templates.</p>
|
|
||||||
|
|
||||||
<p> This is not to imply that the "one procedure call" for a template
|
|
||||||
should not be modularized into sub-procedures for readability and
|
|
||||||
maintainability, or that it should not call other auxilliary
|
|
||||||
procedures for such things as formatting the data and converting it to
|
|
||||||
the appropriate strings, etc. But it does mean that there should be
|
|
||||||
one entry point for building the dictionary tree for one template and
|
|
||||||
that entry point should show the data dependencies of that template
|
|
||||||
through its parameter list. This code for populating the data
|
|
||||||
dictionary should <i>NOT</i> be intermingled with data gathering code
|
|
||||||
that should have been done in Phase 1.</p>
|
|
||||||
|
|
||||||
<p>(Inside Google, the convention has been used to name the dictionary
|
|
||||||
building procedure using the pattern <code>fill_..._dictionary</code>
|
|
||||||
where the dots are related to the name of the template the data is
|
|
||||||
being prepared for. For instance, the data for the template named
|
|
||||||
one_search_result.tpl might be placed in a dictionary via a function
|
|
||||||
named <code>fill_one_search_result_dictionary</code>.)
|
|
||||||
|
|
||||||
|
|
||||||
<h2> <A name=tips>Tips, Idioms, and Conventions</a> </h2>
|
|
||||||
|
|
||||||
<ol class=bluelist>
|
|
||||||
|
|
||||||
<li> Choose template names to create unique constant prefixes.
|
|
||||||
|
|
||||||
<p>Template names should contain <em>at least two words</em>
|
|
||||||
to avoid constant prefix clashes (e.g. <code>kxy_</code>
|
|
||||||
instead of <code>kx_</code> ) The name of a new template
|
|
||||||
should be checked against the existing names before
|
|
||||||
proceeding. If your new template name produces a prefix that
|
|
||||||
conflicts with an already existing template, you should change
|
|
||||||
the name of your new template, even though it may be the only
|
|
||||||
perfect name you can come up with. You'll have to use a less
|
|
||||||
than perfect name in that case. (See "Template Syntax Checker
|
|
||||||
and Header File Generator" below for more explanation about
|
|
||||||
constant prefixes.)</p> </li>
|
|
||||||
|
|
||||||
<li> Use SetFormattedValue discriminately.
|
|
||||||
|
|
||||||
<p> This method should never be used to sneak HTML into the
|
|
||||||
executable as in</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
dictionary->SetFormattedValue(kxy_VAR,
|
|
||||||
"<b>%s</b>",
|
|
||||||
some_const_char_string);
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>In that case, the <code><b></code> and
|
|
||||||
<code></b></code> should be moved into the template.</p>
|
|
||||||
|
|
||||||
<li> Never have a section encompass an entire template.
|
|
||||||
|
|
||||||
<p>If the first line of a template is a start section marker
|
|
||||||
and the last line is its matching end section marker, then
|
|
||||||
those markers are unnecessary in almost all cases. They are
|
|
||||||
usually put there to allow the entire template to be hidden or
|
|
||||||
iterated, but since it encompasses the entire file, the
|
|
||||||
section may be hidden by not expanding the file (or by hiding
|
|
||||||
the template-include section that includes the file) and it
|
|
||||||
may be iterated by iterating the template-include marker of
|
|
||||||
the including template. (The only exception might be if the
|
|
||||||
entire page is to be iterated, but this seems a bit of a
|
|
||||||
stretch.)</p> </li>
|
|
||||||
|
|
||||||
<li> An included template is just a section whose contents are
|
|
||||||
located in a separate file. You may iterate over it just
|
|
||||||
like you do sections.
|
|
||||||
|
|
||||||
<p>For example, if your template has the following
|
|
||||||
template-include marker:</p>
|
|
||||||
<pre>
|
|
||||||
{{>MY_INCLUDED_TEMPLATE}}
|
|
||||||
</pre>
|
|
||||||
<p>you may call</p>
|
|
||||||
<pre>
|
|
||||||
google::TemplateDictionary *child_dict =
|
|
||||||
dictionary->AddIncludeDictionary(kxy_MY_INCLUDED_TEMPLATE);
|
|
||||||
</pre>
|
|
||||||
<p>to iterate that section. (Note: Make sure you call
|
|
||||||
<code>child_dict->SetFilename()</code>! If your included
|
|
||||||
template is not showing in the output, this is the first thing
|
|
||||||
you should check.)</p> </li>
|
|
||||||
|
|
||||||
<li> The recommended idiom to fill an include-template dictionary is
|
|
||||||
like this:
|
|
||||||
<pre>
|
|
||||||
fill_include_template_dictionary(dict->AddIncludeDictionary(name), ...);
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>But what do you do if you decide, in
|
|
||||||
<code>fill_include_template_dictionary</code>, that you don't
|
|
||||||
want to display anything for this include-template after all? It
|
|
||||||
seems like it's too late: you've already created the
|
|
||||||
sub-dictionary. The solution is simple: just be sure that
|
|
||||||
<code>fill_include_template_dictionary()</code> doesn't call
|
|
||||||
<code>SetFilename()</code> in that case.</p>
|
|
||||||
|
|
||||||
<li> Never have a section which only contains another section.
|
|
||||||
<p>For example, don't do this:</p>
|
|
||||||
<pre>
|
|
||||||
{{#OUTER_SECTION}}
|
|
||||||
{{#INNER_SECTION}}
|
|
||||||
section contents here
|
|
||||||
{{/INNER_SECTION}}
|
|
||||||
{{/OUTER_SECTION}}
|
|
||||||
</pre>
|
|
||||||
<p>or this equivalent template code (see the previous item):</p>
|
|
||||||
<pre>
|
|
||||||
{{#OUTER_SECTION}}
|
|
||||||
{{>INCLUDED_SECTION}}
|
|
||||||
{{/OUTER_SECTION}}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>This is usually done because the developer thinks the outer
|
|
||||||
section must be used to hide the section when the inner
|
|
||||||
section, intended for iteration, has no iterations. In both
|
|
||||||
cases, you should only have one section (either
|
|
||||||
<code>INNER_SECTION</code> or <code>INCLUDED_SECTION</code> in
|
|
||||||
the examples) and iterate that section either 0 times or more
|
|
||||||
than 0 times. It's the wonder of the dual use of sections,
|
|
||||||
i.e. that they may be conditional or iterative or, in this case,
|
|
||||||
both.</p>
|
|
||||||
|
|
||||||
<p>A related suggestion: Do not have a section whose entire
|
|
||||||
contents is one variable marker with nothing else, unless you
|
|
||||||
need to iterate over that section with multiple values of that
|
|
||||||
variable. You don't need the surrounding section just to hide
|
|
||||||
the marker. A variable marker that is not set, does not
|
|
||||||
produce output. By convention, we set such variables to the
|
|
||||||
empty string. But in neither case do you need to hide it by
|
|
||||||
hiding a surrounding section that contains nothing else.</p>
|
|
||||||
|
|
||||||
<li> Use this hide/show idiom for <code>if-else</code> blocks.
|
|
||||||
|
|
||||||
<p>Since sections are hidden by default, you can use represent
|
|
||||||
if-else logic in your code via <code>ShowSection</code>. For
|
|
||||||
example:</p>
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
if ( my_test ) {
|
|
||||||
dict->ShowSection(kxyz_TRUE_BLOCK);
|
|
||||||
[ more code to fill the values for that section]
|
|
||||||
} else {
|
|
||||||
dict->ShowSection(kxyz_FALSE_BLOCK);
|
|
||||||
[ more code to fill the values for that section]
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<li> <code>Write...</code> vs. <code>Fill...Dictionary</code> methods
|
|
||||||
- Observe the proper division of labor, don't mix them.
|
|
||||||
|
|
||||||
<p>The output (or write) function should create the top level
|
|
||||||
template dictionary, call one or more fill-dictionary routines
|
|
||||||
with it, then get the template and expand it. It should not call
|
|
||||||
dictionary modifying methods, like <code>ShowSection</code>
|
|
||||||
and <code>SetValue</code>. By keeping these separated into
|
|
||||||
their own fill-dictionary routine, the code is more modular and
|
|
||||||
lends itself to template re-use. If you maintain the proper
|
|
||||||
division of labor, the template you are filling and outputting
|
|
||||||
may be filled and included in a larger template by someone
|
|
||||||
else.</p> </li>
|
|
||||||
|
|
||||||
<li> Use <code>AddSectionDictionary</code> only when you want to
|
|
||||||
iterate over a section or, secondarily, if you need to avoid name
|
|
||||||
conflicts.
|
|
||||||
|
|
||||||
<p>Sometimes developers get the idea that every section requires
|
|
||||||
its own child dictionary created by an
|
|
||||||
<code>AddSectionDictionary</code> call. Because of variable
|
|
||||||
inheritence, this isn't usually so. The intended purpose of
|
|
||||||
<code>AddSectionDictionary</code> is to enable iteration over a
|
|
||||||
section. Secondarily, if the section contains generic names that
|
|
||||||
may conflict with the same name in other parts of the template,
|
|
||||||
it may be safer to call <code>AddSectionDictionary</code> to
|
|
||||||
create a separate namespace. In any case, do not assume you must
|
|
||||||
call <code>AddSectionDictionary</code> just because you are
|
|
||||||
working within a section. The main dictionary can be used for all
|
|
||||||
levels of conditional sections as long as you avoid name
|
|
||||||
conflicts by keeping the marker names unique.</p> </li>
|
|
||||||
|
|
||||||
<li> Do not place <code>RegisterTemplateFilename</code>
|
|
||||||
statements in header (<code>.h</code>) files.
|
|
||||||
|
|
||||||
<p><code>RegisterTemplateFilename</code> is a macro that
|
|
||||||
instantiates a <code>TemplateNamelist</code> object. If you place
|
|
||||||
it in a header file, a different object will get created each time
|
|
||||||
it is included in another <code>.cc</code> file.
|
|
||||||
|
|
||||||
<p>The <code>RegisterTemplateFilename</code> statement and its
|
|
||||||
associated <code>#include</code> of the <code>varnames.h</code>
|
|
||||||
file should occur only in the <code>.cc</code> file that
|
|
||||||
implements the fill-dictionary routine for that template. You
|
|
||||||
should never have more than one
|
|
||||||
<code>RegisterTemplateFilename</code> for a single template and
|
|
||||||
you should try hard not to copy the <code>#include</code> file to
|
|
||||||
other files as well. The template versioning makes this more
|
|
||||||
important because a developer may not know that the template name
|
|
||||||
with included version number needs to be updated in more than one
|
|
||||||
file when versioning occurs. [Also see above for more information
|
|
||||||
about what routine uses the filename declared by the
|
|
||||||
<code>RegisterTemplateFilename</code> statement.]</p> </li>
|
|
||||||
|
|
||||||
<li> Never reference more than one template in a
|
|
||||||
fill...dictionary method.
|
|
||||||
|
|
||||||
<p>Each template should have its own fill-dictionary
|
|
||||||
routine. That routine should only reference marker names defined
|
|
||||||
in that template. If this convention is followed, then all the
|
|
||||||
prefixes in a fill-dictionary routine will be the same. [Note
|
|
||||||
that an implication of this convention is that if the template
|
|
||||||
includes another template, via a template-include marker, then
|
|
||||||
containing template's fill-dictionary routine should call the
|
|
||||||
included template's fill-dictionary routine (being careful to
|
|
||||||
observe the convention described above). But
|
|
||||||
then, this is merely a restatement of <A HREF="#oneone">"One
|
|
||||||
template / One procedure call"</A>.]</p> </li>
|
|
||||||
|
|
||||||
<li> Have fill...dictionary call <code>SetFilename</code> even if the
|
|
||||||
dictionary is never used for a template-include.
|
|
||||||
|
|
||||||
<p>SetFilename() is required when a dictionary is created via
|
|
||||||
<code>AddIncludeDictionary()</code>. However, it's safe to set
|
|
||||||
all the time. By setting it always, you make the code work
|
|
||||||
properly if this dictionary ever changes to be template-included
|
|
||||||
after all. Even if not, by saying what template file the
|
|
||||||
dictionary is intended to go with, you are self-documenting your
|
|
||||||
code.</p> </li>
|
|
||||||
|
|
||||||
<li> Do not call <code>c_str()</code> on strings to pass them to
|
|
||||||
<code>TemplateDictionary</code> methods.
|
|
||||||
|
|
||||||
<p>Note that all the TemplateDictionary methods are defined to
|
|
||||||
take <code>TemplateString</code> objects. These are created
|
|
||||||
automatically from both strings and char*'s (and can be created
|
|
||||||
manually if you have a char* and a length). So if you have a
|
|
||||||
string, it's safe and efficient to just pass it in directly; you
|
|
||||||
do not need to extract the const char * from your string object
|
|
||||||
to pass it to these methods. For some reason, this is a common
|
|
||||||
error of noviced template coders.</p>
|
|
||||||
|
|
||||||
<p>The one exception to this rule is when using the method
|
|
||||||
<code>SetFormattedValue</code>. When calling that
|
|
||||||
method, you must call <code>c_str()</code> on strings that are to
|
|
||||||
be inserted
|
|
||||||
into the format string, just as you would when providing data for
|
|
||||||
any other printf format string.</p> </li>
|
|
||||||
|
|
||||||
<li> Do not use <code>SetGlobalValue</code> when you could use
|
|
||||||
<code>SetValue</code>.
|
|
||||||
|
|
||||||
<p><code>SetGlobalValue</code> should be used quite rarely, for
|
|
||||||
constants that really are consistent across all your templates.
|
|
||||||
It's slower to look up a value in the global dictionary than it
|
|
||||||
is in the template-specific dictionary.</p> </li>
|
|
||||||
|
|
||||||
<li> Do not use <code>TemplateFromString</code> unless you have
|
|
||||||
a specific need for its non-file-based attributes.
|
|
||||||
|
|
||||||
<p><code>TemplateFromString</code> was created for use in highly
|
|
||||||
constrained cases where file I/O may be impaired or
|
|
||||||
undesirable, for instance to produce a server error message
|
|
||||||
where there may be disk problems or to produce formatted
|
|
||||||
output where there are processes that do not have a facility
|
|
||||||
for updating data files dynamically. It is not recommended for
|
|
||||||
ordinary use as it is limited in functionality in at least the
|
|
||||||
following ways:</p>
|
|
||||||
<ul class=blacklist>
|
|
||||||
<li> It can neither include nor be included from
|
|
||||||
another template.
|
|
||||||
<li> It cannot be updated dynamically via a
|
|
||||||
data-push; changes always require a binary push.
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li> Use variable-modifiers (eg <code>{{VAR:html_escape}}</code>) or
|
|
||||||
<code>SetEscapedValue</code> when necessary to prevent security
|
|
||||||
violations.
|
|
||||||
|
|
||||||
<p>Variable-modifiers make it very easy to html-escape (or
|
|
||||||
otherwise escape) text that needs to be escaped for safety. Use
|
|
||||||
<code>:h</code>, <code>:j</code> and friends liberally.</p>
|
|
||||||
|
|
||||||
<p>For situations where you need to provisionally escape, or use
|
|
||||||
an escape routine other than the built-in ones, the
|
|
||||||
<code>Escaped</code> versions of the set-value methods
|
|
||||||
are useful utility functions to use.</p>
|
|
||||||
|
|
||||||
<p>As a guide for when to use this: every value accepted
|
|
||||||
from a user must be HTML-escaped before redisplaying it on
|
|
||||||
another page. The escaping
|
|
||||||
prevents the user from executing scripts or displaying raw HTML
|
|
||||||
via their input values. These methods make it simple to prevent
|
|
||||||
scripting security violations when used where necessary.</p> </li>
|
|
||||||
|
|
||||||
<li> Do not leave an extra space when using <code>{{BI_SPACE}}</code>
|
|
||||||
|
|
||||||
<p>The built-in template variable <code>BI_SPACE</code> is itself
|
|
||||||
replaced by a single space. It is used where you need to make
|
|
||||||
sure a space is preserved at the end of a line. It is a common
|
|
||||||
mistake to leave an extra space before this marker, which results
|
|
||||||
in not one, but two, spaces created in the document.</p> </li>
|
|
||||||
|
|
||||||
<p>Incorrect:<pre>
|
|
||||||
<table border=0 {{BI_SPACE}}
|
|
||||||
align=center></pre></p>
|
|
||||||
|
|
||||||
<p>Correct:<pre>
|
|
||||||
<table border=0{{BI_SPACE}}
|
|
||||||
align=center></pre></p>
|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<ul>
|
|
||||||
<li> <A HREF="howto.html">Howto</A> </li>
|
|
||||||
<!--
|
|
||||||
<li> <A HREF="tips.html">Tips</A> </li>
|
|
||||||
-->
|
|
||||||
<li> <A HREF="example.html">Example</A> </li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<address>
|
|
||||||
Craig Silverstein<br>
|
|
||||||
27 February 2006
|
|
||||||
</address>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in New Issue
Block a user