diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index 07534b042e..c150550ff9 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -19,6 +19,7 @@ use NGCP::Panel::Utils::Kamailio; use NGCP::Panel::Utils::Events; use NGCP::Panel::Form::Subscriber; use NGCP::Panel::Form::SubscriberEdit; +use NGCP::Panel::Form::CCMapEntries; use NGCP::Panel::Form::Customer::PbxSubscriberEdit; use NGCP::Panel::Form::Customer::PbxExtensionSubscriberEditSubadmin; use NGCP::Panel::Form::Customer::PbxExtensionSubscriberEditSubadminNoGroup; @@ -271,6 +272,12 @@ sub base :Chained('sub_list') :PathPart('') :CaptureArgs(1) { { name => "pages", search => 1, title => $c->loc('Pages') }, ]); + $c->stash->{ccmap_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [ + { name => "id", search => 1, title => $c->loc('#') }, + { name => "auth_key", search => 1, title => $c->loc('CLI') }, + { name => "source_uuid", search => 1, title => $c->loc('Source UUID') }, + ]); + if($c->stash->{billing_mapping}->product->class eq "pbxaccount") { $c->stash->{pbx_groups} = $c->model('DB')->resultset('voip_subscribers')->search({ contract_id => $c->stash->{subscriber}->contract->id, @@ -3493,6 +3500,119 @@ sub edit_autoattendant :Chained('base') :PathPart('preferences/speeddial/edit') ); } +sub ajax_ccmappings :Chained('base') :PathPart('preferences/ccmappings/ajax') :Args(0) { + my ($self, $c) = @_; + + my $prov_subscriber = $c->stash->{subscriber}->provisioning_voip_subscriber; + my $aa_rs = $prov_subscriber->voip_cc_mappings; + NGCP::Panel::Utils::Datatables::process($c, $aa_rs, $c->stash->{ccmap_dt_columns}); + + $c->detach( $c->view("JSON") ); + return; +} + +sub ccmappings :Chained('base') :PathPart('preferences/ccmappings') :CaptureArgs(1) { + my ($self, $c, $aa_id) = @_; + + my $ccmapping = $c->stash->{subscriber}->provisioning_voip_subscriber->voip_cc_mappings + ->find($aa_id); + unless($ccmapping) { + NGCP::Panel::Utils::Message->error( + c => $c, + log => "no such ccmapping with id '$aa_id' for uuid ".$c->stash->{subscriber}->uuid, + desc => $c->loc('No such auto ccmapping id.'), + ); + NGCP::Panel::Utils::Navigation::back_or($c, + $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]])); + } + $c->stash->{ccmapping} = $ccmapping; + return; +} + +sub delete_ccmapping :Chained('ccmappings') :PathPart('delete') :Args(0) { + my ($self, $c) = @_; + + $c->detach('/denied_page') + if(($c->user->roles eq "admin" || $c->user->roles eq "reseller") && $c->user->read_only); + + try { + $c->stash->{ccmapping}->delete; + $c->flash(messages => [{type => 'success', text => $c->loc('Successfully deleted ccmapping.') }]); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to delete ccmapping.'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, + $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]])); + return; +} + +sub edit_ccmapping :Chained('base') :PathPart('preferences/ccmappings/edit') :Args(0) { + my ($self, $c) = @_; + + $c->detach('/denied_page') + if(($c->user->roles eq "admin" || $c->user->roles eq "reseller") && $c->user->read_only); + + my $posted = ($c->request->method eq 'POST'); + my $prov_subscriber = $c->stash->{subscriber}->provisioning_voip_subscriber; + my $ccmappings = $prov_subscriber->voip_cc_mappings; + my $form = NGCP::Panel::Form::CCMapEntries->new; + + my $params = {}; + unless($posted) { + $params->{mappings} = []; + foreach my $mapping ($ccmappings->all) { + push @{ $params->{mappings} }, { $mapping->get_inflated_columns }; + } + } + + $form->process( + posted => $posted, + params => $c->req->params, + item => $params, + ); + NGCP::Panel::Utils::Navigation::check_form_buttons( + c => $c, + form => $form, + fields => {}, + back_uri => $c->req->uri, + ); + if($posted && $form->validated) { + try { + my $schema = $c->model('DB'); + $schema->txn_do(sub { + $ccmappings->delete_all; + my @fields = $form->field('mappings')->fields; + foreach my $map (@fields) { + $ccmappings->create({ + source_uuid => $map->field('source_uuid')->value || $prov_subscriber->uuid, + auth_key => $map->field('auth_key')->value, + }); + } + }); + $c->flash(messages => [{type => 'success', text => $c->loc('Successfully updated ccmappings.')}]); + } catch($e) { + NGCP::Panel::Utils::Message->error( + c => $c, + error => $e, + desc => $c->loc('Failed to update ccmappings'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, + $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]])); + } + + $c->stash( + template => 'subscriber/preferences.tt', + edit_ccmap_flag => 1, + ccmap_form => $form, + ); + return; +} + sub callflow_base :Chained('base') :PathPart('callflow') :CaptureArgs(1) :Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) { my ($self, $c, $callid) = @_; diff --git a/lib/NGCP/Panel/Form/CCMapEntries.pm b/lib/NGCP/Panel/Form/CCMapEntries.pm new file mode 100644 index 0000000000..ebd0157115 --- /dev/null +++ b/lib/NGCP/Panel/Form/CCMapEntries.pm @@ -0,0 +1,90 @@ +package NGCP::Panel::Form::CCMapEntries; + +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler'; +use Moose::Util::TypeConstraints; + + +with 'NGCP::Panel::Render::RepeatableJs'; + +use HTML::FormHandler::Widget::Block::Bootstrap; + +has '+widget_wrapper' => ( default => 'Bootstrap' ); +has_field 'submitid' => ( type => 'Hidden' ); +sub build_render_list {return [qw/submitid fields actions/]} +sub build_form_element_class {return [qw/form-horizontal/] } + +has_field 'subscriber_id' => ( + type => 'Hidden', + required => 0, + element_attr => { + rel => ['tooltip'], + title => ['The subscriber ID.'], + }, +); + +has_field 'mappings' => ( + type => 'Repeatable', + required => 1, + setup_for_js => 1, + do_wrapper => 1, + do_label => 0, + tags => { + controls_div => 1, + }, + wrapper_class => [qw/hfh-rep/], + element_attr => { + rel => ['tooltip'], + title => ['An Array of mappings, each entry containing the mandatory key "auth_key".'], + }, +); + +has_field 'mappings.id' => ( + type => 'Hidden', +); + +has_field 'mappings.source_uuid' => ( + type => 'Hidden', +); + +has_field 'mappings.auth_key' => ( + type => 'Text', + wrapper_class => [qw/hfh-rep-field/], +); + +has_field 'mappings.rm' => ( + type => 'RmElement', + value => 'Remove', + element_class => [qw/btn btn-primary pull-right/], +); + + +has_field 'mappings_add' => ( + type => 'AddElement', + repeatable => 'mappings', + value => 'Add another Mapping', + element_class => [qw/btn btn-primary pull-right/], +); + +has_block 'fields' => ( + tag => 'div', + class => [qw(modal-body)], + render_list => [qw(mappings mappings_add)], +); + +has_block 'actions' => ( + tag => 'div', + class => [qw/modal-footer/], + render_list => [qw/save/], +); + +has_field 'save' => ( + type => 'Submit', + value => 'Save', + element_class => [qw/btn btn-primary/], + label => '', +); + +1; + +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Form/CCMapEntriesAPI.pm b/lib/NGCP/Panel/Form/CCMapEntriesAPI.pm deleted file mode 100644 index f29012893a..0000000000 --- a/lib/NGCP/Panel/Form/CCMapEntriesAPI.pm +++ /dev/null @@ -1,39 +0,0 @@ -package NGCP::Panel::Form::CCMapEntriesAPI; -use HTML::FormHandler::Moose; -use Moose::Util::TypeConstraints; -extends 'HTML::FormHandler'; - -sub build_render_list {return [qw/fields actions/]} -sub build_form_element_class {return [qw(form-horizontal)]} - -has_field 'subscriber_id' => ( - type => 'Repeatable', - required => 0, - element_attr => { - rel => ['tooltip'], - title => ['The subscriber ID.'], - }, -); - -has_field 'mappings' => ( - type => 'Repeatable', - required => 1, - element_attr => { - rel => ['tooltip'], - title => ['An Array of mappings, each entry containing the mandatory key "auth_key".'], - }, -); - -has_field 'mappings.auth_key' => ( - type => 'Text', -); - -has_block 'fields' => ( - tag => 'div', - class => [qw(modal-body)], - render_list => [qw(cfu cfb cft cfna)], -); - -1; - -# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Role/API/CCMapEntries.pm b/lib/NGCP/Panel/Role/API/CCMapEntries.pm index 721fbfde53..5fcf6617fb 100644 --- a/lib/NGCP/Panel/Role/API/CCMapEntries.pm +++ b/lib/NGCP/Panel/Role/API/CCMapEntries.pm @@ -14,12 +14,12 @@ use HTTP::Status qw(:constants); use JSON::Types; use NGCP::Panel::Form::CFSimpleAPI; use NGCP::Panel::Utils::Subscriber; -use NGCP::Panel::Form::CCMapEntriesAPI; +use NGCP::Panel::Form::CCMapEntries; sub get_form { my ($self, $c) = @_; - return NGCP::Panel::Form::CCMapEntriesAPI->new; + return NGCP::Panel::Form::CCMapEntries->new; } sub hal_from_item { diff --git a/ngcp_panel.conf b/ngcp_panel.conf index 231b5f6cdc..a893dcca31 100644 --- a/ngcp_panel.conf +++ b/ngcp_panel.conf @@ -19,14 +19,16 @@ log4perl.appender.Default.layout.ConversionPattern=%d{ISO8601} [%p] [%F +%L] %m{ - callflow 1 - multidomain 1 + callflow 1 + multidomain 1 - faxserver 1 - conference 1 + faxserver 1 + conference 1 - debug 1 - cloudpbx 1 + debug 1 + cloudpbx 1 + + callingcard 1 diff --git a/share/templates/subscriber/preferences.tt b/share/templates/subscriber/preferences.tt index 9fc1800868..1b80a74a47 100644 --- a/share/templates/subscriber/preferences.tt +++ b/share/templates/subscriber/preferences.tt @@ -486,6 +486,42 @@ [% END -%] + [% IF c.config.features.callingcard && ( + c.user.roles == "admin" || c.user.roles == "reseller" + ) -%] +
+ +
+
+ + [% IF c.user.roles == "subscriber" || c.user.roles == "subscriberadmin" || ((c.user.roles == "admin" || c.user.roles == "reseller") && c.user.read_only != 1) -%] + + [% c.loc('Edit Callthrough CLIs') %] + + [% END -%] +[% + helper.messages = ''; + helper.name = c.loc('foobar'); + helper.identifier = 'ccmapentries'; + helper.column_sort = undef; + helper.dt_columns = ccmap_dt_columns; + helper.length_change = 1; + helper.ajax_uri = c.uri_for_action('/subscriber/ajax_ccmappings', [c.req.captures.0]); + + helper.dt_buttons = [ + { name = c.loc('Delete'), uri = "/subscriber/"_ c.req.captures.0 _"/preferences/ccmappings/'+full.id+'/delete", class = 'btn-small btn-secondary', icon = 'icon-trash' }, + ]; + + PROCESS 'helpers/datatables.tt'; +%] +
+
+
+ [% END -%] + + [% UNLESS c.user.roles == 'subscriber' || c.user.roles == 'subscriberadmin' -%]
@@ -659,6 +695,21 @@ [% END -%]
+[% + modal_footer(); + modal_script(m.close_target = close_target ? close_target : c.uri_for_action('/subscriber/preferences', [c.req.captures.0])); +-%] +[% ELSIF edit_ccmap_flag -%] +[% + PROCESS "helpers/modal.tt"; + modal_header(m.name = 'Callthrough CLIs'); +-%] + + [% IF ccmap_form.has_for_js -%] + [% ccmap_form.render_repeatable_js %] + [% END -%] + [% translate_form(ccmap_form).render %] + [% modal_footer(); modal_script(m.close_target = close_target ? close_target : c.uri_for_action('/subscriber/preferences', [c.req.captures.0]));