Part 10 - A nice but generic start page

Introduction and requirements

As promised in the previous part of our tutorial we will now improve the look as well as the functionality of our start page a little bit. Therefore we will

And as always, you can download the current code of the application here.

Playing with Bootstrap

To improve the look of our application's start page we are going to implement some more element's and features provided by bootstrap.

Corousel

To present one or more cover images in some kind of slideshow, we use Bootstrap's Carousel feature. For this we have to rework our current start page pages/index.html which looks as follows

<div data-template="templates:surround" data-template-with="templates/page.html" data-template-at="content">
    <div class="row">
        <div class="col-md-8">
            <div class="page-header">
                <h1 data-template="config:app-title">Generated page</h1>
            </div>
            <div class="alert alert-success">
                <p>
                    <a data-template="templates:load-source" href="index.html">This</a> is the entry page into your application 
                and was generated by eXide. It uses HTML templates for a clean separation of HTML views and application logic.</p>
                <p>To add your own template functions, start by editing the XQuery module
                <a data-template="templates:load-source" href="modules/app.xql">app.xql</a>.</p>
            </div>
            <div class="row">
                <div class="col-md-6">
                    <p>The page template uses the <a href="http://twitter.github.com/bootstrap/">Bootstrap</a>
                    CSS library for the page layout.</p>
                </div>
                <div class="col-md-6">
                    <div data-template="app:test"/>
                </div>
            </div>
        </div>
        <div class="col-md-4">
            <h2>Application Info</h2>
            <div data-template="config:app-info"/>
        </div>
    </div>
</div>

The new pages/index.html should look like this.

<div data-template="templates:surround" data-template-with="templates/page.html" data-template-at="content">
    <div id="myCarousel" class="carousel slide" data-ride="carousel">
    <!-- Indicators -->
        <ol class="carousel-indicators">
            <li data-target="#myCarousel" data-slide-to="0" class="active"/>
            <li data-target="#myCarousel" data-slide-to="1"/>
        </ol>
        <!-- Wrapper for slides -->
        <div class="carousel-inner" role="listbox">
            <div class="item active">
                <img src="$app-root/resources/img/ACDH_Panorama_Postkarte_motiv01.jpg" alt="First" width="1200" height="600" title="by Sandra Lehecka"/>
                <div class="carousel-caption"/>
            </div>
            <div class="item">
                <img src="$app-root/resources/img/ACDH_Panorama_Postkarte_motiv02.jpg" alt="a nice feature picture" width="1200" height="600" title="by Sandra Lehecka"/>
                <div class="carousel-caption"/>
            </div>
        </div>
        <!-- Left and right controls -->
        <a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
        <!--<span class="sr-only">Previous</span>--></a>
        <a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
        <!--<span class="sr-only">Next</span>--></a>
    </div>
</div>

In case you are interested in the exact meaning and usage of these elements please refere to Bootstrap's website or go through the w3schools.com bootstrap tutorial. Anyhow, our modification of the pages/index.html shows some results. Although one could debate if this kind of change so far could seriously be considered as improvement.

image alt text

As unpleasant the current result is, as predictable it was since the <img> elements reference images which do not (yet) exist under the used paths ($app-root/resources/img/ACDHPanoramaPostkarte_motiv02.jpg). But this is fixed quickly by first creating a img directory in resources and then uploading images into /resources/img/. For this tutorial I will use two images created by Sandra Lehecka but of course feel free to use your own material. Just make sure that the file names of the images match those referenced in the img element by the src attribute.

image alt text

If you want you can add some descriptions/credits/headlines to those images in the so far empty <div class="carousel-caption"/>. In case you want give credits to some images found on wikipedia you could add something like this

<div class="item active">
    <img src="$app-root/resources/img/1200px-Old_book_bindings.jpg" alt="First" width="1200" height="600"/>
    <div class="carousel-caption">
        <p>
            Von <a href="//commons.wikimedia.org/wiki/User:Brighterorange" title="User:Brighterorange">Tom Murphy VII</a> - <span class="int-own-work" lang="de">Eigenes Werk</span>, 
            <a href="http://creativecommons.org/licenses/by-sa/3.0/" title="Creative Commons Attribution-Share Alike 3.0">CC BY-SA 3.0</a>, 
            <a href="https://commons.wikimedia.org/w/index.php?curid=295698">https://commons.wikimedia.org/w/index.php?curid=295698</a>
        </p>
    </div>
</div>

The result looks as depicted below

image alt text

As you can see the readability/visibility of such texts highly depends on the images you are using. So it may need some fiddling and playing with all kind of CSS settings to achieve good results. For our thun-demo app I will remove the content of <div calss="carousel-caption">. Especially as I already credited Laura by the usage of a title="by Sandra Lehecka" attribute in the <img> element.

But some text on the page.

After we covered the images, let's add some text to the start page. But since we don't want to repeat ourself and write down the same things (e.g. title of the application, short description) more than once, we will fetch those texts from the repo.xml. As you might remember from Part 6 this document which was created by eXist-db Deployment Editor stores (or can store) besides other data also information regarding the application's description or its author. Interesting though is that this file does not provide any title-fields. But this information can be found in expath-pgk.xml. So what we have to do now, is to fetch this info and present it in pages/index.html.

Some default variables

The kind people from eXist-db already provided two general variable which refer to the root elements of repo.xml and expath-pkg.xml. Those variables are declared in modules/config.xqm:

declare namespace repo="http://exist-db.org/xquery/repo";
declare namespace expath="http://expath.org/ns/pkg";
...
declare variable $config:repo-descriptor := doc(concat($config:app-root, "/repo.xml"))/repo:meta;
declare variable $config:expath-descriptor := doc(concat($config:app-root, "/expath-pkg.xml"))/expath:package;

In the same document we find also the quite promising variable/function called config:app-title

declare %templates:wrap function config:app-title($node as node(), $model as map(*)) as text() {
    $config:expath-descriptor/expath:title/text()
};

So let's try to call this variable from pages/index.html by adding the <h1> element as shown below.

<div data-template="templates:surround" data-template-with="templates/page.html" data-template-at="content">
    <div id="myCarousel" class="carousel slide" data-ride="carousel"><!-- Indicators -->
        <ol class="carousel-indicators">
            <li data-target="#myCarousel" data-slide-to="0" class="active"/>
            <li data-target="#myCarousel" data-slide-to="1"/>
        </ol>
        <!-- Wrapper for slides -->
        <div class="carousel-inner" role="listbox">
            <div class="item active">
                <img src="$app-root/resources/img/ACDH_Panorama_Postkarte_motiv01.jpg" alt="First" width="1200" height="600" title="by Sandra Lehecka"/>
                <div class="carousel-caption"/>
            </div>
            <div class="item">
                <img src="$app-root/resources/img/ACDH_Panorama_Postkarte_motiv02.jpg" alt="a nice feature picture" width="1200" height="600" title="by Sandra Lehecka"/>
                <div class="carousel-caption"/>
            </div>
        </div>
        <!-- Left and right controls -->
        <a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
        <!--<span class="sr-only">Previous</span>--></a>
        <a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
        <!--<span class="sr-only">Next</span>--></a>
    </div>
    <div class="container">
        <!-- headline fetches application's title-->
        <h1 style="text-align:center" data-template="config:app-title"/>
    </div>
</div>

This renders a page looking like depicted below:

image alt text

Unfortunately there is no already declared variable for the application's description. But we can take config:app-title as an example an write some config:app-description in modules/config.xqm:

declare %templates:wrap function config:app-description($node as node(), $model as map(*)) as text() {
    $config:repo-descriptor/repo:description/text()
};

Now we should be able to call this variable in pages/index.html:

...
    <div class="container">
        <!-- headline fetches application's title-->
        <h1 style="text-align:center" data-template="config:app-title"/>
        <!-- paragraph fetches application's description-->
        <p style="text-align:center" data-template="config:app-description"/>
    </div>
...

As we filled out the description form in a very minimalistic way, the current descriptions only contains "Thun Demo". Let's add some more. And since we are quite familiar with XML in general and the application's layout in specific we will modify the repo.xml directly.

<meta xmlns="http://exist-db.org/xquery/repo">
    <description>Thun Demo App is a lightweight prototype of a highly generic framework to publish digital editions like those of the Correspondence of Loe Thun von Hohenstein.</description>
    <author/>
    <website/>
    <status>alpha</status>
    <license>GNU-LGPL</license>
    <copyright>true</copyright>
    <type>application</type>
    <target>thun-demo</target>
    <prepare>pre-install.xql</prepare>
    <finish/>
    <permissions user="admin" group="dba" mode="rw-rw-r--"/>
    <deployed>2016-09-12T15:37:36.516+02:00</deployed>
</meta>

Our start page pages/index.html should now provide this information:

image alt text

Since one can't use any (HTML)tags in the <description> element of repo.xml one would have to remove the <p style="text-align:center" data-template="config:app-description"/> element from pages/index.html and write some more elaborate description directly into the pages/index.html.

More improvements

Add a button, ...

To improve the application's usability and spare our users from at least one click to get to the core of our application - the table of content exposing the edition's data - let's add a button to pages/index.html referencing pages/toc.html.

...
    <div class="container">
        <!-- headline fetches application's title-->
        <h1 style="text-align:center" data-template="config:app-title"/>
        <!-- paragraph fetches application's description-->
        <p style="text-align:center" data-template="config:app-description"/>
        <h1 style="text-align:center">
            <a href="toc.html" class="btn btn-info btn-lg">explore</a>
        </h1>
    </div>
...

image alt text

...a footer, ...

What's left to do is now to add a sticky footer whereas sticky means that the footer will always be at the bottom of the page, no matter how much (of little) content there is on the page. Since we want this footer to run through all our pages, we will add the necessary markup into our base template templates/pages.html. Since such a footer is possible not the most essential part of the application and more a matter of design and personal taste, I won't elaborate on the following code listing. Just be aware of the new resources like logos, images, custom css document which are referenced which means of course that those have to be stored in the according collections in our application's resources collection.

pages/index.html

The modified templates/page.html could look as follows:

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title data-template="config:app-title">App Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <meta data-template="config:app-meta"/>
        <link rel="shortcut icon" href="$shared/resources/images/exist_icon_16x16.ico"/>
        <link rel="stylesheet" type="text/css" href="$app-root/resources/css/bootstrap-3.0.3.min.css"/>
        <link rel="stylesheet" type="text/css" href="$app-root/resources/css/style.css"/>
        <script type="text/javascript" src="$app-root/resources/js/jquery/jquery-2.2.1.min.js"/>
        <script type="text/javascript" src="$app-root/resources/js/bootstrap-3.0.3.min.js"/>
        <script type="text/javascript" src="$app-root/resources/js/custom.js"/>
    </head>
    <body id="body">
        <nav class="navbar navbar-default" role="navigation">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"/>
                    <span class="icon-bar"/>
                    <span class="icon-bar"/>
                </button>
                <a data-template="config:app-title" class="navbar-brand" href="./index.html">App Title</a>
            </div>
            <div class="navbar-collapse collapse" id="navbar-collapse-1">
                <ul class="nav navbar-nav">
                    <li class="dropdown" id="about">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">Home</a>
                        <ul class="dropdown-menu">
                            <li>
                                <a href="index.html">Home</a>
                            </li>
                            <li>
                                <a href="toc.html">Table of Content</a>
                            </li>
                            <li>
                                <a href="persons.html">Persons</a>
                            </li>
                            <li>
                                <a href="ft_search.html">Fulltext Search</a>
                            </li>
                        </ul>
                    </li>
                </ul>
            </div>
        </nav>
        <section class="main-content">
            <div id="content" class="container"/>
        </section>
        <footer>
            <div class="container">
                <div class="row text-center">
                    <div class="col-sm-4">
                        <a href="https://acdh.oeaw.ac.at/acdh/" class="navlink" target="_blank">
                            <img src="../resources/img/ACDH_kurz_Weiss.png" alt="ACDH" width="30%"/>
                        </a>
                    </div>
                    <div class="col-sm-4">
                        <a href="https://github.com/csae8092/generic-de-web-app" class="navlink" target="_blank">
                            <img src="../resources/img/GitHub-Mark-Light-64px.png" alt="github" width="17%"/>
                        </a>
                    </div>
                    <div class="col-sm-4">
                        <a href="http://exist-db.org/" class="navlink" target="_blank">
                            <img src="../resources/img/existdb.png" alt="eXist-db" width="30%"/>
                        </a>
                    </div>
                </div>
            </div>
        </footer>
    </body>
</html>

resources/css/style.css

To turn the footer into a sticky footer, we have to add some css-rules. Credits and thanks to Ksenia Zaytseva who wrote most parts of it.

/* Application styles could go here */
html{
    height:100%;
}
body{
    display:flex;
    flex-direction:column;
    height:100%;
}
header{
    flex:0 0 auto;
    background-color:#333; /*#F8F8F8*/
    color:#ffffff;
}
.main-content{
    flex:1 0 auto;
}
footer{
    flex:0 0 auto;
    background-color:#333; /*#F8F8F8*/
    color:#ffffff;
    font-size:13px;
    padding:20px;
    /* position: absolute;*/
    left:0;
    right:0;
    width:100%;
    margin-top:10px;
}
footer .poweredby { float: right; margin: 33px 10px 0 0;}
footer .poweredby img { width: 120px; }

Now our humble application looks as follows:

image alt text

... and a search field.

While we are already working on improving the application's usability we can implement another feature to spare our users from additional clicking. If users want to conduct a full text search they have to navigate through the main nav bar to 'pages/ft_search.html* first before they are enabled to enter any search expression. To ease this process, let's add a search field to our main nav bar, so it will show up in all the pages of our web app. To acchive this, we simply have to modify the nav barin our base template templates/page.html like in the code snippet below:

<div class="navbar-collapse collapse" id="navbar-collapse-1">
    <ul class="nav navbar-nav">
        <li class="dropdown" id="about">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">Home</a>
            <ul class="dropdown-menu">
                <li>
                    <a href="index.html">Home</a>
                </li>
                <li>
                    <a href="toc.html">Table of Content</a>
                </li>
                <li>
                    <a href="persons.html">Persons</a>
                </li>
                <li>
                    <a href="ft_search.html">Fulltext Search</a>
                </li>
            </ul>
        </li>
    </ul>
    <div class="pull-right">
        <form method="get" action="ft_search.html" class="navbar-form" id="pageform">
            <div class="form-group">
                <div class="input-group">
                    <input type="text" class="form-control" name="searchexpr" placeholder="search in all inventories" pattern=".{3,}" required="" title="3 characters minimum"/>
                </div>
                <button type="submit" class="btn btn-primary">search</button>
            </div>
        </form>
    </div>
</div>

This will render as a search field in the top right corner as depicted in the following screenshot:

image alt text

By adding the attributes required="" and pattern=".{3,}" to the input form, we enforce a minimum length of the search string of three characters.

Having a general search field, we basically don't need to link to ft_search.html any more in the nav bar since we would then provide to our users two ways to achieve the same things and this is something I personally always find a bit annoying. Therefore I will remove the link to this page from the nav bar. Alternatively we could think about implementing something like an advanced search where users can customize e.g. in which parts or elements of the XML/TEI documents they want to search for. But since such a feature very much depends on the actual data, it is not an easy thing to include such a feature in a web applications like ours which tries to be as generic as possible. Likewise we can now remove the input field in ft_search.html as well.

Cookie consent and Share-Buttons

Cookie consent

In case you plan to gather some user statics or you want to implement some other features which depend on cookies, it is a good idea to inform your users that you are using cookings. Since almost every page uses cookies there are plenty of libraries providing the necessary functionalities to inform users accordingly. We use one from Silktide. Here you can also read about any customization options. The only thing we have to do for this to add the following code snippet in our base template templates/page.html somewhere towards the end of the <head> element

...
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
    <script type="text/javascript">
        window.cookieconsent_options = {"message":"This website uses cookies to ensure you get the best experience on our website","dismiss":"Got it!","learnMore":"More info","link":"index.html","theme":"dark-bottom"};
    </script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js"/>
<!-- End Cookie Consent plugin -->

image alt text

share buttons

To enable our users to share the contents of our web app, we will implement also some share buttons for facebook, twitter, and google+. For this we are following this tutorial provided by the fantastic site http://tutorialzine.com/. In case you are interested in the actual implementation, please look into the finalized code of this tutorial which you can download here. On the screenshots below you can see the result of our efforts:

image alt text image alt text

One last thing

Already in the 3rd Part I mentioned eXist-db's REST(like)-API. But for the time being, only people well familiar with eXist-db might know that the data of our application can be fetched that easily. Therefore, lets add a new nav bar entry, linking to our data/editions collection. To accomplish this, just add the following code snippet into the nav bar in our base template located at templates/page.html.

<ul class="dropdown-menu">
    <li>
        <a href="index.html">Home</a>
    </li>
    <li>
        <a href="toc.html">Table of Content</a>
    </li>
    <li>
        <a href="persons.html">Persons</a>
    </li>
    <li>
        <a href="../data/editions">API</a>
    </li>
    <!--<li>
            <a href="ft_search.html">Fulltext Search</a>
        </li>-->
</ul>

Conclusion and outlook

Now we are in possession of an eXist-db application which can easily be packed, downloaded, redeployed and modified and therefore serve as a very solid starting point for building a small to medium sized framework for publishing a digital edition. With this 10th tutorial we meet all requirements described in the first part of this series of HowTos. In the future, we will add more posts sharing our ways on how to improve the current application. But since most of those improvements will depend very much from the actual data, we restrain our selfs from implementing those functionalities/features into the current application.

Get the app

In case you were following this tutorials only theoretically and you didn't wrote any line of code, you can download the packed application here.


Comment/Edit this post on GitHub.
export blog text