Skip to content

05 add templates

djbpitt edited this page Aug 8, 2023 · 10 revisions

Recap

In Stage 4 you created a controller that passes the model (in this case created by modules/titles.xql) into an XQuery resource that transforms it to the view (in this case views/titles-to-html.xql). The data sources are in the TEI namespace (bound to the prefix tei:), the model is created in a custom model namespace (bound to the prefix m:), and the HTML is created in the HTML namespace (bound to the prefix html:). At the moment the HTML that is returned has a root element of type <section>, which means that it is an HTML fragment, and not a complete HTML document. A complete HTML document requires a root element called <html> with exactly two children, <head> and <body>. The <head> must contain a <title> (the content of which will be rendered in the browser tab, and not in the browser window). The content of the <body> is rendered in the browser window, which means that <section> should be a descendant of <body>.

Goals

In this step you’ll modify the pipeline to transform the HTML fragment into a complete and valid HTML document by adding the missing required elements. You’ll do that by adding a pipeline step to the <view> portion of the controller; not only will the controller pass the model into XQuery that creates an HTML fragment, but it will then pass that fragment into another XQuery resource that transforms the fragment into a complete HTML document by wrapping it in the required HTML superstructure.

The XQuery that creates the original fragment will be different for each type of page that the user can request, so that, for example, modules/titles.xql relies on views/titles-to-html.xql to create the HTML fragment for the list of titles, while the HTML fragments for other views (such as a reading view of an individual source document) will be created by other XQuery resources (e.g., modules/read.xql will communicate with views/read-to-html.xql). This is why the controller constructs the name of the XQuery that generates the view dynamically:

<view>
  <forward url="{concat($exist:controller, '/views/', $exist:path, '-to-html.xql')}"/>
</view>

In the controller line above, the value of $exist:path is automatically set to whatever the user requested in the original URL (e.g., titles), and the controller uses that value to build a path to the URL-specific XQuery that transforms the model to HTML for the view.

Enhancing the controller

The <view> portion of the controller as illustrated above contains a single <forward> element, which forwards the model into the XQuery that creates the HTML <section>. That’s the simplest type of view step, but the <view> portion of the controller can contain multiple <forward> elements, which operate as a pipeline, so that the output of each becomes the input to the next. This means that after creating an HTML <section> you can forward it into XQuery that will create the full HTML wrapper by modifying the controller along the lines of:

<view>
 <forward url="{concat($exist:controller, '/views/', $exist:path, '-to-html.xql')}"/>
 <forward url="{concat($exist:controller, '/views/', 'html-template.xql')}"/>
</view>

With this change the controller knows that instead of returning the output of views/titles-to-html.xql to the user it should forward it as input into views/html-template.xql and return the output of that XQuery to the user. XQuery that creates the view for each page contains page-specific code, which is why the path is constructed dynamically inside the controller in a way that incorporates the page-specific value of the $exist:path variable. The wrapping step is the same for all pages, though. Forwarding to a wrapper XQuery can work only if views/html-template.xql exists, so the next step is to create it.

Creating XQuery to produce a wrapper

Inside the views subdirectory create a new file called html-template.xql with the following content:

xquery version "3.1";

declare namespace tei="http://www.tei-c.org/ns/1.0"; 
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare default element namespace "http://www.w3.org/1999/xhtml";


declare option output:method "xhtml";
declare option output:media-type "application/xhtml+xml";
declare option output:omit-xml-declaration "no";
declare option output:html-version "5.0";
declare option output:indent "no";
declare option output:include-content-type "no";

(:=====
this variable allows the pipeline to work by providing
input for the section created by titles-to-html.xql
=====:)
declare variable $text as document-node() := request:get-data(); 
     
<html>
    <head>
        <title>Hoax</title>
    </head>
    <body>
        <section class="nav-menu">
            <img src="icon.png" width="35" style="margin-right:1em;"/>                     
            <header>
                <h1><a href="index">Hoax: ghosts in 19th-c. British press</a></h1>
            </header>
            <nav>
                <ul>
                    <li><a href="titles">Articles</a></li>   
                </ul>
            </nav>
        </section> 
        <main>{$text}</main>
        <footer>
            <hr/>
            <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img 
                alt="Creative Commons License" 
                style="border-width:0" 
                src="resources/img/cc_license_88x31.png" height="15" width="45"
            /></a> This work is licensed under a 
                <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.
                <br/>Project data and research artifacts are available under an open license on <a href="https://github.com/Pittsburgh-NEH-Institute/pr-app">GitHub</a>.</footer>
    </body>
</html>

Here’s how it works:

Namespaces

  • You start by declaring the TEI namespace. You won’t need the TEI namespace when outputting titles because the titles have already been transformed out of the TEI namespace by the time they reach this stage of the processing. Some other pages will output content in the TEI namespace, though, and not only in the HTML namespace. Stay tuned!
  • Declare a namespace that you bind to the prefix output:. See below for why this is needed.
  • Declare the HTML namespace, bound to the prefix html:, because both the input and the output of this step of the pipeline are normally in the HTML namespace. Note that you don’t declare the model namespace because the first step in the <view> transformed all model elements into HTML elements, which means that this XQuery will never see anything in the model namespace.

Output serialization

You want your output to be HTML5 that uses the XML syntax. The XQuery is creating an XML document that is essentially a tree of nodes, and for output those nodes need to be translated into characters (in XML terms, markup and content). This translation is called serialization, and the option declarations (which use the option: namespace declared above) ensure that the serialization will be recognized by the browser as HTML5 in XML syntax. Specifically:

  • An output:method value of xhtml combined with an output:html-version value of 5.0 tells the eXist-db serializer that it is creating HTML5 that uses the XML syntax.
  • When a web server, such as the jetty server that underlies eXist-db, responds to a request, it includes information about the media type (also called mime type) of whatever it returns. The standard value for this property for HTML5 that uses XML syntax is application/xhtml+xml. This information is passed in a header that is not part of the page content as seen by the user, but the browser sees it and uses it to render the page correctly.
  • The XML declaration at the beginning of an XML document (<?xml version="1.0" encoding="UTF-8"?>) is optional, and the eXist-db serialization of HTML5 with the XML syntax omits it by default. Overriding the default to specify that it should be included confirms for the browser that the document observes XML syntax.
  • Setting the indent property to no tells eXist-db not to pretty-print (format and wrap) the HTML. Pretty-printing makes the raw document easier to read, but under some circumstances it can introduce incorrect white-space handling, which means that turning it off is more robust than leaving it on. (For that reason we often set the value to yes during development and debugging, and then change it to no for deployment.)
  • HTML documents can indicate the character set they use by including an empty <meta charset="utf-8"/> element as a child of <head>. (HTML5 documents that don’t use the XML syntax can also use <meta http-equiv="Content-Type" content="text/html; charset=utf-8">, which isn’t available here because you’re requiring the XML syntax.) This <meta> element is, however, unnecessary if the XML declaration (see above) is present because the XML declaration specifies the character set, which means that it can (= should) be omitted.

Pipeline

The controller passes the output of the preceding pipeline step along as a document, which the new XQuery resource receives with request:get-data() and assigns as the value of a variable called $text.

Content

The XQuery resource constructs a literal HTML document that provides the required wrapper information: the <head> with the obligatory <title> and the <body>. The <body> has three child elements:

  1. A <section> that contains an image, a <header>, and a <nav>. The content of this <section> is specified literally.
  2. A <main> into which it inserts the <section> forwarded from the preceding path step.
  3. A <footer>. The content of this <section> is specified literally.

The header and footer are boilerplate that will appear on every page of the site. For the moment the page has no styling, and you’ll add links to CSS to control the styling later.

Learning more about HTML

Creating valid HTML output assumes knowledge of the HTML resources you need. If you are new to HTML or would like a refresher:

  1. For simple and accessible tutorials see W3Schools HTML Introduction and HTML basic examples.
  2. For more detailed (that is, less simplified) information see HTML: HyperText Markup Language (and associated links) at the Mozilla Development Network (MDN).
  3. HTML5 is called a “living standard”, which means that is subject to constant change and, unlike most standards, does not have a stable definition bound to a version number. The specification is available at HTML living standard.
  4. All major browsers aim to implement the HTML5 specification, but because the specification is unstable, the support for newer features is unpredictable. The Can I use site aims to track browser support for HTML and other web specifications.

The resources above are written primarily for HTML that uses the HTML (not XML) syntax. This means that some examples may need to be modified to be used with the stricter XML syntax.

Summary

In this stage you added a new HTML template to your views directory and updated your controller to incorporate the template transformation into the processing pipeline. This means that instead of sending back HTML fragments, you send a well-formed HTML document back to the user's browser. In the next stage you’ll add a default home page so that users can begin navigating directly within the app interface.

The code for 05-add-templates can be found at https://github.com/Pittsburgh-NEH-Institute/hoaXed/tree/05-add-templates.