You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1615 lines
51 KiB
1615 lines
51 KiB
package NGCP::Panel::Utils::ProvisioningTemplates;
|
|
|
|
use Sipwise::Base;
|
|
|
|
use NGCP::Panel::Form::ProvisioningTemplate::ProvisioningTemplate qw();
|
|
use DateTime::TimeZone qw();
|
|
use String::MkPasswd qw();
|
|
use Eval::Closure qw(eval_closure);
|
|
use Tie::IxHash;
|
|
use Data::Dumper;
|
|
$Data::Dumper::Sortkeys = sub {
|
|
my ($hash) = @_;
|
|
my @keys = ();
|
|
foreach my $k (keys %$hash) {
|
|
next if grep { ref $hash->{$k} eq $_; } qw(
|
|
DateTime
|
|
);
|
|
push(@keys,$k);
|
|
}
|
|
return \@keys;
|
|
};
|
|
use Text::CSV_XS qw();
|
|
use YAML::XS qw();
|
|
use NGCP::Panel::Utils::DateTime qw();
|
|
use NGCP::Panel::Utils::BillingMappings qw();
|
|
use NGCP::Panel::Utils::Contract qw();
|
|
use NGCP::Panel::Utils::ProfilePackages qw();
|
|
use NGCP::Panel::Utils::Subscriber qw();
|
|
use NGCP::Panel::Utils::Preferences qw();
|
|
use NGCP::Panel::Utils::Kamailio qw();
|
|
use NGCP::Panel::Utils::CallForwards qw();
|
|
|
|
use JE::Destroyer qw();
|
|
use JE qw();
|
|
use JSON qw();
|
|
use NGCP::Panel::Utils::Generic qw(escape_js);
|
|
|
|
my $IDENTIFIER_FNAME = 'identifier';
|
|
my $CODE_SUFFIX_FNAME = '_code';
|
|
my $FIELD_TYPE_ATTRIBUTE = 'type';
|
|
my $FIELD_VALUE_ATTRIBUTE = 'value';
|
|
my @INIT_FIELD_NAMES = qw(cc_ac_map default_cc);
|
|
my $PURGE_FIELD_NAME = 'purge';
|
|
|
|
my $JE_ANON_CLASS = 'je_anon';
|
|
sub je_anon::TO_JSON {
|
|
return _unbless(@_);
|
|
};
|
|
my $STRICT_CLOSURE = 1;
|
|
|
|
my @DISABLED_CORE_FUNCTIONS = qw(
|
|
binmode close closedir dbmclose dbmopen eof fileno flock format getc read
|
|
readdir rewinddir say seek seekdir select syscall sysread sysseek
|
|
syswrite tell telldir truncate write print printf
|
|
|
|
chdir chmod chown chroot fcntl glob ioctl link lstat mkdir open opendir readlink
|
|
rename rmdir stat symlink sysopen umask unlink utime
|
|
|
|
alarm exec fork getpgrp getppid getpriority kill pipe setpgrp setpriority sleep
|
|
system times wait waitpid
|
|
|
|
accept bind connect getpeername getsockname getsockopt listen recv send setsockopt
|
|
shutdown socket socketpair
|
|
|
|
msgctl msgget msgrcv msgsnd semctl semget semop shmctl shmget shmread shmwrite
|
|
|
|
endgrent endhostent endnetent endpwent getgrent getgrgid getgrnam getlogin getpwent
|
|
getpwnam getpwuid setgrent setpwent
|
|
|
|
endprotoent endservent gethostbyaddr gethostbyname gethostent getnetbyaddr
|
|
getnetbyname getnetent getprotobyname getprotobynumber getprotoent getservbyname
|
|
getservbyport getservent sethostent setnetent setprotoent setservent
|
|
|
|
exit goto
|
|
);
|
|
|
|
my @SUPPORTED_LANGS = qw(perl js);
|
|
|
|
my $PERL_ENV = 'use subs qw(' . join(' ', @DISABLED_CORE_FUNCTIONS) . ");\n";
|
|
foreach my $sub (@DISABLED_CORE_FUNCTIONS) {
|
|
$PERL_ENV .= 'sub ' . $sub . " { die('$sub called'); }\n";
|
|
}
|
|
|
|
my $JS_ENV = '';
|
|
|
|
sub load_template_map {
|
|
|
|
my $c = shift;
|
|
my $templates = { %{$c->config->{provisioning_templates} // {}} };
|
|
map {
|
|
$templates->{$_}->{name} = $_;
|
|
$templates->{$_}->{static} = 1;
|
|
$templates->{$_}->{id} = undef;
|
|
$templates->{$_}->{reseller} = undef;
|
|
} keys %$templates;
|
|
|
|
my $rs = $c->model('DB')->resultset('provisioning_templates')->search_rs();
|
|
if($c->user->roles eq "admin" || $c->user->roles eq "ccareadmin") {
|
|
} elsif($c->user->roles eq "reseller" || $c->user->roles eq "ccare") {
|
|
$rs = $rs->search_rs({ -or => [
|
|
reseller_id => $c->user->reseller_id,
|
|
reseller_id => undef
|
|
], },);
|
|
} else {
|
|
$rs = $rs->search_rs({ -or => [
|
|
reseller_id => $c->user->contract->contact->reseller_id,
|
|
reseller_id => undef
|
|
], },);
|
|
}
|
|
$c->stash->{template_rs} = $rs;
|
|
foreach my $db_template ($rs->all) {
|
|
my $template = { $db_template->get_inflated_columns };
|
|
eval {
|
|
%$template = ( %{parse_template($c, $template->{id}, $template->{name}, $template->{yaml})}, %$template );
|
|
#use Data::Dumper;
|
|
#$c->log->error(Dumper($template));
|
|
delete $template->{yaml};
|
|
};
|
|
if ($@) {
|
|
die $@;
|
|
next;
|
|
}
|
|
$template->{static} = 0;
|
|
if ($db_template->reseller) {
|
|
$template->{reseller} = $db_template->reseller->name;
|
|
$templates->{$template->{reseller} . '/' . $template->{name}} = $template;
|
|
} else {
|
|
$templates->{$template->{name}} = $template;
|
|
}
|
|
}
|
|
$c->stash->{provisioning_templates} = $templates;
|
|
|
|
}
|
|
|
|
sub validate_template {
|
|
|
|
my ($data,$prefix) = @_;
|
|
$prefix //= 'template: ';
|
|
die($prefix . "not a hash\n") unless 'HASH' eq ref $data;
|
|
foreach my $section (qw/subscriber/) {
|
|
die($prefix . "section '$section' required\n") unless exists $data->{$section};
|
|
die($prefix . "section '$section' is not a hash\n") unless 'HASH' eq ref $data->{$section};
|
|
}
|
|
|
|
}
|
|
|
|
sub validate_template_name {
|
|
|
|
my ($c,$name,$old_name,$reseller,$old_reseller) = @_;
|
|
unless ($name =~ /^[a-zA-Z0-9 -]+$/) {
|
|
die("template name contains invalid characters\n");
|
|
}
|
|
|
|
if (not defined $old_name
|
|
or $old_name ne $name) {
|
|
unless ($c->stash->{provisioning_templates}) {
|
|
load_template_map($c);
|
|
}
|
|
die("a provisioning template with name '" . $name . "' already exists\n")
|
|
if exists $c->stash->{provisioning_templates}->{$reseller ? ($reseller->name . '/' . $name) : $name};
|
|
}
|
|
|
|
}
|
|
|
|
sub dump_template {
|
|
|
|
my ($c,$id,$name,$template) = @_;
|
|
my $yaml;
|
|
eval {
|
|
$yaml = YAML::XS::Dump($template);
|
|
};
|
|
if ($@) {
|
|
$c->log->error("error parsing provisioning_template id $id '$name': " . $@) if $c;
|
|
die($@);
|
|
}
|
|
return $yaml;
|
|
|
|
}
|
|
|
|
sub parse_template {
|
|
|
|
my ($c,$id,$name,$yaml) = @_;
|
|
my $template;
|
|
#die ("yaml: " . $yaml);
|
|
eval {
|
|
$template = YAML::XS::Load($yaml);
|
|
};
|
|
if ($@) {
|
|
$c->log->error("error parsing provisioning_template id $id '$name': " . $@) if $c;
|
|
die($@);
|
|
}
|
|
return $template;
|
|
|
|
}
|
|
|
|
sub get_provisioning_template_form {
|
|
|
|
my ($c,$fields) = @_;
|
|
$fields //= get_fields($c,0);
|
|
my $form = NGCP::Panel::Form::ProvisioningTemplate::ProvisioningTemplate->new({
|
|
ctx => $c,
|
|
fields_config => [ values %$fields ],
|
|
});
|
|
$form->create_structure([ keys %$fields ]);
|
|
return $form;
|
|
|
|
}
|
|
|
|
sub create_provisioning_template_form {
|
|
|
|
my %params = @_;
|
|
my ($c,
|
|
$base_uri) = @params{qw/
|
|
c
|
|
base_uri
|
|
/};
|
|
|
|
my $template = $c->stash->{provisioning_template_name};
|
|
|
|
my $fields = get_fields($c,0);
|
|
my $form;
|
|
|
|
try {
|
|
$form = get_provisioning_template_form($c,$fields);
|
|
} catch($e) {
|
|
NGCP::Panel::Utils::Message::error(
|
|
c => $c,
|
|
error => $e,
|
|
data => $fields,
|
|
desc => $c->loc("Provisioning template '[_1]' failed: [_2]", $template, $e),
|
|
);
|
|
$c->response->redirect($base_uri);
|
|
return 1;
|
|
}
|
|
|
|
my $params = {};
|
|
my $posted = ($c->request->method eq 'POST');
|
|
$form->process(
|
|
posted => $posted,
|
|
params => $c->request->params,
|
|
item => $params,
|
|
);
|
|
NGCP::Panel::Utils::Navigation::check_form_buttons(
|
|
c => $c,
|
|
form => $form,
|
|
fields => {},
|
|
back_uri => $c->req->uri,
|
|
);
|
|
|
|
if($posted && $form->validated) {
|
|
my %log_data = %{$c->request->params};
|
|
my $context;
|
|
try {
|
|
$context = provision_begin(
|
|
c => $c,
|
|
);
|
|
provision_commit_row(
|
|
c => $c,
|
|
context => $context,
|
|
'values' => $form->values,
|
|
);
|
|
provision_finish(
|
|
c => $c,
|
|
context => $context,
|
|
);
|
|
NGCP::Panel::Utils::Message::info(
|
|
c => $c,
|
|
data => \%log_data,
|
|
desc => $c->loc("Provisioning template '[_1]' done: subscriber [_2] created", $template, $context->{subscriber}->{username} . '@' . $context->{domain}->{domain}),
|
|
);
|
|
} catch($e) {
|
|
provision_cleanup($c, $context);
|
|
NGCP::Panel::Utils::Message::error(
|
|
c => $c,
|
|
error => $e,
|
|
data => \%log_data,
|
|
desc => $c->loc("Provisioning template '[_1]' failed: [_2]", $template, $e),
|
|
);
|
|
$c->response->redirect($base_uri);
|
|
return 1;
|
|
}
|
|
$c->response->redirect($base_uri);
|
|
return 1;
|
|
|
|
}
|
|
|
|
$form->process if ($posted && $form->validated);
|
|
$c->stash(form => $form);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
sub process_csv {
|
|
|
|
my(%params) = @_;
|
|
my ($c,
|
|
$data,
|
|
$purge) = @params{qw/
|
|
c
|
|
data
|
|
purge
|
|
/};
|
|
|
|
my $template = $c->stash->{provisioning_template_name};
|
|
$c->log->debug("provisioning template $template - processing csv");
|
|
my $csv = Text::CSV_XS->new({
|
|
allow_whitespace => 1,
|
|
binary => 1,
|
|
keep_meta_info => 1
|
|
});
|
|
my $fields = get_fields($c,0);
|
|
my @cols = keys %$fields;
|
|
my @fails = ();
|
|
my $linenum = 0;
|
|
my $context = provision_begin(
|
|
c => $c,
|
|
purge => $purge,
|
|
);
|
|
open(my $fh, '<:encoding(utf8)', $data);
|
|
while ( my $line = <$fh> ){
|
|
++$linenum;
|
|
next unless length $line;
|
|
unless($csv->parse($line)) {
|
|
push(@fails,{ linenum => $linenum, });
|
|
next;
|
|
}
|
|
my $row = {};
|
|
@{$row}{keys %$fields} = $csv->fields();
|
|
try {
|
|
provision_commit_row(
|
|
c => $c,
|
|
context => $context,
|
|
'values' => $row,
|
|
);
|
|
} catch($e) {
|
|
$c->log->error($e);
|
|
push(@fails,{ linenum => $linenum, msg => $e });
|
|
}
|
|
}
|
|
|
|
provision_finish(
|
|
c => $c,
|
|
context => $context,
|
|
);
|
|
|
|
$c->log->debug("provisioning template $template - csv done");
|
|
|
|
return ($linenum,\@fails);
|
|
}
|
|
|
|
sub provision_begin {
|
|
|
|
my %params = @_;
|
|
my ($c,
|
|
$purge) = @params{qw/
|
|
c
|
|
purge
|
|
/};
|
|
|
|
my $template = $c->stash->{provisioning_template_name};
|
|
my $schema = $c->model('DB');
|
|
$schema->set_transaction_isolation('READ COMMITTED');
|
|
|
|
my $context = {};
|
|
$context->{_dfrd} = {};
|
|
$context->{_purge} = $purge // 0;
|
|
|
|
my $fields = get_fields($c,1);
|
|
my $init_values = {};
|
|
foreach my $fname (@INIT_FIELD_NAMES) {
|
|
next unless exists $fields->{$fname};
|
|
my ($k,$v) = _calculate_field($init_values, $fname, $fields);
|
|
$init_values->{$k} = $v;
|
|
}
|
|
|
|
my %subs = ();
|
|
|
|
if (exists $init_values->{cc_ac_map} and not exists $context->{split_number}) {
|
|
my $cc_ac_map = $init_values->{cc_ac_map};
|
|
die("invalid cc ac map") unless ('HASH' eq ref $cc_ac_map);
|
|
my $ac_len = {};
|
|
my $default_cc = $init_values->{default_cc}; #undef
|
|
my $cc_len_min = ~0;
|
|
my $cc_len_max = 0;
|
|
foreach my $cc (keys %$cc_ac_map) {
|
|
my $ac_map = $cc_ac_map->{$cc};
|
|
$cc_len_min = length($cc) if length($cc) < $cc_len_min;
|
|
$cc_len_max = length($cc) if length($cc) > $cc_len_max;
|
|
$ac_len->{$cc} = { min => ~0, max => 0, };
|
|
if ('HASH' ne ref $ac_map) {
|
|
die("invalid $cc ac map");
|
|
} else {
|
|
foreach my $ac (keys %$ac_map) {
|
|
if ($ac_map->{$ac}) { # ac enabled
|
|
$ac_len->{$cc}->{min} = length($ac) if length($ac) < $ac_len->{$cc}->{min};
|
|
$ac_len->{$cc}->{max} = length($ac) if length($ac) > $ac_len->{$cc}->{max};
|
|
} else {
|
|
delete $ac_map->{$ac};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$subs{split_number} = sub {
|
|
my ($dn) = @_;
|
|
$dn //= '';
|
|
$dn = '' . $dn; #force JE:: unboxing
|
|
my ($cc,$ac,$sn) = ('','',$dn);
|
|
|
|
if ($default_cc) {
|
|
$cc = $default_cc;
|
|
$dn =~ s/^0//;
|
|
$sn = $dn;
|
|
} else {
|
|
foreach my $cc_length ($cc_len_min .. $cc_len_max) {
|
|
my ($_cc,$_dn) = (substr($dn,0,$cc_length), substr($dn,$cc_length));
|
|
if (exists $cc_ac_map->{$_cc}) {
|
|
$cc = $_cc;
|
|
$sn = $_dn;
|
|
$dn = $_dn;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
if (exists $cc_ac_map->{$cc}) {
|
|
my $ac_map = $cc_ac_map->{$cc};
|
|
foreach my $ac_length ($ac_len->{$cc}->{min} .. $ac_len->{$cc}->{max}) {
|
|
my ($_ac,$_sn) = (substr($dn,0,$ac_length), substr($dn,$ac_length));
|
|
if (exists $ac_map->{$_ac}) {
|
|
$ac = $_ac;
|
|
$sn = $_sn;
|
|
#$dn = '';
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bless { ac => $ac, cc => $cc, sn => $sn, }, $JE_ANON_CLASS;
|
|
};
|
|
}
|
|
|
|
foreach my $sub (qw(debug info warn error)) {
|
|
$subs{$sub} = sub {
|
|
return $c->log->$sub(( map { ($_ // '') . ''; } @_));
|
|
};
|
|
}
|
|
|
|
_switch_lang(
|
|
$context,
|
|
$context->{_lang} = $c->stash->{provisioning_templates}->{$template}->{lang},
|
|
perl => sub {
|
|
$context->{now} = NGCP::Panel::Utils::DateTime::current_local();
|
|
$context->{schema} = $schema;
|
|
@{$context}{keys %subs} = values %subs;
|
|
},
|
|
js => sub {
|
|
$context->{_je} = JE->new();
|
|
$context->{_je}->eval($JS_ENV . "\nvar _func;\nvar now = new Date('" .
|
|
NGCP::Panel::Utils::DateTime::current_local() . "');\n");
|
|
$subs{'quotemeta'} = sub {
|
|
return quotemeta(_unbox_je_value(shift @_));
|
|
};
|
|
$subs{'sprintf'} = sub {
|
|
return sprintf(_unbox_je_value(shift @_), map {
|
|
_unbox_je_value($_);
|
|
} @_);
|
|
};
|
|
while (each %subs) {
|
|
$context->{_je}->new_function($_ => $subs{$_});
|
|
}
|
|
$context->{_je_env} = {};
|
|
}
|
|
);
|
|
|
|
return $context;
|
|
|
|
}
|
|
|
|
sub provision_commit_row {
|
|
|
|
my %params = @_;
|
|
my ($c,
|
|
$context,
|
|
$values) = @params{qw/
|
|
c
|
|
context
|
|
values
|
|
/};
|
|
|
|
my $template = $c->stash->{provisioning_template_name};
|
|
my $schema = $c->model('DB');
|
|
_init_row_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$values,
|
|
);
|
|
|
|
my $guard = $schema->txn_scope_guard;
|
|
_init_contract_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_contract_contact(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
_create_contract(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
_init_contract_preferences_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_contract_preferences(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
|
|
my $purge = $context->{_purge} || $values->{$PURGE_FIELD_NAME};
|
|
|
|
try {
|
|
_init_subscriber_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_subscriber(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
} catch(DBIx::Class::Exception $e where { /Duplicate entry/ }) {
|
|
my ($msg, $subscriber) = _get_duplicate_subs(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$e,
|
|
$purge,
|
|
);
|
|
if ($purge && $subscriber) {
|
|
$c->log->debug("provisioning template - terminating subscriber id " . $subscriber->id);
|
|
NGCP::Panel::Utils::Subscriber::terminate(c => $c, subscriber => $subscriber);
|
|
_create_subscriber(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
} else {
|
|
die $msg;
|
|
}
|
|
} catch($e where { /Subscriber already exists/ }) {
|
|
my ($msg, $subscriber) = _get_duplicate_subs(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$e,
|
|
$purge,
|
|
);
|
|
if ($purge && $subscriber) {
|
|
$c->log->debug("provisioning template - terminating subscriber id " . $subscriber->id);
|
|
NGCP::Panel::Utils::Subscriber::terminate(c => $c, subscriber => $subscriber);
|
|
_init_subscriber_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_subscriber(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
} else {
|
|
die $msg;
|
|
}
|
|
} catch($e where { /alias '([^']+)' already exists/ }) {
|
|
my ($msg, $subscriber) = _get_duplicate_subs(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$e,
|
|
$purge,
|
|
);
|
|
if ($purge && $subscriber && scalar @$subscriber) {
|
|
foreach (@$subscriber) {
|
|
$subscriber = $_;
|
|
$c->log->debug("provisioning template - terminating subscriber id " . $subscriber->id);
|
|
NGCP::Panel::Utils::Subscriber::terminate(c => $c, subscriber => $subscriber);
|
|
_init_subscriber_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_subscriber(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
}
|
|
} else {
|
|
die $msg;
|
|
}
|
|
}
|
|
|
|
_init_subscriber_preferences_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_subscriber_preferences(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
_init_registrations_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_registrations(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
_init_trusted_sources_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_trusted_sources(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
_init_cf_mappings_context(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
$c->stash->{provisioning_templates}->{$template},
|
|
);
|
|
_create_cf_mappings(
|
|
$c,
|
|
$context,
|
|
$schema,
|
|
);
|
|
|
|
#die();
|
|
$guard->commit;
|
|
|
|
$c->log->debug("provisioning template $template done: " . $context->{subscriber}->{username} . '@' . $context->{domain}->{domain});
|
|
|
|
}
|
|
|
|
sub provision_finish {
|
|
|
|
my %params = @_;
|
|
my ($c,
|
|
$context) = @params{qw/
|
|
c
|
|
context
|
|
/};
|
|
|
|
provision_cleanup($c, $context);
|
|
|
|
if (exists $context->{_dfrd}->{kamailio_trusted_reload}
|
|
and $context->{_dfrd}->{kamailio_trusted_reload} > 0) {
|
|
my (undef, $xmlrpc_res) = NGCP::Panel::Utils::Kamailio::trusted_reload($c);
|
|
delete $context->{_dfrd}->{kamailio_trusted_reload};
|
|
}
|
|
|
|
if (exists $context->{_dfrd}->{kamailio_flush}
|
|
and $context->{_dfrd}->{kamailio_flush} > 0) {
|
|
NGCP::Panel::Utils::Kamailio::flush($c);
|
|
delete $context->{_dfrd}->{kamailio_flush};
|
|
}
|
|
|
|
}
|
|
|
|
sub provision_cleanup {
|
|
|
|
my ($c, $context) = @_;
|
|
|
|
return unless $context;
|
|
|
|
if ($context->{_je}) {
|
|
JE::Destroyer::destroy($context->{_je}); # break circular refs
|
|
undef $context->{_je};
|
|
undef $context->{_je_env};
|
|
}
|
|
|
|
}
|
|
|
|
sub _init_row_context {
|
|
|
|
my ($c, $context, $schema, $values) = @_;
|
|
|
|
delete $context->{contract_contact};
|
|
delete $context->{contract};
|
|
delete $context->{contract_preferences};
|
|
delete $context->{subscriber};
|
|
delete $context->{subscriber_preferences};
|
|
|
|
$context->{registrations} = [];
|
|
$context->{trusted_sources} = [];
|
|
delete $context->{cf_mappings};
|
|
|
|
delete $context->{reseller};
|
|
delete $context->{billing_profile};
|
|
delete $context->{profile_package};
|
|
delete $context->{domain};
|
|
delete $context->{provisioning_domain};
|
|
delete $context->{product};
|
|
|
|
delete $context->{_bm};
|
|
delete $context->{_cp};
|
|
delete $context->{_cs};
|
|
|
|
delete $context->{row};
|
|
|
|
my $fields = get_fields($c,1);
|
|
my %row = ();
|
|
$row{sip_username} = _generate_username(10);
|
|
$row{sip_password} = String::MkPasswd::mkpasswd(
|
|
-length => 12,
|
|
-minnum => 1, -minlower => 1, -minupper => 1, -minspecial => 1,
|
|
-distribute => 1, -fatal => 1,
|
|
);
|
|
$row{web_username} = _generate_username(10);
|
|
$row{web_password} = String::MkPasswd::mkpasswd(
|
|
-length => 12,
|
|
-minnum => 1, -minlower => 1, -minupper => 1, -minspecial => 1,
|
|
-distribute => 1, -fatal => 1,
|
|
);
|
|
%row = (%row, %$values);
|
|
$context->{row} = \%row;
|
|
foreach my $fname (keys %$fields) {
|
|
next if grep { $fname eq $_; } @INIT_FIELD_NAMES;
|
|
my ($k,$v) = _calculate_field($context, $fname, $fields);
|
|
$row{$k} = $v;
|
|
}
|
|
|
|
$c->log->debug("provisioning template - row: " . Dumper($context->{row}));
|
|
|
|
}
|
|
|
|
sub _init_contract_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
if (exists $template->{contract_contact}) {
|
|
my %contract_contact = ();
|
|
my @identifiers = _get_identifiers($template->{contract_contact});
|
|
foreach my $col (keys %{$template->{contract_contact}}) { #no inter-field dependency
|
|
next if $col eq $IDENTIFIER_FNAME;
|
|
my ($k,$v) = _calculate($context,$col, $template->{contract_contact}->{$col});
|
|
$contract_contact{$k} = $v;
|
|
}
|
|
if (exists $contract_contact{reseller}) {
|
|
$context->{_r_c} //= {};
|
|
if (exists $context->{_r_c}->{$contract_contact{reseller}}
|
|
or ($context->{_r_c}->{$contract_contact{reseller}} = $schema->resultset('resellers')->search_rs({
|
|
name => $contract_contact{reseller},
|
|
status => { '!=' => 'terminated' },
|
|
})->first)) {
|
|
$contract_contact{reseller_id} = $context->{_r_c}->{$contract_contact{reseller}}->id;
|
|
$context->{reseller} = { $context->{_r_c}->{$contract_contact{reseller}}->get_inflated_columns };
|
|
} else {
|
|
die("unknown reseller $contract_contact{reseller}");
|
|
}
|
|
delete $contract_contact{reseller};
|
|
}
|
|
$context->{contract_contact} = \%contract_contact;
|
|
if (scalar @identifiers) {
|
|
delete $contract_contact{id};
|
|
foreach my $e ($schema->resultset('contacts')->search_rs({
|
|
map { $_ => $contract_contact{$_}; } @identifiers
|
|
})->all) {
|
|
if ('terminated' ne $e->status) {
|
|
$contract_contact{id} = $e->id;
|
|
last;
|
|
}
|
|
}
|
|
} else {
|
|
delete $contract_contact{id};
|
|
}
|
|
$contract_contact{create_timestamp} //= $context->{now};
|
|
$contract_contact{modify_timestamp} //= $context->{now};
|
|
if (exists $contract_contact{timezone} and $contract_contact{timezone}) {
|
|
die("invalid timezone $contract_contact{timezone}") unless DateTime::TimeZone->is_valid_name($contract_contact{timezone});
|
|
}
|
|
|
|
$c->log->debug("provisioning template - contract contact: " . Dumper($context->{contract_contact}));
|
|
}
|
|
|
|
if (exists $template->{contract}) {
|
|
my %contract = ();
|
|
my @identifiers = _get_identifiers($template->{contract});
|
|
foreach my $col (keys %{$template->{contract}}) {
|
|
next if $col eq $IDENTIFIER_FNAME;
|
|
my ($k,$v) = _calculate($context,$col, $template->{contract}->{$col});
|
|
$contract{$k} = $v;
|
|
}
|
|
if (exists $contract{profile_package}) {
|
|
$context->{_pp_c} //= {};
|
|
if (exists $context->{_pp_c}->{$contract{profile_package}}
|
|
or ($context->{_pp_c}->{$contract{profile_package}} = $schema->resultset('profile_packages')->search_rs({
|
|
name => $contract{profile_package},
|
|
#reseller_id
|
|
#status => { '!=' => 'terminated' },
|
|
})->first)) {
|
|
$contract{profile_package_id} = $context->{_pp_c}->{$contract{profile_package}}->id;
|
|
$context->{profile_package} = { $context->{_pp_c}->{$contract{profile_package}}->get_inflated_columns };
|
|
} else {
|
|
die("unknown profile package $contract{profile_package}");
|
|
}
|
|
delete $contract{profile_package};
|
|
}
|
|
if (exists $contract{billing_profile}) {
|
|
$context->{_bp_c} //= {};
|
|
if (exists $context->{_bp_c}->{$contract{billing_profile}}
|
|
or ($context->{_bp_c}->{$contract{billing_profile}} = $schema->resultset('billing_profiles')->search_rs({
|
|
name => $contract{billing_profile},
|
|
#todo: reseller_id
|
|
status => { '!=' => 'terminated' },
|
|
})->first)) {
|
|
$contract{billing_profile_id} = $context->{_bp_c}->{$contract{billing_profile}}->id;
|
|
$context->{billing_profile} = { $context->{_bp_c}->{$contract{billing_profile}}->get_inflated_columns };
|
|
} else {
|
|
die("unknown billing profile $contract{billing_profile}");
|
|
}
|
|
delete $contract{billing_profile};
|
|
}
|
|
if (exists $contract{product}) {
|
|
$context->{_pr_c} //= {};
|
|
if (exists $context->{_pr_c}->{$contract{product}}
|
|
or ($context->{_pr_c}->{$contract{product}} = $schema->resultset('products')->search_rs({
|
|
name => $contract{product},
|
|
})->first)) {
|
|
$contract{product_id} = $context->{_pr_c}->{$contract{product}}->id;
|
|
$context->{product} = { $context->{_pr_c}->{$contract{product}}->get_inflated_columns };
|
|
} else {
|
|
die("unknown product $contract{product}");
|
|
}
|
|
delete $contract{product};
|
|
}
|
|
#todo: subscriber_email_template_id
|
|
#todo: passreset_email_template_id
|
|
#todo: invoice_email_template_id
|
|
#todo: invoice_template_id
|
|
|
|
$context->{contract} = \%contract;
|
|
if (scalar @identifiers) {
|
|
delete $contract{id};
|
|
foreach my $e ($schema->resultset('contracts')->search_rs({
|
|
map { $_ => $contract{$_}; } @identifiers
|
|
})->all) {
|
|
if ('terminated' ne $e->status) {
|
|
$contract{id} = $e->id;
|
|
last;
|
|
}
|
|
}
|
|
} else {
|
|
delete $contract{id};
|
|
}
|
|
$contract{create_timestamp} //= $context->{now};
|
|
$contract{modify_timestamp} //= $context->{now};
|
|
|
|
$context->{_bm} = [];
|
|
NGCP::Panel::Utils::BillingMappings::prepare_billing_mappings(
|
|
c => $c,
|
|
resource => $context->{contract},
|
|
old_resource => undef,
|
|
mappings_to_create => $context->{_bm},
|
|
err_code => sub {
|
|
my ($err) = @_;
|
|
die($err);
|
|
});
|
|
|
|
$c->log->debug("provisioning template - contract: " . Dumper($context->{contract}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sub _init_subscriber_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
{
|
|
my @identifiers = _get_identifiers($template->{subscriber});
|
|
my %subscriber = ();
|
|
foreach my $col (keys %{$template->{subscriber}}) {
|
|
next if $col eq $IDENTIFIER_FNAME;
|
|
my ($k,$v) = _calculate($context,$col, $template->{subscriber}->{$col});
|
|
$subscriber{$k} = $v;
|
|
}
|
|
if (exists $subscriber{domain}) {
|
|
$context->{_bd_c} //= {};
|
|
if (exists $context->{_bd_c}->{$subscriber{domain}}
|
|
or ($context->{_bd_c}->{$subscriber{domain}} = $schema->resultset('domains')->search_rs({
|
|
domain => $subscriber{domain},
|
|
#todo: reseller_id
|
|
#status => { '!=' => 'terminated' },
|
|
})->first)) {
|
|
$subscriber{domain_id} = $context->{_bd_c}->{$subscriber{domain}}->id;
|
|
$context->{domain} = { $context->{_bd_c}->{$subscriber{domain}}->get_inflated_columns };
|
|
$context->{provisioning_domain} = { $schema->resultset('voip_domains')->find(
|
|
{domain => $subscriber{domain}})->get_inflated_columns };
|
|
} else {
|
|
die("unknown domain $subscriber{domain}");
|
|
}
|
|
delete $subscriber{domain};
|
|
}
|
|
#todo: profile_id
|
|
#todo: profile_set_id
|
|
|
|
$context->{subscriber} = \%subscriber;
|
|
|
|
my $item;
|
|
if (scalar @identifiers) {
|
|
delete $subscriber{id};
|
|
foreach my $e ($schema->resultset('voip_subscribers')->search_rs({
|
|
map { $_ => $subscriber{$_}; } @identifiers
|
|
},{
|
|
#join => 'domain',
|
|
})->all) {
|
|
if ('terminated' ne $e->status) {
|
|
$subscriber{id} = $e->id;
|
|
$item = $e;
|
|
last;
|
|
}
|
|
}
|
|
} else {
|
|
delete $subscriber{id};
|
|
}
|
|
|
|
$context->{subscriber}->{customer_id} //= $context->{contract}->{id};
|
|
|
|
$context->{_cs} = NGCP::Panel::Utils::Subscriber::prepare_resource(
|
|
c => $c,
|
|
schema => $schema,
|
|
resource => $context->{subscriber},
|
|
err_code => sub {
|
|
my ($code, $msg) = @_;
|
|
die($msg);
|
|
},
|
|
validate_code => sub {
|
|
my ($res) = @_;
|
|
#todo
|
|
return 1;
|
|
},
|
|
getcustomer_code => sub {
|
|
my ($cid) = @_;
|
|
my $contract = $schema->resultset('contracts')->find($cid);
|
|
NGCP::Panel::Utils::Contract::acquire_contract_rowlocks(
|
|
c => $c, schema => $schema, contract_id => $contract->id) if $contract;
|
|
return $contract;
|
|
},
|
|
item => $item,
|
|
);
|
|
|
|
$c->log->debug("provisioning template - subscriber: " . Dumper($context->{subscriber}));
|
|
}
|
|
}
|
|
|
|
sub _init_subscriber_preferences_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
if (exists $template->{subscriber_preferences}) {
|
|
$context->{_cp} = NGCP::Panel::Utils::Preferences::prepare_resource(
|
|
c => $c,
|
|
schema => $schema,
|
|
item => $schema->resultset('voip_subscribers')->find({
|
|
id => $context->{subscriber}->{id},
|
|
}),
|
|
type => 'subscribers',
|
|
);
|
|
|
|
my %subscriber_preferences = %{$context->{_cp}}; #merge
|
|
foreach my $col (keys %{$template->{subscriber_preferences}}) {
|
|
my ($k,$v) = _calculate($context,$col, $template->{subscriber_preferences}->{$col});
|
|
$subscriber_preferences{$k} = $v;
|
|
}
|
|
$context->{subscriber_preferences} = \%subscriber_preferences;
|
|
|
|
$c->log->debug("provisioning template - subscriber preferences: " . Dumper($context->{subscriber_preferences}));
|
|
}
|
|
|
|
}
|
|
|
|
sub _init_contract_preferences_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
if (exists $template->{contract_preferences}) {
|
|
|
|
$context->{_cp} = NGCP::Panel::Utils::Preferences::prepare_resource(
|
|
c => $c,
|
|
schema => $schema,
|
|
item => $schema->resultset('contracts')->find({
|
|
id => $context->{contract}->{id},
|
|
}),
|
|
type => 'contracts',
|
|
);
|
|
|
|
my %contract_preferences = %{$context->{_cp}}; #merge
|
|
foreach my $col (keys %{$template->{contract_preferences}}) {
|
|
my ($k,$v) = _calculate($context,$col, $template->{contract_preferences}->{$col});
|
|
$contract_preferences{$k} = $v;
|
|
}
|
|
$context->{contract_preferences} = \%contract_preferences;
|
|
|
|
$c->log->debug("provisioning template - contract preferences: " . Dumper($context->{contract_preferences}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sub _init_registrations_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
foreach my $template_registration (@{_force_array($template->{registrations})}) {
|
|
my %registration = ();
|
|
foreach my $col (keys %$template_registration) {
|
|
my ($k,$v) = _calculate($context,$col, $template_registration->{$col});
|
|
$registration{$k} = $v;
|
|
}
|
|
|
|
push(@{$context->{registrations}}, \%registration);
|
|
|
|
$registration{flags} = 0;
|
|
$registration{cflags} = 256;
|
|
$registration{cflags} |= 128 if($registration{nat});
|
|
|
|
}
|
|
|
|
$c->log->debug("provisioning template - registrations: " . Dumper($context->{registrations}));
|
|
|
|
}
|
|
|
|
sub _init_trusted_sources_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
my $subscriber = $schema->resultset('voip_subscribers')->find({
|
|
id => $context->{subscriber}->{id},
|
|
});
|
|
|
|
foreach my $template_trusted_source (@{_force_array($template->{trusted_sources})}) {
|
|
my %trusted_source = ();
|
|
foreach my $col (keys %$template_trusted_source) {
|
|
my ($k,$v) = _calculate($context,$col, $template_trusted_source->{$col});
|
|
$trusted_source{$k} = $v;
|
|
}
|
|
|
|
push(@{$context->{trusted_sources}}, \%trusted_source);
|
|
|
|
$trusted_source{subscriber_id} = $subscriber->provisioning_voip_subscriber->id;
|
|
$trusted_source{uuid} = $subscriber->uuid;
|
|
|
|
}
|
|
|
|
$c->log->debug("provisioning template - trusted sources: " . Dumper($context->{trusted_sources}));
|
|
|
|
}
|
|
|
|
sub _init_cf_mappings_context {
|
|
|
|
my ($c, $context, $schema, $template) = @_;
|
|
|
|
if (exists $template->{cf_mappings}) {
|
|
my %cf_mappings = ();
|
|
foreach my $col (keys %{$template->{cf_mappings}}) {
|
|
my ($k,$v) = _calculate($context,$col, $template->{cf_mappings}->{$col});
|
|
$cf_mappings{$k} = $v;
|
|
}
|
|
|
|
$context->{cf_mappings} = \%cf_mappings;
|
|
|
|
$c->log->debug("provisioning template - cf mappings: " . Dumper($context->{cf_mappings}));
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_contract_contact {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
if (exists $context->{contract_contact}) {
|
|
unless ($context->{contract_contact}->{id}) {
|
|
my $contact = $schema->resultset('contacts')->create(
|
|
$context->{contract_contact},
|
|
);
|
|
$context->{contract_contact}->{id} = $contact->id;
|
|
|
|
$c->log->debug("provisioning template - contract contact id $context->{contract_contact}->{id} created");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_contract {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
if (exists $context->{contract}) {
|
|
unless ($context->{contract}->{id}) {
|
|
$context->{contract}->{contact_id} //= $context->{contract_contact}->{id};
|
|
die("contact_id for contract required") unless $context->{contract}->{contact_id};
|
|
my $contract = $schema->resultset('contracts')->create(
|
|
$context->{contract},
|
|
);
|
|
$context->{contract}->{id} = $contract->id;
|
|
NGCP::Panel::Utils::BillingMappings::append_billing_mappings(c => $c,
|
|
contract => $contract,
|
|
mappings_to_create => $context->{_bm},
|
|
);
|
|
NGCP::Panel::Utils::ProfilePackages::create_initial_contract_balances(c => $c,
|
|
contract => $contract,
|
|
);
|
|
$c->log->debug("provisioning template - contract id $context->{contract}->{id} created");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_subscriber {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
unless ($context->{subscriber}->{id}) {
|
|
my $error_info = { extended => {} };
|
|
|
|
my @events_to_create = ();
|
|
my $event_context = { events_to_create => \@events_to_create };
|
|
my $subscriber = NGCP::Panel::Utils::Subscriber::create_subscriber(
|
|
c => $c,
|
|
schema => $schema,
|
|
contract => $context->{_cs}->{customer},
|
|
params => $context->{_cs}->{resource},
|
|
preferences => $context->{_cs}->{preferences},
|
|
admin_default => 0,
|
|
event_context => $event_context,
|
|
error => $error_info,
|
|
);
|
|
$context->{subscriber}->{id} = $subscriber->id;
|
|
if($context->{_cs}->{resource}->{status} eq 'locked') {
|
|
NGCP::Panel::Utils::Subscriber::lock_provisoning_voip_subscriber(
|
|
c => $c,
|
|
prov_subscriber => $subscriber->provisioning_voip_subscriber,
|
|
level => $context->{_cs}->{resource}->{lock} || 4,
|
|
);
|
|
} else {
|
|
NGCP::Panel::Utils::ProfilePackages::underrun_lock_subscriber(c => $c, subscriber => $subscriber);
|
|
}
|
|
NGCP::Panel::Utils::Subscriber::update_subscriber_numbers(
|
|
c => $c,
|
|
schema => $schema,
|
|
alias_numbers => $context->{_cs}->{alias_numbers},
|
|
reseller_id => $context->{_cs}->{customer}->contact->reseller_id,
|
|
subscriber_id => $subscriber->id,
|
|
);
|
|
$subscriber->discard_changes; # reload row because of new number
|
|
NGCP::Panel::Utils::Subscriber::manage_pbx_groups(
|
|
c => $c,
|
|
schema => $schema,
|
|
groups => $context->{_cs}->{groups},
|
|
groupmembers => $context->{_cs}->{groupmembers},
|
|
customer => $context->{_cs}->{customer},
|
|
subscriber => $subscriber,
|
|
);
|
|
NGCP::Panel::Utils::Events::insert_deferred(
|
|
c => $c, schema => $schema,
|
|
events_to_create => \@events_to_create,
|
|
);
|
|
|
|
$c->log->debug("provisioning template - subscriber id $context->{subscriber}->{id} created");
|
|
}
|
|
}
|
|
|
|
sub _create_subscriber_preferences {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
if (exists $context->{subscriber_preferences}) {
|
|
NGCP::Panel::Utils::Preferences::update_preferences(
|
|
c => $c,
|
|
schema => $schema,
|
|
item => $schema->resultset('voip_subscribers')->find({
|
|
id => $context->{subscriber}->{id},
|
|
}),
|
|
old_resource => $context->{_cp},
|
|
resource => $context->{subscriber_preferences},
|
|
type => 'subscribers',
|
|
replace => 0,
|
|
err_code => sub {
|
|
my ($code, $msg) = @_;
|
|
die($msg);
|
|
},
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_contract_preferences {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
if (exists $context->{contract_preferences}) {
|
|
NGCP::Panel::Utils::Preferences::update_preferences(
|
|
c => $c,
|
|
schema => $schema,
|
|
item => $schema->resultset('contracts')->find({
|
|
id => $context->{contract}->{id},
|
|
}),
|
|
old_resource => $context->{_cp},
|
|
resource => $context->{contract_preferences},
|
|
type => 'contracts',
|
|
replace => 0,
|
|
err_code => sub {
|
|
my ($code, $msg) = @_;
|
|
die($msg);
|
|
},
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_registrations {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
my $subscriber = $schema->resultset('voip_subscribers')->find({
|
|
id => $context->{subscriber}->{id},
|
|
});
|
|
|
|
foreach my $registration (@{$context->{registrations}}) {
|
|
my $ret = NGCP::Panel::Utils::Kamailio::create_location($c,
|
|
$subscriber->provisioning_voip_subscriber,
|
|
$registration
|
|
);
|
|
die("failed to create registration") unless $ret->[0]->[1];
|
|
$context->{_dfrd}->{kamailio_flush} //= 0;
|
|
$context->{_dfrd}->{kamailio_flush} += 1;
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_trusted_sources {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
foreach my $trusted_source (@{$context->{trusted_sources}}) {
|
|
$schema->resultset('voip_trusted_sources')->create($trusted_source);
|
|
$context->{_dfrd}->{kamailio_trusted_reload} //= 0;
|
|
$context->{_dfrd}->{kamailio_trusted_reload} += 1;
|
|
}
|
|
|
|
}
|
|
|
|
sub _create_cf_mappings {
|
|
|
|
my ($c, $context, $schema) = @_;
|
|
|
|
if (exists $context->{cf_mappings}) {
|
|
my $subscriber = $schema->resultset('voip_subscribers')->find({
|
|
id => $context->{subscriber}->{id},
|
|
});
|
|
|
|
$subscriber = NGCP::Panel::Utils::CallForwards::update_cf_mappings(
|
|
c => $c,
|
|
resource => $context->{cf_mappings},
|
|
item => $subscriber,
|
|
err_code => sub {
|
|
my ($msg) = @_;
|
|
die($msg);
|
|
},
|
|
validate_mapping_code => sub {
|
|
my $res = shift;
|
|
#todo
|
|
return 1;
|
|
#return $self->validate_form(
|
|
# c => $c,
|
|
# form => $form,
|
|
# resource => $res,
|
|
#);
|
|
},
|
|
validate_destination_set_code => sub {
|
|
my $res = shift;
|
|
#todo
|
|
return 1;
|
|
#return $self->validate_form(
|
|
# c => $c,
|
|
# form => NGCP::Panel::Role::API::CFDestinationSets->get_form($c),
|
|
# resource => $res,
|
|
#);
|
|
},
|
|
validate_time_set_code => sub {
|
|
my $res = shift;
|
|
#todo
|
|
return 1;
|
|
#return $self->validate_form(
|
|
# c => $c,
|
|
# form => NGCP::Panel::Role::API::CFTimeSets->get_form($c),
|
|
# resource => $res,
|
|
#);
|
|
},
|
|
validate_source_set_code => sub {
|
|
my $res = shift;
|
|
#todo
|
|
return 1;
|
|
#return $self->validate_form(
|
|
# c => $c,
|
|
# form => NGCP::Panel::Role::API::CFSourceSets->get_form($c),
|
|
# resource => $res,
|
|
#);
|
|
},
|
|
validate_bnumber_set_code => sub {
|
|
my $res = shift;
|
|
#todo
|
|
return 1;
|
|
#return $self->validate_form(
|
|
# c => $c,
|
|
# form => NGCP::Panel::Role::API::CFBNumberSets->get_form($c),
|
|
# resource => $res,
|
|
#);
|
|
},
|
|
params => undef,
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
sub _calculate_field {
|
|
|
|
my ($context,$fname,$fields) = @_;
|
|
my ($f, $col, $cr) = ($fields->{$fname}, undef, undef);
|
|
if (exists $f->{$FIELD_VALUE_ATTRIBUTE}) {
|
|
($col, $cr) = ($fname, $f->{$FIELD_VALUE_ATTRIBUTE});
|
|
} elsif (exists $f->{$FIELD_VALUE_ATTRIBUTE . $CODE_SUFFIX_FNAME}) {
|
|
($col, $cr) = ($fname . $CODE_SUFFIX_FNAME, $f->{$FIELD_VALUE_ATTRIBUTE . $CODE_SUFFIX_FNAME});
|
|
}
|
|
return _calculate($context, $col, $cr);
|
|
}
|
|
|
|
sub _calculate {
|
|
|
|
my ($context,$f,$c) = @_;
|
|
if ($f =~ /^([a-z0-9_]+)$CODE_SUFFIX_FNAME$/) {
|
|
return _switch_lang(
|
|
$context,
|
|
$context->{_lang},
|
|
perl => sub {
|
|
my $cl;
|
|
if ($STRICT_CLOSURE) {
|
|
$cl = eval_closure(
|
|
source => ($PERL_ENV . $c),
|
|
environment => {
|
|
map { if ('ARRAY' eq ref $context->{$_}) {
|
|
('@' . $_) => $context->{$_};
|
|
} elsif ('HASH' eq ref $context->{$_}) {
|
|
('%' . $_) => $context->{$_};
|
|
} elsif ($JE_ANON_CLASS eq ref $context->{$_}) {
|
|
('%' . $_) => _unbless($context->{$_});
|
|
} elsif ('CODE' eq ref $context->{$_}) {
|
|
('&' . $_) => $context->{$_};
|
|
} elsif (ref $context->{$_}) {
|
|
('$' . $_) => \$context->{$_};
|
|
} else {
|
|
('$' . $_) => \$context->{$_};
|
|
} } grep { substr($_,0,1) ne '_'; } keys %$context
|
|
},
|
|
terse_error => 0,
|
|
description => $f,
|
|
alias => 0,
|
|
);
|
|
} else {
|
|
$context->{_cr_c} //= {};
|
|
if (exists $context->{_cr_c}->{$c}) {
|
|
$cl = $context->{_cr_c}->{$c};
|
|
} else {
|
|
## no critic (BuiltinFunctions::ProhibitStringyEval)
|
|
$cl = eval($PERL_ENV . $c);
|
|
$context->{_cr_c}->{$c} = $cl;
|
|
}
|
|
}
|
|
die("$f: " . $@) if $@;
|
|
my $v;
|
|
eval {
|
|
$v = $cl->($context);
|
|
$v = _unbless($v) if ($v and $JE_ANON_CLASS eq ref $v);
|
|
};
|
|
if ($@) {
|
|
die("$f: " . $@);
|
|
}
|
|
return ($1 => $v);
|
|
},
|
|
js => sub {
|
|
|
|
$context->{_je}->eval(join (";\n",
|
|
map { if ('CODE' eq ref $context->{$_}) {
|
|
die('no coderefs allowed');
|
|
} elsif (('ARRAY' eq ref $context->{$_})
|
|
or ('HASH' eq ref $context->{$_})
|
|
or ($JE_ANON_CLASS eq ref $context->{$_})) {
|
|
if ($context->{_je_env}->{$_}) {
|
|
$_ . ' = ' . _to_json($context->{$_});
|
|
} else { $context->{_je_env}->{$_} = 1;
|
|
'var ' . $_ . ' = ' . _to_json($context->{$_}); }
|
|
} elsif (ref $context->{$_}) {
|
|
die('no refs allowed');
|
|
} else { if ($context->{_je_env}->{$_}) {
|
|
$_ . " = '" . escape_js($context->{$_}) . "'";
|
|
} else { $context->{_je_env}->{$_} = 1;
|
|
'var ' . $_ . " = '" . escape_js($context->{$_}) . "'"; }
|
|
} } grep { substr($_,0,1) ne '_'; } keys %$context) .
|
|
";\n_func = $c;");
|
|
die("$f: " . $@) if $@;
|
|
my $v;
|
|
eval {
|
|
$v = _unbox_je_value($context->{_je}->eval('_func();'));
|
|
};
|
|
if ($@) {
|
|
die("$f: " . $@);
|
|
}
|
|
return ($1 => $v);
|
|
|
|
}
|
|
);
|
|
} elsif ('HASH' eq ref $c) {
|
|
my %data = ();
|
|
foreach my $col (keys %$c) {
|
|
my ($k,$v) = _calculate($context,$col, $c->{$col});
|
|
$data{$k} = $v;
|
|
}
|
|
return ($f => \%data);
|
|
} elsif ('ARRAY' eq ref $c) {
|
|
my @data = ();
|
|
my $i = 0;
|
|
foreach my $el (@$c) {
|
|
my ($k,$v) = _calculate($context, $f . '[' . $i . ']', $el);
|
|
push(@data,$v);
|
|
$i++;
|
|
}
|
|
return ($f => \@data);
|
|
} else {
|
|
return ($f => $c);
|
|
}
|
|
|
|
}
|
|
|
|
sub _unbox_je_value {
|
|
|
|
my $v = shift;
|
|
return unless defined $v;
|
|
if ((ref $v) =~ /^JE::/) {
|
|
$v = $v->value;
|
|
} elsif ($JE_ANON_CLASS eq ref $v) {
|
|
$v = _unbless($v);
|
|
}
|
|
if ('ARRAY' eq ref $v) {
|
|
return [ map { _unbox_je_value($_); } @$v ];
|
|
} elsif ('HASH' eq ref $v) {
|
|
return { map { $_ => _unbox_je_value($v->{$_}); } keys %$v };
|
|
} else {
|
|
return $v;
|
|
}
|
|
|
|
}
|
|
|
|
sub _unbless {
|
|
my $obj = shift;
|
|
return { %$obj }; #unbless
|
|
};
|
|
|
|
sub _to_json {
|
|
return JSON::to_json(shift, {
|
|
allow_nonref => 1, allow_blessed => 1,
|
|
convert_blessed => 1, pretty => 0 });
|
|
}
|
|
|
|
sub _generate_username {
|
|
|
|
my ($length, @chars) = @_;
|
|
return unless $length;
|
|
@chars = ("A".."Z", "a".."z", "0".."9") unless scalar @chars;
|
|
my $username = '';
|
|
$username .= $chars[rand @chars] for 1..$length;
|
|
return $username;
|
|
|
|
}
|
|
|
|
sub get_fields {
|
|
|
|
my ($c, $calculated) = @_;
|
|
my $template = $c->stash->{provisioning_template_name};
|
|
my %fields = ();
|
|
my $fields_tied = tie(%fields, 'Tie::IxHash');
|
|
foreach my $f (@{_force_array($c->stash->{provisioning_templates}->{$template}->{fields})}) {
|
|
next unless ($f->{$FIELD_TYPE_ATTRIBUTE} =~ /calculated/i ? $calculated : not $calculated);
|
|
$fields{$f->{name}} = { %$f };
|
|
}
|
|
return \%fields;
|
|
|
|
}
|
|
|
|
sub _get_duplicate_subs {
|
|
|
|
my ($c, $context, $schema, $e, $get) = @_;
|
|
my $idx;
|
|
my $val;
|
|
my $msg;
|
|
my $subscriber;
|
|
if ($e =~ /Duplicate entry '([^']+)' for key '([^']+)'/) {
|
|
$idx = $2;
|
|
$val = $1;
|
|
}
|
|
if ($e =~ /alias '([^']+)' already exists/) {
|
|
$val = $1;
|
|
$msg = $e;
|
|
if ($get) {
|
|
$c->log->debug("provisioning template - $msg");
|
|
my %subs = ();
|
|
foreach my $alias ($c->model('DB')->resultset('voip_dbaliases')->search_rs({
|
|
username => $val,
|
|
},undef)->all) {
|
|
$subscriber = $alias->subscriber->voip_subscriber;
|
|
$subs{$subscriber->id} = $subscriber unless exists $subs{$subscriber->id};
|
|
}
|
|
$subscriber = [ values %subs ];
|
|
}
|
|
} elsif ($idx and 'number_idx' eq $idx) {
|
|
$msg = "number already exists: $e";
|
|
if ($get) {
|
|
$c->log->debug("provisioning template - $msg");
|
|
my %flt;
|
|
@flt{qw(cc ac sn)} = split(/-/,$val);
|
|
$subscriber = $c->model('DB')->resultset('voip_numbers')->find({
|
|
%flt
|
|
})->subscriber;
|
|
}
|
|
} elsif (not $idx or ($idx and 'user_dom_idx' eq $idx)) {
|
|
$msg = "username already exists: $e";
|
|
if ($get) {
|
|
$c->log->debug("provisioning template - $msg");
|
|
$subscriber = $schema->resultset('provisioning_voip_subscribers')->find({
|
|
username => $context->{subscriber}->{username},
|
|
domain_id => $context->{provisioning_domain}->{id},
|
|
})->voip_subscriber;
|
|
}
|
|
} elsif ($idx and 'webuser_dom_idx' eq $idx) {
|
|
$msg = "webusername already exists: $e";
|
|
if ($get) {
|
|
$c->log->debug("provisioning template - $msg");
|
|
$subscriber = $schema->resultset('provisioning_voip_subscribers')->find({
|
|
webusername => $context->{subscriber}->{webusername},
|
|
domain_id => $context->{provisioning_domain}->{id},
|
|
})->voip_subscriber;
|
|
}
|
|
} else {
|
|
$msg = "$e";
|
|
}
|
|
|
|
return ($msg, $subscriber);
|
|
|
|
}
|
|
|
|
sub _get_identifiers {
|
|
|
|
my ($template_item) = @_;
|
|
my $identifier = (exists $template_item->{$IDENTIFIER_FNAME} ? $template_item->{$IDENTIFIER_FNAME} : undef);
|
|
my @identifiers = ();
|
|
if (length($identifier)) {
|
|
@identifiers = map { $_ =~ s/^\s|\s$//gr; } split(/[,; \t]+/,$identifier);
|
|
}
|
|
return @identifiers;
|
|
|
|
}
|
|
|
|
sub _switch_lang {
|
|
|
|
my ($context, $lang, %code) = @_;
|
|
|
|
die('scripting lang not defined') unless $lang;
|
|
die("unknown scripting lang '$lang'") unless exists $code{$lang};
|
|
die("scripting lang '$lang' not supported") unless grep { $_ eq $lang } @SUPPORTED_LANGS;
|
|
return &{$code{$lang}}($context);
|
|
|
|
}
|
|
|
|
sub _force_array {
|
|
my $value = shift;
|
|
$value //= [];
|
|
$value = [ $value ] if 'ARRAY' ne ref $value;
|
|
return $value;
|
|
}
|
|
|
|
1;
|