diff --git a/Netdisco/Changes b/Netdisco/Changes index f5aac50e..fc777318 100644 --- a/Netdisco/Changes +++ b/Netdisco/Changes @@ -1,3 +1,9 @@ +2.024003_001 - 2014-03-02 + + [ENHANCEMENTS] + + * Partial search on Device Port MAC address + 2.024003 - 2014-02-27 [BUG FIXES] diff --git a/Netdisco/lib/App/Netdisco/Util/Web.pm b/Netdisco/lib/App/Netdisco/Util/Web.pm index b0a86692..86d48182 100644 --- a/Netdisco/lib/App/Netdisco/Util/Web.pm +++ b/Netdisco/lib/App/Netdisco/Util/Web.pm @@ -8,7 +8,7 @@ use Time::Piece; use Time::Seconds; our @EXPORT = (); our @EXPORT_OK = qw/ - sort_port sort_modules interval_to_daterange + sort_port sort_modules interval_to_daterange sql_match /; our %EXPORT_TAGS = (all => \@EXPORT_OK); @@ -25,6 +25,35 @@ subroutines. =head1 EXPORT_OK +=head2 sql_match( $value, $exact? ) + +Convert wildcard characters "C<*>" and "C" to "C<%>" and "C<_>" +respectively. + +Pass a true value to C<$exact> to only substitute the existing wildcards, and +not also add "C<*>" to each end of the value. + +In list context, returns two values, the translated value, and also an +L LIKE clause. + +=cut + +sub sql_match { + my ($text, $exact) = @_; + return unless $text; + + $text =~ s/^\s+//; + $text =~ s/\s+$//; + + $text =~ s/[*]+/%/g; + $text =~ s/[?]/_/g; + + $text = '%'. $text . '%' unless $exact; + $text =~ s/\%+/%/g; + + return ( wantarray ? ($text, {-ilike => $text}) : $text ); +} + =head2 sort_port( $a, $b ) Sort port names of various types used by device vendors. Interface is as diff --git a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm index 1dda5c89..c32e926f 100644 --- a/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm +++ b/Netdisco/lib/App/Netdisco/Web/Plugin/Search/Port.pm @@ -5,6 +5,7 @@ use Dancer::Plugin::DBIC; use Dancer::Plugin::Auth::Extensible; use App::Netdisco::Web::Plugin; +use App::Netdisco::Util::Web 'sql_match'; register_search_tab({ tag => 'port', label => 'Port', provides_csv => 1 }); @@ -19,13 +20,14 @@ get '/ajax/content/search/port' => require_login sub { ->search({vlan => $q}); } else { - my $query = $q; - if (param('partial')) { - $q = "\%$q\%" if $q !~ m/%/; - $query = { -ilike => $q }; - } + my ($likeval, $likeclause) = sql_match($q); + $set = schema('netdisco')->resultset('DevicePort') - ->search({name => $query}); + ->search({-or => [ + {name => (param('partial') ? $likeclause : $q)}, + (length $q == 17 ? {mac => $q} + : \['mac::text ILIKE ?', $likeval]), + ]}); } return unless $set->count; diff --git a/Netdisco/lib/App/Netdisco/Web/Search.pm b/Netdisco/lib/App/Netdisco/Web/Search.pm index 511f1452..54fa0b6d 100644 --- a/Netdisco/lib/App/Netdisco/Web/Search.pm +++ b/Netdisco/lib/App/Netdisco/Web/Search.pm @@ -5,6 +5,8 @@ use Dancer::Plugin::Ajax; use Dancer::Plugin::DBIC; use Dancer::Plugin::Auth::Extensible; +use App::Netdisco::Util::Web 'sql_match'; + hook 'before' => sub { # view settings for node options var('node_options' => [ @@ -75,6 +77,7 @@ get '/search' => require_login sub { } else { my $nd = $s->resultset('Device')->search_fuzzy($q); + my ($likeval, $likeclause) = sql_match($q); if ($nd and $nd->count) { if ($nd->count == 1) { @@ -90,7 +93,14 @@ get '/search' => require_login sub { params->{'tab'} = 'device'; } elsif ($s->resultset('DevicePort') - ->search({name => "\%$q\%"})->count) { + ->search({ + -or => [ + {name => $likeclause}, + (length $q == 17 ? {mac => $q} + : \['mac::text ILIKE ?', $likeval]), + ], + })->count) { + params->{'tab'} = 'port'; } } diff --git a/Netdisco/share/views/sidebar/search/port.tt b/Netdisco/share/views/sidebar/search/port.tt index c11ca2c2..3266940a 100644 --- a/Netdisco/share/views/sidebar/search/port.tt +++ b/Netdisco/share/views/sidebar/search/port.tt @@ -7,7 +7,7 @@ name="partial"[% ' checked="checked"' IF params.partial %]/>