Skip to main content
Join the official 2019 Python Developers SurveyStart the survey!

A set of Jinja2 templates implementing the BAS Style Kit

Project description

BAS Style Kit Jinja Templates

A set of Jinja2 templates implementing the BAS Style Kit.

Installation

Pip package

The recommended way to get these templates is installing its PyPi package, bas-style-kit-jekyll-templates.

Usage

Quickstart

For a typical Flask application add this wherever your Flask application is defined:

from flask import Flask, render_template
from jinja2 import PrefixLoader, PackageLoader, FileSystemLoader

from bas_style_kit_jinja_templates import BskTemplates

app = Flask(__name__)
app.jinja_loader = PrefixLoader({
    'app': FileSystemLoader('templates'),
    'bas_style_kit': PackageLoader('bas_style_kit_jinja_templates'),
})
app.config['bsk_templates'] = BskTemplates()

# Required/recommended settings
app.config['bsk_templates'].site_title = 'Example service'
app.config['bsk_templates'].site_description = 'Service to act as an example'
app.config['bsk_templates'].bsk_site_nav_brand_text = 'Example service'
app.config['bsk_templates'].bsk_site_development_phase = 'beta'
app.config['bsk_templates'].bsk_site_feedback_href = '/feedback'
app.config['bsk_templates'].bsk_site_footer_policies_cookies_href = '/legal/cookies'
app.config['bsk_templates'].bsk_site_footer_policies_copyright_href = '/legal/copyright'
app.config['bsk_templates'].bsk_site_footer_policies_privacy_href = '/legal/privacy'

# Optional - add a custom CSS file with a relative URL
app.config['bsk_templates'].site_styles.append({'href': '/css/app.css'})
# Optional - add a custom JS file with a SRI value
app.config['bsk_templates'].site_scripts.append({'href': 'https://example.com/js/example.js', 'integrity': 'abc123'})
# Optional - enable Google Analytics
app.config['bsk_templates'].site_analytics['id'] = '1234'
# Optional - choose between the `bsk-container` (used by default) and `bsk-container-fluid` layout container
app.config['bsk_templates'].bsk_container_classes = ['bsk-container']
# Optional - add navigation menu items
app.config['bsk_templates'].bsk_site_nav_primary.append({'value': 'Item', 'href': '#'})
app.config['bsk_templates'].bsk_site_nav_secondary.append({
    'value': 'Dropdown', 
    'items': [
        {'value': 'Sub-item 1', 'href': '#', 'target': '_blank'}
    ]
})
app.config['bsk_templates'].bsk_site_nav_launcher.append({'value': 'Related service', 'href': 'https://example.com'})


@app.route('/')
def index():
    # noinspection PyUnresolvedReferences
    return render_template(f"app/index.j2")

Where app/index.j2 is a template located in templates/index.j2 which extends an application layout:

{% extends 'app/layouts/app.j2' %}
{% block main_content %}
<p>Index view content...</p>
{% endblock %}

Where app/layouts/app.j2 is a template located in templates/layouts/app.j2 which extends a layout provided by these templates:

{% exnteds 'bas_style_kit/layouts/bsk_standard.j2' %}

Using a page pattern

To create a page in an application based on a page pattern, such as the page not found pattern, create a template (e.g. templates/errors/404.j2) with the following:

{% extends 'bas_style_kit/patterns/bsk_page-not-found.j2' %}

To use this template as the 404 error handler in a Flask application:

@app.errorhandler(404)
def page_not_found(e):
    # note that we set the 404 status explicitly
    return render_template('app/errors/404.j2'), 404

Using custom CSS/JS

Non-Style Kit CSS an/or JavaScript resources can be included either as references to files, or as inline content.

Note: This won't work if you are using the Blank layout.

  • CSS resources are outputted in the styles block, at the end of the <head> element
  • JS resources are outputted in the scripts block, at the end of the <body> element

Using custom CSS/JS files

  • CSS files are added as a resource object to the site_styles property of the BskTemplates class instance
  • JS files are added as a resource object to the site_scripts property of the BskTemplates class instance

Files will be included after the Style Kit's own resources (where a Style Kit layout is used) to ensure they have priority.

Each file reference consists of an object with these properties:

Property Data Type Required Allowed Values Example Value
href String Yes Any URL /css/app.css / https://example.com/js/app.js
integrity String No Any SRI value sha256-ClILH8AIH4CkAybtlKhzqqQUYR4eSDiNTK5LIWfF4qQ=

For example (using a Flask application with a css/app.css file with the default static files route):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].site_styles.append({'href': '/static/css/app.css'})

The integrity property is used to specify a Subresource Integrity (SRI) value for a resource. If specified an integrity attribute and will be added to the generated markup. A crossorigin attribute will also be added for Cross-Origin Resource Sharing (CORS) support with a hard-coded, anonymous, value.

Using custom CSS/JS inline content

  • CSS content should be appended to the styles block
  • JS content should be appended to the scripts block
  • inline content will be added after any files to ensure they have priority

Note: To append to a block use {{ super() }}, rather than replacing the contents of a block.

For example (using a Jinja template):

{% block scripts %}
    {{ super() }}
    console.log('jQuery version: ' + jQuery.fn.jquery);
{% endblock %}

Navigation menu items

When using the bsk_standard layout, a navbar is included as part of the 'standard header', which consists of a cookie banner, navbar and site development phase banner.

This navbar consists of three menus (and other elements, documented elsewhere):

  • a primary navigation menu - aligned left, after brand elements
  • a secondary navigation menu - aligned right, before the launcher menu
  • a navigation launcher menu - aligned right, after the secondary navigation menu

The navigation launcher is a restricted menu, used to link to other BAS websites and applications. By default it contains links to the BAS public website and the BAS data catalogue. Other websites and applications can be added as well where relevant.

  • primary navigation menu items should be added to the BskTemplates.bsk_site_nav_primary variable
  • secondary navigation menu items should be added to the BskTemplates.bsk_site_nav_secondary variable
  • navigation launcher menu items should be added to the BskTemplates.bsk_site_nav_launcher variable

The primary and secondary navigation menu's support:

The navigation launcher menu, which is implemented as a drop-down menu, supports:

Menu item objects have the following properties:

Property Data Type Required Allowed Values Example Value Notes
value String Yes Any string About -
href String Yes Any URL /about / https://www.example.com/about Ignored if items set
items Array No Array of menu item objects - Ignored for navigation launcher items
Any String No As per attribute - Arbitrary attribute/value key/values

Note: The items property is only recursed once, deeper objects will be ignored.

For example (using a Flask application):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_nav_primary.push({'value': 'Item', 'href': '/about'})
app.config['bsk_templates'].bsk_site_nav_primary.push({'value': 'Another Item', 'href': '#', 'target': '_blank'})
app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_nav_secondary.push({
    'value': 'Dropdown', 
    'items': [
        {'value': 'Sub-item 1', 'href': 'https://www.example.com'}
    ]
})
app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_nav_launcher.push({'value': 'Related service', 'href': 'https://example.com'})

Active navigation items

These templates will automatically add the .bsk-active class to the relevant navigation item, and if relevant, sub-item, where its href attribute exactly matches the current URL given by {{ request.path }}.

For example for a navigation item {'value': 'About', 'href': '/about'}, when visiting https://www.example/about, the about navigation item will be made active, as the current path /about matches the href attribute.

This support doesn't support URL patterns, such as /foo/{id} where {id} is a dynamic value. In these cases the active_nav_item variable can be set to the href value of a navigation item to make it active explicitly.

For example (using flask application):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_nav_primary.push({'value': 'Foo', 'href': '/foo'})

@app.route('/foo/<foo_id>')
def foo_details(foo_id: str):
    foo = get_foo(foo_id)

    return render_template(f"app/views/foo-details.j2", foo=foo, active_nav_item='/foo')

Navbar branding

Navbars are used to display the name/identity of a website or application, to remind users where they are. These elements are referred to as 'brand' elements within the Style Kit.

Supported brand elements:

  • brand text - set using the BskTemplates.bsk_site_nav_brand_text property
  • brand image - set using the BskTemplates.bsk_site_nav_brand_img_href property

Brand elements can be used together or individually, with fix classes applied automatically as needed.

Brand elements are linked to a location specified by the bsk_attributes.site_nav_brand_href variable, which should be, and is by default, the index of each website or application (i.e. /).

For example (using a Flask application):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_nav_brand_text = 'Example service'
app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_nav_brand_img_href = '/assets/img/navbar-brand-img.png'

Site development phase

The site development phase indicates the stage of development for a website or application, e.g. alpha or live. Conventional phases are described in the Style Kit documentation.

For websites or applications that are not firmly in the 'live' phase, a banner should be shown to inform users and request feedback. This forms part of the 'standard header' of cookie banner, navbar and site development phase banner.

In these templates, the BskTemplates.bsk_site_development_phase property is used to specify the current phase for a website or application. When using the bsk_standard layout the banner will be shown automatically.

To disable this banner, use the live-stable. Strictly speaking this isn't a real phase but is recommended by these templates to distinguish between a newly released or mature and well-established website or application.

For example (using a Flask application):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_development_phase = 'beta'

Experimental site phase

Where a website or application is in a staging environment, or otherwise used for development/testing activities, the site phase can be set to experimental to use the conventional experimental phase.

For example (using a Flask application):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_development_phase = 'experimental'

Google analytics

To include the Google Analytics universal tracking library (gtag), set the BskTemplates.bsk_site_analytics['id'] property to relevant Google Analytics property ID.

Note: When used the anonymise IP option in Google Analytics is enabled by default.

For example (using a Flask application):

app.config['bsk_templates'] = BskTemplates()
app.config['bsk_templates'].bsk_site_analytics['id'] = 'UA-12345678'

Footer content

Add custom footer content to the footer_content block. It will be shown between the divider and Governance footer components.

It is recommended to include a spacer component after any footer content to balance the whitespace within the footer.

For example:

{% block footer_content %}
<div>Footer content</div>
<div role="separator" class="bsk-footer-spacer"></div>
{% endblock %}

You can also set custom classes on the footer element by appending to the bsk_footer_classes list, or replacing all classes by overriding the list.

Components

All components are located in the bas_style_kit_jinja_templates package. Variables are defined in bas_style_kit_jinja_templates/__init__.py, with all other components defined in bas_style_kit_jinja_templates//templates/.

Components that are specific to the Style Kit are prefixed with bsk-- or bsk_.

Jinja setup

These templates requiring a PrefixLoader and PackageLoader Jinja loader.

loader = PrefixLoader({
    'bas_style_kit': PackageLoader('bas-style-kit-jekyll-templates')
})

Typically your application with have its own templates as well, which can be loaded under a different prefix (such as app) using a relevant loader, such as the default FileSystemloader.

loader = PrefixLoader({
    'app': FileSystemLoader('templates'),
    'bas_style_kit': PackageLoader('bas-style-kit-jekyll-templates')
})

In addition, a BskTemplates class instance is needed to define Variable values. This instance should be available in the Jinja environment as config.bsk_templates. For Flask applications this will occur automatically by adding the class instance to app.config, otherwise this instance will need to be passed manually.

For example (using a Flask application):

app.config['bsk_templates'] = BskTemplates()

Where a PrefixeLoader is used, references to resources should include a prefix and a deliminator (/ by default).

For example an application layout would change from:

{% extends "layouts/base.j2" %}

To:

{% extends "app/layouts/base.j2" %}

To use a layout from these templates:

{% extends "bas_style_kit/layouts/bsk_base.j2" %}

Layouts

Layouts are 'base' templates from which views or other layouts inherit. Layouts in these templates are hierarchical, with each layout extending the last in this order:

  1. blank.j2: lowest level layout, intentionally as minimal as possible and not intended for direct use, unless non-HTML output is needed
  2. html.j2: defines a minimal, accessible, HTML5 structure with some recommended best practices for cross-platform compatibility
  3. bsk_base.j2: intentionally implements the BAS Style Kit as minimally as possible and not intended for direct use, unless the bsk_standard.j2 layout is unsuitable
  4. bsk_standard.j2: defines an opinionated, conventional, page layout with a 'standard' header/footer, recommended as a base for application/website layouts

Layouts can be used using the extend keyword and defining content in the relevant block:

Layout Content Block
blank.j2 content
html.j2 main_content
bsk_base.j2 main_content
bsk_standard.j2 main_content

For example:

{% extends 'bas_style_kit/layouts/bsk_standard.j2' %}

{% block main_content %}
Layout content
{% endblock %}

Blocks

Blocks are used for template inheritance and provide a logical structure/hierarchy.

Blocks are defined in Layouts, typically with default content using Includes. Some blocks are empty, designed for user content or extensibility.

To implement or override a block, redefine it in a template or view:

{% block example_block %}
content ...
{% endblock %}

To append to a block, without overriding its existing content, use the special {{ super() }} variable:

{% block example_block %}
{{ super() }}
content ...
{% endblock %}

Includes

Includes are used for organising content, to make management easier, and to allow common elements to be used in multiple places, typically in Blocks.

For example the content needed for using Google Analytics is encapsulated in the body--analytics-script.j2 include.

Macros

Macros are used to provide configurable, reusable, functionality.

For example, primary and secondary navigation menus process navigation items the same way, using the bsk--nav.j2 macro.

Variables

Various elements in these templates are configurable, such as the name of the application or website, or the CSS/JS resources to include. A Python class BskTemplates is used to configure these elements and which should be passed to the Jinja environment.

These variables should be changed or set for each website or application:

site_title : Name of the application or website

site_description : Description of the application or website

site_analytics.id : Google Analytics property ID

bsk_site_nav_brand_text : Name of the application or website

bsk_site_nav_primary : Primary navigation menu items

bsk_site_development_phase : Site development phase

bsk_site_feedback_href : URL or mailto: value for application or website feedback

bsk_site_footer_policies_cookies_href : URL to application or website cookies policy

bsk_site_footer_policies_copyright_href : URL to application or website copyright notice

bsk_site_footer_policies_privacy_href : URL to application or website privacy policy

These variables may, but don't need to be, changed or set for each website or application:

site_styles : Array of additional CSS files

site_scripts : Array of additional JS files

container_classes : Array of non-Style Kit classes which set the layout of content, including main content and headers/footers which should align the same way

main_content_classes : Array of non-Style Kit classes which should only be applied to main page content

bsk_container_classes : Array of Style Kit classes which should set the layout of content, including main content and headers/footers which should align the same way

bsk_main_content_classes : Array of Style Kit classes which should only be applied to main page content

bsk_footer_classes : Array of Style Kit classes which should be applied to the standard footer element

bsk_site_favicon : Name of the Favicon to include (valid options: [default])

bsk_site_nav_secondary : Secondary navigation menu items

bsk_site_nav_brand_img_href : URL to Navbar brand image

bsk_site_nav_brand_href : URL for Navbar brand elements, which should be the index or home of the application or website

bsk_site_nav_launcher : Navigation launcher items

These variables do not normally, and should not, need to be changed or set:

site_back_to_top_target_id : ID of the anchor element representing the top of the current page/view

site_main_content_target_id : ID of the element representing the main content in a page/view (i.e. skipping navigation elements)

bsk_site_footer_ogl_symbol_a_href : URL to the Open Government Licence symbol

bsk_site_footer_ogl_text_href : URL to the Open Government Licence text (i.e. the actual licence)

bsk_site_footer_ogl_text_version : Version of the Open Government Licence used

These variables must not be changed and should be treated as read only:

templates_version : Version of these templates

bsk_version : Version of the Style Kit used by these templates

Patterns

Patterns demonstrate preferred ways to ask information from, or provide information to, end users for various tasks.

Page patterns

Page patterns define content for common pages such as Page not found (404) pages.

These templates implement page patterns as layouts/views, typically without the need to provide additional information. Where additional information is available, such as contact instructions or details about current maintenance etc., the pattern_content block can be used.

For example:

{% extends 'bas_style_kit/patterns/bsk_service-unavailable.j2' %}

{% block pattern_content %}
Additional information clarifying details or circumstances.
{% endblock %}

Some patterns support multiple variants, or can be configured, using variables and blocks described here.

Service unavailable

Variables:

availability : Set to replaced for the replaced variant. Set to closed for the closed variant.

For example:

(basic variant)

{% extends 'bas_style_kit/patterns/bsk_service-unavailable.j2' %}

{% block pattern_content %}
Contact the <a href="mailto:servicedesk.bas.ac.uk">BAS IT Service Desk</a> for more information.
{% endblock %}

(closed variant)

{% extends 'bas_style_kit/patterns/bsk_service-unavailable.j2' %}

{% set availability = 'closed' %}

{% block pattern_content %}
Contact the <a href="mailto:servicedesk.bas.ac.uk">BAS IT Service Desk</a> for more information.
{% endblock %}
Start

Variables:

call_to_action_url : Set to the href the call to action button should go to

call_to_action_variant : Set to default for a standard 'start now' call to action button Set to sign-in-microsoft for a combined 'sign-in to continue' and 'start now' button

Blocks:

pattern_content_uses : Set to an unordered list of items for the 'use this service to:' list

pattern_content : Additional, optional, content such as 'before you begin' or 'more information' sections

For example:

(basic variant)

{% extends 'bas_style_kit/patterns/bsk_start.j2' %}

{% set call_to_action_url = '#' %}

{% block pattern_content_uses %}
<ul>
    <li>A task</li>
    <li>Another task</li>
</ul>
{% endblock %}

(more information variant)

{% extends 'bas_style_kit/patterns/bsk_start.j2' %}

{% set call_to_action_url = '#' %}
{% set call_to_action_variant = 'sign-in-microsoft' %}

{% block pattern_content_uses %}
    <ul>
        <li>A task</li>
        <li>Another task</li>
    </ul>
{% endblock %}

{% block pattern_content %}
    <section class="bsk-before-you-start">
        <h2 class="bsk-h3">Before you start</h2>
        <p>Before you start information</p>
    </section>
    <section class="bsk-more-information">
        <h2 class="bsk-h3">More information</h2>
        <p>More information</p>
    </section>
{% endblock %}
Sign-in (Microsoft)

Variables:

call_to_action_url : Set to the href the call to action button should go to

Blocks:

pattern_content : Additional, optional, content such as 'before you begin' or 'more information' sections

For example:

(basic variant)

{% extends 'bas_style_kit/patterns/bsk_sign-in-microsoft.j2' %}

{% set call_to_action_url = '#' %}

Development

A docker container ran through Docker Compose is used as a development environment for this project. It includes development only dependencies listed in requirements.txt and a local Flask application in app.py.

Ensure classes and methods are defined within the bas_style_kit_jinja_templates package.

If you have access to the BAS GitLab instance, you pull the Docker image from the BAS Docker Registry:

$ docker login docker-registry.data.bas.ac.uk
$ docker-compose pull

# To run the local Flask application using the Flask development server
$ docker-compose up

# To start a shell
$ docker-compose run app ash

Code Style

PEP-8 style and formatting guidelines must be used for this project, with the exception of the 80 character line limit.

Flake8 is used to ensure compliance, and is ran on each commit through Continuous Integration.

To check compliance locally:

$ docker-compose run app flake8 . --ignore=E501

Dependencies

Development Python dependencies should be declared in requirements.txt to be included in the development environment.

Runtime Python dependencies should be declared in requirements.txt and setup.py to also be installed as dependencies of this package in other applications.

All dependencies should be periodically reviewed and update as new versions are released.

$ docker-compose run app ash
$ pip install [dependency]==
# this will display a list of available versions, add the latest to `requirements.txt` and or `setup.py`
$ exit
$ docker-compose down
$ docker-compose build

If you have access to the BAS GitLab instance, push the Docker image to the BAS Docker Registry:

$ docker login docker-registry.data.bas.ac.uk
$ docker-compose push

Dependency vulnerability scanning

To ensure the security of this API, all dependencies are checked against Snyk for vulnerabilities.

Warning: Snyk relies on known vulnerabilities and can't check for issues that are not in it's database. As with all security tools, Snyk is an aid for spotting common mistakes, not a guarantee of secure code.

Some vulnerabilities have been ignored in this project, see .snyk for definitions and the Dependency exceptions section for more information.

Through Continuous Integration, on each commit current dependencies are tested and a snapshot uploaded to Snyk. This snapshot is then monitored for vulnerabilities.

Dependency vulnerability exceptions

This project contains known vulnerabilities that have been ignored for a specific reason.

Static security scanning

To ensure the security of this API, source code is checked against Bandit for issues such as not sanitising user inputs or using weak cryptography.

Warning: Bandit is a static analysis tool and can't check for issues that are only be detectable when running the application. As with all security tools, Bandit is an aid for spotting common mistakes, not a guarantee of secure code.

Through Continuous Integration, each commit is tested.

To check locally:

$ docker-compose run app bandit -r .

Testing

Continuous Integration

All commits will trigger a Continuous Integration process using GitLab's CI/CD platform, configured in .gitlab-ci.yml.

Pip dependencies are also checked and monitored for vulnerabilities.

Distribution

Both source and binary versions of the package are build using SetupTools, which can then be published to the Python package index for use in other applications. Package settings are defined in setup.py.

This project is built and published to PyPi automatically through Continuous Deployment.

To build the source and binary artefacts for this project manually:

$ docker-compose run app ash
# build package to /build, /dist and /bas_style_kit_jinja_templates.egg-info
$ python setup.py sdist bdist_wheel
$ exit
$ docker-compose down

To publish built artefacts for this project manually to PyPi testing:

$ docker-compose run app ash
$ python -m twine upload --repository-url https://test.pypi.org/legacy/ dist/*
# project then available at: https://test.pypi.org/project/bas-style-kit-jinja-templates/
$ exit
$ docker-compose down

To publish manually to PyPi:

$ docker-compose run app ash
$ python -m twine upload --repository-url https://pypi.org/legacy/ dist/*
# project then available at: https://pypi.org/project/bas-style-kit-jinja-templates/
$ exit
$ docker-compose down

Continuous Deployment

A Continuous Deployment process using GitLab's CI/CD platform is configured in .gitlab-ci.yml. This will:

  • build the source and binary artefacts for this project
  • publish built artefacts for this project to the relevant PyPi repository

This process will deploy changes to PyPi testing on all commits to the master branch.

This process will deploy changes to PyPi on all tagged commits.

Release procedure

At release:

  1. create a release branch
  2. close release in CHANGELOG.md
  3. push changes, merge the release branch into master and tag with version

The project will be built and published to PyPi automatically through Continuous Deployment.

After release:

  1. create a next-release branch
  2. bump templates_version variable in bas_style_kit_jinja_templates/__init__.py
  3. push changes and merge the next-release branch into master

Feedback

The maintainer of this project is the BAS Web & Applications Team, they can be contacted at: servicedesk@bas.ac.uk.

Issue tracking

This project uses issue tracking, see the Issue tracker for more information.

Note: Read & write access to this issue tracker is restricted. Contact the project maintainer to request access.

License

© UK Research and Innovation (UKRI), 2019, British Antarctic Survey.

You may use and re-use this software and associated documentation files free of charge in any format or medium, under the terms of the Open Government Licence v3.0.

You may obtain a copy of the Open Government Licence at http://www.nationalarchives.gov.uk/doc/open-government-licence/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for bas-style-kit-jinja-templates, version 0.4.0
Filename, size File type Python version Upload date Hashes
Filename, size bas_style_kit_jinja_templates-0.4.0-py3-none-any.whl (26.2 kB) File type Wheel Python version py3 Upload date Hashes View hashes
Filename, size bas-style-kit-jinja-templates-0.4.0.tar.gz (34.7 kB) File type Source Python version None Upload date Hashes View hashes

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page