diff --git a/lib/NGCP/Panel/Controller/Customer.pm b/lib/NGCP/Panel/Controller/Customer.pm index 7a7ab3b064..f666b4164f 100644 --- a/lib/NGCP/Panel/Controller/Customer.pm +++ b/lib/NGCP/Panel/Controller/Customer.pm @@ -2375,12 +2375,17 @@ sub preferences_base :Chained('base') :PathPart('preferences') :CaptureArgs(1) { $c->detach('/denied_page'); } + my $blob_short_value_size = NGCP::Panel::Utils::Preferences::get_blob_short_value_size; $c->stash->{preference} = $c->model('DB') ->resultset('voip_contract_preferences') ->search({ attribute_id => $pref_id, contract_id => $c->stash->{contract}->id, location_id => $c->stash->{location}{id} || undef, + },{ + join => 'blob', + '+select' => [ \"SUBSTRING(blob.value, 1, $blob_short_value_size)" ], + '+as' => [ 'short_blob_value' ], }); $c->stash->{pref_id} = $pref_id; $c->stash(template => 'customer/preferences.tt'); @@ -2407,6 +2412,7 @@ sub preferences_edit :Chained('preferences_base') :PathPart('edit') :Args(0) { enums => \@enums, base_uri => $base_uri, edit_uri => $edit_uri, + blob_rs => $c->model('DB')->resultset('voip_contract_preferences_blob'), ); } @@ -2425,10 +2431,17 @@ sub load_preference_list :Private { my %pref_values; foreach my $value($contract_pref_values->all) { - - $pref_values{$value->attribute} = [ - map {$_->value} $value->voip_contract_preferences->all - ]; + if ($value->data_type eq "blob") { + $pref_values{$value->attribute} = [ + map {$_->blob + ? $_->blob->content_type + : ''} $value->voip_contract_preferences->all + ]; + } else { + $pref_values{$value->attribute} = [ + map {$_->value} $value->voip_contract_preferences->all + ]; + } } my $reseller_id = $c->stash->{contract}->contact->reseller_id; diff --git a/lib/NGCP/Panel/Controller/Domain.pm b/lib/NGCP/Panel/Controller/Domain.pm index bedaf94321..eaeb67afe3 100644 --- a/lib/NGCP/Panel/Controller/Domain.pm +++ b/lib/NGCP/Panel/Controller/Domain.pm @@ -351,12 +351,17 @@ sub preferences_base :Chained('base') :PathPart('preferences') :CaptureArgs(1) { ->single({id => $pref_id}); my $domain_name = $c->stash->{domain}->{domain}; my $provisioning_domain_id = $c->stash->{provisioning_domain_result}->id; + my $blob_short_value_size = NGCP::Panel::Utils::Preferences::get_blob_short_value_size; $c->stash->{preference} = $c->model('DB') ->resultset('voip_dom_preferences') ->search({ attribute_id => $pref_id, domain_id => $provisioning_domain_id, + },{ + join => 'blob', + '+select' => [ \"SUBSTRING(blob.value, 1, $blob_short_value_size)" ], + '+as' => [ 'short_blob_value' ], }); $c->stash(template => 'domain/preferences.tt'); } @@ -382,6 +387,7 @@ sub preferences_edit :Chained('preferences_base') :PathPart('edit') :Args(0) { enums => \@enums, base_uri => $c->uri_for_action('/domain/preferences', [$c->req->captures->[0]]), edit_uri => $c->uri_for_action('/domain/preferences_edit', $c->req->captures), + blob_rs => $c->model('DB')->resultset('voip_dom_preferences_blob'), ); } @@ -398,10 +404,17 @@ sub load_preference_list :Private { my %pref_values; foreach my $value($dom_pref_values->all) { - - $pref_values{$value->attribute} = [ - map {$_->value} $value->voip_dom_preferences->all - ]; + if ($value->data_type eq "blob") { + $pref_values{$value->attribute} = [ + map {$_->blob + ? $_->blob->content_type + : ''} $value->voip_dom_preferences->all + ]; + } else { + $pref_values{$value->attribute} = [ + map {$_->value} $value->voip_dom_preferences->all + ]; + } } my $correct_reseller_id = $c->stash->{domain_result}->domain_resellers->first->reseller_id; diff --git a/lib/NGCP/Panel/Controller/Peering.pm b/lib/NGCP/Panel/Controller/Peering.pm index e1d05a193a..e6da43cc09 100644 --- a/lib/NGCP/Panel/Controller/Peering.pm +++ b/lib/NGCP/Panel/Controller/Peering.pm @@ -528,9 +528,17 @@ sub servers_preferences_list :Chained('servers_base') :PathPart('preferences') : my %pref_values; foreach my $value($x_pref_values->all) { - - $pref_values{$value->attribute} = - [ map {$_->value} $value->voip_peer_preferences->all ]; + if ($value->data_type eq "blob") { + $pref_values{$value->attribute} = [ + map {$_->blob + ? $_->blob->content_type + : ''} $value->voip_peer_preferences->all + ]; + } else { + $pref_values{$value->attribute} = [ + map {$_->value} $value->voip_peer_preferences->all + ]; + } } my $rewrite_rule_sets_rs = $c->model('DB') @@ -576,6 +584,7 @@ sub servers_preferences_base :Chained('servers_preferences_list') :PathPart('') }) ->find({id => $pref_id}); + my $blob_short_value_size = NGCP::Panel::Utils::Preferences::get_blob_short_value_size; $c->stash->{preference} = $c->model('DB') ->resultset('voip_peer_preferences') ->search({ @@ -583,6 +592,9 @@ sub servers_preferences_base :Chained('servers_preferences_list') :PathPart('') 'peer_host.id' => $c->stash->{server}->{id}, },{ prefetch => 'peer_host', + join => 'blob', + '+select' => [ \"SUBSTRING(blob.value, 1, $blob_short_value_size)" ], + '+as' => [ 'short_blob_value' ], }); return; } @@ -603,6 +615,7 @@ sub servers_preferences_edit :Chained('servers_preferences_base') :PathPart('edi enums => \@enums, base_uri => $c->uri_for_action('/peering/servers_preferences_root', [@{ $c->req->captures }[0,1]]), edit_uri => $c->uri_for_action('/peering/servers_preferences_edit', $c->req->captures), + blob_rs => $c->model('DB')->resultset('voip_peer_preferences_blob'), ); return; } diff --git a/lib/NGCP/Panel/Controller/Subscriber.pm b/lib/NGCP/Panel/Controller/Subscriber.pm index ec46969de0..24e318cbab 100644 --- a/lib/NGCP/Panel/Controller/Subscriber.pm +++ b/lib/NGCP/Panel/Controller/Subscriber.pm @@ -792,11 +792,16 @@ sub preferences_base :Chained('base') :PathPart('preferences') :CaptureArgs(1) { $c->detach('/denied_page'); } + my $blob_short_value_size = NGCP::Panel::Utils::Preferences::get_blob_short_value_size; $c->stash->{preference} = $c->model('DB') ->resultset('voip_usr_preferences') ->search({ attribute_id => $pref_id, subscriber_id => $c->stash->{subscriber}->provisioning_voip_subscriber->id + },{ + join => 'blob', + '+select' => [ \"SUBSTRING(blob.value, 1, $blob_short_value_size)" ], + '+as' => [ 'short_blob_value' ], }); $c->stash(template => 'subscriber/preferences.tt'); } @@ -835,6 +840,7 @@ sub preferences_edit :Chained('preferences_base') :PathPart('edit') :Args(0) { enums => \@enums, base_uri => $c->uri_for_action('/subscriber/preferences', [$c->req->captures->[0]]), edit_uri => $c->uri_for_action('/subscriber/preferences_edit', $c->req->captures), + blob_rs => $c->model('DB')->resultset('voip_usr_preferences_blob'), ); my $attr = $c->stash->{preference_meta}->attribute; if ($c->req->method eq "POST" && $attr && ($attr eq "voicemail_echo_number" || $attr eq "cli")) { @@ -2561,14 +2567,21 @@ sub load_preference_list :Private { ->search({ 'subscriber.id' => $c->stash->{subscriber}->provisioning_voip_subscriber->id },{ - prefetch => {'voip_usr_preferences' => 'subscriber'}, + prefetch => { 'voip_usr_preferences' => [ 'subscriber', 'blob' ] }, }); my %pref_values; - foreach my $value($usr_pref_values->all) { - - $pref_values{$value->attribute} = [ - map {$_->value} $value->voip_usr_preferences->all - ]; + foreach my $value ($usr_pref_values->all) { + if ($value->data_type eq "blob") { + $pref_values{$value->attribute} = [ + map {$_->blob + ? $_->blob->content_type + : ''} $value->voip_usr_preferences->all + ]; + } else { + $pref_values{$value->attribute} = [ + map {$_->value} $value->voip_usr_preferences->all + ]; + } } my $rewrite_rule_sets_rs = $c->model('DB') diff --git a/lib/NGCP/Panel/Field/BlobUpload.pm b/lib/NGCP/Panel/Field/BlobUpload.pm new file mode 100644 index 0000000000..22040f44e2 --- /dev/null +++ b/lib/NGCP/Panel/Field/BlobUpload.pm @@ -0,0 +1,98 @@ +package NGCP::Panel::Field::BlobUpload; +use HTML::FormHandler::Moose; +extends 'HTML::FormHandler::Field::Compound'; + +has_field 'show_data' => ( + type => 'Button', + label => '', + value => "Show Data", + element_class => [qw/ngcp-blob-show-data/], + element_attr => { + readonly => 1, + rel => ['tooltip'], + title => ['Show/hide file content.'], + }, +); + +has_field 'content_data' => ( + type => 'TextArea', + label => 'Content Data', + default => '', + cols => 200, + rows => 10, + maxlength => '16777216', # 16MB + element_class => [qw/ngcp-blob-data-area/], + element_attr => { + readonly => 1 + }, + inflate_default_method => \&inflate_content_data_field, +); + +has_field 'content_type' => ( + type => 'Text', + label => 'Content Type', + default => 'application/octet-stream', + element_attr => { + rel => ['tooltip'], + title => ['The content type of this file.'] + }, + inflate_default_method => \&inflate_content_type_field, +); + +has_field 'file' => ( + type => 'Upload', + label => 'File', + max_size => '16777216', # MEDIUMBLOB max size +); + +has_field 'delete' => ( + type => 'Submit', + value => 'Delete', + element_class => [qw/btn btn-secondary/], + label => '', +); + +has_field 'download' => ( + type => 'Submit', + value => 'Download', + element_class => [qw(btn btn-tertiary pull-right)], + label => '', +); + +has_block 'actions' => ( + tag => 'div', + class => [qw/modal-footer/], + render_list => [qw/delete download/], +); + +sub inflate_content_data_field { + my ($self, $value) = @_; + + my $c = $self->form->ctx; + my $preference = $c->stash->{preference}->first // return $value; + + if ($preference->blob) { + if ($preference->blob->content_type =~ /^(text|aplication\/json)/) { + my %pref_data = $preference->get_inflated_columns; + return $pref_data{short_blob_value} + } else { + return "#binary-data#"; + } + } + return $value; +} + +sub inflate_content_type_field { + my ($self, $value) = @_; + + my $c = $self->form->ctx; + my $preference = $c->stash->{preference}->first // return $value; + + if ($preference->blob) { + return $preference->blob->content_type; + } + return $value; +} + +no Moose; +1; diff --git a/lib/NGCP/Panel/Form/Preferences.pm b/lib/NGCP/Panel/Form/Preferences.pm index cea10cc987..3a25e146e6 100644 --- a/lib/NGCP/Panel/Form/Preferences.pm +++ b/lib/NGCP/Panel/Form/Preferences.pm @@ -8,6 +8,7 @@ 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)]} @@ -126,6 +127,11 @@ sub field_list { name => $meta->attribute, type => 'Integer', }; + } elsif($meta->data_type eq "blob") { + $field = { + name => $meta->attribute, + type => '+NGCP::Panel::Field::BlobUpload', + }; } else { # string if($meta->max_occur == 1) { $field = { diff --git a/lib/NGCP/Panel/Utils/Preferences.pm b/lib/NGCP/Panel/Utils/Preferences.pm index f9614eb0bf..24e3fbf4cc 100644 --- a/lib/NGCP/Panel/Utils/Preferences.pm +++ b/lib/NGCP/Panel/Utils/Preferences.pm @@ -9,6 +9,8 @@ use NGCP::Panel::Utils::I18N qw//; use NGCP::Panel::Utils::Sems; use JSON qw(); use HTTP::Status qw(:constants); +use File::Type; +use Readonly; use constant _DYNAMIC_PREFERENCE_PREFIX => '__'; @@ -27,6 +29,7 @@ our $TYPE_PREF_MAP = { my $API_TRANSFORM_OUT; my $API_TRANSFORM_IN; my $CODE_SUFFIX_FNAME = '_code'; +Readonly my $blob_short_value_size => 4096; sub validate_ipnet { my ($field) = @_; @@ -1283,6 +1286,7 @@ sub create_preference_form { my $base_uri = $params{base_uri}; my $edit_uri = $params{edit_uri}; my $enums = $params{enums}; + my $blob_rs = $params{blob_rs}; my $aip_grp_rs; my $aip_group_id; @@ -1431,7 +1435,7 @@ sub create_preference_form { } } elsif ($c->stash->{preference_meta}->max_occur == 1) { if ($c->stash->{preference}->first) { - $preselected_value = $c->stash->{preference}->first->value; + $preselected_value = $c->stash->{preference}->first->value unless ($c->stash->{preference_meta}->data_type eq 'blob'); } } @@ -1456,6 +1460,12 @@ sub create_preference_form { } my $posted = ($c->request->method eq 'POST'); + if($posted && $c->stash->{preference_meta}->data_type eq 'blob') { + # construct the form field name for blob data types, + # since we need to pass the Catalyst::Upload object to req params + my $field_name = $c->stash->{preference_meta}->attribute . '.file'; + $c->req->params->{$field_name} = $c->req->upload($field_name) if ($c->req->upload($field_name)); + } $form->process( posted => $posted, params => $c->request->params, @@ -1957,6 +1967,59 @@ sub create_preference_form { $c->response->redirect($base_uri); return 1; } + } elsif($c->stash->{preference_meta}->data_type eq 'blob') { + try { + my $preference = $pref_rs->search({ attribute_id => $c->stash->{preference_meta}->id }); + my $file = $form->field("$attribute.file")->value; + my $content_type = $form->field("$attribute.content_type")->value; + if ($c->req->body_parameters->{"$attribute.delete"}) { + $preference->delete if $preference; + } elsif ($c->req->body_parameters->{"$attribute.download"}) { + my $blob = $blob_rs->search({ preference_id => $preference->first->id }); + my $data = $blob->first->value; + my $ft = File::Type->new(); + $c->response->header('Content-Disposition' => 'attachment; filename="' . $blob->first->id . '-' . $attribute . '"'); + $c->response->content_type($ft->mime_type($blob->first->value) || $blob->first->content_type); + $c->response->body($data); + return 1; + } elsif ($preference->first) { + my $blob = $blob_rs->search({ preference_id => $preference->first->id }); + if ($blob->first) { + $blob->update({ + preference_id => $preference->first->id, + $file ? (value => $file->slurp) : (), + $content_type ? (content_type => $content_type) : (), + }); + } else { + $blob_rs->create({ + preference_id => $preference->first->id, + value => ($file ? $file->slurp : undef), + content_type => $content_type, + }); + } + } else { + $preference->create({ value => 0 }); + $blob_rs->create({ + preference_id => $preference->first->id, + value => ($file ? $file->slurp : undef), + content_type => $content_type, + }); + } + NGCP::Panel::Utils::Message::info( + c => $c, + data => \%log_data, + desc => $c->loc('Preference [_1] successfully updated', $attribute), + ); + } catch($e) { + NGCP::Panel::Utils::Message::error( + c => $c, + error => $e, + data => \%log_data, + desc => $c->loc('Failed to update preference [_1]', $attribute), + ); + $c->response->redirect($base_uri); + return 1; + } } else { try { $pref_rs->update_or_create({ @@ -2712,6 +2775,10 @@ sub dynamic_pref_attribute_to_db { return $attribute; } +sub get_blob_short_value_size { + return $blob_short_value_size; +} + 1; =head1 NAME diff --git a/share/templates/helpers/pref_table.tt b/share/templates/helpers/pref_table.tt index 6cf9894e8f..c73c0dc4e1 100644 --- a/share/templates/helpers/pref_table.tt +++ b/share/templates/helpers/pref_table.tt @@ -138,7 +138,7 @@ [% ELSIF r.data_type == "boolean" %] - [% ELSIF r.data_type == "string" || r.data_type == "int" %] + [% ELSIF r.data_type == "string" || r.data_type == "int" || r.data_type == "blob" %] [% IF r.max_occur == 1 %] [% r.value %] [% ELSE %] @@ -189,6 +189,21 @@ m.name = c.loc("Preference '") _ c.loc(helper.preference_meta.label) _"'"); -%] + [% IF helper.preference_meta.data_type == "blob" %] + + [% END %] [% IF helper.preference_meta.attribute == "allowed_ips" %]