parent
							
								
									427bd2e312
								
							
						
					
					
						commit
						19d6b47796
					
				| @ -0,0 +1,243 @@ | ||||
| package NGCP::Panel::Controller::API::SubscriberProfiles; | ||||
| 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 subscriber profiles which specify the available features for a subscriber.', | ||||
| ); | ||||
| 
 | ||||
| class_has 'query_params' => ( | ||||
|     is => 'ro', | ||||
|     isa => 'ArrayRef', | ||||
|     default => sub {[ | ||||
|         { | ||||
|             param => 'profile_set_id', | ||||
|             description => 'Filter for profiles  belonging to a specific profile set', | ||||
|             query => { | ||||
|                 first => sub { | ||||
|                     my $q = shift; | ||||
|                     { set_id => $q }; | ||||
|                 }, | ||||
|                 second => sub {}, | ||||
|             }, | ||||
|         }, | ||||
|     ]}, | ||||
| ); | ||||
| 
 | ||||
| with 'NGCP::Panel::Role::API::SubscriberProfiles'; | ||||
| 
 | ||||
| class_has('resource_name', is => 'ro', default => 'subscriberprofiles'); | ||||
| class_has('dispatch_path', is => 'ro', default => '/api/subscriberprofiles/'); | ||||
| class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-subscriberprofiles'); | ||||
| 
 | ||||
| __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 = int($items->count); | ||||
|         $items = $items->search(undef, { | ||||
|             page => $page, | ||||
|             rows => $rows, | ||||
|         }); | ||||
|         my (@embedded, @links); | ||||
|         my $form = $self->get_form($c); | ||||
|         for my $item ($items->search({}, {order_by => {-asc => 'me.id'}})->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) = @_; | ||||
| 
 | ||||
|     if($c->user->roles eq "reseller" && !$c->config->{profile_sets}->{reseller_edit}) { | ||||
|         $c->log->error("profile creation by reseller forbidden via config"); | ||||
|         $self->error($c, HTTP_FORBIDDEN, "Subscriber profile creation forbidden for resellers."); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     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 $attributes = delete $resource->{attributes}; | ||||
| 
 | ||||
|         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; | ||||
|         } | ||||
|         $resource->{set_id} = delete $resource->{profile_set_id}; | ||||
| 
 | ||||
|         my $set = $c->model('DB')->resultset('voip_subscriber_profile_sets'); | ||||
|         if($c->user->roles eq "reseller") { | ||||
|             $set = $set->search({ | ||||
|                 reseller_id => $c->user->reseller_id, | ||||
|             }); | ||||
|         } | ||||
|         $set = $set->find($resource->{set_id}); | ||||
|         unless($set) { | ||||
|             $c->log->error("subscriber profile set with id '$$resource{set_id}' does not exist"); # TODO: user, message, trace, ... | ||||
|             $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'profile_set_id', does not exist"); | ||||
|             last; | ||||
|         } | ||||
| 
 | ||||
|         my $item; | ||||
|         $item = $set->voip_subscriber_profiles->find({ | ||||
|             name => $resource->{name}, | ||||
|         }); | ||||
|         if($item) { | ||||
|             $c->log->error("subscriber profile with name '$$resource{name}' already exists for profile_set_id '$$resource{set_id}'"); # TODO: user, message, trace, ... | ||||
|             $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber profile with this name already exists for this profile set"); | ||||
|             last; | ||||
|         } | ||||
|         if($resource->{set_default}) { | ||||
|             $set->voip_subscriber_profiles->update({ | ||||
|                 set_default => 0, | ||||
|             }); | ||||
|         } | ||||
|         unless($set->voip_subscriber_profiles->count) { | ||||
|             $resource->{set_default} = 1; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             $item = $set->voip_subscriber_profiles->create($resource); | ||||
|             my $meta_rs = $c->model('DB')->resultset('voip_preferences')->search({ | ||||
|                 -or => [ | ||||
|                 { | ||||
|                     usr_pref => 1, | ||||
|                     expose_to_customer => 1, | ||||
|                 }, | ||||
|                 { | ||||
|                     attribute => { -in => [qw/cfu cft cfna cfb/] }, | ||||
|                 }, | ||||
|                 ], | ||||
|             }); | ||||
|             foreach my $a(@{ $attributes }) { | ||||
|                 my $meta = $meta_rs->find({ attribute => $a }); | ||||
|                 next unless $meta; | ||||
|                 $item->profile_attributes->create({ attribute_id => $meta->id }); | ||||
|             } | ||||
|         } catch($e) { | ||||
|             $c->log->error("failed to create subscriber profile: $e"); # TODO: user, message, trace, ... | ||||
|             $self->error($c, HTTP_INTERNAL_SERVER_ERROR, "Failed to create subscriber 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) = @_; | ||||
| 
 | ||||
|     $self->log_response($c); | ||||
| } | ||||
| 
 | ||||
| # vim: set tabstop=4 expandtab: | ||||
| @ -0,0 +1,204 @@ | ||||
| package NGCP::Panel::Controller::API::SubscriberProfilesItem; | ||||
| 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::SubscriberProfiles'; | ||||
| 
 | ||||
| class_has('resource_name', is => 'ro', default => 'subscriberprofiles'); | ||||
| class_has('dispatch_path', is => 'ro', default => '/api/subscriberprofiles/'); | ||||
| class_has('relation', is => 'ro', default => 'http://purl.org/sipwise/ngcp-api/#rel-subscriberprofiles'); | ||||
| 
 | ||||
| __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, subscriberprofile => $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 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 $form = $self->get_form($c); | ||||
| 
 | ||||
|         my $item = $self->item_by_id($c, $id); | ||||
|         last unless $self->resource_exists($c, subscriberprofile => $item); | ||||
|         my $old_resource = $self->resource_from_item($c, $item, $form); | ||||
|         my $resource = $self->apply_patch($c, $old_resource, $json); | ||||
|         last unless $resource; | ||||
| 
 | ||||
|         $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, subscriberprofile => $item); | ||||
|         my $resource = $self->get_valid_put_data( | ||||
|             c => $c, | ||||
|             id => $id, | ||||
|             media_type => 'application/json', | ||||
|         ); | ||||
|         last unless $resource; | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
| 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, subscriberprofile => $item); | ||||
| 
 | ||||
|         $c->model('DB')->resultset('contracts')->search({ | ||||
|             subscriber_email_template_id => $item->id, | ||||
|         })->update({ | ||||
|             subscriber_email_template_id => undef, | ||||
|         }); | ||||
|         $c->model('DB')->resultset('contracts')->search({ | ||||
|             passreset_email_template_id => $item->id, | ||||
|         })->update({ | ||||
|             passreset_email_template_id => undef, | ||||
|         }); | ||||
| 
 | ||||
|         $item->delete; | ||||
| 
 | ||||
|         $guard->commit; | ||||
| 
 | ||||
|         $c->response->status(HTTP_NO_CONTENT); | ||||
|         $c->response->body(q()); | ||||
|     } | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| sub end : Private { | ||||
|     my ($self, $c) = @_; | ||||
| 
 | ||||
|     $self->log_response($c); | ||||
| } | ||||
| 
 | ||||
| # vim: set tabstop=4 expandtab: | ||||
| @ -0,0 +1,198 @@ | ||||
| package NGCP::Panel::Role::API::SubscriberProfiles; | ||||
| 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::SubscriberProfile::ApiProfile; | ||||
| 
 | ||||
| sub item_rs { | ||||
|     my ($self, $c) = @_; | ||||
| 
 | ||||
|     my $item_rs = $c->model('DB')->resultset('voip_subscriber_profiles'); | ||||
|     if($c->user->roles eq "admin") { | ||||
|     } elsif($c->user->roles eq "reseller") { | ||||
|         $item_rs = $item_rs->search({ 'profile_set.reseller_id' => $c->user->reseller_id }, { | ||||
|             join => 'profile_set', | ||||
|         }); | ||||
|     } | ||||
|     return $item_rs; | ||||
| } | ||||
| 
 | ||||
| sub get_form { | ||||
|     my ($self, $c) = @_; | ||||
|     return NGCP::Panel::Form::SubscriberProfile::ApiProfile->new; | ||||
| } | ||||
| 
 | ||||
| sub hal_from_item { | ||||
|     my ($self, $c, $item, $form) = @_; | ||||
|     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:subscriberprofilesets', href => sprintf("/api/subscriberprofilesets/%d", $item->set_id)), | ||||
|         ], | ||||
|         relation => 'ngcp:'.$self->resource_name, | ||||
|     ); | ||||
| 
 | ||||
|     $form //= $self->get_form($c); | ||||
| 
 | ||||
|     $resource->{attribute} = delete $resource->{attributes}; | ||||
|     $self->validate_form( | ||||
|         c => $c, | ||||
|         resource => $resource, | ||||
|         form => $form, | ||||
|         run => 0, | ||||
|     ); | ||||
| 
 | ||||
|     $resource->{attributes} = delete $resource->{attribute}; | ||||
|     $resource->{id} = int($item->id); | ||||
|     $hal->resource($resource); | ||||
|     return $hal; | ||||
| } | ||||
| 
 | ||||
| sub resource_from_item { | ||||
|     my ($self, $c, $item, $form) = @_; | ||||
| 
 | ||||
|     $form //= $self->get_form($c); | ||||
| 
 | ||||
|     my %resource = $item->get_inflated_columns; | ||||
|     my @att = map { $_->attribute->attribute } $item->profile_attributes->all; | ||||
|     $resource{attributes} = \@att; | ||||
|     $resource{profile_set_id} = delete $resource{set_id}; | ||||
| 
 | ||||
|     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) = @_; | ||||
| 
 | ||||
|     # delete $resource->{attribute} in case reseller not allowed to update set | ||||
| 
 | ||||
|     $resource->{attribute} = delete $resource->{attributes}; | ||||
|     $form //= $self->get_form($c); | ||||
|     return unless $self->validate_form( | ||||
|         c => $c, | ||||
|         form => $form, | ||||
|         resource => $resource, | ||||
|     ); | ||||
|     $resource->{set_id} = delete $resource->{profile_set_id}; | ||||
| 
 | ||||
|     my $set = $c->model('DB')->resultset('voip_subscriber_profile_sets'); | ||||
|     if($c->user->roles eq "reseller") { | ||||
|         $set = $set->search({ | ||||
|             reseller_id => $c->user->reseller_id, | ||||
|         }); | ||||
|     } | ||||
|     $set = $set->find($resource->{set_id}); | ||||
| 
 | ||||
|     unless($set) { | ||||
|         $c->log->error("subscriber profile set id '$$resource{set_id}' does not exist"); # TODO: user, message, trace, ... | ||||
|         $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Invalid 'profile_set_id', does not exist"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     my $dup_item = $set->voip_subscriber_profiles->find({ | ||||
|         name => $resource->{name}, | ||||
|     }); | ||||
|     if($dup_item && $dup_item->id != $item->id) { | ||||
|         $c->log->error("subscriber profile with name '$$resource{name}' already exists for profile_set_id '$$resource{set_id}'"); # TODO: user, message, trace, ... | ||||
|         $self->error($c, HTTP_UNPROCESSABLE_ENTITY, "Subscriber profile with this name already exists for this profile set"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     my $attributes; | ||||
|     if($c->user->roles eq "reseller" && !$c->config->{profile_sets}->{reseller_edit}) { | ||||
|         # don't let reseller update attributes in this case | ||||
|         $attributes = [ map { $_->attribute->attribute } $item->profile_attributes->all ]; | ||||
|     } else { | ||||
|         $attributes = $resource->{attribute}; | ||||
|     } | ||||
|     delete $resource->{attribute}; | ||||
| 
 | ||||
|     if($item->set_default && !$resource->{set_default}) { | ||||
|         $set->voip_subscriber_profiles->first->update({ | ||||
|             set_default => 1, | ||||
|         }); | ||||
|     } elsif(!$item->set_default && $resource->{set_default}) { | ||||
|         $set->voip_subscriber_profiles->all->update({ | ||||
|             set_default => 0, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     $item->update($resource); | ||||
| 
 | ||||
|     my %old_attributes = map { $_ => 1 } | ||||
|         $item->profile_attributes->get_column('attribute_id')->all; | ||||
| 
 | ||||
|     # TODO: reuse attributes for efficiency reasons? | ||||
|     $item->profile_attributes->delete; | ||||
| 
 | ||||
|     my $meta_rs = $c->model('DB')->resultset('voip_preferences')->search({ | ||||
|         -or => [ | ||||
|         { | ||||
|             usr_pref => 1, | ||||
|             expose_to_customer => 1, | ||||
|         }, | ||||
|         { | ||||
|             attribute => { -in => [qw/cfu cft cfna cfb/] }, | ||||
|         }, | ||||
|         ], | ||||
|     }); | ||||
|     foreach my $a(@{ $attributes }) { | ||||
|         my $meta = $meta_rs->find({ attribute => $a }); | ||||
|         next unless $meta; | ||||
|         # mark as seen, so later we can unprovision the remaining ones, | ||||
|         # which are the ones not set here: | ||||
|         delete $old_attributes{$meta->id}; | ||||
| 
 | ||||
|         $item->profile_attributes->create({ attribute_id => $meta->id }); | ||||
|     } | ||||
|     # go over remaining attributes (those which were set before but are not set anymore) | ||||
|     # and clear them from usr-preferences | ||||
|     if(keys %old_attributes) { | ||||
|         my $cfs = $c->model('DB')->resultset('voip_preferences')->search({ | ||||
|             id => { -in => [ keys %old_attributes ] }, | ||||
|             attribute => { -in => [qw/cfu cfb cft cfna/] }, | ||||
|         }); | ||||
|         my @subs = $c->model('DB')->resultset('provisioning_voip_subscribers') | ||||
|             ->search({ | ||||
|                 profile_id => $item->id, | ||||
|             })->all; | ||||
|         foreach my $sub(@subs) { | ||||
|             $sub->voip_usr_preferences->search({ | ||||
|                 attribute_id => { -in => [ keys %old_attributes ] }, | ||||
|             })->delete; | ||||
|             $sub->voip_cf_mappings->search({ | ||||
|                 type => { -in => [ map { $_->attribute } $cfs->all ] }, | ||||
|             })->delete; | ||||
|         } | ||||
|     } | ||||
|          | ||||
|     return $item; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
| # vim: set tabstop=4 expandtab: | ||||
					Loading…
					
					
				
		Reference in new issue