TT#5954 Add announcement field to the destinations

Change-Id: I2ac25edfd03bb9f72f343be1cb398a09ef29fb12
changes/35/9935/17
Irina Peshinskaya 9 years ago
parent c379dc9493
commit 37f6d0ea6e

@ -197,8 +197,7 @@ sub POST :Allow {
if (! exists $resource->{destinations} ) {
$resource->{destinations} = [];
}
if (ref $resource->{destinations} ne "ARRAY") {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field 'destinations'. Must be an array.");
if(!$self->check_destinations($c, $resource)){
last;
}
try {

@ -70,7 +70,7 @@ sub GET :Allow {
{
last unless $self->valid_id($c, $id);
my $item = $self->item_by_id($c, $id);
last unless $self->resource_exists($c, subscriber => $item);
last unless $self->resource_exists($c, 'callforward' => $item);
my $hal = $self->hal_from_item($c, $item);

@ -931,7 +931,13 @@ sub preferences_callforward :Chained('base') :PathPart('preferences/callforward'
$params = { destination => $c->stash->{cf_tmp_params} };
$params->{destination}{destination} = $d;
$params->{ringtimeout} = $ringtimeout;
$params->{destination}->{announcement_id} = $destination ? $destination->announcement_id : '';
}
$c->stash->{custom_announcements_rs} = $c->model('DB')->resultset('voip_sound_handles')->search({
'group.name' => 'custom_announcements',
},{
join => 'group',
});
if($c->config->{features}->{cloudpbx}) {
my $pbx_pref = NGCP::Panel::Utils::Preferences::get_usr_preference_rs(
@ -1020,6 +1026,7 @@ sub preferences_callforward :Chained('base') :PathPart('preferences/callforward'
destination => $d,
timeout => $t,
priority => 1,
announcement_id => (('customhours' eq $dest->field('destination')->value) and $dest->field('announcement_id')->value) || undef,
});
unless(defined $map) {
@ -1322,6 +1329,11 @@ sub preferences_callforward_destinationset_create :Chained('base') :PathPart('pr
$c->stash->{pbx} = 1;
}
}
$c->stash->{custom_announcements_rs} = $c->model('DB')->resultset('voip_sound_handles')->search({
'group.name' => 'custom_announcements',
},{
join => 'group',
});
my $form = NGCP::Panel::Form::DestinationSet->new(ctx => $c);
@ -1375,6 +1387,7 @@ sub preferences_callforward_destinationset_create :Chained('base') :PathPart('pr
destination => $d,
timeout => $t,
priority => $dest->field('priority')->value,
announcement_id => (('customhours' eq $dest->field('destination')->value) and $dest->field('announcement_id')->value) || undef,
});
}
}
@ -1462,14 +1475,20 @@ sub preferences_callforward_destinationset_edit :Chained('preferences_callforwar
($d, $duri) = NGCP::Panel::Utils::Subscriber::destination_to_field($d);
push @destinations, {
destination => $d,
uri => {timeout => $t, destination => $duri},
priority => $dest->priority,
id => $dest->id,
destination => $d,
uri => {timeout => $t, destination => $duri},
priority => $dest->priority,
announcement_id => $dest->announcement_id,
id => $dest->id,
};
}
$params->{destination} = \@destinations;
}
$c->stash->{custom_announcements_rs} = $c->model('DB')->resultset('voip_sound_handles')->search({
'group.name' => 'custom_announcements',
},{
join => 'group',
});
$c->stash->{cf_tmp_params} = $params;
my $form = NGCP::Panel::Form::DestinationSet->new(ctx => $c);
@ -1544,6 +1563,7 @@ sub preferences_callforward_destinationset_edit :Chained('preferences_callforwar
destination => $d,
timeout => $t,
priority => $dest->field('priority')->value,
announcement_id => (('customhours' eq $dest->field('destination')->value) and $dest->field('announcement_id')->value) || undef,
});
}
$set->discard_changes; # reload (destinations may be cached)

@ -68,6 +68,12 @@ has_field 'destinations.priority' => (
default => 1,
);
has_field 'destinations.announcement_id' => (
type => '+NGCP::Panel::Field::PosInteger',
label => 'Announcement ID',
default => 1,
);
1;
# vim: set tabstop=4 expandtab:

@ -97,6 +97,10 @@ has_field 'cfu.destinations.timeout' => (
type => 'PosInteger',
);
has_field 'cfu.destinations.announcement_id' => (
type => 'PosInteger',
);
has_field 'cfu.times' => (
type => 'Repeatable',
do_wrapper => 1,
@ -127,6 +131,10 @@ has_field 'cfb.destinations.timeout' => (
type => 'PosInteger',
);
has_field 'cfb.destinations.announcement_id' => (
type => 'PosInteger',
);
has_field 'cfb.times' => (
type => 'Repeatable',
do_wrapper => 1,
@ -163,6 +171,10 @@ has_field 'cft.times' => (
do_label => 0,
);
has_field 'cft.destinations.announcement_id' => (
type => 'PosInteger',
);
has_field 'cft.sources' => (
type => 'Repeatable',
do_wrapper => 1,
@ -187,6 +199,10 @@ has_field 'cfna.destinations.timeout' => (
type => 'PosInteger',
);
has_field 'cfna.destinations.announcement_id' => (
type => 'PosInteger',
);
has_field 'cfna.times' => (
type => 'Repeatable',
do_wrapper => 1,
@ -217,6 +233,10 @@ has_field 'cfs.destinations.timeout' => (
type => 'PosInteger',
);
has_field 'cfs.destinations.announcement_id' => (
type => 'PosInteger',
);
has_field 'cfs.times' => (
type => 'Repeatable',
do_wrapper => 1,

@ -102,6 +102,16 @@ has_field 'destination.priority' => (
required => 1,
);
has_field 'destination.announcement_id' => (
type => 'Select',
#widget => 'RadioGroup',
label => 'Custom announcement',
options_method => \&build_announcements,
wrapper_class => [qw/hfh-rep-field ngcp-destination ngcp-destination-customhours/],
required => 0,
);
has_field 'destination.rm' => (
type => 'RmElement',
value => 'Remove',
@ -155,6 +165,17 @@ sub validate_destination{
}
return $result;
}
sub build_announcements {
my ($self) = @_;
my @options = ();
my $c = $self->form->ctx;
push @options, { label => 'Select announcement', value => '' };
foreach($c->stash->{custom_announcements_rs}->all){
push @options, { label => $_->name, value => $_->id };
}
return \@options;
}
1;
# vim: set tabstop=4 expandtab:

@ -66,6 +66,8 @@ has_field 'destination.uri' => (
type => 'Compound',
do_label => 0,
);
has_field 'destination.uri.destination' => (
type => '+NGCP::Panel::Field::URI',
label => 'URI/Number',
@ -75,6 +77,14 @@ has_field 'destination.uri.timeout' => (
label => 'for (seconds)',
default => 300,
);
has_field 'destination.announcement_id' => (
type => 'Select',
#widget => 'RadioGroup',
label => 'Custom announcement',
options_method => \&build_announcements,
wrapper_class => [qw/hfh-rep-field ngcp-destination ngcp-destination-customhours/],
required => 0,
);
has_field 'cf_actions' => (
type => 'Compound',
@ -116,6 +126,18 @@ sub validate_destination{
}
return $result;
}
sub build_announcements {
my ($self) = @_;
my @options = ();
my $c = $self->form->ctx;
push @options, { label => 'Select announcement', value => '' };
foreach($c->stash->{custom_announcements_rs}->all){
push @options, { label => $_->name, value => $_->id };
}
return \@options;
}
1;
# vim: set tabstop=4 expandtab:

@ -116,17 +116,9 @@ sub update_item {
if (! exists $resource->{destinations} ) {
$resource->{destinations} = [];
}
if (ref $resource->{destinations} ne "ARRAY") {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field 'destinations'. Must be an array.");
if(!$self->check_destinations($c, $resource)){
return;
}
for my $d (@{ $resource->{destinations} }) {
if (exists $d->{timeout} && ! is_int($d->{timeout})) {
$c->log->error("Invalid field 'timeout'.");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field 'timeout'.");
return;
}
}
my $b_subscriber = $schema->resultset('voip_subscribers')->find($resource->{subscriber_id});
unless ($b_subscriber) {
@ -136,7 +128,7 @@ sub update_item {
my $subscriber = $b_subscriber->provisioning_voip_subscriber;
unless($subscriber) {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid subscriber.");
last;
return;
}
try {
@ -185,5 +177,46 @@ sub update_item {
return $item;
}
sub check_destinations{
my($self,$c,$resource) = @_;
if (ref $resource->{destinations} ne "ARRAY") {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid field 'destinations'. Must be an array.");
return;
}
for my $d (@{ $resource->{destinations} }) {
if (exists $d->{timeout} && ! is_int($d->{timeout})) {
$c->log->error("Invalid timeout for the destination '".$d->{destination}."'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid timeout for the destination '".$d->{destination}."'");
return;
}
if (exists $d->{priority} && ! is_int($d->{priority})) {
$c->log->error("Invalid priority for the destination '".$d->{destination}."'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid priority for the destination '".$d->{destination}."'");
return;
}
if (defined $d->{announcement_id}) {
#todo: I think that user expects that put and get will be the same
if(('customhours' ne $d->{destination}) && ('sip:custom-hours@app.local' ne $d->{destination}) ){
$c->log->error("Invalid parameter 'announcement_id' for the destination '".$d->{destination}."'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid parameter 'announcement_id' for the destination '".$d->{destination}."'");
return;
}elsif(! is_int($d->{announcement_id})){
$c->log->error("Invalid announcement_id");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid announcement_id");
return;
}elsif(! $c->model('DB')->resultset('voip_sound_handles')->search_rs({
'me.id' => $d->{announcement_id},
'group.name' => 'custom_announcements',
},{
'join' => 'group',
})->first() ){
$c->log->error("Unknown announcement_id: ".$d->{announcement_id});
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Unknown announcement_id:".$d->{announcement_id});
return;
}
}
}
return 1;
}
1;
# vim: set tabstop=4 expandtab:

@ -119,6 +119,27 @@ sub update_item {
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid timeout in '$type'");
return;
}
if (defined $d->{announcement_id}) {
if('customhours' ne $d->{destination}){
$c->log->error("Invalid parameter 'announcement_id' for the destination '".$d->{destination}."' in '$type'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid parameter 'announcement_id' for the destination '".$d->{destination}."' in '$type'");
return;
}elsif(! is_int($d->{announcement_id})){
$c->log->error("Invalid announcement_id in '$type'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid announcement_id in '$type'");
return;
}elsif(! $c->model('DB')->resultset('voip_sound_handles')->search_rs({
'me.id' => $d->{announcement_id},
'group.name' => 'custom_announcements',
},{
'join' => 'group'
}
)->first() ){
$c->log->error("Unknown announcement_id in '$type'");
$self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Unknown announcement_id in '$type'");
return;
}
}
}
}

@ -573,4 +573,9 @@ div.login {
font-size: 12px;
}
/*callforwards announcement_id field for the different destinations*/
.ngcp-destination {
display: none;
}
/* vim: set tabstop=4 syntax=css expandtab: */

@ -14,7 +14,6 @@
{ level = 5, text = c.loc("ported (call forwarding only)") },
];
-%]
<script src="/js/jquery.dump.js"></script>
<div class="row">
<span>

@ -54,6 +54,44 @@
((c.user.roles == "subscriber" || c.user.roles == "subscriberadmin") && !special_prefs.check) ||
c.user.roles == "admin" || c.user.roles == "reseller" -%]
<script>
function destinationDynamicFields(selectedValue, inputNumber){
if($.isNumeric( inputNumber )){
inputNumber = inputNumber.toString();
}else if(!inputNumber){
inputNumber = '';
}
var name = inputNumber + '.announcement_id';
$(":input[name$='" + name + "']").closest('.ngcp-destination').css("display","none");
$(":input[name$='" + name + "']").closest('.ngcp-destination-' + selectedValue ).css("display","block");
}
function inputNumberFromName(name){
var inputNumber = name;
var inputNumbers = inputNumber.match(/\.(\d+)\./);
if(inputNumbers){
inputNumber = inputNumbers[1];
}else{
inputNumber = '';
}
return inputNumber;
}
$( document ).ready(function() {
$("input:radio[name$='.destination']:checked").each(function(index, item){
destinationDynamicFields( $(this).val(), inputNumberFromName($(this).attr('name')) );
});
$("input:radio[name$='.destination']").on('click',function(){
destinationDynamicFields($(this).val(), inputNumberFromName($(this).attr('name')) );
});
$('.controls').on('click','input:radio',function(){
if($(this).attr("checked")){
destinationDynamicFields($(this).val(), inputNumberFromName($(this).attr('name')) );
}
});
});
</script>
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" data-toggle="collapse" data-parent="#preference_groups" href="#collapse_cf">[% c.loc('Call Forwards') %]</a>

@ -38,6 +38,12 @@ $fake_data->set_data_from_script({
priority => "2",
timeout => "300"
},
{
destination => "customhours",
priority => "1",
timeout => "300",
announcement_id => sub { return shift->get_id('soundhandles_custom_announcements',@_); },
},
],
sources => [
{
@ -68,6 +74,10 @@ $test_machine->methods->{item}->{allowed} = {map {$_ => 1} qw(GET HEAD OPT
$test_machine->DATA_ITEM_STORE($fake_data->process('callforwards'));
$test_machine->form_data_item( );
my $announcement_id = $test_machine->DATA_ITEM->{cfb}->{destinations}->[2]->{announcement_id};
ok($announcement_id =~/^\d+$/,"announcement_id should be a positiv integer: $announcement_id");
SKIP:{
my ($res,$req,$content);
my $cf1 = $test_machine->get_item_hal();
@ -84,12 +94,17 @@ SKIP:{
(undef, $cf1single) = $test_machine->check_item_get($cf1single_uri,"fetch cf id $cf1_id");
#check cf structure
delete $cf1single->{_links};
is(ref $cf1single, "HASH", "cf should be hash");
ok(exists $cf1single->{cfu}, "cf should have key cfu");
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");
my @valid_types = (qw/cfu cfb cft cfna cfs/);
my %valid_types;
@valid_types{@valid_types} = ( 1 ) x @valid_types;
foreach my $type(@valid_types){
ok(exists $cf1single->{$type}, "cf should have key $type");
}
foreach my $test_type (keys %{$cf1single}){
ok( exists $valid_types{$test_type} , "check cf against unknown types: $test_type");
}
#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 );
@ -101,6 +116,10 @@ SKIP:{
is ($cf1_put->{content}->{cfb}{destinations}->[0]->{destination}, "customhours", "Check customhours destination");
is ($cf1_put->{content}->{cfb}{destinations}->[1]->{destination}, "officehours", "Check customhours destination");
is ($cf1_put->{content}->{cfb}{destinations}->[2]->{announcement_id}, $announcement_id, "Check announcement_id after put");
is ($cf1_get->{content}->{cfb}{destinations}->[2]->{announcement_id}, $announcement_id, "Check announcement_id after get");
#write invalid 'timeout'
($res,$content,$req) = $test_machine->request_put({
cfu => {
@ -137,6 +156,50 @@ SKIP:{
($res,$mod_cf1) = $test_machine->request_patch( [ { op => 'replace', path => '/cfu/destinations/0/timeout', value => 'invalid' } ] );
is($res->code, 422, "check patched invalid status");
#5954
my $data = {
destinations => [
{
destination => "officehours",
timeout => "15",
announcement_id => $announcement_id,
},
],
};
($res,$content,$req) = $test_machine->request_put({
data_in => {
cfu => $data,
},
uri => $cf1single_uri,
});
is ($content->{cfu}->{destinations}->[0]->{announcement_id}, undef, "Check announcement_id after put into other destination (officehours)");
#$test_machine->http_code_msg(422, "Check announcement_id for the officehours", $res, $content);#got 200 here
$data->{destinations}->[0]->{destination} = 'customhours';
($res,$content,$req) = $test_machine->request_put({ cfu => $data}, $cf1single_uri );
is($content->{cfu}->{destinations}->[0]->{announcement_id}, $announcement_id, "Check announcement_id after put into correct destination (customhours)");
foreach my $destination (qw/officehours customhours/){
$data->{destinations}->[0]->{destination} = $destination;
#$data->{destinations}->[0]->{destination} = 'customhours';
$data->{destinations}->[0]->{announcement_id} = 9999999;
($res,$content,$req) = $test_machine->request_put({ cfu => $data}, $cf1single_uri);
$test_machine->http_code_msg(422, "Check absent announcement_id", $res, $content);
$data->{destinations}->[0]->{announcement_id} = 'aaaaa';
($res,$content,$req) = $test_machine->request_put({ cfu => $data}, $cf1single_uri);
$test_machine->http_code_msg(422, "Check invalid announcement_id", $res, $content);
my $wrong_announcement_hal = $test_machine->get_item_hal('soundhandles', '/api/soundhandles/?group=pbx');
$data->{destinations}->[0]->{announcement_id} = $wrong_announcement_hal->{content}->{id};
($res,$content,$req) = $test_machine->request_put({ cfu => $data }, $cf1single_uri );
$test_machine->http_code_msg(422, "Check announcement_id from other group", $res, $content);
}
#return initial state:
$test_machine->request_put( $cf1single, $cf1single_uri );
}
done_testing;

@ -4,6 +4,7 @@ use warnings;
use Test::More;
use Test::Collection;
use Test::FakeData;
use Data::Dumper;
my $test_machine = Test::Collection->new(
name => 'cfdestinationsets',
@ -18,7 +19,14 @@ $fake_data->set_data_from_script({
data => {
destinations => [
{
destination => "sip:custom-hours\@app.local",
destination => "customhours",
priority => 1,
timeout => 300,
announcement_id => sub { return shift->get_id('soundhandles_custom_announcements',@_); },,
},
#without announcement
{
destination => "customhours",
priority => 1,
timeout => 300,
}
@ -94,8 +102,36 @@ SKIP:
($res, $content) = $test_machine->request_get('/api/cftimesets/99987');
is($res->code, 404, "check get nonexistent cftimesets item");
}
{
#5954
my($res,$content,$req);
$test_machine->runas('admin');
my $d = $test_machine->check_create_correct( 1, sub{
$_[0]->{name} .= '5954' ;
} )->[0];
ok(exists $d->{content}->{destinations}->[0]->{announcement_id},"Check announcement_id existance");
my (undef,$announcement_hal) = $test_machine->check_item_get('/api/soundhandles/'.$d->{content}->{destinations}->[0]->{announcement_id});
ok($announcement_hal->{group} eq 'custom_announcements', 'Check announcement group' );
$d->{content}->{destinations}->[0]->{announcement_id} = 'aaa';
($res,$content,$req) = $test_machine->request_put(@$d{qw/content location/});
$test_machine->http_code_msg(422, "Check invalid announcement_id", $res, $content);
$d->{content}->{destinations}->[0]->{announcement_id} = '999999';
($res,$content,$req) = $test_machine->request_put(@$d{qw/content location/});
$test_machine->http_code_msg(422, "Check absent announcement_id", $res, $content);
my $wrong_announcement_hal = $test_machine->get_item_hal('soundhandles', '/api/soundhandles/?group=pbx');
$d->{content}->{destinations}->[0]->{announcement_id} = $wrong_announcement_hal->{content}->{id};
($res,$content,$req) = $test_machine->request_put(@$d{qw/content location/});
$test_machine->http_code_msg(422, "Check announcement_id from other group", $res, $content);
}
$fake_data->clear_test_data_all();
$test_machine->clear_test_data_all();
undef $fake_data;
undef $test_machine;
done_testing;
# vim: set tabstop=4 expandtab:

@ -1022,15 +1022,16 @@ sub check_create_correct{
sub clear_test_data_all{
my($self,$uri,$strict) = @_;
my $name = $self->name // '';
my @uris = $uri ? (('ARRAY' eq ref $uri) ? @$uri : ($uri)) : keys %{ $self->DATA_CREATED->{ALL} };
foreach my $del_uri(@uris){
$del_uri = $self->normalize_uri($del_uri);
my($req,$res,$content) = $self->request_delete($del_uri);
if($strict){#for particular deletion test
$self->http_code_msg(204, "$self->name: check delete item $del_uri",$res,$content);
$self->http_code_msg(204, "$name: check delete item $del_uri",$res,$content);
}elsif($res->code == 404){
#todo: if fake data will provide tree of the cascade deletion - it can be checked here, I think
diag($self->name.": Item $del_uri is absent already.");
diag($name.": Item $del_uri is absent already.");
}
}
$self->clear_data_created();
@ -1150,7 +1151,7 @@ sub resource_clear_file{
sub get_id_from_hal{
my($self,$hal,$name) = @_;
$name //= $self->name;
my $id = $hal->{_embedded}->{'ngcp:'.$name}->{_links}{self}{href} =~ m!${name}/([0-9]*)$!;
(my ($id)) = $hal->{_embedded}->{'ngcp:'.$name}->{_links}{self}{href} =~ m!${name}/([0-9]*)$!;
return $id;
}
sub uri2location{

@ -241,6 +241,14 @@ sub build_data{
'no_delete_available' => 1,
'collection' => 'customers',
},
'soundhandles_custom_announcements' => {
'data' => {
group => 'custom_announcements',
},
'query' => ['group'],
'no_delete_available' => 1,
'collection' => 'soundhandles',
},
'billingprofiles' => {
'data' => {
name => 'api_test test profile'.time(),
@ -471,10 +479,14 @@ sub get_id{
my( $collection_name ) = @_;
$self->load_collection_data($collection_name);
my $res_id;
if( $self->collection_id_exists($collection_name) ){
$res_id = $self->get_existent_id($collection_name);
if('CODE' eq ref $self->data->{$collection_name}->{get_id}){
$res_id = $self->data->{$collection_name}->get_id();
}else{
$res_id = $self->create(@_);
if( $self->collection_id_exists($collection_name) ){
$res_id = $self->get_existent_id($collection_name);
}else{
$res_id = $self->create(@_);
}
}
return $res_id;
}

Loading…
Cancel
Save