Squashed commit of the following:
commitb054119d9cAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 17 14:29:58 2013 +0000 hide Reports menu if there are no reports commitd86e670600Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 17 14:23:59 2013 +0000 add Report docs for developers commitee8351eb30Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 17 14:06:39 2013 +0000 split Plugin docs into user and developer pages commit5e4b8f3063Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 17 12:59:47 2013 +0000 add duplex report into default config commit8fd622f50cAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 17 12:56:51 2013 +0000 update query for duplex mismatch to check left and right are both not DOWN commit6d9170598cAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 11 23:10:38 2013 +0000 use the new duplex mismatch query in a template commit786977354bAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 11 22:54:21 2013 +0000 add VIEW for duplex mismatches commitf37ae8568eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Mon Mar 11 22:54:01 2013 +0000 remove unecessary assign commit13af853582Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 23:40:53 2013 +0000 fixes to main app to support reports commit55a0f3d8dcAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 22:18:48 2013 +0000 also update fontawesome to match bootstrap version commit83a2c74242Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 22:07:23 2013 +0000 update bootstrap again, to include glyphicons with correct path commit25be8bfa92Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 19:27:37 2013 +0000 implement first report - duplex mismatch - as a placeholder only commit00265a9323Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 18:41:40 2013 +0000 report error on failure to load a plugin commitaf8f124bb2Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 18:37:19 2013 +0000 change id for tag in device and search tab plugins commitb818d4156fAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 18:29:57 2013 +0000 change id for tag in navbar plugins commitf513000f08Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 17:21:23 2013 +0000 implement register_report() and replace More menu with Reports menu commit4a16e3fde3Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Mar 10 17:00:44 2013 +0000 Upgraded to Twitter Bootstrap 2.3.1 - customized for 13px font 18px line
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package App::Netdisco::DB::Result::Virtual::DuplexMismatch;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
|
||||
|
||||
__PACKAGE__->table('duplex_mismatch');
|
||||
__PACKAGE__->result_source_instance->is_virtual(1);
|
||||
__PACKAGE__->result_source_instance->view_definition(<<ENDSQL
|
||||
SELECT dp.ip AS left_ip, d1.dns AS left_dns, dp.port AS left_port, dp.duplex AS left_duplex,
|
||||
di.ip AS right_ip, d2.dns AS right_dns, dp.remote_port AS right_port, dp2.duplex AS right_duplex
|
||||
FROM ( SELECT device_port.ip, device_port.remote_ip, device_port.port, device_port.duplex, device_port.remote_port
|
||||
FROM device_port
|
||||
WHERE
|
||||
device_port.remote_port IS NOT NULL
|
||||
AND device_port.up NOT ILIKE '%down%'
|
||||
GROUP BY device_port.ip, device_port.remote_ip, device_port.port, device_port.duplex, device_port.remote_port
|
||||
ORDER BY device_port.ip) dp
|
||||
LEFT JOIN device_ip di ON dp.remote_ip = di.alias
|
||||
LEFT JOIN device d1 ON dp.ip = d1.ip
|
||||
LEFT JOIN device d2 ON di.ip = d2.ip
|
||||
LEFT JOIN device_port dp2 ON (di.ip = dp2.ip AND dp.remote_port = dp2.port)
|
||||
WHERE di.ip IS NOT NULL
|
||||
AND dp.duplex <> dp2.duplex
|
||||
AND dp.ip <= di.ip
|
||||
AND dp2.up NOT ILIKE '%down%'
|
||||
ORDER BY dp.ip
|
||||
ENDSQL
|
||||
);
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
'left_ip' => {
|
||||
data_type => 'inet',
|
||||
},
|
||||
'left_dns' => {
|
||||
data_type => 'text',
|
||||
},
|
||||
'left_port' => {
|
||||
data_type => 'text',
|
||||
},
|
||||
'left_duplex' => {
|
||||
data_type => 'text',
|
||||
},
|
||||
'right_ip' => {
|
||||
data_type => 'inet',
|
||||
},
|
||||
'right_dns' => {
|
||||
data_type => 'text',
|
||||
},
|
||||
'right_port' => {
|
||||
data_type => 'text',
|
||||
},
|
||||
'right_duplex' => {
|
||||
data_type => 'text',
|
||||
},
|
||||
);
|
||||
|
||||
1;
|
||||
246
Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pm
Normal file
246
Netdisco/lib/App/Netdisco/Manual/WritingPlugins.pm
Normal file
@@ -0,0 +1,246 @@
|
||||
=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;
|
||||
|
||||
=head1 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({
|
||||
tag => '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.
|
||||
|
||||
=head1 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({tag => '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 tag>
|
||||
|
||||
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({tag => '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({tag => 'newfeature', label => 'My New Feature'});
|
||||
|
||||
=head1 Reports
|
||||
|
||||
Report components contain pre-canned searches which the user community have
|
||||
found to be useful. The implementation is very similar to one of the Search
|
||||
and Device page Tabs, so please read that documentation above, first.
|
||||
|
||||
Report plugins usually live in the C<App::Netdisco::Web::Plugin::Report>
|
||||
namespace. To register a handler for display as a Report, you need to pick the
|
||||
I<category> of the report. Here are the pre-defined categories:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Device
|
||||
|
||||
=item *
|
||||
|
||||
Port
|
||||
|
||||
=item *
|
||||
|
||||
Node
|
||||
|
||||
=item *
|
||||
|
||||
VLAN
|
||||
|
||||
=item *
|
||||
|
||||
Network
|
||||
|
||||
=item *
|
||||
|
||||
Wireless
|
||||
|
||||
=back
|
||||
|
||||
Once your category is selected, use the following registration code:
|
||||
|
||||
register_report({
|
||||
category => 'Port', # pick one from the list
|
||||
tag => 'newreport',
|
||||
label => 'My New Report',
|
||||
});
|
||||
|
||||
You will note that like Device and Search page Tabs, there's no path
|
||||
specified in the registration. The reports engine will make an AJAX request to
|
||||
the following URL:
|
||||
|
||||
/ajax/content/report/<report tag>
|
||||
|
||||
Therefore you should implement in your plugin an AJAX handler for this path.
|
||||
The handler must return the HTML content for the report. It can also process
|
||||
any query parameters which might customize the report search.
|
||||
|
||||
See the L<App::Netdisco::Web::Plugin::Report::DuplexMismatch> module for a
|
||||
simple example of how to implement the handler.
|
||||
|
||||
=head1 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
|
||||
|
||||
@@ -12,6 +12,7 @@ use URI::QueryParam (); # part of URI, to add helper methods
|
||||
use App::Netdisco::Web::AuthN;
|
||||
use App::Netdisco::Web::Search;
|
||||
use App::Netdisco::Web::Device;
|
||||
use App::Netdisco::Web::Report;
|
||||
use App::Netdisco::Web::TypeAhead;
|
||||
use App::Netdisco::Web::PortControl;
|
||||
|
||||
@@ -25,6 +26,7 @@ sub _load_web_plugins {
|
||||
|
||||
debug "loading Netdisco plugin $plugin";
|
||||
eval "require $plugin";
|
||||
error $@ if $@;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ set(
|
||||
'navbar_items' => [],
|
||||
'search_tabs' => [],
|
||||
'device_tabs' => [],
|
||||
'reports_menu' => {},
|
||||
'reports' => {},
|
||||
'report_order' => [qw/Device Port Node VLAN Network Wireless/],
|
||||
);
|
||||
|
||||
# this is what Dancer::Template::TemplateToolkit does by default
|
||||
@@ -28,7 +31,7 @@ register 'register_template_path' => sub {
|
||||
register 'register_navbar_item' => sub {
|
||||
my ($self, $config) = plugin_args(@_);
|
||||
|
||||
if (!length $config->{id}
|
||||
if (!length $config->{tag}
|
||||
or !length $config->{path}
|
||||
or !length $config->{label}) {
|
||||
|
||||
@@ -37,7 +40,7 @@ register 'register_navbar_item' => sub {
|
||||
}
|
||||
|
||||
foreach my $item (@{ setting('navbar_items') }) {
|
||||
if ($item->{id} eq $config->{id}) {
|
||||
if ($item->{tag} eq $config->{tag}) {
|
||||
$item = $config;
|
||||
return;
|
||||
}
|
||||
@@ -50,7 +53,7 @@ sub _register_tab {
|
||||
my ($nav, $config) = @_;
|
||||
my $stash = setting("${nav}_tabs");
|
||||
|
||||
if (!length $config->{id}
|
||||
if (!length $config->{tag}
|
||||
or !length $config->{label}) {
|
||||
|
||||
error "bad config to register_${nav}_item";
|
||||
@@ -58,7 +61,7 @@ sub _register_tab {
|
||||
}
|
||||
|
||||
foreach my $item (@{ $stash }) {
|
||||
if ($item->{id} eq $config->{id}) {
|
||||
if ($item->{tag} eq $config->{tag}) {
|
||||
$item = $config;
|
||||
return;
|
||||
}
|
||||
@@ -77,6 +80,30 @@ register 'register_device_tab' => sub {
|
||||
_register_tab('device', $config);
|
||||
};
|
||||
|
||||
register 'register_report' => sub {
|
||||
my ($self, $config) = plugin_args(@_);
|
||||
my @categories = @{ setting('report_order') };
|
||||
|
||||
if (!length $config->{category}
|
||||
or !length $config->{tag}
|
||||
or !length $config->{label}
|
||||
or 0 == scalar grep {$config->{category} eq $_} @categories) {
|
||||
|
||||
error "bad config to register_report";
|
||||
return;
|
||||
}
|
||||
|
||||
foreach my $item (@{setting('reports_menu')->{ $config->{category} }}) {
|
||||
if ($item eq $config->{tag}) {
|
||||
setting('reports')->{$config->{tag}} = $config;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
push @{setting('reports_menu')->{ $config->{category} }}, $config->{tag};
|
||||
setting('reports')->{$config->{tag}} = $config;
|
||||
};
|
||||
|
||||
register_plugin;
|
||||
true;
|
||||
|
||||
@@ -86,12 +113,12 @@ App::Netdisco::Web::Plugin - Plugin subsystem for App::Netdisco Web UI component
|
||||
|
||||
=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.
|
||||
L<App::Netdisco>'s plugin subsystem allows the user more control of Netdisco
|
||||
UI components displayed in the web browser. Plugins can be distributed
|
||||
independently from Netdisco and are a better alternative to source code
|
||||
patches.
|
||||
|
||||
So far, the following UI compoents are implemented as plugins:
|
||||
The following UI components are implemented as plugins:
|
||||
|
||||
=over 4
|
||||
|
||||
@@ -103,12 +130,14 @@ Navigation Bar items (e.g. Inventory link)
|
||||
|
||||
Tabs for Search and Device pages
|
||||
|
||||
=item *
|
||||
|
||||
Reports (pre-canned searches)
|
||||
|
||||
=back
|
||||
|
||||
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.
|
||||
This document explains how to configure which plugins are loaded. See
|
||||
L<App::Netdisco::Manual::WritingPlugins> if you want to develop new plugins.
|
||||
|
||||
=head1 Application Configuration
|
||||
|
||||
@@ -119,6 +148,7 @@ be loaded. For example:
|
||||
|
||||
web_plugins:
|
||||
- Inventory
|
||||
- Report::DuplexMismatch
|
||||
- Search::Device
|
||||
- Search::Node
|
||||
- Search::Port
|
||||
@@ -142,9 +172,7 @@ namespaces:
|
||||
web_plugins:
|
||||
- Inventory
|
||||
- Search::Device
|
||||
- Search::Node
|
||||
- Device::Details
|
||||
- Device::Ports
|
||||
- +My::Other::Netdisco::Web::Component
|
||||
|
||||
The order of the entries in C<web_plugins> is significant. Unsurprisingly, the
|
||||
@@ -163,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
|
||||
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
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_device_tab({ id => 'addresses', label => 'Addresses' });
|
||||
register_device_tab({ tag => 'addresses', label => 'Addresses' });
|
||||
|
||||
# device interface addresses
|
||||
ajax '/ajax/content/device/addresses' => sub {
|
||||
|
||||
@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_device_tab({ id => 'details', label => 'Details' });
|
||||
register_device_tab({ tag => 'details', label => 'Details' });
|
||||
|
||||
# device details table
|
||||
ajax '/ajax/content/device/details' => sub {
|
||||
|
||||
@@ -5,7 +5,7 @@ use Dancer::Plugin::Ajax;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_device_tab({ id => 'modules', label => 'Modules' });
|
||||
register_device_tab({ tag => 'modules', label => 'Modules' });
|
||||
|
||||
ajax '/ajax/content/device/:thing' => sub {
|
||||
return "<p>Hello, this is where the ". param('thing') ." content goes.</p>";
|
||||
|
||||
@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_device_tab({ id => 'netmap', label => 'Neighbors' });
|
||||
register_device_tab({ tag => 'netmap', label => 'Neighbors' });
|
||||
|
||||
ajax '/ajax/content/device/netmap' => sub {
|
||||
content_type('text/html');
|
||||
|
||||
@@ -7,7 +7,7 @@ use Dancer::Plugin::DBIC;
|
||||
use App::Netdisco::Util::Web (); # for sort_port
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_device_tab({ id => 'ports', label => 'Ports' });
|
||||
register_device_tab({ tag => 'ports', label => 'Ports' });
|
||||
|
||||
# device ports with a description (er, name) matching
|
||||
ajax '/ajax/content/device/ports' => sub {
|
||||
|
||||
@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_navbar_item({
|
||||
id => 'inventory',
|
||||
tag => 'inventory',
|
||||
path => '/inventory',
|
||||
label => 'Inventory',
|
||||
});
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package App::Netdisco::Web::Plugin::Report::DuplexMismatch;
|
||||
|
||||
use Dancer ':syntax';
|
||||
use Dancer::Plugin::Ajax;
|
||||
use Dancer::Plugin::DBIC;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_report({
|
||||
category => 'Port',
|
||||
tag => 'duplexmismatch',
|
||||
label => 'Duplex Mismatches Between Devices',
|
||||
});
|
||||
|
||||
ajax '/ajax/content/report/duplexmismatch' => sub {
|
||||
my $set = schema('netdisco')->resultset('Virtual::DuplexMismatch');
|
||||
return unless $set->count;
|
||||
|
||||
content_type('text/html');
|
||||
template 'ajax/report/duplexmismatch.tt', {
|
||||
results => $set,
|
||||
}, { layout => undef };
|
||||
};
|
||||
|
||||
true;
|
||||
@@ -8,7 +8,7 @@ use List::MoreUtils ();
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_search_tab({id => 'device', label => 'Device'});
|
||||
register_search_tab({ tag => 'device', label => 'Device' });
|
||||
|
||||
# device with various properties or a default match-all
|
||||
ajax '/ajax/content/search/device' => sub {
|
||||
|
||||
@@ -9,7 +9,7 @@ use Net::MAC ();
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_search_tab({ id => 'node', label => 'Node' });
|
||||
register_search_tab({ tag => 'node', label => 'Node' });
|
||||
|
||||
# nodes matching the param as an IP or DNS hostname or MAC
|
||||
ajax '/ajax/content/search/node' => sub {
|
||||
|
||||
@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_search_tab({ id => 'port', label => 'Port' });
|
||||
register_search_tab({ tag => 'port', label => 'Port' });
|
||||
|
||||
# device ports with a description (er, name) matching
|
||||
ajax '/ajax/content/search/port' => sub {
|
||||
|
||||
@@ -6,7 +6,7 @@ use Dancer::Plugin::DBIC;
|
||||
|
||||
use App::Netdisco::Web::Plugin;
|
||||
|
||||
register_search_tab({ id => 'vlan', label => 'VLAN' });
|
||||
register_search_tab({ tag => 'vlan', label => 'VLAN' });
|
||||
|
||||
# devices carrying vlan xxx
|
||||
ajax '/ajax/content/search/vlan' => sub {
|
||||
|
||||
17
Netdisco/lib/App/Netdisco/Web/Report.pm
Normal file
17
Netdisco/lib/App/Netdisco/Web/Report.pm
Normal file
@@ -0,0 +1,17 @@
|
||||
package App::Netdisco::Web::Report;
|
||||
|
||||
use Dancer ':syntax';
|
||||
|
||||
get '/report/*' => sub {
|
||||
my ($tag) = splat;
|
||||
|
||||
# trick the ajax into working as if this were a tabbed page
|
||||
params->{tab} = $tag;
|
||||
|
||||
var(nav => 'reports');
|
||||
template 'report', {
|
||||
report => setting('reports')->{ $tag },
|
||||
};
|
||||
};
|
||||
|
||||
true;
|
||||
Reference in New Issue
Block a user