From b6dbe0f12dbbe4ea5209367f905f937789b5b607 Mon Sep 17 00:00:00 2001 From: mark Date: Wed, 28 Dec 2011 23:56:20 +0000 Subject: [PATCH] broadband_nas export, #15284 --- FS/FS/Schema.pm | 1 + FS/FS/nas.pm | 4 +- FS/FS/part_export/broadband_nas.pm | 145 ++++++++++++++++++++++++++++++ FS/FS/svc_broadband.pm | 12 +-- httemplate/browse/nas.html | 10 ++- httemplate/edit/nas.html | 4 +- httemplate/edit/part_export.cgi | 15 +++- httemplate/edit/process/svc_broadband.cgi | 2 +- httemplate/edit/svc_broadband.cgi | 58 ++++++++++-- httemplate/view/svc_broadband.cgi | 22 +++++ 10 files changed, 253 insertions(+), 20 deletions(-) create mode 100644 FS/FS/part_export/broadband_nas.pm diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index bd708a7b6..68a072f1a 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -2282,6 +2282,7 @@ sub tables_hashref { 'server', 'varchar', 'NULL', 64, '', '', 'community', 'varchar', 'NULL', 50, '', '', 'description', 'varchar', '', 200, 'RADIUS Client', '', + 'svcnum', 'int', 'NULL', '', '', '', ], 'primary_key' => 'nasnum', 'unique' => [ [ 'nasname' ], ], diff --git a/FS/FS/nas.pm b/FS/FS/nas.pm index af5a23a24..c7f245912 100644 --- a/FS/FS/nas.pm +++ b/FS/FS/nas.pm @@ -50,6 +50,7 @@ FS::Record. The following fields are currently supported: =item description - a longer descriptive name +=item svcnum - the L record that 'owns' this device =back @@ -94,7 +95,7 @@ sub delete { ) || $self->SUPER::delete; if ( $error ) { - $dbh->rollback; + $dbh->rollback if $oldAutoCommit; return $error; } @@ -159,6 +160,7 @@ sub check { || $self->ut_textn('server') || $self->ut_textn('community') || $self->ut_text('description') + || $self->ut_foreign_keyn('svcnum', 'svc_broadband', 'svcnum') ; return $error if $error; diff --git a/FS/FS/part_export/broadband_nas.pm b/FS/FS/part_export/broadband_nas.pm new file mode 100644 index 000000000..a160c9944 --- /dev/null +++ b/FS/FS/part_export/broadband_nas.pm @@ -0,0 +1,145 @@ +package FS::part_export::broadband_nas; + +use strict; +use vars qw(%info $DEBUG); +use base 'FS::part_export'; +use FS::Record qw(qsearch qsearchs); +use FS::nas; +use FS::export_nas; +use FS::svc_broadband; +use FS::part_export::sqlradius; +use Tie::IxHash; + +$DEBUG = 0; + +my $me = '['.__PACKAGE__.']'; + +tie my %options, 'Tie::IxHash', + '1' => { type => 'title', label => 'Defaults' }, + default_shortname => { label => 'Short name' }, + default_secret => { label => 'Shared secret' }, + default_type => { label => 'Type' }, + default_ports => { label => 'Ports' }, + default_server => { label => 'Virtual server' }, + default_community => { label => 'Community' }, + '2' => { type => 'title', label => 'Export to' }, + # default export_nas entries will be inserted at runtime +; + +FS::UID->install_callback( + sub { + #creating new options based on records in a table, + #has to be done after initialization + foreach ( FS::part_export::sqlradius->all_sqlradius ) { + my $name = 'exportnum' . $_->exportnum; + $options{$name} = + { type => 'checkbox', label => $_->exportnum . ': ' . $_->label }; + + } + } +); + +%info = ( + 'svc' => 'svc_broadband', + 'desc' => 'Create a NAS entry in Freeside', + 'options' => \%options, + 'weight' => 10, + 'notes' => <<'END' +

Create an entry in the NAS (RADIUS client) table, inheriting the IP +address and description of the broadband service. This can be used +with 'sqlradius' or 'broadband_sqlradius' exports to maintain entries +in the client table on a RADIUS server.

+

Most broadband configurations should not use this, even if they use +RADIUS for access control.

+END +); + +=item export_insert NEWSVC + +=item export_replace NEWSVC OLDSVC + +NEWSVC can contain pseudo-field entries for fields in nas. Those changes +will be applied to the attached NAS record. + +=cut + +sub export_insert { + my $self = shift; + my $svc_broadband = shift; + my %hash = map { $_ => $svc_broadband->get($_) } FS::nas->fields; + my $nas = $self->default_nas( + %hash, + 'nasname' => $svc_broadband->ip_addr, + 'description' => $svc_broadband->description, + 'svcnum' => $svc_broadband->svcnum, + ); + + my $error = + $nas->insert() + || $nas->process_m2m('link_table' => 'export_nas', + 'target_table' => 'part_export', + 'params' => { $self->options }); + die $error if $error; + return; +} + +sub export_delete { + my $self = shift; + my $svc_broadband = shift; + my $svcnum = $svc_broadband->svcnum; + my $nas = qsearchs('nas', { 'svcnum' => $svcnum }); + if ( !$nas ) { + # we were going to delete it anyway... + warn "linked NAS with svcnum $svcnum not found for deletion\n"; + return; + } + my $error = $nas->delete; # will clean up export_nas records + die $error if $error; + return; +} + +sub export_replace { + my $self = shift; + my ($new_svc, $old_svc) = (shift, shift); + + my $svcnum = $new_svc->svcnum; + my $nas = qsearchs('nas', { 'svcnum' => $svcnum }); + if ( !$nas ) { + warn "linked nas with svcnum $svcnum not found for update, creating new\n"; + # then we should insert it + # (this happens if the nas table is wiped out, or if the broadband_nas + # export is newly applied to an existing svcpart) + return $self->export_insert($new_svc); + } + + my %hash = $new_svc->hash; + foreach (FS::nas->fields) { + $nas->set($_, $hash{$_}) if exists($hash{$_}); + } + + $nas->nasname($new_svc->ip_addr); # this must always be true + + my $error = $nas->replace; + die $error if $error; + return; +} + +=item default_nas HASH + +Returns a new L object containing the default values, plus anything +in HASH. + +=cut + +sub default_nas { + my $self = shift; + FS::nas->new({ + map( { $_ => $self->option("default_$_") } + qw(shortname type ports secret server community) + ), + @_ + }); +} + + +1; diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm index 1d1fa762c..a85fc5cb0 100755 --- a/FS/FS/svc_broadband.pm +++ b/FS/FS/svc_broadband.pm @@ -2,6 +2,8 @@ package FS::svc_broadband; use strict; use vars qw(@ISA $conf); + +use base qw(FS::svc_Radius_Mixin FS::svc_Tower_Mixin FS::svc_Common); use NetAddr::IP; use FS::Record qw( qsearchs qsearch dbh ); use FS::svc_Common; @@ -10,8 +12,6 @@ use FS::addr_block; use FS::part_svc_router; use FS::tower_sector; -@ISA = qw( FS::svc_Radius_Mixin FS::svc_Tower_Mixin FS::svc_Common ); - $FS::UID::callback{'FS::svc_broadband'} = sub { $conf = new FS::Conf; }; @@ -398,11 +398,11 @@ sub check { if ( $cust_pkg && ! $self->latitude && ! $self->longitude ) { my $l = $cust_pkg->cust_location_or_main; if ( $l->ship_latitude && $l->ship_longitude ) { - $self->latitude = $l->ship_latitude; - $self->longitude = $l->ship_longitude; + $self->latitude( $l->ship_latitude ); + $self->longitude( $l->ship_longitude ); } elsif ( $l->latitude && $l->longitude ) { - $self->latitude = $l->latitude; - $self->longitude = $l->longitude; + $self->latitude( $l->latitude ); + $self->longitude( $l->longitude ); } } diff --git a/httemplate/browse/nas.html b/httemplate/browse/nas.html index c9d57e8ed..5ce729b65 100644 --- a/httemplate/browse/nas.html +++ b/httemplate/browse/nas.html @@ -28,6 +28,14 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); -my $link = [ $p.'edit/nas.html?', 'nasnum' ]; +my $link = sub { + my $nas = shift; + if ( $nas->svcnum ) { + return [ $p.'view/svc_broadband.cgi?', 'svcnum' ]; + } + else { + return [ $p.'edit/nas.html?', 'nasnum' ]; + } +}; diff --git a/httemplate/edit/nas.html b/httemplate/edit/nas.html index 9d9b8e9fd..2e66fc3be 100644 --- a/httemplate/edit/nas.html +++ b/httemplate/edit/nas.html @@ -48,7 +48,9 @@ sub html_bottom { 'source_obj' => $nas, 'link_table' => 'export_nas', 'target_table' => 'part_export', - 'hashref' => { 'exporttype' => 'sqlradius' }, + 'hashref' => { 'exporttype' => + { op => 'LIKE', value => '%sqlradius' } + }, 'name_callback' => sub { $_[0]->label }, 'default' => 'yes', 'target_link' => $p.'edit/part_export.cgi?', diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi index 9a0e0bd29..1450ac3b3 100644 --- a/httemplate/edit/part_export.cgi +++ b/httemplate/edit/part_export.cgi @@ -51,8 +51,12 @@ $action ||= $part_export->exportnum ? 'Edit' : 'Add'; #my $exports = FS::part_export::export_info($svcdb); my $exports = FS::part_export::export_info(); -my %layers = map { $_ => "$_ - ". $exports->{$_}{desc} } keys %$exports; -$layers{''}=''; +tie my %layers, 'Tie::IxHash', + '' => '', + map { $_ => "$_ - ". $exports->{$_}{desc} } + sort { $a cmp $b } + keys %$exports; +; my $widget = new HTML::Widgets::SelectLayers( 'selected_layer' => $part_export->exporttype, @@ -83,6 +87,13 @@ my $widget = new HTML::Widgets::SelectLayers( ? $optinfo->{default} : '' ); + if ( $type eq 'title' ) { + $html .= qq!! . + $label . + ''; + next; + } + # 'freeform': disables table formatting of options. Instead, each # option can define "before" and "after" strings which are inserted # around the selector. diff --git a/httemplate/edit/process/svc_broadband.cgi b/httemplate/edit/process/svc_broadband.cgi index 36c64d11a..4184f5fa6 100644 --- a/httemplate/edit/process/svc_broadband.cgi +++ b/httemplate/edit/process/svc_broadband.cgi @@ -1,6 +1,6 @@ <& elements/svc_Common.html, table => 'svc_broadband', - fields => [ fields('svc_broadband'), 'usergroup' ], + fields => [ fields('svc_broadband'), fields('nas'), 'usergroup' ], precheck_callback => \&precheck, &> <%init> diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi index ad4d6046d..b9e2fd2d9 100644 --- a/httemplate/edit/svc_broadband.cgi +++ b/httemplate/edit/svc_broadband.cgi @@ -3,7 +3,9 @@ 'name' => 'broadband service', 'table' => 'svc_broadband', 'fields' => \@fields, - 'field_callback' => $callback, + 'field_callback' => $field_callback, + 'svc_new_callback' => $svc_edit_callback, + 'svc_edit_callback' => $svc_edit_callback, 'dummy' => $cgi->query_string, 'onsubmit' => 'validate_coords', 'html_foot' => $js, @@ -113,17 +115,57 @@ if ( $conf->exists('svc_broadband-radius') ) { } } - my $fixedblock = ''; -my $callback = sub { - my ($cgi, $object, $fieldref) = @_; +my $part_svc; + +my $svc_edit_callback = sub { + my ($cgi, $svc_x, $part_svc_x, $cust_pkg, $fields, $opt) = @_; + + $part_svc = $part_svc_x; #for field_callback to use - my $svcpart = $object->svcnum ? $object->cust_svc->svcpart - : $cgi->param('svcpart'); + $opt->{'labels'}{'block_label'} = 'Block'; - my $part_svc = qsearchs( 'part_svc', { svcpart => $svcpart } ); - die "No part_svc entry!" unless $part_svc; + my ($nas_export) = $part_svc->part_export('broadband_nas'); + #can we assume there's only one of these per part_svc? + if ( $nas_export ) { + my $nas; + if ( $svc_x->svcnum ) { + $nas = qsearchs('nas', { 'svcnum' => $svc_x->svcnum }); + } + $nas ||= $nas_export->default_nas; + $svc_x->set($_, $nas->$_) foreach fields('nas'); + + # duplicates the fields in httemplate/edit/nas.html (mostly) + push @$fields, + { type => 'tablebreak-tr-title', + #value => 'Attached NAS', + value => $nas_export->exportname, + colspan => 2, + }, + { field=>'nasnum', type=>'hidden', }, + { field=>'shortname', size=>16, maxlength=>32 }, + { field=>'secret', size=>40, maxlength=>60, required=>1 }, + { field=>'type', type=>'select', + options=>[qw( cisco computone livingston max40xx multitech netserver + pathras patton portslave tc usrhiper other )], + }, + { field=>'ports', size=>5 }, + { field=>'server', size=>40, maxlength=>64 }, + { field=>'community', size=>40, maxlength=>50 }, + ; + + $opt->{'labels'}{'shortname'} = 'Short name'; + $opt->{'labels'}{'secret'} = 'Shared secret'; + $opt->{'labels'}{'type'} = 'Type'; + $opt->{'labels'}{'ports'} = 'Ports'; + $opt->{'labels'}{'server'} = 'Server'; + $opt->{'labels'}{'community'} = 'Community'; + } +}; + +my $field_callback = sub { + my ($cgi, $object, $fieldref) = @_; my $columndef = $part_svc->part_svc_column($fieldref->{'field'}); if ($columndef->columnflag eq 'F') { diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi index de39f6ae4..2e93d42fd 100644 --- a/httemplate/view/svc_broadband.cgi +++ b/httemplate/view/svc_broadband.cgi @@ -2,6 +2,7 @@ table => 'svc_broadband', labels => \%labels, fields => \@fields, + svc_callback => \&svc_callback, &> <%init> @@ -97,4 +98,25 @@ sub coordinates { include('/elements/coord-links.html', $s->latitude, $s->longitude, $d); } +sub svc_callback { + # trying to move to the callback style + my ($cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt) = @_; + # again, we assume at most one of these exports per part_svc + my ($nas_export) = $part_svc->part_export('broadband_nas'); + if ( $nas_export ) { + my $nas = qsearchs('nas', { 'svcnum' => $svc_x->svcnum }); + if ( $nas ) { + $svc_x->set($_, $nas->$_) foreach (fields('nas')); + push @$fields, qw(shortname secret type ports server community); + $opt->{'labels'}{'shortname'} = 'Short name'; + $opt->{'labels'}{'secret'} = 'Shared secret'; + $opt->{'labels'}{'type'} = 'Type'; + $opt->{'labels'}{'ports'} = 'Ports'; + $opt->{'labels'}{'server'} = 'Server'; + $opt->{'labels'}{'community'} = 'Community'; + } #if $nas + } #$nas_export +}; + + -- 2.11.0