- Documentation
- Reference manual
- Packages
- SWI-Prolog HTTP support
- The HTTP server libraries
- Creating an HTTP reply
- library(http/http_dispatch): Dispatch requests in the HTTP server
- library(http/http_dirindex): HTTP directory listings
- library(http/http_files): Serve plain files from a hierarchy
- library(http/http_session): HTTP Session management
- library(http/http_cors): Enable CORS: Cross-Origin Resource Sharing
- library(http/http_authenticate): Authenticate HTTP connections using 401 headers
- library(http/http_digest): HTTP Digest authentication
- library(http/http_dyn_workers): Dynamically schedule HTTP workers.
- Custom Error Pages
- library(http/http_openid): OpenID consumer and server library
- Get parameters from HTML forms
- Request format
- Running the server
- The wrapper library
- library(http/http_host): Obtain public server location
- library(http/http_log): HTTP Logging module
- library(http/http_server_health): HTTP Server health statistics
- Debugging HTTP servers
- library(http/http_header): Handling HTTP headers
- The library(http/html_write) library
- library(http/js_write): Utilities for including JavaScript
- library(http/http_path): Abstract specification of HTTP server locations
- library(http/html_head): Automatic inclusion of CSS and scripts links
- library(http/http_pwp): Serve PWP pages through the HTTP server
- The HTTP server libraries
- SWI-Prolog HTTP support
3.21 The library(http/html_write)
library
Producing output for the web in the form of an HTML document is a requirement for many Prolog programs. Just using format/2 is not satisfactory as it leads to poorly readable programs generating poor HTML. This library is based on using DCG rules.
The library(http/html_write)
structures the generation
of HTML from a program. It is an extensible library, providing a DCG
framework for generating legal HTML under (Prolog) program control. It
is especially useful for the generation of structured pages (e.g. tables)
from Prolog data structures.
The normal way to use this library is through the DCG html//1. This non-terminal provides the central translation from a structured term with embedded calls to additional translation rules to a list of atoms that can then be printed using print_html/[1,2].
- html(:Spec)
//
- The DCG non-terminal html//1 is the main predicate of this library. It
translates the specification for an HTML page into a list of atoms that
can be written to a stream using print_html/[1,2].
The expansion rules of this predicate may be extended by defining the
multifile DCG html_write:expand//1. Spec is either a single
specification or a list of single specifications. Using nested lists is
not allowed to avoid ambiguity caused by the atom
[]
- Atomic data
Atomic data is quoted using html_quoted//1. - Fmt - Args
Fmt and Args are used as format-specification and argument list to format/3. The result is quoted and added to the output list. \
List
Escape sequence to add atoms directly to the output list. This can be used to embed external HTML code or emit script output. List is a list of the following terms:- Fmt - Args
Fmt and Args are used as format-specification and argument list to format/3. The result is added to the output list. - Atomic
Atomic values are added directly to the output list.
- Fmt - Args
\
Term
Invoke the non-terminal Term in the calling module. This is the common mechanism to realise abstraction and modularisation in generating HTML.- Module:Term
Invoke the non-terminal <Module>:<Term>. This is similar to\
Term but allows for invoking grammar rules in external packages. - &(Entity)
Emit&<Entity>;
or&#<Entity>;
if Entity is an integer. SWI-Prolog atoms and strings are represented as Unicode. Explicit use of this construct is rarely needed because code-points that are not supported by the output encoding are automatically converted into character-entities. Tag(Content)
Emit HTML element Tag using Content and no attributes. Content is handed to html//1. See section 3.21.4 for details on the automatically generated layout.Tag(Attributes, Content)
Emit HTML element Tag using Attributes and Content. Attributes is either a single attribute of a list of attributes. Each attributes is of the formatName(Value)
or Name=Value. Value is the atomic attribute value but allows for a limited functional notation:- A + B
Concatenation of A and B - Format-Arguments
Use format/3 and emit the result as quoted value. encode(Atom)
Use uri_encoded/3 to create a valid URL query component.location_by_id(ID)
HTTP location of the HTTP handler with given ID. See http_location_by_id/2.#
(ID)
Abbreviated for forlocation_by_id(ID)
.- A + List
List is handled as a URL‘search’component. The list members are terms of the format Name = Value orName(Value)
. Values are encoded as in the encode option described above. - List
Emit SGML multi-valued attributes (e.g.,NAMES
). Each value in list is separated by a space. This is particularly useful for setting multipleclass
attributes on an element. For example:... span(class([c1,c2]), ...),
The example below generates a URL that references the predicate set_lang/1 in the application with given parameters. The http_handler/3 declaration binds
/setlang
to the predicate set_lang/1 for which we provide a very simple implementation. The code between...
is part of an HTML page showing the english flag which, when pressed, callsset_lang(Request)
where Request contains the search parameterlang
=en
. Note that the HTTP location (path)/setlang
can be moved without affecting this code.:- http_handler('/setlang', set_lang, []). set_lang(Request) :- http_parameters(Request, [ lang(Lang, []) ]), http_session_retractall(lang(_)), http_session_assert(lang(Lang)), reply_html_page(title('Switched language'), p(['Switch language to ', Lang])). ... html(a(href(location_by_id(set_lang) + [lang(en)]), img(src('/www/images/flags/en.png')))), ...
- A + B
- Atomic data
- page(:HeadContent,
:BodyContent)
//
- The DCG non-terminal page//2 generated a complete page, including the
SGML
DOCTYPE
declaration. HeadContent are elements to be placed in thehead
element and BodyContent are elements to be placed in thebody
element.To achieve common style (background, page header and footer), it is possible to define DCG non-terminals head//1 and/or body//1. Non-terminal page//1 checks for the definition of these non-terminals in the module it is called from as well as in the
user
module. If no definition is found, it creates a head with only the HeadContent (note that thetitle
is obligatory) and abody
withbgcolor
set towhite
and the provided BodyContent.Note that further customisation is easily achieved using html//1 directly as page//2 is (besides handling the hooks) defined as:
page(Head, Body) --> html([ \['<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 4.0//EN">\n'], html([ head(Head), body(bgcolor(white), Body) ]) ]).
- page(:Contents)
//
- This version of the page/[1,2]
only gives you the SGML
DOCTYPE
and theHTML
element. Contents is used to generate both the head and body of the page. - html_begin(+Begin)
//
- Just open the given element. Begin is either an atom or a
compound term, In the latter case the arguments are used as arguments to
the begin-tag. Some examples:
html_begin(table) html_begin(table(border(2), align(center)))
This predicate provides an alternative to using the
\
Command syntax in the html//1 specification. The following two fragments are the same. The preferred solution depends on your preferences as well as whether the specification is generated or entered by the programmer.table(Rows) --> html(table([border(1), align(center), width('80%')], [ \table_header, \table_rows(Rows) ])). % or table(Rows) --> html_begin(table(border(1), align(center), width('80%'))), table_header, table_rows, html_end(table).
- html_end(+End)
//
- End an element. See html_begin/1 for details.
3.21.1 Emitting HTML documents
The non-terminal html//1 translates a specification into a list of
atoms and layout instructions. Currently the layout instructions are
terms of the format nl(N)
, requesting at least N
newlines. Multiple consecutive nl(1)
terms are combined to
an atom containing the maximum of the requested number of newline
characters.
To simplify handing the data to a client or storing it into a file, the following predicates are available from this library:
- reply_html_page(:Head, :Body)
- Same as
reply_html_page(default, Head, Body)
. - reply_html_page(+Style, :Head, :Body)
- Writes an HTML page preceded by an HTTP header as required by
library(http_wrapper)
(CGI-style). Here is a simple typical example:reply(Request) :- reply_html_page(title('Welcome'), [ h1('Welcome'), p('Welcome to our ...') ]).
The header and footer of the page can be hooked using the grammar-rules user:head//2 and user:body//2. The first argument passed to these hooks is the Style argument of reply_html_page/3 and the second is the 2nd (for head//2) or 3rd (for body//2) argument of reply_html_page/3. These hooks can be used to restyle the page, typically by embedding the real body content in a
div
. E.g., the following code provides a menu on top of each page of that is identified using the style myapp.:- multifile user:body//2. user:body(myapp, Body) --> html(body([ div(id(top), \application_menu), div(id(content), Body) ])).
Redefining the
head
can be used to pull in scripts, but typically html_requires//1 provides a more modular approach for pulling scripts and CSS-files. - reply_html_partial(+HTML)
- Reply with partial HTML document. The reply only contains the element
from HTML, i.e., this predicate does not add a
DOCTYPE
header,html
,head
orbody
. It is intended for JavaScript handlers that request a partial document and insert that somewhere into the existing page DOM. See reply_html_page/3 to reply with a complete (valid) HTML page. - print_html(+List)
- Print the token list to the Prolog current output stream.
- print_html(+Stream, +List)
- Print the token list to the specified output stream
- html_print_length(+List, -Length)
- When calling html_print/[1,2]
on List, Length characters will be produced.
Knowing the length is needed to provide the
Content-length
field of an HTTP reply-header.
3.21.2 Repositioning HTML for CSS and javascript links
Modern HTML commonly uses CSS and Javascript. This requires <link> elements in the HTML <head> element or <script> elements in the <body>. Unfortunately this seriously harms re-using HTML DCG rules as components as each of these components may rely on their own style sheets or JavaScript code. We added a‘mailing’system to reposition and collect fragments of HTML. This is implemented by html_post//2, html_receive//1 and html_receive//2.
- [det]html_post(+Id,
:HTML)
//
- Reposition HTML to the receiving Id. The html_post//2
call processes HTML using html//1.
Embedded
\
-commands are executed by mailman/1 from print_html/1 or html_print_length/2. These commands are called in the calling context of the html_post//2 call.A typical usage scenario is to get required CSS links in the document head in a reusable fashion. First, we define css//1 as:
css(URL) --> html_post(css, link([ type('text/css'), rel('stylesheet'), href(URL) ])).
Next we insert the unique CSS links, in the pagehead using the following call to reply_html_page/2:
reply_html_page([ title(...), \html_receive(css) ], ...)
- [det]html_receive(+Id)
//
- Receive posted HTML tokens. Unique sequences of tokens posted with html_post//2
are inserted at the location where
html_receive//1 appears.
- See also
- - The local predicate sorted_html//1
handles the output of
html_receive//1.
- html_receive//2 allows for post-processing the posted material.
- [det]html_receive(+Id,
:Handler)
//
- This extended version of html_receive//1
causes Handler to be called to process all messages posted to
the channal at the time output is generated. Handler is
called as below, where
PostedTerms is a list of Module:Term created from calls to
html_post//2. Module is the
context module of html_post and Term is the unmodified term. Members in PostedTerms
are in the order posted and may contain duplicates.
phrase(Handler, PostedTerms, HtmlTerms, Rest)
Typically, Handler collects the posted terms, creating a term suitable for html//1 and finally calls html//1.
The library predefines the receiver channel head
at the
end of the
head
element for all pages that write the html head
through this library. The following code can be used anywhere inside an
HTML generating rule to demand a javascript in the header:
js_script(URL) --> html_post(head, script([ src(URL), type('text/javascript') ], [])).
This mechanism is also exploited to add XML namespace (xmlns
)
declarations to the (outer) html
element using xhml_ns//2:
- xhtml_ns(+Id,
+Value)
//
- Demand an xmlns:id=Value in the outer html tag. This uses the
html_post/2 mechanism to post to the
xmlns
channel. Rdfa (http://www.w3.org/2006/07/SWD/RDFa/syntax/), embedding RDF in (x)html provides a typical usage scenario where we want to publish the required namespaces in the header. We can define:rdf_ns(Id) --> { rdf_global_id(Id:'', Value) }, xhtml_ns(Id, Value).
After which we can use rdf_ns//1 as a normal rule in html//1 to publish namespaces from
library(semweb/rdf_db)
. Note that this macro only has effect if the dialect is set toxhtml
. Inhtml
mode it is silently ignored.The required
xmlns
receiver is installed by html_begin//1 using thehtml
tag and thus is present in any document that opens the outerhtml
environment through this library.
3.21.3 Adding rules for html//1
In some cases it is practical to extend the translations imposed by
html//1. We used this technique to define translation rules for the
output of the SWI-Prolog library(sgml)
package.
The html//1 non-terminal first calls the multifile ruleset html_write:expand//1.
- html_write:expand(+Spec)
//
- Hook to add additional translation rules for html//1.
- html_quoted(+Atom)
//
- Emit the text in Atom, inserting entity-references for the
SGML special characters
<&>
. - html_quoted_attribute(+Atom)
//
- Emit the text in Atom suitable for use as an SGML attribute,
inserting entity-references for the SGML special characters
<&>"
.
3.21.4 Generating layout
Though not strictly necessary, the library attempts to generate reasonable layout in SGML output. It does this only by inserting newlines before and after tags. It does this on the basis of the multifile predicate html_write:layout/3
- html_write:layout(+Tag, -Open, -Close)
- Specify the layout conventions for the element Tag, which is
a lowercase atom. Open is a term Pre-Post.
It defines that the element should have at least Pre newline
characters before and Post after the tag. The Close
specification is similar, but in addition allows for the atom
, requesting the output generator to omit the close-tag altogether or-
empty
, telling the library that the element has declared empty content. In this case the close-tag is not emitted either, but in addition html//1 interprets Arg inTag(Arg)
as a list of attributes rather than the content.A tag that does not appear in this table is emitted without additional layout. See also print_html/[1,2]. Please consult the library source for examples.
3.21.5 Examples for using the HTML write library
In the following example we will generate a table of Prolog predicates we find from the SWI-Prolog help system based on a keyword. The primary database is defined by the predicate predicate/5 We will make hyperlinks for the predicates pointing to their documentation.
html_apropos(Kwd) :- findall(Pred, apropos_predicate(Kwd, Pred), Matches), phrase(apropos_page(Kwd, Matches), Tokens), print_html(Tokens). % emit page with title, header and table of matches apropos_page(Kwd, Matches) --> page([ title(['Predicates for ', Kwd]) ], [ h2(align(center), ['Predicates for ', Kwd]), table([ align(center), border(1), width('80%') ], [ tr([ th('Predicate'), th('Summary') ]) | \apropos_rows(Matches) ]) ]). % emit the rows for the body of the table. apropos_rows([]) --> []. apropos_rows([pred(Name, Arity, Summary)|T]) --> html([ tr([ td(\predref(Name/Arity)), td(em(Summary)) ]) ]), apropos_rows(T). % predref(Name/Arity) % % Emit Name/Arity as a hyperlink to % % /cgi-bin/plman?name=Name&arity=Arity % % we must do form-encoding for the name as it may contain illegal % characters. www_form_encode/2 is defined in library(url). predref(Name/Arity) --> { www_form_encode(Name, Encoded), sformat(Href, '/cgi-bin/plman?name=~w&arity=~w', [Encoded, Arity]) }, html(a(href(Href), [Name, /, Arity])). % Find predicates from a keyword. '$apropos_match' is an internal % undocumented predicate. apropos_predicate(Pattern, pred(Name, Arity, Summary)) :- predicate(Name, Arity, Summary, _, _), ( '$apropos_match'(Pattern, Name) -> true ; '$apropos_match'(Pattern, Summary) ).
3.21.6 Remarks
on the library(http/html_write)
library
This library is the result of various attempts to reach at a more satisfactory and Prolog-minded way to produce HTML text from a program. We have been using Prolog for the generation of web pages in a number of projects. Just using format/2 never was not a real option, generating error-prone HTML from clumsy syntax. We started with a layer on top of format/2, keeping track of the current nesting and thus always capable of properly closing the environment.
DCG based translation however, naturally exploits Prolog's term-rewriting primitives. If generation fails for whatever reason it is easy to produce an alternative document (for example holding an error message).
In a future version we will probably define a goal_expansion/2
to do compile-time optimisation of the library. Quotation of known text
and invocation of sub-rules using the \
RuleSet
and
<Module>:<RuleSet> operators are
costly operations in the analysis that can be done at compile-time.