From 1fb0e8a32d1ecbb4be5088df1bedb3e5a6d8ee03 Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Thu, 17 May 2018 11:03:11 +0200 Subject: [PATCH] TT#36055 Add field device preferences web UI Change-Id: I64bdc413d1dbe6690c742bc8732fb41ec2ee2c7f --- lib/NGCP/Panel/Controller/Customer.pm | 105 ++++++++++ lib/NGCP/Panel/Controller/Device.pm | 144 +++++++++++++- lib/NGCP/Panel/Form/Device/Preference.pm | 184 ++++++++++++++++++ lib/NGCP/Panel/Utils/Generic.pm | 3 +- lib/NGCP/Panel/Utils/I18N.pm | 4 +- lib/NGCP/Panel/Utils/Preferences.pm | 150 +++++++++++++- share/templates/customer/details.tt | 3 + .../customer/pbx_fdev_preferences.tt | 23 +++ share/templates/device/preferences.tt | 7 + share/templates/helpers/pref_table.tt | 25 ++- 10 files changed, 635 insertions(+), 13 deletions(-) create mode 100644 lib/NGCP/Panel/Form/Device/Preference.pm create mode 100644 share/templates/customer/pbx_fdev_preferences.tt diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index 12423b7f41..89a98a5eb4 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -1778,6 +1778,7 @@ sub pbx_device_edit :Chained('pbx_device_base') :PathPart('edit') :Args(0) { description => $c->loc('PBX Device'), ); } + sub pbx_device_lines_update :Private{ my($self, $c, $schema, $fdev, $lines) = @_; my $err = 0; @@ -1812,6 +1813,7 @@ sub pbx_device_lines_update :Private{ } return $err; } + sub pbx_device_delete :Chained('pbx_device_base') :PathPart('delete') :Args(0) { my ($self, $c) = @_; @@ -1989,6 +1991,109 @@ sub pbx_device_sync :Chained('pbx_device_base') :PathPart('sync') :Args(0) { ); } +sub pbx_device_preferences_list :Chained('pbx_device_base') :PathPart('preferences') :CaptureArgs(0) { + my ($self, $c) = @_; + my $fdev = $c->stash->{pbx_device}; + my $devmod = $fdev->profile->config->device; + $c->stash->{devmod} = $devmod; + my $dev_pref_rs = NGCP::Panel::Utils::Preferences::get_preferences_rs( + c => $c, + type => 'fielddev', + id => $fdev->id, + ); + + my $pref_values = get_inflated_columns_all($dev_pref_rs,'hash' => 'attribute', 'column' => 'value', 'force_array' => 1); + + NGCP::Panel::Utils::Preferences::load_preference_list( + c => $c, + pref_values => $pref_values, + fielddev_pref => 1, + search_conditions => [{ + 'attribute' => + [ -or => + { 'like' => 'vnd_'.lc($devmod->vendor).'%' }, + {'-not_like' => 'vnd_%' }, + ], + #relation type is defined by preference flag dev_pref, + #so here we select only linked to the current model, or not linked to any model at all + '-or' => [ + 'voip_preference_relations.autoprov_device_id' => $devmod->id, + 'voip_preference_relations.reseller_id' => $devmod->reseller_id, + 'voip_preference_relations.voip_preference_id' => undef + ], + },{ + join => {'voip_preferences' => 'voip_preference_relations'}, + } + ] + ); + + $c->stash(template => 'customer/pbx_fdev_preferences.tt'); + return; +} + +sub pbx_device_preferences_root :Chained('pbx_device_preferences_list') :PathPart('') :Args(0) { + return; +} + +sub pbx_device_preferences_base :Chained('pbx_device_preferences_list') :PathPart('') :CaptureArgs(1) { + my ($self, $c, $pref_id) = @_; + + $c->stash->{preference_meta} = $c->model('DB') + ->resultset('voip_preferences') + ->search({ + -or => [ + 'voip_preferences_enums.fielddev_pref' => 1, + 'voip_preferences_enums.fielddev_pref' => undef + ], + },{ + prefetch => 'voip_preferences_enums', + }) + ->find({id => $pref_id}); + + $c->stash->{preference} = $c->model('DB') + ->resultset('voip_fielddev_preferences') + ->search({ + 'attribute_id' => $pref_id, + 'device_id' => $c->stash->{pbx_device}->id, + }); + return; +} + +sub pbx_device_preferences_edit :Chained('pbx_device_preferences_base') :PathPart('edit') :Args(0) { + my ($self, $c) = @_; + + $c->stash(edit_preference => 1); + + my @enums = $c->stash->{preference_meta} + ->voip_preferences_enums + ->all; + my $devmod = $c->stash->{devmod}; + my $pref_rs = $c->stash->{pbx_device}->voip_fielddev_preferences->search_rs({ + 'attribute' => + [ -or => + { 'like' => 'vnd_'.lc($devmod->vendor).'%' }, + {'-not_like' => 'vnd_%' }, + ], + #relation type is defined by preference flag dev_pref, + #so here we select only linked to the current model, or not linked to any model at all + '-or' => [ + 'voip_preference_relations.autoprov_device_id' => $devmod->id, + 'voip_preference_relations.reseller_id' => $devmod->reseller_id, + 'voip_preference_relations.voip_preference_id' => undef + ], + },{ + join => {'attribute' => 'voip_preference_relations'}, + }); + NGCP::Panel::Utils::Preferences::create_preference_form( + c => $c, + pref_rs => $pref_rs, + enums => \@enums, + base_uri => $c->uri_for_action('/customer/pbx_device_preferences_root', [@{ $c->req->captures }[0,1]] ), + edit_uri => $c->uri_for_action('/customer/pbx_device_preferences_edit', $c->req->captures ), + ); + return; +} + sub location_ajax :Chained('base') :PathPart('location/ajax') :Args(0) { my ($self, $c) = @_; NGCP::Panel::Utils::Datatables::process($c, diff --git a/lib/NGCP/Panel/Controller/Device.pm b/lib/NGCP/Panel/Controller/Device.pm index 766a80482c..eee2da85bd 100644 --- a/lib/NGCP/Panel/Controller/Device.pm +++ b/lib/NGCP/Panel/Controller/Device.pm @@ -13,6 +13,7 @@ use NGCP::Panel::Utils::DeviceBootstrap; use NGCP::Panel::Utils::Device; use NGCP::Panel::Utils::DeviceFirmware; use NGCP::Panel::Utils::DateTime; +use NGCP::Panel::Utils::Preferences; use DateTime::Format::HTTP; use parent 'Catalyst::Controller'; @@ -260,6 +261,7 @@ sub devmod_create :Chained('base') :PathPart('model/create') :Args(0) :Does(ACL) form => $form, ); } + sub prepare_connectable :Private{ my ($self, $c, $model) = @_; my $values = []; @@ -1397,6 +1399,7 @@ sub dev_field_config :Chained('/') :PathPart('device/autoprov/config') :Args() { $c->response->content_type($result->{content_type}); $c->response->body(${$result->{content}}); } + sub dev_field_encrypt :Private{ my ($self, $c, $dev, $processed_data, $vars) = @_; @@ -1932,7 +1935,6 @@ sub dev_field_firmware_latest :Chained('dev_field_firmware_version_base') :PathP )); } - sub devices_preferences_list :Chained('devmod_base') :PathPart('preferences') :CaptureArgs(0) { my ($self, $c) = @_; @@ -1947,13 +1949,25 @@ sub devices_preferences_list :Chained('devmod_base') :PathPart('preferences') :C NGCP::Panel::Utils::Preferences::load_preference_list( c => $c, pref_values => $pref_values, + #we don't need fielddev_pref flag, because it always will be just more narrow than dev_pref. dev_pref => 1, - search_conditions => { + search_conditions => [{ + 'attribute' => + [ -or => + { 'like' => 'vnd_'.lc($c->stash->{devmod}->vendor).'%' }, + {'-not_like' => 'vnd_%' }, + ], + #relation type is defined by preference flag dev_pref, + #so here we select only linked to the current model, or not linked to any model at all '-or' => [ - {'attribute' => {'like' => 'vnd_'.lc($c->stash->{devmod}->vendor).'%' } }, - {'attribute' => {'-not_like' => 'vnd_%' }} - ], - } + 'voip_preference_relations.autoprov_device_id' => $c->stash->{devmod}->id, + 'voip_preference_relations.reseller_id' => $c->stash->{devmod}->reseller_id, + 'voip_preference_relations.voip_preference_id' => undef + ], + },{ + join => {'voip_preferences' => 'voip_preference_relations'}, + } + ] ); $c->stash(template => 'device/preferences.tt'); @@ -1986,6 +2000,124 @@ sub devices_preferences_base :Chained('devices_preferences_list') :PathPart('') return; } +sub devices_preferences_create :Chained('devices_preferences_list') :PathPart('create') :Args(0) { + my ($self, $c) = @_; + my $posted = ($c->request->method eq 'POST'); + my $form = NGCP::Panel::Form::get("NGCP::Panel::Form::Device::Preference", $c); + + my $params = {}; + $params = merge($params, $c->session->{created_objects}); + $form->process( + posted => $posted, + params => $c->request->params, + item => $params + ); + + if($posted && $form->validated) { + try { + my $schema = $c->model('DB'); + $schema->txn_do(sub { + my $resource = $form->values; + + $resource->{dev_pref} = 1; + $resource->{autoprov_device_id} = $c->stash->{devmod}->id; + + my $preference = NGCP::Panel::Utils::Preferences::create_dynamic_preference( + $c, $resource, + group_name => 'CPBX Device Administration', + ); + + $c->session->{created_objects}->{preference} = { id => $preference->id }; + }); + NGCP::Panel::Utils::Message::info( + c => $c, + desc => $c->loc('Successfully created device model preference'), + ); + } catch($e) { + NGCP::Panel::Utils::Message::error( + c => $c, + error => $e, + desc => $c->loc('Failed to create device model preference'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/device')); + } + $c->stash( + create_flag => 1, + form => $form, + ); +} + +sub devices_preferences_editmeta :Chained('devices_preferences_base') :PathPart('editmeta') :Args(0) { + my ($self, $c) = @_; + my $posted = ($c->request->method eq 'POST'); + my $form = NGCP::Panel::Form::get("NGCP::Panel::Form::Device::Preference", $c); + + my $params = { $c->stash->{preference_meta}->get_inflated_columns }; + $params->{enum} = [ map { {$_->get_inflated_columns} } $c->stash->{preference_meta}->voip_preferences_enums->all ]; + $params = merge($params, $c->session->{created_objects}); + $form->process( + posted => $posted, + params => $c->request->params, + item => $params + ); + + if($posted && $form->validated) { + try { + my $schema = $c->model('DB'); + $schema->txn_do(sub { + my $resource = $form->values; + + $resource->{dev_pref} = 1; + $resource->{autoprov_device_id} = $c->stash->{devmod}->id; + + NGCP::Panel::Utils::Preferences::update_dynamic_preference( + $c, $c->stash->{preference_meta}, $resource + ); + }); + NGCP::Panel::Utils::Message::info( + c => $c, + desc => $c->loc('Successfully updated device model preference'), + ); + } catch($e) { + NGCP::Panel::Utils::Message::error( + c => $c, + error => $e, + desc => $c->loc('Failed to update device model preference'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/device')); + } + $c->stash( + editmeta_flag => 1, + form => $form, + ); +} + +sub devices_preferences_delete :Chained('devices_preferences_base') :PathPart('delete') :Args(0):Does(ACL) :ACLDetachTo('/denied_page') :AllowedRole(admin) :AllowedRole(reseller) { + my ($self, $c) = @_; + + try { + NGCP::Panel::Utils::Preferences::delete_dynamic_preference( + $c, $c->stash->{preference_meta} + ); + NGCP::Panel::Utils::Message::info( + c => $c, + data => { id => $c->stash->{preference_meta}->id, + attribute => $c->stash->{preference_meta}->attribute }, + desc => $c->loc('Device model preference successfully deleted'), + ); + } catch($e) { + NGCP::Panel::Utils::Message::error( + c => $c, + error => "failed to delete device model preference with id '".$c->stash->{preference_meta}->id."': $e", + desc => $c->loc('Failed to delete device model preference'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/device')); +} + + sub devices_preferences_edit :Chained('devices_preferences_base') :PathPart('edit') :Args(0) { my ($self, $c) = @_; diff --git a/lib/NGCP/Panel/Form/Device/Preference.pm b/lib/NGCP/Panel/Form/Device/Preference.pm new file mode 100644 index 0000000000..fb8ab8483d --- /dev/null +++ b/lib/NGCP/Panel/Form/Device/Preference.pm @@ -0,0 +1,184 @@ +package NGCP::Panel::Form::Device::Preference; + +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler'; +with 'NGCP::Panel::Render::RepeatableJs'; + +use HTML::FormHandler::Widget::Block::Bootstrap; +use NGCP::Panel::Utils::Preferences; + +has '+widget_wrapper' => ( default => 'Bootstrap' ); +sub build_render_list {[qw/fields actions/]} +sub build_form_element_class {[qw(form-horizontal)]} + +has_field 'id' => ( + type => 'Hidden' +); + +has_field 'attribute' => ( + type => 'Text', + required => 1, + label => 'Name', + maxlength => '31', + element_attr => { + rel => ['tooltip'], + title => ['Name will be prefixed with double underscore.'] + }, +); + +has_field 'label' => ( + type => 'Text', + maxlength => '255', + required => 1, + label => 'Label', +); + +has_field 'description' => ( + type => 'Text', + required => 1, + label => 'Description', +); + +has_field 'fielddev_pref' => ( + type => 'Boolean', + default => 1, + label => 'Override on deployed device', +); + +has_field 'max_occur' => ( + type => 'Boolean', + default => '0', + label => 'Only one is allowed.', +); + +has_field 'data_type' => ( + type => 'Select', + required => 1, + label => 'Data type', + options => [ + { value => '', label => 'none' }, + { value => 'boolean', label => 'Boolean' }, + { value => 'string', label => 'String' }, + { value => 'int', label => 'Integer' }, + { value => 'enum', label => 'Enum' }, + ], + element_attr => { + rel => ['tooltip'], + title => ['Preference data type.'] + }, +); + +has_field 'enum' => ( + type => 'Repeatable', + required => 0, + setup_for_js => 1, + num_when_empty => 0, + do_wrapper => 1, + do_label => 1, + label => 'Enum values', + tags => { + controls_div => 1, + }, + wrapper_class => [qw/hfh-rep-block/], + element_attr => { + rel => ['tooltip'], + title => ['An Array of enum.'], + }, +); + +has_field 'enum.id' => ( + type => 'Hidden', +); + +has_field 'enum.label' => ( + type => 'Text', + wrapper_class => [qw/hfh-rep-field/], +); + +has_field 'enum.value' => ( + type => 'Text', + wrapper_class => [qw/hfh-rep-field/], + element_attr => { + rel => ['tooltip'], + title => ['Name will be prefixed with double underscore.'] + }, +); + +has_field 'enum.default_val' => ( + type => 'Boolean', + wrapper_class => [qw/hfh-rep-field/], +); + +has_field 'enum.rm' => ( + type => 'RmElement', + value => 'Remove enum value description', + order => 100, + element_class => [qw/btn btn-primary pull-right/], +); + +has_field 'enum_add' => ( + type => 'AddElement', + repeatable => 'enum', + value => 'Add enum value', + element_class => [qw/btn btn-primary pull-right/], +); + +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/id attribute fielddev_pref label max_occur description data_type enum enum_add/], +); + +has_block 'actions' => ( + tag => 'div', + class => [qw/modal-footer/], + render_list => [qw/save/], +); + +sub validate_attribute { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + unless ($field->value =~ /^[a-z0-9_]+$/) { + my $err_msg = 'Only lower-case, digits and _ allowed'; + $field->add_error($err_msg); + } + my $existing = $c->model('DB')->resultset('voip_preferences')->search_rs({ + attribute => NGCP::Panel::Utils::Preferences::dynamic_pref_attribute_to_db($field->value) + })->first; + + #TODO: make it common, but what package to use? + my $edit_id = -1; + if (uc($c->req->method) eq 'PUT' || uc($c->req->method) eq 'PATCH') { + $edit_id = $c->req->args->[0]; + } elsif($self->form->field('id')) { + $edit_id = $self->form->field('id')->value + } + #/TODO + + if ( $existing && $existing->id != $edit_id ) { + my $err_msg = 'This dynamic attribute already exists.'; + $field->add_error($err_msg); + } +} + +sub validate_enum_value { + my ($self, $field) = @_; + my $c = $self->form->ctx; + return unless $c; + + unless($field->value =~ /^[a-z0-9_]+$/) { + my $err_msg = 'Only lower-case, digits and _ allowed'; + $field->add_error($err_msg); + } +} +1; +# vim: set tabstop=4 expandtab: diff --git a/lib/NGCP/Panel/Utils/Generic.pm b/lib/NGCP/Panel/Utils/Generic.pm index fa773db12c..74576c85e9 100644 --- a/lib/NGCP/Panel/Utils/Generic.pm +++ b/lib/NGCP/Panel/Utils/Generic.pm @@ -94,7 +94,8 @@ sub get_inflated_columns_all{ $hash->{$key} = $value; } }; - my $hashvalue_column = $params{column}; foreach($rs->all){ + my $hashvalue_column = $params{column}; + foreach($rs->all){ $register_value->(\%lres,$_->{$hashkey_column}, $hashvalue_column ? $_->{$hashvalue_column} : $_); } $res = \%lres; diff --git a/lib/NGCP/Panel/Utils/I18N.pm b/lib/NGCP/Panel/Utils/I18N.pm index f0dc8ac77a..488f7e0327 100644 --- a/lib/NGCP/Panel/Utils/I18N.pm +++ b/lib/NGCP/Panel/Utils/I18N.pm @@ -16,8 +16,10 @@ sub _translate_fields_recursive { my @strings = (); for my $field (@$fields) { if ($field->label) { + my $label = $field->label; push @strings, $field->label if $extract_strings; - $field->label( $c->loc($field->label) ); + $label =~s/^_+//; + $field->label( $c->loc($label) ); } if ($field->isa('HTML::FormHandler::Field::Submit') || $field->isa('HTML::FormHandler::Field::Button')) { diff --git a/lib/NGCP/Panel/Utils/Preferences.pm b/lib/NGCP/Panel/Utils/Preferences.pm index 3f22f09df4..aabad02c41 100644 --- a/lib/NGCP/Panel/Utils/Preferences.pm +++ b/lib/NGCP/Panel/Utils/Preferences.pm @@ -7,6 +7,8 @@ use NGCP::Panel::Form::Preferences; use NGCP::Panel::Utils::Generic qw(:all); use NGCP::Panel::Utils::Sems; +use constant _DYNAMIC_PREFERENCE_PREFIX => '__'; + sub validate_ipnet { my ($field) = @_; my ($ip, $net) = split /\//, $field->value; @@ -38,6 +40,7 @@ sub load_preference_list { my $dom_pref = $params{dom_pref}; my $dev_pref = $params{dev_pref}; my $devprof_pref = $params{devprof_pref}; + my $fielddev_pref = $params{fielddev_pref}; my $prof_pref = $params{prof_pref}; my $usr_pref = $params{usr_pref}; my $contract_pref = $params{contract_pref}; @@ -70,6 +73,9 @@ sub load_preference_list { $devprof_pref ? ('voip_preferences.devprof_pref' => 1, -or => ['voip_preferences_enums.devprof_pref' => 1, 'voip_preferences_enums.devprof_pref' => undef]) : (), + $fielddev_pref ? ('voip_preferences.fielddev_pref' => 1, + -or => ['voip_preferences_enums.fielddev_pref' => 1, + 'voip_preferences_enums.fielddev_pref' => undef]) : (), $prof_pref ? ('voip_preferences.prof_pref' => 1, -or => ['voip_preferences_enums.prof_pref' => 1, 'voip_preferences_enums.prof_pref' => undef]) : (), @@ -88,7 +94,7 @@ sub load_preference_list { }); } if($search_conditions) { - if('ARRAY' eq $search_conditions){ + if('ARRAY' eq ref $search_conditions){ $pref_rs = $pref_rs->search(@$search_conditions); }else{ $pref_rs = $pref_rs->search($search_conditions); @@ -409,12 +415,19 @@ sub create_preference_form { device_vendor => $c->stash->{devprof}->{config_id}, device_model => $c->stash->{devprof}->{name}, ); + } elsif ($c->stash->{pbx_device}) { + %log_data = ( %log_data, + type => 'fielddev', + device_id => $c->stash->{pbx_device}->{id}, + device_vendor => $c->stash->{pbx_device}->{profile_id}, + device_model => $c->stash->{pbx_device}->{identifier}, + ); } if($posted && $form->validated) { - my $preference_id = $c->stash->{preference}->first ? $c->stash->{preference}->first->id : undef; - my $attribute = $c->stash->{preference_meta}->attribute; - if ($attribute eq "allowed_ips") { + my $preference_id = $c->stash->{preference}->first ? $c->stash->{preference}->first->id : undef; + my $attribute = $c->stash->{preference_meta}->attribute; + if ($attribute eq "allowed_ips") { unless(validate_ipnet($form->field($attribute))) { goto OUT; } @@ -922,6 +935,7 @@ sub get_preferences_rs { 'peer' => [qw/voip_peer_preferences peer_pref peer_host_id/], 'dev' => [qw/voip_dev_preferences dev_pref device_id/], 'devprof' => [qw/voip_devprof_preferences devprof_pref profile_id/], + 'fielddev' => [qw/voip_fielddev_preferences fielddev_pref device_id/], 'contract' => [qw/voip_contract_preferences contract_pref contract_id/], 'contract_location' => [qw/voip_contract_preferences contract_location_pref location_id/], ); @@ -1028,6 +1042,7 @@ sub get_peer_preference_rs { peer_host_id => $host->id, }); } + sub get_dev_preference_rs { my %params = @_; @@ -1043,6 +1058,7 @@ sub get_dev_preference_rs { device_id => $device->id, }); } + sub get_devprof_preference_rs { my %params = @_; @@ -1058,6 +1074,23 @@ sub get_devprof_preference_rs { profile_id => $profile->id, }); } + +sub get_fielddev_preference_rs { + my %params = @_; + + my $c = $params{c}; + my $attribute = $params{attribute}; + my $device = $params{device}; + + my $preference = $c->model('DB')->resultset('voip_preferences')->find({ + attribute => $attribute, 'fielddev_pref' => 1, + }); + return unless($preference); + return $preference->voip_fielddev_preferences->search_rs({ + device_id => $device->id, + }); +} + sub get_contract_preference_rs { my %params = @_; @@ -1252,6 +1285,115 @@ sub get_subscriber_allowed_prefs { return \%allowed_prefs } +sub create_dynamic_preference { + my ($c, $resource, %params) = @_; + + my $group_name = $params{group_name}; + my $relations = {}; + + $resource->{voip_preference_groups_id} = $c->model('DB') + ->resultset('voip_preference_groups')->find({name => $group_name})->id; + $resource->{attribute} = dynamic_pref_attribute_to_db($resource->{attribute}); + $resource->{dynamic} = 1; + $resource->{internal} = 0; + $resource->{expose_to_customer} = 1; + + $relations->{autoprov_device_id} = delete $resource->{autoprov_device_id}; + $relations->{reseller_id} = delete $resource->{reseller_id}; + + my $enums = delete $resource->{enum}; + my $preference = $c->model('DB')->resultset('voip_preferences')->create($resource); + my @flags = grep {$_ =~/^[a-z]+_pref$/} keys %$resource; + if(defined $enums and ref $enums eq 'ARRAY'){ + foreach my $enum (@$enums) { + @{$enum}{@flags} = (1) x @flags; + $preference->create_related('voip_preferences_enums', $enum); + } + } + + save_dynamic_preference_relations($c, $resource, $preference, $relations); + + return $preference; +} + +sub update_dynamic_preference { + my ($c, $preference, $resource, %params) = @_; + + my $relations = {}; + + $resource->{attribute} = dynamic_pref_attribute_to_db($resource->{attribute}); + + $relations->{autoprov_device_id} = delete $resource->{autoprov_device_id}; + $relations->{reseller_id} = delete $resource->{reseller_id}; + + my $enums = delete $resource->{enum}; + + $preference->update($resource); + my @flags = grep {$_ =~/^[a-z]+_pref$/} keys %$resource; + if(defined $enums and ref $enums eq 'ARRAY'){ + my $enums_rs = $preference->voip_preferences_enums; + my @old_enum_ids = $enums_rs->get_column('id')->all; + my %old_enum_ids; + @old_enum_ids{@old_enum_ids} = @old_enum_ids; + foreach my $enum (@$enums) { + my $id = delete $enum->{id}; + my $enum_exists = $enums_rs->find($id); + @{$enum}{@flags} = (1) x @flags; + if ($enum_exists) { + $enum_exists->update($enum); + delete $old_enum_ids{$id}; + } else { + $preference->create_related('voip_preferences_enums', $enum); + } + } + $enums_rs->search_rs({ + id => { -in => [ keys %old_enum_ids ] }, + })->delete; + } else { + $preference->voip_preferences_enums->delete; + } + + save_dynamic_preference_relations($c, $resource, $preference, $relations); + + return $preference; +} + +sub delete_dynamic_preference { + my ($c, $preference) = @_; + $preference->voip_preferences_enums->delete; + $preference->delete; +} + +sub save_dynamic_preference_relations { + my ($c, $resource, $preference, $relations) = @_; + + if (defined $resource->{dev_pref} && $resource->{dev_pref}) { + if ($relations->{autoprov_device_id}) { + $preference->search_related_rs('voip_preference_relations')->update_or_create({ + autoprov_device_id => $relations->{autoprov_device_id}, + reseller_id => undef, + }); + } elsif ($relations->{reseller_id}) { + $preference->search_related_rs('voip_preference_relations')->update_or_create({ + autoprov_device_id => undef, + reseller_id => $relations->{reseller_id}, + }); + } + } +} + +sub dynamic_pref_attribute_to_standard { + my ($attribute) = @_; + $attribute =~s/^_*//; + return $attribute; +} + +sub dynamic_pref_attribute_to_db { + my ($attribute) = @_; + $attribute =~s/^_*/_DYNAMIC_PREFERENCE_PREFIX/e; + return $attribute; +} + 1; =head1 NAME diff --git a/share/templates/customer/details.tt b/share/templates/customer/details.tt index 4716216e52..6ff15b416b 100644 --- a/share/templates/customer/details.tt +++ b/share/templates/customer/details.tt @@ -373,6 +373,9 @@ $(function() { [% END -%] [% END -%] + + [% c.loc('Preferences') %] + diff --git a/share/templates/customer/pbx_fdev_preferences.tt b/share/templates/customer/pbx_fdev_preferences.tt new file mode 100644 index 0000000000..60843c8917 --- /dev/null +++ b/share/templates/customer/pbx_fdev_preferences.tt @@ -0,0 +1,23 @@ +[% site_config.title = c.loc('PBX Device [_1] "[_2]" [_3] - Preferences', devmod.vendor, devmod.model, pbx_device.identifier) -%] + +[% + helper.messages = messages; + + helper.edit_preference = edit_preference; + helper.preference = preference; + helper.preference_meta = preference_meta; + helper.preference_values = preference_values; + helper.pref_groups = pref_groups; + helper.form = form; + helper.no_edit_meta = 1; + helper.base_uri = c.uri_for_action("/customer/pbx_device_preferences_root", [c.req.captures.0,c.req.captures.1] ); + + helper.top_buttons = [ + { name = c.loc('Back'), uri = c.uri_for(""), icon = 'icon-arrow-left' }, + ]; + + PROCESS 'helpers/pref_table.tt'; +%] + + +[% # vim: set tabstop=4 syntax=html expandtab: -%] diff --git a/share/templates/device/preferences.tt b/share/templates/device/preferences.tt index 538a06d2a4..36622ea793 100644 --- a/share/templates/device/preferences.tt +++ b/share/templates/device/preferences.tt @@ -1,5 +1,6 @@ [% site_config.title = c.loc('Device model "[_1]" for reseller #[_2] - Preferences', devmod.vendor _ " " _ devmod.model, devmod.reseller_id) -%] + [% helper.messages = messages; @@ -9,11 +10,17 @@ helper.preference_values = preference_values; helper.pref_groups = pref_groups; helper.form = form; + helper.create_flag = create_flag; + helper.editmeta_flag = editmeta_flag; helper.base_uri = c.uri_for_action("/device/devices_preferences_root", [c.req.captures.0]); helper.top_buttons = [ { name = c.loc('Back'), uri = c.uri_for(""), icon = 'icon-arrow-left' }, ]; + UNLESS c.user.read_only; + helper.top_buttons.push ( { name = c.loc('Create Custom Preference'), uri = c.uri_for_action('/device/devices_preferences_create', [c.req.captures.0]), icon = 'icon-star' } ); + END; + PROCESS 'helpers/pref_table.tt'; %] diff --git a/share/templates/helpers/pref_table.tt b/share/templates/helpers/pref_table.tt index c28c6befc9..4b5738cd63 100644 --- a/share/templates/helpers/pref_table.tt +++ b/share/templates/helpers/pref_table.tt @@ -145,10 +145,14 @@ [% END %] - +
[% UNLESS (c.user.roles == "admin" || c.user.roles == "reseller") && c.user.read_only -%] [% c.loc('Edit') %] + [%IF r.dynamic && !helper.no_edit_meta %] + [% c.loc('Edit Meta') %] + [% c.loc('Delete') %] + [% END -%] [% END -%]
@@ -224,6 +228,7 @@ [% ELSE %] [% helper.form.render -%] [% END %] + [% modal_footer(); -%] @@ -237,5 +242,23 @@ }); [% END -%] + +[% IF helper.create_flag == 1 || helper.editmeta_flag == 1 -%] +[% + IF helper.form.has_for_js; + helper.form.render_repeatable_js; + END; + PROCESS "helpers/modal.tt"; + modal_header(m.create_flag=helper.create_flag, + m.duplicate_flag=helper.duplicate_flag, + m.edit_flag=helper.editmeta_flag, + m.name = helper.name); + helper.form = translate_form( helper.form ); + helper.form.render; + modal_footer(); + modal_script(m.close_target = helper.close_target); +-%] +[% END -%] + [% # vim: set tabstop=4 syntax=html expandtab: -%]