From 10f57f11e8ce40fe3ac06d83f6fe858f1e4e5ca6 Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Tue, 31 Jul 2018 13:34:55 +0200 Subject: [PATCH] TT#40509 Add "default" option to timezone select Change-Id: Id9f12d57c8cc18e2b2de1eef63f003ff5f791863 --- lib/NGCP/Panel/Controller/Contact.pm | 10 ++- lib/NGCP/Panel/Controller/Subscriber.pm | 2 +- lib/NGCP/Panel/Field/DataTable.pm | 16 ++-- lib/NGCP/Panel/Field/TimezoneSelect.pm | 100 +++++++++++++++++++++++- lib/NGCP/Panel/Utils/Datatables.pm | 18 +++++ lib/NGCP/Panel/Utils/DateTime.pm | 61 ++++++++++++++- 6 files changed, 190 insertions(+), 17 deletions(-) diff --git a/lib/NGCP/Panel/Controller/Contact.pm b/lib/NGCP/Panel/Controller/Contact.pm index bfe3addb58..4f637566c6 100644 --- a/lib/NGCP/Panel/Controller/Contact.pm +++ b/lib/NGCP/Panel/Controller/Contact.pm @@ -40,15 +40,17 @@ sub list_contact :Chained('/') :PathPart('contact') :CaptureArgs(0) :Does(ACL) : ]); } -sub timezone_ajax :Chained('/') :PathPart('contact/timezone_ajax') :Args(0) { - my ($self, $c) = @_; +sub timezone_ajax :Chained('/') :PathPart('contact/timezone_ajax') :Args() { + my ($self, $c, $parent_owner_type, $parent_owner_id) = @_; + + my $default_tz_data = NGCP::Panel::Utils::DateTime::get_default_timezone_name($c, $parent_owner_type, $parent_owner_id); - my $tz_rs = $c->model('DB')->resultset('timezones'); + my $tz_rs = $c->model('DB')->resultset('timezones'); my $tz_cols = NGCP::Panel::Utils::Datatables::set_columns($c, [ { name => "name", search => 1, title => $c->loc('Timezone') }, ]); - NGCP::Panel::Utils::Datatables::process($c, $tz_rs, $tz_cols); + NGCP::Panel::Utils::Datatables::process($c, $tz_rs, $tz_cols, undef, { topData => $default_tz_data } ); $c->detach( $c->view("JSON") ); } diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index b59289ab40..eda7013587 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -2797,7 +2797,7 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet $schema->txn_do(sub { my $email = delete $form->params->{email} || undef; - my $timezone = delete $form->params->{timezone}{name} || undef; + my $timezone = delete $form->values->{timezone}{name} || undef; if ($subscriber->contact) { $subscriber->contact->update({ email => $email, diff --git a/lib/NGCP/Panel/Field/DataTable.pm b/lib/NGCP/Panel/Field/DataTable.pm index d51d222e56..03d2a39dfd 100644 --- a/lib/NGCP/Panel/Field/DataTable.pm +++ b/lib/NGCP/Panel/Field/DataTable.pm @@ -17,18 +17,17 @@ has 'custom_renderers' => ( isa => 'HashRef', is => 'rw' ); has 'no_ordering' => ( isa => 'Bool', is => 'rw' ); has 'language_file' => (isa => 'Str', is => 'rw', default => 'dataTables.default.js' ); +#didn't want to incude some complex role related logic here, +#as these DataTable fields also are used in API +#To don't slow down API +#traits => ['Code'] +has 'adjust_datatable_vars' => ( isa => 'CodeRef', is => 'rw' ); + sub render_element { my ($self) = @_; my $output = ''; (my $tablename = $self->html_name) =~ s!\.!!g; - if($self->ajax_src !~/dt_columns/){ - my $i = 0; - my $dt_columns = [ map {{name=>$_,title=>$self->table_titles->[$i++],}} @{$self->table_fields} ]; - my $decoder = URI::Encode->new; - $self->ajax_src( $self->ajax_src.($self->ajax_src!~/\?/?'?':'&').'dt_columns='.$decoder->encode(to_json($dt_columns)) ); - } - my $vars = { label => $self->label, field_name => $self->html_name, @@ -44,7 +43,8 @@ sub render_element { language_file => $self->language_file, wrapper_class => ref $self->wrapper_class eq 'ARRAY' ? join (' ', @{$self->wrapper_class}) : $self->wrapper_class, }; - + ref $self->adjust_datatable_vars eq 'CODE' and $self->adjust_datatable_vars->($self, $vars); + my $t = new Template({ ABSOLUTE => 1, INCLUDE_PATH => [ diff --git a/lib/NGCP/Panel/Field/TimezoneSelect.pm b/lib/NGCP/Panel/Field/TimezoneSelect.pm index 2f9007c864..af78deb3a4 100644 --- a/lib/NGCP/Panel/Field/TimezoneSelect.pm +++ b/lib/NGCP/Panel/Field/TimezoneSelect.pm @@ -1,4 +1,5 @@ package NGCP::Panel::Field::TimezoneSelect; + use HTML::FormHandler::Moose; use NGCP::Panel::Utils::DateTime; extends 'HTML::FormHandler::Field::Compound'; @@ -13,10 +14,17 @@ has_field 'name' => ( ajax_src => '/contact/timezone_ajax', table_titles => ['Name'], table_fields => ['name'], + adjust_datatable_vars => \&adjust_datatable_vars, + inflate_default_method => \&inflate_timezone,#from db to form + deflate_value_method => \&deflate_timezone,#from form to db ); sub validate { my $self = shift; + my $form = $self->form; + my $c = $form->ctx; + return unless $c; + my $value = $self->value; if (ref $value && exists $value->{name}) { $value = $value->{name}; @@ -25,14 +33,102 @@ sub validate { sprintf 'Invalid validation of unparsed input: %s', $value); return; } - + #is_valid_timezone_name($tz, $all, $c, $allow_empty) #unless(grep { /^\Q$value\E$/ } DateTime::TimeZone->all_names) { - unless (NGCP::Panel::Utils::DateTime::is_valid_timezone_name($value)) { + unless (NGCP::Panel::Utils::DateTime::is_valid_timezone_name($value, 0, $c, 1)) { $self->add_error(sprintf 'Invalid timezone name: %s', $value); } return; } +sub adjust_datatable_vars { + my ($self, $vars) = @_; + my $form = $self->form; + my $ctx = $form->ctx; + return unless $ctx; + my ($tz_owner_parent_type, $tz_owner_parent_id) = get_parent_info($ctx); + $vars->{ajax_src} = ( + $ctx->uri_for_action('/contact/timezone_ajax', $tz_owner_parent_type, $tz_owner_parent_id)->as_string + ); +} + +sub deflate_timezone { # deflate: default value: clean and return empty string + my ( $self, $value ) = @_; + + my $c = $self->form->ctx; + $value = NGCP::Panel::Utils::DateTime::strip_empty_timezone_name($c, $value); + return $value; +} + +sub inflate_timezone { # inflate: name empty timezone properly so it could be checked by form + my ($self, $value) = @_; + if (!$value) { + my $c = $self->form->ctx; + my ($parent_owner_type, $parent_owner_id) = get_parent_info($c); + my $default_tz_data = NGCP::Panel::Utils::DateTime::get_default_timezone_name($c, $parent_owner_type, $parent_owner_id); + $value = $default_tz_data->{name}; + } + return $value; +} + +sub get_parent_info { + my ($c) = @_; + my ($tz_owner_parent_type,$tz_owner_parent_id) = ('', ''); + my $schema = $c->model('DB'); + if ($c->stash->{subscriber}) { + $tz_owner_parent_type = 'contract'; + #billing subscriber + $tz_owner_parent_id = $c->stash->{subscriber}->contract_id; + } elsif ($c->stash->{contract}) { + $tz_owner_parent_type = 'reseller'; + $tz_owner_parent_id = $c->stash->{contract}->contact->reseller_id // ''; + } elsif ($c->stash->{contact}) { + #edit - we can rely on contact owner info + my $contact_id = $c->stash->{contact}->id; + my %timezones_spec = ( + 'subscriber' => 'voip_subscriber_timezone', + 'contract' => 'contract_timezone'); + while (my($type,$result_set) = each %timezones_spec ) { + if (my $owner_tz = $schema->resultset($result_set)->search_rs({contact_id => $contact_id})->first) { + if ($type eq 'subscriber') { + $tz_owner_parent_type = 'contract'; + #billing subscriber + $tz_owner_parent_id = $owner_tz->contract->id; + } elsif ($type eq 'contract') { + $tz_owner_parent_type = 'reseller'; + #billing subscriber + $tz_owner_parent_id = $owner_tz->contract->contact->reseller_id // ''; + } + last; + } + } + } elsif ($c->stash->{close_target}) { + my $close_target = $c->stash->{close_target}; + if ($close_target =~m!/(contract|customer)(?:/[^0-9]+)?/([0-9]+)/!) { + if ($close_target =~m!/subscriber/create/!) { + $tz_owner_parent_type = 'contract'; + $tz_owner_parent_id = $2; + } else { + $tz_owner_parent_type = 'reseller'; + #it may be reseller contract + $tz_owner_parent_id = $schema->resultset('contracts')->search_rs({id => $2})->first->contact->reseller_id; + if (!$tz_owner_parent_id) { + #it is reseller contract + $tz_owner_parent_type = 'top'; + } + } + } elsif ($close_target =~m!/(subscriber)(?:/[^0-9]+)?/([0-9]+)/!) { + $tz_owner_parent_type = 'contract'; + #billing subscriber + $tz_owner_parent_id = $schema->resultset('voip_subscribers')->search_rs({id => $2})->first->contract_id; + } elsif ($close_target =~m!/reseller/!) { + $tz_owner_parent_type = 'top'; + } + } else { + $tz_owner_parent_type = 'noparentinfo'; + } + return $tz_owner_parent_type, $tz_owner_parent_id; +} no Moose; 1; diff --git a/lib/NGCP/Panel/Utils/Datatables.pm b/lib/NGCP/Panel/Utils/Datatables.pm index f759c39337..ad867292cc 100644 --- a/lib/NGCP/Panel/Utils/Datatables.pm +++ b/lib/NGCP/Panel/Utils/Datatables.pm @@ -251,6 +251,24 @@ sub process { } } + # show any arbitrary data rows on top, just like a union would do + # hash is expected or array of hashes expected + my $topData = $params->{topData}; + if (defined $topData) { + my $topDataArray; + if (ref $topData eq 'HASH') { + $topDataArray = [$topData]; + } else { + $topDataArray = $topData; + } + foreach my $topDataRow (@$topDataArray) { + unshift @{ $aaData }, _prune_row($user_tz, $cols, %$topDataRow); + if (defined $row_func) { + $aaData->[0] = {%{$aaData->[0]}, $row_func->($topDataRow)}; + } + } + } + if (keys %{ $aggregations }) { $c->stash(dt_custom_footer => $aggregations); } diff --git a/lib/NGCP/Panel/Utils/DateTime.pm b/lib/NGCP/Panel/Utils/DateTime.pm index ffec60e8ce..977677ecd9 100644 --- a/lib/NGCP/Panel/Utils/DateTime.pm +++ b/lib/NGCP/Panel/Utils/DateTime.pm @@ -18,8 +18,17 @@ use Time::Warp qw(); my $is_fake_time = 0; sub is_valid_timezone_name { - my ($tz,$all) = shift; - return 0 unless $tz; + my ($tz, $all, $c, $allow_empty) = @_; + if (!$tz && !$allow_empty) { + return 0; + } + if ($c) { + $tz = NGCP::Panel::Utils::DateTime::strip_empty_timezone_name($c, $tz); + #we allow empty value to switch to the parent default + if (!$tz) { + return $allow_empty ? 1 : 0; + } + } if ($all) { return DateTime::TimeZone->is_valid_name($tz); } else { @@ -28,6 +37,54 @@ sub is_valid_timezone_name { } } +sub strip_empty_timezone_name { + my ($c, $value) = @_; + my $default_names = join ('|', map {'^'.$_} ($c->loc('customer default'),$c->loc('reseller default'),$c->loc('default'))); + if ($value =~ /$default_names/i) { + return ''; + } + return $value; +} + +sub get_default_timezone_name { + my($c, $parent_owner_type, $parent_owner_id) = @_; + $parent_owner_type //= ''; + my ($default_tz_data, $default_tz_data_rs); + my $parent_tz_rs; + my $noparentinfo = ($parent_owner_type eq 'noparentinfo'); + if ($parent_owner_type && !$noparentinfo) { + if ($parent_owner_id) { + if ($parent_owner_type eq 'contract') { + $parent_tz_rs = $c->model('DB')->resultset('contract_timezone')->search_rs({ + 'contract_id' => $parent_owner_id + },{ + 'columns' => [ { name => \('concat("'.$c->loc('customer default').' (",name,")")')} ], + }); + } elsif ($parent_owner_type eq 'reseller') { + $parent_tz_rs = $c->model('DB')->resultset('reseller_timezone')->search_rs({ + 'reseller_id' => $parent_owner_id + },{ + 'columns' => [ { name => \('concat("'.$c->loc('reseller default').' (",name,")")')} ], + }); + } + } elsif ($parent_owner_type eq 'top') { + $default_tz_data = { name => $c->loc('default (localtime)') }; + } else { + $default_tz_data = { name => $c->loc('default (parent/localtime)') }; + } + } elsif ($noparentinfo) { + $default_tz_data = { name => $c->loc('default (parent/localtime)') }; + } + if (!$default_tz_data) { + if ($parent_tz_rs) { + $default_tz_data = { $parent_tz_rs->first->get_inflated_columns }; + } else { + $default_tz_data = { name => $c->loc('default (parent/localtime)') }; + } + } + return $default_tz_data; +} + sub normalize_db_tz_name { my $tz = shift; if (defined $tz) {