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
.lxp extension ending src attribute
Processes the source file through mod_lxp
Unrecognized extension in src attribute, and no sql or query attribute
Displays a file's literal contents
Parses a token-delimited file, and breaks it up into accessible <field> values
.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
.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
Displays output of an Apache subrequest with a src attribute describing an HTTP URI
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! (firstname.lastname@example.org) && LinuxProgramming: python-dev summary 2001-06-21 - 2001-07-05 http://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 http://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.
Example 13-23. Including a token-delimited file
<lxp> <include src="/home/web/headlines/lthead.txt" delimiter="&&" firstblock="2" lastblock="4" method="parsed"> <table border="0" cellspacing="1"><tr> <td bgcolor="#ffffff" width="100%"> <div class="content"> - <field /> </div> </td> </tr><tr> <td bgcolor="#e0e0e8" width="100%"> <strong> <field type="url" link="Read More..." target="_blank" /> </strong><br /> </td> </tr></table> </include> </lxp>
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:
<table border="0" cellspacing="1"><tr> <td bgcolor="#ffffff" width="100%"> <div class="content"> - LinuxProgramming: python-dev summary 2001-06-21 - 2001-07-05 </div> </td> </tr><tr> <td bgcolor="#e0e0e8" width="100%"> <strong> <a href="http://linuxtoday.com/news_story.php3?ltsn=2001-07-05-019-21-OS-SW" target="_blank">Read More...</a> </strong><br /> </td> </tr></table> <table border="0" cellspacing="1"><tr> <td bgcolor="#ffffff" width="100%"> <div class="content"> - Chicago Sun-Times: Test drive Linux using friendly tryout software </div> </td> </tr><tr> <td bgcolor="#e0e0e8" width="100%"> <strong> <a href="http://linuxtoday.com/news_story.php3?ltsn=2001-07-05-018-21-PS-CY" target="_blank">Read More...</a> </strong><br /> </td> </tr></table> [...]
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:
<?xml version="1.0" encoding="utf-8"?> <languages> <language> <name>C</name> <notes>Built-in language.</notes> </language> <language> <name>LXP</name> <notes>Web-based content language.</notes> </language> <language> <name>PL/pgSQL</name> <notes>PostgreSQL procedural language.</notes> </language> </languages>
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.
Example 13-24. Including an XML file
<lxp> <include src="languages.xml" delimiter="language" method="xml"> Language Name: <field name="name" /><br /> Language Notes: <field name="notes" /><br /> <hr /> </include> </lxp>
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.
Example 13-25. Including an RDF file
<lxp> <include src="/home/web/ports/headlines/slashdot.rdf" lastblock="5"> <table border="0" cellspacing="1"><tr> <td bgcolor="#ffffff" width="100%"> <div class="content">- <field name="title"></div> </td> </tr><tr> <td bgcolor="#e0e0e8" width="100%"> <strong> <field name="link" type="url" link="Read More..." target="_blank"> </strong><br /> </td> </tr></table> </include> </lxp>
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.
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
The database to connect with (defaults to the same name as the connecting user)
The hostname to connect to
The username to connect with (defaults to the user running Apache)
The password to use, if authentication is required
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>
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.numfields (alias to sql.numcols)
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>
The output of Example 13-29 would look like this:
User #1: allen<br /> User #2: barbara<br /> User #3: ben<br /> User #4: corwin<br /> User #5: david<br /> <br /> Selected 5 rows.
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, object.column, 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>