Skip to content

Theme Engine

Bartłomiej Górnicki edited this page Mar 23, 2019 · 2 revisions

Engrave integrates custom theme engine to be able to create different but powerful themes for each blog / website. It uses PugJS syntax with server-side rendering and client-site javascript for interaction.

Theme structure

Each theme should contain two directories: public and theme. Public contains all files that can and should be accessible from the web. For example all CSS files and images. Theme directory contains theme's files with pugjs syntax that need to be interpreted by the renderer and may contain some sensitive data. They are not accessible other than by pug renderer.

Default and minimal theme structure looks as follow:

|--public
   |--css
   |--js 
|--theme
   |--website.pug
   |--index.pug
   |--single.pug
   |--category.pug
   |--404.pug
   |--error.pug

File with website.pug name is actually not used by the rendered but all other top-level files should expand that file. It should contain website base structure. Themes can contain other directories and files. It's a good idea to divide theme into small, separate files dedicated for example for footer, header and other website components.

Variables accesible from theme files (for server-side renderer)

Theme Engine provides lot of backend data to theme renderer. Some variables are accesible global, but most of them depends on current subpage.

Global variables

Those variables are accesible on every subpage and can be used in every file.

blog: {
   title: string // blog title
   slogan: string // blog slogan
   domain: string // blog domain without http:// prefix
   link_facebook: string // facebook fanpage url (full)
   link_twitter: string // twitter account url (full)
   link_twitter: string // twitter account url (full)
   link_linkedin: string // linkedin account url (full)
   link_instagram: string // instagram account url (full)
   logo_url: string // blog logo url (full)
   opengraph_default_image_url: string
   opengraph_default_description: string
   categories: [
      {
         name: string
         slug: string
         abstract: string // category description (may be used on category page)
      }
   ]
}

Specific fields may not exists or be empty strings so it's necessary to check those both cases before using it.

if blog.slogan && blog.slogan != ''
   p= blog.slogan

Single article (single.pug)

article: {
   title: string,
   permlink: string,
   username: string,
   body: string,
   abstract: string, // trimmed body without html and markdown syntax
   created: string (Date),
   tags: string[];
   thumbnail: string; // thumbnail url
   votes_count: number,
   value: number, // float but should be fixed to 2 digits on page
   comments: number, // number of comments
   categories: {
     name: string,
     slug: string,
     abstract: string, // category description
   }
}

featured: [
   // array of article objects
]

Notes:

Body field is a valid html code and could be displayed without escaping: Same for abstract (otherwise it will display html-encoded quote characters etc).

div#article-container
   div!= article.body

Main website view (index.pug)

latest: [
   // array of latest article objects
]

featured: [
   // array of featured article objects
]

Notes:

To display titles of every article in latest array on main page:

each article in latest
   h1= article.title  

Category listing (category.pug)


category: {
   name: string,
   slug: string,
   abstract: string // category long description
}

latest: [
   // array of latest article objects per specific category
]

featured: [
   // array of featured article objects
]

Error 404 (404.pug)

error - error message. It's not necessary to display it. Page can be customized

Generic error page (error.pug)

error - error message. Should be displayed in preformatted text

Comments fetching

Engrave backend provides endpoint for fetching comments for specific post. It's crucial to understand that on Steem blockchain, every user comment and article are the same thing - article is just a top-level comment (without parent). In order to get comments you need to provide author and permlink. This is a jQuery example:

function getContentReplies(author, permlink, callback) {
    $.ajax({
        type: "POST",
        url: "/comments",
        data: {
            author: author,
            permlink: permlink
        },
        success: function (data) {
            callback(null, data);
        },
        error: function (data) {
            callback(data, null);
        }
    });
}

In an example above, data would be a comments array. Every object will contain lot of additional blockchain-related fields but most important one are those:

data: [
   { 
      created: string // Date
      rendered: string // ready to display, rendered html code  
      value: number // reward value, should be fixed to 2 digits manually
      children: number // children comments quantity, should be used to optionally fetch children comments to build comments tree
   }
]

Single Sign-On

In order to enable Single Sign-On on your theme, you need to include file provided by Engrave Auth service. Add following file in your theme header:

script(src='https://auth.engrave.website/sslo.js')

After that, you need to create three javascript functions that are called right after specific events:

  • onLoad()
  • onIdentification(operation)
  • onLogout()

onLoad()

Invoked when the script finishes loading the SSO. It is always after onIdentification but should be used to hide JWT token provided by Auth service (it's passed as a query parameter) and to authenticate

Example:

function onLoad(){

    const urlParams = new URLSearchParams(window.location.search);
    const token = urlParams.get('jwt');

    if(token) {
        sslssso.login(token); // try to identificate with received token
        var url = [location.protocol, '//', location.host, location.pathname].join('');
        window.location.replace(url); // redirect to hide query parameter
    }
}

onIdentification(operation)

Invoked after page load if you have a valid token or after new JWT correct identification (if page is still loaded). If you do not have identification token, this function is not executed. You probably want to save received token in localStorage or cookies if you want to use it in different file (it's necessary to provide JWT token as a header for some operations like comment or vote).

function onIdentification(operation){
    const username = operation.payload.data.username;
    const jwt = operation.jwt;

    localStorage.setItem('aMZr1grXqFXbiRzmOGRM', jwt); // identificator might be random but you need it to get this again from different file

    // interact with some UI elements to mark logged user
    document.getElementById("nav-login").innerText = "Logout, @" + username;
    document.getElementById("nav-login").href = "javascript:sslssso.logout();";

    document.getElementById("login-box").style.display = "none";
    document.getElementById("comment-form-modal").style.display = "block";
}

onLogout()

Invoked when user logs out, should be used to remove stored token and mark that no user is logged in.

function onLogout(){

    document.getElementById("nav-login").innerText = "Login";
    document.getElementById("nav-login").href = "/login";

    document.getElementById("login-box").style.display = "block";
    document.getElementById("comment-form-modal").style.display = "none";

    localStorage.removeItem('aMZr1grXqFXbiRzmOGRM');
}

From now on, you should be able to log in and out using Steemconnect. Just redirect user to /login endpoint and after successful login, it should redirect you to previous location with jwt token as a query parameter. Use it in onLoad() function and execute sslssso.login(token); as in provided example. To logout, execute sslssso.logout();.