TT#109604 Implement blob preferences upload in Panel UI

* Create upload and content type form fields for 'blob'
	    type preferences
	  * Implement blob preference upload/download to database
	  * Show blob content in read only text area if content
	    is text

Change-Id: Ic4b800f84324eab0aadbf8eeb55c03c770ecc94f
mr9.4
Flaviu Mates 4 years ago
parent c344dee22e
commit 055751eb81

@ -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;

@ -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;

@ -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;
}

@ -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')

@ -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;

@ -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 = {

@ -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

@ -138,7 +138,7 @@
[% ELSIF r.data_type == "boolean" %]
<input type="checkbox" disabled="disabled"
[% IF r.value %]checked="checked"[% END %]/>
[% 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" %]
<script>
$(document).ready(function() {
$(".ngcp-blob-data-area").hide();
$(".ngcp-blob-show-data").click(function() {
$(".ngcp-blob-data-area").toggle();
if ($(".ngcp-blob-data-area").is(':visible')) {
$(".ngcp-blob-show-data").attr('value', 'Hide Data');
} else {
$(".ngcp-blob-show-data").attr('value', 'Show Data');
}
});
});
</script>
[% END %]
[% IF helper.preference_meta.attribute == "allowed_ips" %]
<div class="modal-body">
[% FOREACH net IN helper.aip_grp_rs.all %]

Loading…
Cancel
Save