diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceConfigFiles.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigFiles.pm
new file mode 100644
index 0000000000..bc76d7f88e
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigFiles.pm
@@ -0,0 +1,80 @@
+package NGCP::Panel::Controller::API::PbxDeviceConfigFiles;
+use Sipwise::Base;
+use namespace::sweep;
+use boolean qw(true);
+use Data::HAL qw();
+use Data::HAL::Link qw();
+use HTTP::Headers qw();
+use HTTP::Status qw(:constants);
+use MooseX::ClassAttribute qw(class_has);
+use NGCP::Panel::Utils::DateTime;
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
+BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+require Catalyst::ActionRole::ACL;
+require Catalyst::ActionRole::CheckTrailingSlash;
+require Catalyst::ActionRole::HTTPMethods;
+require Catalyst::ActionRole::RequireSSL;
+
+class_has 'api_description' => (
+ is => 'ro',
+ isa => 'Str',
+ default =>
+ 'Defines the actual PbxDeviceConfigs Files.',
+);
+
+class_has 'query_params' => (
+ is => 'ro',
+ isa => 'ArrayRef',
+ default => sub {[
+ ]},
+);
+
+with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
+
+class_has('resource_name', is => 'ro', default => 'pbxdeviceconfigfiles');
+class_has('dispatch_path', is => 'ro', default => '/api/pbxdeviceconfigfiles/');
+class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdeviceconfigfiles');
+
+__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(HTTPMethods)],
+);
+
+sub auto :Private {
+ my ($self, $c) = @_;
+
+ $self->set_body($c);
+ #$self->log_request($c);
+ return 1;
+}
+
+sub OPTIONS :Allow {
+ my ($self, $c) = @_;
+ my $allowed_methods = $self->allowed_methods;
+ $c->response->headers(HTTP::Headers->new(
+ Allow => $allowed_methods->join(', '),
+ 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);
+ return;
+}
+
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceConfigFilesItem.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigFilesItem.pm
new file mode 100644
index 0000000000..d77b7e8b5f
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigFilesItem.pm
@@ -0,0 +1,85 @@
+package NGCP::Panel::Controller::API::PbxDeviceConfigFilesItem;
+use Sipwise::Base;
+use namespace::sweep;
+use HTTP::Headers qw();
+use HTTP::Status qw(:constants);
+use MooseX::ClassAttribute qw(class_has);
+use NGCP::Panel::Utils::DateTime;
+use NGCP::Panel::Utils::ValidateJSON qw();
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
+BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+require Catalyst::ActionRole::ACL;
+require Catalyst::ActionRole::HTTPMethods;
+require Catalyst::ActionRole::RequireSSL;
+
+with 'NGCP::Panel::Role::API::PbxDeviceConfigs';
+
+class_has('resource_name', is => 'ro', default => 'pbxdeviceconfigfiles');
+class_has('dispatch_path', is => 'ro', default => '/api/pbxdeviceconfigfiles/');
+class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdeviceconfigfiles');
+
+__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 }
+ },
+ action_roles => [qw(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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicefirmwarebinary => $item);
+ my $resource = $self->resource_from_item($c, $item);
+
+ $resource->{data} = $item->data;
+ $c->response->header ('Content-Disposition' => 'attachment; filename="pbxdeviceconfig_' . $item->device_id . '_' . $item->id . '"');
+ $c->response->content_type($item->content_type);
+ $c->response->body($resource->{data});
+ 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;
+ $c->response->headers(HTTP::Headers->new(
+ Allow => $allowed_methods->join(', '),
+ Accept_Patch => 'application/json-patch+json',
+ ));
+ $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);
+}
+
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceConfigs.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigs.pm
index 027b991732..e0b0097d90 100644
--- a/lib/NGCP/Panel/Controller/API/PbxDeviceConfigs.pm
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigs.pm
@@ -8,6 +8,8 @@ use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
use NGCP::Panel::Utils::DateTime;
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
require Catalyst::ActionRole::ACL;
require Catalyst::ActionRole::CheckTrailingSlash;
@@ -18,7 +20,7 @@ class_has 'api_description' => (
is => 'ro',
isa => 'Str',
default =>
- 'Specifies a config to be set in PbxDeviceProfiles.',
+ 'Defines configs for a PbxDeviceModel. To create or update a config, do a POST or PUT with the proper Content-Type (e.g. text/xml) and pass the properties via query parameters, e.g. /api/pbxdeviceconfigs/?device_id=1&version=1.0',
);
class_has 'query_params' => (
@@ -26,31 +28,19 @@ class_has 'query_params' => (
isa => 'ArrayRef',
default => sub {[
{
- param => 'content_type',
- description => 'Filter for configs matching a content_type pattern',
+ param => 'device_id',
+ description => 'Filter for configs of a specific device model',
query => {
first => sub {
my $q = shift;
- { content_type => { like => $q } };
+ return { 'device_id' => $q };
},
- second => sub {},
- },
- },
- {
- param => 'version',
- description => 'Filter for configs matching a version name pattern',
- query => {
- first => sub {
- my $q = shift;
- { version => { like => $q } };
- },
- second => sub {},
+ second => sub { },
},
},
]},
);
-
with 'NGCP::Panel::Role::API::PbxDeviceConfigs';
class_has('resource_name', is => 'ro', default => 'pbxdeviceconfigs');
@@ -66,7 +56,7 @@ __PACKAGE__->config(
Does => [qw(ACL CheckTrailingSlash RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
- } } @{ __PACKAGE__->allowed_methods },
+ } } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
@@ -76,7 +66,6 @@ sub auto :Private {
$self->set_body($c);
$self->log_request($c);
- return 1;
}
sub GET :Allow {
@@ -84,15 +73,15 @@ sub GET :Allow {
my $page = $c->request->params->{page} // 1;
my $rows = $c->request->params->{rows} // 10;
{
- my $field_devs = $self->item_rs($c);
-
- (my $total_count, $field_devs) = $self->paginate_order_collection($c, $field_devs);
+ my $items = $self->item_rs($c);
+ (my $total_count, $items) = $self->paginate_order_collection($c, $items);
my (@embedded, @links);
- for my $dev ($field_devs->all) {
- push @embedded, $self->hal_from_item($c, $dev);
+ my $form = $self->get_form($c);
+ for my $item ($items->all) {
+ push @embedded, $self->hal_from_item($c, $item, $form);
push @links, Data::HAL::Link->new(
relation => 'ngcp:'.$self->resource_name,
- href => sprintf('%s%d', $self->dispatch_path, $dev->id),
+ href => sprintf('/%s%d', $c->request->path, $item->id),
);
}
push @links,
@@ -103,12 +92,12 @@ sub GET :Allow {
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));
+ Data::HAL::Link->new(relation => 'self', href => sprintf('/%s?page=%s&rows=%s', $c->request->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));
+ push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('/%s?page=%d&rows=%d', $c->request->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));
+ push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page - 1, $rows));
}
my $hal = Data::HAL->new(
@@ -146,11 +135,67 @@ sub OPTIONS :Allow {
return;
}
+sub POST :Allow {
+ my ($self, $c) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $data = $self->get_valid_raw_post_data(
+ c => $c,
+ media_type => [qw#text/plain text/xml#],
+ );
+ last unless $data;
+ my $resource = $c->req->query_params;
+
+ my $form = $self->get_form($c);
+ last unless $self->validate_form(
+ c => $c,
+ resource => $resource,
+ form => $form,
+ exceptions => [ "device_id" ],
+ );
+
+ my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
+ id => $resource->{device_id}
+ });
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $model_rs = $model_rs->search({
+ reseller_id => $c->user->reseller_id,
+ });
+ }
+ my $model = $model_rs->first;
+ unless($model) {
+ $c->log->error("invalid device_id '$$resource{device_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
+ last;
+ }
+
+ $resource->{data} = $data;
+ $resource->{content_type} = $c->request->header('Content-Type');
+
+ my $item;
+ try {
+ $item = $model->autoprov_configs->create($resource);
+ } catch($e) {
+ $c->log->error("failed to create pbxdeviceconfig: $e"); # TODO: user, message, trace, ...
+ $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create pbxdeviceconfig.");
+ last;
+ }
+
+ $guard->commit;
+
+ $c->response->status(HTTP_CREATED);
+ $c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
+ $c->response->body(q());
+ }
+ return;
+}
+
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
- return 1;
}
# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceConfigsItem.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigsItem.pm
index aac22fec25..a7a1d8cdbf 100644
--- a/lib/NGCP/Panel/Controller/API/PbxDeviceConfigsItem.pm
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceConfigsItem.pm
@@ -1,14 +1,11 @@
package NGCP::Panel::Controller::API::PbxDeviceConfigsItem;
use Sipwise::Base;
use namespace::sweep;
-use boolean qw(true);
-use Data::HAL qw();
-use Data::HAL::Link qw();
use HTTP::Headers qw();
use HTTP::Status qw(:constants);
use MooseX::ClassAttribute qw(class_has);
-use NGCP::Panel::Utils::ValidateJSON qw();
use NGCP::Panel::Utils::DateTime;
+use NGCP::Panel::Utils::ValidateJSON qw();
use Path::Tiny qw(path);
use Safe::Isa qw($_isa);
BEGIN { extends 'Catalyst::Controller::ActionRole'; }
@@ -31,7 +28,7 @@ __PACKAGE__->config(
Does => [qw(ACL RequireSSL)],
Method => $_,
Path => __PACKAGE__->dispatch_path,
- } } @{ __PACKAGE__->allowed_methods },
+ } } @{ __PACKAGE__->allowed_methods }
},
action_roles => [qw(HTTPMethods)],
);
@@ -41,22 +38,22 @@ sub auto :Private {
$self->set_body($c);
$self->log_request($c);
- return 1;
}
sub GET :Allow {
my ($self, $c, $id) = @_;
{
last unless $self->valid_id($c, $id);
- my $field_dev = $self->item_by_id($c, $id);
- last unless $self->resource_exists($c, deviceprofile => $field_dev);
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdeviceconfig => $item);
- my $hal = $self->hal_from_item($c, $field_dev);
+ my $hal = $self->hal_from_item($c, $item);
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"|r
- =~ s/rel=self/rel="item self"/r;
+ 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);
@@ -85,11 +82,71 @@ sub OPTIONS :Allow {
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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdeviceconfig => $item);
+ my $data = $self->get_valid_raw_put_data(
+ c => $c,
+ id => $id,
+ media_type => [qw#text/plain text/xml#],
+ );
+ last unless $data;
+ my $resource = $c->req->query_params;
+ $resource->{data} = $data;
+ my $form = $self->get_form($c);
+ my $old_resource = $self->resource_from_item($c, $item, $form);
+
+ $item = $self->update_item($c, $item, $old_resource, $resource, $form);
+ last unless $item;
+
+ $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, $item, $form);
+ 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;
+}
+
+=pod
+sub DELETE :Allow {
+ my ($self, $c, $id) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdeviceconfig => $item);
+ $item->delete;
+
+ $guard->commit;
+
+ $c->response->status(HTTP_NO_CONTENT);
+ $c->response->body(q());
+ }
+ return;
+}
+=cut
+
sub end : Private {
my ($self, $c) = @_;
$self->log_response($c);
- return 1;
}
# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwareBinaries.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwareBinaries.pm
new file mode 100644
index 0000000000..e4513377e9
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwareBinaries.pm
@@ -0,0 +1,80 @@
+package NGCP::Panel::Controller::API::PbxDeviceFirmwareBinaries;
+use Sipwise::Base;
+use namespace::sweep;
+use boolean qw(true);
+use Data::HAL qw();
+use Data::HAL::Link qw();
+use HTTP::Headers qw();
+use HTTP::Status qw(:constants);
+use MooseX::ClassAttribute qw(class_has);
+use NGCP::Panel::Utils::DateTime;
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
+BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+require Catalyst::ActionRole::ACL;
+require Catalyst::ActionRole::CheckTrailingSlash;
+require Catalyst::ActionRole::HTTPMethods;
+require Catalyst::ActionRole::RequireSSL;
+
+class_has 'api_description' => (
+ is => 'ro',
+ isa => 'Str',
+ default =>
+ 'Defines the actual PbxDeviceFirmwares Binary.',
+);
+
+class_has 'query_params' => (
+ is => 'ro',
+ isa => 'ArrayRef',
+ default => sub {[
+ ]},
+);
+
+with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
+
+class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwarebinaries');
+class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwarebinaries/');
+class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwarebinaries');
+
+__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(HTTPMethods)],
+);
+
+sub auto :Private {
+ my ($self, $c) = @_;
+
+ $self->set_body($c);
+ #$self->log_request($c);
+ return 1;
+}
+
+sub OPTIONS :Allow {
+ my ($self, $c) = @_;
+ my $allowed_methods = $self->allowed_methods;
+ $c->response->headers(HTTP::Headers->new(
+ Allow => $allowed_methods->join(', '),
+ 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);
+ return;
+}
+
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwareBinariesItem.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwareBinariesItem.pm
new file mode 100644
index 0000000000..5f4ee05811
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwareBinariesItem.pm
@@ -0,0 +1,85 @@
+package NGCP::Panel::Controller::API::PbxDeviceFirmwareBinariesItem;
+use Sipwise::Base;
+use namespace::sweep;
+use HTTP::Headers qw();
+use HTTP::Status qw(:constants);
+use MooseX::ClassAttribute qw(class_has);
+use NGCP::Panel::Utils::DateTime;
+use NGCP::Panel::Utils::ValidateJSON qw();
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
+BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+require Catalyst::ActionRole::ACL;
+require Catalyst::ActionRole::HTTPMethods;
+require Catalyst::ActionRole::RequireSSL;
+
+with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
+
+class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwarebinaries');
+class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwarebinaries/');
+class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwarebinaries');
+
+__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 }
+ },
+ action_roles => [qw(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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicefirmwarebinary => $item);
+ my $resource = $self->resource_from_item($c, $item);
+
+ $resource->{data} = $item->data;
+ $c->response->header ('Content-Disposition' => 'attachment; filename="' . $resource->{filename} . '"');
+ $c->response->content_type('application/octet-stream');
+ $c->response->body($resource->{data});
+ 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;
+ $c->response->headers(HTTP::Headers->new(
+ Allow => $allowed_methods->join(', '),
+ Accept_Patch => 'application/json-patch+json',
+ ));
+ $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);
+}
+
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwares.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwares.pm
new file mode 100644
index 0000000000..5a7ea82ba4
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwares.pm
@@ -0,0 +1,201 @@
+package NGCP::Panel::Controller::API::PbxDeviceFirmwares;
+use Sipwise::Base;
+use namespace::sweep;
+use boolean qw(true);
+use Data::HAL qw();
+use Data::HAL::Link qw();
+use HTTP::Headers qw();
+use HTTP::Status qw(:constants);
+use MooseX::ClassAttribute qw(class_has);
+use NGCP::Panel::Utils::DateTime;
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
+BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+require Catalyst::ActionRole::ACL;
+require Catalyst::ActionRole::CheckTrailingSlash;
+require Catalyst::ActionRole::HTTPMethods;
+require Catalyst::ActionRole::RequireSSL;
+
+class_has 'api_description' => (
+ is => 'ro',
+ isa => 'Str',
+ default =>
+ 'Defines firmwares for a PbxDeviceModel. To create or update a firmware, do a POST or PUT with Content-Type application/octet-stream and pass the properties via query parameters, e.g. /api/pbxdevicefirmwares/?device_id=1&filename=test.bin&version=1.0',
+);
+
+class_has 'query_params' => (
+ is => 'ro',
+ isa => 'ArrayRef',
+ default => sub {[
+ {
+ param => 'device_id',
+ description => 'Filter for firmwares of a specific device model',
+ query => {
+ first => sub {
+ my $q = shift;
+ return { 'device_id' => $q };
+ },
+ second => sub { },
+ },
+ },
+ ]},
+);
+
+with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
+
+class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwares');
+class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwares/');
+class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwares');
+
+__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(HTTPMethods)],
+);
+
+sub auto :Private {
+ my ($self, $c) = @_;
+
+ $self->set_body($c);
+ $self->log_request($c);
+}
+
+sub GET :Allow {
+ my ($self, $c) = @_;
+ my $page = $c->request->params->{page} // 1;
+ my $rows = $c->request->params->{rows} // 10;
+ {
+ my $items = $self->item_rs($c);
+ (my $total_count, $items) = $self->paginate_order_collection($c, $items);
+ my (@embedded, @links);
+ my $form = $self->get_form($c);
+ for my $item ($items->all) {
+ push @embedded, $self->hal_from_item($c, $item, $form);
+ push @links, Data::HAL::Link->new(
+ relation => 'ngcp:'.$self->resource_name,
+ href => sprintf('/%s%d', $c->request->path, $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', $c->request->path, $page, $rows));
+ if(($total_count / $rows) > $page ) {
+ push @links, Data::HAL::Link->new(relation => 'next', href => sprintf('/%s?page=%d&rows=%d', $c->request->path, $page + 1, $rows));
+ }
+ if($page > 1) {
+ push @links, Data::HAL::Link->new(relation => 'prev', href => sprintf('/%s?page=%d&rows=%d', $c->request->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;
+ $c->response->headers(HTTP::Headers->new(
+ Allow => $allowed_methods->join(', '),
+ 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 POST :Allow {
+ my ($self, $c) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $binary = $self->get_valid_raw_post_data(
+ c => $c,
+ media_type => 'application/octet-stream',
+ );
+ last unless $binary;
+ my $resource = $c->req->query_params;
+
+ my $form = $self->get_form($c);
+ last unless $self->validate_form(
+ c => $c,
+ resource => $resource,
+ form => $form,
+ exceptions => [ "device_id" ],
+ );
+
+ my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
+ id => $resource->{device_id}
+ });
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $model_rs = $model_rs->search({
+ reseller_id => $c->user->reseller_id,
+ });
+ }
+ my $model = $model_rs->first;
+ unless($model) {
+ $c->log->error("invalid device_id '$$resource{device_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
+ last;
+ }
+
+ $resource->{data} = $binary;
+ last unless($resource);
+
+ my $item;
+ try {
+ $item = $model->autoprov_firmwares->create($resource);
+ } catch($e) {
+ $c->log->error("failed to create pbxdevicefirmware: $e"); # TODO: user, message, trace, ...
+ $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create pbxdevicefirmware.");
+ last;
+ }
+
+ $guard->commit;
+
+ $c->response->status(HTTP_CREATED);
+ $c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
+ $c->response->body(q());
+ }
+ return;
+}
+
+sub end : Private {
+ my ($self, $c) = @_;
+
+ $self->log_response($c);
+}
+
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwaresItem.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwaresItem.pm
new file mode 100644
index 0000000000..9887e66519
--- /dev/null
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceFirmwaresItem.pm
@@ -0,0 +1,152 @@
+package NGCP::Panel::Controller::API::PbxDeviceFirmwaresItem;
+use Sipwise::Base;
+use namespace::sweep;
+use HTTP::Headers qw();
+use HTTP::Status qw(:constants);
+use MooseX::ClassAttribute qw(class_has);
+use NGCP::Panel::Utils::DateTime;
+use NGCP::Panel::Utils::ValidateJSON qw();
+use Path::Tiny qw(path);
+use Safe::Isa qw($_isa);
+BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+require Catalyst::ActionRole::ACL;
+require Catalyst::ActionRole::HTTPMethods;
+require Catalyst::ActionRole::RequireSSL;
+
+with 'NGCP::Panel::Role::API::PbxDeviceFirmwares';
+
+class_has('resource_name', is => 'ro', default => 'pbxdevicefirmwares');
+class_has('dispatch_path', is => 'ro', default => '/api/pbxdevicefirmwares/');
+class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-pbxdevicefirmwares');
+
+__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 }
+ },
+ action_roles => [qw(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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicefirmware => $item);
+
+ my $hal = $self->hal_from_item($c, $item);
+
+ 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;
+ $c->response->headers(HTTP::Headers->new(
+ Allow => $allowed_methods->join(', '),
+ Accept_Patch => 'application/json-patch+json',
+ ));
+ $c->response->content_type('application/json');
+ $c->response->body(JSON::to_json({ methods => $allowed_methods })."\n");
+ 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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicefirmware => $item);
+ my $recording = $self->get_valid_raw_put_data(
+ c => $c,
+ id => $id,
+ media_type => 'application/octet-stream',
+ );
+ last unless $recording;
+ my $resource = $c->req->query_params;
+ $resource->{data} = $recording;
+ my $form = $self->get_form($c);
+ my $old_resource = $self->resource_from_item($c, $item, $form);
+
+ $item = $self->update_item($c, $item, $old_resource, $resource, $form);
+ last unless $item;
+
+ $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, $item, $form);
+ 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;
+}
+
+=pod
+sub DELETE :Allow {
+ my ($self, $c, $id) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicefirmware => $item);
+ $item->delete;
+
+ $guard->commit;
+
+ $c->response->status(HTTP_NO_CONTENT);
+ $c->response->body(q());
+ }
+ return;
+}
+=cut
+
+sub end : Private {
+ my ($self, $c) = @_;
+
+ $self->log_response($c);
+}
+
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceModels.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceModels.pm
index f357cfac92..f7b1b6e1d7 100644
--- a/lib/NGCP/Panel/Controller/API/PbxDeviceModels.pm
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceModels.pm
@@ -25,6 +25,17 @@ class_has 'query_params' => (
is => 'ro',
isa => 'ArrayRef',
default => sub {[
+ {
+ param => 'reseller_id',
+ description => 'Filter for models belonging to a certain reseller',
+ query => {
+ first => sub {
+ my $q = shift;
+ { reseller_id => $q };
+ },
+ second => sub {},
+ },
+ },
{
param => 'model',
description => 'Filter for models matching a model name pattern',
@@ -146,6 +157,82 @@ sub OPTIONS :Allow {
return;
}
+sub POST :Allow {
+ my ($self, $c) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $resource = $self->get_valid_post_data(
+ c => $c,
+ media_type => 'application/json',
+ );
+ last unless $resource;
+
+ my $form = $self->get_form($c);
+ last unless $self->validate_form(
+ c => $c,
+ resource => $resource,
+ form => $form,
+ );
+
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $resource->{reseller_id} = $c->user->reseller_id;
+ }
+
+ my $reseller = $c->model('DB')->resultset('resellers')->find($resource->{reseller_id});
+ unless($reseller) {
+ $c->log->error("invalid reseller_id '$$resource{reseller_id}', does not exist");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid reseller_id, does not exist");
+ last;
+ }
+
+ my $item;
+ $item = $c->model('DB')->resultset('autoprov_devices')->find({
+ reseller_id => $resource->{reseller_id},
+ vendor => $resource->{vendor},
+ model => $resource->{model},
+ });
+ if($item) {
+ $c->log->error("device model with vendor '$$resource{vendor}' and model '$$resource{model}'already exists for reseller_id '$$resource{reseller_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Device model already exists for this reseller");
+ last;
+ }
+
+ my $linerange = delete $resource->{linerange};
+ unless(ref $linerange eq "ARRAY") {
+ $c->log->error("linerange must be array");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid linerange parameter, must be array");
+ last;
+ }
+
+
+ try {
+ $item = $c->model('DB')->resultset('autoprov_devices')->create($resource);
+ foreach my $range(@{ $linerange }) {
+ unless(ref $range eq "HASH") {
+ use Data::Dumper;
+ $c->log->error("all elements in linerange must be hashes, but this is " . ref $range . ": " . Dumper $range);
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid range definition inside linerange parameter, all must be hash");
+ return;
+ }
+ $item->autoprov_device_line_ranges->create($range);
+ }
+ } catch($e) {
+ $c->log->error("failed to create device model: $e");
+ $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create device model.");
+ last;
+ }
+
+ $guard->commit;
+
+ $c->response->status(HTTP_CREATED);
+ $c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
+ $c->response->body(q());
+ }
+ return;
+}
+
sub end : Private {
my ($self, $c) = @_;
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceModelsItem.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceModelsItem.pm
index ba06797dae..ca5983ec18 100644
--- a/lib/NGCP/Panel/Controller/API/PbxDeviceModelsItem.pm
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceModelsItem.pm
@@ -85,6 +85,116 @@ sub OPTIONS :Allow {
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',
+ );
+ last unless $json;
+
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicemodel => $item);
+ my $old_resource = { $item->get_inflated_columns };
+ my $resource = $self->apply_patch($c, $old_resource, $json);
+ last unless $resource;
+
+ my $form = $self->get_form($c);
+ $item = $self->update_item($c, $item, $old_resource, $resource, $form);
+ last unless $item;
+
+ $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, $item, $form);
+ 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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdevicemodel => $item);
+ my $resource = $self->get_valid_put_data(
+ c => $c,
+ id => $id,
+ media_type => 'application/json',
+ );
+ last unless $resource;
+ my $old_resource = { $item->get_inflated_columns };
+
+ my $form = $self->get_form($c);
+ $item = $self->update_item($c, $item, $old_resource, $resource, $form);
+ last unless $item;
+
+ $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, $item, $form);
+ 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;
+}
+
+=pod
+sub DELETE :Allow {
+ my ($self, $c, $id) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, emailtemplate => $item);
+ foreach(qw/subscriber_email_template_id passreset_email_template_id invoice_email_template_id/){
+ $c->model('DB')->resultset('contracts')->search({
+ $_ => $item->id,
+ })->update({
+ $_ => undef,
+ });
+ }
+
+ $item->delete;
+
+ $guard->commit;
+
+ $c->response->status(HTTP_NO_CONTENT);
+ $c->response->body(q());
+ }
+ return;
+}
+=cut
+
sub end : Private {
my ($self, $c) = @_;
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceProfiles.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceProfiles.pm
index a28a87cb25..a8806f54b0 100644
--- a/lib/NGCP/Panel/Controller/API/PbxDeviceProfiles.pm
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceProfiles.pm
@@ -135,6 +135,70 @@ sub OPTIONS :Allow {
return;
}
+sub POST :Allow {
+ my ($self, $c) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $resource = $self->get_valid_post_data(
+ c => $c,
+ media_type => 'application/json',
+ );
+ last unless $resource;
+
+ my $form = $self->get_form($c);
+ last unless $self->validate_form(
+ c => $c,
+ resource => $resource,
+ form => $form,
+ );
+
+ my $item;
+ $item = $c->model('DB')->resultset('autoprov_profiles')->find({
+ config_id => $resource->{config_id},
+ name => $resource->{name},
+ });
+ if($item) {
+ $c->log->error("Pbx device profile with name '$$resource{name}' already exists for config_id '$$resource{config_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device profile with this name already exists for this config");
+ last;
+ }
+ my $config_rs = $c->model('DB')->resultset('autoprov_configs')->search({
+ id => $resource->{config_id},
+ });
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $config_rs = $config_rs->search({
+ 'device.reseller_id' => $c->user->reseller_id,
+ },{
+ join => 'device',
+ });
+ }
+ my $config = $config_rs->first;
+ unless($config) {
+ $c->log->error("Pbx device config with confg_id '$$resource{config_id}' does not exist");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device config does not exist");
+ last;
+ }
+
+ try {
+ $item = $config->autoprov_profiles->create($resource);
+ } catch($e) {
+ $c->log->error("failed to create pbx device profile: $e"); # TODO: user, message, trace, ...
+ $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create pbx device profile.");
+ last;
+ }
+
+ $guard->commit;
+
+ $c->response->status(HTTP_CREATED);
+ $c->response->header(Location => sprintf('/%s%d', $c->request->path, $item->id));
+ $c->response->body(q());
+ }
+ return;
+}
+
+
sub end : Private {
my ($self, $c) = @_;
diff --git a/lib/NGCP/Panel/Controller/API/PbxDeviceProfilesItem.pm b/lib/NGCP/Panel/Controller/API/PbxDeviceProfilesItem.pm
index 00fa76ca4a..ba32b1268f 100644
--- a/lib/NGCP/Panel/Controller/API/PbxDeviceProfilesItem.pm
+++ b/lib/NGCP/Panel/Controller/API/PbxDeviceProfilesItem.pm
@@ -85,6 +85,116 @@ sub OPTIONS :Allow {
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',
+ );
+ last unless $json;
+
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdeviceprofile => $item);
+ my $old_resource = { $item->get_inflated_columns };
+ my $resource = $self->apply_patch($c, $old_resource, $json);
+ last unless $resource;
+
+ my $form = $self->get_form($c);
+ $item = $self->update_item($c, $item, $old_resource, $resource, $form);
+ last unless $item;
+
+ $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, $item, $form);
+ 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 $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, pbxdeviceprofile => $item);
+ my $resource = $self->get_valid_put_data(
+ c => $c,
+ id => $id,
+ media_type => 'application/json',
+ );
+ last unless $resource;
+ my $old_resource = { $item->get_inflated_columns };
+
+ my $form = $self->get_form($c);
+ $item = $self->update_item($c, $item, $old_resource, $resource, $form);
+ last unless $item;
+
+ $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, $item, $form);
+ 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;
+}
+
+=pod
+sub DELETE :Allow {
+ my ($self, $c, $id) = @_;
+
+ my $guard = $c->model('DB')->txn_scope_guard;
+ {
+ my $item = $self->item_by_id($c, $id);
+ last unless $self->resource_exists($c, emailtemplate => $item);
+ foreach(qw/subscriber_email_template_id passreset_email_template_id invoice_email_template_id/){
+ $c->model('DB')->resultset('contracts')->search({
+ $_ => $item->id,
+ })->update({
+ $_ => undef,
+ });
+ }
+
+ $item->delete;
+
+ $guard->commit;
+
+ $c->response->status(HTTP_NO_CONTENT);
+ $c->response->body(q());
+ }
+ return;
+}
+=cut
+
sub end : Private {
my ($self, $c) = @_;
diff --git a/lib/NGCP/Panel/Form/Device/ConfigAPI.pm b/lib/NGCP/Panel/Form/Device/ConfigAPI.pm
new file mode 100644
index 0000000000..9bf99e86ff
--- /dev/null
+++ b/lib/NGCP/Panel/Form/Device/ConfigAPI.pm
@@ -0,0 +1,51 @@
+package NGCP::Panel::Form::Device::ConfigAPI;
+
+use HTML::FormHandler::Moose;
+extends 'HTML::FormHandler';
+use Moose::Util::TypeConstraints;
+
+use HTML::FormHandler::Widget::Block::Bootstrap;
+
+has '+widget_wrapper' => ( default => 'Bootstrap' );
+has_field 'submitid' => ( type => 'Hidden' );
+has '+enctype' => ( default => 'multipart/form-data');
+sub build_render_list {[qw/submitid fields actions/]}
+sub build_form_element_class {[qw(form-horizontal)]}
+
+has_field 'device_id' => (
+ type => 'PosInteger',
+ required => 1,
+ label => 'Device Model',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The pbx device model id this config belongs to.']
+ },
+);
+
+has_field 'version' => (
+ type => 'Text',
+ required => 1,
+ label => 'Version',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The version number of this config.']
+ },
+);
+
+has_field 'content_type' => (
+ type => 'Text',
+ label => 'Filename',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The content type this config is served as.']
+ },
+);
+
+has_block 'fields' => (
+ tag => 'div',
+ class => [qw/modal-body/],
+ render_list => [qw/device_id version content_type/],
+);
+
+1;
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Form/Device/FirmwareAPI.pm b/lib/NGCP/Panel/Form/Device/FirmwareAPI.pm
new file mode 100644
index 0000000000..b5b0bcbbae
--- /dev/null
+++ b/lib/NGCP/Panel/Form/Device/FirmwareAPI.pm
@@ -0,0 +1,52 @@
+package NGCP::Panel::Form::Device::FirmwareAPI;
+
+use HTML::FormHandler::Moose;
+extends 'HTML::FormHandler';
+use Moose::Util::TypeConstraints;
+
+use HTML::FormHandler::Widget::Block::Bootstrap;
+
+has '+widget_wrapper' => ( default => 'Bootstrap' );
+has_field 'submitid' => ( type => 'Hidden' );
+has '+enctype' => ( default => 'multipart/form-data');
+sub build_render_list {[qw/submitid fields actions/]}
+sub build_form_element_class {[qw(form-horizontal)]}
+
+has_field 'device_id' => (
+ type => 'PosInteger',
+ required => 1,
+ label => 'Device Model',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The pbx device model id this firmware belongs to.']
+ },
+);
+
+has_field 'version' => (
+ type => 'Text',
+ required => 1,
+ label => 'Version',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The version number of this firmware.']
+ },
+);
+
+has_field 'filename' => (
+ type => 'Text',
+ required => 1,
+ label => 'Filename',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The filename of this firmware.']
+ },
+);
+
+has_block 'fields' => (
+ tag => 'div',
+ class => [qw/modal-body/],
+ render_list => [qw/device_id version filename/],
+);
+
+1;
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Form/Device/Model.pm b/lib/NGCP/Panel/Form/Device/Model.pm
index 2d178b0206..0ad37c8c54 100644
--- a/lib/NGCP/Panel/Form/Device/Model.pm
+++ b/lib/NGCP/Panel/Form/Device/Model.pm
@@ -18,12 +18,20 @@ has_field 'vendor' => (
type => 'Text',
required => 1,
label => 'Vendor',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The vendor name of this device.'],
+ },
);
has_field 'model' => (
type => 'Text',
required => 1,
label => 'Model',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The model name of this device.'],
+ },
);
has_field 'front_image' => (
@@ -45,6 +53,10 @@ has_field 'sync_uri' => (
required => 0,
label => 'Bootstrap Sync URI',
default => 'http://[% client.ip %]/admin/resync',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The sync URI to set the provisioning server of the device (e.g. http://client.ip/admin/resync. The client.ip variable is automatically expanded during provisioning time.'],
+ },
);
has_field 'sync_method' => (
@@ -56,6 +68,10 @@ has_field 'sync_method' => (
{ label => 'POST', value => 'POST' },
],
default => 'GET',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The HTTP method to set the provisioning server (one of GET, POST).'],
+ },
);
has_field 'sync_params' => (
@@ -63,6 +79,10 @@ has_field 'sync_params' => (
required => 0,
label => 'Bootstrap Sync Parameters',
default => '[% server.uri %]/$MA',
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['The parameters appended to the sync URI when setting the provisioning server, e.g. server.uri/$MA. The server.uri variable is automatically expanded during provisioning time.'],
+ },
);
has_field 'linerange' => (
@@ -76,6 +96,10 @@ has_field 'linerange' => (
controls_div => 1,
},
wrapper_class => [qw/hfh-rep-block/],
+ element_attr => {
+ rel => ['tooltip'],
+ title => ['An array of line/key definitions for this device. Each element is a hash containing the keys name, num_lines (defining the number of lines/keys for this range), can_private, can_shared, can_blf.'],
+ },
);
has_field 'linerange.id' => (
diff --git a/lib/NGCP/Panel/Form/Device/ModelAPI.pm b/lib/NGCP/Panel/Form/Device/ModelAPI.pm
new file mode 100644
index 0000000000..dce3d7b365
--- /dev/null
+++ b/lib/NGCP/Panel/Form/Device/ModelAPI.pm
@@ -0,0 +1,25 @@
+package NGCP::Panel::Form::Device::ModelAPI;
+
+use HTML::FormHandler::Moose;
+extends 'NGCP::Panel::Form::Device::ModelAdmin';
+use Moose::Util::TypeConstraints;
+
+has_block 'fields' => (
+ tag => 'div',
+ class => [qw/modal-body/],
+ render_list => [qw/reseller vendor model linerange sync_uri sync_method sync_params/],
+);
+
+override 'field_list' => sub {
+ my $self = shift;
+ my $c = $self->ctx;
+ return unless $c;
+
+ super();
+ foreach my $f(qw/front_image mac_image linerange_add/) {
+ $self->field($f)->inactive(1);
+ }
+};
+
+1;
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Role/API.pm b/lib/NGCP/Panel/Role/API.pm
index 992d5843a7..d324028251 100644
--- a/lib/NGCP/Panel/Role/API.pm
+++ b/lib/NGCP/Panel/Role/API.pm
@@ -200,9 +200,18 @@ sub forbid_link_header {
sub valid_media_type {
my ($self, $c, $media_type) = @_;
- return 1 if($c->request->header('Content-Type') &&
+
+ my $type;
+ if(ref $media_type eq "ARRAY") {
+ $type = join ' or ', @{ $media_type };
+ return 1 if $c->request->header('Content-Type') &&
+ $c->request->header('Content-Type') ~~ $media_type;
+ } else {
+ $type = $media_type;
+ return 1 if($c->request->header('Content-Type') &&
index($c->request->header('Content-Type'), $media_type) == 0);
- $self->error($c, HTTP_UNSUPPORTED_MEDIA_TYPE, "Unsupported media type, accepting '$media_type' only.");
+ }
+ $self->error($c, HTTP_UNSUPPORTED_MEDIA_TYPE, "Unsupported media type, accepting $type only.");
return;
}
diff --git a/lib/NGCP/Panel/Role/API/PbxDeviceConfigs.pm b/lib/NGCP/Panel/Role/API/PbxDeviceConfigs.pm
index ed54338027..dc476a6484 100644
--- a/lib/NGCP/Panel/Role/API/PbxDeviceConfigs.pm
+++ b/lib/NGCP/Panel/Role/API/PbxDeviceConfigs.pm
@@ -11,18 +11,33 @@ use TryCatch;
use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
-use JSON::Types;
-use NGCP::Panel::Form::Device::Config;
+use NGCP::Panel::Form::Device::ConfigAPI;
+
+sub item_rs {
+ my ($self, $c) = @_;
+
+ my $item_rs = $c->model('DB')->resultset('autoprov_configs');
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $item_rs = $item_rs->search({
+ 'device.reseller_id' => $c->user->reseller_id
+ },{
+ join => 'device',
+ });
+ }
+ return $item_rs;
+}
sub get_form {
my ($self, $c) = @_;
- return NGCP::Panel::Form::Device::Config->new;
+ return NGCP::Panel::Form::Device::ConfigAPI->new(ctx => $c);
}
sub hal_from_item {
- my ($self, $c, $item) = @_;
- my $form;
- my $type = 'pbxdeviceconfigs';
+ my ($self, $c, $item, $form) = @_;
+
+ $form //= $self->get_form($c);
+ my $resource = $self->resource_from_item($c, $item, $form);
my $hal = Data::HAL->new(
links => [
@@ -32,56 +47,80 @@ sub hal_from_item {
name => 'ngcp',
templated => true,
),
- Data::HAL::Link->new(relation => 'collection', href => sprintf("%s", $self->dispatch_path)),
+ Data::HAL::Link->new(relation => 'collection', href => sprintf("/api/%s/", $self->resource_name)),
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
Data::HAL::Link->new(relation => 'self', href => sprintf("%s%d", $self->dispatch_path, $item->id)),
- Data::HAL::Link->new(relation => "ngcp:$type", href => sprintf("/api/%s/%d", $type, $item->id)),
Data::HAL::Link->new(relation => 'ngcp:pbxdevicemodels', href => sprintf("/api/pbxdevicemodels/%d", $item->device_id)),
+ Data::HAL::Link->new(relation => 'ngcp:pbxdeviceconfigfiles', href => sprintf("/api/pbxdeviceconfigfiles/%d", $item->id)),
],
relation => 'ngcp:'.$self->resource_name,
);
- my $resource = $self->resource_from_item($c, $item);
+
+ $self->validate_form(
+ c => $c,
+ resource => $resource,
+ form => $form,
+ run => 0,
+ exceptions => [qw/device_id/],
+ );
+
+ $resource->{id} = int($item->id);
+ delete $resource->{data};
$hal->resource($resource);
return $hal;
}
sub resource_from_item {
- my ($self, $c, $item) = @_;
+ my ($self, $c, $item, $form) = @_;
+
+ my $resource = { $item->get_inflated_columns };
+ delete $resource->{data};
- my %resource = $item->get_inflated_columns;
- delete $resource{device_id};
+ return $resource;
+}
- my $form = $self->get_form($c);
+sub item_by_id {
+ my ($self, $c, $id) = @_;
+ my $item_rs = $self->item_rs($c);
+ return $item_rs->find($id);
+}
+
+sub update_item {
+ my ($self, $c, $item, $old_resource, $resource, $form) = @_;
+
+ my $binary = delete $resource->{data};
+ $resource->{content_type} = $c->request->header('Content-Type');
+
+ $form //= $self->get_form($c);
return unless $self->validate_form(
c => $c,
form => $form,
- resource => \%resource,
- run => 0,
+ resource => $resource,
+ exceptions => [qw/device_id/],
);
- $resource{id} = int($item->id);
- return \%resource;
-}
-
-sub item_rs {
- my ($self, $c) = @_;
- my $item_rs = $c->model('DB')->resultset('autoprov_configs');
+ my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
+ id => $resource->{device_id}
+ });
if($c->user->roles eq "admin") {
- } elsif ($c->user->roles eq "reseller") {
- $item_rs = $item_rs->search(
- { 'device.reseller_id' => $c->user->reseller_id, },
- { prefetch => 'device', });
+ } elsif($c->user->roles eq "reseller") {
+ $model_rs = $model_rs->search({
+ reseller_id => $c->user->reseller_id,
+ });
+ }
+ my $model = $model_rs->first;
+ unless($model) {
+ $c->log->error("invalid device_id '$$resource{device_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
+ last;
}
- return $item_rs;
-}
+ $resource->{data} = $binary;
-sub item_by_id {
- my ($self, $c, $id) = @_;
+ $item->update($resource);
- my $item_rs = $self->item_rs($c);
- return $item_rs->find($id);
+ return $item;
}
1;
diff --git a/lib/NGCP/Panel/Role/API/PbxDeviceFirmwares.pm b/lib/NGCP/Panel/Role/API/PbxDeviceFirmwares.pm
new file mode 100644
index 0000000000..cf3acbcd65
--- /dev/null
+++ b/lib/NGCP/Panel/Role/API/PbxDeviceFirmwares.pm
@@ -0,0 +1,125 @@
+package NGCP::Panel::Role::API::PbxDeviceFirmwares;
+use Moose::Role;
+use Sipwise::Base;
+with 'NGCP::Panel::Role::API' => {
+ -alias =>{ item_rs => '_item_rs', },
+ -excludes => [ 'item_rs' ],
+};
+
+use boolean qw(true);
+use TryCatch;
+use Data::HAL qw();
+use Data::HAL::Link qw();
+use HTTP::Status qw(:constants);
+use NGCP::Panel::Form::Device::FirmwareAPI;
+
+sub item_rs {
+ my ($self, $c) = @_;
+
+ my $item_rs = $c->model('DB')->resultset('autoprov_firmwares');
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $item_rs = $item_rs->search({
+ 'device.reseller_id' => $c->user->reseller_id
+ },{
+ join => 'device',
+ });
+ }
+ return $item_rs;
+}
+
+sub get_form {
+ my ($self, $c) = @_;
+ return NGCP::Panel::Form::Device::FirmwareAPI->new(ctx => $c);
+}
+
+sub hal_from_item {
+ my ($self, $c, $item, $form) = @_;
+
+ $form //= $self->get_form($c);
+ my $resource = $self->resource_from_item($c, $item, $form);
+
+ my $hal = Data::HAL->new(
+ 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 => 'collection', href => sprintf("/api/%s/", $self->resource_name)),
+ Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
+ Data::HAL::Link->new(relation => 'self', href => sprintf("%s%d", $self->dispatch_path, $item->id)),
+ Data::HAL::Link->new(relation => 'ngcp:pbxdevicemodels', href => sprintf("/api/pbxdevicemodels/%d", $item->device_id)),
+ Data::HAL::Link->new(relation => 'ngcp:pbxdevicefirmwarebinaries', href => sprintf("/api/pbxdevicefirmwarebinaries/%d", $item->id)),
+ ],
+ relation => 'ngcp:'.$self->resource_name,
+ );
+
+
+ $self->validate_form(
+ c => $c,
+ resource => $resource,
+ form => $form,
+ run => 0,
+ exceptions => [qw/device_id/],
+ );
+
+ $resource->{id} = int($item->id);
+ delete $resource->{data};
+ $hal->resource($resource);
+ return $hal;
+}
+
+sub resource_from_item {
+ my ($self, $c, $item, $form) = @_;
+
+ my $resource = { $item->get_inflated_columns };
+ delete $resource->{data};
+
+ return $resource;
+}
+
+sub item_by_id {
+ my ($self, $c, $id) = @_;
+ my $item_rs = $self->item_rs($c);
+ return $item_rs->find($id);
+}
+
+sub update_item {
+ my ($self, $c, $item, $old_resource, $resource, $form) = @_;
+
+ my $binary = delete $resource->{data};
+ $form //= $self->get_form($c);
+ return unless $self->validate_form(
+ c => $c,
+ form => $form,
+ resource => $resource,
+ exceptions => [qw/device_id/],
+ );
+
+ my $model_rs = $c->model('DB')->resultset('autoprov_devices')->search({
+ id => $resource->{device_id}
+ });
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $model_rs = $model_rs->search({
+ reseller_id => $c->user->reseller_id,
+ });
+ }
+ my $model = $model_rs->first;
+ unless($model) {
+ $c->log->error("invalid device_id '$$resource{device_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device model does not exist");
+ last;
+ }
+
+ $resource->{data} = $binary;
+
+ $item->update($resource);
+
+ return $item;
+}
+
+1;
+# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Role/API/PbxDeviceModels.pm b/lib/NGCP/Panel/Role/API/PbxDeviceModels.pm
index c9bcb83046..8e59657058 100644
--- a/lib/NGCP/Panel/Role/API/PbxDeviceModels.pm
+++ b/lib/NGCP/Panel/Role/API/PbxDeviceModels.pm
@@ -12,17 +12,17 @@ use Data::HAL qw();
use Data::HAL::Link qw();
use HTTP::Status qw(:constants);
use JSON::Types;
-use NGCP::Panel::Form::Device::Model;
+use NGCP::Panel::Form::Device::ModelAPI;
sub get_form {
my ($self, $c) = @_;
- return NGCP::Panel::Form::Device::Model->new;
+ return NGCP::Panel::Form::Device::ModelAPI->new($c);
}
sub hal_from_item {
my ($self, $c, $item) = @_;
my $form;
- my $type = 'pbxdevicemodels';
+ #my $type = 'pbxdevicemodels';
my $hal = Data::HAL->new(
links => [
@@ -35,7 +35,7 @@ sub hal_from_item {
Data::HAL::Link->new(relation => 'collection', href => sprintf("%s", $self->dispatch_path)),
Data::HAL::Link->new(relation => 'profile', href => 'http://purl.org/sipwise/ngcp-api/'),
Data::HAL::Link->new(relation => 'self', href => sprintf("%s%d", $self->dispatch_path, $item->id)),
- Data::HAL::Link->new(relation => "ngcp:$type", href => sprintf("/api/%s/%d", $type, $item->id)),
+ Data::HAL::Link->new(relation => "ngcp:pbxdevicefirmwares", href => sprintf("/api/pbxdevicefirmwares/?device_id=%d", $item->id)),
],
relation => 'ngcp:'.$self->resource_name,
);
@@ -62,6 +62,19 @@ sub resource_from_item {
$resource{reseller_id} = int($item->reseller_id);
$resource{id} = int($item->id);
+ $resource{linerange} = [];
+ foreach my $range($item->autoprov_device_line_ranges->all) {
+ my $r = { $range->get_inflated_columns };
+ foreach my $f(qw/device_id/) {
+ delete $r->{$f};
+ }
+ $r->{id} = int($r->{id});
+ foreach my $f(qw/can_private can_shared can_blf/) {
+ $r->{$f} = JSON::Types::bool($r->{$f});
+ }
+
+ push @{ $resource{linerange} }, $r;
+ }
return \%resource;
}
@@ -83,5 +96,88 @@ sub item_by_id {
return $item_rs->find($id);
}
+sub update_item {
+
+ my ($self, $c, $item, $old_resource, $resource, $form) = @_;
+ $form //= $self->get_form($c);
+
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $resource->{reseller_id} = $c->user->reseller_id;
+ }
+
+ my $reseller = $c->model('DB')->resultset('resellers')->find($resource->{reseller_id});
+ unless($reseller) {
+ $c->log->error("invalid reseller_id '$$resource{reseller_id}', does not exist");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid reseller_id, does not exist");
+ return;
+ }
+
+ my $dup_item;
+ $dup_item = $c->model('DB')->resultset('autoprov_devices')->find({
+ reseller_id => $resource->{reseller_id},
+ vendor => $resource->{vendor},
+ model => $resource->{model},
+ });
+ if($dup_item && $dup_item->id != $item->id) {
+ $c->log->error("device model with vendor '$$resource{vendor}' and model '$$resource{model}'already exists for reseller_id '$$resource{reseller_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Device model already exists for this reseller");
+ return;
+ }
+
+ my $linerange = delete $resource->{linerange};
+ unless(ref $linerange eq "ARRAY") {
+ $c->log->error("linerange must be array");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid linerange parameter, must be array");
+ return;
+ }
+
+ $item->update($resource);
+
+ my @existing_range = ();
+ my $range_rs = $item->autoprov_device_line_ranges;
+ foreach my $range(@{ $linerange }) {
+ next unless(defined $range);
+ my $old_range;
+ if(defined $range->{id}) {
+ # should be an existing range, do update
+ $old_range = $range_rs->find($range->{id});
+ delete $range->{id};
+ unless($old_range) {
+ $old_range = $range_rs->create($range);
+ } else {
+ # formhandler only passes set check-boxes, so explicitely unset here
+ $range->{can_private} //= 0;
+ $range->{can_shared} //= 0;
+ $range->{can_blf} //= 0;
+ $old_range->update($range);
+ }
+ } else {
+ # new range
+ $old_range = $range_rs->create($range);
+ }
+ push @existing_range, $old_range->id; # mark as valid (delete others later)
+
+ # delete field device line assignments with are out-of-range or use a
+ # feature which is not supported anymore after edit
+ foreach my $fielddev_line($c->model('DB')->resultset('autoprov_field_device_lines')
+ ->search({ linerange_id => $old_range->id })->all) {
+ if($fielddev_line->key_num >= $old_range->num_lines ||
+ ($fielddev_line->line_type eq 'private' && !$old_range->can_private) ||
+ ($fielddev_line->line_type eq 'shared' && !$old_range->can_shared) ||
+ ($fielddev_line->line_type eq 'blf' && !$old_range->can_blf)) {
+
+ $fielddev_line->delete;
+ }
+ }
+ }
+ # delete invalid range ids (e.g. removed ones)
+ $range_rs->search({
+ id => { 'not in' => \@existing_range },
+ })->delete_all;
+
+ return $item;
+}
+
1;
# vim: set tabstop=4 expandtab:
diff --git a/lib/NGCP/Panel/Role/API/PbxDeviceProfiles.pm b/lib/NGCP/Panel/Role/API/PbxDeviceProfiles.pm
index ea930df0c3..91359bbdda 100644
--- a/lib/NGCP/Panel/Role/API/PbxDeviceProfiles.pm
+++ b/lib/NGCP/Panel/Role/API/PbxDeviceProfiles.pm
@@ -61,6 +61,7 @@ sub resource_from_item {
);
$resource{id} = int($item->id);
+ $resource{config_id} = int($item->config_id);
return \%resource;
}
@@ -84,5 +85,41 @@ sub item_by_id {
return $item_rs->find($id);
}
+sub update_item {
+ my ($self, $c, $item, $old_resource, $resource, $form) = @_;
+
+ my $dup_item = $c->model('DB')->resultset('autoprov_profiles')->find({
+ config_id => $resource->{config_id},
+ name => $resource->{name},
+ });
+ if($dup_item && $dup_item->id != $item->id) {
+ $c->log->error("Pbx device profile with name '$$resource{name}' already exists for config_id '$$resource{config_id}'");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device profile with this name already exists for this config");
+ last;
+ }
+ my $config_rs = $c->model('DB')->resultset('autoprov_configs')->search({
+ id => $resource->{config_id},
+ });
+ if($c->user->roles eq "admin") {
+ } elsif($c->user->roles eq "reseller") {
+ $config_rs = $config_rs->search({
+ 'device.reseller_id' => $c->user->reseller_id,
+ },{
+ join => 'device',
+ });
+ }
+ my $config = $config_rs->first;
+ unless($config) {
+ $c->log->error("Pbx device config with confg_id '$$resource{config_id}' does not exist");
+ $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Pbx device config does not exist");
+ last;
+ }
+
+ $item->update($resource);
+
+ return $item;
+}
+
+
1;
# vim: set tabstop=4 expandtab: