Working with Templates

Kong Portal is built on top of the lua-resty-template templating library, which can be viewed here: https://github.com/bungle/lua-resty-template. Basic usage of the library will be described below. Refer to the source documentation for a more in-depth look at what it can accomplish.

Syntax

(excerpt from lua-resty-templates documentation)

You may use the following tags in templates:

  • {{expression}}, writes result of expression - HTML escaped
  • {*expression*}, writes result of expression
  • {% lua code %}, executes Lua code
  • {(path-to-partial)}, include partial file by path, you may also supply context for the file {(partials/header.html, { message = "Hello, World" } )}

  • {-block-}...{-block-}, wraps inside of a {-block-} to a value stored in a blocks table with a key block (in this case), see using blocks. Don’t use predefined block names verbatim and raw.

  • {-verbatim-}...{-verbatim-} and {-raw-}...{-raw-} are predefined blocks whose inside is not processed by the lua-resty-template but the content is outputted as is.
  • {# comments #} everything between {# and #} is considered to be commented out (i.e., not outputted or executed).

Show custom properties

You may work with custom properties in your OpenAPI spec. To expose custom properties in Dev Portal, change the property showExtensions to true in the spec-renderer.html file. By default, showExtensions is false.

Partials

Partials are snippets of HTML that layouts can reference. Partials have access to all the same data that its layout does, and can even call other partials. Breaking your code into partials can help organize large pages, as well as allow different layouts share common page elements.

content/index.txt

  1. ---
  2. layout: index.html
  3. title: Partials
  4. header_logo: assets/images/example.jpeg
  5. header_nav_items:
  6. about:
  7. href: /about
  8. guides:
  9. href: /guides
  10. hero_title: Partials Info
  11. hero_description: Partials are wicked sick!
  12. ---

layouts/index.html

  1. {(partials/header.html)}
  2. <div class="content">
  3. {(partials/hero.html)}
  4. </div>
  5. {(partials/footer.html)}

partials/header.html

  1. <header class="row">
  2. <div class="column">
  3. <img src="{{page.header_logo}}"/>
  4. </div>
  5. <div class="column">
  6. {(partials/header_nav.html)}
  7. </div>
  8. </header>

partials/header_nav.html

  1. <ul>
  2. {% for title, href in each(page.header_nav_items) do %}
  3. <li><a href="{{href}}">{{title}}</a></li>
  4. {% end %}
  5. </ul>

partials/hero.html

  1. <h1>{{page.hero_title}}</h1>
  2. <p>{{page.hero_description}}</p>

partials/hero.html

  1. <footer>
  2. <p>footer</p>
  3. </footer>

Output:

  1. <header class="row">
  2. <div class="column">
  3. <img src="assets/images/example.jpeg"/>
  4. </div>
  5. <div class="column">
  6. <ul>
  7. <li><a href="/about">about</a></li>
  8. <li><a href="/guieds">guides</a></li>
  9. </ul>
  10. </div>
  11. </header>
  12. <h1>Partials Info</h1>
  13. <p>Partials are wicked sick!</p>
  14. <footer>
  15. <p>footer</p>
  16. </footer>

Blocks

Blocks can be used to embed a view or partial into another template. Blocks are particularly useful when you want different templates to share a common wrapper.

In the example below, notice that the content file is referencing index.html, and not wrapper.html.

content/index.txt

  1. ---
  2. layout: index.html
  3. title: Blocks
  4. description: Blocks are the future!
  5. ---

layouts/index.html

  1. {% layout = "layouts/wrapper.html" %} <- syntax declaring where to find the block
  2. {-main-} <- delimiter describing what content renders in block
  3. <div class="content">
  4. <h1>{{page.title}}</h1>
  5. <p>{{page.description}}<p>
  6. </div>
  7. {-main-}

layouts/wrapper.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Testing lua-resty-template blocks</title>
  5. </head>
  6. <body>
  7. <header>
  8. <p>header</p>
  9. </header>
  10. {*main*} <- syntax indicating where to place the block
  11. <footer>
  12. <p>footer</p>
  13. </footer>
  14. </body>
  15. </html>

Output:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Testing lua-resty-template blocks</title>
  5. </head>
  6. <body>
  7. <header>
  8. <p>header</p>
  9. </header>
  10. <div class="content">
  11. <h1>Blocks</h1>
  12. <p>Blocks are the future!<p>
  13. </div>
  14. <footer>
  15. <p>footer</p>
  16. </footer>
  17. </body>
  18. </html>

Collections

Collections are a powerful tool enabling you to render sets of content as a group. Content rendered as a collection share a configurable route pattern, as well as a layout. Collections are configured in your portals portal.conf.yaml file.

The example below shows all the necessary configuration/files needed to render a basic blog collection made up of individual posts.

portal.conf.yaml

  1. name: Kong Portal
  2. theme:
  3. name: base
  4. collections:
  5. posts:
  6. output: true
  7. route: /:stub/:collection/:name
  8. layout: post.html

Above you can see a collections object was declared, which is made up of individual collection configurations. In this example, you are configuring a collection called posts. The renderer looks for a root directory called _posts within the content folder for individual pages to render. If you created another collection conf called animals, the renderer would look for a directory called _animals for content files to render. Each configuration item is made up of a few parts:

  • output
    • required: false
    • type: boolean
    • description: This optional attribute determines whether the collections should render or not. When set to false, virtual routes for the collection are not created.
  • route
    • required: true
    • type: string
    • default: none
    • description: The route attribute is required and tells the renderer what pattern to generate collection routes from. A collection route should always include at least one valid dynamic namespace that uniquely identifies each collection member.
      • Any namespace in the route declaration which begins with : is considered dynamic.
      • Only certain dynamic namespaces are recognized by Kong as valid:
        • :title: Replaces namespace with a contents title, declared in headmatter.
        • :name: Replaces namespace with the filename of a piece of content.
        • :collection: Replaces namespace with name of current collection.
        • :stub: Replaces namespace with value of headmatter.stub in each contents headmatter.
  • layout

    • required: true

      • type: string
      • description: The layout attribute determines what HTML layout the collections use to render. The path root is accessed from within the current themes layouts directory.

        content/_posts/post1.md

  1. ---
  2. title: Post One
  3. stub: blog
  4. ---
  5. This is my first post!

content/_posts/post2.md

  1. ---
  2. title: Post Two
  3. stub: blog
  4. ---
  5. This is my second post!

themes/base/layouts/post.html

  1. <h1>{{ page.title }}</h1>
  2. <p>{* page.body *}</p>

Output:

From <kong_portal_gui_url>/blog/posts/post1:

  1. <h1>Post One</h1>
  2. <p>This is my first post!</p>

From <kong_portal_gui_url>/blog/posts/post2:

  1. <h1>Post Two</h1>
  2. <p>This is my second post!</p>

Kong Template Helpers - Lua API

Kong Template Helpers are a collection of objects that give access to your portal data at the time of render and provide powerful integrations into Kong.

Global:

  • l - Locale helper, first version, gets values from the currently active page.
  • each - Commonly used helper to iterate over lists or tables.
  • print - Commonly used helper to print lists / tables.
  • markdown - Commonly used helper to print lists / tables.
  • json_decode - Decode JSON to Lua table.
  • json_encode - Encode Lua table to JSON.

Objects:

  • portal - The portal object refers to the current workspace portal being accessed.
  • page - The page object refers to the currently active page and its contents.
  • user - The user object represents the currently logged in developer accessing the Kong Portal.
  • theme - The theme object represents the currently active theme and its variables.
  • tbl = Table helper methods. Examples: map, filter, find, sort.
  • str = String helper methods. Examples: lower, upper, reverse, endswith.
  • helpers - Helper functions simplify common tasks or provide easy shortcuts to Kong Portal methods.

Terminology / Definitions:

  • list - Also referred to commonly as an array ([1, 2, 3]) in Lua is a table-like object ({1, 2, 3}). Lua list index starts at 1 not 0. Values can be accessed by array notation (list[1]).
  • table - Also commonly known as an object or HashMap ({1: 2}) in Lua looks like ({1 = 2}). Values can be accessed by array or dot notation (table.one or table["one"]).

l(key, fallback)

Returns the current translation by key from the currently active page.

Return Type

  1. string

Usage

Using content/en/example.txt:

  1. ---
  2. layout: example.html
  3. locale:
  4. title: Welcome to {{portal.name}}
  5. slogan: The best developer portal ever created.
  6. ---

Using content/es/example.txt:

  1. ---
  2. layout: example.html
  3. locale:
  4. title: Bienvenido a {{portal.name}}
  5. slogan: El mejor portal para desarrolladores jamás creado.
  6. ---

Using layouts/example.html:

  1. <h1>{* l("title", "Welcome to" .. portal.name) *}</h1>
  2. <p>{* l("slogan", "My amazing developer portal!") *}</p>
  3. <p>{* l("powered_by", "Powered by Kong.") *}</p>

Output:

For en/example:

  1. <h1>Welcome to Kong Portal</h1>
  2. <p>The best developer portal ever created.</p>
  3. <p>Powered by Kong.</p>

For es/example:

  1. <h1>Bienvenido a Kong Portal</h1>
  2. <p>El mejor portal para desarrolladores jamás creado.</p>
  3. <p>Powered by Kong.</p>

Notes

  • l(...) is a helper from the page object. It can be also accessed via page.l. However, page.l does not support template interpolation (for example, `` will not work.)

each(list_or_table)

Returns the appropriate iterator depending on what type of argument is passed.

Return Type

  1. Iterator

Usage

Template (List):

  1. {% for index, value in each(table) do %}
  2. <ul>
  3. <li>Index: {{index}}</li>
  4. <li>Value: {{ print(value) }}</li>
  5. </ul>
  6. {% end %}

Template (Table):

  1. {% for key, value in each(table) do %}
  2. <ul>
  3. <li>Key: {{key}}</li>
  4. <li>Value: {{ print(value) }}</li>
  5. </ul>
  6. {% end %}

print(any)

Returns the output of an input value as a string.

Return Type

  1. string

Usage

Template (Table):

  1. <pre>{{print(page)}}</pre>

markdown(string)

Returns HTML from the markdown string passed as an argument. If a string argument is not valid markdown, the function will return the string as is. To render properly, the helper should be used with raw {* *} delimiters.

Return Type

  1. string

Usage

Template (string as an argument):

  1. <pre>{* markdown("##This is Markdown") *}</pre>

Template (content val as an argument):

  1. <pre>{* markdown(page.description) *}</pre>

json_encode(object)

JSON encodes Lua table passed as argument

Return Type

  1. string

Usage

Template:

  1. <pre>{{ json_encode({ dog = cat }) }}</pre>

json_decode(string)

Decodes JSON string argument to Lua table

Return Type

  1. table

Usage

Template:

  1. <pre>{{ print(json_encode('{"dog": "cat"}')) }}</pre>

portal

portal gives access to data relating to the current portal, this includes things like portal configuration, content, specs, and layouts.

You can access the current workspace portal config directly on the portal object like so:

  1. portal[config_key] or portal.config_key

For example portal.auth is a portal config value. You can find a list of config values by reading the portal section of kong.conf.

From kong.conf

The portal only exposes config values that start with portal_, and they can be access by removing the portal_ prefix.

Some configuration values are modified or customized, these customizations are documented under the Portal Members section.

portal.workspace

Returns the current portal’s workspace.

Return Type
  1. string
Usage

Template:

  1. {{portal.workspace}}

Output:

  1. default

portal.url

Returns the current portal’s url with workspace.

Return Type
  1. string
Usage

Template:

  1. {{portal.url}}

Output:

  1. http://127.0.0.1:8003/default

portal.api_url

Returns the configuration value for portal_api_url with the current workspace appended.

Return Type
  1. string or nil
Usage

Template:

  1. {{portal.api_url}}

Output when portal_api_url = http://127.0.0.1:8004:

  1. http://127.0.0.1:8004/default

portal.auth

Returns the current portal’s authentication type.

Return Type
  1. string
Usage

Printing a value

Input:

  1. {{portal.auth}}

Output when portal_auth = basic-auth:

  1. basic-auth

Checking if authentication is enabled

Input:

  1. {% if portal.auth then %}
  2. Authentication is enabled!
  3. {% end %}

Output when portal_auth = basic-auth:

  1. Authentication is enabled!

portal.specs

Returns an array of specification files contained within the current portal.

Return type
  1. array
Usage

Viewing a content value

Template:

  1. <pre>{{ print(portal.specs) }}</pre>

Output:

  1. {
  2. {
  3. "path" = "content/example1_spec.json",
  4. "content" = "..."
  5. },
  6. {
  7. "path" = "content/documentation/example1_spec.json",
  8. "content" = "..."
  9. },
  10. ...
  11. }

Looping through values

Template:

  1. {% for _, spec in each(portal.specs) %}
  2. <li>{{spec.path}}</li>
  3. {% end %}

Output:

  1. <li>content/example1_spec.json</li>
  2. <li>content/documentation/example1_spec.json</li>

Filter by path

Template:

  1. {% for _, spec in each(helpers.filter_by_path(portal.specs, "content/documentation")) %}
  2. <li>{{spec.path}}</li>
  3. {% end %}

Output:

  1. <li>content/documentation/example1_spec.json</li>

portal.developer_meta_fields

Returns an array of developer meta fields available/required by Kong to register a developer.

Return Type

  1. array
Usage

Printing a value

Template:

  1. {{ print(portal.developer_meta_fields) }}

Output:

  1. {
  2. {
  3. label = "Full Name",
  4. name = "full_name",
  5. type = "text",
  6. required = true,
  7. },
  8. ...
  9. }

Looping through values

Template:

  1. {% for i, field in each(portal.developer_meta_fields) do %}
  2. <ul>
  3. <li>Label: {{field.label}}</li>
  4. <li>Name: {{field.name}}</li>
  5. <li>Type: {{field.type}}</li>
  6. <li>Required: {{field.required}}</li>
  7. </ul>
  8. {% end %}

Output:

  1. <ul>
  2. <li>Label: Full Name</li>
  3. <li>Name: full_name</li>
  4. <li>Type: text</li>
  5. <li>Required: true</li>
  6. </ul>
  7. ...

page

page gives access to data relating to the current page, which includes things like page url, path, breadcrumbs, and more.

When you create a new content page, you are able to define key-values. Here you are going to learn how to access those values and a few other interesting things.

You can access the key-values you define directly on the page object like so:

  1. page[key_name] or page.key_name

You can also access nested keys like so:

  1. page.key_name.nested_key

Be careful! To avoid output errors, make sure that the key_name exists before accessing nested_key as shown below:

  1. {{page.key_name and page.key_name.nested_key}}

page.route

Returns the current page’s route/path.

Return Type
  1. string
Usage

Template:

  1. {{page.route}}

Output, given url is http://127.0.0.1:8003/default/guides/getting-started:

  1. guides/getting-started

page.url

Returns the current page’s url.

Return Type
  1. string
Usage

Template:

  1. {{page.url}}

Output, given url is http://127.0.0.1:8003/default/guides/getting-started:

  1. http://127.0.0.1:8003/default/guides/getting-started

page.breadcrumbs

Returns the current page’s breadcrumb collection.

Return Type
  1. table[]
Item Properties
  • item.path - Full path to item, no forward-slash prefix.
  • item.display_name - Formatted name.
  • item.is_first - Is this the first item in the list?
  • item.is_last - Is this the last item in the list?
Usage

Template:

  1. <div id="breadcrumbs">
  2. <a href="">Home</a>
  3. {% for i, crumb in each(page.breadcrumbs) do %}
  4. {% if crumb.is_last then %}
  5. / {{ crumb.display_name }}
  6. {% else %}
  7. / <a href="{{crumb.path}}">{{ crumb.display_name }}</a>
  8. {% end %}
  9. {% end %}
  10. </div>

page.body

Returns the body of the current page as a string. If the route’s content file has a .md or .markdown extension, the body will be parsed from markdown to HTML.

Return Type
  1. string
Usage for .txt, .json, .yaml, .yml templates

index.txt:

  1. This is text content.

Template:

  1. <h1>This is a title</h1>
  2. <p>{{ page.body) }}</p>

Output:

  1. > # This is a title
  2. > This is text content.
Usage for .md, .markdown templates

Template (markdown): Use the raw delimiter syntax {* *} to render markdown within a template.

index.txt

  1. # This is a title
  2. This is text content.

Template:

  1. {* page.body *}

Output:

  1. > # This is a title
  2. > This is text content.

user

user gives access to data relating to the currently authenticated user. User object is only applicable when KONG_PORTAL_AUTH is enabled.

user.is_authenticated

Returns boolean value as to the current user’s authentication status.

Return Type
  1. boolean
Usage

Template:

  1. {{print(user.is_authenticated)}}

Output:

  1. true

user.has_role

Returns true if a user has a role given as an argument.

Return Type
  1. boolean
Usage

Template:

  1. {{print(user.has_role("blue"))}}

Output:

  1. true

user.get

Takes developer attribute as an argument and returns value if present.

Return Type
  1. any
Usage

Template:

  1. {{user.get("email")}}
  2. {{print(user.get("meta"))}}

Output:

  1. example123@konghq.com
  2. { "full_name" = "example" }

theme

The theme object exposes values set in your theme.conf.yaml file. In addition, any variable overrides contained in portal.conf.yaml will be included as well.

theme.colors

Returns a table of color variables and their values as key-value pairs.

Return Type
  1. table
Usage

theme.conf.yaml:

  1. name: Kong
  2. colors:
  3. primary:
  4. value: '#FFFFFF'
  5. description: 'Primary Color'
  6. secondary:
  7. value: '#000000'
  8. description: 'Secondary Color'
  9. tertiary:
  10. value: '#1DBAC2'
  11. description: 'Tertiary Color'

Template:

  1. {% for k,v in each(theme.colors) do %}
  2. <p>{{k}}: {{v}}</p>
  3. {% end %}

Output:

  1. <p>primary: #FFFFFF</p>
  2. <p>secondary: #000000</p>
  3. <p>tertiary: #1DBAC2</p>

theme.color

Description

Takes color var by string argument, returns value.

Return Type
  1. string
Usage

theme.conf.yaml:

  1. name: Kong
  2. colors:
  3. primary:
  4. value: '#FFFFFF'
  5. description: 'Primary Color'
  6. secondary:
  7. value: '#000000'
  8. description: 'Secondary Color'
  9. tertiary:
  10. value: '#1DBAC2'
  11. description: 'Tertiary Color'

Template:

  1. <p>primary: {{theme.color("primary")}}</p>
  2. <p>secondary: {{theme.color("secondary")}}</p>
  3. <p>tertiary: {{theme.color("tertiary")}}</p>

Output:

  1. <p>primary: #FFFFFF</p>
  2. <p>secondary: #000000</p>
  3. <p>tertiary: #1DBAC2</p>

theme.fonts

Returns table of font vars and their values as key-value pairs.

Return Type
  1. table
Usage

theme.conf.yaml:

  1. name: Kong
  2. fonts:
  3. base: Roboto
  4. code: Roboto Mono
  5. headings: Lato

Template:

  1. {% for k,v in each(theme.fonts) do %}
  2. <p>{{k}}: {{v}}</p>
  3. {% end %}

Output:

  1. <p>base: Roboto</p>
  2. <p>code: Roboto Mono</p>
  3. <p>headings: Lato</p>

theme.font

Takes font name by string argument, returns value.

Return Type
  1. string
Usage

theme.conf.yaml:

  1. name: Kong
  2. fonts:
  3. base: Roboto
  4. code: Roboto Mono
  5. headings: Lato

Template:

  1. <p>base: {{theme.font("base")}}</p>
  2. <p>code: {{theme.font("code")}}</p>
  3. <p>headings: {{theme.font("headings")}}</p>

Output:

  1. <p>base: #FFFFFF</p>
  2. <p>code: #000000</p>
  3. <p>headings: #1DBAC2</p>

str

Table containing useful string helper methods.

Usage

.upper() example:

  1. <pre>{{ str.upper("dog") }}</pre>

Methods

str.byte
str.char
str.dump
str.find
str.format
str.gfind
str.gmatch
str.gsub
str.len
str.lower
str.match
str.rep
str.reverse
str.sub
str.upper
str.isalpha
str.isdigit
str.isalnum
str.isspace
str.islower
str.isupper
str.startswith
str.endswith
str.join
str.splitlines
str.split
str.expandtabs
str.lfind
str.rfind
str.replace
str.count
str.ljust
str.rjust
str.center
str.lstrip
str.rstrip
str.strip
str.splitv
str.partition
str.rpartition
str.at
str.lines
str.title
str.shorten
str.quote_string

tbl

Table containing useful table helper methods

Usage

.map() example:

  1. {% tbl.map({"dog", "cat"}, function(item) %}
  2. {% if item ~= "dog" then %}
  3. {% return true %}
  4. {% end %}
  5. {% end) %}

Methods

tbl.getn
tbl.setn
tbl.maxn
tbl.insert
tbl.remove
tbl.concat
tbl.map
tbl.foreach
tbl.foreachi
tbl.sort
tbl.sortv
tbl.filter
tbl.size
tbl.index_by
tbl.transform
tbl.range
tbl.reduce
tbl.index_map
tbl.makeset
tbl.union
tbl.intersection
tbl.count_map
tbl.set
tbl.new
tbl.clear
tbl.removevalues
tbl.readonly
tbl.update
tbl.copy
tbl.deepcopy
tbl.icopy
tbl.move
tbl.insertvalues
tbl.deepcompare
tbl.compare
tbl.compare_no_order
tbl.find
tbl.find_if
tbl.keys
tbl.values
tbl.sub
tbl.merge
tbl.difference
tbl.zip