Skip to content

Quickstart: Developing Themes

Brian edited this page Mar 17, 2015 · 24 revisions

###Video Tutorial Available

Jump to:

Note: you may want to read HTML Templates, Controllers, and Plugins before starting this quickstart subject. They cover the more general topics of setting up a plugin and providing alternate controllers and templates for existing PencilBlue pages.

Themes are an integral part of website development with PencilBlue, and we've added multiple tools to simplify the process for developers. From hierarchical loading of templates, to built-in localization, to a slew of services designed to easily retrieve the data you need, you'll find that building themes for PencilBlue is a quick and repeatable process.

Create the Plugin

For this quickstart, we'll be creating the landing page for a theme called "Company Website." Create a new plugin under /plugins/company/ and add a details.json file with the following content:

{
    "uid": "company",
    "name": "Company Website",
    "description": "A company website theme example.",
    "version": "0.0.1",
    "icon": "/imgs/sample.ico",
    "author": {
        "name": "John Doe",
        "email": "john.doe@customdev.com",
        "website": "http://www.customdev.com",
        "contributors": [
            {
                "name": "Jane Doe",
                "email": "jane.doe@customdev.com"
            }
        ]
    },
    "settings": [],
    "permissions": {
        "ACCESS_USER": [],
        "ACCESS_WRITER": [],
        "ACCESS_EDITOR": [],
        "ACCESS_MANAGING_EDITOR": []
    },
    "main_module": {
        "path": "company.js"
    },
    "theme": {
        "settings": [],
        "content_templates":[]
    }
}

The next thing we'll need to create is the main module file listed in details.json. Create /plugins/company/company.js and add the following code:

module.exports = function(pb) {

  function Company(){}

  /**
   * Called when the application is being installed for the first time.
   *
   * @param cb A callback that must be called upon completion.  cb(err, result).
   * The result is ignored
   */
  Company.onInstall = function(cb) {
    cb(null, true);
  };

  /**
   * Called when the application is uninstalling this plugin.  The plugin should
   * make every effort to clean up any plugin-specific DB items or any in function
   * overrides it makes.
   *
   * @param cb A callback that must be called upon completion.  cb(err, result).
   * The result is ignored
   */
  Company.onUninstall = function(cb) {
    cb(null, true);
  };

  /**
   * Called when the application is starting up. The function is also called at
   * the end of a successful install. It is guaranteed that all core PB services
   * will be available including access to the core DB.
   *
   * @param cb A callback that must be called upon completion.  cb(err, result).
   * The result is ignored
   */
  Company.onStartup = function(cb) {
    cb(null, true);
  };

  /**
   * Called when the application is gracefully shutting down.  No guarantees are
   * provided for how much time will be provided the plugin to shut down.
   *
   * @param cb A callback that must be called upon completion.  cb(err, result).
   * The result is ignored
   */
  Company.onShutdown = function(cb) {
    cb(null, true);
  };

  //return the module prototype
  return Company;
};

We'll be adding more to the details.json file later, but for now the minimum requirements will do.

Build a Custom Page

Many themes are tailored to a certain niche of website and offer the creation and management of pages that are expected of their genre. The home page of one theme might be a blog feed, like the default PencilBlue theme, while another might be a landing page with information on a company. For the latter type, the root route of PencilBlue must be overridden with a landing page controller.

Following the steps from the controllers quickstart, we'll start by creating a basic controller for the landing page at /plugins/company/controllers/landing_page.js.

module.exports = function(pb) {

  //PB dependencies
  var util           = pb.util;
  var BaseController = pb.BaseController;

  // Instantiate the controller & extend from the base controller
  var LandingPage = function(){};
  util.inherits(LandingPage, pb.BaseController);

  // Define the content to be rendered
  LandingPage.prototype.render = function(cb) {
    var output = {
        content_type: 'text/html',
        code: 200
    };
    this.ts.load('landing_page', function(error, result) {
        output.content = result;
        cb(output);
    });
  };

  // Define the routes for the controller
  LandingPage.getRoutes = function(cb) {
    var routes = [{
        method: 'get',
        path: "/",
        auth_required: false,
        content_type: 'text/html'
    }];
    cb(null, routes);
  };

  //return the prototype
  return LandingPage;
};

You'll notice that the render function of the controller is loading a template called "landing_page." We'll now need to create that template under /plugins/company/templates/landing_page.html. We'll keep this simple too, for now.

<!DOCTYPE html>
<html>
    <head>
        <title>^site_name^</title>
    </head>
    <body>
        This will be a company website theme.
    </body>
</html>

You'll see that the title tag contains the global directive of ^site_name^, which will be replaced with the site name from the website configuration.

Installing and Activating Your Theme

In order for the new theme to be used by PencilBlue, it must first be installed and set as the active theme in the admin section. In a browser, navigate to your site's /admin directory, log in, and navigate to Plugins->Manage.

If everything was set up correctly, the company plugins will have an Install button available. If not, look at the details of the plugin to see what went wrong. Install the plugin and, once the installation is complete, navigate to Plugins->Themes.

Under the Themes tab will be a select element showing the current active theme. Change this to Company Website and click the Apply Theme button.

Open a new tab and navigate to the root of your website. The new landing page should be displayed.

Activated theme landing page

Adding Custom Settings

Themes are meant to be shells in which users put their own content, so we'll want to add some custom settings to our plugin that will drive content on the landing page. Update the settings portion of your details.json file with the following:

"settings": [
    {
        "name": "landing_page_headline"
        "value": ""
    },
    {
        "name": "landing_page_subheader"
        "value": ""
    }
],

Now let's create a jumbotron in the landing page that's populated with the headline and subheader. Modify /plugins/company/templates/landing_page.html to contain the following:

<!DOCTYPE html>
<html>
    <head>
        <title>^site_name^</title>
        
        <link rel="stylesheet" type="text/css" href="^bootstrap_css_src^">
        
        <script type="text/javascript" src="^jquery_src^"></script>
        <script type="text/javascript" src="^bootstrap_js_src^"></script>
    </head>
    <body>
        <div class="jumbotron">
            <div class="container">
                <h1>^landing_page_headline^</h1>
                <p>^landing_page_subheader^</p>
            </div>
        </div>
    </body>
</html>

You'll notice a few new directives being used, here's what they'll get replaced with:

  • ^bootstrap_css_src^ the url for Bootstrap's CSS (CDN, Bower, or custom)
  • ^jquery_src^ the url for the jQuery library
  • ^bootstrap_js_src the url for Bootstrap's JavaScript
  • ^landing_page_headline^ we'll make this load our headline setting
  • ^landing_page_subheader^ we'll make this load our subheader setting

Now let's modify the landing page controller to load our theme's settings and replace our custom directives with them. Change the render function in /plugins/company/controllers/landing_page.js to this:

//PB dependencies
var PluginService = new pb.PluginService();

LandingPage.prototype.render = function(cb) {
    // Allows us to call this.[whatever] from inside anonymous functions 
    var self = this;
    var output = {
        content_type: 'text/html',
        code: 200
    };
    
    //Load the headline setting from the company plugin
    var pluginService = new PluginService();
    pluginService.getSetting('landing_page_headline', 'company', function(err, headline) {
        //Load the subheader setting
        pluginService.getSetting('landing_page_subheader', 'company', function(err, subheader) {
            // Register the custom directives
            self.ts.registerLocal('landing_page_headline', headline);
            self.ts.registerLocal('landing_page_subheader', subheader);
            self.ts.load('landing_page', function(error, result) {
                output.content = result;
                cb(output);
            });
        });
    });
};

Changing Plugin Settings

We'll need to uninstall and reinstall our theme plugin for the setting fields to be loaded into PencilBlue. Log back in to the admin section and navigate to Plugins->Manage. Click the dropdown arrow next to the Settings button for the Company Website plugin and select uninstall. Once the uninstall is complete, reinstall the plugin then go back to the themes page and re-apply Company Website as the active theme.

Navigate back to the manage plugins page and click the Settings button for the Company Website plugin. You'll be brought to a page with a form containing fields for the headline and subheading settings. Set them to whatever you want and click Save. Now when you navigate to the main page of your site, you'll see your custom settings populating a jumbotron.

Landing page with jumbotron

Next Steps

This is obviously just the beginning steps to creating a theme, but the concepts here cover most of the core actions needed to create pages and settings. To see how more complicated themes are put together, look at the Portfolio and Devblog themes.

If you have any questions, reach out to us on Twitter at @GetPencilBlue or on our subreddit.