Adding fully offline capability

Overview

Teaching: 0 min
Exercises: 0 min
Questions
  • How simple would it be to add “Fully Offline” capablity to the current style?

  • What changes to the current style’s files would be required?

  • What changes to a typical lesson’s content would be required?

  • Would these changes prevent the existing behaviour from being produced if required?

Objectives
  • Become aware of _config.yml and the way in which the lesson style makes use of “global variables” and templates.

  • Become aware of of the small set of changes, to the current style’s files, that are needed to add “Fully Offline” capablity.

  • Become aware of the additions to lesson content that are needed.

  • Become aware that the existing behavious can be preserved.

This episode looks at how the style templates use "global variables", that is, variables that can be referenced within any source file for a Lesson template, for any Episode’s, or for auxilliary, content, and then shows how to use the power of Software Carpentry’s rendering methodology, and add a variable to provides the filename that is currently missing from the link targets of current Lessons that are browsed via file:/// URIS.

Many of the "global variables" that can be used with an SWC Lesson’s template, Episodes, or auxilliary source files, are defined in the top-level file _config.yml, and the following snippet of such a file shows how these vaiables are defined:

#------------------------------------------------------------
# Values for this lesson.
#------------------------------------------------------------

# Which carpentry is this ("swc", "dc", or "lc")?
carpentry: "swc"

# Overall title for pages.
title: "Offline Capable Lessons"

...

# Sites.
amy_site: "https://amy.software-carpentry.org/workshops"
dc_site: "http://datacarpentry.org"
swc_github: "https://github.com/swcarpentry"
swc_site: "https://software-carpentry.org"

...

# Set the default layout for things in the episodes collection.
defaults:
  - values:
      root: ..
  - scope:
      path: ""
      type: episodes
    values:
      layout: episode
...

whilst the top part of the source file for this Episode, looks like this

title: "Adding fully offline capability"
teaching: 0
exercises: 0
questions:
- "How simple would it be to add \"Fully Offline\" capablity to the current style?"
...

Note that a variable called title has been defined in two places, once in the lesson’s _config.yml file and once in the episode’s souce file.

In order to see how these variables can be used, we need to take a look at a section from one of the standard Lesson style’s template files, in this case _includes\main_title.html

{% comment %}
  Main title for lesson pages.
{% endcomment %}
<h1 class="maintitle"><a href="{{ page.root }}/">{{ site.title }}</a>{% if page.title %}: {{ page.title }}{% endif %}</h1>

The first three lines are simply a comment block that gets ignored when the file is used to render the Lesson’s content, whilst the last line contains a mixture of HTML markup, ome strings within pairs of double braces - page.root, site.title and page.title - and a block of if-then-else-endif logic in which the clauses of the statement are contained within blocks denoted by brace-percent pairs.

If we now allow the rendering of this Lesson to present to us what the above template puts into the stream of content it sends to the browser we see

<h1 class="maintitle"><a href="../">Offline Capable Lessons</a>: Adding fully offline capability</h1>

that the instance of site.title with the double brace pair has been replaced with the string from the lesson’s (think site) _config.yml value for title, which the page.title with the double brace pair has been replaced with the string from this episode’s (think page) value for title.

Note also that the page.root for this episode has been replaced by the string ../ in the rendered output, even though a root variable is only defined in _config.yml and not this episode’s source file.

We can now see that double-brace pair variables that are qualified with a prepended site. refer to variables defined in _config.yml, whilst variables that are qualified with a prepended page, refer to variables defined in the episode then being rendered, unless no such variable exists in which case the page values inherits the value defined as the site level.

We can also see that when we click on the link to the Lesson’s title , we are only going to be sent to a target that is the directory above the one that this episode’s HTMl file resides in, ie the href="../", whereas we know that we really want href="../index.html"

We might consider the addition of our known default filename into the style’s template file, _includes\main_title.html as follows

{% comment %}
  Main title for lesson pages.
{% endcomment %}
<h1 class="maintitle"><a href="{{ page.root }}/index.html">{{ site.title }}</a>{% if page.title %}: {{ page.title }}{% endif %}</h1>

which would then render

<h1 class="maintitle"><a href="../index.html">Offline Capable Lessons</a>: Adding fully offline capability</h1>

which is what’s needed for navigating through ~lesson content that is being browsed offline, as well as being a link target that we know will allow us to navgiate through lesson content that is being presented by a webserver.

So that’s “just” what we need then?

No: not quite.

Firstly, the file _includes\main_title.html only handles the link in the main title bar, and not the various links in the other places where navigation links are presented.

Secondly, the hard-coding of the filename into the template would

  1. Force everyone using such a mofified template to use that filename, and there may be reasons where that wouldn’t be the desired name, and
  2. Force them to render their Lessons with a filename appearing in them even when presented via webservers that “do the right thing” as regards a bare directory name.

Let’s address that second issue first.

Rather than hard-code the filename into parts of the template, we can define a Lesson/site-wide variable, by adding one into the _config.ywml and then refering to it in the template files, so we might now have, in _config.ywml

#------------------------------------------------------------
# Values for this lesson.
#------------------------------------------------------------

# Which carpentry is this ("swc", "dc", or "lc")?
carpentry: "swc"

# Overall title for pages.
title: "Offline Capable Lessons"

# Filename matching webserver default, for offline browsing
index: "index.html"
...

and in _includes\main_title.html,

{% comment %}
  Main title for lesson pages.
{% endcomment %}
<h1 class="maintitle"><a href="{{ page.root }}/{{ site.index }}">{{ site.title }}</a>{% if page.title %}: {{ page.title }}{% endif %}</h1>

As modified above, that would render the main title bar as

<h1 class="maintitle"><a href="../index.html">Offline Capable Lessons</a>: Adding fully offline capability</h1>

but note what happens if we chose to define the value of site.index to be empty, by specifying it in _config.ywml as follows

#------------------------------------------------------------
# Values for this lesson.
#------------------------------------------------------------

# Which carpentry is this ("swc", "dc", or "lc")?
carpentry: "swc"

# Overall title for pages.
title: "Offline Capable Lessons"

# Filename matching webserver default, for offline browsing
index: ""
...

Now our modified template generates the following HTML

<h1 class="maintitle"><a href="../">Offline Capable Lessons</a>: Adding fully offline capability</h1>

which, a bare directory path, is exactly what the current template would generate.

This would appear to be a very good thing, in that someone who writes their Lesson content using the modified template could do most of their work with an empty definition of the site.index variable, and see all previous behaviours - renderings of GitHub’s gh-pages branches, and when rendered via a make serve in a local source tree - maintained, whilst allowing for the creation of a “fully offline capable” rendering, merely by altering one line in _config.yml, invoking a make site, and then copying the _site directory as a standalone resource.

So just where else, within the lesson style’s template, do we need to make changes ?

Let’s now address the first of the two questions we raised above, namely, “the various links in the other places where navigation links are presented”..

Because Software Carpentry’s approach to generating content makes such a big use of templates for common sections within each episode, or auxiliary content, it may come as a pleasant surprise to find out just how few files, and in just how few places within those files, require the introduction of the site.index variable.

The full list of files, along with

which we saw edited above, is just

and the actual changes, (smply inserting site.index) just

    _config.yml                    1 change
    _includes/all_keypoints.html   1 change
    _includes/episode_navbar.html  5 changes
    _includes/main_title.html      1 change
    _includes/navbar.html         10 changes
    _includes/syllabus.html        2 changes

and of course, once those changes were in the official style’s template files, no-one would need to make that set of changes ever again.

But wait: there’s more !

Even though the changes described above are enough to allow for an “offline” navigation through the Lesson’s episodes, there are still one or two places where we can enhance the “offline” experience, and present to the reader auxiliary content that, although one might expect it be “local to the Lesson”, is currently only available within the Lesson’s remote, so at GitHub, repository.

In the next episode though, we’ll address that too.

Key Points

  • A small set of changes to the existing style templates would allow for a “Fully Offline Capble” rendering of a lesson’s content.

  • Using the modifed style templates with existing lesson content will see that rendered as it was with the existing style templates.

  • The existing behaviour can be preserved within a lesson that uses the modifed style templates.