renamed www/admin to www_admin


			
			
				1.0@653
			
			
		
Daniel Tiefnig 17 years ago
commit 41e9261d94

@ -0,0 +1,3 @@
---
name: admin
view: Sipwise

@ -0,0 +1,81 @@
package admin;
use strict;
use warnings;
use Catalyst::Runtime '5.70';
use XML::Simple;
# Set flags and add plugins for the application
#
# -Debug: activates the debug mode for very useful log messages
# ConfigLoader: will load the configuration from a YAML file in the
# application's home directory
# Static::Simple: will serve static files from the application's root
# directory
use Catalyst::Log::Log4perl;
use Catalyst qw/-Debug ConfigLoader Static::Simple
Authentication Authentication::Store::Minimal Authentication::Credential::Password
Session Session::Store::FastMmap Session::State::Cookie
/;
our $VERSION = '0.01';
# Configure the application.
#
# Note that settings in admin.yml (or other external
# configuration file that you set up manually) take precedence
# over this when using ConfigLoader. Thus configuration
# details given here can function as a default configuration,
# with a external configuration file acting as an override for
# local deployment.
# load configuration from admin.conf XML
my $xs = new XML::Simple;
my $xc = $xs->XMLin( '/usr/local/etc/admin.conf', ForceArray => 0);
__PACKAGE__->config( authentication => {}, %$xc );
if(__PACKAGE__->config->{log4perlconf}) {
__PACKAGE__->log( Catalyst::Log::Log4perl->new(
__PACKAGE__->config->{log4perlconf}
));
}
# Start the application
__PACKAGE__->setup;
=head1 NAME
admin - Catalyst based application
=head1 DESCRIPTION
The core module of the administration framework.
=head1 BUGS AND LIMITATIONS
=over
=item none so far
=back
=head1 SEE ALSO
L<admin::Controller::Root>, L<Catalyst>
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The admin module is Copyright (c) 2007 Sipwise GmbH, Austria. All rights
reserved.
=cut
1;

@ -0,0 +1,121 @@
package admin::Controller::Root;
use strict;
use warnings;
use base 'Catalyst::Controller';
#
# Sets the actions in this controller to be registered with no prefix
# so they function identically to actions created in MyApp.pm
#
__PACKAGE__->config->{namespace} = '';
=head1 NAME
admin::Controller::Root - Root Controller for admin
=head1 DESCRIPTION
This provides basic functionality for the admin web interface.
=head1 METHODS
=head2 auto
Verify user is logged in.
=cut
# Note that 'auto' runs after 'begin' but before your actions and that
# 'auto' "chain" (all from application path to most specific class are run)
sub auto : Private {
my ($self, $c) = @_;
if ($c->controller =~ /^admin::Controller::Root\b/
or $c->controller =~ /^admin::Controller::login\b/)
{
$c->log->debug('***Root::auto front page or login access granted.');
return 1;
}
if (!$c->user_exists) {
$c->log->debug('***Root::auto User not found, forwarding to /');
$c->response->redirect($c->uri_for('/'));
return;
}
return 1;
}
=head2 default
Display default page.
=cut
sub default : Private {
my ( $self, $c ) = @_;
if ($c->user_exists) {
$c->stash->{template} = 'tt/default.tt';
} else {
$c->stash->{template} = 'tt/login.tt';
}
}
=head2 end
Attempt to render a view, if needed.
=cut
sub end : ActionClass('RenderView') {
my ( $self, $c ) = @_;
$c->stash->{current_view} = $c->config->{view};
unless($c->response->{status} =~ /^3/) { # only if not a redirect
if(exists $c->session->{prov_error}) {
$c->stash->{prov_error} =
$c->model('Provisioning')->localize($c->view($c->config->{view})->
config->{VARIABLES}{site_config}{language},
$c->session->{prov_error});
delete $c->session->{prov_error};
}
if(exists $c->session->{messages}) {
$c->stash->{messages} = $c->model('Provisioning')->localize($c->view($c->config->{view})->
config->{VARIABLES}{site_config}{language},
$c->session->{messages});
delete $c->session->{messages};
}
}
return 1;
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The Root controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,331 @@
package admin::Controller::account;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::account - Catalyst Controller
=head1 DESCRIPTION
This provides functionality for VoIP account administration.
=head1 METHODS
=head2 index
Display search form.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/account.tt';
return 1;
}
=head2 getbyid
Check entered account ID and redirect.
=cut
sub getbyid : Local {
my ( $self, $c ) = @_;
my $account_id = $c->request->params->{account_id};
if(defined $account_id and $account_id =~ /^\d+$/) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'get_voip_account_by_id',
{ id => $account_id },
\$c->session->{voip_account}
))
{
$c->response->redirect("/account/detail?account_id=$account_id");
return;
}
delete $c->session->{prov_error} if $c->session->{prov_error} eq 'Client.Voip.NoSuchAccount';
$c->session->{messages} = { accsearcherr => 'Client.Voip.NoSuchAccount' };
} else {
$c->session->{messages} = { accsearcherr => 'Client.Syntax.AccountID' };
}
$c->response->redirect("/account");
return;
}
=head2 detail
Show account details.
=cut
sub detail : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/account_detail.tt';
my $account_id = $c->request->params->{account_id};
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_voip_account_by_id',
{ id => $account_id },
\$c->session->{voip_account}
);
if($c->config->{billing_features}) {
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_voip_account_balance',
{ id => $account_id },
\$c->session->{voip_account}{balance}
);
$c->session->{voip_account}{balance}{cash_balance} =
sprintf "%.2f", $c->session->{voip_account}{balance}{cash_balance} / 100;
$c->session->{voip_account}{balance}{cash_balance_interval} =
sprintf "%.2f", $c->session->{voip_account}{balance}{cash_balance_interval} / 100;
if(ref $c->session->{restore_account_input} eq 'HASH') {
$c->stash->{account}{product} = $c->session->{restore_account_input}{product};
$c->stash->{account}{billing_profile} = $c->session->{restore_account_input}{billing_profile};
delete $c->session->{restore_account_input};
}
if(ref $c->session->{restore_balance_input} eq 'HASH') {
$c->stash->{balanceadd} = $c->session->{restore_balance_input};
delete $c->session->{restore_balance_input};
}
$c->stash->{edit_account} = $c->request->params->{edit_account};
$c->stash->{edit_balance} = $c->request->params->{edit_balance};
# we only use this to fill the drop-down lists
if($c->request->params->{edit_account}) {
my $products;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_products',
undef,
\$products
);
$c->stash->{products} = [ grep { $$_{class} eq 'voip' } @{$$products{result}} ];
my $billing_profiles;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_billing_profiles',
undef,
\$billing_profiles
);
$c->stash->{billing_profiles} = $$billing_profiles{result};
}
$c->stash->{billing_features} = 1;
}
delete $c->session->{voip_account}{subscribers}
if exists $c->session->{voip_account}{subscribers}
and !defined $c->session->{voip_account}{subscribers}
or ref $c->session->{voip_account}{subscribers} ne 'ARRAY'
or $#{$c->session->{voip_account}{subscribers}} == -1;
$c->stash->{account} = $c->session->{voip_account};
# $c->stash->{account}{is_locked} = 1 if $c->session->{voip_account}{status} eq 'locked';
return 1;
}
=head2 create_account
Creates a new VoIP account.
=cut
sub create_account : Local {
my ( $self, $c ) = @_;
my %messages;
my $acid;
if($c->model('Provisioning')->call_prov( $c, 'billing', 'create_voip_account',
{ product => $c->config->{def_product},
billing_group => $c->config->{def_billprof},
},
\$acid))
{
$messages{accmsg} = 'Web.Account.Created';
$c->session->{messages} = \%messages;
$c->response->redirect("/account/detail?account_id=$acid");
return;
}
$c->response->redirect("/account");
return;
}
=head2 update_account
Update details of a VoIP account.
=cut
sub update_account : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $account_id = $c->request->params->{account_id};
my $product = $c->request->params->{product};
$settings{product} = $product if defined $product;
my $billing_profile = $c->request->params->{billing_profile};
$settings{billing_profile} = $billing_profile if defined $billing_profile;
if(keys %settings) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'update_voip_account',
{ id => $account_id,
data => \%settings,
},
undef))
{
$messages{accmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/account/detail?account_id=$account_id");
return;
}
}
$c->session->{messages} = \%messages;
$c->session->{restore_account_input} = \%settings;
$c->response->redirect("/account/detail?account_id=$account_id&edit_account=1");
return;
}
=head2 lock
Locks and unlocks an account.
=cut
sub lock : Local {
my ( $self, $c ) = @_;
my $account_id = $c->request->params->{account_id};
my $lock = $c->request->params->{lock};
$c->model('Provisioning')->call_prov( $c, 'billing', 'lock_voip_account',
{ id => $account_id,
lock => $lock,
},
undef
);
$c->response->redirect("/account/detail?account_id=$account_id");
}
=head2 terminate
Terminates an account.
=cut
sub terminate : Local {
my ( $self, $c ) = @_;
my %messages;
my $account_id = $c->request->params->{account_id};
if($c->model('Provisioning')->call_prov( $c, 'billing', 'terminate_voip_account',
{ id => $account_id },
undef))
{
$messages{topmsg} = 'Server.Voip.SubscriberDeleted';
$c->session->{messages} = \%messages;
$c->response->redirect("/account");
return;
}
$c->session->{messages} = \%messages;
$c->response->redirect("/account/detail?account_id=$account_id");
return;
}
=head2 update_account
Update a VoIP account cash and free time balance.
=cut
sub update_balance : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $account_id = $c->request->params->{account_id};
my $add_cash = $c->request->params->{add_cash};
if(defined $add_cash and length $add_cash) {
$settings{cash} = $add_cash;
if($settings{cash} =~ /^[+-]?\d+(?:[.,]\d+)?$/) {
$settings{cash} =~ s/,/./;
$settings{cash} *= 100;
} else {
$messages{addcash} = 'Client.Syntax.CashValue';
}
}
my $add_time = $c->request->params->{add_time};
if(defined $add_time and length $add_time) {
$messages{addtime} = 'Client.Syntax.TimeValue'
unless $add_time =~ /^[+-]?\d+$/;
$settings{free_time} = $add_time;
}
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'update_voip_account_balance',
{ id => $account_id,
data => \%settings,
},
undef))
{
$messages{balmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/account/detail?account_id=$account_id");
return;
}
} else {
$messages{balerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_balance_input} = \%settings;
$c->session->{restore_balance_input}{cash} = $add_cash
if defined $add_cash and length $add_cash;
$c->response->redirect("/account/detail?account_id=$account_id&edit_balance=1");
return;
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Sipwise::Provisioning::Billing, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The account controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,200 @@
package admin::Controller::admin;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::admin - Catalyst Controller
=head1 DESCRIPTION
Catalyst Controller.
=head1 METHODS
=head2 index
Display admin list.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/admin.tt';
if($c->session->{admin}{is_master} or $c->session->{admin}{is_superuser}) {
my $admins;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_admins',
undef,
\$admins
);
$c->stash->{admins} = $$admins{result};
} else { # only own settings
my $admin;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_admin',
{ login => $c->session->{admin}{login} },
\$admin
);
$c->stash->{admins} = [ $admin ];
}
$c->stash->{edit_admin} = $c->request->params->{edit_admin};
if(ref $c->session->{restore_admedit_input} eq 'HASH') {
$c->stash->{erefill} = $c->session->{restore_admedit_input};
delete $c->session->{restore_admedit_input};
}
if(ref $c->session->{restore_admadd_input} eq 'HASH') {
$c->stash->{arefill} = $c->session->{restore_admadd_input};
delete $c->session->{restore_admadd_input};
}
return 1;
}
=head2 do_edit_admin
Change settings for an admin.
=cut
sub do_edit_admin : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $admin = $c->request->params->{admin};
$settings{password} = $c->request->params->{password};
if(defined $settings{password} and length $settings{password}) {
$messages{epass} = 'Client.Voip.PassLength'
unless length $settings{password} >= 6;
} else {
delete $settings{password};
}
$settings{is_master} = $c->request->params->{is_master} ? 1 : 0;
$settings{is_active} = $c->request->params->{is_active} ? 1 : 0;
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'update_admin',
{ login => $admin,
data => \%settings,
},
undef
))
{
$messages{eadmmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/admin");
return;
}
} else {
$messages{eadmerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_admedit_input} = \%settings;
$c->response->redirect("/admin?edit_admin=$admin");
return;
}
=head2 do_create_admin
Create a new admin.
=cut
sub do_create_admin : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $admin = $c->request->params->{admin};
$messages{alogin} = 'Client.Syntax.MalformedLogin'
unless $admin =~ /^\w+$/;
$settings{password} = $c->request->params->{password};
$messages{apass} = 'Client.Voip.PassLength'
unless length $settings{password} >= 6;
$settings{is_master} = $c->request->params->{is_master} ? 1 : 0;
$settings{is_active} = $c->request->params->{is_active} ? 1 : 0;
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'create_admin',
{ login => $admin,
data => \%settings,
},
undef
))
{
$messages{cadmmsg} = 'Web.Admin.Created';
$c->session->{messages} = \%messages;
$c->response->redirect("/admin");
return;
}
} else {
$messages{cadmerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_admadd_input} = \%settings;
$c->session->{restore_admadd_input}{admin} = $admin;
$c->response->redirect("/admin");
return;
}
=head2 do_delete_admin
Delete a admin.
=cut
sub do_delete_admin : Local {
my ( $self, $c ) = @_;
my $admin = $c->request->params->{admin};
if($c->model('Provisioning')->call_prov( $c, 'billing', 'delete_admin',
{ login => $admin },
undef
))
{
$c->session->{messages}{eadmmsg} = 'Web.Admin.Deleted';
$c->response->redirect("/admin");
return;
}
$c->response->redirect("/admin");
return;
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Sipwise::Provisioning::Billing, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The admin controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,328 @@
package admin::Controller::customer;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::customer - Catalyst Controller
=head1 DESCRIPTION
This provides functionality for customer administration.
=head1 METHODS
=head2 index
Display search form.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/customer.tt';
return 1;
}
=head2 search
Search for customers and display results.
=cut
sub search : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/customer.tt';
my $search_string = $c->request->params->{search_string};
my $customer_list;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'search_customers',
{ filter => { anything => '%'.$search_string.'%' } },
\$customer_list
);
$c->stash->{customer_list} = $$customer_list{customers}
if ref $$customer_list{customers} eq 'ARRAY';
}
=head2 getbyid
Check entered customer ID and redirect.
=cut
sub getbyid : Local {
my ( $self, $c ) = @_;
my $customer_id = $c->request->params->{customer_id};
if(defined $customer_id and $customer_id =~ /^\d+$/) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'get_customer',
{ id => $customer_id },
\$c->session->{customer}
))
{
$c->response->redirect("/customer/detail?customer_id=$customer_id");
return;
}
if($c->session->{prov_error} eq 'Client.Billing.NoSuchCustomer') {
delete $c->session->{prov_error};
$c->session->{messages} = { custgeterr => 'Client.Billing.NoSuchCustomer' };
}
} else {
$c->session->{messages} = { custgeterr => 'Web.Syntax.Numeric' };
}
$c->response->redirect("/customer");
return;
}
=head2 detail
Show customer details.
=cut
sub detail : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/customer_detail.tt';
my $customer_id = $c->request->params->{customer_id};
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_customer',
{ id => $customer_id },
\$c->session->{customer}
);
my $contracts;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_customer_contracts',
{ id => $customer_id },
\$contracts
);
$c->session->{customer}{contracts} = $$contracts{result};
my $orders;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_customer_orders',
{ id => $customer_id },
\$orders
);
$c->session->{customer}{orders} = $$orders{result};
$c->stash->{customer} = $c->session->{customer};
if(ref $c->session->{restore_customer_input} eq 'HASH') {
$c->stash->{customer} = $c->session->{restore_customer_input};
delete $c->session->{restore_customer_input};
}
if(ref $c->session->{restore_contact_input} eq 'HASH') {
$c->stash->{customer}{contact} = $c->session->{restore_contact_input};
delete $c->session->{restore_contact_input};
}
if(ref $c->session->{restore_comm_contact_input} eq 'HASH') {
$c->stash->{customer}{comm_contact} = $c->session->{restore_comm_contact_input};
delete $c->session->{restore_comm_contact_input};
}
if(ref $c->session->{restore_tech_contact_input} eq 'HASH') {
$c->stash->{customer}{tech_contact} = $c->session->{restore_tech_contact_input};
delete $c->session->{restore_tech_contact_input};
}
$c->stash->{show_pass} = $c->request->params->{show_pass};
$c->stash->{edit_customer} = $c->request->params->{edit_customer};
$c->stash->{edit_contact} = $c->request->params->{edit_contact};
$c->stash->{edit_commercial} = $c->request->params->{edit_commercial};
$c->stash->{edit_technical} = $c->request->params->{edit_technical};
return 1;
}
=head2 create_customer
Creates a new customer. Not yet implemented.
=cut
sub create_customer : Local {
my ( $self, $c ) = @_;
my $customer_id;
if($c->model('Provisioning')->call_prov( $c, 'billing', 'create_customer',
{
},
\$customer_id))
{
$c->response->redirect("/customer/detail?customer_id=$customer_id");
return;
}
$c->response->redirect("/customer");
return;
}
=head2 update_customer
Update details of a customer.
=cut
sub update_customer : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $customer_id = $c->request->params->{customer_id};
if(defined $c->request->params->{shopuser} and length $c->request->params->{shopuser}) {
$settings{shopuser} = $c->request->params->{shopuser};
my $checkresult;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'check_username',
$settings{shopuser}, \$checkresult
);
$messages{username} = 'Client.Syntax.MalformedUsername' unless $checkresult;
}
if(defined $c->request->params->{shoppass} and length $c->request->params->{shoppass}) {
$settings{shoppass} = $c->request->params->{shoppass};
$messages{password} = 'Client.Voip.PassLength' unless length $settings{shoppass} >= 6;
}
if(keys %settings and ! keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'update_customer',
{ id => $customer_id,
data => \%settings,
},
undef))
{
$messages{accmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/customer/detail?customer_id=$customer_id");
return;
}
}
$c->session->{messages} = \%messages;
$c->session->{restore_customer_input} = \%settings;
$c->response->redirect("/customer/detail?customer_id=$customer_id&edit_customer=1");
return;
}
=head2 update_contact
Update details of a customer's contact.
=cut
sub update_contact : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $customer_id = $c->request->params->{customer_id};
my $ctype = $c->request->params->{ctype};
if(defined $c->request->params->{gender} and length $c->request->params->{gender}) {
$settings{gender} = $c->request->params->{gender};
} else {
$settings{gender} = undef;
}
if(defined $c->request->params->{firstname} and length $c->request->params->{firstname}) {
$settings{firstname} = $c->request->params->{firstname};
} else {
$settings{firstname} = undef;
}
if(defined $c->request->params->{lastname} and length $c->request->params->{lastname}) {
$settings{lastname} = $c->request->params->{lastname};
} else {
$settings{lastname} = undef;
}
if(defined $c->request->params->{comregnum} and length $c->request->params->{comregnum}) {
$settings{comregnum} = $c->request->params->{comregnum};
} else {
$settings{comregnum} = undef;
}
if(defined $c->request->params->{company} and length $c->request->params->{company}) {
$settings{company} = $c->request->params->{company};
} else {
$settings{company} = undef;
}
if(defined $c->request->params->{street} and length $c->request->params->{street}) {
$settings{street} = $c->request->params->{street};
} else {
$settings{street} = undef;
}
if(defined $c->request->params->{postcode} and length $c->request->params->{postcode}) {
$settings{postcode} = $c->request->params->{postcode};
} else {
$settings{postcode} = undef;
}
if(defined $c->request->params->{phonenumber} and length $c->request->params->{phonenumber}) {
$settings{phonenumber} = $c->request->params->{phonenumber};
} else {
$settings{phonenumber} = undef;
}
if(defined $c->request->params->{mobilenumber} and length $c->request->params->{mobilenumber}) {
$settings{mobilenumber} = $c->request->params->{mobilenumber};
} else {
$settings{mobilenumber} = undef;
}
if(defined $c->request->params->{email} and length $c->request->params->{email}) {
$settings{email} = $c->request->params->{email};
} else {
$settings{email} = undef;
}
if(defined $c->request->params->{newsletter} and $c->request->params->{newsletter}) {
$settings{newsletter} = 1;
} else {
$settings{newsletter} = 0;
}
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'update_customer',
{ id => $customer_id,
data => { $ctype => \%settings },
},
undef))
{
$messages{accmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/customer/detail?customer_id=$customer_id");
return;
}
}
$c->session->{messages} = \%messages;
$c->session->{'restore_'.$ctype.'_input'} = \%settings;
$c->response->redirect("/customer/detail?customer_id=$customer_id&edit_customer=1");
return;
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Sipwise::Provisioning::Billing, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The account controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,191 @@
package admin::Controller::domain;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::domain - Catalyst Controller
=head1 DESCRIPTION
Catalyst Controller.
=head1 METHODS
=head2 index
Display domain list.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/domain.tt';
my $domains;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_domains',
undef,
\$domains
);
$c->stash->{domains} = $$domains{result};
$c->stash->{edit_domain} = $c->request->params->{edit_domain};
if(ref $c->session->{restore_domedit_input} eq 'HASH') {
foreach my $domain (@{$c->stash->{domains}}) {
next unless $$domain{domain} eq $c->stash->{edit_domain};
$domain = { %$domain, %{$c->session->{restore_domedit_input}} };
last;
}
delete $c->session->{restore_domedit_input};
}
if(ref $c->session->{restore_domadd_input} eq 'HASH') {
$c->stash->{arefill} = $c->session->{restore_domadd_input};
delete $c->session->{restore_domadd_input};
}
return 1;
}
=head2 do_edit_domain
Change settings for a domain.
=cut
sub do_edit_domain : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $domain = $c->request->params->{domain};
$settings{cc} = $c->request->params->{cc};
$messages{ecc} = 'Client.Voip.MalformedCc'
unless $settings{cc} =~ /^\d+$/;
$settings{timezone} = $c->request->params->{timezone};
$messages{etimezone} = 'Client.Syntax.MalformedTimezone'
unless $settings{timezone} =~ m#^\w+/\w.+$#;
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'update_domain',
{ domain => $domain,
data => \%settings,
},
undef
))
{
$messages{edommsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/domain");
return;
}
} else {
$messages{edomerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_domedit_input} = \%settings;
$c->response->redirect("/domain?edit_domain=$domain");
return;
}
=head2 do_create_domain
Create a new domain.
=cut
sub do_create_domain : Local {
my ( $self, $c ) = @_;
my %messages;
my %settings;
my $domain = $c->request->params->{domain};
$settings{cc} = $c->request->params->{cc};
$messages{acc} = 'Client.Voip.MalformedCc'
unless $settings{cc} =~ /^\d+$/;
$settings{timezone} = $c->request->params->{timezone};
$messages{atimezone} = 'Client.Syntax.MalformedTimezone'
unless $settings{timezone} =~ m#^\w+/\w.+$#;
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', 'create_domain',
{ domain => $domain,
data => \%settings,
},
undef
))
{
$messages{cdommsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/domain");
return;
}
} else {
$messages{cdomerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_domadd_input} = \%settings;
$c->session->{restore_domadd_input}{domain} = $domain;
$c->response->redirect("/domain");
return;
}
=head2 do_delete_domain
Delete a domain.
=cut
sub do_delete_domain : Local {
my ( $self, $c ) = @_;
my $domain = $c->request->params->{domain};
if($c->model('Provisioning')->call_prov( $c, 'billing', 'delete_domain',
{ domain => $domain },
undef
))
{
$c->session->{messages}{edommsg} = 'Web.Domain.Deleted';
$c->response->redirect("/domain");
return;
}
$c->response->redirect("/domain");
return;
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Sipwise::Provisioning::Billing, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The domain controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,67 @@
package admin::Controller::login;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::login - Catalyst Controller
=head1 DESCRIPTION
This allows a user to log in.
=head1 METHODS
=head2 index
The authentication function.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->log->debug('***login::index called');
my $username = $c->request->params->{username} || "";
my $password = $c->request->params->{password} || "";
if ($username && $password) {
if($c->model('Provisioning')->login($c, $username, $password)) {
$c->log->debug('***Login::index login successfull');
}
} else {
$c->session->{prov_error} = 'Client.Syntax.LoginMissingPass' unless length $password;
$c->session->{prov_error} = 'Client.Syntax.LoginMissingUsername' unless length $username;
}
$c->response->redirect($c->uri_for('/'));
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The login controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,59 @@
package admin::Controller::logout;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::logout - Catalyst Controller
=head1 DESCRIPTION
This will log a user out.
=head1 METHODS
=head2 index
The logout function.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->log->debug('***logout::index called');
$c->logout();
delete $c->session->{admin};
$c->response->redirect($c->uri_for('/'));
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The logout controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,850 @@
package admin::Controller::subscriber;
use strict;
use warnings;
use base 'Catalyst::Controller';
=head1 NAME
admin::Controller::subscriber - Catalyst Controller
=head1 DESCRIPTION
This provides functionality for VoIP subscriber administration.
=head1 METHODS
=head2 index
Display search form.
=cut
sub index : Private {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/subscriber.tt';
return 1;
}
=head2 search
Search for subscribers and display results.
=cut
sub search : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/subscriber.tt';
my $limit = 10;
my $searchstring = $c->request->params->{search_string};
my $offset = $c->request->params->{offset} || 0;
$offset = 0 if $offset !~ /^\d+$/;
my $subscriber_list;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'search_subscribers',
{ filter => { username => '%'.$searchstring.'%',
limit => $limit,
offset => $limit * $offset,
},
},
\$subscriber_list
);
$c->stash->{search_string} = $searchstring;
$c->stash->{searched} = 1;
if(ref $$subscriber_list{subscribers} eq 'ARRAY' and @{$$subscriber_list{subscribers}}) {
$c->stash->{subscriber_list} = $$subscriber_list{subscribers};
$c->stash->{total_count} = $$subscriber_list{total_count};
$c->stash->{offset} = $offset;
if($$subscriber_list{total_count} > @{$$subscriber_list{subscribers}}) {
# paginate!
my @pagination;
foreach my $page (0 .. int(($$subscriber_list{total_count} - 1) / $limit)) {
push @pagination, { offset => $page };
}
$c->stash->{max_offset} = $#pagination;
if($#pagination > 10) {
if($offset <= 5) {
splice @pagination, 9, @pagination - (10), ({offset => -1});
} else {
if($offset < @pagination - 6) {
splice @pagination, $offset + 4, @pagination - ($offset + 5), ({offset => -1});
splice @pagination, 1, $offset - 4, ({offset => -1});
} else {
splice @pagination, 1, @pagination - 10, ({offset => -1});
}
}
}
$c->stash->{pagination} = \@pagination;
}
}
return 1;
}
=head2 detail
Display subscriber details.
=cut
sub detail : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/subscriber_detail.tt';
my $is_new = $c->request->params->{new};
my $preferences;
unless($is_new) {
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}
);
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$preferences
);
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_voicebox_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$c->session->{subscriber}{voicebox_preferences}
);
$c->stash->{subscriber} = $c->session->{subscriber};
$c->stash->{subscriber}{subscriber_id} = $subscriber_id;
$c->stash->{subscriber}{is_locked} = $c->model('Provisioning')->localize($c->view($c->config->{view})->
config->{VARIABLES}{site_config}{language},
'Web.Subscriber.Lock'.$$preferences{lock})
if $$preferences{lock};
} else {
$c->stash->{account_id} = $c->request->params->{account_id};
$c->stash->{edit_subscriber} = 1;
my $domains;
return unless $c->model('Provisioning')->call_prov( $c, 'billing', 'get_domains',
undef, \$domains
);
$c->stash->{domains} = $$domains{result};
}
my $db_prefs;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_preferences',
undef, \$db_prefs
);
$c->session->{voip_preferences} = $$db_prefs{result};
### restore data entered by the user ###
if(ref $c->session->{restore_subscriber_input} eq 'HASH') {
if(ref $c->stash->{subscriber} eq 'HASH') {
$c->stash->{subscriber} = { %{$c->stash->{subscriber}}, %{$c->session->{restore_subscriber_input}} };
} else {
$c->stash->{subscriber} = $c->session->{restore_subscriber_input};
}
$c->stash->{subscriber}{edit_pass} = $c->session->{restore_subscriber_input}{password}
if defined $c->session->{restore_subscriber_input}{password};
$c->stash->{subscriber}{edit_webpass} = $c->session->{restore_subscriber_input}{webpassword}
if defined $c->session->{restore_subscriber_input}{webpassword};
delete $c->session->{restore_subscriber_input};
}
if(ref $c->session->{restore_preferences_input} eq 'HASH') {
if(ref $preferences eq 'HASH') {
$preferences = { %$preferences, %{$c->session->{restore_preferences_input}} };
} else {
$preferences = $c->session->{restore_preferences_input};
}
delete $c->session->{restore_preferences_input};
}
if(ref $c->session->{restore_vboxprefs_input} eq 'HASH') {
if(ref $c->stash->{subscriber}{voicebox_preferences} eq 'HASH') {
$c->stash->{subscriber}{voicebox_preferences} = { %{$c->stash->{subscriber}{voicebox_preferences}},
%{$c->session->{restore_vboxprefs_input}} };
} else {
$c->stash->{subscriber}{voicebox_preferences} = $c->session->{restore_vboxprefs_input};
}
delete $c->session->{restore_vboxprefs_input};
}
### build preference array for TT ###
if(ref $c->session->{voip_preferences} eq 'ARRAY') {
my $cftarget;
my @stashprefs;
foreach my $pref (@{$c->session->{voip_preferences}}) {
# not a subscriber preference
next if $$pref{attribute} eq 'cc';
# managed separately
next if $$pref{attribute} eq 'lock';
# only for extensions enabled systems
next if ( $$pref{attribute} eq 'base_cli'
or $$pref{attribute} eq 'base_user'
or $$pref{attribute} eq 'extension'
or $$pref{attribute} eq 'has_extension' )
and !$c->config->{extension_features};
if($$pref{attribute} eq 'cfu'
or $$pref{attribute} eq 'cfb'
or $$pref{attribute} eq 'cft'
or $$pref{attribute} eq 'cfna')
{
if(defined $$preferences{$$pref{attribute}} and length $$preferences{$$pref{attribute}}) {
if($$preferences{$$pref{attribute}} =~ /voicebox\.local$/) {
$$cftarget{voicebox} = 1;
} else {
$$cftarget{sipuri} = $$preferences{$$pref{attribute}};
$$cftarget{sipuri} =~ s/^sip://i;
if($$cftarget{sipuri} =~ /^\+?\d+\@/) {
$$cftarget{sipuri} =~ s/\@.*$//;
}
}
}
} elsif($$pref{attribute} eq 'cli') {
if(defined $$preferences{$$pref{attribute}} and length $$preferences{$$pref{attribute}}) {
$$preferences{$$pref{attribute}} =~ s/^sip://i;
$$preferences{$$pref{attribute}} =~ s/\@.*$//
if $$preferences{$$pref{attribute}} =~ /^\+?\d+\@/;
}
}
push @stashprefs,
{ key => $$pref{attribute},
value => $$preferences{$$pref{attribute}},
max_occur => $$pref{max_occur},
error => $c->session->{messages}{$$pref{attribute}}
? $c->model('Provisioning')->localize($c->view($c->config->{view})->
config->{VARIABLES}{site_config}{language},
$c->session->{messages}{$$pref{attribute}})
: undef,
};
}
# OMG
# reorder preferences so "cftarget" appears just above "cfu" and friends
foreach my $stashpref (@stashprefs) {
if($$stashpref{key} eq 'cfu') {
push @{$c->stash->{subscriber}{preferences_array}},
{ key => 'cftarget',
value => $cftarget,
max_occur => 1,
error => $c->session->{messages}{cftarget}
? $c->model('Provisioning')->localize($c->view($c->config->{view})->
config->{VARIABLES}{site_config}{language},
$c->session->{messages}{cftarget})
: undef,
};
}
push @{$c->stash->{subscriber}{preferences_array}}, $stashpref;
}
}
$c->stash->{show_pass} = $c->request->params->{show_pass};
$c->stash->{show_webpass} = $c->request->params->{show_webpass};
$c->stash->{edit_subscriber} = $c->request->params->{edit_subscriber}
unless $is_new;
$c->stash->{edit_preferences} = $c->request->params->{edit_preferences};
$c->stash->{edit_voicebox} = $c->request->params->{edit_voicebox};
return 1;
}
=head2 update_subscriber
Update subscriber data or create a new subscriber.
=cut
sub update_subscriber : Local {
my ( $self, $c ) = @_;
my (%settings, %messages);
my $subscriber_id = $c->request->params->{subscriber_id};
if($subscriber_id) {
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_byid',
{ subscriber_id => $subscriber_id },
\$c->session->{subscriber}
);
} else {
my $checkresult;
$c->session->{subscriber}{account_id} = $c->request->params->{account_id};
$c->session->{subscriber}{username} = $settings{webusername} = $c->request->params->{username};
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'check_username',
$c->session->{subscriber}{username}, \$checkresult
);
$messages{username} = 'Client.Syntax.MalformedUsername' unless($checkresult);
$c->session->{subscriber}{domain} = $c->request->params->{domain};
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'check_domain',
$c->session->{subscriber}{domain}, \$checkresult
);
$messages{domain} = 'Client.Syntax.MalformedDomain' unless($checkresult);
}
$settings{admin} = 1 if $c->request->params->{admin};
my $password = $c->request->params->{password};
if(length $password) {
$settings{password} = $password;
if(length $password < 6) {
$messages{password} = 'Client.Voip.PassLength';
}
}
my $webpassword = $c->request->params->{webpassword};
if(length $webpassword) {
$settings{webpassword} = $webpassword;
if(length $webpassword < 6) {
$messages{webpassword} = 'Client.Voip.PassLength';
}
}
my $cc = $c->request->params->{cc};
my $ac = $c->request->params->{ac};
my $sn = $c->request->params->{sn};
if(length $cc or length $ac or length $sn) {
$settings{cc} = $cc;
$settings{ac} = $ac;
$settings{sn} = $sn;
unless(length $cc and length $ac and length $sn) {
$messages{number} = 'Client.Voip.MissingNumberPart';
} else {
$messages{number_cc} = 'Client.Voip.MalformedCc'
unless $cc =~ /^[1-9][0-9]{0,2}$/;
$messages{number_ac} = 'Client.Voip.MalformedAc'
unless $ac =~ /^[1-9][0-9]{0,4}$/;
$messages{number_sn} = 'Client.Voip.MalformedSn'
unless $sn =~ /^[1-9][0-9]+$/;
}
}
my $timezone = $c->request->params->{timezone};
if(length $timezone) {
$settings{timezone} = $timezone;
$messages{timezone} = 'Client.Syntax.MalformedTimezone'
unless $timezone =~ m#^\w+/\w.+$#;
}
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'billing', ($subscriber_id
? 'update_voip_account_subscriber'
: 'add_voip_account_subscriber'),
{ id => $c->session->{subscriber}{account_id},
subscriber => { username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
%settings
},
},
undef))
{
$messages{submsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
if($subscriber_id) {
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id");
} else {
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$c->session->{subscriber}
);
$c->response->redirect("/subscriber/detail?subscriber_id=". $c->session->{subscriber}{subscriber_id});
}
return;
}
} else {
$messages{suberr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_subscriber_input} = \%settings;
if($subscriber_id) {
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id&edit_subscriber=1");
} else {
$c->session->{restore_subscriber_input}{username} = $c->session->{subscriber}{username};
$c->response->redirect("/subscriber/detail?account_id=". $c->session->{subscriber}{account_id} ."&new=1");
}
return;
}
=head2 lock
Locks a subscriber.
=cut
sub lock : Local {
my ( $self, $c ) = @_;
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}
);
my $lock = $c->request->params->{lock};
$c->model('Provisioning')->call_prov( $c, 'billing', 'lock_voip_account_subscriber',
{ id => $c->session->{subscriber}{account_id},
username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
lock => $lock,
},
undef
);
$c->response->redirect("/subscriber/detail?subscriber_id=". $c->request->params->{subscriber_id});
}
=head2 terminate
Terminates a subscriber.
=cut
sub terminate : Local {
my ( $self, $c ) = @_;
my %messages;
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}
);
if($c->model('Provisioning')->call_prov( $c, 'billing', 'terminate_voip_account_subscriber',
{ id => $c->session->{subscriber}{account_id},
username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
undef))
{
$messages{topmsg} = 'Server.Voip.SubscriberDeleted';
$c->session->{messages} = \%messages;
$c->response->redirect("/subscriber");
return;
}
$c->session->{messages} = \%messages;
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id");
return;
}
sub update_preferences : Local {
my ( $self, $c ) = @_;
my %messages;
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}
);
my $preferences;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$preferences
);
## remove preferences that can't be changed
delete $$preferences{prepaid};
delete $$preferences{base_cli};
delete $$preferences{extension};
delete $$preferences{base_user};
delete $$preferences{has_extension};
### blocklists ###
my $block_in_mode = $c->request->params->{block_in_mode};
if(defined $block_in_mode) {
$$preferences{block_in_mode} = $block_in_mode eq 'whitelist' ? 1 : 0;
}
my $block_out_mode = $c->request->params->{block_out_mode};
if(defined $block_out_mode) {
$$preferences{block_out_mode} = $block_out_mode eq 'whitelist' ? 1 : 0;
}
$$preferences{block_in_clir} = $c->request->params->{block_in_clir} ? 1 : undef;
my $block_in_list = $c->request->params->{block_in_list};
### call forwarding ###
my $fw_target_select = $c->request->params->{fw_target};
unless($fw_target_select) {
$messages{target} = 'Client.Voip.MalformedTargetClass';
}
my $fw_target;
if($fw_target_select eq 'sipuri') {
$fw_target = $c->request->params->{fw_sipuri};
# normalize, so we can do some checks.
$fw_target =~ s/^sip://i;
if($fw_target =~ /^\+?\d+\@[a-z0-9.-]+$/i) {
$fw_target =~ s/\@.+$//;
}
if($fw_target =~ /^\+?\d+$/) {
if($fw_target =~ /^\+[1-9][0-9]+$/) {
$fw_target = 'sip:'. $fw_target .'@'. $c->session->{subscriber}{domain};
} elsif($fw_target =~ /^00[1-9][0-9]+$/) {
$fw_target =~ s/^00/+/;
$fw_target = 'sip:'. $fw_target .'@'. $c->session->{subscriber}{domain};
} elsif($fw_target =~ /^0[1-9][0-9]+$/) {
$fw_target =~ s/^0/'+'.$c->session->{subscriber}{cc}/e;
$fw_target = 'sip:'. $fw_target .'@'. $c->session->{subscriber}{domain};
} else {
$messages{target} = 'Client.Voip.MalformedNumber';
$fw_target = $c->request->params->{fw_sipuri};
}
} elsif($fw_target =~ /^[a-z0-9&=+\$,;?\/_.!~*'()-]+\@[a-z0-9.-]+$/i) {
$fw_target = 'sip:'. lc $fw_target;
} elsif($fw_target =~ /^[a-z0-9&=+\$,;?\/_.!~*'()-]+$/) {
$fw_target = 'sip:'. lc($fw_target) .'@'. $c->session->{subscriber}{domain};
} else {
$messages{target} = 'Client.Voip.MalformedTarget';
$fw_target = $c->request->params->{fw_sipuri};
}
} elsif($fw_target_select eq 'voicebox') {
$fw_target = 'sip:vmu'.$c->session->{subscriber}{cc}.$c->session->{subscriber}{ac}.$c->session->{subscriber}{sn}.'@voicebox.local';
} else {
# wtf?
}
my $cfu = $c->request->params->{cfu};
my $cfb = $c->request->params->{cfb};
my $cft = $c->request->params->{cft};
my $cfna = $c->request->params->{cfna};
# clear all forwards
$$preferences{cfu} = undef;
$$preferences{cft} = undef;
$$preferences{cfb} = undef;
$$preferences{cfna} = undef;
$$preferences{ringtimeout} = undef;
unless(defined $cfu or defined $cfb or defined $cft or defined $cfna) {
delete $messages{target} if exists $messages{target};
} else {
if(defined $cfu) {
# forward unconditionally
$$preferences{cfu} = $fw_target;
} else {
if(defined $cfb) {
$$preferences{cfb} = $fw_target;
}
if(defined $cft) {
$$preferences{cft} = $fw_target;
}
if(defined $cfna) {
$$preferences{cfna} = $fw_target;
}
}
}
if(defined $$preferences{cft}) {
$$preferences{ringtimeout} = $c->request->params->{ringtimeout};
unless(defined $$preferences{ringtimeout} and $$preferences{ringtimeout} =~ /^\d+$/
and $$preferences{ringtimeout} < 301 and $$preferences{ringtimeout} > 4)
{
$messages{ringtimeout} = 'Client.Voip.MissingRingtimeout';
}
}
### outgoing calls ###
$$preferences{cli} = $c->request->params->{cli} or undef;
if(defined $$preferences{cli} and $$preferences{cli} =~ /^\d+$/) {
$$preferences{cli} = 'sip:'.$$preferences{cli}.'@'.$c->session->{subscriber}{domain};
}
$$preferences{clir} = $c->request->params->{clir} ? 1 : undef;
$$preferences{cc} = $c->request->params->{cc} || undef;
if(defined $$preferences{cc} and $$preferences{cc} !~ /^[1-9]\d*$/) {
$messages{cc} = 'Client.Voip.MalformedCc';
}
$$preferences{ac} = $c->request->params->{ac} || undef;
if(defined $$preferences{ac} and $$preferences{ac} !~ /^[1-9]\d*$/) {
$messages{ac} = 'Client.Voip.MalformedAc';
}
$$preferences{svc_ac} = $c->request->params->{svc_ac} || undef;
if(defined $$preferences{svc_ac} and $$preferences{svc_ac} !~ /^[1-9]\d*$/) {
$messages{svc_ac} = 'Client.Voip.MalformedAc';
}
$$preferences{emerg_ac} = $c->request->params->{emerg_ac} || undef;
if(defined $$preferences{emerg_ac} and $$preferences{emerg_ac} !~ /^[1-9]\d*$/) {
$messages{emerg_ac} = 'Client.Voip.MalformedAc';
}
### save settings ###
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'voip', 'set_subscriber_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
preferences => $preferences,
},
undef
))
{
$messages{prefmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id#userprefs");
return;
}
} else {
$messages{preferr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_preferences_input} = $preferences;
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id&edit_preferences=1#userprefs");
return;
}
sub update_voicebox : Local {
my ( $self, $c ) = @_;
my %messages;
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}
);
my $vboxprefs;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_voicebox_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$vboxprefs
);
$$vboxprefs{password} = $c->request->params->{password} || undef;
if(defined $$vboxprefs{password} and $$vboxprefs{password} !~ /^\d{4}$/) {
$messages{vpin} = 'Client.Syntax.VoiceBoxPin';
}
$$vboxprefs{email} = $c->request->params->{email};
if(defined $$vboxprefs{email} and length $$vboxprefs{email}) {
my $checkresult;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'check_email',
$$vboxprefs{email}, \$checkresult
);
$messages{vemail} = 'Client.Syntax.Email' unless($checkresult);
} else {
$$vboxprefs{email} = undef;
}
$$vboxprefs{attach} = $c->request->params->{attach} ? 1 : 0;
### save settings ###
unless(keys %messages) {
if($c->model('Provisioning')->call_prov( $c, 'voip', 'set_subscriber_voicebox_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
preferences => $vboxprefs,
},
undef
))
{
$messages{vboxmsg} = 'Server.Voip.SavedSettings';
$c->session->{messages} = \%messages;
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id#vboxprefs");
return;
}
} else {
$messages{vboxerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->session->{restore_vboxprefs_input} = $vboxprefs;
$c->response->redirect("/subscriber/detail?subscriber_id=$subscriber_id&edit_voicebox=1#vboxprefs");
return;
}
sub edit_list : Local {
my ( $self, $c ) = @_;
$c->stash->{template} = 'tt/subscriber_edit_list.tt';
my %messages;
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}
);
my $preferences;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$preferences
);
my $list = $c->request->params->{list_name};
if(defined $$preferences{$list}) {
my $block_list = ref $$preferences{$list} ? $$preferences{$list} : [ $$preferences{$list} ];
my @block_list_to_sort;
foreach my $blockentry (@$block_list) {
my $active = $blockentry =~ s/^#// ? 0 : 1;
$blockentry =~ s/^([1-9])/+$1/;
push @block_list_to_sort, { entry => $blockentry, active => $active };
}
my $bg = '';
my $i = 1;
foreach my $blockentry (sort {$a->{entry} cmp $b->{entry}} @block_list_to_sort) {
push @{$c->stash->{list_data}}, { number => $$blockentry{entry},
background => $bg ? '' : 'alt',
id => $i++,
active => $$blockentry{active},
};
$bg = !$bg;
}
}
$c->stash->{subscriber} = $c->session->{subscriber};
$c->stash->{subscriber_id} = $subscriber_id;
$c->stash->{list_name} = $list;
if(defined $c->session->{blockaddtxt}) {
$c->stash->{blockaddtxt} = $c->session->{blockaddtxt};
delete $c->session->{blockaddtxt};
}
return 1;
}
sub do_edit_list : Local {
my ( $self, $c ) = @_;
my %messages;
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}
);
my $preferences;
return unless $c->model('Provisioning')->call_prov( $c, 'voip', 'get_subscriber_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
},
\$preferences
);
my $list = $c->request->params->{list_name};
# input text field to add new entry to block list
my $add = $c->request->params->{block_add};
if(defined $add) {
if($add =~ /^\+?[?*0-9\[\]]+$/) {
if($add =~ /^[1-9\[]/) {
$add =~ s/^/$c->session->{subscriber}{cc}.$c->session->{subscriber}{ac}/e;
} elsif($add =~ /^0[^0]/) {
$add =~ s/^0/$c->session->{subscriber}{cc}/e;
}
$add =~ s/^\+/00/;
$add =~ s/^00+//;
my $blocklist = $$preferences{$list};
$blocklist = [] unless defined $blocklist;
$blocklist = [ $blocklist ] unless ref $blocklist;
$$preferences{$list} = [ @$blocklist, $add ];
} else {
$messages{msgadd} = 'Client.Voip.MalformedNumberPattern';
$c->session->{blockaddtxt} = $add;
}
}
# delete link next to entries in block list
my $del = $c->request->params->{block_del};
if(defined $del) {
my $blocklist = $$preferences{$list};
if(defined $blocklist) {
$del =~ s/^\+//;
$del =~ s/^0/$c->session->{subscriber}{cc}/e;
$blocklist = [ $blocklist ] unless ref $blocklist;
if($c->request->params->{block_stat}) {
$$preferences{$list} = [ grep { $_ ne $del } @$blocklist ];
} else {
$$preferences{$list} = [ grep { $_ ne '#'.$del } @$blocklist ];
}
}
}
# activate/deactivate link next to entries in block list
my $act = $c->request->params->{block_act};
if(defined $act) {
print STDERR "Got request to de/activate $act...\n";
my $blocklist = $$preferences{$list};
if(defined $blocklist) {
$act =~ s/^\+//;
$act =~ s/^0/$c->session->{subscriber}{cc}/e;
$blocklist = [ $blocklist ] unless ref $blocklist;
if($c->request->params->{block_stat}) {
$$preferences{$list} = [ grep { $_ ne $act } @$blocklist ];
push @{$$preferences{$list}}, '#'.$act;
} else {
$$preferences{$list} = [ grep { $_ ne '#'.$act } @$blocklist ];
push @{$$preferences{$list}}, $act;
}
}
}
unless(keys %messages) {
$c->model('Provisioning')->call_prov( $c, 'voip', 'set_subscriber_preferences',
{ username => $c->session->{subscriber}{username},
domain => $c->session->{subscriber}{domain},
preferences => {
$list => $$preferences{$list},
},
},
undef
);
} else {
$messages{numerr} = 'Client.Voip.InputErrorFound';
}
$c->session->{messages} = \%messages;
$c->response->redirect("/subscriber/edit_list?subscriber_id=$subscriber_id&list_name=$list");
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Provisioning model, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The subscriber controller is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,212 @@
package admin::Model::Provisioning;
use strict;
use warnings;
use base 'Catalyst::Model';
use Scalar::Util;
use Catalyst::Plugin::Authentication;
use Sipwise::Provisioning::Voip;
use Sipwise::Provisioning::Billing;
=head1 NAME
admin::Model::Provisioning - Sipwise provisioning catalyst model
=head1 DESCRIPTION
Catalyst Model that uses Sipwise::Provisioning::Voip to get and set VoIP
admin and user data.
=cut
sub new {
my $class = shift;
my $self = {};
$$self{voip} = Sipwise::Provisioning::Voip->new();
$$self{billing} = Sipwise::Provisioning::Billing->new();
return bless $self, $class;
}
sub call_prov {
# model, catalyst, scalar, scalar, hash-ref, scalar-ref
my ($self, $c, $backend, $function, $parameter, $result) = @_;
$c->log->debug("***Provisioning::call_prov calling '$backend\::$function'");
eval {
$$result = $$self{$backend}->handle_request( $function,
{
authentication => {
type => 'admin',
username => $c->session->{admin}{login},
password => $c->session->{admin}{password},
},
parameters => $parameter,
});
};
if($@) {
if(ref $@ eq 'SOAP::Fault') {
$c->log->error("***Provisioning::call_prov: $backend\::$function failed: ". $@->faultstring);
$c->session->{prov_error} = $@->faultcode;
} else {
$c->log->error("***Provisioning::call_prov: $backend\::$function failed: $@");
$c->session->{prov_error} = 'Server.Internal';
}
return;
}
return 1;
}
##########################
# non-standard functions #
##########################
sub login {
my ($self, $c, $admin, $password) = @_;
$c->log->debug('***Provisioning::login called, authenticating...');
unless(defined $admin and length $admin) {
$c->session->{prov_error} = 'Client.Voip.MissingUsername';
return;
}
unless(defined $password and length $password) {
$c->session->{prov_error} = 'Client.Voip.MissingPass';
return;
}
unless(Scalar::Util::blessed($admin)
and ($Catalyst::Plugin::Authentication::VERSION < 0.10003
? $admin->isa("Catalyst::Plugin::Authentication::User")
: $admin->isa("Catalyst::Authentication::User")))
{
if(my $user_obj = $self->_get_admin($c, $admin)) {
$admin = $user_obj;
} else {
if($c->session->{prov_error} and $c->session->{prov_error} eq 'Server.Voip.NoSuchAdmin') {
$c->log->info("***Provisioning::login authentication failed for '$admin', unknown login.");
$c->session->{prov_error} = 'Client.Voip.AuthFailed';
}
return;
}
}
if($self->_auth_admin($c, $admin, $password)) {
$c->set_authenticated($admin);
$c->log->debug('***Provisioning::login authentication succeeded.');
$$admin{password} = $password;
$c->session->{admin} = $admin;
return 1;
}
$c->log->info("***Provisioning::login authentication failed for '$$admin{login}', wrong password.");
$c->session->{prov_error} = 'Client.Voip.AuthFailed';
return;
}
sub localize {
my ($self, $lang, $messages) = @_;
return unless defined $messages;
if(ref $messages eq 'HASH') {
my %translations;
foreach my $msgname (keys %$messages) {
$translations{$msgname} = eval { $$self{voip}->get_localized_string({language => $lang, code => $$messages{$msgname}}) };
unless(defined $translations{$msgname}) {
$translations{$msgname} = eval { $$self{voip}->get_localized_string({language => $lang, code => 'Server.Internal'}) };
}
}
return \%translations;
} elsif(!ref $messages) {
my $translation = eval { $$self{voip}->get_localized_string({language => $lang, code => $messages}) };
unless(defined $translation) {
$translation = eval { $$self{voip}->get_localized_string({language => $lang, code => 'Server.Internal'}) };
}
return $translation;
}
return;
}
####################
# helper functions #
####################
sub _get_admin {
my ($self, $c, $login) = @_;
my $admin_obj = eval {
$$self{billing}->get_admin({login => $login});
};
if($@) {
if(ref $@ eq 'SOAP::Fault') {
$c->log->error("***Provisioning::_get_admin failed to get admin '$login' from DB: ". $@->faultstring)
unless $@->faultcode eq 'Server.Voip.NoSuchAdmin';
$c->session->{prov_error} = $@->faultcode;
} else {
$c->log->error("***Provisioning::_get_admin failed to get admin '$login' from DB: $@.");
$c->session->{prov_error} = 'Server.Internal';
}
return;
}
my $return = { %$admin_obj, id => $login, store => $self };
if($Catalyst::Plugin::Authentication::VERSION < 0.10003) {
return bless $return, "Catalyst::Plugin::Authentication::User::Hash";
} else {
return bless $return, "Catalyst::Authentication::User::Hash";
}
}
sub _auth_admin {
my ($self, $c, $admin, $pass) = @_;
eval { $$self{billing}->authenticate_admin({ login => $$admin{login},
password => $pass,
});
};
if($@) {
if(ref $@ eq 'SOAP::Fault') {
$c->log->error("***Provisioning::_auth_admin failed to auth admin '$$admin{login}': ". $@->faultstring);
$c->session->{prov_error} = $@->faultcode;
} else {
$c->log->error("***Provisioning::_auth_admin failed to auth admin '$$admin{login}': $@.");
$c->session->{prov_error} = 'Server.Internal';
}
return;
}
return 1;
}
=head1 BUGS AND LIMITATIONS
=over
=item currently none
=back
=head1 SEE ALSO
Sipwise::Provisioning::Voip, Sipwise::Provisioning::Billing, Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The Provisioning model is Copyright (c) 2007 Sipwise GmbH, Austria. All
rights reserved.
=cut
# ende gelaende
1;

@ -0,0 +1,64 @@
package admin::View::Sipwise;
use strict;
use warnings;
use base 'Catalyst::View::TT';
=head1 NAME
admin::View::Sipwise - Sipwise Catalyst View for NGCP admin interface
=head1 DESCRIPTION
Sipwise Catalyst View for NGCP admin interface.
=cut
__PACKAGE__->config(
## PRE_PROCESS => 'config/main',
## TEMPLATE_EXTENSION => '.tt',
MACRO => 'debug(message) CALL Catalyst.log.debug(message)',
INCLUDE_PATH => [
admin->path_to( 'root' ),
],
WRAPPER => 'layout/wrapper',
ERROR => 'tt/error.tt',
CATALYST_VAR => 'Catalyst',
VARIABLES => {
site_config => {
language => 'en',
language_string => 'English',
css => [ '/css/sipwise.css', '/css/admin.css' ],
title => 'Sipwise NGCP admin interface',
company => {
name => 'Sipwise GmbH'
},
},
},
);
=head1 BUGS AND LIMITATIONS
=over
=item none.
=back
=head1 SEE ALSO
Catalyst
=head1 AUTHORS
Daniel Tiefnig <dtiefnig@sipwise.com>
=head1 COPYRIGHT
The Sipwise view is Copyright (c) 2007 Sipwise GmbH, Austria. All rights
reserved.
=cut
# over and out
1;

@ -0,0 +1,260 @@
/*
* Relative size definitions like "large" ,"0.8em", etc.
* don't work very well 'cross different browsers.
* Allthought it is recommended to use them, they don't
* give useable results.
*/
body {
font: 14px Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif !important;
/* hover effects for IE6 */
behavior:url("/css/csshover.htc");
}
h3 {
font-size: 18px !important;
}
#header #sipwise_logo {
margin: 3px;
width: 200px;
}
#header #heading {
position: absolute;
top: 20px;
left: 250px;
text-align: center;
width: 400px;
font-weight: bold;
font-size: 20px;
}
#header #client_logo {
position: absolute;
top: 2px;
right: 2px;
margin: 3px;
width: 200px;
text-align: right;
}
/* hmm, menu doesn't extend container height ... */
#container #content {
min-height: 570px;
}
/* contentmenu's width and contentplace' margin-left should match */
#container #contentmenu {
width: 230px;
}
#container #contentplace {
margin-left: 230px;
}
/* red error messages */
.errormsg {
width: auto;
margin: 0;
padding: 5px;
font-weight: bold;
color: #FF1010;
}
/* green success messages */
.goodmsg {
width: auto;
margin: 0;
padding: 5px;
font-weight: bold;
color: #10BB40;
}
/* highlight text */
.alert {
width: auto;
margin: 0;
padding: 0;
font-weight: bold;
color: #FF1010;
}
/* increase vertical spacing in tables */
#contentplace td {
padding: 3px 5px 3px 5px;
}
/* some table alignment aliases */
#contentplace .tdcenter {
text-align: center;
}
#contentplace .tdmiddle {
vertical-align: top;
}
#contentplace .tdkey {
font-weight: bold;
white-space: nowrap;
}
#contentplace .table_header {
font-weight: bold;
white-space: nowrap;
}
#contentplace .nowrap {
white-space: nowrap;
}
/* format links and images in post div's */
#contentplace .postlink label {
font-weight: bold;
cursor: pointer;
color: #006600;
text-decoration: none;
}
#contentplace .postlink label:hover {
color: #D60808;
text-decoration: underline;
}
#contentplace .postlink label:after {
content: "\bb";
font-weight: bold;
}
#contentplace .postlink .hidden {
position: absolute;
top: -90000px;
left: -90000px;
}
/*
* use smaller font for input elements (textfields)
* and select boxes
*/
#container #contentplace input {
font-size: 11px;
padding: 2px 2px 2px 3px;
border: 1px solid #606060;
}
/* sigh, no border for checkboxes */
#contentplace .checkbox {
border: none !important;
}
#contentplace .table input {
margin: -2px 0 -2px 0;
}
#contentplace select {
font-size: 11px;
border: 1px solid #606060;
}
/* use smaller font for buttons */
#contentplace .but {
font-size: 11px;
text-align: center;
border: 1px solid #606060;
background-color: #e0e0e0;
padding: 2px !important;
margin: 3px 0 5px 0;
}
/* print ">>" after hyper refs */
#contentplace a:after {
content: "\bb";
font-weight: bold;
}
/* no ">>" in heading */
#contentplace h3 a:after {
content: "";
}
/* specify position of edit/lock/delete action links */
#contentplace .actions {
margin: 0 0 -17px 23px;
position: relative;
}
#contentplace .aaction {
margin: 0 8px 0 0;
font-weight: bold;
}
/* colors for disabled input fields */
#contentplace .disabled {
color: #808080;
background-color: #f4f4f4;
}
#contentplace .txtpreference {
width: 160px;
}
/* customize login form */
#login {
border: none;
margin: 0 0 0 10px;
}
#login label {
font-weight: bold;
}
#login_user, #login_pass {
font-size: 11px;
margin: 3px 0 5px 0;
padding: 3px;
width: 120px;
}
/* decrease input text field for password */
#edit_pass, #edit_webpass {
width: 95px;
}
/* decrease input text fields for cc/ac */
.ishort {
width: 40px;
}
/* increase spacing between password and show/hide href */
#contentplace .apass {
vertical-align: middle;
margin: 0 0 0 5px;
}
/* increase spacing between amount and add textfield in
* account balance edit
*/
#contentplace .account_add {
margin: 0 0 0 15px;
}
/* decrease input text field for amounts */
#contentplace .account_add input {
width: 50px;
}
/* floating menu for subscriber lock */
#contentplace .floating {
z-index: 100;
display: none;
position: absolute;
top: 20px;
left: 50px;
border: 1px solid #006600;
background-color: #DDFFDD;
padding: 3px 10px 3px 5px;
}
#contentplace .menu_foo {
z-index: 200;
overflow: auto;
margin: 0;
width: 200px;
height: 200px;
}
/* correct margin for active paginations */
.pagination li.currentpage, .pagination li.disablepage {
margin: 0 5px 0 0;
}
/* no ">>" in pagination next / previous */
#contentplace .pagination li.nextpage a:after {
content: "";
}

@ -0,0 +1,120 @@
<attach event="ondocumentready" handler="parseStylesheets" />
<script>
/**
* Whatever:hover - V1.42.060206 - hover & active
* ------------------------------------------------------------
* (c) 2005 - Peter Nederlof
* Peterned - http://www.xs4all.nl/~peterned/
* License - http://creativecommons.org/licenses/LGPL/2.1/
*
* Whatever:hover is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* Whatever:hover is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Credits and thanks to:
* Arnoud Berendsen, Martin Reurings, Robert Hanson
*
* howto: body { behavior:url("csshover.htc"); }
* ------------------------------------------------------------
*/
var csshoverReg = /(^|\s)(([^a]([^ ]+)?)|(a([^#.][^ ]+)+)):(hover|active)/i,
currentSheet, doc = window.document, hoverEvents = [], activators = {
onhover:{on:'onmouseover', off:'onmouseout'},
onactive:{on:'onmousedown', off:'onmouseup'}
}
function parseStylesheets() {
if(!/MSIE (5|6)/.test(navigator.userAgent)) return;
window.attachEvent('onunload', unhookHoverEvents);
var sheets = doc.styleSheets, l = sheets.length;
for(var i=0; i<l; i++)
parseStylesheet(sheets[i]);
}
function parseStylesheet(sheet) {
if(sheet.imports) {
try {
var imports = sheet.imports, l = imports.length;
for(var i=0; i<l; i++) parseStylesheet(sheet.imports[i]);
} catch(securityException){}
}
try {
var rules = (currentSheet = sheet).rules, l = rules.length;
for(var j=0; j<l; j++) parseCSSRule(rules[j]);
} catch(securityException){}
}
function parseCSSRule(rule) {
var select = rule.selectorText, style = rule.style.cssText;
if(!csshoverReg.test(select) || !style) return;
var pseudo = select.replace(/[^:]+:([a-z-]+).*/i, 'on$1');
var newSelect = select.replace(/(\.([a-z0-9_-]+):[a-z]+)|(:[a-z]+)/gi, '.$2' + pseudo);
var className = (/\.([a-z0-9_-]*on(hover|active))/i).exec(newSelect)[1];
var affected = select.replace(/:(hover|active).*$/, '');
var elements = getElementsBySelect(affected);
if(elements.length == 0) return;
currentSheet.addRule(newSelect, style);
for(var i=0; i<elements.length; i++)
new HoverElement(elements[i], className, activators[pseudo]);
}
function HoverElement(node, className, events) {
if(!node.hovers) node.hovers = {};
if(node.hovers[className]) return;
node.hovers[className] = true;
hookHoverEvent(node, events.on, function() { node.className += ' ' + className; });
hookHoverEvent(node, events.off, function() { node.className = node.className.replace(new RegExp('\\s+'+className, 'g'),''); });
}
function hookHoverEvent(node, type, handler) {
node.attachEvent(type, handler);
hoverEvents[hoverEvents.length] = {
node:node, type:type, handler:handler
};
}
function unhookHoverEvents() {
for(var e,i=0; i<hoverEvents.length; i++) {
e = hoverEvents[i];
e.node.detachEvent(e.type, e.handler);
}
}
function getElementsBySelect(rule) {
var parts, nodes = [doc];
parts = rule.split(' ');
for(var i=0; i<parts.length; i++) {
nodes = getSelectedNodes(parts[i], nodes);
} return nodes;
}
function getSelectedNodes(select, elements) {
var result, node, nodes = [];
var identify = (/\#([a-z0-9_-]+)/i).exec(select);
if(identify) {
var element = doc.getElementById(identify[1]);
return element? [element]:nodes;
}
var classname = (/\.([a-z0-9_-]+)/i).exec(select);
var tagName = select.replace(/(\.|\#|\:)[a-z0-9_-]+/i, '');
var classReg = classname? new RegExp('\\b' + classname[1] + '\\b'):false;
for(var i=0; i<elements.length; i++) {
result = tagName? elements[i].all.tags(tagName):elements[i].all;
for(var j=0; j<result.length; j++) {
node = result[j];
if(classReg && !classReg.test(node.className)) continue;
nodes[nodes.length] = node;
}
}
return nodes;
}
</script>

@ -0,0 +1,552 @@
body
{
margin: 0;
padding: 0;
color: #333;
font: 0.8em Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif;
background: #e0e0e0;
}
a
{
color: #006600;
text-decoration: none;
}
a:hover
{
color: #D60808;
text-decoration: underline;
}
img
{
border: none;
}
#container
{
margin: 20px auto;
width: 900px;
position: relative;
}
* html #container
{
width: 900px;
w\idth: 900px;
height: auto;
}
#header
{
background: #fff;
border: 2px solid #000;
}
#headerlogin
{
position: absolute;
top: 10px;
right: 20px;
margin: 0;
}
#headermenu
{
}
#headermenu ul
{
text-align: center;
margin-left: 0;
padding-left: 0;
}
#headermenu li
{
list-style-type: none;
font-size: normal;
font-weight: bold;
padding: 0.25em 1em;
display: inline;
border-left: 1px solid #e0e0e0;
}
#headermenu li:first-child
{
border-left: none;
}
#headerlogo img
{
display: block;
margin-left: auto;
margin-right: auto;
}
#content
{
background: #fff;
min-height: 400px;
height: auto !important;
height: 400px; /* IE hack */
border: 2px solid #000;
margin-top: 10px;
padding: 10px;
}
#contentmenu h3
{
font-weight: bold;
font-size: normal;
margin: 20px 20px 5px 10px;
}
#contentmenu h3#first
{
font-weight: bold;
font-size: normal;
margin: 0px 20px 5px 10px;
}
#contentmenu
{
float: left;
padding-right: 20px;
padding-top: 20px;
}
#contentmenu ul
{
width: 150px;
list-style-type: none;
margin: 0;
padding: 0 0 0 20px;
}
#contentmenu ul li
{
font-size: normal;
font-weight: normal;
border-bottom: 1px solid #e0e0e0;
display: block;
padding: 5px 0;
}
#contentmenu ul li a
{
font-size: normal;
font-weight: bold;
}
#contentplace
{
margin-left: 220px;
}
#contentplace .expert
{
position: relative;
float: right;
margin-top: 10px;
margin-right: 30px;
}
#contentplace h3
{
font-weight: bold;
font-size: large;
margin-left: 20px;
}
#contentplace h3 img
{
margin: 10px;
margin-bottom: 0px;
padding: 0px;
}
#contentplace p
{
border: 0;
margin: 10px;
padding: 0;
}
#contentplace .p1
{
border: 1px solid #e0e0e0;
margin: 20px 20px 20px 20px;
padding: 10px 10px 10px 10px;
}
#contentplace .p1full
{
border: 1px solid #e0e0e0;
margin: 20px 20px 20px 20px;
padding: 10px 10px 20px 10px;
}
#contentplace .p1center
{
margin-left: auto;
margin-right: auto;
padding: 10px 10px 10px 10px;
}
#contentplace .p2center
{
margin-left: auto;
margin-right: auto;
padding: 10px 10px 10px 10px;
}
#contentplace .p2right
{
float: right;
padding: 10px 10px 10px 10px;
}
#contentplace .p3
{
border: 0;
margin: 30px;
padding: 10px;
position: relative;
}
#contentplace .p3image
{
position: relative;
margin-bottom: 20px;
text-align: center;
}
#contentplace .p3 .p3text
{
margin: 0;
font-weight: bold;
}
#contentplace form input
{
border: 1px solid #606060;
background-color: #fff;
vertical-align: middle;
padding: 3px;
}
#contentplace form .nb
{
border: none;
padding: 3px;
}
#contentplace form .but
{
border: 1px solid #606060;
background-color: #e0e0e0;
padding: 4px;
margin-top: 8px;
margin-bottom: 8px;
}
#contentplace table
{
padding: 0;
margin: 0;
}
#contentplace .p1center table
{
margin-left: auto;
margin-right: auto;
border: 1px solid #e0e0e0;
padding: 10px;
}
#contentplace .p1center .tdcenter
{
margin-left: auto;
margin-right: auto;
text-align: center;
}
#contentplace .p2center table
{
margin-left: auto;
margin-right: auto;
padding: 10px;
}
#contentplace .p2center td
{
text-align: center;
border: 1px solid #e0e0e0;
padding: 20px;
width: 200px;
}
#contentplace .p2center .tdcenter
{
margin-left: auto;
margin-right: auto;
text-align: center;
}
#contentplace tr
{
text-align: left;
}
#contentplace td
{
padding: 3px;
margin: 0;
border: 0;
text-align: left;
}
#contentplace tr .desc
{
background-color: #e0e0e0;
padding: 0;
margin: 0;
}
#contentplace td .desc
{
background-color: #e0e0e0;
}
#contentplace .template table
{
margin-left: auto;
margin-right: auto;
width: 100%;
}
#contentplace .template tr
{
vertical-align: top;
}
#contentplace .template td
{
padding-left: 15px;
padding-right: 15px;
padding-bottom: 20px;
width: 50%;
border: 1px solid #e0e0e0;
}
#contentplace .template ul
{
list-style-type: circle;
}
#footer
{
margin: 0;
padding: 20px;
text-align: center;
font-size: small;
font-weight: normal;
color: #606060;
}
#contentplace .p1 .listing td
{
padding: 0px 5px 0px 5px;
}
#contentplace .p1 .listing td.center
{
padding: 0px 5px 0px 5px;
text-align: center
}
#contentplace .p1 .listing th
{
padding: 5px;
text-align: center;
}
.p2
{
}
.p2int
{
padding-left: 20px;
}
.error
{
color: #f00;
padding-left: 20px;
}
.errorExplanation
{
color: #f00;
border: 1px solid #f00;
/*margin: 10px;*/
margin: 20px 20px 20px 20px;
padding: 10px;
}
.errorExplanation h2
{
font-weight: bold;
font-size: large;
}
.errorExplanation h2.center
{
font-weight: bold;
font-size: large;
text-align: center;
}
#notice
{
color: #060;
border: 1px solid #060;
margin: 20px 20px 20px 20px;
padding: 10px;
text-align: center;
font-weight: bold;
font-size: normal;
}
#warning
{
color: #f90;
border: 1px solid #f90;
margin: 20px 20px 20px 20px;
padding: 10px;
text-align: center;
font-weight: bold;
font-size: normal;
}
#warning a
{
color: #D60808;
}
#confcode
{
font: 10px Courier;
color: #000
}
#username
{
font-weight: bold;
}
.fcattable
{
/*border: 1px solid #060;*/
width: 100%;
}
.fcatheader
{
background-color: #060;
color: #fff;
}
.price
{
color: #f00;
}
.price_free
{
color: #0c0;
}
.logo-1
{
color: #000000;
}
.logo-2
{
color: #006600;
}
.pagination
{
padding: 2px;
margin-top: 10px;
}
.pagination ul
{
margin: 0;
padding: 0;
text-align: center;
}
.pagination li
{
list-style-type: none;
display: inline;
padding-bottom: 1px;
}
.pagination a, .pagination a:visited
{
padding: 0 5px;
border: 1px solid #060;
text-decoration: none;
color: #060;
}
.pagination a:hover, .pagination a:active
{
border: 1px solid #060;
color: #000;
background-color: lightyellow;
}
.pagination li.currentpage
{
font-weight: bold;
padding: 0 5px;
border: 1px solid #060;
background-color: #060;
color: #fff;
}
.pagination li.disablepage
{
padding: 0 5px;
border: 1px solid #929292;
color: #929292;
}
.pagination li.nextpage
{
font-weight: bold;
}
* html .pagination li.currentpage, * html .pagination li.disablepage
{
margin-right: 5px;
padding-right: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,10 @@
oc = "open";
function openclose() {
if (oc == "open") {
document.getElementById('lock_menu').style.display = 'inline';
oc = "close";
} else {
document.getElementById('lock_menu').style.display = 'none';
oc = "open";
}
}

@ -0,0 +1,21 @@
<div id="container">
[% PROCESS layout/header %]
<div id="content">
[% PROCESS layout/menu %]
<div id="contentplace">
[% IF prov_error %]<div class="errormsg">[% prov_error %]</div>[% END %]
[% IF messages.topmsg %]<div class="goodmsg">[% messages.topmsg %]</div>[% END %]
[% IF messages.toperr %]<div class="errormsg">[% messages.toperr %]</div>[% END %]
[% content %]
</div>
</div>
[% PROCESS layout/footer %]
</div>

@ -0,0 +1,6 @@
<!-- BEGIN layout/footer -->
<div id="footer">
Copyright 2007 Sipwise, Austria.<br />
<a href="http://sipwise.com/">http://sipwise.com</a>
</div>
<!-- END layout/footer -->

@ -0,0 +1,12 @@
<!-- BEGIN layout/header -->
<div id="header">
<div id="sipwise_logo">
<a href="http://sipwise.com/"><img src="/static/images/sipwise_logo_96.png" alt="Sipwise GmbH, Austria" /></a>
</div>
<div id="heading">Sipwise NGCP Administration Interface</div>
<div id="client_logo">
<a href="[% site_config.company.homepage %]"><img src="/static/images/client_logo.png" alt="" /></a>
</div>
</div>
<!-- END layout/header -->

@ -0,0 +1,28 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="[% site_config.language %]">
<head>
<title>[% template.title or site_config.title %]</title>
[% FOREACH css_file = site_config.css %]
<link rel="stylesheet" type="text/css" href="[% css_file %]" />
[% END %]
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta http-equiv="content-language" content="[% site_config.language %]" />
<meta name="description" content="[% template.title or site_config.title %]" />
<meta name="title" content="[% template.title or site_config.title %]" />
<meta name="Company" content="[% site_config.company.name %]" />
<meta name="keywords" content="" />
<meta name="Language" content="[% site_config.language_string %]" />
</head>
<body>
[% content %]
</body>
</html>

@ -0,0 +1,35 @@
<div id="contentmenu">
<h3 id="first">Authentication</h3>
<ul>
[% IF Catalyst.user_exists %]
<li>Logged in as <b>[% Catalyst.user.login %]</b></li>
<li><a href="/logout">&#187; Logout</a></li>
[% ELSE %]
<li>&nbsp;</li>
<li>Not logged in</li>
[% END %]
</ul>
[% IF Catalyst.session.admin %]
<h3>User Administration</h3>
<ul>
[% IF Catalyst.config.billing_features %]
<li><a href="/customer">Customers</a></li>
[% END %]
<li><a href="/account">Accounts</a></li>
<li><a href="/subscriber">Subscribers</a></li>
[%# <li><a href="/registration">Devices</a></li> %]
[%# <li><a href="/registration">Registrations</a></li> %]
</ul>
<h3>System Administration</h3>
<ul>
<li><a href="/domain">Domains</a></li>
<li><a href="/admin">Administrators</a></li>
[%# <li><a href="/gwgroup">Gateway Settings</a></li> %]
[%# <li><a href="/sipserver">SIP Server Settings</a></li> %]
</ul>
[% END %]
</div>

@ -0,0 +1,8 @@
[% IF template.name.match('(\.html$|\.css$|\.js$|\.txt$)');
debug("Passing page through as text: $template.name");
content;
ELSE;
debug("Applying HTML page layout wrappers to $template.name\n");
content WRAPPER layout/html + layout/body;
END;
-%]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -0,0 +1,52 @@
<h3>Get by ID</h3>
<div class="p1">
<form action="/account/getbyid" method="GET">
<input type="text" id="account_id" name="account_id" value="[% account_id %]" />
<input type="submit" value="Get &#187;" class="but" />
</form>
[% IF messages.accsearcherr %]<div class="errormsg">[% messages.accsearcherr %]</div>[% END %]
</div>
<h3>Search by subscriber</h3>
<div class="p1">
<form action="/subscriber/search" method="post">
<input type="text" id="search_subscriber" name="search_string" value="[% search_string %]" />
<input type="submit" value="Search &#187;" class="but" />
</form>
</div>
<h3>Create new account</h3>
<div class="p1">
<form action="/account/create_account" method="post">
<input type="submit" value="Create &#187;" class="but" />
</form>
</div>
[% IF subscriber_list %]
<h3>Subscribers</h3>
<div class="p1">
<table>
<tr><td>SIP URI</td><td class="tdcenter">Subscriber ID</td><td class="tdcenter">Account ID</td></tr>
[% FOREACH subscriber = subscriber_list %]
<tr>
<td><a href="/subscriber/detail?subscriber_id=[% subscriber.id %]">
[% subscriber.username %]@[% subscriber.domain %]
</a></td>
<td class="tdcenter"><a href="/subscriber/detail?subscriber_id=[% subscriber.id %]">
[% subscriber.id %]
</a></td>
<td class="tdcenter"><a href="/account/detail?account_id=[% subscriber.account_id %]">
[% subscriber.account_id %]
</a></td>
</tr>
[% END %]
</table>
</div>
[% END %]

@ -0,0 +1,167 @@
<h3> VoIP Account
<a class="noarrow" href="detail?account_id=[% account.id %]">
#[% account.id %]</a>
</h3>
[% IF ! account.terminate_timestamp %]
<div class="actions">
[% IF billing_features %]
<a href="detail?account_id=[% account.id %]&amp;edit_account=1" class="aaction">edit</a>
[% END %]
[% IF edit_account %]
<a href="detail?account_id=[% account.id %]" class="aaction">cancel</a>
[% ELSE %]
[% IF account.is_locked %]
<a href="lock?account_id=[% account.id %]&amp;lock=none" class="aaction">unlock</a>
[% ELSE %]
<a href="lock?account_id=[% account.id %]&amp;lock=global" class="aaction">lock</a>
[% END %]
<a href="terminate?account_id=[% account.id %]" class="aaction">terminate</a>
[% END %]
</div>
[% END %]
<div class="p1">
[% IF account.is_locked %]
<div class="alert">Account is LOCKED!</div>
[% END %]
[% IF messages.accmsg %]<div class="goodmsg">[% messages.accmsg %]</div>[% END %]
[% IF messages.accerr %]<div class="errormsg">[% messages.accerr %]</div>[% END %]
<form action="update_account" method="post">
<input type="hidden" name="account_id" value="[% account.id %]" />
<table>
<tr><td class="tdkey">ID:</td><td>[% account.id %]</td></tr>
[% IF billing_features %]
<tr>
<td class="tdkey">customer:</td>
<td>
[% IF account.customer_id %]
<a href="/customer/detail?customer_id=[% account.customer_id %]">[% account.customer_id %]</a>
[% END %]
</td>
</tr>
<tr>
<td class="tdkey">product:</td>
<td>
[% IF edit_account %]
<select size="1" name="product">
[% FOREACH product = products %]
<option[% IF product.name == account.product %] selected="selected"[% END %]>[% product.name %]</option>
[% END %]
</select>
[% ELSE %]
[% account.product %]
[% END %]
</td>
</tr>
<tr>
<td class="tdkey">billing profile:</td>
<td>
[% IF edit_account %]
<select size="1" name="billing_profile">
[% FOREACH profile = billing_profiles %]
<option[% IF profile.name == account.billing_profile %] selected="selected"[% END %]>[% profile.name %]</option>
[% END %]
</select>
[% ELSE %]
[% account.billing_profile %]
[% END %]
</td>
</tr>
[% END %]
<tr><td class="tdkey">created:</td><td>[% account.create_timestamp %]</td></tr>
<tr><td class="tdkey">modified:</td><td>[% account.modify_timestamp %]</td></tr>
[% IF account.terminate_timestamp %]
<tr><td class="tdkey">terminated:</td><td><div class="alert">[% account.terminate_timestamp %]</div></td></tr>
[% END %]
</table>
[% IF edit_account %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
[% IF billing_features %]
<h3>Account Balance</h3>
[% IF ! account.terminate_timestamp %]
<div class="actions">
<a href="detail?account_id=[% account.id %]&amp;edit_balance=1" class="aaction">edit</a>
[% IF edit_balance %]
<a href="detail?account_id=[% account.id %]" class="aaction">cancel</a>
[% END %]
</div>
[% END %]
<div class="p1">
[% IF messages.balmsg %]<div class="goodmsg">[% messages.balmsg %]</div>[% END %]
[% IF messages.balerr %]<div class="errormsg">[% messages.balerr %]</div>[% END %]
Current totals
<form action="update_balance" method="post">
<input type="hidden" name="account_id" value="[% account.id %]" />
<table>
<tr>
<td class="tdkey">cash:</td>
<td class="nowrap">&euro; [% account.balance.cash_balance %]</td>
<td>
[% IF edit_balance %]
<div class="account_add">add <input type="text" name="add_cash" value="[% balanceadd.cash %]" /> Euro</div>
[% END %]
</td>
</tr>
[% IF messages.addcash %]<tr><td /><td /><td><div class="errormsg">[% messages.addcash %]</div></td></tr>[% END %]
<tr>
<td class="tdkey">free time:</td>
<td class="nowrap">[% account.balance.free_time_balance %] s</td>
<td>
[% IF edit_balance %]
<div class="account_add">add <input type="text" name="add_time" value="[% balanceadd.free_time %]" /> seconds</div>
[% END %]
</td>
</tr>
[% IF messages.addtime %]<tr><td /><td /><td><div class="errormsg">[% messages.addtime %]</div></td></tr>[% END %]
</table>
[% IF edit_balance %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<div class="p1">
Spent this billing interval
<table>
<tr><td class="tdkey">cash:</td><td>&euro; [% account.balance.cash_balance_interval %]</td></tr>
<tr><td class="tdkey">free time:</td><td>[% account.balance.free_time_balance_interval %] s</td></tr>
</table>
</div>
[% END %]
<h3>Subscribers</h3>
<div class="actions">
<a href="/subscriber/detail?account_id=[% account.id %]&amp;new=1" class="aaction">create new</a>
</div>
<div class="p1">
[% IF account.subscribers %]
<table>
[% FOREACH subscriber = account.subscribers %]
<tr>
<td>
[% IF subscriber.subscriber_id %]
<a href="/subscriber/detail?subscriber_id=[% subscriber.subscriber_id %]">
[% subscriber.username %]@[% subscriber.domain %]</a>
[% ELSE %]
[% subscriber.username %]@[% subscriber.domain %]
[% END %]
</td>
<td>[% IF subscriber.sn %]+[% subscriber.cc %] [% subscriber.ac %] [% subscriber.sn %][% END %]</td>
<td>[% subscriber.uuid %]</td>
</tr>
[% END %]
</table>
[% ELSE %]
<div>
This account does not have any subscribers yet.
</div>
[% END %]
</div>

@ -0,0 +1,119 @@
<h3>Manage Administrator Accounts</h3>
<div class="p1">
[% IF messages.eadmmsg %]<div class="goodmsg">[% messages.eadmmsg %]</div>[% END %]
[% IF messages.eadmerr %]<div class="errormsg">[% messages.eadmerr %]</div>[% END %]
<table>
<tr class="table_header">
<td>login</td>
<td>password</td>
<td>master</td>
<td>active</td>
<td />
<td />
</tr>
[% id = 0 %]
[% FOREACH admin = admins %]
<tr>
<td>[% admin.login %]</td>
[% IF admin.login == edit_admin %]
<form action="/admin/do_edit_admin" method="post">
<input type="hidden" name="admin" value="[% admin.login %]" />
<td><input type="password" name="password" value="" /></td>
<td class="tdcenter">
<input type="checkbox" class="checkbox" name="is_master"
[% IF admin.login == Catalyst.session.admin.login %]disabled="disabled"[% END %]
[% IF erefill.is_master or !erefill && admin.is_master %]checked="checked" [% END %] />
</td>
<td class="tdcenter">
<input type="checkbox" class="checkbox" name="is_active"
[% IF admin.login == Catalyst.session.admin.login %]disabled="disabled"[% END %]
[% IF erefill.is_active or !erefill && admin.is_active %]checked="checked" [% END %] />
</td>
<td>
<div class="postlink">
<label for="admsave[% id %]">save</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="admsave[% id %]" />
</div>
</td>
</form>
<td><a href="/admin" class="aaction">cancel</a></td>
[% ELSE %]
<td>********</td>
<td class="tdcenter"><input type="checkbox" class="checkbox" disabled="disabled"[% IF admin.is_master %] checked="checked"[% END %] /></td>
<td class="tdcenter"><input type="checkbox" class="checkbox" disabled="disabled"[% IF admin.is_active %] checked="checked"[% END %] /></td>
<td><a href="/admin?edit_admin=[% admin.login %]" class="aaction">edit</a></td>
[% IF admin.login != Catalyst.session.admin.login %]
<form action="/admin/do_delete_admin" method="post">
<td>
<input type="hidden" name="admin" value="[% admin.login %]" />
<div class="postlink">
<label for="admdel[% id %]">delete</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="admdel[% id %]" />
</div>
</td>
</form>
[% ELSE %]
<td />
[% END %]
[% END %]
</tr>
[% IF admin.login == edit_admin %]
[% IF messages.epass %]
<tr><td colspan="5">
<div class="errormsg">
[% messages.epass %]
</div>
</td></tr>
[% END %]
[% END %]
[% id = id + 1 %]
[% END %]
</table>
</div>
[% IF Catalyst.session.admin.is_master || Catalyst.session.admin.is_superuser %]
<h3>Create Administrator Account</h3>
<div class="p1">
[% IF messages.cadmmsg %]<div class="goodmsg">[% messages.cadmmsg %]</div>[% END %]
[% IF messages.cadmerr %]<div class="errormsg">[% messages.cadmerr %]</div>[% END %]
<table>
<tr class="table_header">
<td>login</td>
<td>password</td>
<td>master</td>
<td>active</td>
<td />
<td />
</tr>
<form action="/admin/do_create_admin" method="post">
<tr>
<td><input type="text" size="20" name="admin" id="adminaddtxt" value="[% arefill.admin %]" /></td>
<td><input type="password" name="password" value="" /></td>
<td class="tdcenter"><input type="checkbox" class="checkbox" name="is_master"[% IF arefill.is_master %] checked="checked"[% END %] /></td>
<td class="tdcenter"><input type="checkbox" class="checkbox" name="is_active"[% IF arefill.is_active %] checked="checked"[% END %] /></td>
<td>
<div class="postlink">
<label for="adminadd">Add</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="adminadd" />
</div>
</td>
<td />
</tr>
[% IF messages.alogin || messages.apass %]
<tr><td colspan="5">
<div class="errormsg">
[% messages.alogin %]
[% IF messages.alogin && messages.apass %]<br />[% END %]
[% messages.apass %]
</div>
</td></tr>
[% END %]
</form>
</table>
</div>
[% END %]

@ -0,0 +1,58 @@
<h3>Get by ID</h3>
<div class="p1">
<form action="/customer/getbyid" method="GET">
<input type="text" id="customer_id" name="customer_id" value="[% customer_id %]" />
<input type="submit" value="Get &#187;" class="but" />
</form>
[% IF messages.accgeterr %]<div class="errormsg">[% messages.accgeterr %]</div>[% END %]
</div>
<h3>Search by contacts</h3>
<div class="p1">
<form action="/customer/search" method="post">
<input type="text" id="search_string" name="search_string" value="[% search_string %]" />
<input type="submit" value="Search &#187;" class="but" />
</form>
[% IF messages.accsearcherr %]<div class="errormsg">[% messages.accsearcherr %]</div>[% END %]
</div>
<!--
<h3>Create new customer</h3>
<div class="p1">
<form action="/customer/create_customer" method="post">
<input type="submit" value="Create &#187;" class="but" />
</form>
</div>
-->
[% IF customer_list %]
<h3>Customers</h3>
<div class="p1">
<table>
<tr class="table_header"><td>CustomerID</td><td>Name</td><td>Firma</td></tr>
[% FOREACH customer = customer_list %]
<tr>
<td><a href="/customer/detail?customer_id=[% customer.id %]">
[% customer.id %]
</a></td>
<td><a href="/customer/detail?customer_id=[% customer.id %]">
[% customer.contact.firstname %] [% customer.contact.lastname %]
</a></td>
[% IF customer.contact.company %]
<td><a href="/customer/detail?customer_id=[% customer.id %]">
[% customer.contact.company %]
</a></td>
[% ELSE %]
<td />
[% END %]
</tr>
[% END %]
</table>
</div>
[% END %]

@ -0,0 +1,378 @@
<h3> Customer
<a class="noarrow" href="detail?customer_id=[% customer.id %]">
#[% customer.id %]</a>
</h3>
<div class="actions">
<a href="detail?customer_id=[% customer.id %]&amp;edit_customer=1" class="aaction">edit</a>
[% IF edit_customer %]
<a href="detail?customer_id=[% customer.id %]" class="aaction">cancel</a>
[% END %]
</div>
<div class="p1">
[% IF messages.custmsg %]<div class="goodmsg">[% messages.custmsg %]</div>[% END %]
[% IF messages.custerr %]<div class="errormsg">[% messages.custerr %]</div>[% END %]
<form action="update_customer" method="post">
<input type="hidden" name="customer_id" value="[% customer.id %]" />
<table>
<tr><td class="tdkey">ID:</td><td>[% customer.id %]</td></tr>
<tr>
<td class="tdkey">shopuser:</td>
<td>
<input type="text" id="shopuser" [% IF ! edit_customer %]class="disabled" disabled="disabled"[% END %]
name="shopuser" value="[% customer.shopuser %]" />
</td>
</tr>
<tr>
<td class="tdkey">shoppass:</td>
<td>
[% IF edit_customer %]
<input type="text" id="shoppass" name="shoppass" value="[% customer.edit_shoppass %]" />
[% ELSE %]
<input type="text" id="shoppass" name="shoppass" class="disabled" disabled="disabled"
[% IF show_pass %]
value="[% customer.shoppass %]" /> <a href="?customer_id=[% customer.id %]" class="apass">Hide</a>
[% ELSE %]
[% IF customer.shoppass %]
value="********" /> <a href="?customer_id=[% customer.id %]&amp;show_pass=1" class="apass">Show</a>
[% ELSE %]
value="" />
[% END %]
[% END %]
[% END %]
</td>
</tr>
<tr><td class="tdkey">created:</td><td>[% customer.create_timestamp %]</td></tr>
<tr><td class="tdkey">modified:</td><td>[% customer.modify_timestamp %]</td></tr>
</table>
[% IF edit_customer %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<h3 id="contact">Contact</h3>
<div class="actions">
<a href="detail?customer_id=[% customer.id %]&amp;edit_contact=1#contact" class="aaction">edit</a>
[% IF edit_contact %]
<a href="detail?customer_id=[% customer.id %]#contact" class="aaction">cancel</a>
[% END %]
</div>
<div class="p1">
[% IF messages.contmsg %]<div class="goodmsg">[% messages.contmsg %]</div>[% END %]
[% IF messages.conterr %]<div class="errormsg">[% messages.conterr %]</div>[% END %]
<form action="update_contact#contact" method="post">
<input type="hidden" name="ctype" value="contact" />
<input type="hidden" name="customer_id" value="[% customer.id %]" />
<table>
<tr>
<td class="tdkey">name:</td>
<td>
<select [% IF ! edit_contact %]class="disabled" disabled="disabled" [% END %] name="gender">
<option value="male"[% IF customer.contact.gender == "male" %] selected="selected"[% END %]>Mr.</option>
<option value="female"[% IF customer.contact.gender == "female" %] selected="selected"[% END %]>Ms./Mrs.</option>
</select>
<input type="text" id="cont_firstname" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="firstname" value="[% customer.contact.firstname %]" />
<input type="text" id="cont_lastname" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="lastname" value="[% customer.contact.lastname %]" />
</td>
</tr>
<tr>
<td class="tdkey">comm. register num.:</td>
<td>
<input type="text" id="cont_comregnum" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="comregnum" value="[% customer.contact.comregnum %]" />
</td>
</tr>
<tr>
<td class="tdkey">company:</td>
<td>
<input type="text" id="cont_company" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="company" value="[% customer.contact.company %]" />
</td>
</tr>
<tr>
<td class="tdkey">street:</td>
<td>
<input type="text" id="cont_street" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="street" value="[% customer.contact.street %]" />
</td>
</tr>
<tr>
<td class="tdkey">postcode / city:</td>
<td>
<input type="text" id="cont_postcode" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="postcode" value="[% customer.contact.postcode %]" />
<input type="text" id="cont_city" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="city" value="[% customer.contact.city %]" />
</td>
</tr>
<tr>
<td class="tdkey">phonenumber:</td>
<td>
<input type="text" id="cont_phonenumber" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="phonenumber" value="[% customer.contact.phonenumber %]" />
</td>
</tr>
<tr>
<td class="tdkey">mobilenumber:</td>
<td>
<input type="text" id="cont_mobilenumber" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="mobilenumber" value="[% customer.contact.mobilenumber %]" />
</td>
</tr>
<tr>
<td class="tdkey">email:</td>
<td>
<input type="text" id="cont_email" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="email" value="[% customer.contact.email %]" />
</td>
</tr>
<tr>
<td class="tdkey">newsletter:</td>
<td>
<input type="checkbox" id="cont_newsletter" [% IF ! edit_contact %]class="disabled" disabled="disabled"[% END %]
name="newsletter" [% IF customer.contact.newsletter %]checked="checked"[% END %] />
</td>
</tr>
<tr>
<td class="tdkey">created:</td>
<td> [% customer.contact.create_timestamp %]</td>
</tr>
<tr>
<td class="tdkey">modified:</td>
<td> [% customer.contact.modify_timestamp %]</td>
</tr>
</table>
[% IF edit_contact %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<h3 id="commercial">Commercial Contact</h3>
<div class="actions">
<a href="detail?customer_id=[% customer.id %]&amp;edit_commercial=1#commercial" class="aaction">edit</a>
[% IF edit_commercial %]
<a href="detail?customer_id=[% customer.id %]#commercial" class="aaction">cancel</a>
[% END %]
</div>
<div class="p1">
[% IF messages.commmsg %]<div class="goodmsg">[% messages.commmsg %]</div>[% END %]
[% IF messages.commerr %]<div class="errormsg">[% messages.commerr %]</div>[% END %]
<form action="update_contact#commercial" method="post">
<input type="hidden" name="ctype" value="comm_contact" />
<input type="hidden" name="customer_id" value="[% customer.id %]" />
<table>
<tr>
<td class="tdkey">name:</td>
<td>
<select [% IF ! edit_commercial %]class="disabled" disabled="disabled" [% END %] name="gender">
<option value="male"[% IF customer.comm_contact.gender == "male" %] selected="selected"[% END %]>Mr.</option>
<option value="female"[% IF customer.comm_contact.gender == "female" %] selected="selected"[% END %]>Ms./Mrs.</option>
</select>
<input type="text" id="comm_firstname" [% IF ! edit_commercial %]class="disabled" disabled="disabled"[% END %]
name="firstname" value="[% customer.comm_contact.firstname %]" />
<input type="text" id="comm_lastname" [% IF ! edit_commercial %]class="disabled" disabled="disabled"[% END %]
name="lastname" value="[% customer.comm_contact.lastname %]" />
</td>
</tr>
<tr>
<td class="tdkey">phonenumber:</td>
<td>
<input type="text" id="comm_phonenumber" [% IF ! edit_commercial %]class="disabled" disabled="disabled"[% END %]
name="phonenumber" value="[% customer.comm_contact.phonenumber %]" />
</td>
</tr>
<tr>
<td class="tdkey">mobilenumber:</td>
<td>
<input type="text" id="comm_mobilenumber" [% IF ! edit_commercial %]class="disabled" disabled="disabled"[% END %]
name="mobilenumber" value="[% customer.comm_contact.mobilenumber %]" />
</td>
</tr>
<tr>
<td class="tdkey">email:</td>
<td>
<input type="text" id="comm_email" [% IF ! edit_commercial %]class="disabled" disabled="disabled"[% END %]
name="email" value="[% customer.comm_contact.email %]" />
</td>
</tr>
<tr>
<td class="tdkey">newsletter:</td>
<td>
<input type="checkbox" id="comm_newsletter" [% IF ! edit_commercial %]class="disabled" disabled="disabled"[% END %]
name="newsletter" [% IF customer.comm_contact.newsletter %]checked="checked"[% END %] />
</td>
</tr>
<tr>
<td class="tdkey">created:</td>
<td> [% customer.comm_contact.create_timestamp %]</td>
</tr>
<tr>
<td class="tdkey">modified:</td>
<td> [% customer.comm_contact.modify_timestamp %]</td>
</tr>
</table>
[% IF edit_commercial %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<h3 id="technical">Technical Contact</h3>
<div class="actions">
<a href="detail?customer_id=[% customer.id %]&amp;edit_technical=1#technical" class="aaction">edit</a>
[% IF edit_technical %]
<a href="detail?customer_id=[% customer.id %]#technical" class="aaction">cancel</a>
[% END %]
</div>
<div class="p1">
[% IF messages.techmsg %]<div class="goodmsg">[% messages.techmsg %]</div>[% END %]
[% IF messages.techerr %]<div class="errormsg">[% messages.techerr %]</div>[% END %]
<form action="update_contact#technical" method="post">
<input type="hidden" name="ctype" value="tech_contact" />
<input type="hidden" name="customer_id" value="[% customer.id %]" />
<table>
<tr>
<td class="tdkey">name:</td>
<td>
<select [% IF ! edit_technical %]class="disabled" disabled="disabled" [% END %] name="gender">
<option value="male"[% IF customer.tech_contact.gender == "male" %] selected="selected"[% END %]>Mr.</option>
<option value="female"[% IF customer.tech_contact.gender == "female" %] selected="selected"[% END %]>Ms./Mrs.</option>
</select>
<input type="text" id="tech_firstname" [% IF ! edit_technical %]class="disabled" disabled="disabled"[% END %]
name="firstname" value="[% customer.tech_contact.firstname %]" />
<input type="text" id="tech_lastname" [% IF ! edit_technical %]class="disabled" disabled="disabled"[% END %]
name="lastname" value="[% customer.tech_contact.lastname %]" />
</td>
</tr>
<tr>
<td class="tdkey">phonenumber:</td>
<td>
<input type="text" id="tech_phonenumber" [% IF ! edit_technical %]class="disabled" disabled="disabled"[% END %]
name="phonenumber" value="[% customer.tech_contact.phonenumber %]" />
</td>
</tr>
<tr>
<td class="tdkey">mobilenumber:</td>
<td>
<input type="text" id="tech_mobilenumber" [% IF ! edit_technical %]class="disabled" disabled="disabled"[% END %]
name="mobilenumber" value="[% customer.tech_contact.mobilenumber %]" />
</td>
</tr>
<tr>
<td class="tdkey">email:</td>
<td>
<input type="text" id="tech_email" [% IF ! edit_technical %]class="disabled" disabled="disabled"[% END %]
name="email" value="[% customer.tech_contact.email %]" />
</td>
</tr>
<tr>
<td class="tdkey">newsletter:</td>
<td>
<input type="checkbox" id="tech_newsletter" [% IF ! edit_technical %]class="disabled" disabled="disabled"[% END %]
name="newsletter" [% IF customer.tech_contact.newsletter %]checked="checked"[% END %] />
</td>
</tr>
<tr>
<td class="tdkey">created:</td>
<td> [% customer.tech_contact.create_timestamp %]</td>
</tr>
<tr>
<td class="tdkey">modified:</td>
<td> [% customer.tech_contact.modify_timestamp %]</td>
</tr>
</table>
[% IF edit_technical %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<h3>Contracts</h3>
<!-- <div class="actions">
<a href="/subscriber/detail?customer_id=[% customer.id %]&amp;new=1" class="aaction">create new</a>
</div>
--> <div class="p1">
[% IF customer.contracts %]
<table>
<tr>
<td class="table_header">ContractID</td>
<td class="table_header">Product</td>
<td class="table_header">status</td>
<td class="table_header">created</td>
</tr>
[% FOREACH contract = customer.contracts %]
<tr>
<td>
[% IF contract.class == 'voip' %]
<a href="/account/detail?account_id=[% contract.id %]">
[% contract.id %]
</a>
[% ELSE %]
[% contract.id %]
[% END %]
</td>
<td>
[% IF contract.class == 'voip' %]
<a href="/account/detail?account_id=[% contract.id %]">
[% contract.product %]
</a>
[% ELSE %]
[% contract.product %]
[% END %]
</td>
<td>[% contract.status %]</td>
<td>[% contract.create_timestamp %]</td>
</tr>
[% END %]
</table>
[% ELSE %]
<div>
This customer does not have any contracts yet.
</div>
[% END %]
</div>
<h3>Orders</h3>
<!-- <div class="actions">
<a href="/subscriber/detail?customer_id=[% customer.id %]&amp;new=1" class="aaction">create new</a>
</div>
--> <div class="p1">
[% IF customer.orders %]
<table>
<tr>
<td class="table_header">OrderID</td>
<td class="table_header">status</td>
<td class="table_header">created</td>
<td class="table_header">modified</td>
<td class="table_header">completed</td>
</tr>
[% FOREACH order = customer.orders %]
<tr>
<td>[% order.id %]</td>
<td>[% order.status %]</td>
<td>[% order.create_timestamp %]</td>
<td>[% order.modify_timestamp %]</td>
<td>[% order.complete_timestamp %]</td>
</tr>
[% END %]
</table>
[% ELSE %]
<div>
This customer does not have any orders yet.
</div>
[% END %]
</div>

@ -0,0 +1,104 @@
<h3>Edit Domains</h3>
<div class="p1">
[% IF messages.edommsg %]<div class="goodmsg">[% messages.edommsg %]</div>[% END %]
[% IF messages.edomerr %]<div class="errormsg">[% messages.edomerr %]</div>[% END %]
<table>
<tr class="table_header">
<td>domain</td>
<td>country code</td>
<td>timezone</td>
<td />
<td />
</tr>
[% id = 0 %]
[% FOREACH domain = domains %]
<tr>
<td>[% domain.domain %]</td>
[% IF domain.domain == edit_domain %]
<form action="/domain/do_edit_domain" method="post">
<input type="hidden" name="domain" value="[% domain.domain %]" />
<td class="tdcenter"><input class="ishort" name="cc" value="[% domain.cc %]" /></td>
<td><input name="timezone" value="[% domain.timezone %]" /></td>
<td>
<div class="postlink">
<label for="domsave[% id %]">save</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="domsave[% id %]" />
</div>
</td>
</form>
<td><a href="/domain" class="aaction">cancel</a></td>
[% ELSE %]
<td class="tdcenter">[% domain.cc %]</td>
<td>[% domain.timezone %]</td>
<td><a href="/domain?edit_domain=[% domain.domain %]" class="aaction">edit</a></td>
<form action="/domain/do_delete_domain" method="post">
<input type="hidden" name="domain" value="[% domain.domain %]" />
<td>
<div class="postlink">
<label for="domdel[% id %]">delete</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="domdel[% id %]" />
</div>
</td>
</form>
[% END %]
</tr>
[% IF domain.domain == edit_domain %]
[% IF messages.ecc || messages.etimezone %]
<tr><td colspan="5">
<div class="errormsg">
[% messages.ecc %]
[% IF messages.ecc && messages.etimezone %]<br />[% END %]
[% messages.etimezone %]
</div>
</td></tr>
[% END %]
[% END %]
[% id = id + 1 %]
[% END %]
</table>
</div>
<h3>Create domain</h3>
<div class="p1">
[% IF messages.cdommsg %]<div class="goodmsg">[% messages.cdommsg %]</div>[% END %]
[% IF messages.cdomerr %]<div class="errormsg">[% messages.cdomerr %]</div>[% END %]
<table>
<tr class="table_header">
<td>domain</td>
<td>country code</td>
<td>timezone</td>
<td />
<td />
</tr>
<form action="/domain/do_create_domain" method="post">
<tr>
<td><input type="text" size="20" name="domain" id="domainaddtxt" value="[% arefill.domain %]" /></td>
<td class="tdcenter"><input class="ishort" name="cc" value="[% arefill.cc %]" /></td>
<td><input name="timezone" value="[% arefill.timezone %]" /></td>
<td>
<div class="postlink">
<label for="domainadd">Add</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="domainadd" />
</div>
</td>
<td />
</tr>
[% IF messages.acc || messages.atimezone %]
<tr><td colspan="5">
<div class="errormsg">
[% messages.acc %]
[% IF messages.acc && messages.atimezone %]<br />[% END %]
[% messages.atimezone %]
</div>
</td></tr>
[% END %]
</form>
</table>
</div>

@ -0,0 +1,10 @@
<h3>Login</h3>
<div class="p1">
<form id="login" action="/login" method="post">
<label for="login_user">Username</label><br />
<input type="text" id="login_user" name="username" /><br />
<label for="login_pass">Password</label><br />
<input type="password" id="login_pass" name="password" /><br />
<input type="submit" value="Login &#187;" class="but" />
</form>
</div>

@ -0,0 +1,83 @@
<h3>Search Subscribers</h3>
<div class="p1">
<form action="/subscriber/search" method="post">
<input type="text" id="search_subscriber" name="search_string" value="[% search_string %]" />
<input type="submit" value="Search &#187;" class="but" />
</form>
</div>
[% IF subscriber_list %]
<h3>Subscribers</h3>
<div class="p1">
<table>
<tr><td>SIP URI</td><td class="tdcenter">Account ID</td></tr>
[% FOREACH subscriber = subscriber_list %]
<tr>
<td>
[% IF subscriber.subscriber_id %]
<a href="/subscriber/detail?subscriber_id=[% subscriber.subscriber_id %]">
[% subscriber.username %]@[% subscriber.domain %]</a>
[% ELSE %]
[% subscriber.username %]@[% subscriber.domain %]
[% END %]
</td>
<td class="tdcenter">
<a href="/account/detail?account_id=[% subscriber.account_id %]">
[% subscriber.account_id %]</a>
</td>
</tr>
[% END %]
</table>
[% IF pagination %]
<div class="pagination">
<ul>
[% IF offset == 0 %]
<li class="disablepage">&#171; prev</li>
[% ELSE %]
<li class="nextpage">
<a href="/subscriber/search?search_string=[% search_string %]&amp;offset=[% offset - 1 %]">&#171; prev</a>
</li>
[% END %]
[% FOREACH pagine = pagination %]
[% IF pagine.offset == offset %]
<li class="currentpage">
[% pagine.offset + 1 %]
</li>
[% ELSIF pagine.offset == -1 %]
...
[% ELSE %]
<li>
<a href="/subscriber/search?search_string=[% search_string
%]&amp;offset=[% pagine.offset %]">[% pagine.offset + 1 %]</a>
</li>
[% END %]
[% END %]
[% IF offset >= max_offset %]
<li class="disablepage">
next &#187;
</li>
[% ELSE %]
<li class="nextpage">
<a href="/subscriber/search?search_string=[% search_string %]&amp;offset=[% offset + 1 %]">next &#187;</a>
</li>
[% END %]
</ul>
</div>
[% END %]
</div>
[% ELSIF searched %]
<div class="p1">
No matching subscribers found.
</div>
[% END %]

@ -0,0 +1,333 @@
[% IF subscriber.subscriber_id %]
<h3> Subscriber
<a class="noarrow" href="detail?subscriber_id=[% subscriber.subscriber_id %]">
[% subscriber.username %]@[% subscriber.domain %]</a>
</h3>
<div class="actions">
<a href="detail?subscriber_id=[% subscriber.subscriber_id %]&amp;edit_subscriber=1" class="aaction">edit</a>
[% ELSE %]
<h3>New subscriber</h3>
<div class="actions">
[% END %]
[% IF edit_subscriber %]
[% IF subscriber.subscriber_id %]
<a href="detail?subscriber_id=[% subscriber.subscriber_id %]" class="aaction">cancel</a>
[% ELSE %]
<a href="/account/detail?account_id=[% account_id %]" class="aaction">cancel</a>
[% END %]
[% ELSE %]
<script language="JavaScript" src="/js/openclose.js" type="text/javascript"></script>
<a href="javascript:;" class="aaction" onclick="javascript:openclose();" onkeypress="javascript:openclose();">lock</a>
<span id="lock_menu" class="floating" onmouseout="javascript:openclose();">
<a href="lock?subscriber_id=[% subscriber.subscriber_id %]&amp;lock=none" class="aaction">unlock</a><br />
<a href="lock?subscriber_id=[% subscriber.subscriber_id %]&amp;lock=foreign" class="aaction">foreign</a><br />
<a href="lock?subscriber_id=[% subscriber.subscriber_id %]&amp;lock=outgoing" class="aaction">all outgoing</a><br />
<a href="lock?subscriber_id=[% subscriber.subscriber_id %]&amp;lock=incoming" class="aaction">incoming and outgoing</a><br />
<a href="lock?subscriber_id=[% subscriber.subscriber_id %]&amp;lock=global" class="aaction">global</a><br />
</span>
<a href="terminate?subscriber_id=[% subscriber.subscriber_id %]" class="aaction">terminate</a>
[% END %]
</div>
<div class="p1">
[% IF subscriber.is_locked %]
<div class="alert">[% subscriber.is_locked %]</div>
[% END %]
[% IF messages.submsg %]<div class="goodmsg">[% messages.submsg %]</div>[% END %]
[% IF messages.suberr %]<div class="errormsg">[% messages.suberr %]</div>[% END %]
<form action="update_subscriber" method="post">
<input type="hidden" name="subscriber_id" value="[% subscriber.subscriber_id %]" />
<input type="hidden" name="account_id" value="[% account_id %]" />
<table class="table">
<tr><td class="tdkey">ID:</td><td>[% subscriber.subscriber_id %]</td></tr>
<tr>
<td class="tdkey">account ID:</td>
<td>
<a href="/account/detail?account_id=[% subscriber.account_id || account_id %]">
[% subscriber.account_id || account_id %]</a>
</td>
</tr>
<tr><td class="tdkey">user:</td><td>[% subscriber.webusername %]</td></tr>
[% IF subscriber.subscriber_id %]
<tr><td class="tdkey">SIP URI:</td><td>[% subscriber.username %]@[% subscriber.domain %]</td></tr>
[% ELSE %]
<tr>
<td class="tdkey">SIP URI:</td>
<td>
<input type="text" name="username" value="[% subscriber.username %]" />
@
<select size="1" name="domain">
[% FOREACH sdom = domains %]
<option>[% sdom.domain %]</option>
[% END %]
</select>
</td>
</tr>
[% END %]
<tr>
<td class="tdkey">E.164 number:</td>
<td>
[% IF edit_subscriber %]
<input type="text" name="cc" class="ishort" value="[% subscriber.cc %]" />
<input type="text" name="ac" class="ishort" value="[% subscriber.ac %]" />
<input type="text" name="sn" value="[% subscriber.sn %]" />
[% ELSE %]
<input type="text" class="disabled" disabled="disabled"
[% IF subscriber.sn %]
value="+[% subscriber.cc %] [% subscriber.ac %] [% subscriber.sn %]" />
[% ELSE %]
value="" />
[% END %]
[% END %]
</td>
</tr>
[% IF messages.number %]<tr><td /><td><div class="errormsg">[% messages.number %]</div></td></tr>[% END %]
[% IF messages.number_cc %]<tr><td /><td><div class="errormsg">[% messages.number_cc %]</div></td></tr>[% END %]
[% IF messages.number_ac %]<tr><td /><td><div class="errormsg">[% messages.number_ac %]</div></td></tr>[% END %]
[% IF messages.number_sn %]<tr><td /><td><div class="errormsg">[% messages.number_sn %]</div></td></tr>[% END %]
<tr>
<td class="tdkey">web password:</td>
<td>
[% IF edit_subscriber %]
<input type="text" name="webpassword" id="edit_webpass" value="[% subscriber.edit_webpass %]" />
[% ELSE %]
<input type="text" id="edit_webpass" class="disabled" disabled="disabled"
[% IF show_webpass %]
value="[% subscriber.webpassword %]" /> <a href="?subscriber_id=[% subscriber.subscriber_id %]" class="apass">Hide</a>
[% ELSE %]
[% IF subscriber.webpassword %]
value="********" /> <a href="?subscriber_id=[% subscriber.subscriber_id %]&amp;show_webpass=1" class="apass">Show</a>
[% ELSE %]
value="" />
[% END %]
[% END %]
[% END %]
</td>
</tr>
<tr>
<td class="tdkey">SIP password:</td>
<td>
[% IF edit_subscriber %]
<input type="text" name="password" id="edit_pass" value="[% subscriber.edit_pass %]" />
[% ELSE %]
<input type="text" id="edit_pass" class="disabled" disabled="disabled"
[% IF show_pass %]
value="[% subscriber.password %]" /> <a href="?subscriber_id=[% subscriber.subscriber_id %]" class="apass">Hide</a>
[% ELSE %]
[% IF subscriber.password %]
value="********" /> <a href="?subscriber_id=[% subscriber.subscriber_id %]&amp;show_pass=1" class="apass">Show</a>
[% ELSE %]
value="" />
[% END %]
[% END %]
[% END %]
</td>
</tr>
[% IF messages.password %]<tr><td /><td><div class="errormsg">[% messages.password %]</div></td></tr>[% END %]
<tr>
<td class="tdkey">administrative:</td>
<td>
<input type="checkbox" name="admin" class="checkbox" [% IF ! edit_subscriber %]disabled="disabled"[% END %]
[% IF subscriber.admin %]checked="checked"[% END %] />
</td>
</tr>
<tr>
<td class="tdkey">timezone:</td>
<td>
<input type="text" name="timezone" [% IF ! edit_subscriber %]class="disabled" disabled="disabled"[% END %]
value="[% subscriber.timezone %]" />
</td>
</tr>
[% IF messages.timezone %]<tr><td /><td><div class="errormsg">[% messages.timezone %]</div></td></tr>[% END %]
<tr><td class="tdkey">UUID:</td><td>[% subscriber.uuid %]</td></tr>
<tr><td class="tdkey">created:</td><td>[% subscriber.create_timestamp %]</td></tr>
<tr><td class="tdkey">modified:</td><td>[% subscriber.modify_timestamp %]</td></tr>
</table>
[% IF edit_subscriber %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<h3 id="userprefs">User Preferences</h3>
<div class="actions">
<a href="detail?subscriber_id=[% subscriber.subscriber_id %]&amp;edit_preferences=1#userprefs" class="aaction">edit</a>
[% IF edit_preferences %]
<a href="detail?subscriber_id=[% subscriber.subscriber_id %]#userprefs" class="aaction">cancel</a>
[% END %]
</div>
<div class="p1">
[% IF messages.prefmsg %]<div class="goodmsg">[% messages.prefmsg %]</div>[% END %]
[% IF messages.preferr %]<div class="errormsg">[% messages.preferr %]</div>[% END %]
<form action="update_preferences" method="post">
<input type="hidden" name="subscriber_id" value="[% subscriber.subscriber_id %]" />
<table>
[% FOREACH preference = subscriber.preferences_array %]
[% IF preference.max_occur == 1 %]
[% IF preference.key == "block_in_mode" || preference.key == "block_out_mode" %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
<select size="1" name="[% preference.key %]"
[% IF ! edit_preferences %]class="disabled" disabled="disabled"[% END %] >
<option [% IF ! preference.value %]selected="selected"[% END %]>blacklist</option>
<option [% IF preference.value %]selected="selected"[% END %]>whitelist</option>
</select>
</td>
</tr>
[% ELSIF preference.key == "block_in_clir"
|| preference.key == "clir"
|| preference.key == "cfu"
|| preference.key == "cfb"
|| preference.key == "cft"
|| preference.key == "cfna" %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
<input type="checkbox" name="[% preference.key %]" class="checkbox"
[% IF ! edit_preferences %]disabled="disabled"[% END %]
[% IF preference.value %]checked="checked"[% END %] />
</td>
</tr>
[% ELSIF preference.key == "prepaid"
|| preference.key == "has_extension" %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
<input type="checkbox" name="[% preference.key %]" class="checkbox"
disabled="disabled" [% IF preference.value %]checked="checked"[% END %] />
</td>
</tr>
[% ELSIF preference.key == "cftarget" %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
[% IF edit_preferences %]
<label for="cfvoicebox">
<input type="radio" id="cfvoicebox" value="voicebox" name="fw_target" class="radio"
[% IF ! preference.value.sipuri %]checked="checked"[% END %] />
Voicebox
</label>
<br clear="all" />
<label for="cfsipuri">
<input type="radio" id="cfsipuri" value="sipuri" name="fw_target" class="radio"
[% IF preference.value.sipuri %]checked="checked"[% END %] />
number or SIP-URI:
</label>
<input type="text" id="cfsipuritxt" name="fw_sipuri" size="25" value="[% preference.value.sipuri %]"
[% IF ! edit_preferences %]class="disabled" disabled="disabled"[% END %] />
[% ELSE %]
[% IF preference.value.sipuri %]
<input type="text" size="25" value="[% preference.value.sipuri %]"
class="disabled" disabled="disabled" />
[% ELSE %]
Voicebox
[% END %]
[% END %]
</td>
</tr>
[% IF messages.target %]<tr><td /><td><div class="errormsg">[% messages.target %]</div></td></tr>[% END %]
[% ELSIF preference.key == "base_cli"
|| preference.key == "extension"
|| preference.key == "lock"
|| preference.key == "base_user" %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
<input type="text" name="[% preference.key %]"
class="disabled txtpreference" disabled="disabled"
value="[% preference.value %]" />
</td>
</tr>
[% ELSE %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
<input type="text" name="[% preference.key %]" [% IF ! edit_preferences %]
class="disabled txtpreference" disabled="disabled"
[% ELSE %]
class="txtpreference"
[% END %]
value="[% preference.value %]" />
</td>
</tr>
[% END %]
[% ELSE %]
<tr>
<td class="tdkey">[% preference.key %]:</td>
<td>
[% IF preference.value %]
<select size="1" name="[% preference.key %]">
[% FOREACH pref_entry = preference.value %]
<option>[% pref_entry %]</option>
[% END %]
</select>
[% ELSE %]
<select size="1" name="[% preference.key %]">
<option />
</select>
[% END %]
[% IF ! edit_preferences %]
&nbsp;
<a href="edit_list?subscriber_id=[% subscriber.subscriber_id %]&amp;list_name=[% preference.key %]"
class="aaction">edit list</a>
[% END %]
</td>
</tr>
[% END %]
[% IF preference.error %]<tr><td /><td><div class="errormsg">[% preference.error %]</div></td></tr>[% END %]
[% END %]
</table>
[% IF edit_preferences %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>
<h3 id="vboxprefs">Voicebox Preferences</h3>
<div class="actions">
<a href="detail?subscriber_id=[% subscriber.subscriber_id %]&amp;edit_voicebox=1#vboxprefs" class="aaction">edit</a>
[% IF edit_voicebox %]
<a href="detail?subscriber_id=[% subscriber.subscriber_id %]#vboxprefs" class="aaction">cancel</a>
[% END %]
</div>
<div class="p1">
[% IF messages.vboxmsg %]<div class="goodmsg">[% messages.vboxmsg %]</div>[% END %]
[% IF messages.vboxerr %]<div class="errormsg">[% messages.vboxerr %]</div>[% END %]
<form action="update_voicebox" method="post">
<input type="hidden" name="subscriber_id" value="[% subscriber.subscriber_id %]" />
<table>
<tr>
<td class="tdkey">PIN:</td>
<td>
<input type="text" name="password" [% IF ! edit_voicebox %]class="disabled" disabled="disabled"[% END %]
value="[% subscriber.voicebox_preferences.password %]" />
</td>
</tr>
[% IF messages.vpin %]<tr><td /><td><div class="errormsg">[% messages.vpin %]</div></td></tr>[% END %]
<tr>
<td class="tdkey">E-Mail:</td>
<td>
<input type="text" name="email" [% IF ! edit_voicebox %]class="disabled" disabled="disabled"[% END %]
value="[% subscriber.voicebox_preferences.email %]" />
</td>
</tr>
[% IF messages.vemail %]<tr><td /><td><div class="errormsg">[% messages.vemail %]</div></td></tr>[% END %]
<tr>
<td class="tdkey">Attach WAV:</td>
<td>
<input type="checkbox" name="attach" class="checkbox" [% IF ! edit_voicebox %]disabled="disabled"[% END %]
[% IF subscriber.voicebox_preferences.attach %]checked="checked"[% END %] />
</td>
</tr>
</table>
[% IF edit_voicebox %]
<input type="submit" class="but" value="Save &#187;" />
[% END %]
</form>
</div>

@ -0,0 +1,69 @@
<h3>Edit [% list_name %] for
<a class="noarrow" href="detail?subscriber_id=[% subscriber_id %]">
[% subscriber.username %]@[% subscriber.domain %]</a>
</h3>
<div class="actions">
<a href="detail?subscriber_id=[% subscriber_id %]" class="aaction">back</a>
</div>
<div class="p1">
[% IF messages.nummsg %]<div class="goodmsg">[% messages.nummsg %]</div>[% END %]
[% IF messages.numerr %]<div class="errormsg">[% messages.numerr %]</div>[% END %]
<table>
[% FOREACH blockentry = list_data %]
<tr class="[% blockentry.background %]">
<td > [% blockentry.number %] </td>
<form action="do_edit_list" method="post">
<td >
<input type="hidden" name="subscriber_id" value="[% subscriber_id %]" />
<input type="hidden" name="list_name" value="[% list_name %]" />
<input type="hidden" name="block_del" value="[% blockentry.number %]" />
<input type="hidden" name="block_stat" value="[% blockentry.active %]" />
<div class="postlink">
<label for="listdel[% blockentry.id %]">delete</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="listdel[% blockentry.id %]" />
</div>
</td>
</form>
<form action="do_edit_list" method="post">
<td>
<input type="hidden" name="subscriber_id" value="[% subscriber_id %]" />
<input type="hidden" name="list_name" value="[% list_name %]" />
[% IF blockentry.active == 1 %]
<input type="hidden" name="block_act" value="[% blockentry.number %]" />
<input type="hidden" name="block_stat" value="1" />
<div class="postlink">
<label for="blockact[% blockentry.id %]">deactivate</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="blockact[% blockentry.id %]" />
</div>
[% ELSIF blockentry.active == 0 %]
<input type="hidden" name="block_act" value="[% blockentry.number %]" />
<input type="hidden" name="block_stat" value="0" />
<div class="postlink">
<label for="blockact[% blockentry.id %]">activate</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="blockact[% blockentry.id %]" />
</div>
[% END %]
</td>
</form>
</tr>
[% END %]
</table>
<br />
<form action="/subscriber/do_edit_list" method="post">
<input type="hidden" name="subscriber_id" value="[% subscriber_id %]" />
<input type="hidden" name="list_name" value="[% list_name %]" />
<label for="blockaddtxt">New entry:</label><br />
<div class="postlink">
<input type="text" size="20" name="block_add" id="blockaddtxt" value="[% blockaddtxt %]" /> &nbsp;
<label for="blockadd">Add entry</label>
<input type="image" class="hidden" src="/static/images/dot_trans.gif" alt="" id="blockadd" />
[% IF messages.msgadd %]<div class="errormsg">[% messages.msgadd %]</div>[% END %]
</div><br clear="all" />
</form>
</div>

@ -0,0 +1,37 @@
#!/usr/bin/perl -w
BEGIN { $ENV{CATALYST_ENGINE} ||= 'CGI' }
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../lib";
use admin;
admin->run;
1;
=head1 NAME
admin_cgi.pl - Catalyst CGI
=head1 SYNOPSIS
See L<Catalyst::Manual>
=head1 DESCRIPTION
Run a Catalyst application as a cgi script.
=head1 AUTHOR
Sebastian Riedel, C<sri@oook.de>
=head1 COPYRIGHT
This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut

@ -0,0 +1,75 @@
#!/usr/bin/perl -w
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use Catalyst::Helper;
my $force = 0;
my $mech = 0;
my $help = 0;
GetOptions(
'nonew|force' => \$force,
'mech|mechanize' => \$mech,
'help|?' => \$help
);
pod2usage(1) if ( $help || !$ARGV[0] );
my $helper = Catalyst::Helper->new( { '.newfiles' => !$force, mech => $mech } );
pod2usage(1) unless $helper->mk_component( 'admin', @ARGV );
1;
=head1 NAME
admin_create.pl - Create a new Catalyst Component
=head1 SYNOPSIS
admin_create.pl [options] model|view|controller name [helper] [options]
Options:
-force don't create a .new file where a file to be created exists
-mechanize use Test::WWW::Mechanize::Catalyst for tests if available
-help display this help and exits
Examples:
admin_create.pl controller My::Controller
admin_create.pl controller My::Controller BindLex
admin_create.pl -mechanize controller My::Controller
admin_create.pl view My::View
admin_create.pl view MyView TT
admin_create.pl view TT TT
admin_create.pl model My::Model
admin_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
dbi:SQLite:/tmp/my.db
admin_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
dbi:Pg:dbname=foo root 4321
See also:
perldoc Catalyst::Manual
perldoc Catalyst::Manual::Intro
=head1 DESCRIPTION
Create a new Catalyst Component.
Existing component files are not overwritten. If any of the component files
to be created already exist the file will be written with a '.new' suffix.
This behavior can be suppressed with the C<-force> option.
=head1 AUTHOR
Sebastian Riedel, C<sri@oook.de>
Maintained by the Catalyst Core Team.
=head1 COPYRIGHT
This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut

@ -0,0 +1,80 @@
#!/usr/bin/perl -w
BEGIN { $ENV{CATALYST_ENGINE} ||= 'FastCGI' }
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use FindBin;
use lib "$FindBin::Bin/../lib";
use admin;
my $help = 0;
my ( $listen, $nproc, $pidfile, $manager, $detach, $keep_stderr );
GetOptions(
'help|?' => \$help,
'listen|l=s' => \$listen,
'nproc|n=i' => \$nproc,
'pidfile|p=s' => \$pidfile,
'manager|M=s' => \$manager,
'daemon|d' => \$detach,
'keeperr|e' => \$keep_stderr,
);
pod2usage(1) if $help;
admin->run(
$listen,
{ nproc => $nproc,
pidfile => $pidfile,
manager => $manager,
detach => $detach,
keep_stderr => $keep_stderr,
}
);
1;
=head1 NAME
admin_fastcgi.pl - Catalyst FastCGI
=head1 SYNOPSIS
admin_fastcgi.pl [options]
Options:
-? -help display this help and exits
-l -listen Socket path to listen on
(defaults to standard input)
can be HOST:PORT, :PORT or a
filesystem path
-n -nproc specify number of processes to keep
to serve requests (defaults to 1,
requires -listen)
-p -pidfile specify filename for pid file
(requires -listen)
-d -daemon daemonize (requires -listen)
-M -manager specify alternate process manager
(FCGI::ProcManager sub-class)
or empty string to disable
-e -keeperr send error messages to STDOUT, not
to the webserver
=head1 DESCRIPTION
Run a Catalyst application as fastcgi.
=head1 AUTHOR
Sebastian Riedel, C<sri@oook.de>
Maintained by the Catalyst Core Team.
=head1 COPYRIGHT
This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut

@ -0,0 +1,111 @@
#!/usr/bin/perl -w
BEGIN {
$ENV{CATALYST_ENGINE} ||= 'HTTP';
$ENV{CATALYST_SCRIPT_GEN} = 30;
require Catalyst::Engine::HTTP;
}
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use FindBin;
use lib "$FindBin::Bin/../lib";
my $debug = 0;
my $fork = 0;
my $help = 0;
my $host = undef;
my $port = $ENV{ADMIN_PORT} || $ENV{CATALYST_PORT} || 3000;
my $keepalive = 0;
my $restart = $ENV{ADMIN_RELOAD} || $ENV{CATALYST_RELOAD} || 0;
my $restart_delay = 1;
my $restart_regex = '\.yml$|\.yaml$|\.pm$';
my $restart_directory = undef;
my @argv = @ARGV;
GetOptions(
'debug|d' => \$debug,
'fork' => \$fork,
'help|?' => \$help,
'host=s' => \$host,
'port=s' => \$port,
'keepalive|k' => \$keepalive,
'restart|r' => \$restart,
'restartdelay|rd=s' => \$restart_delay,
'restartregex|rr=s' => \$restart_regex,
'restartdirectory=s' => \$restart_directory,
);
pod2usage(1) if $help;
if ( $restart && $ENV{CATALYST_ENGINE} eq 'HTTP' ) {
$ENV{CATALYST_ENGINE} = 'HTTP::Restarter';
}
if ( $debug ) {
$ENV{CATALYST_DEBUG} = 1;
}
# This is require instead of use so that the above environment
# variables can be set at runtime.
require admin;
admin->run( $port, $host, {
argv => \@argv,
'fork' => $fork,
keepalive => $keepalive,
restart => $restart,
restart_delay => $restart_delay,
restart_regex => qr/$restart_regex/,
restart_directory => $restart_directory,
} );
1;
=head1 NAME
admin_server.pl - Catalyst Testserver
=head1 SYNOPSIS
admin_server.pl [options]
Options:
-d -debug force debug mode
-f -fork handle each request in a new process
(defaults to false)
-? -help display this help and exits
-host host (defaults to all)
-p -port port (defaults to 3000)
-k -keepalive enable keep-alive connections
-r -restart restart when files get modified
(defaults to false)
-rd -restartdelay delay between file checks
-rr -restartregex regex match files that trigger
a restart when modified
(defaults to '\.yml$|\.yaml$|\.pm$')
-restartdirectory the directory to search for
modified files
(defaults to '../')
See also:
perldoc Catalyst::Manual
perldoc Catalyst::Manual::Intro
=head1 DESCRIPTION
Run a Catalyst Testserver for this application.
=head1 AUTHOR
Sebastian Riedel, C<sri@oook.de>
Maintained by the Catalyst Core Team.
=head1 COPYRIGHT
This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut

@ -0,0 +1,54 @@
#!/usr/bin/perl -w
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use FindBin;
use lib "$FindBin::Bin/../lib";
use Catalyst::Test 'admin';
my $help = 0;
GetOptions( 'help|?' => \$help );
pod2usage(1) if ( $help || !$ARGV[0] );
print request($ARGV[0])->content . "\n";
1;
=head1 NAME
admin_test.pl - Catalyst Test
=head1 SYNOPSIS
admin_test.pl [options] uri
Options:
-help display this help and exits
Examples:
admin_test.pl http://localhost/some_action
admin_test.pl /some_action
See also:
perldoc Catalyst::Manual
perldoc Catalyst::Manual::Intro
=head1 DESCRIPTION
Run a Catalyst action from the command line.
=head1 AUTHOR
Sebastian Riedel, C<sri@oook.de>
Maintained by the Catalyst Core Team.
=head1 COPYRIGHT
This library is free software, you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
Loading…
Cancel
Save