From 03acd88a1a50f29ee53b17ad850bf71ba4be511b Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Thu, 25 Aug 2016 05:00:56 +0300 Subject: [PATCH] TT#2452 Implement device preferences web and api interface Change-Id: I72a548203d66e74b2907e44d98ee4f72ae15731a --- .../Controller/API/PbxDevicePreferenceDefs.pm | 115 +++++++++ .../Controller/API/PbxDevicePreferences.pm | 136 +++++++++++ .../API/PbxDevicePreferencesItem.pm | 220 ++++++++++++++++++ lib/NGCP/Panel/Controller/API/Root.pm | 1 + lib/NGCP/Panel/Controller/Device.pm | 92 +++++++- lib/NGCP/Panel/Role/API/Preferences.pm | 35 +++ lib/NGCP/Panel/Utils/Generic.pm | 7 +- lib/NGCP/Panel/Utils/Preferences.pm | 59 ++++- share/templates/device/list.tt | 1 + share/templates/device/preferences.tt | 22 ++ t/api-rest/api-preferences.t | 5 +- t/api-rest/api-root.t | 2 + 12 files changed, 686 insertions(+), 9 deletions(-) create mode 100644 lib/NGCP/Panel/Controller/API/PbxDevicePreferenceDefs.pm create mode 100644 lib/NGCP/Panel/Controller/API/PbxDevicePreferences.pm create mode 100644 lib/NGCP/Panel/Controller/API/PbxDevicePreferencesItem.pm create mode 100644 share/templates/device/preferences.tt diff --git a/lib/NGCP/Panel/Controller/API/PbxDevicePreferenceDefs.pm b/lib/NGCP/Panel/Controller/API/PbxDevicePreferenceDefs.pm new file mode 100644 index 0000000000..81cde82b62 --- /dev/null +++ b/lib/NGCP/Panel/Controller/API/PbxDevicePreferenceDefs.pm @@ -0,0 +1,115 @@ +package NGCP::Panel::Controller::API::PbxDevicePreferenceDefs; +use NGCP::Panel::Utils::Generic qw(:all); + +use Sipwise::Base; + +use boolean qw(true); +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Headers qw(); +use HTTP::Status qw(:constants); + +use NGCP::Panel::Utils::DateTime; +use NGCP::Panel::Utils::Preferences; +use Path::Tiny qw(path); +use Safe::Isa qw($_isa); +use JSON::Types qw(); +require Catalyst::ActionRole::ACL; +require Catalyst::ActionRole::CheckTrailingSlash; +require NGCP::Panel::Role::HTTPMethods; +require Catalyst::ActionRole::RequireSSL; + +sub allowed_methods{ + return [qw/GET OPTIONS HEAD/]; +} + +use parent qw/Catalyst::Controller NGCP::Panel::Role::API/; + +sub resource_name{ + return 'pbxdevicepreferencedefs'; +} +sub dispatch_path{ + return '/api/pbxdevicepreferencedefs/'; +} +sub relation{ + return 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicepreferencedefs'; +} + +__PACKAGE__->config( + action => { + map { $_ => { + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => [qw/admin reseller/], + Args => 0, + Does => [qw(ACL CheckTrailingSlash RequireSSL)], + Method => $_, + Path => __PACKAGE__->dispatch_path, + } } @{ __PACKAGE__->allowed_methods } + }, + action_roles => [qw(+NGCP::Panel::Role::HTTPMethods)], +); + +sub auto :Private { + my ($self, $c) = @_; + + $self->set_body($c); + $self->log_request($c); +} + +sub GET :Allow { + my ($self, $c) = @_; + { + my @links; + push @links, + Data::HAL::Link->new( + relation => 'curies', + href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}', + name => 'ngcp', + templated => true, + ), + Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'), + Data::HAL::Link->new(relation => 'self', href => sprintf('%s', $self->dispatch_path)); + + my $hal = Data::HAL->new( + links => [@links], + ); + my $resource = NGCP::Panel::Utils::Preferences::api_preferences_defs( c => $c, preferences_group => 'dev_pref' ); + $hal->resource($resource); + + my $response = HTTP::Response->new(HTTP_OK, undef, + HTTP::Headers->new($hal->http_headers(skip_links => 1)), $hal->as_json); + $c->response->headers($response->headers); + $c->response->body($response->content); + return; + } + return; +} + +sub HEAD :Allow { + my ($self, $c) = @_; + $c->forward(qw(GET)); + $c->response->body(q()); + return; +} + +sub OPTIONS :Allow { + my ($self, $c) = @_; + my $allowed_methods = $self->allowed_methods_filtered($c); + $c->response->headers(HTTP::Headers->new( + Allow => join(', ', @{ $allowed_methods }), + Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name, + )); + $c->response->content_type('application/json'); + $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n"); + return; +} + +sub end : Private { + my ($self, $c) = @_; + + $self->log_response($c); +} + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/API/PbxDevicePreferences.pm b/lib/NGCP/Panel/Controller/API/PbxDevicePreferences.pm new file mode 100644 index 0000000000..6b98b08f9b --- /dev/null +++ b/lib/NGCP/Panel/Controller/API/PbxDevicePreferences.pm @@ -0,0 +1,136 @@ +package NGCP::Panel::Controller::API::PbxDevicePreferences; +use NGCP::Panel::Utils::Generic qw(:all); + +use Sipwise::Base; + +use boolean qw(true); +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Headers qw(); +use HTTP::Status qw(:constants); + +use NGCP::Panel::Utils::DateTime; +use Path::Tiny qw(path); +use Safe::Isa qw($_isa); +require Catalyst::ActionRole::ACL; +require Catalyst::ActionRole::CheckTrailingSlash; +require NGCP::Panel::Role::HTTPMethods; +require Catalyst::ActionRole::RequireSSL; + +sub allowed_methods{ + return [qw/GET OPTIONS HEAD/]; +} + +sub api_description { + return 'Specifies certain properties (preferences) for a PBX Device Models. The full list of properties can be obtained via PbxDevicePreferenceDefs.'; +}; + +use parent qw/Catalyst::Controller NGCP::Panel::Role::API::Preferences/; + +sub resource_name{ + return 'pbxdevicepreferences'; +} +sub dispatch_path{ + return '/api/pbxdevicepreferences/'; +} +sub relation{ + return 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicepreferences'; +} + +__PACKAGE__->config( + action => { + map { $_ => { + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => [qw/admin reseller/], + Args => 0, + Does => [qw(ACL CheckTrailingSlash RequireSSL)], + Method => $_, + Path => __PACKAGE__->dispatch_path, + } } @{ __PACKAGE__->allowed_methods } + }, + action_roles => [qw(+NGCP::Panel::Role::HTTPMethods)], +); + +sub auto :Private { + my ($self, $c) = @_; + + $self->set_body($c); + $self->log_request($c); +} + +sub GET :Allow { + my ($self, $c) = @_; + my $container_type = 'pbxdevicemodels'; + my $page = $c->request->params->{page} // 1; + my $rows = $c->request->params->{rows} // 10; + { + my $container_items = $self->item_rs($c, $container_type); + (my $total_count, $container_items) = $self->paginate_order_collection($c, $container_items); + my (@embedded, @links); + for my $container_item ($container_items->all) { + push @embedded, $self->hal_from_item($c, $container_item, $container_type); + push @links, Data::HAL::Link->new( + relation => 'ngcp:'.$self->resource_name, + href => sprintf('%s%d', $self->dispatch_path, $container_item->id), + ); + } + push @links, + Data::HAL::Link->new( + relation => 'curies', + href => 'http://purl.org/sipwise/ngcp-api/#rel-{rel}', + name => 'ngcp', + templated => true, + ), + Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'), + Data::HAL::Link->new(relation => 'self', href => sprintf('%s?page=%s&rows=%s', $self->dispatch_path, $page, $rows)); + if(($total_count / $rows) > $page ) { + push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page + 1, $rows)); + } + if($page > 1) { + push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('%s?page=%d&rows=%d', $self->dispatch_path, $page - 1, $rows)); + } + + my $hal = Data::HAL->new( + embedded => [@embedded], + links => [@links], + ); + $hal->resource({ + total_count => $total_count, + }); + my $response = HTTP::Response->new(HTTP_OK, undef, + HTTP::Headers->new($hal->http_headers(skip_links => 1)), $hal->as_json); + $c->response->headers($response->headers); + $c->response->body($response->content); + return; + } + return; +} + +sub HEAD :Allow { + my ($self, $c) = @_; + $c->forward(qw(GET)); + $c->response->body(q()); + return; +} + +sub OPTIONS :Allow { + my ($self, $c) = @_; + my $allowed_methods = $self->allowed_methods_filtered($c); + $c->response->headers(HTTP::Headers->new( + Allow => join(', ', @{ $allowed_methods }), + Accept_Post => 'application/hal+json; profile=http://purl.org/sipwise/ngcp-api/#rel-'.$self->resource_name, + )); + $c->response->content_type('application/json'); + $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n"); + return; +} + +sub end : Private { + my ($self, $c) = @_; + + $self->log_response($c); +} + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/API/PbxDevicePreferencesItem.pm b/lib/NGCP/Panel/Controller/API/PbxDevicePreferencesItem.pm new file mode 100644 index 0000000000..8929e40bc7 --- /dev/null +++ b/lib/NGCP/Panel/Controller/API/PbxDevicePreferencesItem.pm @@ -0,0 +1,220 @@ +package NGCP::Panel::Controller::API::PbxDevicePreferencesItem; +use NGCP::Panel::Utils::Generic qw(:all); + +use Sipwise::Base; + +use boolean qw(true); +use Data::HAL qw(); +use Data::HAL::Link qw(); +use HTTP::Headers qw(); +use HTTP::Status qw(:constants); + +use NGCP::Panel::Utils::ValidateJSON qw(); +use NGCP::Panel::Utils::DateTime; +use Path::Tiny qw(path); +use Safe::Isa qw($_isa); +require Catalyst::ActionRole::ACL; +require NGCP::Panel::Role::HTTPMethods; +require Catalyst::ActionRole::RequireSSL; + +sub allowed_methods{ + return [qw/GET OPTIONS HEAD PATCH PUT/]; +} + +use parent qw/Catalyst::Controller NGCP::Panel::Role::API::Preferences/; + +sub resource_name{ + return 'pbxdevicepreferences'; +} +sub dispatch_path{ + return '/api/pbxdevicepreferences/'; +} +sub relation{ + return 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicepreferences'; +} + +sub journal_query_params { + my($self,$query_params) = @_; + return $self->get_journal_query_params($query_params); +} + +__PACKAGE__->config( + action => { + (map { $_ => { + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => [qw/admin reseller/], + Args => 1, + Does => [qw(ACL RequireSSL)], + Method => $_, + Path => __PACKAGE__->dispatch_path, + } } @{ __PACKAGE__->allowed_methods }), + @{ __PACKAGE__->get_journal_action_config(__PACKAGE__->resource_name,{ + ACLDetachTo => '/api/root/invalid_user', + AllowedRole => [qw/admin reseller/], + Does => [qw(ACL RequireSSL)], + }) } + }, + action_roles => [qw(+NGCP::Panel::Role::HTTPMethods)], +); + +sub auto :Private { + my ($self, $c) = @_; + + $self->set_body($c); + $self->log_request($c); +} + +sub GET :Allow { + my ($self, $c, $id) = @_; + { + last unless $self->valid_id($c, $id); + my $container_type = "pbxdevicemodels"; + my $preferences_type = "pbxdevicepreference"; + my $container_item = $self->item_by_id($c, $id, $container_type); + last unless $self->resource_exists($c, $preferences_type => $container_item); + + my $hal = $self->hal_from_item($c, $container_item, $container_type); + + my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( + (map { # XXX Data::HAL must be able to generate links with multiple relations + s|rel="(http://purl.org/sipwise/ngcp-api/#rel-resellers)"|rel="item $1"|; + s/rel=self/rel="item self"/; + $_ + } $hal->http_headers), + ), $hal->as_json); + $c->response->headers($response->headers); + $c->response->body($response->content); + return; + } + return; +} + +sub HEAD :Allow { + my ($self, $c, $id) = @_; + $c->forward(qw(GET)); + $c->response->body(q()); + return; +} + +sub OPTIONS :Allow { + my ($self, $c, $id) = @_; + my $allowed_methods = $self->allowed_methods_filtered($c); + $c->response->headers(HTTP::Headers->new( + Allow => join(', ', @{ $allowed_methods }), + Accept_Patch => 'application/json-patch+json', + )); + $c->response->content_type('application/json'); + $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n"); + return; +} + +sub PATCH :Allow { + my ($self, $c, $id) = @_; + my $guard = $c->model('DB')->txn_scope_guard; + { + my $preference = $self->require_preference($c); + last unless $preference; + + my $json = $self->get_valid_patch_data( + c => $c, + id => $id, + media_type => 'application/json-patch+json', + ops => [qw/add replace remove copy/], + ); + last unless $json; + + my $container_type = "pbxdevicemodels"; + my $preferences_type = "pbxdevicepreference"; + my $container_item = $self->item_by_id($c, $id, $container_type); + last unless $self->resource_exists($c, $preferences_type => $container_item); + my $old_resource = $self->get_resource($c, $container_item, $container_type); + my $resource = $self->apply_patch($c, $old_resource, $json); + last unless $resource; + + # last param is "no replace" to NOT delete existing prefs + # for proper PATCH behavior + $container_item = $self->update_item($c, $container_item, $old_resource, $resource, 0, $container_type); + last unless $container_item; + + my $hal = $self->hal_from_item($c, $container_item, $container_type); + last unless $self->add_update_journal_item_hal($c,$hal); + + $guard->commit; + + if ('minimal' eq $preference) { + $c->response->status(HTTP_NO_CONTENT); + $c->response->header(Preference_Applied => 'return=minimal'); + $c->response->body(q()); + } else { + #my $hal = $self->hal_from_item($c, $container_item, $container_type); + my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( + $hal->http_headers, + ), $hal->as_json); + $c->response->headers($response->headers); + $c->response->header(Preference_Applied => 'return=representation'); + $c->response->body($response->content); + } + } + return; +} + +sub PUT :Allow { + my ($self, $c, $id) = @_; + my $guard = $c->model('DB')->txn_scope_guard; + { + my $preference = $self->require_preference($c); + last unless $preference; + + my $container_type = "pbxdevicemodels"; + my $preferences_type = "pbxdevicepreference"; + my $container_item = $self->item_by_id($c, $id, $container_type); + # TODO: systemcontact? + last unless $self->resource_exists($c, $preferences_type => $container_item); + my $resource = $self->get_valid_put_data( + c => $c, + id => $id, + media_type => 'application/json', + ); + last unless $resource; + my $old_resource = $self->get_resource($c, $container_item, $container_type); + + # last param is "replace" to delete all existing prefs + # for proper PUT behavior + $container_item = $self->update_item($c, $container_item, $old_resource, $resource, 1, $container_type); + last unless $container_item; + + my $hal = $self->hal_from_item($c, $container_item, $container_type); + last unless $self->add_update_journal_item_hal($c,$hal); + + $guard->commit; + + if ('minimal' eq $preference) { + $c->response->status(HTTP_NO_CONTENT); + $c->response->header(Preference_Applied => 'return=minimal'); + $c->response->body(q()); + } else { + #my $hal = $self->hal_from_item($c, $container_item, $container_type); + my $response = HTTP::Response->new(HTTP_OK, undef, HTTP::Headers->new( + $hal->http_headers, + ), $hal->as_json); + $c->response->headers($response->headers); + $c->response->header(Preference_Applied => 'return=representation'); + $c->response->body($response->content); + } + } + return; +} + +sub get_journal_methods{ + return [qw/handle_item_base_journal handle_journals_get handle_journalsitem_get handle_journals_options handle_journalsitem_options handle_journals_head handle_journalsitem_head/]; +} + +sub end : Private { + my ($self, $c) = @_; + + $self->log_response($c); +} + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Controller/API/Root.pm b/lib/NGCP/Panel/Controller/API/Root.pm index 201b264193..acc30d6f9c 100644 --- a/lib/NGCP/Panel/Controller/API/Root.pm +++ b/lib/NGCP/Panel/Controller/API/Root.pm @@ -57,6 +57,7 @@ sub GET : Allow { "CustomerPreferenceDefs" => 1, "ProfilePreferenceDefs" => 1, "PeeringServerPreferenceDefs" => 1, + "PbxDevicePreferenceDefs" => 1, }; my @colls = $self->get_collections; diff --git a/lib/NGCP/Panel/Controller/Device.pm b/lib/NGCP/Panel/Controller/Device.pm index 79489b0389..b0b254efa5 100644 --- a/lib/NGCP/Panel/Controller/Device.pm +++ b/lib/NGCP/Panel/Controller/Device.pm @@ -1036,7 +1036,6 @@ sub get_annotated_info :Privat { foreach(qw/front_image mac_image/){ delete $device_info->{$_}; } - use Data::Dumper; my $gather_ranges_info = sub { my $rs = shift; return [ map { @@ -1335,9 +1334,10 @@ sub dev_field_config :Chained('/') :PathPart('device/autoprov/config') :Args() { cc => '', ac => '', ); - my $pref_rs = NGCP::Panel::Utils::Preferences::get_usr_preferences_rs( + my $pref_rs = NGCP::Panel::Utils::Preferences::get_preferences_rs( c => $c, - prov_subscriber => $sub, + id => $sub->id, + type => 'usr', #attribute => [keys %sub_preferences_vars], ); my $preferences = get_inflated_columns_all($pref_rs, 'hash' => 'attribute', 'column' => 'value' ); @@ -1876,7 +1876,7 @@ sub dev_field_firmware_next :Chained('dev_field_firmware_version_base') :PathPar $q = $c->req->params->{q}; } if(defined $q) { - $q=~s/\.rom$//i; #yealink v > 80 needs filename at the end of the link. + $q=~s/\.rom$//i; #yealink v > 80 needs filename at the end of the link. $rs = $rs->search({ version => { 'like' => $q . '%' }, }); @@ -1908,7 +1908,7 @@ sub dev_field_firmware_latest :Chained('dev_field_firmware_version_base') :PathP $q = $c->req->params->{q}; } if(defined $q) { - $q=~s/\.rom$//i; #yealink v > 80 needs filename at the end of the link. + $q=~s/\.rom$//i; #yealink v > 80 needs filename at the end of the link. $rs = $rs->search({ version => { 'like' => $q . '%' }, }); @@ -1928,6 +1928,88 @@ sub dev_field_firmware_latest :Chained('dev_field_firmware_version_base') :PathP } +sub devices_preferences_list :Chained('devmod_base') :PathPart('preferences') :CaptureArgs(0) { + my ($self, $c) = @_; + + my $dev_pref_rs = $c->model('DB') + ->resultset('voip_preferences') + ->search({ + 'device.id' => $c->stash->{devmod}->id, + },{ + prefetch => {'voip_dev_preferences' => 'device'}, + }); + my %pref_values; + foreach my $value($dev_pref_rs->all) { + $pref_values{$value->attribute} = + [ map {$_->value} $value->voip_dev_preferences->all ]; + } + my $pref_values = \%pref_values; + + #my $dev_pref_rs = NGCP::Panel::Utils::Preferences::get_preferences_rs( + # c => $c, + # type => 'dev', + # id => $c->stash->{devmod}->id, + #); + #my $pref_values = get_inflated_columns_all($dev_pref_rs,'hash' => 'attribute', 'column' => 'value', 'force_array' => 1); + #use Data::Dumper; + #$c->log->debug(Dumper(["pref_values",$pref_values])); + + NGCP::Panel::Utils::Preferences::load_preference_list( + c => $c, + pref_values => $pref_values, + dev_pref => 1, + ); + + $c->stash(template => 'device/preferences.tt'); + return; +} + +sub devices_preferences_root :Chained('devices_preferences_list') :PathPart('') :Args(0) { + return; +} + +sub devices_preferences_base :Chained('devices_preferences_list') :PathPart('') :CaptureArgs(1) { + my ($self, $c, $pref_id) = @_; + + $c->stash->{preference_meta} = $c->model('DB') + ->resultset('voip_preferences') + ->search({ + -or => ['voip_preferences_enums.dev_pref' => 1, + 'voip_preferences_enums.dev_pref' => undef], + },{ + prefetch => 'voip_preferences_enums', + }) + ->find({id => $pref_id}); + + $c->stash->{preference} = $c->model('DB') + ->resultset('voip_dev_preferences') + ->search({ + 'attribute_id' => $pref_id, + 'device_id' => $c->stash->{devmod}->id, + }); + return; +} + +sub devices_preferences_edit :Chained('devices_preferences_base') :PathPart('edit') :Args(0) { + my ($self, $c) = @_; + + $c->stash(edit_preference => 1); + + my @enums = $c->stash->{preference_meta} + ->voip_preferences_enums + ->all; + + my $pref_rs = $c->stash->{devmod}->voip_dev_preferences; + NGCP::Panel::Utils::Preferences::create_preference_form( + c => $c, + pref_rs => $pref_rs, + enums => \@enums, + base_uri => $c->uri_for_action('/device/devices_preferences_root', [@{ $c->req->captures }[0]] ), + edit_uri => $c->uri_for_action('/device/devices_preferences_edit', $c->req->captures ), + ); + return; +} + __PACKAGE__->meta->make_immutable; 1; diff --git a/lib/NGCP/Panel/Role/API/Preferences.pm b/lib/NGCP/Panel/Role/API/Preferences.pm index 6454b9f8ea..f568702f01 100644 --- a/lib/NGCP/Panel/Role/API/Preferences.pm +++ b/lib/NGCP/Panel/Role/API/Preferences.pm @@ -78,6 +78,8 @@ sub get_resource { $prefs = $item->voip_contract_preferences->search( { location_id => $c->request->param('location_id') || undef }, undef); + } elsif($type eq "pbxdevicemodels") { + $prefs = $item->voip_dev_preferences; } $prefs = $prefs->search({ }, { @@ -231,6 +233,9 @@ sub get_resource { } elsif($type eq "peerings") { $resource->{peering_id} = int($item->id); $resource->{id} = int($item->id); + } elsif($type eq "pbxdevicemodels") { + $resource->{device_id} = int($item->id); + $resource->{id} = int($item->id); } elsif($type eq "contracts") { $resource->{customer_id} = int($item->id); $resource->{id} = int($item->id); @@ -289,6 +294,20 @@ sub _item_rs { } else { return; } + } elsif($type eq "pbxdevicemodels") { + if($c->user->roles eq "admin") { + $item_rs = $c->model('DB')->resultset('autoprov_devices'); + #don't select images + #$item_rs = $c->model('DB')->resultset('autoprov_devices')->search_rs( + # undef, + # { + # 'columns' + # => [qw/id reseller_id type vendor model front_image_type mac_image_type num_lines bootstrap_method bootstrap_uri extensions_num/] + # } + #); + } else { + $item_rs = $c->model('DB')->resultset('autoprov_devices')->search({'reseller_id' => $c->user->reseller_id}); + } } elsif($type eq "contracts") { if($c->user->roles eq "admin") { $item_rs = $c->model('DB')->resultset('contracts')->search({ @@ -345,6 +364,12 @@ sub get_preference_rs { attribute => $attr, peer_host => $elem, ); + } elsif($type eq "pbxdevicemodels") { + $rs = NGCP::Panel::Utils::Preferences::get_dev_preference_rs( + c => $c, + attribute => $attr, + device => $elem, + ); } elsif($type eq "contracts") { $rs = NGCP::Panel::Utils::Preferences::get_contract_preference_rs( c => $c, @@ -419,6 +444,16 @@ sub update_item { undef); $pref_type = 'contract_pref'; $reseller_id = $item->contact->reseller_id; + } elsif($type eq "pbxdevicemodels") { + delete $resource->{device_id}; + delete $old_resource->{device_id}; + delete $resource->{pbxdevicepreferences_id}; + delete $old_resource->{pbxdevicepreferences_id}; + $accessor = $item->id; + $elem = $item; + $full_rs = $elem->voip_dev_preferences->search_rs(); + $pref_type = 'dev_pref'; + $reseller_id = $item->reseller_id; } else { return; } diff --git a/lib/NGCP/Panel/Utils/Generic.pm b/lib/NGCP/Panel/Utils/Generic.pm index df01fc680f..b177dae2cd 100644 --- a/lib/NGCP/Panel/Utils/Generic.pm +++ b/lib/NGCP/Panel/Utils/Generic.pm @@ -69,13 +69,18 @@ sub compare { sub get_inflated_columns_all{ my ($rs,%params) = @_; + #params = { + # hash => result will be hash, with key, taken from the column with name, stored in this param, + # column => if hash param exists, value of the hash will be taken from the column with, stored in the param "column" + # force_array => hash values always will be an array ref + #} my ($res); $rs->result_class('DBIx::Class::ResultClass::HashRefInflator'); if(my $hashkey_column = $params{hash}){ my %lres; my $register_value = sub { my($hash,$key,$value) = @_; - if(exists $hash->{$key}){ + if(exists $hash->{$key} || $params{force_array}){ if('ARRAY' eq ref $hash->{$key}){ push @{$hash->{$key}}, $value; }else{ diff --git a/lib/NGCP/Panel/Utils/Preferences.pm b/lib/NGCP/Panel/Utils/Preferences.pm index 0f7f680b7b..bda0f30887 100644 --- a/lib/NGCP/Panel/Utils/Preferences.pm +++ b/lib/NGCP/Panel/Utils/Preferences.pm @@ -36,6 +36,7 @@ sub load_preference_list { my $pref_values = $params{pref_values}; my $peer_pref = $params{peer_pref}; my $dom_pref = $params{dom_pref}; + my $dev_pref = $params{dev_pref}; my $prof_pref = $params{prof_pref}; my $usr_pref = $params{usr_pref}; my $contract_pref = $params{contract_pref}; @@ -60,6 +61,9 @@ sub load_preference_list { $dom_pref ? ('voip_preferences.dom_pref' => 1, -or => ['voip_preferences_enums.dom_pref' => 1, 'voip_preferences_enums.dom_pref' => undef]) : (), + $dev_pref ? ('voip_preferences.dev_pref' => 1, + -or => ['voip_preferences_enums.dev_pref' => 1, + 'voip_preferences_enums.dev_pref' => undef]) : (), $prof_pref ? ('voip_preferences.prof_pref' => 1, -or => ['voip_preferences_enums.prof_pref' => 1, 'voip_preferences_enums.prof_pref' => undef]) : (), @@ -376,6 +380,14 @@ sub create_preference_form { peer_host_id => $c->stash->{server}{id}, peer_host_name => $c->stash->{server}{name}, ); + } elsif ($c->stash->{devmod}) { + %log_data = ( %log_data, + type => 'dev', + device_id => $c->stash->{devmod}->{id}, + device_vendor => $c->stash->{devmod}->{vendor}, + device_model => $c->stash->{devmod}->{model}, + reseller_id => $c->stash->{devmod}->{reseller_id}, + ); } if($posted && $form->validated) { @@ -442,7 +454,7 @@ sub create_preference_form { $c->response->redirect($base_uri); return 1; } - } elsif ($attribute eq "man_allowed_ips") { + } elsif ($attribute eq "man_allowed_ips") { unless(validate_ipnet($form->field($attribute))) { goto OUT; } @@ -847,6 +859,37 @@ sub get_usr_preferences_rs { return $pref_rs; } +sub get_preferences_rs { + my %params = @_; + + my $c = $params{c}; + my $preferences_type = $params{type}; + my $attribute = $params{attribute}; + my $item_id = $params{id}; + my $schema = $params{schema} // $c->model('DB'); + + my %config = ( + 'usr' => [qw/voip_usr_preferences usr_pref subscriber_id/], + 'dom' => [qw/voip_dom_preferences dom_pref domain_id/], + 'prof' => [qw/voip_prof_preferences prof_pref profile_id/], + 'peer' => [qw/voip_peer_preferences peer_pref peer_host_id/], + 'dev' => [qw/voip_dev_preferences dev_pref device_id/], + 'contract' => [qw/voip_contract_preferences contract_pref contract_id/], + 'contract_location' => [qw/voip_contract_preferences contract_location_pref location_id/], + ); + my $pref_rs = $schema->resultset($config{$preferences_type}->[0])->search({ + 'attribute.'.$config{$preferences_type}->[1] => 1, + $attribute ? ( 'attribute.attribute' => (('ARRAY' eq ref $attribute) ? { '-in' => $attribute } : $attribute ) ) : () , + $item_id ? ('me.'.$config{$preferences_type}->[2] => $item_id) : (), + },{ + '+select' => ['attribute.attribute'], + '+as' => ['attribute'], + 'join' => 'attribute', + }); + + return $pref_rs; +} + sub get_usr_preference_rs { my %params = @_; @@ -921,7 +964,21 @@ sub get_peer_preference_rs { peer_host_id => $host->id, }); } +sub get_dev_preference_rs { + my %params = @_; + + my $c = $params{c}; + my $attribute = $params{attribute}; + my $device = $params{device}; + my $preference = $c->model('DB')->resultset('voip_preferences')->find({ + attribute => $attribute, 'dev_pref' => 1, + }); + return unless($preference); + return $preference->voip_dev_preferences->search_rs({ + device_id => $device->id, + }); +} sub get_contract_preference_rs { my %params = @_; diff --git a/share/templates/device/list.tt b/share/templates/device/list.tt index cc8e78baa0..610a323b2b 100644 --- a/share/templates/device/list.tt +++ b/share/templates/device/list.tt @@ -122,6 +122,7 @@ $( document ).ready(function() { helper.dt_buttons = helper.dt_buttons.merge([ { name = c.loc('Delete'), uri = "/device/model/'+full.id+'/delete", class = 'btn-small btn-secondary', icon = 'icon-trash' }, { name = c.loc('Edit'), uri = "/device/model/'+full.id+'/edit", class = 'btn-small btn-primary', icon = 'icon-edit' }, + { name = c.loc('Preferences'), uri = "/device/model/'+full.id+'/preferences", class = 'btn-small btn-primary', icon = 'icon-list' }, ]); END; diff --git a/share/templates/device/preferences.tt b/share/templates/device/preferences.tt new file mode 100644 index 0000000000..538a06d2a4 --- /dev/null +++ b/share/templates/device/preferences.tt @@ -0,0 +1,22 @@ +[% site_config.title = c.loc('Device model "[_1]" for reseller #[_2] - Preferences', devmod.vendor _ " " _ devmod.model, devmod.reseller_id) -%] + +[% + helper.messages = messages; + + helper.edit_preference = edit_preference; + helper.preference = preference; + helper.preference_meta = preference_meta; + helper.preference_values = preference_values; + helper.pref_groups = pref_groups; + helper.form = form; + helper.base_uri = c.uri_for_action("/device/devices_preferences_root", [c.req.captures.0]); + + helper.top_buttons = [ + { name = c.loc('Back'), uri = c.uri_for(""), icon = 'icon-arrow-left' }, + ]; + + PROCESS 'helpers/pref_table.tt'; +%] + + +[% # vim: set tabstop=4 syntax=html expandtab: -%] diff --git a/t/api-rest/api-preferences.t b/t/api-rest/api-preferences.t index 00c7360aee..365abbf179 100644 --- a/t/api-rest/api-preferences.t +++ b/t/api-rest/api-preferences.t @@ -23,6 +23,7 @@ $fake_data->set_data_from_script({ subscriber_id => sub { return shift->get_id('subscribers',@_); }, domain_id => sub { return shift->get_id('domains',@_); }, profile_id => sub { return shift->get_id('subscriberprofiles',@_); }, + pbxdevice_id => sub { return shift->get_id('pbxdevicemodels',@_); }, rewriteruleset_id => sub { return shift->get_id('rewriterulesets',@_); }, soundset_id => sub { return shift->get_id('soundsets',@_); }, @@ -35,8 +36,8 @@ $fake_data->set_data_from_script({ $test_machine->DATA_ITEM_STORE($fake_data->process('preferences')); $test_machine->form_data_item( ); -my @apis = qw/subscriber domain peeringserver customer profile/; -#my @apis = qw/peeringserver/; +my @apis = qw/subscriber domain peeringserver customer profile pbxdevice/; +#my @apis = qw/pbxdevice/; foreach my $api (@apis){ my $preferences_old; diff --git a/t/api-rest/api-root.t b/t/api-rest/api-root.t index 4d7f3062a4..758c150f8e 100644 --- a/t/api-rest/api-root.t +++ b/t/api-rest/api-root.t @@ -95,6 +95,8 @@ $ua->credentials($netloc, "api_admin_http", $user, $pass); pbxdevicefirmwares => 1, pbxdevicemodelimages => 1, pbxdevicemodels => 1, + pbxdevicepreferencedefs => 1, + pbxdevicepreferences => 1, pbxdeviceprofiles => 1, pbxdevices => 1, peeringgroups => 1,