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.
456 lines
14 KiB
456 lines
14 KiB
package NGCP::Panel::Utils::Phonebook;
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Sipwise::Base;
|
|
use English;
|
|
use NGCP::Panel::Utils::Generic qw(:all);
|
|
use NGCP::Panel::Utils::Message;
|
|
|
|
sub get_reseller_phonebook {
|
|
my ($c, $reseller_id) = @_;
|
|
my @pb;
|
|
|
|
my $r_pb_rs = $c->model('DB')->resultset('reseller_phonebook')->search({
|
|
reseller_id => $reseller_id,
|
|
});
|
|
|
|
for my $r ($r_pb_rs->all) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
}
|
|
|
|
return [ sort { $a->{name} cmp $b->{name} } @pb ];
|
|
}
|
|
|
|
sub get_contract_phonebook {
|
|
my ($c, $contract_id) = @_;
|
|
my @pb;
|
|
my %c_numbers;
|
|
|
|
my $contract = $c->model('DB')->resultset('contracts')->search({
|
|
id => $contract_id,
|
|
})->first;
|
|
|
|
my $r_pb_rs = $c->model('DB')->resultset('reseller_phonebook')->search({
|
|
reseller_id => $contract->contact->reseller->id,
|
|
});
|
|
|
|
my $c_pb_rs = $c->model('DB')->resultset('contract_phonebook')->search({
|
|
contract_id => $contract_id,
|
|
});
|
|
|
|
for my $r ($c_pb_rs->all) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
$c_numbers{$r->number} = $r->name;
|
|
}
|
|
|
|
for my $r ($r_pb_rs->all) {
|
|
unless (exists $c_numbers{$r->number}) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
}
|
|
}
|
|
|
|
return [ sort { $a->{name} cmp $b->{name} } @pb ];
|
|
}
|
|
|
|
sub get_subscriber_phonebook {
|
|
my ($c, $subscriber_id) = @_;
|
|
my @pb;
|
|
my %c_numbers;
|
|
|
|
my $sub = $c->model('DB')->resultset('voip_subscribers')->search({
|
|
id => $subscriber_id,
|
|
})->first;
|
|
|
|
my $r_pb_rs = $c->model('DB')->resultset('reseller_phonebook')->search({
|
|
reseller_id => $sub->contract->contact->reseller->id,
|
|
});
|
|
|
|
my $c_pb_rs = $c->model('DB')->resultset('contract_phonebook')->search({
|
|
contract_id => $sub->contract_id,
|
|
});
|
|
|
|
my $a_pb_rs = $c->model('DB')->resultset('subscriber_phonebook')->search({
|
|
shared => 1,
|
|
'contract.id' => $sub->contract_id,
|
|
},{
|
|
join => { 'subscriber' => 'contract' },
|
|
});
|
|
|
|
my $s_pb_rs = $c->model('DB')->resultset('subscriber_phonebook')->search({
|
|
subscriber_id => $subscriber_id,
|
|
});
|
|
|
|
for my $r ($s_pb_rs->all) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
$c_numbers{$r->number} = $r->name;
|
|
}
|
|
|
|
for my $r ($c_pb_rs->all) {
|
|
unless (exists $c_numbers{$r->number}) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
$c_numbers{$r->number} = $r->name;
|
|
}
|
|
}
|
|
|
|
for my $r ($a_pb_rs->all) {
|
|
unless (exists $c_numbers{$r->number}) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
$c_numbers{$r->number} = $r->name;
|
|
}
|
|
}
|
|
|
|
for my $r ($r_pb_rs->all) {
|
|
unless (exists $c_numbers{$r->number}) {
|
|
push @pb, { name => $r->name, number => $r->number };
|
|
}
|
|
}
|
|
|
|
return [ sort { $a->{name} cmp $b->{name} } @pb ];
|
|
}
|
|
|
|
sub ui_upload_csv {
|
|
my ($c, $rs, $form, $owner, $owner_id, $action, $back) = @_;
|
|
|
|
my $upload = $c->req->upload('upload_phonebook');
|
|
my $posted = $c->req->method eq 'POST';
|
|
my @params = ( upload_phonebook => $posted ? $upload : undef, );
|
|
$form->process(
|
|
posted => $posted,
|
|
params => { @params },
|
|
action => $action,
|
|
);
|
|
if($form->validated) {
|
|
unless($upload) {
|
|
NGCP::Panel::Utils::Message::error(
|
|
c => $c,
|
|
desc => $c->loc('No phonebook entries file specified!'),
|
|
);
|
|
$c->response->redirect($back);
|
|
return;
|
|
}
|
|
my $data = $upload->slurp;
|
|
my($entries, $fails, $text_success);
|
|
try {
|
|
my ($entries, $fails, $text) =
|
|
upload_csv($c, $rs, $owner, $owner_id,
|
|
$c->req->params->{purge_existing}, \$data);
|
|
NGCP::Panel::Utils::Message::info(
|
|
c => $c,
|
|
desc => $$text,
|
|
);
|
|
} catch($e) {
|
|
NGCP::Panel::Utils::Message::error(
|
|
c => $c,
|
|
error => $e,
|
|
desc => $c->loc('Failed to upload Phonebook entries'),
|
|
);
|
|
}
|
|
|
|
$c->response->redirect($back);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub upload_csv {
|
|
my ($c, $rs, $owner, $owner_id, $purge, $data) = @_;
|
|
|
|
my $csv = Text::CSV_XS->new({ allow_whitespace => 1, binary => 1, keep_meta_info => 1 });
|
|
|
|
my $schema = $c->model('DB');
|
|
|
|
my @cols = qw/name number/;
|
|
my @fields ;
|
|
my @fails = ();
|
|
my $linenum = 0;
|
|
my @entries;
|
|
|
|
my $no_access = 0;
|
|
my %contract_ids;
|
|
my %subscriber_ids;
|
|
# check user access to owner_id
|
|
if ($c->user->roles eq 'reseller') {
|
|
if ($owner_id && $owner eq 'reseller' && $owner_id != $c->user->reseller_id) {
|
|
$no_access = 1;
|
|
} elsif ($owner eq 'contract') {
|
|
my $found = 0;
|
|
foreach my $row ($schema->resultset('contracts')->search({
|
|
'contact.reseller_id' => $c->user->reseller_id,
|
|
},{
|
|
select => ['me.id'],
|
|
as => ['id'],
|
|
join => 'contact',
|
|
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
|
|
})->all) {
|
|
my $id = $row->{id};
|
|
if ($owner_id) {
|
|
if ($id == $owner_id) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
} else {
|
|
$contract_ids{$id} = 1;
|
|
}
|
|
}
|
|
if ($owner_id) {
|
|
$no_access = 1 unless $found;
|
|
}
|
|
} elsif ($owner eq 'subscriber') {
|
|
my $found = 0;
|
|
foreach my $row ($schema->resultset('voip_subscribers')->search({
|
|
'contact.reseller_id' => $c->user->reseller_id,
|
|
},{
|
|
select => ['me.id'],
|
|
as => ['id'],
|
|
join => { 'contract' => 'contact' },
|
|
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
|
|
})->all) {
|
|
my $id = $row->{id};
|
|
if ($owner_id) {
|
|
if ($id == $owner_id) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
} else {
|
|
$subscriber_ids{$id} = 1;
|
|
}
|
|
}
|
|
if ($owner_id) {
|
|
$no_access = 1 unless $found;
|
|
}
|
|
}
|
|
} elsif ($c->user->roles eq 'subscriberadmin') {
|
|
if ($owner_id && $owner eq 'reseller') {
|
|
$no_access = 1;
|
|
} elsif ($owner_id && $owner eq 'contract' && $owner_id != $c->user->account_id) {
|
|
$no_access = 1;
|
|
} elsif ($owner eq 'subscriber') {
|
|
my $found = 0;
|
|
foreach my $row ($schema->resultset('voip_subscribers')->search({
|
|
'contract_id' => $c->user->account_id,
|
|
},{
|
|
select => ['me.id'],
|
|
as => ['id'],
|
|
result_class => 'DBIx::Class::ResultClass::HashRefInflator',
|
|
})->all) {
|
|
my $id = $row->{id};
|
|
if ($owner_id) {
|
|
if ($id == $owner_id) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
} else {
|
|
$subscriber_ids{$id} = 1;
|
|
}
|
|
}
|
|
if ($owner_id) {
|
|
$no_access = 1 unless $found;
|
|
}
|
|
}
|
|
} elsif ($owner_id && $c->user->roles eq 'subscriber') {
|
|
if ($owner eq 'reseller') {
|
|
$no_access = 1;
|
|
} elsif ($owner eq 'contract') {
|
|
$no_access = 1;
|
|
} elsif ($owner eq 'subscriber' && $owner_id != $c->user->voip_subscriber->id) {
|
|
$no_access = 1;
|
|
}
|
|
}
|
|
|
|
if ($no_access) {
|
|
my $accepted = 0;
|
|
my (@entries, @fails);
|
|
my $text = 'Phonebook entries upload failed: ';
|
|
$text .= "user does not have access to $owner with id $owner_id\n";
|
|
return (\@entries, \@fails, \$text);
|
|
}
|
|
|
|
$schema->txn_do(sub {
|
|
|
|
open(my $fh, '<:encoding(utf8)', $data);
|
|
|
|
while ( my $line = <$fh> ){
|
|
++$linenum;
|
|
next unless length $line;
|
|
unless($csv->parse($line)) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
@fields = $csv->fields();
|
|
|
|
my $row = {};
|
|
|
|
if ($owner_id) {
|
|
# name,number
|
|
if ($owner ne 'subscriber' && scalar @fields >= 2) {
|
|
@{$row}{@cols} = splice @fields, 0, 2;
|
|
# name,number,shared
|
|
} elsif ($owner eq 'subscriber' && scalar @fields >= 3) {
|
|
my $shared = int($fields[2]);
|
|
if ($shared != 1 && $shared != 0) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
@{$row}{@cols,'shared'} = splice @fields, 0, 3;
|
|
} else {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
$row->{$owner.'_id'} = $owner_id;
|
|
} else {
|
|
if ($owner eq 'reseller') {
|
|
# name,number,reseller_id
|
|
if (scalar @fields != 3) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
my $reseller_id_f = int($fields[2]);
|
|
if ($c->user->roles eq 'admin') {
|
|
@{$row}{@cols,'reseller_id'} = @fields;
|
|
} elsif ($c->user->roles eq 'reseller' &&
|
|
$reseller_id_f == $c->user->reseller_id) {
|
|
@{$row}{@cols,'reseller_id'} = @fields;
|
|
} else {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
} elsif ($owner eq 'contract') {
|
|
# name,number,contract_id
|
|
if (scalar @fields != 3) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
my $contract_id_f = int($fields[2]);
|
|
if ($c->user->roles eq 'admin') {
|
|
@{$row}{@cols,'contract_id'} = @fields;
|
|
} elsif ($c->user->roles eq 'reseller' &&
|
|
exists $contract_ids{$contract_id_f}) {
|
|
@{$row}{@cols,'contract_id'} = @fields;
|
|
} elsif ($c->user->roles eq 'subscriberadmin' &&
|
|
$contract_id_f == $c->user->account_id) {
|
|
@{$row}{@cols,'contract_id'} = @fields;
|
|
} else {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
} elsif ($owner eq 'subscriber') {
|
|
# name,number,shared,subscriber_id
|
|
if (scalar @fields != 4) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
my $subscriber_id_f = int($fields[3]);
|
|
my $shared = int($fields[2]);
|
|
if ($shared != 1 && $shared != 0) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
if ($c->user->roles eq 'admin') {
|
|
@{$row}{@cols,'shared','subscriber_id'} = @fields;
|
|
} elsif ($c->user->roles eq 'reseller' &&
|
|
exists $subscriber_ids{$subscriber_id_f}) {
|
|
@{$row}{@cols,'shared','subscriber_id'} = @fields;
|
|
} elsif ($c->user->roles eq 'subscriberadmin' &&
|
|
exists $subscriber_ids{$subscriber_id_f}) {
|
|
@{$row}{@cols,'shared','subscriber_id'} = @fields;
|
|
} elsif ($c->user->roles eq 'subscriber' &&
|
|
$subscriber_id_f == $c->user->voip_subscriber->id) {
|
|
@{$row}{@cols,'shared','subscriber_id'} = @fields;
|
|
} else {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
} else {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
}
|
|
|
|
unless ($purge) {
|
|
try {
|
|
$rs->update_or_create($row,{key=>'rel_u_idx'});
|
|
} catch($e) {
|
|
push @fails, $linenum;
|
|
next;
|
|
}
|
|
}
|
|
push @entries, $row;
|
|
}
|
|
|
|
if ($purge) {
|
|
my ($start, $end);
|
|
$start = time;
|
|
$rs->delete;
|
|
$end = time;
|
|
$c->log->debug("Purging phonebook entries took " . ($end - $start) . "s");
|
|
$start = time;
|
|
$rs->populate(\@entries);
|
|
$end = time;
|
|
$c->log->debug("Populating phonebook entries took " . ($end - $start) . "s");
|
|
}
|
|
});
|
|
|
|
my $text = $c->loc('Phonebook entries successfully uploaded');
|
|
if (@fails) {
|
|
$text .= $c->loc(", but skipped the following line numbers: ") . (join ", ", @fails);
|
|
}
|
|
|
|
return ( \@entries, \@fails, \$text );
|
|
}
|
|
|
|
sub download_csv {
|
|
my ($c, $rs, $owner) = @_;
|
|
|
|
my @cols = qw/name number/;
|
|
|
|
if ($owner eq 'reseller' && $c->user->roles eq 'admin') {
|
|
push @cols, 'reseller_id';
|
|
} elsif ($owner eq 'subscriber') {
|
|
push @cols, 'shared';
|
|
}
|
|
|
|
my ($start, $end);
|
|
$start = time;
|
|
foreach my $row ($rs->all) {
|
|
my %entry = $row->get_inflated_columns;
|
|
delete $entry{id};
|
|
$c->res->write_fh->write_encoded(join (",", @entry{@cols}) );
|
|
$c->res->write_fh->write("\n");
|
|
}
|
|
$c->res->write_fh->close;
|
|
$end = time;
|
|
$c->log->debug("Creating phonebook entries CSV for download took " . ($end - $start) . "s");
|
|
return 1;
|
|
}
|
|
|
|
1;
|
|
|
|
=head1 NAME
|
|
|
|
NGCP::Panel::Utils::Phonebook
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
A helper to manipulate the phonebook data
|
|
|
|
=head1 METHODS
|
|
|
|
=head2 get_reseller_phonebook
|
|
|
|
=head2 get_contract_phonebook
|
|
|
|
=head2 get_subscriber_phonebook
|
|
|
|
=head1 AUTHOR
|
|
|
|
Sipwise Development Team
|
|
|
|
=head1 LICENSE
|
|
|
|
This library is free software. You can redistribute it and/or modify
|
|
it under the same terms as Perl itself.
|
|
|
|
=cut
|
|
# vim: set tabstop=4 expandtab:
|