diff --git a/lib/NGCP/Panel/Controller/Contract.pm b/lib/NGCP/Panel/Controller/Contract.pm index 1c70bb0fc6..9b1d6c4110 100644 --- a/lib/NGCP/Panel/Controller/Contract.pm +++ b/lib/NGCP/Panel/Controller/Contract.pm @@ -32,6 +32,7 @@ sub contract_list :Chained('/') :PathPart('contract') :CaptureArgs(0) { { name => 'billing_profile_name', accessor => "billing_profile_name", search => 0, title => $c->loc('Billing Profile'), literal_sql => NGCP::Panel::Utils::BillingMappings::get_actual_billing_mapping_stmt(c => $c, now => $now, projection => 'billing_profile.name' ) }, { name => "status", search => 0, title => $c->loc("Status") }, + { title => $c->loc("Contact Email"), search => 1, no_column => 1 }, ]); my $rs = NGCP::Panel::Utils::Contract::get_contract_rs( diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index ee70394001..6eb4d57a61 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -57,6 +57,7 @@ sub list_customer :Chained('/') :PathPart('customer') :CaptureArgs(0) { literal_sql => '""' }, { name => "status", search => 0, title => $c->loc("Status") }, { name => "max_subscribers", search => 0, title => $c->loc("Max. Subscribers") }, + { title => $c->loc("Contact Email"), search => 1, no_column => 1 }, ]); my $rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c); #, now => $now); diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index bc6b8bddbc..f69152f747 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -100,6 +100,8 @@ sub sub_list :Chained('/') :PathPart('subscriber') :CaptureArgs(0) { { name => "status", search => 0, title => $c->loc('Status') }, { name => "number", search => 0, title => $c->loc('Number'), literal_sql => "concat(primary_number.cc, primary_number.ac, primary_number.sn)" }, { name => "provisioning_voip_subscriber.voip_subscriber_profile.name", search => 0, title => $c->loc('Profile') }, + { title => $c->loc("Contact Email"), search => 1, no_column => 1 }, + { title => $c->loc("Aliases"), search => 1, no_column => 1 }, ]); } diff --git a/lib/NGCP/Panel/Field/DataTable.pm b/lib/NGCP/Panel/Field/DataTable.pm index f3c8b7d573..b954ff70d5 100644 --- a/lib/NGCP/Panel/Field/DataTable.pm +++ b/lib/NGCP/Panel/Field/DataTable.pm @@ -18,7 +18,7 @@ has 'no_ordering' => ( isa => 'Bool', is => 'rw' ); has 'no_pagination' => ( isa => 'Bool', is => 'rw', default => 0 ); has 'only_visible_values' => ( isa => 'Bool', is => 'rw', default => 0 ); has 'language_file' => (isa => 'Str', is => 'rw', default => 'dataTables.default.js' ); -has 'search_tooltip' => (isa => 'Str', is => 'rw', default => 'Filter for column values matching the pattern string, e.g. 12*45. The * (wildcard) is implicitly prepended and appended.' ); +has 'search_tooltip' => (isa => 'Str', is => 'rw' ); #didn't want to include some complex role related logic here, #as these DataTable fields also are used in API diff --git a/lib/NGCP/Panel/Utils/Datatables.pm b/lib/NGCP/Panel/Utils/Datatables.pm index e923f51af7..7c171d70ac 100644 --- a/lib/NGCP/Panel/Utils/Datatables.pm +++ b/lib/NGCP/Panel/Utils/Datatables.pm @@ -35,7 +35,7 @@ sub process { ### Search processing section - ($rs,my @searchColumns) = _apply_search_filters($c,$rs,$cols,$use_rs_cb,$params->{extra_or}); + ($rs,my @searchColumns) = _apply_search_filters($c,$rs,$cols,$use_rs_cb,$params->{extra_or},$params->{extra_or_descr}); my $is_set_operations = 0; ($displayRecords, $displayRecordCountClipped, $is_set_operations) = _get_count_safe($c,$rs,$params) if (!$use_rs_cb); @@ -109,6 +109,7 @@ sub process { # first, get the fields we're actually showing my @displayedFields = (); for my $col(@{ $cols }) { + next if $col->{no_column}; next unless $col->{title}; my $name = get_column_order_name($col); push @displayedFields, $name; @@ -197,9 +198,9 @@ sub process { } sub apply_dt_joins_filters { - my ($c,$rs, $cols, $extra_or) = @_; + my ($c,$rs, $cols, $extra_or, $extra_or_descr) = @_; $rs = _resolve_joins($rs, $cols, undef, 1, 1); - ($rs,my @searchColumns) = _apply_search_filters($c, $rs, $cols, $extra_or); + ($rs,my @searchColumns) = _apply_search_filters($c, $rs, $cols, $extra_or, $extra_or_descr); return $rs; } @@ -207,6 +208,7 @@ sub _resolve_joins { my ($rs, $cols, $aggregate_cols, $skip_aggregates,$join_only) = @_; for my $col(@{ $cols }) { + next unless $col->{name}; if ($col->{show_total}) { push(@$aggregate_cols, $col) if defined $aggregate_cols; next if $skip_aggregates; @@ -264,7 +266,7 @@ sub get_search_string_pattern { sub _apply_search_filters { - my ($c,$rs,$cols,$use_rs_cb,$extra_or) = @_; + my ($c,$rs,$cols,$use_rs_cb,$extra_or,$extra_or_descr) = @_; # generic searching my @searchColumns = (); my %conjunctSearchColumns = (); @@ -273,6 +275,7 @@ sub _apply_search_filters { if (length($searchString) && !$use_rs_cb) { #for search string from one search input we need to check all columns which contain the 'search' spec (now: qw/search search_lower_column search_upper_column/). so, for example user entered into search input ip address - we don't know that it is ip address, so we check that name like search OR id like search OR search is between network_lower_value and network upper value foreach my $col(@{ $cols }) { + next unless $col->{name}; my ($name,$search_value,$op,$convert); # avoid amigious column names if we have the same column in different joined tables if($col->{search} or $col->{strict_search} or $col->{int_search}){ @@ -352,6 +355,7 @@ sub _apply_search_filters { $to_date = NGCP::Panel::Utils::DateTime::from_forminput_string($to_date_in, $c->session->{user_tz}); } foreach my $col(@{ $cols }) { + next unless $col->{name}; # avoid amigious column names if we have the same column in different joined tables my $name = _get_joined_column_name_($col->{name}); if($col->{search_from_epoch} && $from_date) { @@ -466,6 +470,7 @@ sub set_columns { for my $col(@{ $cols }) { next if defined $col->{accessor}; + next unless $col->{name}; $col->{accessor} = $col->{name}; $col->{accessor} =~ s/\./_/g; } @@ -475,7 +480,7 @@ sub set_columns { sub _prune_row { my ($user_tz, $columns, %row) = @_; while (my ($k,$v) = each %row) { - unless (first { $_->{accessor} eq $k && ($_->{title} || $_->{field}) } @{ $columns }) { + unless (first { !$_->{no_column} && $_->{accessor} eq $k && ($_->{title} || $_->{field}) } @{ $columns }) { delete $row{$k}; next; } diff --git a/share/static/css/bootstrap/bootstrap.css b/share/static/css/bootstrap/bootstrap.css index 5b7fe7e856..788a19506d 100644 --- a/share/static/css/bootstrap/bootstrap.css +++ b/share/static/css/bootstrap/bootstrap.css @@ -5332,7 +5332,6 @@ input[type="submit"].btn.btn-mini { } .tooltip-inner { - max-width: 200px; padding: 8px; color: #ffffff; text-align: center; @@ -5341,6 +5340,11 @@ input[type="submit"].btn.btn-mini { -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; + +} + +.tooltip-inner dl { + test-align: left; } .tooltip-arrow { diff --git a/share/templates/helpers/datatables.tt b/share/templates/helpers/datatables.tt index cba2ae1db5..ead18b416c 100644 --- a/share/templates/helpers/datatables.tt +++ b/share/templates/helpers/datatables.tt @@ -6,14 +6,27 @@ helper.custom_renderers = helper.custom_renderers ? helper.custom_renderers : {}; helper.options = {}; helper.columns_hidden = {}; - helper.strict_search = 0; + helper.search_tooltip = c.loc('Filter records for values of columns matching either condition:') _ '
'; FOR col IN helper.dt_columns; IF !col.accessor; col.accessor = col.name; col.accessor = col.accessor.replace('\.','_'); END; - IF col.strict_search; - helper.strict_search = 1; + search_dt = col.title; + UNLESS search_dt; + search_dt = col.accessor; + END; + search_dd = ''; + IF col.search; + search_dd = c.loc('Search by wildcard pattern. A wildcard is implicitly appended at the end of the search string if no wildcard is used. A leading wildcard prevents using databases indexes (slow).'); + ELSIF col.strict_search; + search_dd = c.loc('Search by the exact search string value (The * wildcard is considered as a literal string).'); + ELSIF col.int_search; + search_dd = c.loc('Search string is converted to an integer value to search for.'); + END; + IF search_dd; + helper.search_tooltip = helper.search_tooltip _ '
' _ search_dt _ '
'; + helper.search_tooltip = helper.search_tooltip _ '
' _ search_dd _ '
'; END; NEXT UNLESS col.title; helper.column_titles.push(col.title); @@ -30,6 +43,7 @@ helper.options.${col.accessor}.dont_skip_empty_data = 1; END; END; + helper.search_tooltip = helper.search_tooltip _ '
'; END; backuri = c.req.uri; tmp = backuri.query_param_delete('back'); @@ -38,10 +52,6 @@ helper.identifier = 0; # make sure we don't use the same identifier twice IF search_tooltip; helper.search_tooltip = c.loc(search_tooltip); - ELSIF helper.strict_search; - helper.search_tooltip = c.loc('Filter for column values matching the string, e.g. 12345. A leading * (wildcard) can result in long response times.'); - ELSE; - helper.search_tooltip = c.loc('Filter for column values matching the pattern string, e.g. 12*45. The * (wildcard) is implicitly prepended and appended.'); END; -%] @@ -285,7 +295,9 @@ $(document).ready(function() { "initComplete": function(settings, json) { if ("[% helper.search_tooltip %]".length > 0) { $( "#[% helper.id_from_name %]_table_filter input" ).tooltip({ - title: "[% helper.search_tooltip %]" + title: "[% helper.search_tooltip %]", + html: true, + placement: 'bottom' }); } }