The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

App::Netdisco::Web::Plugin - Plugin subsystem for App::Netdisco Web UI components

Introduction

App::Netdisco's plugin subsystem allows developers to write and test web user interface (UI) components without needing to patch the main Netdisco application. It also allows the end-user more control over the UI components displayed in their browser.

So far, the following UI compoents are implemented as plugins:

  • Navigation Bar items (e.g. Inventory link)

  • Tabs for Search and Device pages

In the future there will be other components supported, such as Reports.

This document explains first how to configure which plugins are loaded (useful for the end-user) and then also how to write and install your own plugins.

Application Configuration

In the main config.yml file for App::Netdisco (located in share/...) you'll find the web_plugins configuration directive. This lists, in YAML format, a set of Perl module names (or partial names) which are the plugins to be loaded. For example:

 web_plugins:
   - Inventory
   - Search::Device
   - Search::Node
   - Search::Port
   - Device::Details
   - Device::Ports

When the name is specified as above, App::Netdisco automatically prepends "App::Netdisco::Web::Plugin::" to the name. This makes, for example, App::Netdisco::Web::Plugin::Inventory. This is the module which is loaded to add a user interface component.

Such plugin modules can either ship with the App::Netdisco distribution itself, or be installed separately. Perl uses the standard @INC path searching mechanism to load the plugin modules.

If an entry in the web_plugins list starts with a "+" (plus) sign then App::Netdisco attemps to load the module as-is, without prepending anything to the name. This allows you to have App::Netdiso web UI plugins in other namespaces:

 web_plugins:
   - Inventory
   - Search::Device
   - Search::Node
   - Device::Details
   - Device::Ports
   - +My::Other::Netdisco::Web::Component

The order of the entries in web_plugins is significant. Unsurprisingly, the modules are loaded in order. Therefore Navigation Bar items appear in the order listed, and Tabs appear on the Search and Device pages in the order listed.

The consequence of this is that if you want to change the order (or add or remove entries) then simply edit the web_plugins setting. In fact, we recommend adding this setting to your <environment>.yml file and leaving the config.yml file alone. Your Environment's version will take prescedence.

Finally, if you want to add components without completely overriding the web_plugins setting, use the extra_web_plugins setting instead in your Environment configuration. Any Navigation Bar items or Page Tabs are added after those in web_plugins.

Developing Plugins

A plugin is simply a Perl module which is loaded. Therefore it can do anything you like, but most usefully for the App::Netdisco web application, the module will install a Dancer route handler subroutine, and link this to a web user interface (UI) component.

Explaining how to write Dancer route handlers is beyond the scope of this document, but by examining the source to the plugins in App::Netdisco you'll probably get enough of an idea to begin on your own.

App::Netdisco plugins should load the App::Netdisco::Web::Plugin module. This exports a set of helper subroutines to register the new UI components. Here's the boilerplate code for our example plugin module:

 package App::Netdisco::Web::Plugin::MyNewFeature
 
 use Dancer ':syntax';
 use Dancer::Plugin::Ajax;
 use Dancer::Plugin::DBIC;
 
 use App::Netdisco::Web::Plugin;
 
 # plugin registration code goes here, ** see below **
 
 # your Dancer route handler, for example:
 get '/mynewfeature' => sub {
   # ...lorem ipsum...
 };
 
 true;

These components appear in the black navigation bar at the top of each page, as individual items (i.e. not in a menu). The canonical example of this is the Inventory link.

To register an item for display in the navigation bar, use the following code:

 register_navbar_item({
   id    => 'newfeature',
   path  => '/mynewfeature',
   label => 'My New Feature',
 });

This causes an item to appear in the Navigation Bar with a visible text of "My New Feature" which when clicked sends the user to the /mynewfeature page. Note that this won't work for any target link - the path must be an App::Netdisco Dancer route handler. Please bug the App::Netdisco devs if you want arbitrary links supported.

Search and Device page Tabs

These components appear as tabs in the interface when the user reaches the Search page or Device details page. Note that Tab plugins usually live in the App::Netdisco::Web::Plugin::Device or App::Netdisco::Web::Plugin::Search namespace.

To register a handler for display as a Search page Tab, use the following code:

 register_search_tab({id => 'newfeature', label => 'My New Feature'});

This causes a tab to appear with the label "My New Feature". So how does App::Netdisco know what the link should be? Well, as the App::Netdisco::Developing documentation says, tab content is retrieved by an AJAX call back to the web server. This uses a predictable URL path format:

 /ajax/content/<search or device>/<feature ID>

For example:

 /ajax/content/search/newfeature

Therefore your plugin module should look like the following:

 package App::Netdisco::Web::Plugin::Search::MyNewFeature
 
 use Dancer ':syntax';
 use Dancer::Plugin::Ajax;
 use Dancer::Plugin::DBIC;
 
 use App::Netdisco::Web::Plugin;
 
 register_search_tab({id => 'newfeature', label => 'My New Feature'});
 
 ajax '/ajax/content/search/newfeature' => sub {
   # ...lorem ipsum...
 
   content_type('text/html');
   # return some HTML content here, probably using a template
 };
 
 true;

If this all sounds a bit daunting, take a look at the App::Netdisco::Web::Plugin::Search::Port module which is fairly straightforward.

To register a handler for display as a Device page Tab, the only difference is the name of the registration helper sub:

 register_device_tab({id => 'newfeature', label => 'My New Feature'});

Templates

All of Netdisco's web page templates are stashed away in its distribution, probably installed in your system's or user's Perl directory. It's not recommended that you mess about with those files.

So in order to replace a template with your own version, or to reference a template file of your own in your plugin, you need a new path.

TODO: this bit!

Template Variables

Some useful variables are made available in your templates automatically by App::Netdisco:

search_node

A base url which links to the Node tab of the Search page, together with the correct default search options set.

search_device

A base url which links to the Device tab of the Search page, together with the correct default search options set.

device_ports

A base url which links to the Ports tab of the Device page, together with the correct default column view options set.

uri_base

Used for linking to static content within App::Netdisco safely if the base of the app is relocated, for example:

 <link rel="stylesheet" href="[% uri_base %]/css/toastr.css"/>
uri_for

Simply the Dancer uri_for method. Allows you to do things like this in the template safely if the base of the app is relocated:

 <a href="[% uri_for('/search') %]" ...>
self_options

Available in the Device tabs, use this if you need to refer back to the current page with some additional parameters, for example:

 <a href="[% uri_for('/device', self_options) %]&foo=bar" ...>