Files
netdisco/lib/App/Netdisco/Web/GenericReport.pm
Oliver Gorwits bc47b54f67 improve searchable generic report fields (#1133)
* implement links on all v4/v6/mac found in _searchable fields

* word boundaries on matching
2023-12-07 07:49:18 +00:00

140 lines
5.0 KiB
Perl
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package App::Netdisco::Web::GenericReport;
use Dancer ':syntax';
use Dancer::Plugin::DBIC;
use Dancer::Plugin::Auth::Extensible;
use App::Netdisco::Web::Plugin;
use Path::Class 'file';
use Storable 'dclone';
use Safe;
use HTML::Entities 'encode_entities';
use Regexp::Common qw( RE_net_IPv4 RE_net_IPv6 RE_net_MAC RE_net_domain );
use Regexp::Common::net::CIDR ();
our ($config, @data);
foreach my $report (@{setting('reports')}) {
my $r = $report->{tag};
register_report({
tag => $r,
label => $report->{label},
category => ($report->{category} || 'My Reports'),
($report->{hidden} ? (hidden => true) : ()),
provides_csv => true,
api_endpoint => true,
bind_params => $report->{bind_params},
api_parameters => $report->{api_parameters},
});
get "/ajax/content/report/$r" => require_login sub {
# TODO: this should be done by creating a new Virtual Result class on
# the fly (package...) and then calling DBIC register_class on it.
my $schema = ($report->{database} || vars->{'tenant'});
my $rs = schema($schema)->resultset('Virtual::GenericReport')->result_source;
(my $query = $report->{query}) =~ s/;$//;
# unpick the rather hairy config of 'columns' to get field,
# displayname, and "_"-prefixed options
my %column_config = ();
my @column_order = ();
foreach my $col (@{ $report->{columns} }) {
foreach my $k (keys %$col) {
if ($k !~ m/^_/) {
push @column_order, $k;
$column_config{$k} = dclone($col || {});
$column_config{$k}->{displayname} = delete $column_config{$k}->{$k};
}
}
}
$rs->view_definition($query);
$rs->remove_columns($rs->columns);
$rs->add_columns( exists $report->{query_columns}
? @{ $report->{query_columns} } : @column_order
);
my $set = schema($schema)->resultset('Virtual::GenericReport')
->search(undef, {
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
( (exists $report->{bind_params})
? (bind => [map { param($_) } @{ $report->{bind_params} }]) : () ),
});
@data = $set->all;
# Data Munging support...
my $compartment = Safe->new;
$config = $report; # closure for the config of this report
$compartment->share(qw/$config @data/);
$compartment->permit_only(qw/:default sort/);
my $munger = file(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'site_plugins', $r)->stringify;
my @results = ((-f $munger) ? $compartment->rdo( $munger ) : @data);
return if $@ or (0 == scalar @results);
# searchable field support..
my $recidr4 = $RE{net}{CIDR}{IPv4}{-keep}; #RE_net_CIDR_IPv4(-keep);
my $rev4 = RE_net_IPv4(-keep);
my $rev6 = RE_net_IPv6(-keep);
my $remac = RE_net_MAC(-keep);
#my $redom = RE_net_domain(-keep, -nospace, -rfc1101);
foreach my $row (@results) {
foreach my $col (@column_order) {
next unless $column_config{$col}->{_searchable};
my $fields = (ref $row->{$col} ? $row->{$col} : [$row->{$col}]);
foreach my $f (@$fields) {
# seems too sensitive match to be useful :-(
#$f =~ s!\b${redom}\b!'<a href="'.
# uri_for('/search', {q => $1 .($2 ? "/$2" : '')})->path_query
# .'">'. encode_entities($1 .($2 ? "/$2" : '')) .'</a>'!gex;
$f =~ s!\b${recidr4}\b!'<a href="'.
uri_for('/search', {q => "$1/$2"})->path_query
.'">'. encode_entities("$1/$2") .'</a>'!gex;
if (not $1 and not $2) {
$f =~ s!\b${rev4}\b!'<a href="'.
uri_for('/search', {q => $1})->path_query
.'">'. encode_entities($1) .'</a>'!gex;
}
$f =~ s!\b${rev6}\b!'<a href="'.
uri_for('/search', {q => $1})->path_query
.'">'. encode_entities($1) .'</a>'!gex;
$f =~ s!\b${remac}\b!'<a href="'.
uri_for('/search', {q => $1})->path_query
.'">'. encode_entities($1) .'</a>'!gex;
$row->{$col} = $f if not ref $row->{$col};
}
}
}
if (request->is_ajax) {
template 'ajax/report/generic_report.tt',
{ results => \@results,
is_custom_report => true,
column_options => \%column_config,
headings => [map {$column_config{$_}->{displayname}} @column_order],
columns => [@column_order] }, { layout => 'noop' };
}
else {
header( 'Content-Type' => 'text/comma-separated-values' );
template 'ajax/report/generic_report_csv.tt',
{ results => \@results,
headings => [map {$column_config{$_}->{displayname}} @column_order],
columns => [@column_order] }, { layout => 'noop' };
}
};
}
true;