split Plugin docs into user and developer pages
This commit is contained in:
187
Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pm
Normal file
187
Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pm
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
App::Netdisco::Manual::WritingPlugins - Documentation on Plugins for Developers
|
||||||
|
|
||||||
|
=head1 Introduction
|
||||||
|
|
||||||
|
L<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.
|
||||||
|
|
||||||
|
See L<App::Netdisco::Web::Plugin> for more general information about plugins.
|
||||||
|
|
||||||
|
=head1 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 L<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 L<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;
|
||||||
|
|
||||||
|
=head2 Navigation Bar items
|
||||||
|
|
||||||
|
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 C</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.
|
||||||
|
|
||||||
|
=head2 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 C<App::Netdisco::Web::Plugin::Device> or
|
||||||
|
C<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
|
||||||
|
L<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
|
||||||
|
L<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'});
|
||||||
|
|
||||||
|
=head2 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:
|
||||||
|
|
||||||
|
package App::Netdisco::Web::Plugin::Search::MyNewFeature
|
||||||
|
|
||||||
|
use File::ShareDir 'dist_dir';
|
||||||
|
register_template_path(
|
||||||
|
dist_dir( 'App-Netdisco-Web-Plugin-Search-MyNewFeature' ));
|
||||||
|
|
||||||
|
The registered path will be searched before the built-in C<App::Netdisco>
|
||||||
|
path. We recommend use of the L<File::ShareDir> module to package and ship
|
||||||
|
templates along with your plugin, as shown.
|
||||||
|
|
||||||
|
Each path added using C<register_template_path> is searched I<before> any
|
||||||
|
existing paths in the template config.
|
||||||
|
|
||||||
|
=head3 Template Variables
|
||||||
|
|
||||||
|
Some useful variables are made available in your templates automatically by
|
||||||
|
App::Netdisco:
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item C<search_node>
|
||||||
|
|
||||||
|
A base url which links to the Node tab of the Search page, together with the
|
||||||
|
correct default search options set.
|
||||||
|
|
||||||
|
=item C<search_device>
|
||||||
|
|
||||||
|
A base url which links to the Device tab of the Search page, together with the
|
||||||
|
correct default search options set.
|
||||||
|
|
||||||
|
=item C<device_ports>
|
||||||
|
|
||||||
|
A base url which links to the Ports tab of the Device page, together with
|
||||||
|
the correct default column view options set.
|
||||||
|
|
||||||
|
=item C<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"/>
|
||||||
|
|
||||||
|
=item C<uri_for>
|
||||||
|
|
||||||
|
Simply the Dancer C<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') %]" ...>
|
||||||
|
|
||||||
|
=item C<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" ...>
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
@@ -113,12 +113,12 @@ App::Netdisco::Web::Plugin - Plugin subsystem for App::Netdisco Web UI component
|
|||||||
|
|
||||||
=head1 Introduction
|
=head1 Introduction
|
||||||
|
|
||||||
L<App::Netdisco>'s plugin subsystem allows developers to write and test web
|
L<App::Netdisco>'s plugin subsystem allows the user more control of Netdisco
|
||||||
user interface (UI) components without needing to patch the main Netdisco
|
UI components displayed in the web browser. Plugins can be distributed
|
||||||
application. It also allows the end-user more control over the UI components
|
independently from Netdisco and are a better alternative to source code
|
||||||
displayed in their browser.
|
patches.
|
||||||
|
|
||||||
So far, the following UI compoents are implemented as plugins:
|
The following UI components are implemented as plugins:
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@@ -130,12 +130,14 @@ Navigation Bar items (e.g. Inventory link)
|
|||||||
|
|
||||||
Tabs for Search and Device pages
|
Tabs for Search and Device pages
|
||||||
|
|
||||||
|
=item *
|
||||||
|
|
||||||
|
Reports (pre-canned searches)
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
In the future there will be other components supported, such as Reports.
|
This document explains how to configure which plugins are loaded. See
|
||||||
|
L<App::Netdisco::Manual::WritingPlugins> if you want to develop new plugins.
|
||||||
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.
|
|
||||||
|
|
||||||
=head1 Application Configuration
|
=head1 Application Configuration
|
||||||
|
|
||||||
@@ -146,6 +148,7 @@ be loaded. For example:
|
|||||||
|
|
||||||
web_plugins:
|
web_plugins:
|
||||||
- Inventory
|
- Inventory
|
||||||
|
- Report::DuplexMismatch
|
||||||
- Search::Device
|
- Search::Device
|
||||||
- Search::Node
|
- Search::Node
|
||||||
- Search::Port
|
- Search::Port
|
||||||
@@ -169,9 +172,7 @@ namespaces:
|
|||||||
web_plugins:
|
web_plugins:
|
||||||
- Inventory
|
- Inventory
|
||||||
- Search::Device
|
- Search::Device
|
||||||
- Search::Node
|
|
||||||
- Device::Details
|
- Device::Details
|
||||||
- Device::Ports
|
|
||||||
- +My::Other::Netdisco::Web::Component
|
- +My::Other::Netdisco::Web::Component
|
||||||
|
|
||||||
The order of the entries in C<web_plugins> is significant. Unsurprisingly, the
|
The order of the entries in C<web_plugins> is significant. Unsurprisingly, the
|
||||||
@@ -190,174 +191,5 @@ C<web_plugins> setting, use the C<extra_web_plugins> setting instead in your
|
|||||||
Environment configuration. Any Navigation Bar items or Page Tabs are added
|
Environment configuration. Any Navigation Bar items or Page Tabs are added
|
||||||
after those in C<web_plugins>.
|
after those in C<web_plugins>.
|
||||||
|
|
||||||
=head1 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 L<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 L<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;
|
|
||||||
|
|
||||||
=head2 Navigation Bar items
|
|
||||||
|
|
||||||
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 C</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.
|
|
||||||
|
|
||||||
=head2 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 C<App::Netdisco::Web::Plugin::Device> or
|
|
||||||
C<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
|
|
||||||
L<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
|
|
||||||
L<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'});
|
|
||||||
|
|
||||||
=head2 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:
|
|
||||||
|
|
||||||
package App::Netdisco::Web::Plugin::Search::MyNewFeature
|
|
||||||
|
|
||||||
use File::ShareDir 'dist_dir';
|
|
||||||
register_template_path(
|
|
||||||
dist_dir( 'App-Netdisco-Web-Plugin-Search-MyNewFeature' ));
|
|
||||||
|
|
||||||
The registered path will be searched before the built-in C<App::Netdisco>
|
|
||||||
path. We recommend use of the L<File::ShareDir> module to package and ship
|
|
||||||
templates along with your plugin, as shown.
|
|
||||||
|
|
||||||
=head3 Template Variables
|
|
||||||
|
|
||||||
Some useful variables are made available in your templates automatically by
|
|
||||||
App::Netdisco:
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item C<search_node>
|
|
||||||
|
|
||||||
A base url which links to the Node tab of the Search page, together with the
|
|
||||||
correct default search options set.
|
|
||||||
|
|
||||||
=item C<search_device>
|
|
||||||
|
|
||||||
A base url which links to the Device tab of the Search page, together with the
|
|
||||||
correct default search options set.
|
|
||||||
|
|
||||||
=item C<device_ports>
|
|
||||||
|
|
||||||
A base url which links to the Ports tab of the Device page, together with
|
|
||||||
the correct default column view options set.
|
|
||||||
|
|
||||||
=item C<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"/>
|
|
||||||
|
|
||||||
=item C<uri_for>
|
|
||||||
|
|
||||||
Simply the Dancer C<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') %]" ...>
|
|
||||||
|
|
||||||
=item C<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" ...>
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user