diff --git a/debian/control b/debian/control index 315ac70324..5d72102584 100644 --- a/debian/control +++ b/debian/control @@ -42,6 +42,7 @@ Depends: gettext, libemail-sender-perl, libemail-valid-perl, libfcgi-procmanager-perl, + libfile-fnmatch-perl, libfile-slurp-perl, libfile-slurp-unicode-perl, libfile-spec-perl (>= 3.4000~) | perl-base (>= 5.20.0-60~), @@ -89,6 +90,7 @@ Depends: gettext, libtext-csv-xs-perl, libtext-glob-perl, libtext-table-perl, + libtime-period-perl, libtime-warp-perl, libtypes-path-tiny-perl, liburi-encode-perl, diff --git a/lib/NGCP/Panel/Controller/API/CFMappings.pm b/lib/NGCP/Panel/Controller/API/CFMappings.pm index f2fa968c00..63faf069ee 100644 --- a/lib/NGCP/Panel/Controller/API/CFMappings.pm +++ b/lib/NGCP/Panel/Controller/API/CFMappings.pm @@ -20,7 +20,7 @@ sub allowed_methods{ } sub api_description { - return 'Specifies callforward mappings of a subscriber, where multiple mappings can be specified per type (cfu, cfb, cft cfna) ' . + return 'Specifies callforward mappings of a subscriber, where multiple mappings can be specified per type (cfu, cfb, cft, cfna, cfs) ' . 'Each mapping consists of a destinationset name (see CFDestinationSets), a timeset name ' . '(see CFTimeSets) and a sourceset name (see CFSourceSets).'; } @@ -41,6 +41,7 @@ sub documentation_sample { cft => [], cft_ringtimeout => "200", cfu => [], + cfs => [], } ; } diff --git a/lib/NGCP/Panel/Controller/API/CallForwards.pm b/lib/NGCP/Panel/Controller/API/CallForwards.pm index 7481c5a708..8286626d73 100644 --- a/lib/NGCP/Panel/Controller/API/CallForwards.pm +++ b/lib/NGCP/Panel/Controller/API/CallForwards.pm @@ -23,7 +23,7 @@ sub allowed_methods{ sub api_description { return 'Specifies basic callforwards of a subscriber, where a number of destinations, times and sources ' . - ' can be specified for each type (cfu, cfb, cft cfna). For more complex configurations with ' . + ' can be specified for each type (cfu, cfb, cft, cfna, cfs). For more complex configurations with ' . ' multiple combinations of Timesets, Destinationsets and SourceSets see CFMappings.'; }; @@ -45,6 +45,7 @@ sub documentation_sample { cfna => {}, cft => { "ringtimeout" => "199" }, cfu => {}, + cfs => {}, }; } diff --git a/lib/NGCP/Panel/Controller/API/CallLists.pm b/lib/NGCP/Panel/Controller/API/CallLists.pm index 80feb1231d..a2c0f6b0bc 100644 --- a/lib/NGCP/Panel/Controller/API/CallLists.pm +++ b/lib/NGCP/Panel/Controller/API/CallLists.pm @@ -127,7 +127,7 @@ sub query_params { }, { param => 'type', - description => 'Filter for calls with a specific type. One of "call", "cfu", "cfb", "cft", "cfna".', + description => 'Filter for calls with a specific type. One of "call", "cfu", "cfb", "cft", "cfna", "cfs".', query => { first => sub { my $q = shift; @@ -140,7 +140,7 @@ sub query_params { }, { param => 'type_ne', - description => 'Filter for calls not having a specific type. One of "call", "cfu", "cfb", "cft", "cfna".', + description => 'Filter for calls not having a specific type. One of "call", "cfu", "cfb", "cft", "cfna", "cfs".', query => { first => sub { my $q = shift; diff --git a/lib/NGCP/Panel/Controller/API/SMS.pm b/lib/NGCP/Panel/Controller/API/SMS.pm index 58e3742b6a..81af468ad3 100644 --- a/lib/NGCP/Panel/Controller/API/SMS.pm +++ b/lib/NGCP/Panel/Controller/API/SMS.pm @@ -82,11 +82,11 @@ sub query_params { }, { param => 'direction', - description => 'Filter for messages sent ("out") or received ("in").', + description => 'Filter for messages sent ("out"), received ("in") or forwarded ("forward").', query => { first => sub { my $q = shift; - if ($q eq "out" || $q eq "in") { + if ($q eq "out" || $q eq "in" || $q eq "forward") { return { "me.direction" => $q }; } else { return {}, diff --git a/lib/NGCP/Panel/Controller/API/SubscriberProfiles.pm b/lib/NGCP/Panel/Controller/API/SubscriberProfiles.pm index 1a0226c4a5..5c014d0a63 100644 --- a/lib/NGCP/Panel/Controller/API/SubscriberProfiles.pm +++ b/lib/NGCP/Panel/Controller/API/SubscriberProfiles.pm @@ -223,7 +223,7 @@ sub POST :Allow { expose_to_customer => 1, }, { - attribute => { -in => [qw/cfu cft cfna cfb/] }, + attribute => { -in => [qw/cfu cft cfna cfb cfs/] }, }, ], }); diff --git a/lib/NGCP/Panel/Controller/InternalSms.pm b/lib/NGCP/Panel/Controller/InternalSms.pm index 2c93d700df..5701815ff9 100644 --- a/lib/NGCP/Panel/Controller/InternalSms.pm +++ b/lib/NGCP/Panel/Controller/InternalSms.pm @@ -2,8 +2,9 @@ package NGCP::Panel::Controller::InternalSms; use NGCP::Panel::Utils::Generic qw(:all); use Sipwise::Base; - use parent 'Catalyst::Controller'; +use Time::Period; +use File::FnMatch qw(:fnmatch); #sub auto :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) { sub auto { @@ -38,6 +39,7 @@ sub receive :Chained('list') :PathPart('receive') :Args(0) { $to =~ s/^\+//; $from =~ s/^\+//; + my $now = time; try { my $schema = $c->model('DB'); @@ -62,6 +64,101 @@ sub receive :Chained('list') :PathPart('receive') :Args(0) { callee => $to, text => $text, }); + + # check for cfs + + my $cf_maps = $c->model('DB')->resultset('voip_cf_mappings')->search({ + subscriber_id => $prov_dbalias->subscriber_id, + type => 'cfs' + }); + unless($cf_maps->count) { + $c->log->info("No cfs for inbound sms from $from to $to found."); + last; + } + + my $dset; + foreach my $map($cf_maps->all) { + # check source set + my $source_pass = 1; + if($map->source_set) { + $source_pass = 0; + foreach my $source($map->source_set->voip_cf_sources->all) { + $c->log->info(">>>> checking $from against ".$source->source); + if(fnmatch($source->source, $from)) { + $c->log->info(">>>> matched $from against ".$source->source.", pass"); + $source_pass = 1; + last; + } + } + } + if($source_pass) { + $c->log->info(">>>> source check for $from passed, continue with time check"); + } else { + $c->log->info(">>>> source check for $from failed, trying next map entry"); + next; + } + + my $time_pass = 1; + if($map->time_set) { + $time_pass = 0; + foreach my $time($map->time_set->voip_cf_periods->all) { + my $timestring = join(' ', + $time->year // '', + $time->month // '', + $time->mday // '', + $time->wday // '', + $time->hour // '', + $time->minute // '' + ); + $c->log->info(">>>> checking $now against ".$timestring); + if(inPeriod($now, $timestring)) { + $c->log->info(">>>> matched $now against ".$timestring.", pass"); + $time_pass = 1; + last; + } + } + } + if($time_pass) { + $c->log->info(">>>> time check for $now passed, use destination set"); + $dset = $map->destination_set; + last; + } else { + $c->log->info(">>>> time check for $now failed, trying next map entry"); + next; + } + } + + unless($dset) { + $c->log->info(">>>> checks failed, bailing out without forwarding"); + last; + } + + $c->log->info(">>>> proceed sms forwarding"); + + unless($dset->voip_cf_destinations->first) { + $c->log->info(">>>> detected cf mapping has no destinations in destination set"); + last; + } + my $dst = $dset->voip_cf_destinations->first->destination; + $dst =~ s/^sip:(.+)\@.+$/$1/; + $c->log->info(">>>> forward sms to $dst"); + + # feed back into kannel + my $error_msg; + NGCP::Panel::Utils::SMS::send_sms( + c => $c, + caller => $to, # use the original to as new from + callee => $dst, + text => $text, + err_code => sub {$error_msg = shift;}, + ); + my $fwd_item = $c->model('DB')->resultset('sms_journal')->create({ + subscriber_id => $prov_dbalias->subscriber_id, + direction => "forward", + caller => $to, + callee => $dst, + text => $text, + }); }); } catch($e) { $c->log->error("Failed to store received SMS message."); diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index df6e7cfc8b..31bb4fb43c 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -624,7 +624,7 @@ sub preferences :Chained('base') :PathPart('preferences') :Args(0) { my $prov_subscriber = $c->stash->{subscriber}->provisioning_voip_subscriber; my $cfs = {}; - foreach my $type(qw/cfu cfna cft cfb/) { + foreach my $type(qw/cfu cfna cft cfb cfs/) { my $maps = $prov_subscriber->voip_cf_mappings ->search({ type => $type }); $cfs->{$type} = []; @@ -695,7 +695,7 @@ sub preferences :Chained('base') :PathPart('preferences') :Args(0) { $c->stash->{pref_groups} = \@newprefgroups; my $special_prefs = { check => 1 }; - foreach my $pref(qw/cfu cft cfna cfb + foreach my $pref(qw/cfu cft cfna cfb cfs speed_dial reminder auto_attendant voice_mail fax_server/) { my $preference = $c->model('DB')->resultset('voip_preferences')->find({ @@ -839,6 +839,10 @@ sub preferences_callforward :Chained('base') :PathPart('preferences/callforward' $cf_desc = $c->loc('Call Forward Unavailable'); last SWITCH; }; + /^cfs$/ && do { + $cf_desc = $c->loc('Call Forward SMS'); + last SWITCH; + }; # default NGCP::Panel::Utils::Message::error( c => $c, @@ -1096,6 +1100,10 @@ sub preferences_callforward_advanced :Chained('base') :PathPart('preferences/cal $cf_desc = $c->loc('Call Forward Unavailable'); last SWITCH; }; + /^cfs$/ && do { + $cf_desc = $c->loc('Call Forward SMS'); + last SWITCH; + }; # default NGCP::Panel::Utils::Message::error( c => $c, @@ -2620,7 +2628,7 @@ sub edit_master :Chained('master') :PathPart('edit') :Args(0) :Does(ACL) :ACLDet if(keys %old_profile_attributes) { my $cfs = $c->model('DB')->resultset('voip_preferences')->search({ id => { -in => [ keys %old_profile_attributes ] }, - attribute => { -in => [qw/cfu cfb cft cfna/] }, + attribute => { -in => [qw/cfu cfb cft cfna cfs/] }, }); $prov_subscriber->voip_usr_preferences->search({ attribute_id => { -in => [ keys %old_profile_attributes ] }, diff --git a/lib/NGCP/Panel/Controller/SubscriberProfile.pm b/lib/NGCP/Panel/Controller/SubscriberProfile.pm index c7f8d204d8..ce382b2800 100644 --- a/lib/NGCP/Panel/Controller/SubscriberProfile.pm +++ b/lib/NGCP/Panel/Controller/SubscriberProfile.pm @@ -517,7 +517,7 @@ sub profile_edit :Chained('profile_base') :PathPart('edit') :Does(ACL) :ACLDetac if(keys %old_attributes) { my $cfs = $c->model('DB')->resultset('voip_preferences')->search({ id => { -in => [ keys %old_attributes ] }, - attribute => { -in => [qw/cfu cfb cft cfna/] }, + attribute => { -in => [qw/cfu cfb cft cfna cfs/] }, }); my @subs = $c->model('DB')->resultset('provisioning_voip_subscribers') ->search({ diff --git a/lib/NGCP/Panel/Form/CFMappingsAPI.pm b/lib/NGCP/Panel/Form/CFMappingsAPI.pm index 49474f41fa..47243f374c 100644 --- a/lib/NGCP/Panel/Form/CFMappingsAPI.pm +++ b/lib/NGCP/Panel/Form/CFMappingsAPI.pm @@ -64,6 +64,19 @@ has_field 'cfna' => ( }, ); +has_field 'cfs' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['Call Forward SMS, Number of Objects, each containing the keys ' . + '"destinationset", "timeset" and "sourceset". The values must be the name of ' . + 'a corresponding set which belongs to the same subscriber.'], + }, +); + has_field 'cfu.destinationset' => ( type => 'Text', do_wrapper => 1, @@ -136,6 +149,24 @@ has_field 'cfna.sourceset' => ( do_label => 0, ); +has_field 'cfs.destinationset' => ( + type => 'Text', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfs.timeset' => ( + type => 'Text', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfs.sourceset' => ( + type => 'Text', + do_wrapper => 1, + do_label => 0, +); + has_field 'cft_ringtimeout' => ( type => 'PosInteger', do_wrapper => 1, @@ -145,7 +176,7 @@ has_field 'cft_ringtimeout' => ( has_block 'fields' => ( tag => 'div', class => [qw(modal-body)], - render_list => [qw(cfu cfb cft cfna)], + render_list => [qw(cfu cfb cft cfna cfs)], ); 1; diff --git a/lib/NGCP/Panel/Form/CFSimpleAPI.pm b/lib/NGCP/Panel/Form/CFSimpleAPI.pm index c5d594dd49..c39e315684 100644 --- a/lib/NGCP/Panel/Form/CFSimpleAPI.pm +++ b/lib/NGCP/Panel/Form/CFSimpleAPI.pm @@ -69,6 +69,20 @@ has_field 'cfna' => ( }, ); +has_field 'cfs' => ( + type => 'Compound', + do_wrapper => 1, + do_label => 0, + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['Call Forward SMS, Contains the keys "destinations", "times" and "sources". "destinations" is an Array of Objects ' . + 'having a "destination", "priority" and "timeout" field. "times" is an Array of Objects having the fields ' . + '"minute", "hour", "wday", "mday", "month", "year". "times" can be empty, then the CF is applied always. ' . + '"sources" is an Array of Objects having one field "source". "sources" can be empty.'], + }, +); + has_field 'cfu.destinations' => ( type => 'Repeatable', do_wrapper => 1, @@ -189,6 +203,36 @@ has_field 'cfna.sources.source' => ( type => 'Text', ); +has_field 'cfs.destinations' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfs.destinations.destination' => ( + type => 'Text', +); + +has_field 'cfs.destinations.timeout' => ( + type => 'PosInteger', +); + +has_field 'cfs.times' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfs.sources' => ( + type => 'Repeatable', + do_wrapper => 1, + do_label => 0, +); + +has_field 'cfs.sources.source' => ( + type => 'Text', +); + has_field 'cft.ringtimeout' => ( type => 'PosInteger', do_wrapper => 1, @@ -198,7 +242,7 @@ has_field 'cft.ringtimeout' => ( has_block 'fields' => ( tag => 'div', class => [qw(modal-body)], - render_list => [qw(cfu cfb cft cfna)], + render_list => [qw(cfu cfb cft cfna cfs)], ); 1; diff --git a/lib/NGCP/Panel/Form/Call/Reseller.pm b/lib/NGCP/Panel/Form/Call/Reseller.pm index 39bfed623c..d43c32e1ea 100644 --- a/lib/NGCP/Panel/Form/Call/Reseller.pm +++ b/lib/NGCP/Panel/Form/Call/Reseller.pm @@ -400,10 +400,11 @@ has_field 'call_type' => ( { label => 'cfb', value => 'cfb' }, { label => 'cft', value => 'cft' }, { label => 'cfna', value => 'cfna' }, + { label => 'cfs', value => 'cfs' }, ], element_attr => { rel => ['tooltip'], - title => ['The type of call, one of call, cfu, cfb, cft, cfna.'] + title => ['The type of call, one of call, cfu, cfb, cft, cfna, cfs.'] }, ); diff --git a/lib/NGCP/Panel/Form/CallList/Subscriber.pm b/lib/NGCP/Panel/Form/CallList/Subscriber.pm index 8df3017025..209c62eef1 100644 --- a/lib/NGCP/Panel/Form/CallList/Subscriber.pm +++ b/lib/NGCP/Panel/Form/CallList/Subscriber.pm @@ -80,10 +80,11 @@ has_field 'type' => ( { label => 'cfb', value => 'cfb' }, { label => 'cft', value => 'cft' }, { label => 'cfna', value => 'cfna' }, + { label => 'cfs', value => 'cfs' }, ], element_attr => { rel => ['tooltip'], - title => ['The type of call, one of call, cfu, cfb, cft, cfna.'] + title => ['The type of call, one of call, cfu, cfb, cft, cfna, cfs.'] }, ); diff --git a/lib/NGCP/Panel/Form/SMSAPI.pm b/lib/NGCP/Panel/Form/SMSAPI.pm index 0571bb0789..179288fa07 100644 --- a/lib/NGCP/Panel/Form/SMSAPI.pm +++ b/lib/NGCP/Panel/Form/SMSAPI.pm @@ -33,12 +33,13 @@ has_field 'direction' => ( options => [ { value => 'in', label => 'inbound' }, { value => 'out', label => 'outbound' }, + { value => 'forward', label => 'forwarded' }, ], default => 'out', # FYI, default is not considered with API required => 0, # should be "1" actually, see above element_attr => { rel => ['tooltip'], - title => ['Whether the logged message is sent or received'], + title => ['Whether the logged message is sent, received or forwarded'], }, ); diff --git a/lib/NGCP/Panel/Form/SubscriberProfile/Profile.pm b/lib/NGCP/Panel/Form/SubscriberProfile/Profile.pm index 532d5c5247..543bab7522 100644 --- a/lib/NGCP/Panel/Form/SubscriberProfile/Profile.pm +++ b/lib/NGCP/Panel/Form/SubscriberProfile/Profile.pm @@ -76,7 +76,7 @@ sub field_list { expose_to_customer => 1, }, { - attribute => { -in => [qw/cfu cft cfna cfb/] }, + attribute => { -in => [qw/cfu cft cfna cfb cfs/] }, } ], }); diff --git a/lib/NGCP/Panel/Role/API/CFMappings.pm b/lib/NGCP/Panel/Role/API/CFMappings.pm index 9bc7c5f66f..a16ca26d41 100644 --- a/lib/NGCP/Panel/Role/API/CFMappings.pm +++ b/lib/NGCP/Panel/Role/API/CFMappings.pm @@ -24,7 +24,7 @@ sub hal_from_item { my ($self, $c, $item, $type) = @_; my $form; - my $resource = { subscriber_id => $item->id, cfu => [], cfb => [], cfna => [], cft => [], }; + my $resource = { subscriber_id => $item->id, cfu => [], cfb => [], cfna => [], cft => [], cfs => []}; my $b_subs_id = $item->id; my $p_subs_id = $item->provisioning_voip_subscriber->id; @@ -125,7 +125,7 @@ sub update_item { my $tsets_rs = $c->model('DB')->resultset('voip_cf_time_sets'); my $ssets_rs = $c->model('DB')->resultset('voip_cf_source_sets'); - for my $type ( qw/cfu cfb cft cfna/) { + for my $type ( qw/cfu cfb cft cfna cfs/) { if (ref $resource->{$type} ne "ARRAY") { $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field '$type'. Must be an array."); return; @@ -174,7 +174,7 @@ sub update_item { $autoattendant_count += NGCP::Panel::Utils::Subscriber::check_dset_autoattendant_status($map->destination_set); } $mappings_rs->delete; - for my $type ( qw/cfu cfb cft cfna/) { + for my $type ( qw/cfu cfb cft cfna cfs/) { $cf_preferences{$type}->delete; } for my $mapping ( @new_mappings ) { diff --git a/lib/NGCP/Panel/Role/API/CallForwards.pm b/lib/NGCP/Panel/Role/API/CallForwards.pm index f3a3e68261..b2f34ee617 100644 --- a/lib/NGCP/Panel/Role/API/CallForwards.pm +++ b/lib/NGCP/Panel/Role/API/CallForwards.pm @@ -46,7 +46,7 @@ sub hal_from_item { ], relation => 'ngcp:'.$self->resource_name, ); - @resource{qw/cfu cfb cft cfna/} = ({}) x 4; + @resource{qw/cfu cfb cft cfna cfs/} = ({}) x 5; for my $item_cf ($item->provisioning_voip_subscriber->voip_cf_mappings->all) { $resource{$item_cf->type} = $self->_contents_from_cfm($c, $item_cf, $item); } @@ -111,7 +111,7 @@ sub update_item { run => 1, ); - for my $type (qw/cfu cfb cft cfna/) { + for my $type (qw/cfu cfb cft cfna cfs/) { next unless "ARRAY" eq ref $resource->{$type}{destinations}; for my $d (@{ $resource->{$type}{destinations} }) { if (exists $d->{timeout} && ! is_int($d->{timeout})) { @@ -122,7 +122,7 @@ sub update_item { } } - for my $type (qw/cfu cfb cft cfna/) { + for my $type (qw/cfu cfb cft cfna cfs/) { my $mapping = $c->model('DB')->resultset('voip_cf_mappings')->search_rs({ subscriber_id => $prov_subscriber_id, type => $type, diff --git a/lib/NGCP/Panel/Role/API/SubscriberProfiles.pm b/lib/NGCP/Panel/Role/API/SubscriberProfiles.pm index a78de62f28..5e02263698 100644 --- a/lib/NGCP/Panel/Role/API/SubscriberProfiles.pm +++ b/lib/NGCP/Panel/Role/API/SubscriberProfiles.pm @@ -157,7 +157,7 @@ sub update_item { expose_to_customer => 1, }, { - attribute => { -in => [qw/cfu cft cfna cfb/] }, + attribute => { -in => [qw/cfu cft cfna cfb cfs/] }, }, ], }); @@ -175,7 +175,7 @@ sub update_item { if(keys %old_attributes) { my $cfs = $c->model('DB')->resultset('voip_preferences')->search({ id => { -in => [ keys %old_attributes ] }, - attribute => { -in => [qw/cfu cfb cft cfna/] }, + attribute => { -in => [qw/cfu cfb cft cfna cfs/] }, }); my @subs = $c->model('DB')->resultset('provisioning_voip_subscribers') ->search({ diff --git a/lib/NGCP/Panel/Role/API/Subscribers.pm b/lib/NGCP/Panel/Role/API/Subscribers.pm index 97471c347e..c012506a75 100644 --- a/lib/NGCP/Panel/Role/API/Subscribers.pm +++ b/lib/NGCP/Panel/Role/API/Subscribers.pm @@ -582,7 +582,7 @@ sub update_item { if(keys %old_profile_attributes) { my $cfs = $schema->resultset('voip_preferences')->search({ id => { -in => [ keys %old_profile_attributes ] }, - attribute => { -in => [qw/cfu cfb cft cfna/] }, + attribute => { -in => [qw/cfu cfb cft cfna cfs/] }, }); $prov_subscriber->voip_usr_preferences->search({ attribute_id => { -in => [ keys %old_profile_attributes ] }, diff --git a/lib/NGCP/Panel/Utils/InvoiceTemplate.pm b/lib/NGCP/Panel/Utils/InvoiceTemplate.pm index e4540f692a..1c3697264a 100644 --- a/lib/NGCP/Panel/Utils/InvoiceTemplate.pm +++ b/lib/NGCP/Panel/Utils/InvoiceTemplate.pm @@ -299,7 +299,7 @@ sub get_dummy_data { start_time => time, source_customer_cost => int(rand(100000)), duration => int(rand(7200)) + 10, - call_type => (qw/cfu cfb cft cfna/)[int(rand 4)], + call_type => (qw/cfu cfb cft cfna cfs/)[int(rand 4)], zone => "Zone $_", zone_detail => "Detail $_", }}(1 .. 50) diff --git a/lib/NGCP/Panel/Widget/Dashboard/SubscriberCFOverview.pm b/lib/NGCP/Panel/Widget/Dashboard/SubscriberCFOverview.pm index 9349a6b4ac..c1508ae4fb 100644 --- a/lib/NGCP/Panel/Widget/Dashboard/SubscriberCFOverview.pm +++ b/lib/NGCP/Panel/Widget/Dashboard/SubscriberCFOverview.pm @@ -21,7 +21,8 @@ sub _get_cf_type_descriptions { return { cfu => $c->loc("Call Forward Unconditional"), cfb => $c->loc("Call Forward Busy"), cft => $c->loc("Call Forward Timeout"), - cfna => $c->loc("Call Forward Unavailable") }; + cfna => $c->loc("Call Forward Unavailable"), + cfs => $c->loc("Call Forward SMS"), }; } sub cfs { @@ -31,7 +32,7 @@ sub cfs { my $cfs = {}; my $descriptions = $self->_get_cf_type_descriptions($c); - foreach my $type (qw/cfu cfna cft cfb/) { + foreach my $type (qw/cfu cfna cft cfb cfs/) { my $maps = $prov_subscriber->voip_cf_mappings ->search({ type => $type }); my @mappings = (); diff --git a/sandbox/callforwards.pl b/sandbox/callforwards.pl index 68cb26c22c..6f9bdb9f1a 100644 --- a/sandbox/callforwards.pl +++ b/sandbox/callforwards.pl @@ -43,7 +43,7 @@ for my $item ($item_rs->all) { # print Dumper({$item->get_inflated_columns}); my %resource = (); my $prov_subs = $item->provisioning_voip_subscriber; - for my $cf_type (qw/cfu cfb cft cfna/) { + for my $cf_type (qw/cfu cfb cft cfna cfs/) { my $mapping = $schema->resultset('voip_cf_mappings')->search({ subscriber_id => $prov_subs->id, type => $cf_type, @@ -73,7 +73,7 @@ $time = time(); for my $item ($item_rs->all) { my %resource = (); my $prov_subs = $item->provisioning_voip_subscriber; - @resource{qw/cfu cfb cft cfna/} = ({}) x 4; + @resource{qw/cfu cfb cft cfna cfs/} = ({}) x 5; for my $item_cf ($item->provisioning_voip_subscriber->voip_cf_mappings->all){ $resource{$item_cf->type} = _contents_from_cfm($item_cf, $item); } @@ -247,4 +247,4 @@ sub validate_fields { return 1; } -1; \ No newline at end of file +1; diff --git a/share/templates/subscriber/preferences.tt b/share/templates/subscriber/preferences.tt index bef9b4233b..5a4ffb97bf 100644 --- a/share/templates/subscriber/preferences.tt +++ b/share/templates/subscriber/preferences.tt @@ -77,7 +77,8 @@ [% FOR cf IN [ { type = "cfu", desc = c.loc("Call Forward Unconditional") }, { type = "cfb", desc = c.loc("Call Forward Busy") }, { type = "cft", desc = c.loc("Call Forward Timeout") }, - { type = "cfna", desc = c.loc("Call Forward Unavailable") } ] -%] + { type = "cfna", desc = c.loc("Call Forward Unavailable") }, + { type = "cfs", desc = c.loc("Call Forward SMS") } ] -%] [% IF c.user.roles == "subscriber" || c.user.roles == "subscriberadmin" -%] [% NEXT IF special_prefs.check && !special_prefs.callforward.${cf.type} -%] [% END -%] diff --git a/t/api-rest/api-callforwards.t b/t/api-rest/api-callforwards.t index d36959a03b..1cc1da159b 100644 --- a/t/api-rest/api-callforwards.t +++ b/t/api-rest/api-callforwards.t @@ -89,6 +89,7 @@ SKIP:{ ok(exists $cf1single->{cfb}, "cf should have key cfb"); ok(exists $cf1single->{cft}, "cf should have key cft"); ok(exists $cf1single->{cfna}, "cf should have key cfna"); + ok(exists $cf1single->{cfs}, "cf should have key cfs"); #write cf and check written values my($cf1_put,$cf1_get) = $test_machine->check_put2get({data_in => $test_machine->DATA_ITEM, uri => $cf1single_uri},undef, 1 ); diff --git a/t/api-rest/api-journals.t b/t/api-rest/api-journals.t index 442bc05353..06435c3b96 100644 --- a/t/api-rest/api-journals.t +++ b/t/api-rest/api-journals.t @@ -891,6 +891,8 @@ sub test_callforwards { times => $cftimeset->{times}}, cfu => { destinations => $cfdestinationset->{destinations}, times => $cftimeset->{times}}, + cfs => { destinations => $cfdestinationset->{destinations}, + times => $cftimeset->{times}}, })); $res = $ua->request($req); is($res->code, 200, _get_request_test_message("PUT test callforwards")); @@ -952,6 +954,8 @@ sub test_cfmapping { timeset => $cftimeset->{name}}], cfu => [{ destinationset => $cfdestinationset->{name}, timeset => $cftimeset->{name}}], + cfs => [{ destinationset => $cfdestinationset->{name}, + timeset => $cftimeset->{name}}], })); $res = $ua->request($req); is($res->code, 200, _get_request_test_message("PUT test cfmappings"));