MT#18395 lazy load dashboard values

csc dashboard recent calls cover last 24h

Change-Id: I19704643203795949c3e46b44fd136a59f83ac2b
changes/18/5018/6
Rene Krenn 10 years ago
parent 9c9f33340a
commit 4438756017

@ -73,7 +73,7 @@ __PACKAGE__->config(
'View::JSON' => {
#Set the stash keys to be exposed to a JSON response
#(sEcho iTotalRecords iTotalDisplayRecords aaData) for datatables
expose_stash => [ qw(sEcho iTotalRecords iTotalDisplayRecords aaData dt_custom_footer) ],
expose_stash => [ qw(sEcho iTotalRecords iTotalDisplayRecords aaData dt_custom_footer widget_data) ],
},
'View::TT' => {
INCLUDE_PATH => [

@ -29,8 +29,8 @@ sub index :Path :Args(0) {
my $widget_templates = [];
foreach($plugin_finder->instantiate_plugins($c, 'dashboard_widgets')) {
$_->handle($c);
push @{ $widget_templates }, $_->template;
$_->{instance}->handle($c); #prepare stash for values rendered by tt
push @{ $widget_templates }, $_->{instance}->template;
}
$c->stash(widgets => $widget_templates);
@ -38,6 +38,28 @@ sub index :Path :Args(0) {
delete $c->session->{redirect_targets};
}
sub ajax :Path('ajax') :Args(1) {
my ($self, $c, $exec) = @_;
my $combined_plugin = NGCP::Panel::Widget->new;
foreach($combined_plugin->instantiate_plugins($c, 'dashboard_widgets')) {
#$_->{instance}->handle($c);
$combined_plugin->load_plugin($_->{name});
}
my $value = undef;
eval {
$value = $combined_plugin->$exec($c);
};
if ($@) {
$c->log->debug("error processing widget ajax request '$exec': " . $@);
}
$c->stash(widget_data => $value);
$c->detach( $c->view("JSON") );
}
=head1 AUTHOR
Andreas Granig,,,

@ -64,10 +64,10 @@ sub auto :Private {
}
unless($c->user_exists) {
if(index($c->controller->catalyst_component_name, 'NGCP::Panel::Controller::API') == 0) {
$c->log->debug("++++++ Root::auto unauthenticated API request");
my $ssl_dn = $c->request->env->{SSL_CLIENT_M_DN} // "";
my $ssl_dn = $c->request->env->{SSL_CLIENT_M_DN} // "";
my $ssl_sn = hex ($c->request->env->{SSL_CLIENT_M_SERIAL} // 0);
if($ssl_sn) {
$c->log->debug("++++++ Root::auto API request with client auth sn '$ssl_sn'");
@ -81,7 +81,7 @@ sub auto :Private {
return;
}
my $res = $c->authenticate({
my $res = $c->authenticate({
ssl_client_m_serial => $ssl_sn,
is_active => 1, # TODO: abused as password until NoPassword handler is available
}, 'api_admin_cert');
@ -152,7 +152,7 @@ sub auto :Private {
$c->response->status(403);
return;
}
# store uri for redirect after login
my $target = undef;
if($c->request->method eq 'GET') {
@ -186,8 +186,8 @@ sub auto :Private {
my $plugin_finder = NGCP::Panel::Widget->new;
my $topmenu_templates = [];
foreach($plugin_finder->instantiate_plugins($c, 'topmenu_widgets')) {
$_->handle($c);
push @{ $topmenu_templates }, $_->template;
$_->{instance}->handle($c);
push @{ $topmenu_templates }, $_->{instance}->template;
}
$c->stash(topmenu => $topmenu_templates);
@ -263,7 +263,7 @@ sub _prune_row {
sub error_page :Private {
my ($self,$c) = @_;
$c->log->error( 'Failed to find path ' . $c->request->path );
if($c->request->path =~ /^api\/.+/) {
$c->response->content_type('application/json');
$c->response->body(JSON::to_json({
@ -278,7 +278,7 @@ sub error_page :Private {
sub denied_page :Private {
my ($self,$c) = @_;
$c->log->error('Access denied to path ' . $c->request->path );
if($c->request->path =~ /^api\/.+/) {
$c->response->content_type('application/json');
@ -321,7 +321,7 @@ sub api_apply_fake_time :Private {
}
NGCP::Panel::Utils::DateTime::set_fake_time();
$c->stash->{is_fake_time} = 0;
#$c->log->debug('resetting faked system time: ' . NGCP::Panel::Utils::DateTime::to_string(NGCP::Panel::Utils::DateTime::current_local));
#$c->log->debug('resetting faked system time: ' . NGCP::Panel::Utils::DateTime::to_string(NGCP::Panel::Utils::DateTime::current_local));
}
}

@ -22,10 +22,10 @@ sub instantiate_plugins {
my $inst = NGCP::Panel::Widget->new;
$inst->load_plugin($_);
if($inst->filter($c, $type_filter)) {
push @instances, $inst;
push @instances, { instance => $inst, name => $_ };
}
}
return sort {$a->priority > $b->priority} @instances;
return sort {$a->{instance}->priority > $b->{instance}->priority} @instances;
}
no Moose;

@ -22,14 +22,49 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
# add queries used in tt here ...
return;
};
sub filter {
my ($self, $c, $type) = @_;
return $self if(
$type eq $self->type &&
$c->user->roles eq 'admin' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
}
sub _get_interval {
my $self = shift;
my $stime = NGCP::Panel::Utils::DateTime::current_local->truncate(to => 'month');
my $etime = $stime->clone->add(months => 1);
return ($stime,$etime);
}
sub _prepare_profiles_count {
my ($self, $c) = @_;
#how to catchup contract balances of all contracts here?
$c->stash(
profiles => $c->model('DB')->resultset('billing_profiles')->search_rs({
status => { '!=' => 'terminated'},
}),
);
}
sub _prepare_peering_sum {
my ($self, $c) = @_;
# how to catchup contract balances of all contracts here?
# well, we don't care for a stats view ...
my ($stime,$etime) = $self->_get_interval();
$c->stash(
peering_sum => $c->model('DB')->resultset('contract_balances')->search_rs({
'start' => { '>=' => $stime },
'end' => { '<' => $etime},
@ -40,7 +75,18 @@ around handle => sub {
alias => 'myinner',
join => 'product',
})->as_query,
})->get_column('cash_balance_interval')->sum,
})->get_column('cash_balance_interval'),
);
}
sub _prepare_reseller_sum {
my ($self, $c) = @_;
# how to catchup contract balances of all contracts here?
# well, we don't care for a stats view ...
my ($stime,$etime) = $self->_get_interval();
$c->stash(
reseller_sum => $c->model('DB')->resultset('contract_balances')->search_rs({
'start' => { '>=' => $stime },
'end' => { '<' => $etime},
@ -51,7 +97,18 @@ around handle => sub {
alias => 'myinner',
join => 'product',
})->as_query,
})->get_column('cash_balance_interval')->sum,
})->get_column('cash_balance_interval'),
);
}
sub _prepare_customer_sum {
my ($self, $c) = @_;
# how to catchup contract balances of all contracts here?
# well, we don't care for a stats view ...
my ($stime,$etime) = $self->_get_interval();
$c->stash(
customer_sum => $c->model('DB')->resultset('contract_balances')->search_rs({
'start' => { '>=' => $stime },
'end' => { '<' => $etime},
@ -62,20 +119,36 @@ around handle => sub {
alias => 'myinner',
join => 'product',
})->as_query,
})->get_column('cash_balance_interval')->sum,
})->get_column('cash_balance_interval'),
);
return;
};
}
sub filter {
my ($self, $c, $type) = @_;
sub profiles_count {
my ($self, $c) = @_;
return $self if(
$type eq $self->type &&
$c->user->roles eq 'admin' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
$self->_prepare_profiles_count($c);
return $c->stash->{profiles}->count;
}
sub peering_sum {
my ($self, $c) = @_;
$self->_prepare_peering_sum($c);
return $c->stash->{peering_sum}->sum;
}
sub reseller_sum {
my ($self, $c) = @_;
$self->_prepare_reseller_sum($c);
return $c->stash->{reseller_sum}->sum;
}
sub customer_sum {
my ($self, $c) = @_;
$self->_prepare_customer_sum($c);
return $c->stash->{customer_sum}->sum;
}
1;

@ -22,15 +22,7 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
my $peer_groups = $c->model('DB')->resultset('voip_peer_groups')->search_rs({});
my $peer_hosts = $peer_groups->search_related_rs('voip_peer_hosts');
my $peer_rules = $peer_groups->search_related_rs('voip_peer_rules');
$c->stash(
groups => $peer_groups,
hosts => $peer_hosts,
rules => $peer_rules,
);
# add queries used in tt here ...
return;
};
@ -46,5 +38,47 @@ sub filter {
return;
}
sub _prepare_peer_groups_count {
my ($self, $c) = @_;
my $peer_groups = $c->model('DB')->resultset('voip_peer_groups')->search_rs({});
$c->stash(
groups => $peer_groups,
);
}
sub _prepare_hosts_count {
my ($self, $c) = @_;
$self->_prepare_peer_groups_count($c);
$c->stash(
hosts => $c->stash->{groups}->search_related_rs('voip_peer_hosts'),
);
}
sub _prepare_rules_count {
my ($self, $c) = @_;
$self->_prepare_peer_groups_count($c);
$c->stash(
rules => $c->stash->{groups}->search_related_rs('voip_peer_rules'),
);
}
sub groups_count {
my ($self, $c) = @_;
$self->_prepare_peer_groups_count($c);
return $c->stash->{groups}->count;
}
sub hosts_count {
my ($self, $c) = @_;
$self->_prepare_hosts_count($c);
return $c->stash->{hosts}->count;
}
sub rules_count {
my ($self, $c) = @_;
$self->_prepare_rules_count($c);
return $c->stash->{rules}->count;
}
1;
# vim: set tabstop=4 expandtab:

@ -22,33 +22,81 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
# add queries used in tt here ...
return;
};
sub filter {
my ($self, $c, $type) = @_;
return $self if(
$type eq $self->type &&
$c->user->roles eq 'admin' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
}
sub _prepare_resellers_count {
my ($self, $c) = @_;
$c->stash(
resellers => $c->model('DB')->resultset('resellers')->search_rs({
status => { '!=' => 'terminated' },
}),
);
}
sub _prepare_domains_count {
my ($self, $c) = @_;
$c->stash(
domains => $c->model('DB')->resultset('domain_resellers')->search_rs({}),
);
}
sub _prepare_customers_count {
my ($self, $c) = @_;
$c->stash(
customers => $c->model('DB')->resultset('contracts')->search_rs({
status => { '!=' => 'terminated' },
'product.class' => { 'not in' => [ 'reseller', 'sippeering', 'pstnpeering' ] },
}, {
join => { 'billing_mappings' => 'product' },
}),
);
}
sub _prepare_subscribers_count {
my ($self, $c) = @_;
$c->stash(
subscribers => $c->model('DB')->resultset('voip_subscribers')->search_rs({
status => { '!=' => 'terminated' },
}),
);
return;
};
}
sub filter {
my ($self, $c, $type) = @_;
sub resellers_count {
my ($self, $c) = @_;
$self->_prepare_resellers_count($c);
return $c->stash->{resellers}->count;
}
return $self if(
$type eq $self->type &&
$c->user->roles eq 'admin' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
sub domains_count {
my ($self, $c) = @_;
$self->_prepare_domains_count($c);
return $c->stash->{domains}->count;
}
sub customers_count {
my ($self, $c) = @_;
$self->_prepare_customers_count($c);
return $c->stash->{customers}->count;
}
sub subscribers_count {
my ($self, $c) = @_;
$self->_prepare_subscribers_count($c);
return $c->stash->{subscribers}->count;
}
1;

@ -22,22 +22,81 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
# add queries used in tt here ...
return;
};
sub _get_interval {
my $self = shift;
my $stime = NGCP::Panel::Utils::DateTime::current_local->truncate(to => 'month');
my $etime = $stime->clone->add(months => 1);
return ($stime,$etime);
}
sub filter {
my ($self, $c, $type) = @_;
return $self if(
$type eq $self->type &&
$c->user->roles eq 'reseller' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
}
sub _get_reseller {
my ($self, $c) = @_;
return $c->model('DB')->resultset('resellers')->find($c->user->reseller_id);
}
sub _prepare_reseller_profiles_count {
my ($self, $c) = @_;
my $reseller = $self->_get_reseller($c);
$c->stash(
profiles => $reseller->billing_profiles,
);
}
my $reseller = $c->model('DB')->resultset('resellers')->find($c->user->reseller_id);
my $reseller_balance = $reseller->contract->contract_balances->search({
'start' => { '>=' => $stime },
'end' => { '<' => $etime},
})->first;
sub _prepare_reseller_balance {
my ($self, $c) = @_;
my $reseller = $self->_get_reseller($c);
my ($stime,$etime) = $self->_get_interval();
$c->stash(
balance => $reseller->contract->contract_balances->search({
'start' => { '>=' => $stime },
'end' => { '<' => $etime},
}),
);
}
sub profiles_count {
my ($self, $c) = @_;
$self->_prepare_reseller_profiles_count($c);
return $c->stash->{profiles}->count;
}
sub reseller_sum {
my ($self, $c) = @_;
$self->_prepare_reseller_balance($c);
my $reseller_balance = $c->stash->{balance}->first;
my $reseller_sum = 0;
if($reseller_balance) {
$reseller_sum = $reseller_balance->cash_balance_interval;
}
return $reseller_sum;
}
sub _prepare_customer_sum {
my ($self, $c) = @_;
my ($stime,$etime) = $self->_get_interval();
# how to catchup contract balances of all contracts here?
# well, we don't care for a stats view ...
$c->stash(
profiles => $reseller->billing_profiles,
reseller_sum => $reseller_sum,
customer_sum => $c->model('DB')->resultset('contract_balances')->search_rs({
'start' => { '>=' => $stime },
'end' => { '<' => $etime},
@ -46,20 +105,14 @@ around handle => sub {
join => {
'contract' => 'contact',
},
})->get_column('cash_balance_interval')->sum,
})->get_column('cash_balance_interval'),
);
return;
};
sub filter {
my ($self, $c, $type) = @_;
}
return $self if(
$type eq $self->type &&
$c->user->roles eq 'reseller' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
sub customer_sum {
my ($self, $c) = @_;
$self->_prepare_customer_sum($c);
return $c->stash->{customer_sum}->sum;
}
1;

@ -22,7 +22,24 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
my $reseller = $c->model('DB')->resultset('resellers')->find($c->user->reseller_id);
# add queries used in tt here ...
return;
};
sub filter {
my ($self, $c, $type) = @_;
return $self if(
$type eq $self->type &&
$c->user->roles eq 'reseller' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
}
sub _prepare_customers_count {
my ($self, $c) = @_;
$c->stash(
customers => $c->model('DB')->resultset('contracts')->search({
@ -32,28 +49,48 @@ around handle => sub {
},{
join => [ 'contact', { 'billing_mappings' => 'product' } ],
}),
);
}
sub _prepare_subscribers_count {
my ($self, $c) = @_;
$c->stash(
subscribers => $c->model('DB')->resultset('voip_subscribers')->search({
'contact.reseller_id' => $c->user->reseller_id,
'me.status' => { '!=' => 'terminated' },
},{
join => { 'contract' => 'contact'},
}),
);
}
sub _prepare_contacts_count {
my ($self, $c) = @_;
$c->stash(
contacts => $c->model('DB')->resultset('contacts')->search({
reseller_id => $c->user->reseller_id,
}),
);
return;
};
}
sub filter {
my ($self, $c, $type) = @_;
sub customers_count {
my ($self, $c) = @_;
$self->_prepare_customers_count($c);
return $c->stash->{customers}->count;
}
return $self if(
$type eq $self->type &&
$c->user->roles eq 'reseller' &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
sub subscribers_count {
my ($self, $c) = @_;
$self->_prepare_subscribers_count($c);
return $c->stash->{subscribers}->count;
}
sub contacts_count {
my ($self, $c) = @_;
$self->_prepare_contacts_count($c);
return $c->stash->{contacts}->count;
}
1;

@ -22,13 +22,8 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
my $reseller = $c->model('DB')->resultset('resellers')->find($c->user->reseller_id);
# add queries used in tt here ...
$c->stash(
domains => $reseller->domain_resellers,
rwr_sets => $reseller->voip_rewrite_rule_sets,
sound_sets => $reseller->voip_sound_sets,
);
return;
};
@ -43,5 +38,52 @@ sub filter {
return;
}
sub _get_reseller {
my ($self, $c) = @_;
return $c->model('DB')->resultset('resellers')->find($c->user->reseller_id);
}
sub _prepare_domains_count {
my ($self, $c) = @_;
my $reseller = $self->_get_reseller($c);
$c->stash(
domains => $reseller->domain_resellers,
);
}
sub _prepare_rwr_sets_count {
my ($self, $c) = @_;
my $reseller = $self->_get_reseller($c);
$c->stash(
rwr_sets => $reseller->voip_rewrite_rule_sets,
);
}
sub _prepare_sound_sets_count {
my ($self, $c) = @_;
my $reseller = $self->_get_reseller($c);
$c->stash(
sound_sets => $reseller->voip_sound_sets,
);
}
sub domains_count {
my ($self, $c) = @_;
$self->_prepare_domains_count($c);
return $c->stash->{domains}->count;
}
sub rwr_sets_count {
my ($self, $c) = @_;
$self->_prepare_rwr_sets_count($c);
return $c->stash->{rwr_sets}->count;
}
sub sound_sets_count {
my ($self, $c) = @_;
$self->_prepare_sound_sets_count($c);
return $c->stash->{sound_sets}->count;
}
1;
# vim: set tabstop=4 expandtab:

@ -22,44 +22,76 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
unless ($c->stash->{subscriber}) {
$c->stash(
subscriber => $c->model('DB')->resultset('voip_subscribers')->find({
uuid => $c->user->uuid,
}),
);
}
return;
};
sub filter {
my ($self, $c, $type) = @_;
return $self if(
$type eq $self->type &&
($c->user->roles eq 'subscriber' || $c->user->roles eq 'subscriberadmin') &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
}
sub _get_cf_type_descriptions {
my ($self, $c) = @_;
return { cfu => $c->loc("Call Forward Unconditional"),
cfb => $c->loc("Call Forward Busy"),
cft => $c->loc("Call Forward Timeout"),
cfna => $c->loc("Call Forward Unavailable") };
}
sub cfs {
my ($self, $c) = @_;
my $prov_subscriber = $c->user;
my $cfs = {};
my $descriptions = $self->_get_cf_type_descriptions($c);
foreach my $type(qw/cfu cfna cft cfb/) {
foreach my $type (qw/cfu cfna cft cfb/) {
my $maps = $prov_subscriber->voip_cf_mappings
->search({ type => $type });
$cfs->{$type} = [];
foreach my $map($maps->all) {
my @mappings = ();
foreach my $map ($maps->all) {
my @dset = map { { $_->get_columns } } $map->destination_set->voip_cf_destinations->search({},
{ order_by => { -asc => 'priority' }})->all;
foreach my $d(@dset) {
$d->{as_string} = NGCP::Panel::Utils::Subscriber::destination_as_string($c, $d);
foreach my $d (@dset) {
my $as_string = NGCP::Panel::Utils::Subscriber::destination_as_string($c, $d);
delete @$d{keys %$d};
$d->{as_string} = $as_string;
}
my @tset = ();
if($map->time_set) {
if ($map->time_set) {
@tset = map { { $_->get_columns } } $map->time_set->voip_cf_periods->all;
foreach my $t(@tset) {
$t->{as_string} = NGCP::Panel::Utils::Subscriber::period_as_string($t);
foreach my $t (@tset) {
my $as_string = NGCP::Panel::Utils::Subscriber::period_as_string($t);
delete @$t{keys %$t};
$t->{as_string} = $as_string;
}
}
push @{ $cfs->{$type} }, { destinations => \@dset, periods => \@tset };
push @mappings, {
destinations => \@dset,
periods => \@tset,
};
}
$cfs->{$type} = {
mappings => \@mappings,
desc => $descriptions->{$type},
};
}
$c->stash(cf_destinations => $cfs);
return;
};
sub filter {
my ($self, $c, $type) = @_;
return $self if(
$type eq $self->type &&
($c->user->roles eq 'subscriber' || $c->user->roles eq 'subscriberadmin') &&
ref $c->controller eq 'NGCP::Panel::Controller::Dashboard'
);
return;
return $cfs;
}
1;

@ -1,6 +1,10 @@
package NGCP::Panel::Widget::Plugin::SubscriberCallsOverview;
use Moose::Role;
use NGCP::Panel::Utils::DateTime;
use DateTime::Format::Strptime;
use URI::Escape;
has 'template' => (
is => 'ro',
isa => 'Str',
@ -22,28 +26,14 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
my $out_rs = $c->model('DB')->resultset('cdr')->search({
source_user_id => $c->user->uuid,
});
my $in_rs = $c->model('DB')->resultset('cdr')->search({
destination_user_id => $c->user->uuid,
});
my $calls_rs = $out_rs->union_all($in_rs)->search(undef, {
order_by => { -desc => 'me.start_time' },
})->slice(0, 4);
unless ($c->stash->{subscriber}) {
$c->stash(
subscriber => $c->model('DB')->resultset('voip_subscribers')->find({
uuid => $c->user->uuid,
}),
);
}
my $sub = $c->user->voip_subscriber;
my $calls = [ map {
my $call = { $_->get_inflated_columns };
$call->{destination_user_in} = NGCP::Panel::Utils::Subscriber::apply_rewrite(
c => $c, subscriber => $sub, number => $call->{destination_user_in}, direction => 'caller_out'
);
$call->{source_cli} = NGCP::Panel::Utils::Subscriber::apply_rewrite(
c => $c, subscriber => $sub, number => $call->{source_cli}, direction => 'caller_out'
);
$call;
} $calls_rs->all ];
$c->stash(calls => $calls);
return;
};
@ -58,5 +48,61 @@ sub filter {
return;
}
sub _prepare_calls {
my ($self, $c) = @_;
my $out_rs = $c->model('DB')->resultset('cdr')->search({
source_user_id => $c->user->uuid,
});
my $in_rs = $c->model('DB')->resultset('cdr')->search({
destination_user_id => $c->user->uuid,
});
my $calls_rs = $out_rs->union_all($in_rs);
$c->stash(calls_rs => $calls_rs);
}
sub calls_count {
my ($self, $c) = @_;
$self->_prepare_calls($c);
my $stime = NGCP::Panel::Utils::DateTime::current_local->subtract(hours => 24);
$c->stash->{calls_rs}->search({
start_time => { '>=' => $stime->epoch }
})->count;
}
sub calls_slice {
my ($self, $c) = @_;
$self->_prepare_calls($c);
my $sub = $c->user->voip_subscriber;
my $datetime_fmt = DateTime::Format::Strptime->new(
pattern => '%F %T',
);
return [ map {
my $call = { $_->get_inflated_columns };
my %resource = ();
$resource{destination_user_in} = URI::Escape::uri_unescape(
NGCP::Panel::Utils::Subscriber::apply_rewrite(
c => $c, subscriber => $sub, number => $call->{destination_user_in}, direction => 'caller_out'
)
);
$resource{source_cli} = ($call->{clir} ? $c->loc('anonymous') : URI::Escape::uri_unescape(
NGCP::Panel::Utils::Subscriber::apply_rewrite(
c => $c, subscriber => $sub, number => $call->{source_cli}, direction => 'caller_out'
)
));
$resource{call_status} = $call->{call_status};
$resource{source_user_id} = $call->{source_user_id};
$resource{start_time} = $datetime_fmt->format_datetime($call->{start_time});
$resource{duration} = $call->{duration};
\%resource;
} $c->stash->{calls_rs}->search(undef, {
order_by => { -desc => 'me.start_time' },
})->slice(0, 4)->all ];
}
1;
# vim: set tabstop=4 expandtab:

@ -22,21 +22,14 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
my $rs = $c->model('DB')->resultset('location')->search({
username => $c->user->username,
});
if($c->config->{features}->{multidomain}) {
$rs = $rs->search({
domain => $c->user->domain->domain,
});
unless ($c->stash->{subscriber}) {
$c->stash(
subscriber => $c->model('DB')->resultset('voip_subscribers')->find({
uuid => $c->user->uuid,
}),
);
}
my $reg_count = $rs->count;
$rs = $rs->slice(0,4);
$c->stash(
regs => $rs,
reg_count => $reg_count,
);
return;
};
@ -51,5 +44,38 @@ sub filter {
return;
}
sub _prepare_registrations {
my ($self, $c) = @_;
my $rs = $c->model('DB')->resultset('location')->search({
username => $c->user->username,
});
if($c->config->{features}->{multidomain}) {
$rs = $rs->search({
domain => $c->user->domain->domain,
});
}
$c->stash(registrations => $rs);
}
sub registrations_slice {
my ($self, $c) = @_;
$self->_prepare_registrations($c);
return [ map {
my $registration = { $_->get_inflated_columns };
my %resource = ();
$resource{user_agent} = $registration->{user_agent};
\%resource;
} $c->stash->{registrations}->slice(0,4)->all ];
}
sub registrations_count {
my ($self, $c) = @_;
$self->_prepare_registrations($c);
return $c->stash->{registrations}->count;
}
1;
# vim: set tabstop=4 expandtab:

@ -1,6 +1,8 @@
package NGCP::Panel::Widget::Plugin::SubscriberVmOverview;
use Moose::Role;
use DateTime::Format::Strptime;
has 'template' => (
is => 'ro',
isa => 'Str',
@ -22,21 +24,14 @@ has 'priority' => (
around handle => sub {
my ($foo, $self, $c) = @_;
my $sub = $c->model('DB')->resultset('voip_subscribers')->find({
uuid => $c->user->uuid,
});
my $rs = $c->model('DB')->resultset('voicemail_spool')->search({
mailboxuser => $c->user->uuid,
msgnum => { '>=' => 0 },
dir => { -like => '%/INBOX' },
}, {
order_by => { -desc => 'me.origtime' },
})->slice(0, 4);
unless ($c->stash->{subscriber}) {
$c->stash(
subscriber => $c->model('DB')->resultset('voip_subscribers')->find({
uuid => $c->user->uuid,
}),
);
}
$c->stash(
subscriber => $sub,
vmails => $rs,
);
return;
};
@ -51,5 +46,50 @@ sub filter {
return;
}
sub _prepare_voicemails {
my ($self, $c) = @_;
# limited by asterisk.voicemail.maxmsg in config.yml
my $rs = $c->model('DB')->resultset('voicemail_spool')->search({
mailboxuser => $c->user->uuid,
msgnum => { '>=' => 0 },
dir => { -like => '%/INBOX' },
});
$c->stash(
voicemails => $rs,
);
}
sub voicemails_count {
my ($self, $c) = @_;
$self->_prepare_voicemails($c);
return $c->stash->{voicemails}->count;
}
sub voicemails_slice {
my ($self, $c) = @_;
$self->_prepare_voicemails($c);
my $sub = $c->model('DB')->resultset('voip_subscribers')->find({
uuid => $c->user->uuid,
});
my $datetime_fmt = DateTime::Format::Strptime->new(
pattern => '%F %T',
);
return [ map {
#my $voicemail= { $_->get_inflated_columns };
#avoid loading the blob here!
my %resource = ();
$resource{play_uri} = $c->uri_for_action('/subscriber/play_voicemail', [$sub->id, $_->id])->as_string;
$resource{callerid} = $_->callerid;
$resource{origtime} = $datetime_fmt->format_datetime($_->origtime);
$resource{duration} = $_->duration;
\%resource;
} $c->stash->{voicemails}->search(undef,{
order_by => { -desc => 'me.origtime' },
})->slice(0, 4)->all ];
}
1;
# vim: set tabstop=4 expandtab:

@ -4,7 +4,7 @@
Project: Dashboard
Version: 1.0
Last change: 10/10/2012
Last change: 10/10/2012
-------------------------------------------------------------------*/
@ -127,7 +127,7 @@ ul.list li {
background-image: none;
}
/*------------------------------------------------------------------
Bootstrap Styles
------------------------------------------------------------------- */
@ -818,7 +818,7 @@ body:not(:-moz-handler-blocked) .dropdown-submenu > a:after {
background-color: #999999;
}
/*------------------------------------------------------------------
jQuery UI Styles
------------------------------------------------------------------- */
@ -1040,7 +1040,7 @@ jQuery UI Styles
opacity: .5;
}
/*------------------------------------------------------------------
Signin
------------------------------------------------------------------- */
@ -1227,7 +1227,7 @@ span.login-checkbox > input[type='checkbox']:checked + label {
}
}
/*------------------------------------------------------------------
Validation Styles
------------------------------------------------------------------- */
@ -1283,7 +1283,7 @@ Validation Styles
left: 8px;
}
/*------------------------------------------------------------------
Shortcuts
------------------------------------------------------------------- */
@ -1341,7 +1341,7 @@ Shortcuts
}
}
/*------------------------------------------------------------------
Pricing Styles
------------------------------------------------------------------- */
@ -1464,6 +1464,15 @@ Pricing Styles
.pricing-plans.plans-4 .plan-container {
width: 25%;
}
.widget-loading {
color:transparent;
text-shadow: none;
background-image: url("/img/loader_transparent.gif");
background-repeat: no-repeat;
background-position: center center;
}
/*------------------------------------------------------------------
[2. Min Width: 767px / Max Width: 979px]
*/
@ -1480,7 +1489,7 @@ Pricing Styles
}
}
/*------------------------------------------------------------------
Faq Styles
------------------------------------------------------------------- */
@ -1550,7 +1559,7 @@ Faq Styles
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
/*------------------------------------------------------------------
Error
------------------------------------------------------------------- */
@ -1581,7 +1590,7 @@ Error
}
}
/*------------------------------------------------------------------
Item Layout
------------------------------------------------------------------- */
@ -1627,7 +1636,7 @@ Item Layout
}
}
/*------------------------------------------------------------------
Gallery
------------------------------------------------------------------- */
@ -1708,7 +1717,7 @@ Gallery
border-radius: 3px;
}
/*------------------------------------------------------------------
Layout: Layout
------------------------------------------------------------------- */
@ -1717,7 +1726,7 @@ body {
background: #FFF;
}
/*------------------------------------------------------------------
Layout: Wrapper
------------------------------------------------------------------- */
@ -1735,7 +1744,7 @@ body {
height: 50px;
}
/*------------------------------------------------------------------
Layout: Topbar
------------------------------------------------------------------- */
@ -1831,7 +1840,7 @@ Layout: Topbar
border-top-color: #c9e3bd;
}
/*------------------------------------------------------------------
Layout: Header
------------------------------------------------------------------- */
@ -1880,7 +1889,7 @@ Layout: Header
text-decoration: none;
}
/*------------------------------------------------------------------
Layout: Main Nav
------------------------------------------------------------------- */
@ -2013,7 +2022,7 @@ Layout: Main Nav
border-top-color: #54893b;
}
/*------------------------------------------------------------------
Layout: Masthead
------------------------------------------------------------------- */
@ -2070,7 +2079,7 @@ Layout: Masthead
vertical-align: top;
}
/*------------------------------------------------------------------
Layout: Content
------------------------------------------------------------------- */
@ -2096,7 +2105,7 @@ Layout: Content
background: none;
}
/*------------------------------------------------------------------
Layout: Footer
------------------------------------------------------------------- */
@ -2120,7 +2129,7 @@ Layout: Footer
text-align: right;
}
/*------------------------------------------------------------------
Layout: Responsive
------------------------------------------------------------------- */

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

@ -0,0 +1,301 @@
;(function(root) {
'use strict';
var $ = root.jQuery || root.Zepto || root.$;
if (typeof $ === 'undefined') throw 'jquery.ajaxq requires jQuery or jQuery-compatible library (e.g. Zepto.js)';
/**
* @type {Function}
*/
var slice = Array.prototype.slice;
/**
* @type {Function}
*/
var noop = function() {};
/**
* Copy of jQuery function
* @type {Function}
*/
var isNumeric = function(obj) {
return !$.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
}
/**
* @type {Function}
*/
var isObject = function(obj) {
return "[object Object]" === Object.prototype.toString.call(obj);
}
var Request = (function (argument) {
function Request(url, settings) {
this._aborted = false;
this._jqXHR = null;
this._calls = {};
this._args = [url, settings];
this._deferred = $.Deferred();
this._deferred.pipe = this._deferred.then;
this.readyState = 1;
}
var proto = Request.prototype;
$.extend(proto, {
// start jqXHR by calling $.ajax
run: function() {
var
deferred = this._deferred,
methodName, argsStack, i;
if (this._jqXHR !== null) {
return this._jqXHR;
}
// clreate new jqXHR object
var
url = this._args[0],
settings = this._args[1];
if (isObject(url)) {
settings = url;
} else {
settings = $.extend(true, settings || {}, {
url: url
});
}
this._jqXHR = $.ajax.call($, settings);
this._jqXHR.done(function() {
deferred.resolve.apply(deferred, arguments);
});
this._jqXHR.fail(function() {
deferred.reject.apply(deferred, arguments);
});
if (this._aborted) {
this._jqXHR.abort(this.statusText);
}
// apply buffered calls
for (methodName in this._calls) {
argsStack = this._calls[methodName];
for (var i in argsStack) {
this._jqXHR[methodName].apply(this._jqXHR, argsStack[i]);
}
}
return this._jqXHR;
},
// returns original jqXHR object if it exists
// or writes to callected method to _calls and returns itself
_call: function(methodName, args) {
if (this._jqXHR !== null) {
if (typeof this._jqXHR[methodName] === 'undefined') {
return this._jqXHR;
}
return this._jqXHR[methodName].apply(this._jqXHR, args);
}
this._calls[methodName] = this._calls[methodName] || [];
this._calls[methodName].push(args);
return this;
},
// returns original jqXHR object if it exists
// or writes to callected method to _calls and returns itself
abort: function(statusText) {
if (this._jqXHR !== null) {
var
self = this,
_copyProperties = ['readyState', 'status', 'statusText'],
_return = this._jqXHR.abort.apply(this._jqXHR, arguments) || this._jqXHR;
if (_return) {
$.each(_copyProperties, function(i, prop) {
self[prop] = _return[prop];
});
}
return _return;
}
this.statusText = statusText || 'abort';
this.status = 0;
this.readyState = 0;
this._aborted = true;
return this;
},
state: function() {
if (this._jqXHR !== null) {
return this._jqXHR.state.apply(this._jqXHR, arguments);
}
return 'pending';
}
});
// each method returns self object
var _chainMethods = ['setRequestHeader', 'overrideMimeType', 'statusCode',
'done', 'fail', 'progress', 'complete', 'success', 'error', 'always' ];
$.each(_chainMethods, function(i, methodName) {
proto[methodName] = function() {
return this._call(methodName, slice.call(arguments)) || this._jqXHR;
}
});
var _nullMethods = ['getResponseHeader', 'getAllResponseHeaders'];
$.each(_nullMethods, function(i, methodName) {
proto[methodName] = function() {
// apply original method if _jqXHR exists
if (this._jqXHR !== null) {
return this._jqXHR[methodName].apply(this, arguments);
}
// return null if origina method does not exists
return null;
};
});
var _promiseMethods = ['pipe', 'then', 'promise'];
$.each(_promiseMethods, function(i, methodName) {
proto[methodName] = function() {
return this._deferred[methodName].apply(this._deferred, arguments);
};
});
return Request;
})()
var Queue = (function() {
var _params = {}, _queueCounter = 0;
function _runNext(queue, request) {
var
removeIndex = _getStarted(queue).indexOf(request),
nextRequest = _getPending(queue).shift();
if (removeIndex !== -1) {
_getStarted(queue).splice(removeIndex, 1);
}
if (typeof nextRequest !== 'undefined') {
nextRequest
.always($.proxy(_runNext, null, queue, nextRequest))
.run();
}
}
function _ajax(queue, request) {
if (_getStarted(queue).length < _getBandwidth(queue)) {
_getStarted(queue).push(request);
request.always($.proxy(_runNext, null, queue, request));
request.run();
} else {
_getPending(queue).push(request)
}
}
function _getParams(queue) {
return _params[queue.id] || (_params[queue.id] = {});
}
function _getParam(queue, name) {
return _getParams(queue)[name];
}
function _getStarted(queue) {
return _getParams(queue).started || (_getParams(queue).started = []);
}
function _getPending(queue) {
return _getParams(queue).pending || (_getParams(queue).pending = []);
}
function _setBandwidth(queue, bandwidth) {
if ((bandwidth = parseInt(bandwidth || 1, 10)) < 1) throw "Bandwidth can\'t be less then 1";
_getParams(queue).bandwidth = bandwidth;
}
function _getBandwidth(queue, bandwidth) {
return _getParams(queue).bandwidth;
}
function Queue(bandwidth) {
if (typeof bandwidth !== 'undefined' && !isNumeric(bandwidth)) throw "number expected";
this.id = ++_queueCounter;
_setBandwidth(this, bandwidth);
};
$.extend(Queue.prototype, {
ajax: function(url, settings) {
var request = new Request(url, settings);
_ajax(this, request);
return request;
},
getJSON: function ( url, data, callback ) {
return this.get( url, data, callback, "json" );
},
getBandwidth: function() {
return _getBandwidth(this);
}
});
$.each(['get', 'post'], function(i, method) {
Queue.prototype[method] = function( url, data, callback, type ) {
// shift arguments if data argument was omitted
if ( $.isFunction( data ) ) {
type = type || callback;
callback = data;
data = undefined;
}
return this.ajax({
url: url,
type: method,
dataType: type,
data: data,
success: callback
});
}
});
return Queue;
})();
if (typeof $.ajaxq !== 'undefined') throw "Namespace $.ajaxq is Alread y busy.";
var _queue = new Queue();
$.ajaxq = function(url, settions) {
return _queue.ajax.apply(_queue, arguments);
};
$.each(['get', 'post', 'getJSON'], function(i, methodName) {
$.ajaxq[methodName] = function() {
return _queue[methodName].apply(_queue, arguments);
}
});
$.ajaxq.Queue = function(bandwidth) {
return new Queue(bandwidth);
};
$.ajaxq.Request = function(url, settings) {
return new Request(url, settings);
}
})(this);

@ -0,0 +1,134 @@
/*! sprintf.js | Copyright (c) 2007-2013 Alexandru Marasteanu <hello at alexei dot ro> | 3 clause BSD license */
(function(ctx) {
var sprintf = function() {
if (!sprintf.cache.hasOwnProperty(arguments[0])) {
sprintf.cache[arguments[0]] = sprintf.parse(arguments[0]);
}
return sprintf.format.call(null, sprintf.cache[arguments[0]], arguments);
};
sprintf.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
for (i = 0; i < tree_length; i++) {
node_type = get_type(parse_tree[i]);
if (node_type === 'string') {
output.push(parse_tree[i]);
}
else if (node_type === 'array') {
match = parse_tree[i]; // convenience purposes only
if (match[2]) { // keyword argument
arg = argv[cursor];
for (k = 0; k < match[2].length; k++) {
if (!arg.hasOwnProperty(match[2][k])) {
throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
}
arg = arg[match[2][k]];
}
}
else if (match[1]) { // positional argument (explicit)
arg = argv[match[1]];
}
else { // positional argument (implicit)
arg = argv[cursor++];
}
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
}
switch (match[8]) {
case 'b': arg = arg.toString(2); break;
case 'c': arg = String.fromCharCode(arg); break;
case 'd': arg = parseInt(arg, 10); break;
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
case 'o': arg = arg.toString(8); break;
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
case 'u': arg = arg >>> 0; break;
case 'x': arg = arg.toString(16); break;
case 'X': arg = arg.toString(16).toUpperCase(); break;
}
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
pad_length = match[6] - String(arg).length;
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
output.push(match[5] ? arg + pad : pad + arg);
}
}
return output.join('');
};
sprintf.cache = {};
sprintf.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
while (_fmt) {
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
parse_tree.push(match[0]);
}
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
parse_tree.push('%');
}
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
if (match[2]) {
arg_names |= 1;
var field_list = [], replacement_field = match[2], field_match = [];
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else {
throw('[sprintf] huh?');
}
}
}
else {
throw('[sprintf] huh?');
}
match[2] = field_list;
}
else {
arg_names |= 2;
}
if (arg_names === 3) {
throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
}
parse_tree.push(match);
}
else {
throw('[sprintf] huh?');
}
_fmt = _fmt.substring(match[0].length);
}
return parse_tree;
};
var vsprintf = function(fmt, argv, _argv) {
_argv = argv.slice(0);
_argv.splice(0, 0, fmt);
return sprintf.apply(null, _argv);
};
/**
* helpers
*/
function get_type(variable) {
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
}
function str_repeat(input, multiplier) {
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
return output.join('');
}
/**
* export to either browser or node.js
*/
ctx.sprintf = sprintf;
ctx.vsprintf = vsprintf;
})(typeof exports != "undefined" ? exports : window);

@ -1,5 +1,29 @@
[% site_config.title = c.loc('Dashboard') -%]
[% wcount = widgets.size() > 4 ? 4 : widgets.size() -%]
<script src="/js/jquery.ajaxq.js"></script>
<script src="/js/sprintf.js"></script>
<script>
var enqueLists = [];
$(document).ready(function () {
if (enqueLists.length > 0) {
var queue = $.ajaxq.Queue(2); //enqueLists.length);
var i = 0;
var widgetDone = 0;
while (widgetDone < enqueLists.length) {
var ajaxCall = enqueLists[i].shift();
if (ajaxCall) {
queue.getJSON('[% c.uri_for('/dashboard/ajax') %]/' + ajaxCall.res, ajaxCall.cb);
} else {
widgetDone++;
}
i = (i + 1) % enqueLists.length
}
console.log('widget data loading enqueued (' + enqueLists.length + ')');
}
});
</script>
[% site_config.title = c.loc('Dashboard') -%]
<div class="row">
<div class="span12">
<div class="pricing-plans plans-[% wcount %]">

@ -1,17 +1,43 @@
<script>
enqueLists.push([{
res: 'peering_sum',
cb: function(data) {
console.log(data);
$("#admin_billing_overview_lazy_items_list").append('<li><strong>' + sprintf('%.02f',data.widget_data / 100.0) + '</strong> [% c.loc('Peering Costs') %]</li>');
}
},{
res: 'reseller_sum',
cb: function(data) {
console.log(data);
$("#admin_billing_overview_lazy_items_list").append('<li><strong>' + sprintf('%.02f',data.widget_data / 100.0) + '</strong> [% c.loc('Reseller Revenue') %]</li>');
}
},{
res: 'customer_sum',
cb: function(data) {
console.log(data);
$("#admin_billing_overview_lazy_items_list").append('<li><strong>' + sprintf('%.02f',data.widget_data / 100.0) + '</strong> [% c.loc('Customer Revenue') %]</li>');
}
},{
res: 'profiles_count',
cb: function(data) {
console.log(data);
$("#admin_billing_overview_lazy_loading").remove();
$("#admin_billing_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Billing Profile') %]' : '[% c.loc('Billing Profiles') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="admin_billing_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Billing') %]</div>
<div class="plan-price">
<span>[% profiles.count %]</span>
<span class="term">[% profiles.count == 1 ? c.loc('Billing Profile') : c.loc('Billing Profiles') %]</span>
</div>
<div class="plan-price widget-loading" id="admin_billing_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features">
<ul>
<li><strong>[% FILTER format("%.02f") %][% (peering_sum || 0) / 100.0 %][% END %]</strong> [% c.loc('Peering Costs') %]</li>
<li><strong>[% FILTER format("%.02f") %][% (reseller_sum || 0) / 100.0 %][% END %]</strong> [% c.loc('Reseller Revenue') %]</li>
<li><strong>[% FILTER format("%.02f") %][% (customer_sum || 0) / 100.0 %][% END %]</strong> [% c.loc('Customer Revenue') %]</li>
<ul id="admin_billing_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,16 +1,37 @@
<script>
enqueLists.push([{
res: 'hosts_count',
cb: function(data) {
console.log(data);
$("#admin_peering_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Peering Server') %]' : '[% c.loc('Peering Servers') %]') + '</li>');
}
},{
res: 'rules_count',
cb: function(data) {
console.log(data);
$("#admin_peering_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Peering Rule') %]' : '[% c.loc('Peering Rules') %]') + '</li>');
}
},{
res: 'groups_count',
cb: function(data) {
console.log(data);
$("#admin_peering_overview_lazy_loading").remove();
$("#admin_peering_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Peering Group') %]' : '[% c.loc('Peering Groups') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="admin_peering_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Peerings') %]</div>
<div class="plan-price">
<span>[% groups.count %]</span>
<span class="term">[% groups.count == 1 ? c.loc('Peering Group') : c.loc('Peering Groups') %]</span>
</div>
<div class="plan-price widget-loading" id="admin_peering_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features">
<ul>
<li><strong>[% hosts.count %]</strong> [% hosts.count == 1 ? c.loc('Peering Server') : c.loc('Peering Servers') %]</li>
<li><strong>[% rules.count %]</strong> [% rules.count == 1 ? c.loc('Peering Rule') : c.loc('Peering Rules') %]</li>
<ul id="admin_peering_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,17 +1,43 @@
<script>
enqueLists.push([{
res: 'domains_count',
cb: function(data) {
console.log(data);
$("#admin_reseller_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Domain') %]' : '[% c.loc('Domains') %]') + '</li>');
}
},{
res: 'customers_count',
cb: function(data) {
console.log(data);
$("#admin_reseller_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Customer') %]' : '[% c.loc('Customers') %]') + '</li>');
}
},{
res: 'subscribers_count',
cb: function(data) {
console.log(data);
$("#admin_reseller_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Subscriber') %]' : '[% c.loc('Subscribers') %]') + '</li>');
}
},{
res: 'resellers_count',
cb: function(data) {
console.log(data);
$("#admin_reseller_overview_lazy_loading").remove();
$("#admin_reseller_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Reseller') %]' : '[% c.loc('Resellers') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="admin_reseller_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Resellers') %]</div>
<div class="plan-price">
<span>[% resellers.count %]</span>
<span class="term">[% resellers.count == 1 ? c.loc('Reseller') : c.loc('Resellers') %]</span>
</div>
<div class="plan-price widget-loading" id="admin_reseller_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features">
<ul>
<li><strong>[% domains.count %]</strong> [% domains.count == 1 ? c.loc('Domain') : c.loc('Domains') %]</li>
<li><strong>[% customers.count %]</strong> [% customers.count == 1 ? c.loc('Customer') : c.loc('Customers') %]</li>
<li><strong>[% subscribers.count %]</strong> [% subscribers.count == 1 ? c.loc('Subscriber') : c.loc('Subscribers') %]</li>
<ul id="admin_reseller_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,16 +1,37 @@
<script>
enqueLists.push([{
res: 'reseller_sum',
cb: function(data) {
console.log(data);
$("#reseller_billing_overview_lazy_items_list").append('<li><strong>' + sprintf('%.02f',data.widget_data / 100.0) + '</strong> [% c.loc('Reseller Cost') %]</li>');
}
},{
res: 'customer_sum',
cb: function(data) {
console.log(data);
$("#reseller_billing_overview_lazy_items_list").append('<li><strong>' + sprintf('%.02f',data.widget_data / 100.0) + '</strong> [% c.loc('Customer Revenue') %]</li>');
}
},{
res: 'profiles_count',
cb: function(data) {
console.log(data);
$("#reseller_billing_overview_lazy_loading").remove();
$("#reseller_billing_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Billing Profile') %]' : '[% c.loc('Billing Profiles') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-title">Billing</div>
<div class="plan-price">
<span>[% profiles.count %]</span>
<span class="term">[% profiles.count == 1 ? c.loc('Billing Profile') : c.loc('Billing Profiles') %]</span>
</div>
<div class="plan-header" id="reseller_billing_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Billing') %]</div>
<div class="plan-price widget-loading" id="reseller_billing_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features">
<ul>
<li><strong>[% FILTER format("%.02f") %][% (reseller_sum || 0) / 100.0 %][% END %]</strong> [% c.loc('Reseller Cost') %]</li>
<li><strong>[% FILTER format("%.02f") %][% (customer_sum || 0) / 100.0 %][% END %]</strong> [% c.loc('Customer Revenue') %]</li>
<ul id="reseller_billing_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,16 +1,37 @@
<script>
enqueLists.push([{
res: 'contacts_count',
cb: function(data) {
console.log(data);
$("#reseller_customer_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Contact') %]' : '[% c.loc('Contacts') %]') + '</li>');
}
},{
res: 'subscribers_count',
cb: function(data) {
console.log(data);
$("#reseller_customer_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Subscriber') %]' : '[% c.loc('Subscribers') %]') + '</li>');
}
},{
res: 'customers_count',
cb: function(data) {
console.log(data);
$("#reseller_customer_overview_lazy_loading").remove();
$("#reseller_customer_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Customer') %]' : '[% c.loc('Customers') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="reseller_customer_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Customers') %]</div>
<div class="plan-price">
<span>[% customers.count %]</span>
<span class="term">[% customers.count == 1 ? c.loc('Customer') : c.loc('Customers') %]</span>
</div>
<div class="plan-price widget-loading" id="reseller_customer_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features">
<ul>
<li><strong>[% contacts.count %]</strong> [% contacts.count == 1 ? c.loc('Contact') : c.loc('Contacts') %]</li>
<li><strong>[% subscribers.count %]</strong> [% subscribers.count == 1 ? c.loc('Subscriber') : c.loc('Subscribers') %]</li>
<ul id="reseller_customer_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,16 +1,37 @@
<script>
enqueLists.push([{
res: 'rwr_sets_count',
cb: function(data) {
console.log(data);
$("#reseller_domain_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Rewrite Rule Set') %]' : '[% c.loc('Rewrite Rule Sets') %]') + '</li>');
}
},{
res: 'sound_sets_count',
cb: function(data) {
console.log(data);
$("#reseller_domain_overview_lazy_items_list").append('<li><strong>' + data.widget_data + '</strong> ' + (data.widget_data == 1 ? '[% c.loc('Sound Set') %]' : '[% c.loc('Sound Sets') %]') + '</li>');
}
},{
res: 'domains_count',
cb: function(data) {
console.log(data);
$("#reseller_domain_overview_lazy_loading").remove();
$("#reseller_domain_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Domain') %]' : '[% c.loc('Domains') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="reseller_domain_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Domains') %]</div>
<div class="plan-price">
<span>[% domains.count %]</span>
<span class="term">[% domains.count == 1 ? c.loc('Domain') : c.loc('Domains') %]</span>
</div>
<div class="plan-price widget-loading" id="reseller_domain_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features">
<ul>
<li><strong>[% rwr_sets.count %]</strong> [% rwr_sets.count == 1 ? c.loc('Rewrite Rule Set') : c.loc('Rewrite Rule Sets') %]</li>
<li><strong>[% sound_sets.count %]</strong> [% sound_sets.count == 1 ? c.loc('Sound Set') : c.loc('Sound Set') %]</li>
<ul id="reseller_domain_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,48 +1,63 @@
[% USE Math -%]
<script>
enqueLists.push([{
res: 'calls_slice',
cb: function(data) {
console.log(data);
var calls = data.widget_data;
if (calls.length == 0) {
$("#subscriber_call_overview_lazy_items_list").append('<li>[% c.loc('No calls yet') %]</li>');
} else {
for (var i = 0; i < calls.length; i++) {
var call = calls[i];
$("#subscriber_call_overview_lazy_items_list").append('<li>' +
'<div class="row-fluid">' +
(call.source_user_id == '[% c.user.uuid %]' ?
'<div class="span1"><i ' +
(call.call_status == "ok" ?
'class="icon-circle-arrow-right" style="color:green"'
:
'class="icon-circle-arrow-up" style="color:red"'
) + '></i></div>' +
'<div class="span8">' + call.destination_user_in + '</div>'
:
'<div class="span1"><i ' +
(call.call_status == "ok" ?
'class="icon-circle-arrow-left" style="color:green"'
:
'class="icon-circle-arrow-down" style="color:red"'
) + '></i></div>' +
'<div class="span8">' + call.source_cli + '</div>'
) +
'</div>' +
'<div class="row-fluid">' +
'<div class="offset1 span8">' + call.start_time + '</div>' +
'<div class="span1">' + Math.floor(call.duration) + 's</div>' +
'</div>' +
'</li>');
}
}
}
},{
res: 'calls_count',
cb: function(data) {
console.log(data);
$("#subscriber_call_overview_lazy_loading").remove();
$("#subscriber_call_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Recent Call') %]' : '[% c.loc('Recent Calls') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="subscriber_call_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Call List') %]</div>
<div class="plan-price">
<span>[% calls.size %]</span>
<span class="term">[% calls.size != 1 ? c.loc('Recent Calls') : c.loc('Recent Call') %]</span>
</div>
<div class="plan-price widget-loading" id="subscriber_call_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features large">
<ul>
[% IF calls.size == 0 -%]
<li>[% c.loc('No calls yet') %]</li>
[% ELSE -%]
[% FOR call IN calls -%]
<li>
<div class="row-fluid">
[% IF call.source_user_id == c.user.uuid -%]
<div class="span1">
[% IF call.call_status == "ok" -%]
<i class="icon-circle-arrow-right" style="color:green"></i>
[% ELSE -%]
<i class="icon-circle-arrow-up" style="color:red"></i>
[% END -%]
</div>
<div class="span8">[% call.destination_user_in | uri_unescape %]</div>
[% ELSE -%]
<div class="span1">
[% IF call.call_status == "ok" -%]
<i class="icon-circle-arrow-left" style="color:green"></i>
[% ELSE -%]
<i class="icon-circle-arrow-down" style="color:red"></i>
[% END -%]
</div>
<div class="span8">[% call.clir ? "anonymous" : call.source_cli | uri_unescape %]</div>
[% END -%]
</div>
<div class="row-fluid">
<div class="offset1 span8">[% call.start_time -%]</div>
<div class="span1">[% Math.int(call.duration) -%]s</div>
</div>
</li>
[% END -%]
[% END -%]
<ul id="subscriber_call_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,33 +1,39 @@
[%
cfs = [ { type = "cfu", desc = c.loc("Call Forward Unconditional") },
{ type = "cfb", desc = c.loc("Call Forward Busy") },
{ type = "cft", desc = c.loc("Call Forward Timeout") },
{ type = "cfna", desc = c.loc("Call Forward Unavailable") } ];
cfcount = 0;
FOR cf IN cfs;
IF cf_destinations.${cf.type}.size;
cfcount = cfcount + 1;
END;
END;
-%]
<script>
enqueLists.push([{
res: 'cfs',
cb: function(data) {
console.log(data);
var cfs = data.widget_data;
var mcount = 0;
for (var type in cfs) {
var cf_mappings = cfs[type];
$("#subscriber_cf_overview_lazy_items_list").append('<li>' +
cf_mappings.desc + ' <strong style="color:' +
(cf_mappings.mappings.length > 0 ?
'green">[% c.loc('active') %]'
:
'grey">[% c.loc('inactive') %]'
) + '</strong>' +
'</li>');
mcount += cf_mappings.mappings.length;
}
$("#subscriber_cf_overview_lazy_loading").remove();
$("#subscriber_cf_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + mcount + '</span>' +
'<span class="term">' + (mcount == 1 ? '[% c.loc('Call Forward Configured') %]' : '[% c.loc('Call Forwards Configured') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="subscriber_cf_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Call Forwards') %]</div>
<div class="plan-price">
<span>[% cfcount %]</span>
<span class="term">[% cfcount == 1 ? c.loc('Call Forward Configured') : c.loc('Call Forwards Configured') %]</span>
</div>
<div class="plan-price widget-loading" id="subscriber_cf_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features large">
<ul>
[% FOR cf IN cfs -%]
<li>
[% cf.desc %] [% cf_destinations.${cf.type}.size ? '<strong style="color:green">' _ c.loc('active') : '<strong color="grey">' _ c.loc('inactive') %]</strong>
</li>
[% END -%]
<ul id="subscriber_cf_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,32 +1,47 @@
[% USE Math -%]
<script>
enqueLists.push([{
res: 'registrations_slice',
cb: function(data) {
console.log(data);
var registrations = data.widget_data;
if (registrations.length == 0) {
$("#subscriber_registration_overview_lazy_items_list").append('<li>[% c.loc('No devices registered') %]</li>');
} else {
for (var i = 0; i < registrations.length; i++) {
var registration = registrations[i];
$("#subscriber_registration_overview_lazy_items_list").append('<li>' +
'<div class="row-fluid"><div>' +
(registration.user_agent.length > 48 ?
registration.user_agent.length.substring(0,45) + '...'
:
registration.user_agent
) +
'</div></div>' +
'</li>');
}
}
}
},{
res: 'registrations_count',
cb: function(data) {
console.log(data);
$("#subscriber_registration_overview_lazy_loading").remove();
$("#subscriber_registration_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('Registered Device') %]' : '[% c.loc('Registered Devices') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="subscriber_registration_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Registered Devices') %]</div>
<div class="plan-price">
<span>[% reg_count %]</span>
<span class="term">[% reg_count == 1 ? c.loc('Registered Device') : c.loc('Registered Devices') %]</span>
</div>
<div class="plan-price widget-loading" id="subscriber_registration_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features large">
<ul>
[% IF reg_count == 0 -%]
<li>[% c.loc('No devices registered') %]</li>
[% ELSE -%]
[% FOR reg IN regs.all -%]
<li>
<div class="row-fluid">
<div>
[% IF reg.user_agent.length > 48 -%]
[% ua = reg.user_agent.substr(45, reg.user_agent.length - 45, '...'); ua %]
[% ELSE -%]
[% reg.user_agent %]
[% END -%]
</div>
</div>
</li>
[% END -%]
[% END -%]
<ul id="subscriber_registration_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

@ -1,34 +1,52 @@
<script>
enqueLists.push([{
res: 'voicemails_slice',
cb: function(data) {
console.log(data);
var voicemails = data.widget_data;
if (voicemails.length == 0) {
$("#subscriber_voicemail_overview_lazy_items_list").append('<li>[% c.loc('No new messages') %]</li>');
} else {
for (var i = 0; i < voicemails.length; i++) {
var voicemail = voicemails[i];
$("#subscriber_voicemail_overview_lazy_items_list").append('<li>' +
'<div class="row-fluid">' +
'<div class="span1">' +
'<a href="' + voicemail.play_uri + '" alt="listen">' +
'<i class="icon-volume-up" style="color:green"></i>' +
'</a>' +
'</div>' +
'<div class="span8">' + voicemail.callerid + '</div>' +
'</div>' +
'<div class="row-fluid">' +
'<div class="offset1 span8">' + voicemail.origtime + '</div>' +
'<div class="span1">' + voicemail.duration + 's</div>' +
'</div>' +
'</li>');
}
}
}
},{
res: 'voicemails_count',
cb: function(data) {
console.log(data);
$("#subscriber_voicemail_overview_lazy_loading").remove();
$("#subscriber_voicemail_overview_lazy_items_header").append(
'<div class="plan-price">' +
'<span>' + data.widget_data + '</span>' +
'<span class="term">' + (data.widget_data == 1 ? '[% c.loc('New Message') %]' : '[% c.loc('New Messages') %]') + '</span>' +
'</div>');
}
}]);
</script>
<div class="plan-container">
<div class="plan">
<div class="plan-header">
<div class="plan-header" id="subscriber_voicemail_overview_lazy_items_header">
<div class="plan-title">[% c.loc('Voicebox Messages') %]</div>
<div class="plan-price">
<span>[% vmails.count %]</span>
<span class="term">[% vmails.count == 1 ? c.loc('New Message') : c.loc('New Messages') %]</span>
</div>
<div class="plan-price widget-loading" id="subscriber_voicemail_overview_lazy_loading"><span>0</span><span class="term">processing ...</span></div>
</div>
<div class="plan-features large">
<ul>
[% IF vmails.count == 0 -%]
<li>[% c.loc('No new messages') %]</li>
[% ELSE -%]
[% FOR vmail IN vmails.all -%]
<li>
<div class="row-fluid">
<div class="span1">
<a href="[% c.uri_for_action('/subscriber/play_voicemail', [subscriber.id, vmail.id]) %]" alt="listen">
<i class="icon-volume-up" style="color:green"></i>
</a>
</div>
<div class="span8">[% vmail.callerid -%]</div>
</div>
<div class="row-fluid">
<div class="offset1 span8">[% vmail.origtime -%]</div>
<div class="span1">[% vmail.duration -%]s</div>
</div>
</li>
[% END -%]
[% END -%]
<ul id="subscriber_voicemail_overview_lazy_items_list">
</ul>
</div>
<div class="plan-actions">

Loading…
Cancel
Save