Skip to content

Quickstart: Controllers

Brian edited this page Mar 17, 2015 · 13 revisions

Jump to:

Controllers are what contain the logic of your PencilBlue website. They are the middlemen between your data and HTML templates, pulling information from the database and fitting it into a layout for user consumption.

The Base Controller

The PencilBlue base controller, located at /controllers/base_controller.js, contains the necessary variables and functions for rendering a page and defining routes to itself. All controllers in PencilBlue extend the BaseController, or extend other controllers that are derived from the base controller themselves.

The following JavaScript code defines a new controller prototype and has it extend BaseController.

module.exports = function(pb) {

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

  // declare the controller & extend form the base controller
  function MyController(){};
  util.inherits(MyController, BaseController);

  return MyController;
};

Rendering Pages

When an incoming request's HTTP verb and URL match that of a registered controller, the RequestHandler will instantiate the controller and initialize it by calling the init function. If extending from the base controller, a default implementation of init is provided for you. It will set accessors for the template service, localization service, the request handle, and the response handle. The RequestHandler will then proceed to call either the function that matches the specified handler or the render function by default. The handler function will always be passed a single parameter of a callback function to return the content to be rendered, including HTML header information like content-type and status code.

The following Javascript declares a controller that returns a string as an HTML output with the render function.

module.exports = function(pb) {

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

  // declare controller and extend from the base controller
  function MyController(){};
  util.inherits(MyController, pb.BaseController);

  MyController.prototype.render = function(cb) {
    var output = {
        content_type: 'text/html',
        code: 200,
        content: '<h1>Hello, World</h1>'
    };
    cb(output);
  };

Very few pages are going to render hardcoded strings. Most of them will load a HTML template with the template service, like in the following example.

module.exports = function(pb) {

  function MyController(){};
  util.inherits(MyController, pb.BaseController);

  MyController.prototype.render = function(cb) {
    var output = {
        content_type: 'text/html',
        code: 200
    };
    this.ts.load('hello_world', function(error, result) {
        output.content = result;
        cb(output);
    });
  };

Registering Routes

Your plugins' custom controllers will need to register new routes or override existing ones. This is accomplished with the getRoutes function in your controller. Note that the getRoutes function is tied directly to the controller and not its prototype.

/**
 * Provides the routes that are to be handled by an instance of this prototype.  
 * The route provides a definition of path, permissions, authentication, and 
 * expected content type. 
 * Method is optional
 * Path is required
 * Permissions are optional
 * Access levels are optional
 * Content type is optional
 * 
 * @param cb A callback of the form: cb(error, array of objects)
 */
MyController.getRoutes = function(cb) {
	var routes = [
		{
            //This is the HTTP verb that should be used for the route.  If none 
            //is provided it is assumed to be executed for all routes.
	    	method: 'get',
            
            //This is the URL pattern that will be used to match against 
            //incoming requests to determine if this the controller to execute. 
            //The route handler supports parameters and wild cards such as: 
            //"/user/:id/*
	    	path: "/sample/random/text",
            
            //indicates if the route requires the user to be authenticated in 
            //order to access the resource.  Defaults to false.
	    	auth_required: true,
            
            //specifies the role that is required of the authenticated user.  
            //Defaults to no role
	    	access_level: ACCESS_USER,
            
            //specifies the specific permissions that are required by users in 
            //order to access the resource.  The permissions are AND'ed 
            //together. Defaults to []
	    	permissions: ["sample_view"],
            
            //Indicates the content type that will be returned to the 
            //requesting client. Defaults to "text/html"
	    	content_type: 'text/html',
            
            //specifies the controller prototype instance function that will be 
            //called to handle the incoming request.  Defaults to "render"
            handler: "getRandomText"
		},
//		{ //Use the setup below to override the home page when your plugin is set as the active theme
//	    	method: 'get',
//	    	path: "/",
//	    	auth_required: false,
//	    	content_type: 'text/html'
//		}
	];
	cb(null, routes);
};

Putting It All Together

With all the pieces in place, a basic but complete controller might look like the following example.

module.exports = function(pb) {

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

  // Instantiate the controller & extend the base controller
  function MyController(){};
  util.inherits(MyController, pb.BaseController);

  /**
   * Renders a HTML page from the hello_world template
   * @method hellWorld
   * @param {Function} cb
   */
  MyController.prototype.helloWorld = function(cb) {
    var output = {

        //specify the content here or when you declare the route
        content_type: 'text/html',
        code: 200
    };
    this.ts.load('hello_world', function(error, result) {
        output.content = result;
        cb(output);
    });
  };

  /**
   * Define the routes for the controller
   * @static
   * @method getRoutes
   * @param {Function} cb Takes two parameters.  The first an Error, if 
   * occurred, and an array of objects describing the resources managed 
   * by this controller
   */
  MyController.getRoutes = function(cb) {
    var routes = [{
        method: 'get',
        path: "/hello-world",
        handler: 'helloWorld',
        auth_required: false,
        content_type: 'text/html'
    }];
    cb(null, routes);
  };

  // return the controller prototype so it can be loaded into the application
  return MyController;
};