CSV download support via provides_csv plugin attr

Squashed commit of the following:

commit e44f203f47
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 20:00:46 2013 +0100

    also set filename on csv download link

commit 26b47d1296
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 19:56:38 2013 +0100

    conditionally show csv icon for device and search tabs

commit 119d2c672d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 19:48:24 2013 +0100

    tweak look of csv icon

commit 89816892be
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 19:38:31 2013 +0100

    enable csv icon link rewrite for all pages

commit ac29769402
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 19:28:49 2013 +0100

    use single route handler for ajax and csv response

commit 4b6f89635d
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 18:59:08 2013 +0100

    no need for separate csv link template

commit 1021329e1c
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 18:57:20 2013 +0100

    display download as csv icon conditionally

commit ce57cdba69
Author: Oliver Gorwits <oliver@cpan.org>
Date:   Fri Sep 20 09:23:57 2013 +0100

    Based on jeneric's CSV download templates;

    - try to reduce code duplication by using same route handler for ajax and csv,
      using request->is_ajax to switch the template, and set content-type

    - use new HTML5 "download" attribute on links so content-disposition header is
      no longer necessary

    - download CSV icon is placed on all tables (per report/device/serach section)

    - update download CSV link using javascript just before table content is
      fetched - this is necessary to make sure updated sidebar query params are
      included

    The idea here is to allow us to support CSV download in the pages which
    display tables by only doing the following:

    - (existing routes:) replace "ajax" with "get" route handler

    - add logic to switch template in handler, based on request->is_ajax

    - write _csv.tt version of the template, to spit out CSV file content

    This makes it much easier for new devs to write reports supporting CSV, I
    think?

commit d3553d2623
Author: Eric A. Miller <emiller@cpan.org>
Date:   Thu Sep 19 22:30:27 2013 -0400

    add csv download to duplex mismatch, half duplex, and port utilization reports

commit 5d4df72a24
Author: Eric A. Miller <emiller@cpan.org>
Date:   Wed Sep 18 23:50:27 2013 -0400

    add csv download to ap channel distribution report

commit 0824d7936a
Author: Eric A. Miller <emiller@cpan.org>
Date:   Wed Sep 18 23:42:53 2013 -0400

    need Template::Plugin::CSV for csv output

commit bb1f842d92
Author: Eric A. Miller <emiller@cpan.org>
Date:   Wed Sep 18 23:35:28 2013 -0400

    add csv download option to device addresses

commit 99ada1132c
Author: Eric A. Miller <emiller@cpan.org>
Date:   Wed Sep 18 23:32:57 2013 -0400

    add csv download option to device, port, and vlan search pages
This commit is contained in:
Oliver Gorwits
2013-09-20 20:15:32 +01:00
parent d2b39a3f0e
commit ce604ca20b
23 changed files with 271 additions and 72 deletions

View File

@@ -36,6 +36,7 @@ requires 'Starman' => 0.3008;
requires 'SNMP::Info' => 3.05; requires 'SNMP::Info' => 3.05;
requires 'SQL::Translator' => 0.11016; requires 'SQL::Translator' => 0.11016;
requires 'Template' => 2.24; requires 'Template' => 2.24;
requires 'Template::Plugin::CSV' => 0.04;
requires 'URL::Encode' => 0.01; requires 'URL::Encode' => 0.01;
requires 'YAML' => 0.84; requires 'YAML' => 0.84;
requires 'YAML::XS' => 0.41; requires 'YAML::XS' => 0.41;

View File

@@ -1,28 +1,33 @@
package App::Netdisco::Web::Plugin::Device::Addresses; package App::Netdisco::Web::Plugin::Device::Addresses;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
register_device_tab({ tag => 'addresses', label => 'Addresses' }); register_device_tab( { tag => 'addresses', label => 'Addresses', provides_csv => 1 } );
# device interface addresses # device interface addresses
ajax '/ajax/content/device/addresses' => require_login sub { get '/ajax/content/device/addresses' => require_login sub {
my $q = param('q'); my $q = param('q');
my $device = schema('netdisco')->resultset('Device') my $device
->search_for_device($q) or send_error('Bad device', 400); = schema('netdisco')->resultset('Device')->search_for_device($q)
or send_error( 'Bad device', 400 );
my $set = $device->device_ips->search({}, {order_by => 'alias'}); my $set = $device->device_ips->search( {}, { order_by => 'alias' } );
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/device/addresses.tt', { template 'ajax/device/addresses.tt', { results => $set, },
results => $set, { layout => undef };
}, { layout => undef }; }
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/device/addresses_csv.tt', { results => $set, },
{ layout => undef };
}
}; };
true; true;

View File

@@ -1,7 +1,6 @@
package App::Netdisco::Web::Plugin::Report::ApChannelDist; package App::Netdisco::Web::Plugin::Report::ApChannelDist;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
@@ -11,10 +10,11 @@ register_report(
{ category => 'Wireless', { category => 'Wireless',
tag => 'apchanneldist', tag => 'apchanneldist',
label => 'Access Point Channel Distribution', label => 'Access Point Channel Distribution',
provides_csv => 1,
} }
); );
ajax '/ajax/content/report/apchanneldist' => require_login sub { get '/ajax/content/report/apchanneldist' => require_login sub {
my $set = schema('netdisco')->resultset('DevicePortWireless')->search( my $set = schema('netdisco')->resultset('DevicePortWireless')->search(
{ channel => { '!=', '0' } }, { channel => { '!=', '0' } },
{ select => [ 'channel', { count => 'channel' } ], { select => [ 'channel', { count => 'channel' } ],
@@ -23,12 +23,17 @@ ajax '/ajax/content/report/apchanneldist' => require_login sub {
order_by => { -desc => [qw/count/] }, order_by => { -desc => [qw/count/] },
}, },
); );
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/report/apchanneldist.tt', { results => $set, }, template 'ajax/report/apchanneldist.tt', { results => $set, },
{ layout => undef }; { layout => undef };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/apchanneldist_csv.tt', { results => $set, },
{ layout => undef };
}
}; };
true; true;

View File

@@ -1,26 +1,32 @@
package App::Netdisco::Web::Plugin::Report::DuplexMismatch; package App::Netdisco::Web::Plugin::Report::DuplexMismatch;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
register_report({ register_report(
category => 'Port', { category => 'Port',
tag => 'duplexmismatch', tag => 'duplexmismatch',
label => 'Duplex Mismatches Between Devices', label => 'Duplex Mismatches Between Devices',
}); provides_csv => 1,
}
);
ajax '/ajax/content/report/duplexmismatch' => require_login sub { get '/ajax/content/report/duplexmismatch' => require_login sub {
my $set = schema('netdisco')->resultset('Virtual::DuplexMismatch'); my $set = schema('netdisco')->resultset('Virtual::DuplexMismatch');
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/report/duplexmismatch.tt', { template 'ajax/report/duplexmismatch.tt', { results => $set, },
results => $set, { layout => undef };
}, { layout => undef }; }
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/duplexmismatch_csv.tt', { results => $set, },
{ layout => undef };
}
}; };
true; true;

View File

@@ -1,7 +1,6 @@
package App::Netdisco::Web::Plugin::Report::HalfDuplex; package App::Netdisco::Web::Plugin::Report::HalfDuplex;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
@@ -11,19 +10,26 @@ register_report({
category => 'Port', category => 'Port',
tag => 'halfduplex', tag => 'halfduplex',
label => 'Ports in Half Duplex Mode', label => 'Ports in Half Duplex Mode',
provides_csv => 1,
}); });
ajax '/ajax/content/report/halfduplex' => require_login sub { get '/ajax/content/report/halfduplex' => require_login sub {
my $format = param('format');
my $set = schema('netdisco')->resultset('DevicePort')->search( my $set = schema('netdisco')->resultset('DevicePort')->search(
{ up => 'up', duplex => { '-ilike' => 'half' } }, { up => 'up', duplex => { '-ilike' => 'half' } },
{ order_by => [qw/device.dns port/], prefetch => 'device' }, { order_by => [qw/device.dns port/], prefetch => 'device' },
); );
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/report/halfduplex.tt', { template 'ajax/report/halfduplex.tt', { results => $set, },
results => $set, { layout => undef };
}, { layout => undef }; }
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/halfduplex_csv.tt', { results => $set, },
{ layout => undef };
}
}; };
true; true;

View File

@@ -1,26 +1,32 @@
package App::Netdisco::Web::Plugin::Report::PortUtilization; package App::Netdisco::Web::Plugin::Report::PortUtilization;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
register_report({ register_report(
category => 'Device', { category => 'Device',
tag => 'portutilization', tag => 'portutilization',
label => 'Port Utilization', label => 'Port Utilization',
}); provides_csv => 1,
}
);
ajax '/ajax/content/report/portutilization' => require_login sub { get '/ajax/content/report/portutilization' => require_login sub {
return unless schema('netdisco')->resultset('Device')->count; return unless schema('netdisco')->resultset('Device')->count;
my $set = schema('netdisco')->resultset('Virtual::PortUtilization'); my $set = schema('netdisco')->resultset('Virtual::PortUtilization');
content_type('text/html'); if (request->is_ajax) {
template 'ajax/report/portutilization.tt', { template 'ajax/report/portutilization.tt', { results => $set, },
results => $set, { layout => undef };
}, { layout => undef }; }
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/portutilization_csv.tt', { results => $set, },
{ layout => undef };
}
}; };
true; true;

View File

@@ -1,7 +1,6 @@
package App::Netdisco::Web::Plugin::Search::Device; package App::Netdisco::Web::Plugin::Search::Device;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
@@ -9,10 +8,12 @@ use List::MoreUtils ();
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
register_search_tab({ tag => 'device', label => 'Device' }); register_search_tab({ tag => 'device', label => 'Device', provides_csv => 1 });
my $headers = ['Device','Contact','Location','System Name','Model','OS Version','Management IP','Serial'];
# device with various properties or a default match-all # device with various properties or a default match-all
ajax '/ajax/content/search/device' => require_login sub { get '/ajax/content/search/device' => require_login sub {
my $has_opt = List::MoreUtils::any {param($_)} my $has_opt = List::MoreUtils::any {param($_)}
qw/name location dns ip description model os_ver vendor/; qw/name location dns ip description model os_ver vendor/;
my $set; my $set;
@@ -28,10 +29,17 @@ ajax '/ajax/content/search/device' => require_login sub {
} }
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/search/device.tt', { template 'ajax/search/device.tt', {results => $set},
{ layout => undef };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/search/device_csv.tt', {
results => $set, results => $set,
headers => $headers,
}, { layout => undef }; }, { layout => undef };
}
}; };
true; true;

View File

@@ -1,16 +1,15 @@
package App::Netdisco::Web::Plugin::Search::Port; package App::Netdisco::Web::Plugin::Search::Port;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
register_search_tab({ tag => 'port', label => 'Port' }); register_search_tab({ tag => 'port', label => 'Port', provides_csv => 1 });
# device ports with a description (er, name) matching # device ports with a description (er, name) matching
ajax '/ajax/content/search/port' => require_login sub { get '/ajax/content/search/port' => require_login sub {
my $q = param('q'); my $q = param('q');
send_error('Missing query', 400) unless $q; send_error('Missing query', 400) unless $q;
my $set; my $set;
@@ -30,10 +29,15 @@ ajax '/ajax/content/search/port' => require_login sub {
} }
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/search/port.tt', { template 'ajax/search/port.tt', {results => $set},
results => $set, { layout => undef };
}, { layout => undef }; }
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/search/port_csv.tt', {results => $set},
{ layout => undef };
}
}; };
true; true;

View File

@@ -1,32 +1,38 @@
package App::Netdisco::Web::Plugin::Search::VLAN; package App::Netdisco::Web::Plugin::Search::VLAN;
use Dancer ':syntax'; use Dancer ':syntax';
use Dancer::Plugin::Ajax;
use Dancer::Plugin::DBIC; use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible; use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin; use App::Netdisco::Web::Plugin;
register_search_tab({ tag => 'vlan', label => 'VLAN' }); register_search_tab( { tag => 'vlan', label => 'VLAN', provides_csv => 1 } );
# devices carrying vlan xxx # devices carrying vlan xxx
ajax '/ajax/content/search/vlan' => require_login sub { get '/ajax/content/search/vlan' => require_login sub {
my $q = param('q'); my $q = param('q');
send_error('Missing query', 400) unless $q; send_error( 'Missing query', 400 ) unless $q;
my $set; my $set;
if ($q =~ m/^\d+$/) { if ( $q =~ m/^\d+$/ ) {
$set = schema('netdisco')->resultset('Device')->carrying_vlan({vlan => $q}); $set = schema('netdisco')->resultset('Device')
->carrying_vlan( { vlan => $q } );
} }
else { else {
$set = schema('netdisco')->resultset('Device')->carrying_vlan_name({name => $q}); $set = schema('netdisco')->resultset('Device')
->carrying_vlan_name( { name => $q } );
} }
return unless $set->count; return unless $set->count;
content_type('text/html'); if (request->is_ajax) {
template 'ajax/search/vlan.tt', { template 'ajax/search/vlan.tt', { results => $set },
results => $set, { layout => undef };
}, { layout => undef }; }
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/search/vlan_csv.tt', { results => $set },
{ layout => undef };
}
}; };
true; true;

View File

@@ -200,6 +200,15 @@ td > form.nd_inline-form {
color: #6D5720; color: #6D5720;
} }
/* reset to normal weight for the download as CSV icon */
#nd_csv-download {
font-weight: normal;
}
#nd_csv-download:hover {
text-decoration: none;
}
/* for the job control admin page play/pause links */ /* for the job control admin page play/pause links */
#nd_countdown-refresh:hover, #nd_countdown-control:hover { #nd_countdown-refresh:hover, #nd_countdown-control:hover {
text-decoration: none; text-decoration: none;

View File

@@ -36,6 +36,12 @@
<i id="nd_countdown-control-icon" class="text-success icon-play"></i></a> <i id="nd_countdown-control-icon" class="text-success icon-play"></i></a>
<span id="nd_countdown"></span> <span id="nd_countdown"></span>
</span> </span>
[% ELSIF task.provides_csv %]
<span id="nd_device-name">
<a id="nd_csv-download" href="#" download="netdisco.csv">
<i id="nd_csv-download-icon" class="text-info icon-file-text-alt icon-large"
rel="tooltip" data-placement="left" data-offset="5" data-title="Download as CSV"></i></a>
</span>
[% END %] [% END %]
</ul> </ul>
<div class="tab-content"> <div class="tab-content">

View File

@@ -0,0 +1,11 @@
[% USE CSV -%]
[% CSV.dump([ 'Address' 'DNS' 'Interface' 'Description' 'Prefix' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% FOREACH col IN [ row.alias row.dns row.port row.device_port.name row.subnet ] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,10 @@
[% USE CSV -%]
[% CSV.dump([ 'Channel' 'Count' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% mylist.push(row.channel) %]
[% mylist.push(row.get_column('ch_count')) %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,13 @@
[% USE CSV -%]
[% CSV.dump([ 'Left Device' 'Port' 'Duplex' 'Right Device' 'Port' 'Duplex' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% device_left = row.left_dns || row.left_ip %]
[% device_right = row.right_dns || row.right_ip %]
[% FOREACH col IN [ device_left row.left_port row.left_duplex.ucfirst device_right row.right_port row.right_duplex.ucfirst ] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,12 @@
[% USE CSV -%]
[% CSV.dump([ 'Device' 'Port' 'Description' 'Duplex' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% device = row.device.dns || row.device.ip %]
[% FOREACH col IN [ device row.port row.name row.duplex.ucfirst ] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,12 @@
[% USE CSV -%]
[% CSV.dump([ 'Device' 'Total Ports' 'In Use' 'Shutdown' 'Free' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% device = row.dns || row.ip %]
[% FOREACH col IN [ device row.port_count row.ports_in_use row.ports_shutdown row.ports_free ] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,12 @@
[% USE CSV -%]
[% CSV.dump(headers) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% device = row.dns || row.ip %]
[% FOREACH col IN [ device row.contact row.location row.name row.model row.os_ver row.ip row.serial] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,12 @@
[% USE CSV -%]
[% CSV.dump([ 'Description' 'Port' 'Name' 'Vlan' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% myport = "$row.ip [ $row.port ] (" _ row.device.dns _ ")" IF row.device.dns %]
[% FOREACH col IN [ row.name myport row.descr row.vlan ] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -0,0 +1,12 @@
[% USE CSV -%]
[% CSV.dump([ 'Vlan' 'Device' 'Description' 'Model' 'OS' 'Vendor' ]) %]
[% WHILE (row = results.next) %]
[% mylist = [] %]
[% device = row.dns || row.ip %]
[% FOREACH col IN [ row.vlan.vlan device row.vlan.description row.model row.os row.vendor ] %]
[% mylist.push(col) %]
[% END %]
[% CSV.dump(mylist) %]
[% END %]

View File

@@ -45,7 +45,12 @@
[% FOREACH tab IN settings._device_tabs %] [% FOREACH tab IN settings._device_tabs %]
<li[% ' class="active"' IF params.tab == tab.tag %]><a id="[% tab.tag %]_link" href="#[% tab.tag %]_pane">[% tab.label %]</a></li> <li[% ' class="active"' IF params.tab == tab.tag %]><a id="[% tab.tag %]_link" href="#[% tab.tag %]_pane">[% tab.label %]</a></li>
[% END %] [% END %]
<span id="nd_device-name">[% d.dns || d.name | html_entity %]</span> <span id="nd_device-name">
[% d.dns || d.name | html_entity %]
<a id="nd_csv-download" href="#" download="netdisco.csv">&nbsp;
<i id="nd_csv-download-icon" class="text-info icon-file-text-alt icon-large"
rel="tooltip" data-placement="left" data-offset="5" data-title="Download as CSV"></i></a>
</span>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
[% FOREACH tab IN settings._device_tabs %] [% FOREACH tab IN settings._device_tabs %]

View File

@@ -1,3 +1,23 @@
// csv download icon on any table page
// needs to be dynamically updated to use current search options
function update_csv_download_link (type, tab, show) {
var form = '#' + tab + '_form';
var query = $(form).serialize();
if (show == '1') {
$('#nd_csv-download')
.attr('href', '/ajax/content/' + type + '/' + tab + '?' + query)
.attr('download', 'netdisco-' + type + '-' + tab + '.csv')
.show();
}
else {
$('#nd_csv-download').hide();
}
}
// page title includes tab name and possibly device name
// this is nice for when you have multiple netdisco pages open in the
// browser
function update_page_title (tab) { function update_page_title (tab) {
var pgtitle = 'Netdisco'; var pgtitle = 'Netdisco';
if ($('#nd_device-name').text().length) { if ($('#nd_device-name').text().length) {
@@ -43,6 +63,7 @@
[% FOREACH tab IN settings._search_tabs %] [% FOREACH tab IN settings._search_tabs %]
$('[% "#${tab.tag}_form" %]').submit(function (event) { $('[% "#${tab.tag}_form" %]').submit(function (event) {
var pgtitle = update_page_title('[% tab.tag %]'); var pgtitle = update_page_title('[% tab.tag %]');
update_csv_download_link('search', '[% tab.tag %]', '[% tab.provides_csv %]');
update_browser_history('[% tab.tag %]', pgtitle); update_browser_history('[% tab.tag %]', pgtitle);
copy_navbar_to_sidebar('[% tab.tag %]'); copy_navbar_to_sidebar('[% tab.tag %]');
do_search(event, '[% tab.tag %]'); do_search(event, '[% tab.tag %]');
@@ -55,8 +76,10 @@
[% FOREACH tab IN settings._device_tabs %] [% FOREACH tab IN settings._device_tabs %]
$('[% "#${tab.tag}_form" %]').submit(function (event) { $('[% "#${tab.tag}_form" %]').submit(function (event) {
var pgtitle = update_page_title('[% tab.tag %]'); var pgtitle = update_page_title('[% tab.tag %]');
update_csv_download_link('device', '[% tab.tag %]', '[% tab.provides_csv %]');
update_browser_history('[% tab.tag %]', pgtitle); update_browser_history('[% tab.tag %]', pgtitle);
copy_navbar_to_sidebar('[% tab.tag %]'); copy_navbar_to_sidebar('[% tab.tag %]');
[% IF tab.tag == 'ports' %] [% IF tab.tag == 'ports' %]
var cookie = $('#ports_form').find('input,select') var cookie = $('#ports_form').find('input,select')
.not('#nd_port-query,input[name="q"],input[name="tab"]') .not('#nd_port-query,input[name="q"],input[name="tab"]')
@@ -66,6 +89,7 @@
}); });
$.cookie('nd_ports-form', $.param(cookie) ,{ expires: 365 }); $.cookie('nd_ports-form', $.param(cookie) ,{ expires: 365 });
[% END %] [% END %]
do_search(event, '[% tab.tag %]'); do_search(event, '[% tab.tag %]');
}); });
[% END %] [% END %]
@@ -74,7 +98,8 @@
[% IF report %] [% IF report %]
// for the report pages // for the report pages
$('[% "#${report.tag}_form" %]').submit(function (event) { $('[% "#${report.tag}_form" %]').submit(function (event) {
update_page_title('[% tab.tag %]'); update_page_title('[% report.tag %]');
update_csv_download_link('report', '[% report.tag %]', '1');
do_search(event, '[% report.tag %]'); do_search(event, '[% report.tag %]');
}); });
[% END -%] [% END -%]
@@ -82,7 +107,8 @@
[% IF task %] [% IF task %]
// for the admin pages // for the admin pages
$('[% "#${task.tag}_form" %]').submit(function (event) { $('[% "#${task.tag}_form" %]').submit(function (event) {
update_page_title('[% tab.tag %]'); update_page_title('[% task.tag %]');
update_csv_download_link('task', '[% task.tag %]', '1');
do_search(event, '[% task.tag %]'); do_search(event, '[% task.tag %]');
}); });
[% END %] [% END %]

View File

@@ -28,6 +28,13 @@
<ul id="nd_search-results" class="nav nav-tabs"> <ul id="nd_search-results" class="nav nav-tabs">
<li class="active"><a id="[% report.tag %]_link" class="nd_single-tab" <li class="active"><a id="[% report.tag %]_link" class="nd_single-tab"
href="#[% report.tag %]_pane">[% report.label %]</a></li> href="#[% report.tag %]_pane">[% report.label %]</a></li>
[% IF report.provides_csv %]
<span id="nd_device-name">
<a id="nd_csv-download" href="#" download="netdisco.csv">
<i id="nd_csv-download-icon" class="text-info icon-file-text-alt icon-large"
rel="tooltip" data-placement="left" data-offset="5" data-title="Download as CSV"></i></a>
</span>
[% END %]
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active" id="[% report.tag %]_pane"></div> <div class="tab-pane active" id="[% report.tag %]_pane"></div>

View File

@@ -32,6 +32,11 @@
[% FOREACH tab IN settings._search_tabs %] [% FOREACH tab IN settings._search_tabs %]
<li[% ' class="active"' IF params.tab == tab.tag %]><a id="[% tab.tag %]_link" href="#[% tab.tag %]_pane">[% tab.label %]</a></li> <li[% ' class="active"' IF params.tab == tab.tag %]><a id="[% tab.tag %]_link" href="#[% tab.tag %]_pane">[% tab.label %]</a></li>
[% END %] [% END %]
<span id="nd_device-name">
<a id="nd_csv-download" href="#" download="netdisco.csv">
<i id="nd_csv-download-icon" class="text-info icon-file-text-alt icon-large"
rel="tooltip" data-placement="left" data-offset="5" data-title="Download as CSV"></i></a>
</span>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
[% FOREACH tab IN settings._search_tabs %] [% FOREACH tab IN settings._search_tabs %]