diff --git a/etc/admin.conf b/etc/admin.conf
index 88c395c..781286f 100644
--- a/etc/admin.conf
+++ b/etc/admin.conf
@@ -29,15 +29,15 @@
use_free_time
- #0
- #1
- #2
- #3
- #4
- #5
- #6
- #7
- #8
- #9
+ *0
+ *1
+ *2
+ *3
+ *4
+ *5
+ *6
+ *7
+ *8
+ *9
diff --git a/lib/admin/Controller/admin.pm b/lib/admin/Controller/admin.pm
index a2b280c..869db8c 100644
--- a/lib/admin/Controller/admin.pm
+++ b/lib/admin/Controller/admin.pm
@@ -86,6 +86,10 @@ sub do_edit_admin : Local {
unless $admin eq $c->session->{admin}{login};
$settings{show_passwords} = $c->request->params->{show_passwords} ? 1 : 0
unless $admin eq $c->session->{admin}{login};
+ $settings{call_data} = $c->request->params->{call_data} ? 1 : 0
+ unless $admin eq $c->session->{admin}{login};
+ $settings{lawful_intercept} = $c->request->params->{lawful_intercept} ? 1 : 0
+ unless $admin eq $c->session->{admin}{login};
unless(keys %messages) {
if(keys %settings) {
@@ -142,6 +146,8 @@ sub do_create_admin : Local {
$settings{is_active} = $c->request->params->{is_active} ? 1 : 0;
$settings{read_only} = $c->request->params->{read_only} ? 1 : 0;
$settings{show_passwords} = $c->request->params->{show_passwords} ? 1 : 0;
+ $settings{call_data} = $c->request->params->{call_data} ? 1 : 0;
+ $settings{lawful_intercept} = $c->request->params->{lawful_intercept} ? 1 : 0;
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'create_admin',
diff --git a/lib/admin/Controller/billing.pm b/lib/admin/Controller/billing.pm
index 66fb425..a957a1f 100644
--- a/lib/admin/Controller/billing.pm
+++ b/lib/admin/Controller/billing.pm
@@ -4,7 +4,7 @@ use strict;
use warnings;
use base 'Catalyst::Controller';
-my @WEEKDAYS = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
+my @WEEKDAYS = qw(Monday Tuesday Wednesday Thursday Friday Saturday Sunday);
=head1 NAME
diff --git a/lib/admin/Controller/subscriber.pm b/lib/admin/Controller/subscriber.pm
index f6d9a25..bb65e6e 100644
--- a/lib/admin/Controller/subscriber.pm
+++ b/lib/admin/Controller/subscriber.pm
@@ -698,7 +698,7 @@ sub update_preferences : Local {
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'check_E164_number', $fw_target, \$checkresult);
$messages{$fwtype} = 'Client.Voip.MalformedNumber'
unless $checkresult;
- } elsif($fw_target =~ /^[a-z0-9&=+\$,;?\/_.!~*'()-]+\@[a-z0-9.-]+$/i) {
+ } elsif($fw_target =~ /^[a-z0-9&=+\$,;?\/_.!~*'()-]+\@[a-z0-9.-]+(:\d{1,5})?$/i) {
$fw_target = 'sip:'. lc $fw_target;
} elsif($fw_target =~ /^[a-z0-9&=+\$,;?\/_.!~*'()-]+$/) {
$fw_target = 'sip:'. lc($fw_target) .'@'. $c->session->{subscriber}{domain};
@@ -792,6 +792,7 @@ sub update_preferences : Local {
### alternative routing ###
$$preferences{alternative_route} = $c->request->params->{alternative_route} ? 1 : undef;
+ $$preferences{is_external} = $c->request->params->{is_external} ? 1 : undef;
### save settings ###
@@ -923,6 +924,176 @@ sub update_voicebox : Local {
return;
}
+=head2 call_data
+
+Display subscriber call list.
+
+=cut
+
+sub call_data : Local {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = 'tt/subscriber_call_data.tt';
+
+ my $subscriber_id = $c->request->params->{subscriber_id};
+ return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_byid',
+ { subscriber_id => $subscriber_id },
+ \$c->session->{subscriber}
+ );
+ $c->stash->{subscriber} = $c->session->{subscriber};
+ $c->stash->{subscriber}{subscriber_id} = $subscriber_id;
+
+ my @localized_months = ( "foo" );
+
+ my $cts = $c->session->{subscriber}{create_timestamp};
+ if($cts =~ s/^(\d{4}-\d\d)-\d\d \d\d:\d\d:\d\d/$1/) {
+ my ($cyear, $cmonth) = split /-/, $cts;
+ my ($nyear, $nmonth) = (localtime)[5,4];
+ $nyear += 1900;
+ $nmonth++;
+
+ for(1 .. 12) {
+ push @localized_months,
+ $c->model('Provisioning')->localize($c->view($c->config->{view})->
+ config->{VARIABLES}{site_config}{language},
+ sprintf("Web.Months.%02d", $_));
+ }
+
+ my @selectmonths;
+
+ while($cyear < $nyear) {
+ my @yearmon;
+ for($cmonth .. 12) {
+ my $amon = sprintf("%02d", $_);
+ unshift @yearmon, { display => $localized_months[$amon] ." $cyear", link => $cyear.$amon };
+ }
+ unshift @selectmonths, { year => $cyear, months => \@yearmon };
+ $cmonth = 1;
+ $cyear++;
+ }
+
+ my @yearmon;
+ for($cmonth .. $nmonth) {
+ my $amon = sprintf("%02d", $_);
+ unshift @yearmon, { display => $localized_months[$amon] ." $cyear", link => $cyear.$amon };
+ }
+ unshift @selectmonths, { year => $cyear, months => \@yearmon };
+
+ $c->stash->{subscriber}{selectmonths} = \@selectmonths;
+ }
+
+ my $listfilter = $c->request->params->{list_filter};
+ if(defined $listfilter) {
+ if(length $listfilter) {
+ $listfilter =~ s/^\*//;
+ $listfilter =~ s/\*$//;
+ $c->session->{calllist}{filter} = $listfilter;
+ } else {
+ delete $c->session->{calllist}{filter};
+ undef $listfilter;
+ }
+ }
+
+ my @localtime = localtime;
+
+ my ($callmonth, $callyear);
+ my $monthselect = $c->request->params->{listmonth};
+ if(defined $monthselect and $monthselect =~ /^(\d{4})(\d{2})$/) {
+ $callyear = $1;
+ $callmonth = $2;
+ $listfilter = $c->session->{calllist}{filter};
+ delete $c->session->{calllist}{start};
+ delete $c->session->{calllist}{end};
+ } else {
+ $callyear = $localtime[5] + 1900;
+ $callmonth = $localtime[4] + 1;
+ delete $c->session->{calllist}{filter};
+ delete $c->session->{calllist}{start};
+ delete $c->session->{calllist}{end};
+ }
+
+ my $liststart = $c->request->params->{list_start};
+ if(defined $liststart) {
+ if(length $liststart) {
+ $c->stash->{subscriber}{list_start} = $liststart;
+ if($liststart =~ /^\d\d\.\d\d\.\d\d\d\d$/) {
+ $c->session->{calllist}{start} = $liststart;
+ } else {
+ $liststart = $c->session->{calllist}{start};
+ $c->session->{messages}{msgdate} = 'Client.Syntax.Date';
+ }
+ } else {
+ delete $c->session->{calllist}{start};
+ undef $liststart;
+ }
+ } else {
+ $c->stash->{subscriber}{list_start} = $c->session->{calllist}{start};
+ }
+
+ my $listend = $c->request->params->{list_end};
+ if(defined $listend) {
+ if(length $listend) {
+ $c->stash->{subscriber}{list_end} = $listend;
+ if($listend =~ /^\d\d\.\d\d\.\d\d\d\d$/) {
+ $c->session->{calls}{end} = $listend;
+ } else {
+ $listend = $c->session->{calllist}{end};
+ $c->session->{messages}{msgdate} = 'Client.Syntax.Date';
+ }
+ } else {
+ delete $c->session->{calllist}{end};
+ undef $listend;
+ }
+ } else {
+ $c->stash->{subscriber}{list_end} = $c->session->{calllist}{end};
+ }
+
+ my ($sdate, $edate);
+ if(!defined $liststart and !defined $listend) {
+ $sdate = { year => $callyear, month => $callmonth };
+ $edate = { year => $callyear, month => $callmonth };
+ $c->stash->{selected_month} = sprintf "%04d%02d", $callyear, $callmonth;
+ } else {
+ if(defined $liststart) {
+ my ($day, $month, $year) = split /\./, $liststart;
+ $sdate = { year => $year, month => $month, day => $day };
+ }
+ if (defined $listend) {
+ my ($day, $month, $year) = split /\./, $listend;
+ $edate = { year => $year, month => $month, day => $day };
+ }
+ }
+
+ my $calls;
+ return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_calls',
+ { username => $c->session->{subscriber}{username},
+ domain => $c->session->{subscriber}{domain},
+ filter => { start_date => $sdate,
+ end_date => $edate,
+ }
+ },
+ \$calls
+ );
+
+ return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_voip_account_by_id',
+ { id => $c->session->{subscriber}{account_id} },
+ \$c->session->{voip_account}
+ );
+ if(eval { defined $c->session->{voip_account}{billing_profile} }) {
+ return 1 unless $c->model('Provisioning')->call_prov($c, 'billing', 'get_billing_profile',
+ { handle => $c->session->{voip_account}{billing_profile} },
+ \$c->session->{voip_account}{billing_profile}
+ );
+ }
+
+ $c->stash->{call_list} = admin::Utils::prepare_call_list($c, $calls, $listfilter);
+ $c->stash->{subscriber}{list_filter} = $listfilter if defined $listfilter;
+
+ undef $c->stash->{call_list} unless eval { @{$c->stash->{call_list}} };
+
+ return;
+}
+
+
sub edit_list : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/subscriber_edit_list.tt';
diff --git a/lib/admin/Utils.pm b/lib/admin/Utils.pm
index fcb6ec5..133fd0f 100644
--- a/lib/admin/Utils.pm
+++ b/lib/admin/Utils.pm
@@ -2,6 +2,8 @@ package admin::Utils;
use strict;
use warnings;
+use Time::Local;
+
# Takes a search result total count, an offset and a limit and returns
# an array containing offset values for a pagination link list
# where each page should list $limit elements.
@@ -45,15 +47,15 @@ sub paginate {
#-# description gets default speed dial slot set from admin.conf
sub get_default_slot_list {
my ($c) = @_;
-
+
if (defined $c->config->{speed_dial_vsc_presets} and ref $c->config->{speed_dial_vsc_presets}->{vsc} eq 'ARRAY') {
return $c->config->{speed_dial_vsc_presets}->{vsc};
} else {
return [];
}
-
+
#my @slots = ();
-
+
#for (my $i = 0; $i < 10; $i++) {
# push @slots,'#' . $i;
#}
@@ -67,7 +69,7 @@ sub get_default_slot_list {
#-# description gets a short representation of a (contract) contact
sub short_contact {
my ($c,$contact) = @_;
-
+
if (defined $contact->{company} and length($contact->{company})) {
return $contact->{company};
} elsif (defined $contact->{lastname} and length($contact->{lastname})) {
@@ -91,7 +93,7 @@ sub short_contact {
#-# description defines contract contact form fields
sub get_contract_contact_form_fields {
my ($c,$contact) = @_;
-
+
return [ { field => 'firstname',
label => 'Firtst Name',
value => $contact->{firstname} },
@@ -101,7 +103,7 @@ sub get_contract_contact_form_fields {
{ field => 'company',
label => 'Company',
value => $contact->{company} }];
-
+
}
sub get_qualified_number_for_subscriber {
@@ -121,4 +123,119 @@ sub get_qualified_number_for_subscriber {
return $number;
}
+# takes a catalyst session with subscriber information and a call list
+# as returned by the prov. interface and returns a reference to an
+# array suited for TT display
+sub prepare_call_list {
+ my ($c, $call_list, $filter) = @_;
+ my $callentries = [];
+
+ my @time = localtime time;
+ my $tmtdy = timelocal(0,0,0,$time[3],$time[4],$time[5]);
+
+ if(defined $filter and length $filter) {
+ $filter =~ s/\*/.*/g;
+ } else {
+ undef $filter;
+ }
+
+ my $b = '';
+ my $ccdp = $c->config->{cc_dial_prefix};
+
+ foreach my $call (@$call_list) {
+ my %callentry;
+ $callentry{background} = $b ? '' : 'tr_alt';
+
+ my @date = localtime $$call{start_time};
+ $date[5] += 1900;
+ $date[4]++;
+ $callentry{date} = sprintf("%02d.%02d.%04d %02d:%02d:%02d", @date[3,4,5,2,1,0]);
+
+ if($$call{duration}) {
+ my $duration = $$call{duration};
+ while($duration > 59) {
+ my $left = sprintf("%02d", $duration % 60);
+ $callentry{duration} = ":$left". (defined $callentry{duration} ? $callentry{duration} : '');
+ $duration = int($duration / 60);
+ }
+ $callentry{duration} = defined $callentry{duration} ? sprintf("%02d", $duration) . $callentry{duration}
+ : sprintf("00:%02d", $duration);
+ } elsif($$call{call_status} eq 'ok') {
+ $callentry{duration} = '00:00';
+ }
+
+ if(defined $$call{call_fee}) {
+ # money is allways returned as cents
+ $callentry{call_fee} = sprintf $c->session->{voip_account}{billing_profile}{data}{currency} . " %.04f", $$call{call_fee}/100;
+ } else {
+ $callentry{call_fee} = '';
+ }
+
+ if(defined $$call{source_user}
+ and $$call{source_user} eq $c->session->{subscriber}{username}
+ and $$call{source_domain} eq $c->session->{subscriber}{domain})
+ {
+ if($$call{call_status} eq 'ok') {
+ $callentry{direction_icon} = 'anruf_aus_small.gif';
+ } else {
+ $callentry{direction_icon} = 'anruf_aus_err_small.gif';
+ }
+ if($$call{destination_user} =~ /^\+?\d+$/) {
+ my $partner = $$call{destination_user};
+ $partner =~ s/^$ccdp/+/;
+ $partner =~ s/^\+*/+/;
+ $callentry{partner} = $partner;
+ } else {
+ $callentry{partner} = $$call{destination_user} .'@'. $$call{destination_domain};
+ }
+ $callentry{partner_number} = $callentry{partner};
+
+ } elsif(defined $$call{destination_user}
+ and $$call{destination_user} eq $c->session->{subscriber}{username}
+ and $$call{destination_domain} eq $c->session->{subscriber}{domain})
+ {
+ if($$call{call_status} eq 'ok') {
+ $callentry{direction_icon} = 'anruf_ein_small.gif';
+ } else {
+ $callentry{direction_icon} = 'anruf_ein_err_small.gif';
+ }
+ if(!defined $$call{source_cli} or !length $$call{source_cli}
+ or $$call{source_cli} !~ /^\+?\d+$/)
+ {
+ if(!defined $$call{source_user} or !length $$call{source_user}) {
+ $callentry{partner} = 'anonym';
+ } elsif($$call{source_user} =~ /^\+?\d+$/) {
+ my $partner = $$call{source_user};
+ $partner =~ s/^$ccdp/+/;
+ $partner =~ s/^\+*/+/;
+ $callentry{partner} = $partner;
+ } else {
+ $callentry{partner} = $$call{source_user} .'@'. $$call{source_domain};
+ }
+ } else {
+ my $partner = $$call{source_cli};
+ $partner =~ s/^$ccdp/+/;
+ $partner =~ s/^\+*/+/;
+ $callentry{partner} = $partner;
+ }
+ $callentry{partner_number} = $callentry{partner};
+
+ } else {
+ $c->log->error("***Utils::prepare_call_list no match on user in call list");
+ next;
+ }
+
+ if(defined $filter) {
+ next unless $callentry{partner} =~ /$filter/i;
+ }
+
+ push @$callentries, \%callentry;
+
+ $b = !$b;
+ }
+
+ return $callentries;
+}
+
+
1;
diff --git a/root/css/admin.css b/root/css/admin.css
index 9e30d40..51a235a 100644
--- a/root/css/admin.css
+++ b/root/css/admin.css
@@ -43,7 +43,7 @@ h3 {
/* hmm, menu doesn't extend container height ... */
#container #content {
- min-height: 570px;
+ min-height: 580px;
}
/* contentmenu's width and contentplace' margin-left should match */
@@ -407,7 +407,7 @@ ul#topsubmenu {
/* colorful table rows */
#contentplace .tr_alt {
- background-color: #ccffcc;
+ background-color: #eeffee;
}
#contentplace .orders,
@@ -483,3 +483,70 @@ ul#topsubmenu {
#save_local_ac {
margin: 0 0 0 15px;
}
+
+/* CDR display styles */
+#contentplace .sortcalllist {
+ margin: 0px;
+ padding: 4px 0;
+}
+#contentplace .sortcalllist img {
+ vertical-align: middle;
+ padding: 0 0 2px 10px;
+}
+#contentplace .calllist {
+ width: 100%;
+ margin: 0 0 10px 0;
+ padding: 0px;
+ border: none;
+ border-collapse: collapse;
+ border-top: 1px solid #D0DCDF;
+}
+#contentplace .calllist td {
+ margin: 0px;
+ padding: 3px 5px;
+ vertical-align: top;
+}
+#contentplace .calllist .calllistheader td {
+ padding: 10px 0 3px 0;
+ font-weight: bold;
+}
+#contentplace .calllist .calltype {
+ width: 20px;
+}
+#contentplace .calllist .calltype img {
+ margin: 1px 0 0 0;
+}
+/* #contentplace .calllist .callnumber, */
+#contentplace .calllist .callduration {
+ text-align: right;
+}
+#contentplace .calllist .callfee,
+#contentplace .calllist .calldate {
+ text-align: right;
+ white-space: nowrap;
+}
+
+/* column layout using tables */
+#columns31 {
+ width: 100%;
+}
+#columns31 .column1 {
+ width: 75%;
+ vertical-align: top;
+}
+#columns31 .column2 {
+ width: 25%;
+ border-left: 1px solid #D0DCDF;
+ vertical-align: top;
+}
+
+/* CDR search styles */
+#contentplace h3#search_cdr {
+ margin-top: 1px;
+}
+#contentplace #callsearch select {
+ margin: 2px 0 10px 0;
+}
+#contentplace #callsearch input {
+ margin: 2px 0;
+}
diff --git a/root/static/images/anruf_alle_small.gif b/root/static/images/anruf_alle_small.gif
new file mode 100644
index 0000000..df6421b
Binary files /dev/null and b/root/static/images/anruf_alle_small.gif differ
diff --git a/root/static/images/anruf_aus_err_small.gif b/root/static/images/anruf_aus_err_small.gif
new file mode 100644
index 0000000..11e6d42
Binary files /dev/null and b/root/static/images/anruf_aus_err_small.gif differ
diff --git a/root/static/images/anruf_aus_small.gif b/root/static/images/anruf_aus_small.gif
new file mode 100644
index 0000000..3519b15
Binary files /dev/null and b/root/static/images/anruf_aus_small.gif differ
diff --git a/root/static/images/anruf_ein_err_small.gif b/root/static/images/anruf_ein_err_small.gif
new file mode 100644
index 0000000..e831235
Binary files /dev/null and b/root/static/images/anruf_ein_err_small.gif differ
diff --git a/root/static/images/anruf_ein_small.gif b/root/static/images/anruf_ein_small.gif
new file mode 100644
index 0000000..0a58f87
Binary files /dev/null and b/root/static/images/anruf_ein_small.gif differ
diff --git a/root/tt/admin.tt b/root/tt/admin.tt
index a7e0c69..a51715a 100644
--- a/root/tt/admin.tt
+++ b/root/tt/admin.tt
@@ -12,6 +12,10 @@
active |
r/o |
PW |
+ CDR |
+ [% IF Catalyst.config.lawful_intercept %]
+ LI |
+ [% END %]
|
|
@@ -46,8 +50,22 @@
+ title="if checked, the administrator may view subscriber passwords" />
+
+
+
+ |
+ [% IF Catalyst.config.lawful_intercept %]
+
+
|
+ [% END %]
@@ -76,8 +94,20 @@
+ title="if checked, the administrator may view subscriber passwords" />
+ |
+
+
+ |
+ [% IF Catalyst.config.lawful_intercept %]
+
+
|
+ [% END %]
[% UNLESS Catalyst.session.admin.read_only && admin.login != Catalyst.session.admin.login %]
edit
@@ -165,6 +195,24 @@
title="if checked, the new administrator may view subscriber passwords" />
|
+
+ | show CDR data: |
+
+
+ |
+
+ [% IF Catalyst.config.lawful_intercept %]
+
+ | lawful intercepts: |
+
+
+ |
+
+ [% END %]
diff --git a/root/tt/peering_rewrite.tt b/root/tt/peering_rewrite.tt
index 6249e17..596c910 100644
--- a/root/tt/peering_rewrite.tt
+++ b/root/tt/peering_rewrite.tt
@@ -97,7 +97,6 @@
[% rule.match_pattern %] |
[% rule.replace_pattern %] |
[% rule.priority %] |
- [% rule.priority %] |
[% rule.description %] |
[% UNLESS Catalyst.session.admin.read_only %]
diff --git a/root/tt/subscriber_call_data.tt b/root/tt/subscriber_call_data.tt
new file mode 100644
index 0000000..eadb547
--- /dev/null
+++ b/root/tt/subscriber_call_data.tt
@@ -0,0 +1,107 @@
+
+
+
+
+
+ Call Data
+
+
+
diff --git a/root/tt/subscriber_detail.tt b/root/tt/subscriber_detail.tt
index 20e545e..e9ff035 100644
--- a/root/tt/subscriber_detail.tt
+++ b/root/tt/subscriber_detail.tt
@@ -10,6 +10,10 @@
|
|
+ [% IF Catalyst.session.admin.call_data %]
+
+ |
+ [% END %]
@@ -22,6 +26,10 @@
|
|
+ [% IF Catalyst.session.admin.call_data %]
+
+ |
+ [% END %]
diff --git a/root/tt/subscriber_preferences.tt b/root/tt/subscriber_preferences.tt
index da645cf..1c4521f 100644
--- a/root/tt/subscriber_preferences.tt
+++ b/root/tt/subscriber_preferences.tt
@@ -9,6 +9,10 @@
|
|
+ [% IF Catalyst.session.admin.call_data %]
+
+ |
+ [% END %]
@@ -62,6 +66,7 @@
|| preference.key == "in_use"
|| preference.key == "ignore_allowed_ips"
|| preference.key == "alternative_route"
+ || preference.key == "is_external"
%]
|
| [% preference.key %]: |
| |