The heart of LXP's content management is its content inclusion workhorse: the
<include> tag. The <include> tag can operate
in one of many ways, depending either on the explicit value of the method attribute with
which it is initiated, or the implicit context determined by its attributes.
The <include> tag can be used, in its simplest form, to simply include a flat
HTML file, such as a standard header, sidebar, and footer. In its more advanced incarnations, the
<include> tag can be used to parse token-delimited files by using arbitrary tokens,
parse basic XML documents, embed PHP output inline within the LXP document, make direct SQL queries, and, of course, include
other LXP documents.
Table 13-1 lists each of the LXP inclusion methods available to the
<include> tag. The method in the first column is value that you supply to the
<include> tag's method attribute. The alias in the second column describes any
alternative names that you can use to invoke the same method. The "Implied by" column shows any attribute values which would
imply a method (bypassing the need for an explicit method attribute), and the "Description"
column gives a brief description of the method itself.
Table 13-1. LXP inclusion methods
Method
Aliases
Implied by
Description
LXP
.lxp extension ending src attribute
Processes the source file through mod_lxp
flat
Unrecognized extension in src attribute, and no sql or query attribute
Displays a file's literal contents
parsed
�
�
Parses a token-delimited file, and breaks it up into accessible <field> values
XML
RSS, RDF
.xml, .rdf or .rss extension at the end of the src attribute
Parses a well-formed XML file, and breaks it up into accessible <field> values
local
Apache
.php, .php3, or .phtml extension at the end of the src attribute
Displays output of an Apache subrequest with a src attribute describing a system filename
URI
�
�
Displays output of an Apache subrequest with a src attribute describing an HTTP URI
SQL
�
Existence of sql or query attribute
Executes a SQL statement, making query results accessible both as variables, and with the <field> tag
The source of content inclusion is invariably defined in the src attribute of the
<include> tag. In most cases this is a system filename, though it may describe a
database source or Apache URI request, depending on the method. When you include a file described by a relative path (one
that is not explicitly defined from the root of the filesystem), LXP will use the working directory of the LXP document
which is performing the inclusion.
Note: To prevent accidental infinite recursion (e.g., including a file that includes itself), LXP documents may only
include to the depth specified in the lxp.conf file's
MaxIncludeDepth directive (see the Section called Nuts and Bolts: Configuring lxp.conf"). The default maximum include
depth is 15.
Any LXP file can be included within another LXP file, if the Apache server has read access to the document specified
in the src attribute. Any variables set in the including LXP document will be both
accessible, and modifiable, by the included LXP document.
To include an LXP file, open an LXP region, and use the following syntax where
lxpfile is the name of the LXP file you wish to include:
<include src="lxpfile" />
Note: When an LXP file is included, it is parsed as if it had been directly called. Therefore, you must still use the
<lxp> tag to open an LXP region in the included LXP document before you are able
to use LXP tags within it.
Since the output of the included LXP document is embedded in place of the
<include> tag itself, no closing tag is necessary with this inclusion method. In
this case, the <include> tag should be an empty-element tag (i.e., with a trailing
slash). If the LXP file you are including does not have an extension ending in .lxp, you may force it
to be parsed by the LXP module by using the method="lxp" attribute.
Suppose that you have an LXP application that provides different content depending on the virtual host accessing the
site. Each virtual host's DocumentRoot could store just a single
index.lxp file, configured to include the root LXP application from another directory. Example 13-21 demonstrates such a simple top-level file, which sets two protected LXP variables, and
includes the root LXP file.
Flat file is a term used to refer to a plain-text document. A flat file is a non-parsed
document (such as a simple HTML document, or text file), as far as the server is concerned.
As with the inclusion of LXP documents, the flat file inclusion method does not require a
closing tag, and should therefore be used as an empty-element tag with a trailing slash. To include a flat file, open an
LXP region, and use the following syntax where flatfile is the name of the file you wish
to include:
<include src="flatfile" />
If the flat file you are including has a recognized file extension, you may force it to be displayed literally by
using the method="flat" attribute. Example 13-22
demonstrates an LXP document which includes three HTML files, from a relative directory called parts,
to be used as a header, sidebar, and footer. Since their extensions do not imply any more complex method, the files are
included as-is in the main document.
Example 13-22. Including flat files
<lxp>
<include src="parts/header.html" />
<include src="parts/leftbar.html" />
Welcome to my home page.<br />
<include src="parts/footer.html />
</lxp>
As you can see, this sort of inclusion can make web sites with consistent themes far easier to maintain by
modularizing components in a manner similar to what is done when using server-side-includes or PHP's
readfile() function. In addition, flat file inclusion allows you to achieve this modularity without
having to leave the simplicity and elegance of mark-up design. This is certainly not the full extent of the
<include> tag's power, as you will find out in subsequent sections.
A common function of many dynamic web sites is to post the contents of token-delimited files (such as Linux Today's
headlines file) on their web site in some kind of programmatically filtered format. These filters
generally are implemented differently from page to page, and site to site, and rely on somewhat involved algorithms to
pull apart the data and put it back together again into a useful format.
The LXP approach to displaying such files is with the use of the <include>
tag, by specifying the method="parsed" attribute. This use of the
<include> tag breaks up the parsed fields into sequential
values, accessible via the general-purpose LXP <field> tag.
Blocks are delimited from one another by the value supplied to the delimiter
attribute. Within a block, fields are separated from one another by each newline (symbolically,
\n, a literal line-wrap) found within the block. You may optionally specify a different
field delimiter value using the separator attribute.
The parsed method for the <include> tag requires a closing
</include> tag, because for each block that LXP reads from the file, it loops back
to the beginning of the <include> tag and re-iterates the mark-up until the last
block is processed.
If you wish to limit the number of blocks to be displayed, the last block number can be specified with the
lastblock attribute. Additionally, the firstblock
attribute can be used to skip any leading blocks (e.g., an introductory statement that might be embedded at the top of the
text file preceding the first delimiter).
Here is an example of such a token-delimited file, from www.linuxtoday.com:
Welcome to lthead.txt. Fields are delimited by two ampersands.
The first field is the headline. The second field is the URL to the story.
The third field is the date the story was posted.
Have Fun! ([email protected])
&&
LinuxProgramming: python-dev summary 2001-06-21 - 2001-07-05
https://linuxtoday.com/news_story.php3?ltsn=2001-07-05-019-21-OS-SW
Jul 5, 2001, 21:30:38
&&
Chicago Sun-Times: Test drive Linux using friendly tryout software
https://linuxtoday.com/news_story.php3?ltsn=2001-07-05-018-21-PS-CY
Jul 5, 2001, 21:00:48
&&
[...]
Example 13-23 opens the file /home/web/headlines/lthead.txt,
and parses it into blocks using the && character sequence as the block
delimiter.
When an inclusion such as the one in Example 13-23 is processed, the
<field> tags are replaced with the field values found within the parsed blocks.
Fields are assigned to <field> tags in the order in which they are found.
As you can see in Example 13-23, you may also specify an alternate
type attribute for an LXP <field>. Valid types in
a parsed inclusion are hidden (this hides the field if there is a value that you wish to
skip over, and not display) and url.
The hidden type is used for a field which you wish to merely skip over. Since
token-delimited files have no identifying name for each block, each field must be processed in the order that is
encountered by LXP in the source file. Therefore, a field can be assigned a type="hidden"
attribute in order to skip it rather than display it, allowing you to display fields that are past it in the file.
The url type is useful in this context when you know that a particular field will
be a URL, as it creates a hyperlink to that URL (with an HTML <a> tag), rather than
just displaying the URL itself. You can set the text of the generated hyperlink to appear as an arbitrary value, other
than just the URL itself (such as the Read More... value used in Example 13-23), by specifying the value of the link attribute
within the <field> tag.
Here is example output of what you would see from LXP, after parsing the mark-up from Example 13-23:
Note: When using an LXP <field type="url"> tag, you can pass non-LXP attributes
such as class, or target, and they will be placed in
the generated <a> tag.
To include an external well-formed XML document, the approach is very similar to the
parsed method. The method attribute may be set to either
XML, RSS, or RDF to
explicitly set the method to XML parsing. Including a src attribute that ends in any of
the .xml, .rss, or .rdf extensions will implicitly invoke
this method as well.
The delimiter attribute in this context sets the name of the element (tag) within
which to look for element fields to parse. For example, most of the relevant fields in an RDF file are contained directly
within the <item> element; for this reason, item
is the default delimiter element. For each delimiting element found, the entire
<include> region will be looped through once.
Like the parsed method, the XML method uses the
generalized <field> tag to display the contents of a field value. In this context,
a field value refers to the character data within a named element (tag) inside the delimiting element. Field values will
be displayed in the order in which they appear in the XML file unless a name attribute is
set within the <field> tag, assigning the name of the element field to output. For
example, a name="title" attribute refers to the character data within
<title> and </title> in the source XML document.
As an example, suppose that you have an XML source document called languages.xml that describes
languages related to PostgreSQL, with the following structure:
In this scheme, notice that each language is described within the <language>
element. To parse such an XML file in the same manner as the RDF example described earlier, set the
delimiter attribute of the <include> tag to
language and the src attribute to
languages.xml. This is demonstrated in Example 13-24.
When processed, the output of Example 13-24 would look like this:
Language Name: C<br />
Language Notes: Built-in language.<br />
<hr />
Language Name: LXP<br />
Language Notes: Web-based content language.<br />
<hr />
Language Name: PL/pgSQL<br />
Language Notes: PostgreSQL procedural language.<br />
<hr />
Example 13-25 demonstrates the display of a simple RDF XML document. This example differs from
Example 13-24 in that it addresses, specifically, an RDF document. As a result, the
delimiter attribute can be omitted, since the default value of item
is appropriate for the RDF schema.
Notice also the use of the lastblock attribute in Example 13-25, which was also
described in the Section called Including Token-Delimited Files" earlier in this chapter. Both the firstblock and lastblock
attributes can also be used with XML, RDF, and RSS files to limit and offset which blocks of data are displayed.
Warning
Remember that any XML document you attempt to include through LXP must be well-formed, or the
parser will fail. XML parse errors should appear in the Apache error log, prefixed with [lxp] XML Parse Error.
To include an external content-type configured within Apache, the <include>
tag can be invoked with either the URI or local method.
Each performs a subrequest to Apache, meaning that the inclusion is processed as if it is a direct request to Apache, with
the output embedded at the location of the <include> tag in the LXP
document.
The difference between these two methods is that the URI method accepts a
src attribute of the form that Apache would literally accept from a web browser, prefixed
with a forward-slash, and beginning at the document root directory of the configured host (e.g.,
/example.php). Alternatively, the local method tells Apache directly
where the file is located on the local filesystem (e.g., /home/web/default/example.php).
Example 13-26 shows an LXP file which includes a PHP script in two ways. Note that
each of these methods goes through Apache, and will thus be reliant on Apache to be properly configured for the requested
content type, and especially in the case of the local method, have the necessary rights
on the directory containing the included script.
Example 13-26. Including other content types
<lxp>
An example PHP script:<br />
<include src="/example.php" method="URI" />
<hr />
The same PHP script, using the local method:<br />
<include src="/home/web/default/example.php" method="local" />
</lxp>
Omitting the method attribute when including a document (specified by the a
src attribute) with a name ending with any of the common PHP extensions
(.php, .php3, and .phtml) results in the method being
implied as local. As of LXP 0.8, however, there is no way to imply the
URI method. You must therefore specify method="URI" to
use the URI method.
The SQL method in LXP offers a great amount of power through direct connectivity to PostgreSQL.
It allows for the embedding of 100% dynamic, database results directly within a web page without the need to call out to a
programming language, create explicit connection or statement programming objects, or even to parse and format the
results.
To use the SQL method, you may either explicitly use the
<include> tag with a method attribute of
SQL, or implicitly define the <include> tag as
using the SQL method by setting the value of the sql attribute to
the SQL statement you wish to execute. In the following example, the SQL method is implied as a result of specifying a
value for the sql attribute:
<include sql="SELECT * FROM pg_database">
Like each of the parsing methods, the <include> tag loops between its opening
<include> and closing </include> tags for
each row returned from a successfully executed SQL query.
When using the SQL inclusion method, the src attribute is
used within the <include> tag to define the database source to connect to. If
this attribute is omitted, LXP will attempt to connect to its persistent database connection, if one exists.
Note: While there exists a single persistent database connection for each Apache
httpd process, the LXP module actually maintains the connection—not Apache.
The format of this connection string will be familiar to anyone who has connected to PostgreSQL through C or PHP.
It is a single, character string, within which there are several sub-attributes describing the data source. Available
sub-attributes are shown in Table 13-2.
Table 13-2. Database Connection Attributes
Attribute
Description
dbname
The database to connect with (defaults to the same name as the connecting user)
host
The hostname to connect to
user
The username to connect with (defaults to the user running Apache)
password
The password to use, if authentication is required
port
The port to connect to (Defaults to 5432)
Within the src attribute's value, attribute pairs are separated by whitespace, and an equal
sign separates each attribute from its value. The order in which the database attributes appear is not
important.
Example 13-27 shows the execution of a SQL query, which uses a connection to a
database called example, on a host named db_server, with the username john.
Example 13-27. Connecting to a non-default database
<lxp>
<include sql="SELECT * FROM users ORDER BY username ASC"
src="dbname=example host=db_server user=john">
User: <field /><br />
</include>
</lxp>
Warning
For LXP 0.8, if you wish to nest a SQL include within another SQL include,
the nested include must have an explicit src attribute defined,
even if it is connecting to the default database connection. This restriction is corrected with LXP 0.8.1.
Column values can be accessed in one of two ways while iterating through a SQL inclusion region; either through
the general <field> tag, or through the this
object, which is populated with a value for each column upon each row iteration.
Like the XML inclusion, a name attribute can be applied to a
<field> tag in order to specify which column is to be displayed. Otherwise, the
column values are displayed in the order they were targeted by the query, from left to right, with each successive use
of the <field> tag.
Alternatively, the values of each column can be accessed by a variable named this.column,
where column is the name of the column to be identified. For example, the following two tags
would output the same value within an included SQL region:
<field name="id" />
<putvar name="this.id" />
The main reason for the existence of the this object is so that branching logic,
and variable substitution, can be performed using the values of the returned SQL result set. Example 13-28 executes a SQL query, and formats its output conditionally through the use of
branching logic.
When executing a SQL query, some special variable values containing data about the current result set are assigned
to an LXP object called sql. These are:
sql.numrows
sql.numcols
sql.numfields (alias to sql.numcols)
sql.row
sql.offset
The sql.numrows variable value contains the number of rows retrieved by the
query. The sql.numcols (and its sql.numfields alias)
variable value contains the number of columns in each row. When looping between
<include> and </include>, the
sql.row variable value contains the numeric index of the current row, counting from
1, while the sql.offset variable value contains the
numeric index of the current row counting from 0.
Example 13-29 uses the the sql.row variable to
display the current row index within the looped <include> region. In addition,
the sql.numrows variable is used after the query results are displayed to show how many
rows were retrieved.
Example 13-29. Using SQL object variable values
<lxp>
<include sql="SELECT * FROM pg_user ORDER BY usename LIMIT 5">
User #<putvar name="sql.row" />: <putvar name="this.usename" /><br />
</include>
<br />
Selected <putvar name="sql.numrows" /> rows.
</lxp>
If you prefer to execute a SQL query only as a means to have access to the result set returned (bypassing the
automatic looping iteration of the <include> tag), you may supply the
setvars attribute with the name of an LXP object to be populated with the query
results, and immediately close the region with a closing </include> tag.
For result sets with a single row returned, this approach sets a variable named
object.column for each
column in the row, where object is the name specified by the setvars
attribute, and column is the name of a column returned by the query. For result sets with
more than a single row, square-brackets containing an offset describing the row number are appended to the column name
(e.g., object.column[0], object.column[1], etc.).
Example 13-30 executes a query on the
pg_user table, to retrieve three columns about a particular user.
Example 13-30. Selecting SQL results into an LXP object
<lxp>
<include sql="SELECT usename, usesuper, usecreatedb
FROM pg_user
WHERE usesysid = $userid"
setvars="userinfo"></include>
<if sql.numrows="1">
User name: <putvar name="userinfo.usename"><br />
<if userinfo.usecreatedb='t'>
<strong>This user can create databases.</strong><br />
</if>
<if userinfo.usesuper='t'>
<strong>This user is a superuser.</strong><br />
</if>
</if>
<else>
Error: No user was found.
</else>
</lxp>