oowebtools

oowebtools v1.3.0 is an object-oriented extension for constructing website generators. The package consists of classes that model and generate HTML entities. Output is highly configurable but often requires only minimal user input as well-chosen defaults cover many typical situations. At present, 70 tags and 20 composite/high-level web widgets and utilities are implemented as TclOO classes. The toolkit is designed so that users can produce web pages without directly using TclOO features.

oowebtools is the successor to jwebtools. The new toolkit is thoroughly revised. Compared to the prior extension, oowebtools has much cleaner syntax and considerably easier to use.

Availability

Fossil repo: https://test.org/fossil/oowt/index
Download: https://test.org/fossil/oowt/download
Documentation: https://thinairarts.com/fossil/oowt/file?name=doc/oowebtools-doc.html&ci=tip

The toolkit was built with Tcl 8.7 and 9.0 on Linux and Windows. With some effort it probably can be made to run with prior Tcl versions.

Installing

After downloading in tar.gz or .zip format, extract in a suitable location. On Linux and Windows simply copy the oowebtools directory from the appropriate unix/tcl(8|9) or win/tcl(8|9) archive directory to a location on Tcl's package path. On unix systems that's often /usr/lib or /usr/local/lib.

The extension can be installed to the lib directories of independently installed Tcl versions. On systems with multiple Tcl installations, modifying the Makefile to include Tcl lib directories enables installing oowebtools to several locations in one go.

Dependencies

oowebtools depends on these Tcl packages:

  • nxproc 1.0 or higher
  • tcllib 2.0 or higher
  • sqlite3 3.44 or higher

These are available from various resources. Given the recent Tcl 9.0b1 release, tcllib2.0 and latest sqlite3 will likely become more widely available in the near future.

Basic features

The core output product of oowebtools is generated HTML, most often incorporating template elements such as Tcl variables, procs/methods, etc. Classes are organized in a shallow hierarchy, at the top is the abstract Element class which provides the fundamental structure and functionality for all HTML tags.

The most frequently used method in user code is write. Every Element derived tag object knows how to write itself according to configuration data supplied by the programmer. In practice, write is called recursively beginning with the Html at the top of the page entity hierarchy.

A simple example:

    proc mkHtml {} {
        Html: --> [subst {
            [Head: -title "Example page" \
                   -descrip "An oowebtools example" --> [subst {
                [Preload: --> {href /js/example.js as script}]
                [Preload: --> {href /css/example.css as style}]
                [Link: --> {href /css/example.css rel stylesheet 
                            type text/css}]
            }]]
            [Body: --> [subst {
                [H1: --> "Example Web Page"]
                [Div: --> {class main} [subst {
                    [Div: --> {class msgdiv} \
                        {This is the message.}]
                    [Div: --> {id imgdiv} [subst {
                        [Img: --> {src /img/big-country.jpg 
                                   alt "Wide open fields"}]
                    }]]
                }]]
            }]]
        }]
    }

Produces this output:

    % set page0 [mkHtml]

    % page0 write ;# ...prints to stdout:

    <!DOCTYPE html>
    <html lang="EN-US">
      <head>
        <title>Example page</title>
        <meta name="description" 
              content="An oowebtools example">    
        <meta charset="UTF-8">
        <meta name="viewport" 
              content="width=device-width, initial-scale=1">
        <link href="/js/example.js" as="script" rel="preload">
        <link href="/css/example.css" as="style" rel="preload">
        <link href="/css/example.css" rel="stylesheet" 
              type="text/css">      
      </head>
      <body>
        <h1>
          Example Web Page
        </h1>
        <div class="main">
          <div class="msgdiv">
           This is the message.
          </div>
          <div id="imgdiv">
            <img src="/img/big-country.jpg" 
                 alt="Wide open fields">
          </div>
        </div>
      </body>
    </html>

How it works

Syntax and use

The HTML tag syntax is uncomplicated.

    <TAG> (:| new) ?<fixed-arg ...>? ?<named-arg ?type? value ...>? 
        ??(-->|->|--) ?{attribute value ...}? ?{text or element list}??

In the example, Head has two named args for which values were given. Preload and Link have no end tag so only an attribute list is accepted. Among the Div entities there's an instance where an attribute list and element list are specified, another with attributes and text, and one with only an element list.

The tags in the example are written with ':' as the construction operator. (The ':' is a kind of alias for ' new'.) Either way it creates a tag object which can call its methods, in this case, write. A number of tags are automatically generated by default. Of course if other values are preferred, it's under complete control of the programmer

The --> symbol is an nxproc command feature. It's like the Tcl convention where a "double-dash" (--) signals the end of arguments. Everything to the right of --> is collected as a list in the variable nxargs, accessible in the procedure body. The value of the variable is parsed into one or more values, that is, an attribute list, a text string, or list of element object commands.

All oowebtools tag constructors are written using nxconstructor which has the same features as nxproc. Visit the nxproc wiki page or fossil repo for more info on using nxproc.

An HTML structure is essentially a set of nested lists well-suited to Tcl creation and processing. Lists can be formed with [list ...] but in elaborate pages list tends to get confusing (due to proliferation of '\' line continuations). Alternatively, using {} notation and [subst ...] commands is slightly wordier but page structure is easier to follow so is generally preferred.

Features

Defined HTML tags and other entity classes

At present these tags are implemented:

    A Article B Bdi Blockquote Body Button Canvas Code Datalist Del Details Div
    Em Fieldset Figure Figcaption Footer Form H1 H2 H3 H4 H5 H6 Header Iframe 
    Ins Legend Li Main Map Meter Nav Ol Optgroup P Picture Pre Select Span 
    Summary Sub Sup Textarea U Ul Area Base Embed Hr Img Input Option Preload 
    Style Script ScriptSrc Link Description Charset Meta Charset Viewport Title 
    Head Html Label Audio Video Source

These composite entities (and supporting classes) are implemented:

    Blist Bsrc Bsrcstub CommentDb CommDiv CommScript CommentRecv Component 
    Contact ContactRecv Dynpage Menu ooWTserv Slider Smtp Srcset WebImg

Utility classes/commands:

    Bool Config Log Resource Util Stack

In addition to the oowebtools directory, the repo also contains directories with essential javascript, css, database and utility files. The css and js resources are basic, but provide a working set of functions and values.

When sourced (via package require oowebtools) all class objects are available for immediate use. Each has its function in the toolkit, some more peripheral than others. Documentation of each class is in progress regarding startup configuration options, methods available and option signatures. The source code is intended to be comprehensible to humans as to how the class can be used.

Examples

Presenting a few examples of using the toolkit's composite objects.

Toolkit's web server

The webserver (class object is ooWtserv) is very easy to run:

    $ /usr/local/Tcl/tcl90b1/bin/tclsh9.0
    % package require oowebtools
    % source localconfig.tcl
    % server

Assuming localconfig.tcl points to the local web root directory, the website will be available in a browser at "localhost:8181/index.html".

However, a more "real-world" idea is running the web server as a backend proxy under nginx. Here is an example nginx configuration:

        server {
        ...
        root /usr/local/prog/Projects/oohgen3;
        ...

                location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
                        expires 365d;
                }

            location / {
                        index index.html;
            # @proxy handles requests for non-static pages 
                try_files $uri $uri/ @proxy;
                add_header Cache-Control public;
                add_header Pragma public;
                add_header Vary Accept-Encoding;
                        expires 365d;
                }

        # oowebtools backend server running as @proxy
                location @proxy {
                    proxy_pass http://localhost:8181;
                proxy_set_header Host $host;
                    proxy_set_header X-Remote-IP $remote_addr;
                        proxy_set_header X-Remote-Port $remote_port;
                        proxy_set_header X-Remote-User $remote_user;
                        proxy_set_header X-Path $uri;
                        proxy_set_header X-Qstr $args;
                        proxy_set_header Connection $http_connection;
                }
        }

Displaying images

The toolkit offers the WebImg object which encapsulates the HTML figure entity while providing a number of convenience features. WebImg automatically adapts the image dimensions, however, size and placement are under the control of the programmer. There are many configuration options divided into HTML attributes and separate CSS styling parameters.

    set ht 18em
    set fdcss [open "mypage-img.css" w]
    ...
    set img0 [WebImg: -src /img/sunset-532x398.webp \
            -srcset {/img/sunset-266x199.webp 266w,
                    /img/sunset-532x398.webp 532w,
                    /img/sunset-798x597.webp 798w} \
            -sizes {(min-width: 625px) 40vw, (min-width: 1000px) 30vw, 55vw} \
            -hres 798 -vres 597 \
            -alt "Photo, dramatic sunset" \
            -caption {Sam Jones, <em>Sunset Near Home</em>, photograph, 2023} ]
    $img0 css -padding "0.25em 0 0 0" -margin ".5em 0 .25em 1em" \
            -height [- $ht 1]em
    $img0 writecss $fdcss

There's no set limit to the number of WebImgs that can a page can hold. In any case the comprehensive set of HTML and CSS options accommodate a tremendous range of layout possibilities.

To assist the programmer, the Srcset object will configure and write out the -src, -srcset, and -sizes parameters for WebImg as well as assisting with other image management tasks.

Email with the Smtp object

The Smtp object is designed to make it easy to handle emailing from a running server, for example, to notify site admins of an event requiring attention. With little effort it also works very well as a freestanding mailer.

Notifications to admins will be sent by default when using the Contact page is included in a site or using the provided comment system. Setting up as mailer utility is quite simple. This is for unix systems:

    #!/usr/bin/env /usr/local/Tcl/tcl90b1/bin/tclsh9.0

    package require oowebtools

    namespace import tcl::mathop::*
    namespace import tcl::mathfunc::*

    source localconfig.tcl

    Log0 loginit
    Smtp create smtp0

    try {
        if {[smtp0 send {*}$argv] == 0} {
            smtp0 logwrite --> 'smtp0 send' SUCCEEDED.
        }
    } on error {e} {
        smtp0 logwrite --> 'smtp0 send $argv' ERROR: $e
        exit 1
    }

Save the script as hsmtp. After modifying paths in localconfig.tcl to match the user's system, sending an email only requires that the message is in the right form. That is contains a series of headers that are parsed to extract sender and recipient information:

    From: The Sender <[email protected]>
    To: Recipient #1 <[email protected]>, Recipient #2 <[email protected]>
    Subject: My great message!
    Date: Mon 29 Jan 2024 03:52:04 -08:00

    Hello there,
    ...

To send email from the command line, prepare the message in the above format (Markdown is highly recommended) and output as an .html file.

    $ hsmtp -htmlfile recipients1-and-2.html
    ---[2024-01-29 03:58:38.809] (OO) Message sent TO: <[email protected]>,<[email protected]> SUBJ: My great message!
    ---[2024-01-29 03:58:38.810] 'smtp0 send' SUCCEEDED.

Sender and recipient addresses can also be entered manually (with -to and -from options) if needed or desired.

2024-01-29 jrapdx