From c9678906cb89a0c05a67d8118898202b64f84fd6 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 6 Oct 2011 20:44:18 +0000 Subject: [PATCH] add dsl_device to track mac addresses, RT#13656 --- FS/FS.pm | 2 + FS/FS/ClientAPI/MyAccount.pm | 78 ++++++++++++++++++++ FS/FS/ClientAPI_XMLRPC.pm | 3 + FS/FS/Record.pm | 36 ++++++++++ FS/FS/Schema.pm | 13 ++++ FS/MANIFEST | 2 + FS/t/dsl_device.t | 5 ++ fs_selfservice/FS-SelfService/SelfService.pm | 3 + httemplate/edit/dsl_device.html | 31 ++++++++ httemplate/edit/process/dsl_device.html | 22 ++++++ httemplate/misc/delete-dsl_device.html | 23 ++++++ httemplate/view/elements/svc_devices.html | 102 +++++++++++++++++++++++++++ httemplate/view/svc_dsl.cgi | 65 ++++++++++------- 13 files changed, 358 insertions(+), 27 deletions(-) create mode 100644 FS/t/dsl_device.t create mode 100644 httemplate/edit/dsl_device.html create mode 100644 httemplate/edit/process/dsl_device.html create mode 100755 httemplate/misc/delete-dsl_device.html create mode 100644 httemplate/view/elements/svc_devices.html diff --git a/FS/FS.pm b/FS/FS.pm index 2f9892049..972a21afb 100644 --- a/FS/FS.pm +++ b/FS/FS.pm @@ -154,6 +154,8 @@ L - DSL, wireless and other broadband class. L - DSL +L - DSL device MAC addresses + L - DSL order notes L - Address block class diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index 3976ab239..e3afac14a 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -25,6 +25,8 @@ use FS::svc_acct; use FS::svc_domain; use FS::svc_phone; use FS::svc_external; +use FS::svc_dsl; +use FS::dsl_device; use FS::part_svc; use FS::cust_main; use FS::cust_bill; @@ -1403,6 +1405,82 @@ sub list_svcs { } +sub _customer_svc_x { + my($custnum, $svcnum, $table) = @_; + + $custnum =~ /^(\d+)$/ or die "illegal custnum"; + my $search = " AND custnum = $1"; + #$search .= " AND agentnum = ". $session->{'agentnum'} if $context eq 'agent'; + + qsearchs( { + 'table' => ($table || 'svc_acct'), + 'addl_from' => 'LEFT JOIN cust_svc USING ( svcnum ) '. + 'LEFT JOIN cust_pkg USING ( pkgnum ) ',#. + #'LEFT JOIN cust_main USING ( custnum ) ', + 'hashref' => { 'svcnum' => $svcnum, }, + 'extra_sql' => $search, #important + } ); + +} + +sub list_dsl_devices { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $svc_dsl = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_dsl' ) + or return { 'error' => "Service not found" }; + + return { + 'devices' => [ map { + +{ 'mac_addr' => $_->mac_addr }; + } $svc_dsl->dsl_device + ], + }; + +} + +sub add_dsl_device { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $svc_dsl = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_dsl' ) + or return { 'error' => "Service not found" }; + + return { 'error' => 'No MAC address supplied' } + unless length($p->{'mac_addr'}); + + my $dsl_device = new FS::dsl_device { 'svcnum' => $svc_dsl->svcnum, + 'mac_addr' => scalar($p->{'mac_addr'}), + }; + my $error = $dsl_device->insert; + return { 'error' => $error }; + +} + +sub delete_dsl_device { + my $p = shift; + + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + my $svc_dsl = _customer_svc_x( $custnum, $p->{'svcnum'}, 'svc_dsl' ) + or return { 'error' => "Service not found" }; + + my $dsl_device = qsearchs('dsl_device', { 'svcnum' => $svc_dsl->svcnum, + 'mac_addr' => scalar($p->{'mac_addr'}), + } + ) + or return { 'error' => 'Unknown MAC address: '. $p->{'mac_addr'} }; + + my $error = $dsl_device->delete; + return { 'error' => $error }; + +} + sub port_graph { my $p = shift; _usage_details( \&_port_graph, $p, diff --git a/FS/FS/ClientAPI_XMLRPC.pm b/FS/FS/ClientAPI_XMLRPC.pm index b077bf27a..2913ffd47 100644 --- a/FS/FS/ClientAPI_XMLRPC.pm +++ b/FS/FS/ClientAPI_XMLRPC.pm @@ -87,6 +87,9 @@ sub ss2clientapi { 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?) 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?) 'list_svc_usage' => 'MyAccount/list_svc_usage', + 'list_dsl_devices' => 'MyAccount/list_dsl_devices', + 'add_dsl_device' => 'MyAccount/add_dsl_device', + 'delete_dsl_device' => 'MyAccount/delete_dsl_device', 'port_graph' => 'MyAccount/port_graph', 'list_cdr_usage' => 'MyAccount/list_cdr_usage', 'list_support_usage' => 'MyAccount/list_support_usage', diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index e52d09424..9596a30b6 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -2217,6 +2217,42 @@ sub ut_hexn { $self->setfield($field, uc($1)); ''; } + +=item ut_mac_addr COLUMN + +Check/untaint mac addresses. May be null. + +=cut + +sub ut_mac_addr { + my($self, $field) = @_; + + my $mac = $self->get($field); + $mac =~ s/\s+//g; + $mac =~ s/://g; + $self->set($field, $mac); + + my $e = $self->ut_hex($field); + return $e if $e; + + return "Illegal (mac address) $field: ". $self->getfield($field) + unless length($self->getfield($field)) == 12; + + ''; + +} + +=item ut_mac_addrn COLUMN + +Check/untaint mac addresses. May be null. + +=cut + +sub ut_mac_addrn { + my($self, $field) = @_; + ($self->getfield($field) eq '') ? '' : $self->ut_mac_addr($field); +} + =item ut_ip COLUMN Check/untaint ip addresses. IPv4 only for now, though ::1 is auto-translated diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index c0bbd0ae4..5d5bd4eab 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -2020,6 +2020,19 @@ sub tables_hashref { 'unique' => [ ], 'index' => [ ['phonenum'], ['vendor_order_id'] ], }, + + 'dsl_device' => { + 'columns' => [ + 'devicenum', 'serial', '', '', '', '', + #part_device? or our own part_dsl_device? + #'devicepart', 'int', '', '', '', '', + 'svcnum', 'int', '', '', '', '', + 'mac_addr', 'varchar', '', 12, '', '', + ], + 'primary_key' => 'devicenum', + 'unique' => [ [ 'mac_addr' ], ], + 'index' => [ [ 'svcnum' ], ], # [ 'devicepart' ] ], + }, 'dsl_note' => { 'columns' => [ diff --git a/FS/MANIFEST b/FS/MANIFEST index 43a1ed87e..557e61279 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -610,3 +610,5 @@ FS/radius_group.pm t/radius_group.t FS/template_content.pm t/template_content.t +FS/dsl_device.pm +t/dsl_device.t diff --git a/FS/t/dsl_device.t b/FS/t/dsl_device.t new file mode 100644 index 000000000..2e97424b8 --- /dev/null +++ b/FS/t/dsl_device.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::dsl_device; +$loaded=1; +print "ok 1\n"; diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm index e778f79c2..356784642 100644 --- a/fs_selfservice/FS-SelfService/SelfService.pm +++ b/fs_selfservice/FS-SelfService/SelfService.pm @@ -49,6 +49,9 @@ $socket .= '.'.$tag if defined $tag && length($tag); 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss (added?) 'list_svcs' => 'MyAccount/list_svcs', #add to ss (added?) 'list_svc_usage' => 'MyAccount/list_svc_usage', + 'list_dsl_devices' => 'MyAccount/list_dsl_devices', + 'add_dsl_device' => 'MyAccount/add_dsl_device', + 'delete_dsl_device' => 'MyAccount/delete_dsl_device', 'port_graph' => 'MyAccount/port_graph', 'list_cdr_usage' => 'MyAccount/list_cdr_usage', 'list_support_usage' => 'MyAccount/list_support_usage', diff --git a/httemplate/edit/dsl_device.html b/httemplate/edit/dsl_device.html new file mode 100644 index 000000000..0d20fe522 --- /dev/null +++ b/httemplate/edit/dsl_device.html @@ -0,0 +1,31 @@ +<% include( 'elements/edit.html', + 'name' => 'DSL device', + 'table' => 'dsl_device', + 'labels' => { + 'devicenum' => 'Device', + 'mac_addr' => 'MAC address', + }, + 'fields' => [ { field => 'mac_addr', + type => 'select-mac', + }, + { 'field' => 'svcnum', + 'type' => 'hidden', + }, + ], + 'menubar' => [], #disable viewall + #'viewall_dir' => 'browse', + 'new_callback' => sub { + my( $cgi, $object ) = @_; + $object->svcnum( $cgi->param('svcnum') ); + }, + ) +%> +<%init> + +# :/ needs agent-virt so you can't futz with arbitrary devices + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + + + diff --git a/httemplate/edit/process/dsl_device.html b/httemplate/edit/process/dsl_device.html new file mode 100644 index 000000000..4dd53d444 --- /dev/null +++ b/httemplate/edit/process/dsl_device.html @@ -0,0 +1,22 @@ +<% include( 'elements/process.html', + 'table' => 'dsl_device', + 'redirect' => sub { + my( $cgi, $dsl_device ) = @_; + popurl(3).'view/svc_dsl.cgi?'. + 'svcnum='. $dsl_device->svcnum. + ';devicenum='; + }, + ) +%> +<%init> + +if($cgi->param('sel_mac_addr') && !$cgi->param('mac_addr')) { + $cgi->param('mac_addr',$cgi->param('sel_mac_addr')); +} + +# :/ needs agent-virt so you can't futz with arbitrary devices + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + + diff --git a/httemplate/misc/delete-dsl_device.html b/httemplate/misc/delete-dsl_device.html new file mode 100755 index 000000000..0b985faa7 --- /dev/null +++ b/httemplate/misc/delete-dsl_device.html @@ -0,0 +1,23 @@ +% if ( $error ) { +% errorpage($error); +% } else { +<% $cgi->redirect($p. "view/svc_dsl.cgi?". $svcnum) %> +% } +<%init> + +# :/ needs agent-virt so you can't futz with arbitrary devices + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +#untaint devicenum +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/ || die "Illegal devicenum"; +my $devicenum = $1; + +my $dsl_device = qsearchs('dsl_device', { 'devicenum' => $devicenum } ); +my $svcnum = $dsl_device->svcnum; + +my $error = $dsl_device->delete; + + diff --git a/httemplate/view/elements/svc_devices.html b/httemplate/view/elements/svc_devices.html new file mode 100644 index 000000000..d71c82f07 --- /dev/null +++ b/httemplate/view/elements/svc_devices.html @@ -0,0 +1,102 @@ +<%doc> + +#Example: + + include( 'elements/svc_devices.html', + #required + 'svc_x' => $svc_phone, #or $svc_dsl + 'table' => 'phone_device', #or dsl_device + + #optional + 'no_edit' => 0, #set true to turn off edit link + ) + + +<% $devices %> +<%init> + + my %opt = @_; + my $table = $opt{'table'}; #part_device, dsl_device + my $svc_x = $opt{'svc_x'}; + + my $devices = ''; + + my $num_part_device = 0; + if ( $table eq 'phone_device' ) { + my $sth = dbh->prepare("SELECT COUNT(*) FROM part_device") + #WHERE disabled = '' OR disabled IS NULL;"); + or die dbh->errstr; + $sth->execute or die $sth->errstr; + $num_part_device = $sth->fetchrow_arrayref->[0]; +} + + my @devices = $svc_x->$table(); + + #should move the below to proper mason code above instead of making $devices + if ( @devices || $num_part_device || $table eq 'dsl_device' ) { + my $svcnum = $svc_x->svcnum; + $devices .= + qq[Devices (Add device)
]; + if ( @devices ) { + + $devices .= qq! + + !; + + + $devices .= + include('/elements/table-grid.html'). + ''; + + $devices .= + 'Type' + if $table eq 'phone_device'; + + $devices .= + 'MAC Addr'. + ''. + ''. + ''; + my $bgcolor1 = '#eeeeee'; + my $bgcolor2 = '#ffffff'; + my $bgcolor = ''; + + foreach my $device ( @devices ) { + + if ( $bgcolor eq $bgcolor1 ) { + $bgcolor = $bgcolor2; + } else { + $bgcolor = $bgcolor1; + } + my $td = qq(); + + my $devicenum = $device->devicenum; + my $export_links = join( '
', @{ $device->export_links } ) + if $device->can('export_links'); + + $devices .= ''; + $devices .= $td. $device->part_device->devicename. '' + if $table eq 'phone_device'; #$devices->can('part_device'); + + $devices .= $td. $device->mac_addr. ''. + $td. $export_links. ''. + "$td( "; + + $devices .= qq(edit | ) + unless $opt{'no_edit'}; + + $devices .= qq(delete). + ' )'. + ''; + } + $devices .= '
'; + } + $devices .= '
'; + } + + diff --git a/httemplate/view/svc_dsl.cgi b/httemplate/view/svc_dsl.cgi index 9d9134a4d..477da268b 100644 --- a/httemplate/view/svc_dsl.cgi +++ b/httemplate/view/svc_dsl.cgi @@ -33,33 +33,44 @@ my $svc_cb = sub { # if no DSL-pulling exports, then just display everything, which is the # default behaviour implemented above - return if ( scalar(@exports) == 0 ); - - my $export = @exports[0]; - - @fields = ( 'phonenum', - { field => 'loop_type', - value => 'FS::part_export::'.$export->exporttype.'::loop_type_long' - }, - { field => 'desired_due_date', type => 'date', }, - { field => 'due_date', type => 'date', }, - { field => 'pushed', type => 'datetime', }, - { field => 'monitored', type => 'checkbox', }, - { field => 'last_pull', type => 'datetime', }, - 'first', - 'last', - 'company' ); - - my $status = ''; - if($export->exporttype eq 'ikano') { - push @fields, qw ( username password isp_chg isp_prev staticips ); - $status = "Ikano " . $svc_dsl->vendor_order_type . " order #" - . $svc_dsl->vendor_order_id . "   Status: " - . $svc_dsl->vendor_order_status; - } - # else add any other export-specific stuff here + if ( scalar(@exports) ) { + + my $export = @exports[0]; + + @fields = ( + 'phonenum', + { field => 'loop_type', + value => 'FS::part_export::'.$export->exporttype.'::loop_type_long' + }, + { field => 'desired_due_date', type => 'date', }, + { field => 'due_date', type => 'date', }, + { field => 'pushed', type => 'datetime', }, + { field => 'monitored', type => 'checkbox', }, + { field => 'last_pull', type => 'datetime', }, + 'first', + 'last', + 'company', + ); + + my $status = ''; + if($export->exporttype eq 'ikano') { + push @fields, qw ( username password isp_chg isp_prev staticips ); + $status = "Ikano " . $svc_dsl->vendor_order_type . " order #" + . $svc_dsl->vendor_order_id . "   Status: " + . $svc_dsl->vendor_order_status; + } + # else add any other export-specific stuff here - $footer = "$status"; + $footer = "$status"; + + } + + $footer .= '

'. + include( '/view/elements/svc_devices.html', + 'svc_x' => $svc_dsl, + 'table' => 'dsl_device', + 'no_edit' => 1, + ); my @notes = $svc_dsl->notes; if ( @notes ) { @@ -68,7 +79,7 @@ my $svc_cb = sub { my $date_format = $conf->config('date_format') || '%m/%d/%Y'; $footer .= - "

Order Notes
". ntable('#cccccc', 2). #id="dsl_notes" + "Order Notes
". ntable('#cccccc', 2). #id="dsl_notes" 'DateByPriorityNote'; foreach my $note ( @notes ) { -- 2.11.0