From d111caee98a635acc5fabf270bd3bb69579f6dfd Mon Sep 17 00:00:00 2001 From: Andreas Granig Date: Mon, 22 Jul 2013 18:07:16 +0200 Subject: [PATCH] Implement editing of subscriber master data. --- lib/NGCP/Panel/Controller/Subscriber.pm | 78 ++++++ lib/NGCP/Panel/Field/SubscriberLockSelect.pm | 19 ++ lib/NGCP/Panel/Form/Subscriber.pm | 4 +- lib/NGCP/Panel/Form/SubscriberEdit.pm | 260 +++++++++++++++++++ share/templates/customer/details.tt | 4 +- share/templates/subscriber/master.tt | 108 ++------ 6 files changed, 384 insertions(+), 89 deletions(-) create mode 100644 lib/NGCP/Panel/Field/SubscriberLockSelect.pm create mode 100644 lib/NGCP/Panel/Form/SubscriberEdit.pm diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 500e7e7654..a90e77f8a6 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -8,6 +8,7 @@ use NGCP::Panel::Utils::Contract; use NGCP::Panel::Utils::Subscriber; use NGCP::Panel::Utils::Datatables; use NGCP::Panel::Form::Subscriber; +use NGCP::Panel::Form::SubscriberEdit; use NGCP::Panel::Form::SubscriberCFSimple; use NGCP::Panel::Form::SubscriberCFTSimple; use NGCP::Panel::Form::SubscriberCFAdvanced; @@ -1309,6 +1310,83 @@ sub details :Chained('master') :PathPart('') :Args(0) { my ($self, $c) = @_; } +sub edit_master :Chained('master') :PathPart('edit') { + my ($self, $c, $attribute) = @_; + + my $form = NGCP::Panel::Form::SubscriberEdit->new; + my $posted = ($c->request->method eq 'POST'); + my $subscriber = $c->stash->{subscriber}; + my $prov_subscriber = $subscriber->provisioning_voip_subscriber; + + my $params; + my $lock = NGCP::Panel::Utils::Subscriber::get_usr_preference_rs( + c => $c, + attribute => 'lock', + prov_subscriber => $prov_subscriber, + ); + + unless($posted) { + $params->{webusername} = $prov_subscriber->webusername; + $params->{webpassword} = $prov_subscriber->webpassword; + $params->{password} = $prov_subscriber->password; + $params->{administrative} = $prov_subscriber->admin; + + $params->{e164}->{cc} = $subscriber->primary_number->cc; + $params->{e164}->{ac} = $subscriber->primary_number->ac; + $params->{e164}->{sn} = $subscriber->primary_number->sn; + $params->{status} = $subscriber->status; + $params->{external_id} = $subscriber->external_id; + + $params->{lock} = $lock->first ? $lock->first->value : undef; + } + + $form->process( + params => $posted ? $c->request->params : $params + ); + + if($posted && $form->validated) { + my $schema = $c->model('DB'); + try { + $schema->txn_do(sub { + $prov_subscriber->update({ + webusername => $form->field('webusername')->value, + webpassword => $form->field('webpassword')->value, + password => $form->field('password')->value, + admin => $form->field('administrative')->value, + }); + $subscriber->update({ + status => $form->field('status')->value, + external_id => $form->field('external_id')->value, + }); + # TODO: check for availablity of cc and sn + $subscriber->primary_number->update({ + cc => $form->field('e164')->field('cc')->value, + ac => $form->field('e164')->field('ac')->value, + sn => $form->field('e164')->field('sn')->value, + }); + if($lock->first) { + $lock->first->update({ value => $form->field('lock')->value }); + } else { + $lock->create({ value => $form->field('lock')->value }); + } + }); + $c->flash(messages => [{type => 'success', text => 'Successfully updated subscriber'}]); + } catch($e) { + $c->log->error("failed to update subscriber: $e"); + $c->flash(messages => [{type => 'error', text => 'Failed to update subscriber'}]); + } + + $c->response->redirect($c->uri_for_action('/subscriber/details', [$c->req->captures->[0]])); + return; + } + + $c->stash( + edit_flag => 1, + form => $form, + ); + +} + sub ajax_calls :Chained('master') :PathPart('calls/ajax') :Args(0) { my ($self, $c) = @_; diff --git a/lib/NGCP/Panel/Field/SubscriberLockSelect.pm b/lib/NGCP/Panel/Field/SubscriberLockSelect.pm new file mode 100644 index 0000000000..7cd7c9483f --- /dev/null +++ b/lib/NGCP/Panel/Field/SubscriberLockSelect.pm @@ -0,0 +1,19 @@ +package NGCP::Panel::Field::SubscriberLockSelect; +use Moose; +extends 'HTML::FormHandler::Field::Select'; + +sub build_options { + my ($self) = @_; + + return [ + { label => 'none', value => undef }, + { label => 'foreign', value => 1 }, + { label => 'outgoing', value => 2 }, + { label => 'incoming', value => 3 }, + { label => 'global', value => 4 }, + ]; +} + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/Subscriber.pm b/lib/NGCP/Panel/Form/Subscriber.pm index 550e3a8943..89136bdfb9 100644 --- a/lib/NGCP/Panel/Form/Subscriber.pm +++ b/lib/NGCP/Panel/Form/Subscriber.pm @@ -37,7 +37,7 @@ has_field 'webusername' => ( ); has_field 'webpassword' => ( - type => 'Password', + type => 'Text', label => 'Web Password', required => 0, element_attr => { @@ -106,7 +106,7 @@ has_field 'domain' => ( ); has_field 'password' => ( - type => 'Password', + type => 'Text', label => 'SIP Password', required => 1, element_attr => { diff --git a/lib/NGCP/Panel/Form/SubscriberEdit.pm b/lib/NGCP/Panel/Form/SubscriberEdit.pm new file mode 100644 index 0000000000..238f3dab87 --- /dev/null +++ b/lib/NGCP/Panel/Form/SubscriberEdit.pm @@ -0,0 +1,260 @@ +package NGCP::Panel::Form::SubscriberEdit; + +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler'; +use Moose::Util::TypeConstraints; + +use HTML::FormHandler::Widget::Block::Bootstrap; + +use NGCP::Panel::Field::Domain; +use NGCP::Panel::Field::CustomerContract; +use NGCP::Panel::Field::Reseller; + +with 'NGCP::Panel::Render::RepeatableJs'; + +has '+widget_wrapper' => ( default => 'Bootstrap' ); +sub build_render_list {[qw/fields actions/]} +sub build_form_element_class { [qw/form-horizontal/] } + +has_field 'reseller' => ( + type => '+NGCP::Panel::Field::Reseller', + label => 'Reseller', + not_nullable => 1, +); + +has_field 'webusername' => ( + type => 'Text', + label => 'Web Username', + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['The username to log into the CSC Panel'] + }, +); + +has_field 'webpassword' => ( + type => 'Text', + label => 'Web Password', + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['The password to log into the CSC Panel'] + }, +); + +has_field 'e164' => ( + type => 'Compound', + order => 99, + required => 0, + label => 'E164 Number', + do_label => 1, + do_wrapper => 1, +); + +has_field 'e164.cc' => ( + type => 'PosInteger', + element_attr => { + class => ['ngcp_e164_cc'], + rel => ['tooltip'], + title => ['Country Code, e.g. 1 for US or 43 for Austria'] + }, + do_label => 0, + do_wrapper => 0, +); + +has_field 'e164.ac' => ( + type => 'PosInteger', + element_attr => { + class => ['ngcp_e164_ac'], + rel => ['tooltip'], + title => ['Area Code, e.g. 212 for NYC or 1 for Vienna'] + }, + do_label => 0, + do_wrapper => 0, +); + +has_field 'e164.sn' => ( + type => 'PosInteger', + element_attr => { + class => ['ngcp_e164_sn'], + rel => ['tooltip'], + title => ['Subscriber Number, e.g. 12345678'] + }, + do_label => 0, + do_wrapper => 0, +); + +has_field 'alias_number' => ( + type => 'Repeatable', + setup_for_js => 1, + do_wrapper => 1, + do_label => 0, + tags => { + controls_div => 1, + }, + wrapper_class => [qw/hfh-rep/], +); + +has_field 'alias_number.id' => ( + type => 'Hidden', +); + +has_field 'alias_number.e164' => ( + type => 'Compound', + order => 99, + required => 0, + label => 'Alias Number', + do_label => 1, + do_wrapper => 1, + wrapper_class => [qw/hfh-rep-field/], +); + +has_field 'alias_number.e164.cc' => ( + type => 'PosInteger', + element_attr => { + class => ['ngcp_e164_cc'], + rel => ['tooltip'], + title => ['Country Code, e.g. 1 for US or 43 for Austria'] + }, + do_label => 0, + do_wrapper => 0, +); + +has_field 'alias_number.e164.ac' => ( + type => 'PosInteger', + element_attr => { + class => ['ngcp_e164_ac'], + rel => ['tooltip'], + title => ['Area Code, e.g. 212 for NYC or 1 for Vienna'] + }, + do_label => 0, + do_wrapper => 0, +); + +has_field 'alias_number.e164.sn' => ( + type => 'PosInteger', + element_attr => { + class => ['ngcp_e164_sn'], + rel => ['tooltip'], + title => ['Subscriber Number, e.g. 12345678'] + }, + do_label => 0, + do_wrapper => 0, +); + +has_field 'alias_number.rm' => ( + type => 'RmElement', + value => 'Remove', + order => 100, + element_class => [qw/btn btn-primary pull-right/], +); + +has_field 'alias_number_add' => ( + type => 'AddElement', + repeatable => 'alias_number', + value => 'Add another number', + element_class => [qw/btn btn-primary pull-right/], +); + +has_field 'password' => ( + type => 'Text', + label => 'SIP Password', + required => 1, + element_attr => { + rel => ['tooltip'], + title => ['The SIP password for the User-Agents'] + }, +); + +has_field 'lock' => ( + type => '+NGCP::Panel::Field::SubscriberLockSelect', + label => 'Lock Level', + not_nullable => 1, +); + +has_field 'status' => ( + type => '+NGCP::Panel::Field::SubscriberStatusSelect', + label => 'Status', + not_nullable => 1, +); + +has_field 'administrative' => ( + type => 'Boolean', + label => 'Administrative', + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['Subscriber can configure other subscribers within the Customer Account'] + }, +); + + +has_field 'external_id' => ( + type => 'Text', + label => 'External ID', + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['An external id, e.g. provided by a 3rd party provisioning'] + }, +); + + +has_field 'save' => ( + type => 'Submit', + value => 'Save', + element_class => [qw/btn btn-primary/], + label => '', +); + + +has_block 'fields' => ( + tag => 'div', + class => [qw/modal-body/], + render_list => [qw/webusername webpassword e164 alias_number alias_number_add password lock status external_id administrative/ ], +); + +has_block 'actions' => ( + tag => 'div', + class => [qw/modal-footer/], + render_list => [qw/save/], +); + +sub validate { + my $self = shift; + my $cc = $self->field('e164.cc')->value; + my $sn = $self->field('e164.sn')->value; + + if (defined $cc && $cc ne '' && (!defined $sn || $sn eq '')) { + my $err_msg = 'Subscriber Number required if Country Code is set'; + $self->field('e164')->add_error($err_msg); + } elsif(defined $sn && $sn ne '' && (!defined $cc || $cc eq '')) { + my $err_msg = 'Country Code required if Subscriber Number is set'; + $self->field('e164')->add_error($err_msg); + } +} + +1; + +=head1 NAME + +NGCP::Panel::Form::Subscriber + +=head1 DESCRIPTION + +Form to modify a subscriber. + +=head1 METHODS + +=head1 AUTHOR + +Gerhard Jungwirth + +=head1 LICENSE + +This library is free software. You can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +# vim: set tabstop=4 expandtab: diff --git a/share/templates/customer/details.tt b/share/templates/customer/details.tt index d33debd33f..3f33632794 100644 --- a/share/templates/customer/details.tt +++ b/share/templates/customer/details.tt @@ -55,9 +55,7 @@

Subscribers

-
- Create Subscriber -
+ Create Subscriber
diff --git a/share/templates/subscriber/master.tt b/share/templates/subscriber/master.tt index 4fc76622fd..263a0dfdea 100644 --- a/share/templates/subscriber/master.tt +++ b/share/templates/subscriber/master.tt @@ -25,104 +25,41 @@
+ Edit +
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + [% + elements = [ + { value = subscriber.provisioning_voip_subscriber.webusername, desc = 'CSC Username'}, + { value = subscriber.provisioning_voip_subscriber.webpassword, desc = 'CSC Password'}, + { value = subscriber.username _ '@' _ subscriber.domain.domain, desc = 'SIP URI'}, + { value = subscriber.primary_number.cc _ ' ' _ subscriber.primary_number.ac _ ' ' _ subscriber.primary_number.sn, desc = 'Primary Number'}, + { value = 'TODO', desc = 'Alias Numbers'}, + { value = subscriber.provisioning_voip_subscriber.admin ? 'yes' : 'no', desc = 'Administrative'}, + { value = subscriber.external_id, desc = 'External #'}, + ] + -%] + [% FOR elem IN elements -%] + + + + + [% END -%] + - - - - - -
Setting Values
Customer # [% subscriber.contract.id %] - -
CSC Username[% subscriber.webusername %] - -
CSC Password[% subscriber.webpassword%] - -
SIP URI[% subscriber.username %]@[% subscriber.domain.domain %] - -
SIP Password[% subscriber.password %] - -
Primary Number[% subscriber.primary_number.cc %] [% subscriber.primary_number.ac %] [% subscriber.primary_number.sn %] - -
Alias Numbers[% subscriber.primary_number.cc %] [% subscriber.primary_number.ac %] [% subscriber.primary_number.sn %] - -
Administrative[% subscriber.administrative ? 'yes' : 'no' %] - -
[% elem.desc %][% elem.value %]
UUID [% subscriber.uuid %] - -
External #[% subscriber.external_id %] - -
@@ -189,6 +126,9 @@ [% IF edit_flag == 1 -%] [% + IF form.has_for_js; + form.render_repeatable_js; + END; PROCESS "helpers/modal.tt"; modal_header(m.create_flag=0, m.name = "Subscriber Master Data");