Mike Slinn

jekyll_flexible_include

Published 2020-10-03. Last modified 2023-05-18.
Time to read: 5 minutes.

This page is part of the jekyll_plugins collection.

Jekyll's built-in include tag does not support including files outside of the _includes folder. Originally called include_absolute, the tag plugin now does much more than just including absolute file names. The plugin was renamed jekyll_flexible_include, and the tag is called flexible_include. This plugin now supports 4 types of includes:

  1. Filenames relative to the top-level directory of the Jekyll web site. It is unnecessary to preface these paths with ./).
  2. Absolute filenames (first character is /). This feature can be modified or denied where security is a concern by specifying an array of Ruby glob expressions in the FLEXIBLE_INCLUDE_PATHS environment variable.
  3. Filenames relative to the user home directory (first character is ~). This feature can also be modified or denied where security is a concern by specifying an array of Ruby glob expressions in the FLEXIBLE_INCLUDE_PATHS environment variable.
  4. Executable filenames on the PATH (first character is !). This feature can be disabled by defining the DISABLE_FLEXIBLE_INCLUDE environment variable before launching Jekyll.

In addition, filenames that require environment expansion because they contain a $ character are expanded according to the environment variables defined when the jekyll build process was launched.

Installation

Gem

Add the following highlighted line to your Jekyll project's Gemfile, within the jekyll_plugins group:

Gemfile
group :jekyll_plugins do 
  gem 'jekyll_flexible_include'
end 

And then execute:

Shell
$ bundle

Add the following to your Jekyll _config.yml:

_config.yml
plugins:
  - flexible_include

CSS and Assets

Copy assets and CSS from the demo/ directory of the jekyll_flexible_include_plugin GitHub project.

  • Copy demo/assets/images/clippy.svg to a directory of the same name in your Jekyll project.
  • Copy demo/assets/css/jekyll_flexible_include.css to your Jekyll project assets directory.
  • Copy demo/assets/css/jekyll_plugin_support.css to your Jekyll project assets directory.
  • Copy demo/assets/css/shared_include_pre.css to your Jekyll project assets directory.
  • Incorporate the CSS stylesheets into the appropriate layout in your Jekyll project:
    _layouts/default.html
    {% assign nowMillis = site.time | date: '%s' %}
    <link rel="stylesheet" href="{{ '/assets/css/jekyll_flexible_include.css?v=' | append: nowMillis }}" type="text/css">
    <link rel="stylesheet" href="{{ '/assets/css/jekyll_plugin_support.css?v=' | append: nowMillis }}" type="text/css">
    <link rel="stylesheet" href="{{ '/assets/css/shared_include_pre.css?v=' | append: nowMillis }}" type="text/css">

    JavaScript

    Copy demo/assets/js/clipboard.min.js from the jekyll_flexible_include_plugin GitHub project to your Jekyll project’s JavaScript directory.

    Modify the Jekyll layout or selected pages to load the JavaScript. You can load it from your project, as shown below, or from a CDN.

    One way of loading JavaScript
    <script defer src="/assets/js/clipboard.min.js"></script>

    Syntax

    The following are equivalent:

    • {% flexible_include path [ OPTIONS ] %}
    • {% flexible_include 'path' [ OPTIONS ] %}
    • {% flexible_include "path" [ OPTIONS ] %}
    • {% flexible_include file='path' [ OPTIONS ] %}
    • {% flexible_include file="path" [ OPTIONS ] %}

    By default, the included file will escape characters <, { and }, unless do_not_escape is specified. Note that the [square brackets] merely indicate optional parameters and are not intended to be written literally.

    The file must be specified as a relative or absolute path on the server, or a command to execute. A URL cannot be provided (you cannot write a file name that starts with http: or https:). This capability is under consideration for a possible future release.

    Options

    • do_not_escape includes the content without HTML escaping it.
    • pre causes the included file to be wrapped inside a <pre></pre> tag, no label is generated.
    • strip removes leading and trailing whitespace.

    The following options imply pre:

    • copyButton draws an icon at the top right of the <pre></pre> area, which causes the included contents to be copied to the clipboard.
    • download uses the name of the file as a label, and displays it above the <pre></pre> tag. Clicking the label causes the file to be downloaded. The file must be local to the Jekyll website.
    • label specifies that an automatically generated label be placed above the contents. There is no need to specify this option if download or copyButton options are provided.
    • label="blah blah" specifies a label for the contents; this value overrides the default label. The value can be enclosed in single or double quotes. If you want to display a text message other than the file name, use this option.
    • number numbers the lines.

    Usage Examples

    This example shows a typical set of options. A label is automatically generated from the file name or process output that is included:

    HTML or markdown
    {% flexible_include copyButton download file="~/.mem_settings.yaml" %}

    Here is what the above looks like when rendered by a web browser:

    append_file_name: sample.html
    output_format: qt
    

    ... and without the download option, but still including the copyButton option:

    .mem_settings.yaml
    append_file_name: sample.html
    output_format: qt
    

    ... now with just the pre option:

    .mem_settings.yaml
    append_file_name: sample.html
    output_format: qt
    

    ... and finally this is the result of using flexible_include without any options. The generated text is outlined in a <pre></pre> tag so it is noticable. The file has a trailing newline, which is apparent below, however flexible_include trims any leading and trailing whitespace when the pre keyword option is specified or implied, or when the strip keyword option is specified. You can disable this behavior by specifying strip=false.

    append_file_name: sample.html
    output_format: qt
    
    
    
    

    Dark Mode

    Normally my website uses light colors, however some content displays better on a dark background. The dark option causes the generated <pre> tag to have the dark class applied. You can define the CSS for the dark and darkLabel classes. The CSS that defines those classes for this web site is here.

    .mem_settings.yaml
    append_file_name: sample.html
    output_format: qt
    

    Home Directory

    The included file path can use a tilde (~) to denote the user $HOME directory.

    {% flexible_include ~/.mem_settings.yaml %}
    append_file_name: sample.html
    output_format: qt
    

    Environment variables can be used; they will be expanded according to the environment variables that were current in the process when the Jekyll generator launched.

    {% flexible_include '/home/mslinn/.gitconfig' %}
    [alias]
      lol=log --graph --decorate --pretty=oneline --abbrev-commit
      lola=log --graph --decorate --pretty=oneline --abbrev-commit --all
      ls=ls-files
      st = status
      ci = commit
      br = branch
      co = checkout
      df = diff
      dc = diff --cached
      dif = diff --word-diff=color --ignore-space-at-eol
      lg = log -p
      ign = ls-files -o -i --exclude-standard
    	pwd = !pwd
    [branch "master"]
    	remote = origin
    	merge = refs/heads/master
    [core]
    	filemode = false
    	autocrlf = input
    	safecrlf = false
    	excludesfile = C:\\Users\\Mike Slinn\\Documents\\gitignore_global.txt
    	pager = less -F
    [color]
    	status = auto
    	branch = auto
        ui = auto
    [gui]
    	trustmtime = true
    [push]
      default = matching
      autoSetupRemote = true  
    [user]
    	name = Mike Slinn
    	email = mslinn@mslinn.com
    [rebase]
    	autostash = true
    [diff "exif"]
    	textconv = exiftool
    [diff]
    	compactionHeuristic = true
    	renames = 0
    [hub]
    	protocol = git
    [pull]
    	rebase = false
    [init]
    	defaultBranch = master
    [log]
    	date = local
    [creategem]
    	githubuser = mslinn
    [nugem]
    	githubuser = mslinn
    	gemserver = ""
    [safe]
    	directory = /mnt/f/work/ibm
    [credential]
    	helper = store
    

    Environment Variable Expansion

    This example includes the output of running the bash command which jekyll, according to the Ruby environment that was current when the Jekyll generator was launched:

    {% flexible_include '!which jekyll' %}
    /home/mslinn/.rbenv/versions/3.1.2/bin/jekyll

    Strip Leading and Trailing Whitespace

    Sometimes a file or process contains leading or trailing whitespace.

    {% flexible_include copyButton download .mem_settings.yaml %}

    Renders as:

    
    
    append_file_name: sample.html
    output_format: qt
    
    
    
    

    The strip keyword option removes leading and trailing whitespace.

    {% flexible_include copyButton download strip .mem_settings.yaml %}

    Renders as:

    append_file_name: sample.html
    output_format: qt

    Does This Make Your Brain Hurt?

    Ready to have your mind twisted? This invocation:

    Shell
    {% flexible_include '~/.mem_settings.yaml' download copyButton
      label='&lcub;% flexible_include download copyButton
        ~/.mem_settings.yaml %&rcub;' %}

    Renders like this:

    append_file_name: sample.html
    output_format: qt
    

    Hint: &lcub; and &rcub; are HTML entities for { and }, respectively.

    Highlighting Text

    A regular expression can be passed to the highlight option. This causes text that matches the regex pattern to be wrapped within a <span class="bg_yellow"></span> tag.

    The following highlights filenames in a directory listing that contain django-admin that might contain dots, underscores, dashes and forward slashes:

    HTML or markdown
    {% flexible_include
      highlight="[\w./\-_]*django-admin[\w.\-_]*"
      label="ls ~/venv/aw/bin/*"
      file="!ls ~/venv/aw/bin/*"
    %}

    Renders as:

    ls ~/venv/aw/bin/*
    /home/mslinn/venv/aw/bin/activate
    /home/mslinn/venv/aw/bin/activate.csh
    /home/mslinn/venv/aw/bin/activate.fish
    /home/mslinn/venv/aw/bin/activate.ps1
    /home/mslinn/venv/aw/bin/activate.xsh
    /home/mslinn/venv/aw/bin/activate_this.py
    /home/mslinn/venv/aw/bin/django-admin
    /home/mslinn/venv/aw/bin/django-admin.py
    /home/mslinn/venv/aw/bin/easy_install
    /home/mslinn/venv/aw/bin/easy_install-3.8
    /home/mslinn/venv/aw/bin/easy_install3
    /home/mslinn/venv/aw/bin/faker
    /home/mslinn/venv/aw/bin/pip
    /home/mslinn/venv/aw/bin/pip3
    /home/mslinn/venv/aw/bin/pip3.10
    /home/mslinn/venv/aw/bin/pip3.11
    /home/mslinn/venv/aw/bin/pybabel
    /home/mslinn/venv/aw/bin/python
    /home/mslinn/venv/aw/bin/python3
    /home/mslinn/venv/aw/bin/python3.8
    /home/mslinn/venv/aw/bin/sqlformat
    /home/mslinn/venv/aw/bin/wheel
    /home/mslinn/venv/aw/bin/wheel-3.8
    /home/mslinn/venv/aw/bin/wheel3
    
    /home/mslinn/venv/aw/bin/__pycache__:
    django-admin.cpython-39.pyc

    Numbering Lines

    HTML or markdown
    {% flexible_include
      label="ls ~/venv/aw/*"
      file="!ls ~/venv/aw/*"
      number
    %}

    Renders as (notice that the numbers are unselectable):

    ls ~/venv/aw/*
      1: /home/mslinn/venv/aw/pyvenv.cfg
      2: 
      3: /home/mslinn/venv/aw/bin:
      4: __pycache__
      5: activate
      6: activate.csh
      7: activate.fish
      8: activate.ps1
      9: activate.xsh
     10: activate_this.py
     11: django-admin
     12: django-admin.py
     13: easy_install
     14: easy_install-3.8
     15: easy_install3
     16: faker
     17: pip
     18: pip3
     19: pip3.10
     20: pip3.11
     21: pybabel
     22: python
     23: python3
     24: python3.8
     25: sqlformat
     26: wheel
     27: wheel-3.8
     28: wheel3
     29: 
     30: /home/mslinn/venv/aw/lib:
     31: python3.10
     32: python3.11
     33: python3.8
     34: python3.9
    

    CSS

    Below are the CSS declarations that I defined for the flexible_include tag that produced the above output. This CSS is the same as used by pre.

    .clear {
      clear: both;
    }
    
    ol li .codeLabel {
      padding-left: 1.75em;
    }
    
    .error {
      color: white;
      background-color: darkred;
      padding: 2px;
    }
    
    #main-content li > div.jekyll_pre, li > div.jekyll_pre {
      margin-top: 20px;
    }
    
    .jekyll_pre + div,
    .jekyll_pre + p,
    .jekyll_pre + ul,
    .jekyll_pre + ol,
    .jekyll_pre + dl {
      margin-top: 20px;
    }
    
    .jekyll_pre + .jekyll_pre {
      margin-top: 2em;
    }
    
    .pre_tag {
      margin-bottom: 1em;
    }
    
    .tree {
      line-height: 1;
    }
    

    Restricting Directory Access

    By default, flexible_include can read from all directories according to the permissions of the user account that launched the jekyll process. For security-conscience environments, the accessible paths can be restricted.

    Defining an environment variable called FLEXIBLE_INCLUDE_PATHS prior to launching Jekyll will restrict the paths that flexible_include will be able to read from. This environment variable consists of a colon-delimited set of regular expressions. For example, the following restricts access to only the files within:

    1. The ~/my_dir directory tree of the account of the user that launched Jekyll.
    2. The directory tree rooted at /var/files.
    3. The directory tree rooted at the expanded value of the $work environment variable.
    Shell
    $ export FLEXIBLE_INCLUDE_PATHS='~/my_dir/.*:/var/files/.*:$work/.*'

    If a reference to an unauthorized file is intercepted, a big red message will appear on the generated web page that says something like

    Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.

    ... and an error message will be logged on the console that looks something like:

    ERROR FlexibleInclude: _posts/2020/2020-10-03-jekyll-plugins.html - Access to #{path} denied by FLEXIBLE_INCLUDE_PATHS value.

    Restricting Arbitrary Processes

    By default, flexible_include can execute any command. You can disable that by setting the environment variable DISABLE_FLEXIBLE_INCLUDE to any non-empty value.

    Shell
    $ export DISABLE_FLEXIBLE_INCLUDE=true

    If a potential command execution is intercepted, a big red message will appear on the generated web page that says:

    Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.

    ... and an error message will be logged on the console that looks something like:

    ERROR FlexibleInclude: #{path} - Arbitrary command execution denied by DISABLE_FLEXIBLE_INCLUDE value.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.