Skip to content

Developer manual: plugin framework

Jan Smolders edited this page Dec 1, 2015 · 1 revision

A 'plugin' is basically a wrapper for a feature you can use within MCJS.

A plugin consists of two parts. A public part and the core backend functionality. (a mini Model View Controller). If you take a look in the root of MCJS you'll see a folder called 'Apps'. These are the core apps and a great place to look around and get a feel for the way things work.

To start building your own plugin, you can use the plugin generator

The files the generator will generate will be the core of your plugin. With an index.js file to control all the incoming route requests, a folder called "Views" which contains the view (client side HTML). Although the JADE template engine supports loops, getters ans setters, we advise you to keep the view as plain as possible and add functionality using AJAX calls.

index.js

If we look at the example of the index.js below, you will see the following contents

// Choose your render engine. The default choice is JADE:  http://jade-lang.com/
exports.engine = 'jade';

// Render the index page
exports.index = function(req, res, next){
	res.render('hello');
};
  • The render engine is the way the view is written down. Currently only JADE is supported.
  • The exports.index is the initial route to the plugin. And in this case will render the hello.jade file in the views folder.
  • The 'index' is the key used by the routing to assign the proper handling. Another example is 'get'.

The public part of an App / Making it public

When we go back to the root folder Of your newly created plugin, you will also see a folder called public. Everything in this folder is accessible from the client.

If you want your plugin to show up in the dashboard, all you need to do is add a tile.png or tile.svg to your public folder root. This will alert MCJS that you want your plugin to be accessible from the dashboard, and it will automatically add it.

(In theory, you can make a background plugin that hooks on an existing plugin, or just runs in the background, without having it showing up in the dashboard simply by not adding the tile. It's up to you how you use this functionality.)

Routing

Basic routing

I tried to implement the routing as RESTfull as possible. This means if your app frontend sends a GET request, it can do so in three layers. A required base level with for instance an ID, a optional second level with a subid for example or an action and finally a third level with usually an action. Which results in a get handler which could look something like this:

exports.get = function(req, res, next){		
	var requiredId = req.params.id  		//Initial param after base name. example: /movies/12
	  , optionalParam = req.params.optionalParam	//Second param after initial param. example: /movies/12/info
	  , action = req.params.action;			//Third param example: /music/muse/bliss/info
		
	if(!action){
		//No third route
		switch(optionalParam) {
			case('play'):
				//Do something with root/action in this case 'play'
				break;
			default:
				//Do nothing 
				break;		
		}
	} else if (!optionalParam && !action){
		//Do something with root id, no second route
	} else if(action === 'play') {
		//Do something with root/subid/action
	}
};

Of course this is just a basic layer. You can extend this in your own route.js file in your plugin folder.

route.js

Although the basic routing is pretty generic and should be sufficient most of the time, you can extend the basic routing table with your own custom routes by adding this JSON file to the root of your core plugin folder and defining your routes.

The 'NAME' will be replaced with the app name (folder name / namespace). You do not have to hard code it. But you can also add routes outside your app namespace. For Example:

{
	"track": [{
		"method": "get",
		"path": "/NAME/track/:album/:track"
	}],
	"album": [{
		"method":"post",
		"path": "/NAME/album"
	}],
	"lookup": [{
		"method":"get",
		"path": "/configuration"
	}]
}

Remote control

If you want the remote to properly navigate your plugin you need to add classes to the DOM elements of your plugin so the remote can find it's way within your plugin.

To specify an element that you can navigate to and from add the following class:

.mcjs-rc-controllable

To specify an element that you can "click" on or in other words interact with, add the following class:

.mcjs-rc-clickable

To specify an element that you want to specific, for instance when a a user has clicked on a list item which reveals more options, you can let the remote know to focus on that specific group by adding the following class:

.mcjs-rc-active

These classes will also enable keyboard navigation for your plugin. Please make sure you have include the socket.io client side javascript and the MCJS core plugin to make sure the remote will work in your app. (Included by default if you extend the layout Jade block.)

Settings

Basic settings injection

Of course your plugin could require some additional settings like a username and password. It is fairly easy to have your settings to show up in the settings panel.

To do so, you need to include a json in the root of your module called settings.json. The JSON should look like this:

[
    {
        "name": "spotifyUser",   //Most be unique!
        "label": "Spotify Username",
        "type": "text",  // Currently only the input element is supported. So type can be "text", "password" etc
        "placeholder": "Spotify Username"
    },
    {
        "name": "spotifyPassword",    //Most be unique!
        "label": "Spotify password",
        "type": "password", // Currently only the input element is supported. So type can be "text", "password" etc
        "placeholder": "Spotify password"
    }
]