RADIUS groups for svc_broadband, #14695
authormark <mark>
Thu, 10 Nov 2011 21:40:25 +0000 (21:40 +0000)
committermark <mark>
Thu, 10 Nov 2011 21:40:25 +0000 (21:40 +0000)
21 files changed:
FS/FS/Conf.pm
FS/FS/nas.pm
FS/FS/part_export.pm
FS/FS/part_export/broadband_sqlradius.pm [new file with mode: 0644]
FS/FS/part_export/phone_sqlradius.pm
FS/FS/part_svc.pm
FS/FS/radius_usergroup.pm
FS/FS/svc_Radius_Mixin.pm [new file with mode: 0644]
FS/FS/svc_acct.pm
FS/FS/svc_broadband.pm
FS/bin/freeside-sqlradius-reset
httemplate/browse/part_svc.cgi
httemplate/edit/process/svc_broadband.cgi
httemplate/edit/svc_acct.cgi
httemplate/edit/svc_broadband.cgi
httemplate/elements/select-radius_group.html
httemplate/elements/tr-fixed.html
httemplate/elements/tr-select-radius_group.html [new file with mode: 0644]
httemplate/view/elements/svc_Common.html
httemplate/view/svc_acct/basics.html
httemplate/view/svc_broadband.cgi

index 4104fda..8d26e91 100644 (file)
@@ -2297,6 +2297,13 @@ and customer address. Include units.',
   },
 
   {
+    'key'         => 'svc_broadband-radius',
+    'section'     => '',
+    'description' => 'Enable RADIUS groups for broadband services.',
+    'type'        => 'checkbox',
+  },
+
+  {
     'key'         => 'svc_acct-alldomains',
     'section'     => '',
     'description' => 'Allow accounts to select any domain in the database.  Normally accounts can only select from the domain set in the service definition and those purchased by the customer.',
index 4564a63..af5a23a 100644 (file)
@@ -87,8 +87,11 @@ sub delete {
   my $dbh = dbh;
 
   my $self = shift;
-  my $error = $self->process_m2m([])
-           || $self->SUPER::delete;
+  my $error = $self->process_m2m(
+    link_table    => 'export_nas',
+    target_table  => 'part_export',
+    params        => []
+  ) || $self->SUPER::delete;
 
   if ( $error ) {
     $dbh->rollback;
index f84f2a0..4b60953 100644 (file)
@@ -128,7 +128,12 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my $error = $self->SUPER::delete;
+  # clean up export_nas records
+  my $error = $self->process_m2m(
+    'link_table'    => 'export_nas',
+    'target_table'  => 'nas',
+    'params'        => [],
+  ) || $self->SUPER::delete;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
diff --git a/FS/FS/part_export/broadband_sqlradius.pm b/FS/FS/part_export/broadband_sqlradius.pm
new file mode 100644 (file)
index 0000000..ae0876d
--- /dev/null
@@ -0,0 +1,100 @@
+package FS::part_export::broadband_sqlradius;
+
+use strict;
+use vars qw($DEBUG @ISA %options %info $conf);
+use Tie::IxHash;
+use FS::Conf;
+use FS::Record qw( dbh str2time_sql ); #qsearch qsearchs );
+use FS::part_export::sqlradius qw(sqlradius_connect);
+
+FS::UID->install_callback(sub { $conf = new FS::Conf });
+
+@ISA = qw(FS::part_export::sqlradius);
+
+$DEBUG = 0;
+
+tie %options, 'Tie::IxHash',
+  'datasrc'  => { label=>'DBI data source ' },
+  'username' => { label=>'Database username' },
+  'password' => { label=>'Database password' },
+  'usergroup'=> { label   => 'Group table',
+                  type    => 'select',
+                  options => [qw( radusergroup usergroup )],
+                },
+# session report doesn't currently know about this export anyway
+#  'hide_ip' => {
+#    type  => 'checkbox',
+#    label => 'Hide IP address on session reports',
+#  },
+  'mac_as_password' => { 
+    type => 'checkbox',
+    default => '1',
+    label => 'Use MAC address as password',
+  },
+  'radius_password' => { label=>'Fixed password' },
+  'ip_addr_as' => { label => 'Send IP address as',
+                    default => 'Framed-IP-Address' },
+;
+
+%info = (
+  'svc'      => 'svc_broadband',
+  'desc'     => 'Real-time export to SQL-backed RADIUS (such as FreeRadius) for broadband services',
+  'options'  => \%options,
+  'nas'      => 'Y',
+  'notes'    => <<END,
+Real-time export of <b>radcheck</b>, <b>radreply</b>, and <b>usergroup</b> 
+tables to any SQL database for 
+<a href="http://www.freeradius.org/">FreeRADIUS</a>
+or <a href="http://radius.innercite.com/">ICRADIUS</a>.
+<br><br>
+
+This export is for broadband service access control based on MAC address.  
+For a more typical RADIUS export, see sqlradius.
+<br><br>
+
+See the
+<a href="http://search.cpan.org/dist/DBI/DBI.pm#connect">DBI documentation</a>
+and the
+<a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a>
+for the exact syntax of a DBI data source.
+
+END
+);
+
+sub rebless { shift; }
+
+sub export_username {
+  my($self, $svc_broadband) = (shift, shift);
+  $svc_broadband->mac_addr;
+}
+
+sub radius_reply {
+  my($self, $svc_broadband) = (shift, shift);
+  my %reply;
+  if (  length($self->option('ip_addr_as',1)) 
+    and length($svc_broadband->ip_addr) ) {
+    $reply{$self->option('ip_addr_as')} = $svc_broadband->ip_addr;
+  }
+  %reply;
+}
+
+sub radius_check {
+  my($self, $svc_broadband) = (shift, shift);
+  my $password_attrib = $conf->config('radius-password') || 'Password';
+  my %check;
+  if ( $self->option('mac_as_password') ) {
+    $check{$password_attrib} = $svc_broadband->mac_addr; #formatting?
+  }
+  elsif ( length( $self->option('radius_password',1)) ) {
+    $check{$password_attrib} = $self->option('radius_password');
+  }
+  %check;
+}
+
+sub _export_suspend {}
+sub _export_unsuspend {}
+
+sub update_svc {} #do nothing
+
+1;
+
index 0816efa..6b14bed 100644 (file)
@@ -44,7 +44,6 @@ tie %options, 'Tie::IxHash',
   'options'  => \%options,
   'notes'    => <<END,
 Real-time export of <b>radcheck</b> table
-<!--, <b>radreply</b> and <b>usergroup</b>-- tables>
 to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a>
 or <a href="http://radius.innercite.com/">ICRADIUS</a>.
 <br><br>
index 4a1b308..1bb66ed 100644 (file)
@@ -192,6 +192,8 @@ sub insert {
     }
   }
 
+  # XXX shouldn't this update fixed values?
+
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
   '';
@@ -736,11 +738,6 @@ sub process {
   my $old = qsearchs('part_svc', { 'svcpart' => $param->{'svcpart'} }) 
     if $param->{'svcpart'};
 
-  $param->{'svc_acct__usergroup'} =
-    ref($param->{'svc_acct__usergroup'})
-      ? join(',', @{$param->{'svc_acct__usergroup'}} )
-      : $param->{'svc_acct__usergroup'};
-
   #unmunge cgp_accessmodes (falze laziness-ish w/edit/process/svc_acct.cgi)
   $param->{'svc_acct__cgp_accessmodes'} ||=
     join(' ', sort
@@ -759,14 +756,17 @@ sub process {
     } ( fields('part_svc'),
         map { my $svcdb = $_;
               my @fields = fields($svcdb);
-              push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
+              push @fields, 'usergroup' if $svcdb eq 'svc_acct'
+                                        or $svcdb eq 'svc_broadband'; #kludge
 
               map {
                     my $f = $svcdb.'__'.$_;
-                    if ( $param->{ $f.'_flag' } =~ /^[MAH]$/ ) {
+                    my $flag = $param->{ $f.'_flag' } || ''; #silence warnings
+                    if ( $flag =~ /^[MAH]$/ ) {
                       $param->{ $f } = delete( $param->{ $f.'_classnum' } );
                     }
-                   if ( $param->{ $f.'_flag' } =~ /^S$/ ) {
+                   if ( $flag =~ /^S$/ 
+                          or $_ eq 'usergroup' ) {
                       $param->{ $f } = ref($param->{ $f })
                                          ? join(',', @{$param->{ $f }} )
                                          : $param->{ $f };
index 2de1423..8085fe8 100644 (file)
@@ -96,25 +96,29 @@ and replace methods.
 
 sub check {
   my $self = shift;
-
+  my $svcnum = $self->svcnum;
   die "radius_usergroup.groupname is deprecated" if $self->groupname;
 
   $self->ut_numbern('usergroupnum')
-    || $self->ut_foreign_key('svcnum','svc_acct','svcnum')
+    || ( $self->ut_foreign_key('svcnum','svc_acct','svcnum')
+      && $self->ut_foreign_key('svcnum','svc_broadband','svcnum')
+      && "Can't find radius_usergroup.svcnum $svcnum in svc_acct.svcnum or svc_broadband.svcnum" ) 
     || $self->ut_foreign_key('groupnum','radius_group','groupnum')
     || $self->SUPER::check
   ;
 }
 
-=item svc_acct
+=item svc_x
 
-Returns the account associated with this record (see L<FS::svc_acct>).
+Returns the account associated with this record (see L<FS::svc_acct> and 
+L<FS::svc_broadband>).
 
 =cut
 
 sub svc_acct {
   my $self = shift;
-  qsearchs('svc_acct', { svcnum => $self->svcnum } );
+  qsearchs('svc_acct', { svcnum => $self->svcnum } ) ||
+  qsearchs('svc_broadband', { svcnum => $self->svcnum } )
 }
 
 =item radius_group
diff --git a/FS/FS/svc_Radius_Mixin.pm b/FS/FS/svc_Radius_Mixin.pm
new file mode 100644 (file)
index 0000000..3a5ce35
--- /dev/null
@@ -0,0 +1,94 @@
+package FS::svc_Radius_Mixin;
+
+use strict;
+use base qw(FS::m2m_Common FS::svc_Common);
+use FS::Record qw(qsearch);
+use FS::radius_group;
+use FS::radius_usergroup;
+use Carp qw(confess);
+
+=head1 NAME
+
+FS::svc_Radius_Mixin - partial base class for services with RADIUS groups
+
+=cut
+
+
+sub insert {
+  my $self = shift;
+  $self->SUPER::insert(@_)
+  || $self->process_m2m(
+    'link_table' => 'radius_usergroup',
+    'target_table' => 'radius_group',
+    'params' => $self->usergroup,
+  );
+}
+
+sub replace  {
+  my $new = shift;
+  my $old = shift;
+  $old = $new->replace_old if !defined($old);
+
+  $old->usergroup; # make sure this is cached for exports
+  $new->process_m2m(
+    'link_table' => 'radius_usergroup',
+    'target_table' => 'radius_group',
+    'params' => $new->usergroup,
+  ) || $new->SUPER::replace($old, @_);
+}
+
+sub delete {
+  my $self = shift;
+  $self->SUPER::delete(@_)
+  || $self->process_m2m(
+    'link_table' => 'radius_usergroup',
+    'target_table' => 'radius_group',
+    'params' => [],
+  );
+}
+
+sub usergroup {
+  my $self = shift;
+  my $value = shift;
+  if ( defined $value ) {
+    if ( ref $value ) {
+      return $self->set('usergroup', $value);
+    }
+    else {
+      return $self->set('usergroup', [ split(/\s*,\s*/, $value) ]);
+    }
+  }
+  $self->get('usergroup') || 
+    # if no argument is passed and usergroup is not set already, 
+    # fetch this service's group assignments
+  $self->set('usergroup', 
+    [ map { $_->groupnum } 
+        qsearch('radius_usergroup', { svcnum => $self->svcnum }) ]
+  );
+}
+
+sub _fieldhandlers {
+  { 
+    'usergroup' => \&usergroup
+  }
+}
+
+=item radius_groups METHOD
+
+Returns a list of RADIUS groups for this service (see L<FS::radius_usergroup>).
+METHOD is the field to return, and can be any method on L<FS::radius_group>.
+Useful values for METHOD include 'groupnum', 'groupname', and 
+'long_description'.  Defaults to 'groupname' for historical reasons.
+
+=cut
+
+sub radius_groups {
+  my $self = shift;
+  my $method = shift || 'groupname';
+  my $groups = join(',', @{$self->usergroup}) || return ();
+  my @groups = qsearch({'table' => 'radius_group',
+                        'extra_sql' => "where groupnum in ($groups)"});
+  return map {$_->$method} @groups;
+}
+
+1;
index 37c55d6..12d3b8e 100644 (file)
@@ -1,7 +1,10 @@
 package FS::svc_acct;
 
 use strict;
-use base qw( FS::svc_Domain_Mixin FS::svc_CGP_Mixin FS::svc_CGPRule_Mixin
+use base qw( FS::svc_Domain_Mixin
+             FS::svc_CGP_Mixin
+             FS::svc_CGPRule_Mixin
+             FS::svc_Radius_Mixin
              FS::svc_Common );
 use vars qw( $DEBUG $me $conf $skip_fuzzyfiles
              $dir_prefix @shells $usernamemin
@@ -339,6 +342,7 @@ sub table_info {
                          type  => 'select-radius_group.html',
                          disable_inventory => 1,
                          disable_select => 1,
+                         multiple => 1,
                        },
         'seconds'   => { label => 'Seconds',
                          label_sort => 'with Time Remaining',
@@ -531,22 +535,6 @@ sub table { 'svc_acct'; }
 
 sub table_dupcheck_fields { ( 'username', 'domsvc' ); }
 
-sub _fieldhandlers {
-  {
-    #false laziness with edit/svc_acct.cgi
-    'usergroup' => sub { 
-                         my( $self, $groups ) = @_;
-                         if ( ref($groups) eq 'ARRAY' ) {
-                           $groups;
-                         } elsif ( length($groups) ) {
-                           [ split(/\s*,\s*/, $groups) ];
-                         } else {
-                           [];
-                         }
-                       },
-  };
-}
-
 sub last_login {
   shift->_lastlog('in', @_);
 }
@@ -699,7 +687,7 @@ sub insert {
   my $dbh = dbh;
 
   my @jobnums;
-  my $error = $self->SUPER::insert(
+  my $error = $self->SUPER::insert( # usergroup is here
     'jobnums'       => \@jobnums,
     'child_objects' => $self->child_objects,
     %options,
@@ -709,20 +697,6 @@ sub insert {
     return $error;
   }
 
-  if ( $self->usergroup ) {
-    foreach my $groupnum ( @{$self->usergroup} ) {
-      my $radius_usergroup = new FS::radius_usergroup ( {
-        svcnum    => $self->svcnum,
-        groupnum  => $groupnum,
-      } );
-      my $error = $radius_usergroup->insert;
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return $error;
-      }
-    }
-  }
-
   unless ( $skip_fuzzyfiles ) {
     $error = $self->queue_fuzzyfiles_update;
     if ( $error ) {
@@ -935,22 +909,12 @@ sub delete {
     }
   }
 
-  my $error = $self->SUPER::delete;
+  my $error = $self->SUPER::delete; # usergroup here
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
   }
 
-  foreach my $radius_usergroup (
-    qsearch('radius_usergroup', { 'svcnum' => $self->svcnum } )
-  ) {
-    my $error = $radius_usergroup->delete;
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return $error;
-    }
-  }
-
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 }
@@ -1011,49 +975,7 @@ sub replace {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  # redundant, but so $new->usergroup gets set
-  $error = $new->check;
-  return $error if $error;
-
-  $old->usergroup( [ $old->radius_groups('NUMBERS') ] );
-  if ( $DEBUG ) {
-    warn $old->email. " old groups: ". join(' ',@{$old->usergroup}). "\n";
-    warn $new->email. " new groups: ". join(' ',@{$new->usergroup}). "\n";
-  }
-  if ( $new->usergroup ) {
-    #(sorta) false laziness with FS::part_export::sqlradius::_export_replace
-    my @newgroups = @{$new->usergroup};
-    foreach my $oldgroup ( @{$old->usergroup} ) {
-      if ( grep { $oldgroup eq $_ } @newgroups ) {
-        @newgroups = grep { $oldgroup ne $_ } @newgroups;
-        next;
-      }
-      my $radius_usergroup = qsearchs('radius_usergroup', {
-        svcnum    => $old->svcnum,
-        groupnum  => $oldgroup,
-      } );
-      my $error = $radius_usergroup->delete;
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return "error deleting radius_usergroup $oldgroup: $error";
-      }
-    }
-
-    foreach my $newgroup ( @newgroups ) {
-      my $radius_usergroup = new FS::radius_usergroup ( {
-        svcnum    => $new->svcnum,
-        groupnum => $newgroup,
-      } );
-      my $error = $radius_usergroup->insert;
-      if ( $error ) {
-        $dbh->rollback if $oldAutoCommit;
-        return "error adding radius_usergroup $newgroup: $error";
-      }
-    }
-
-  }
-
-  $error = $new->SUPER::replace($old, @_);
+  $error = $new->SUPER::replace($old, @_); # usergroup here
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error if $error;
@@ -1191,15 +1113,10 @@ sub check {
 
   my($recref) = $self->hashref;
 
-  my $x = $self->setfixed( $self->_fieldhandlers );
+  my $x = $self->setfixed;
   return $x unless ref($x);
   my $part_svc = $x;
 
-  if ( $part_svc->part_svc_column('usergroup')->columnflag eq "F" ) {
-    $self->usergroup(
-      [ split(',', $part_svc->part_svc_column('usergroup')->columnvalue) ] );
-  }
-
   my $error = $self->ut_numbern('svcnum')
               #|| $self->ut_number('domsvc')
               || $self->ut_foreign_key( 'domsvc', 'svc_domain', 'svcnum' )
@@ -2549,41 +2466,7 @@ sub get_cdrs {
 
 }
 
-=item radius_groups
-
-Returns all RADIUS groups for this account (see L<FS::radius_usergroup>).
-
-=cut
-
-sub radius_groups {
-  my $self = shift;
-  if ( $self->usergroup ) {
-    confess "explicitly specified usergroup not an arrayref: ". $self->usergroup
-      unless ref($self->usergroup) eq 'ARRAY';
-    #when provisioning records, export callback runs in svc_Common.pm before
-    #radius_usergroup records can be inserted...
-    my $groups = join(',',@{$self->usergroup});
-    my @groups;
-    return @groups unless length($groups);
-    @groups = qsearch({ 'table'         => 'radius_group',
-                           'extra_sql'     => "where groupnum in ($groups)",
-                        });
-    map { $_->groupname } @groups;
-  } else {
-     my $format = shift || '';
-     my @groups = qsearch({ 'table'         => 'radius_usergroup',
-                            'addl_from'     => 'left join radius_group using (groupnum)',
-                            'select'        => 'radius_group.*',
-                            'hashref'       => { 'svcnum' => $self->svcnum },
-                        });
-
-     # this is to preserve various legacy behaviour / avoid re-writing other code
-     return map { $_->groupnum } @groups if $format eq 'NUMBERS';
-     return map { $_->description . " (" . $_->groupname . ")" } @groups
-        if $format eq 'COMBINED';
-     map { $_->groupname } @groups;
-  }
-}
+# sub radius_groups has moved to svc_Radius_Mixin
 
 =item clone_suspended
 
index 576684c..ad7dedc 100755 (executable)
@@ -9,7 +9,7 @@ use FS::addr_block;
 use FS::part_svc_router;
 use NetAddr::IP;
 
-@ISA = qw( FS::svc_Common );
+@ISA = qw( FS::svc_Radius_Mixin FS::svc_Common );
 
 $FS::UID::callback{'FS::svc_broadband'} = sub { 
   $conf = new FS::Conf;
@@ -115,6 +115,15 @@ sub table_info {
      'longitude'    => 'Longitude',
      'altitude'     => 'Altitude',
      'vlan_profile' => 'VLAN profile',
+     'usergroup'    => { 
+                         label => 'RADIUS groups',
+                         type  => 'select-radius_group.html',
+                         #select_table => 'radius_group',
+                         #select_key   => 'groupnum',
+                         #select_label => 'groupname',
+                         disable_inventory => 1,
+                         multiple => 1,
+                       },
     },
   };
 }
index a77bad6..c8da60a 100755 (executable)
@@ -19,14 +19,14 @@ adminsuidsetup $user;
 my @exports = ();
 if ( @ARGV ) {
   foreach my $exportnum ( @ARGV ) {
-    foreach my $exporttype (qw( sqlradius sqlradius_withdomain phone_sqlradius )) {
+    foreach my $exporttype (qw( sqlradius sqlradius_withdomain ohone_sqlradius broadband_sqlradius )) {
     push @exports, qsearch('part_export', { exportnum  => $exportnum,
                                             exporttype => $exporttype, } );
     }
   }
  } else {
   @exports = qsearch('part_export', { exporttype=>'sqlradius' } );
-  push @exports, qsearch('part_export', { exporttype=>'sqlradius_withdomain' } );
+  push @exports, qsearch('part_export', { exporttype=>'sqlradius_withdomain' } ); 
 }
 
 unless ( $opt_n ) {
@@ -34,7 +34,8 @@ unless ( $opt_n ) {
     my $icradius_dbh = DBI->connect(
       map { $export->option($_) } qw( datasrc username password )
     ) or die $DBI::errstr;
-    for my $table (qw( radcheck radreply usergroup )) {
+    my $usergroup = $export->option('usergroup') || 'usergroup';
+    for my $table (qw( radcheck radreply ), $usergroup) {
       my $sth = $icradius_dbh->prepare("DELETE FROM $table");
       $sth->execute or die "Can't reset $table table: ". $sth->errstr;
     }
index 4549e44..1cd0943 100755 (executable)
@@ -60,12 +60,14 @@ function part_export_areyousure(href) {
     <TH COLSPAN=2 CLASS="grid" BGCOLOR="#cccccc">Modifier</TH>
 
   </TR>
-
+% my $conf = FS::Conf->new;
 % foreach my $part_svc ( @part_svc ) {
 %     my $svcdb = $part_svc->svcdb;
 %     my $svc_x = "FS::$svcdb"->new( { svcpart => $part_svc->svcpart } );
 %     my @dfields = $svc_x->fields;
-%     push @dfields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge
+%     push @dfields, 'usergroup' if $svcdb eq 'svc_acct' #double kludge
+%                                or ($svcdb eq 'svc_broadband' 
+%                                    and $conf->exists('svc_broadband-radius'));
 %     my @fields =
 %       grep { my $col = $part_svc->part_svc_column($_);
 %              my $def = FS::part_svc->svc_table_fields($svcdb)->{$_};
index d5c9820..36c64d1 100644 (file)
@@ -1,8 +1,20 @@
-<% include('elements/svc_Common.html', 'table' => 'svc_broadband') %>
+<& elements/svc_Common.html,
+  table       => 'svc_broadband',
+  fields      => [ fields('svc_broadband'), 'usergroup' ],
+  precheck_callback => \&precheck,
+&>
 <%init>
+# for historical reasons, process_m2m for usergroup tables is done 
+# in the svc_x::insert/replace/delete methods, not here
 my $curuser = $FS::CurrentUser::CurrentUser;
 
 die "access denied"
   unless $curuser->access_right('Provision customer service'); #something else more specific?
 
+sub precheck {
+  my $cgi = shift;
+  $cgi->param("usergroup", [ $cgi->param('usergroup') ]);
+  ''
+}
+
 </%init>
index 61058ae..52fbd37 100755 (executable)
@@ -312,7 +312,7 @@ function randomPass() {
 % } else { 
 %   my $radius_group_selected = '';
 %   if ( $svc_acct->svcnum ) {
-%      $radius_group_selected = join(',',$svc_acct->radius_groups('NUMBERS'));
+%      $radius_group_selected = join(',',$svc_acct->radius_groups('groupnum'));
 %   }
 %   elsif ( !$svc_acct->svcnum && $part_svc_usergroup->columnflag eq 'D' ) {
 %       $radius_group_selected = $part_svc_usergroup->columnvalue;
@@ -320,6 +320,7 @@ function randomPass() {
     <TD><& /elements/select-radius_group.html, 
                 curr_value => $radius_group_selected,
                 element_name => 'radius_usergroup',
+                multiple => 1,
         &>
     </TD>
 % } 
index a67f6f0..d4baf35 100644 (file)
@@ -1,4 +1,4 @@
-<% include('elements/svc_Common.html',
+<& elements/svc_Common.html,
      'post_url'             => popurl(1). 'process/svc_broadband.cgi',
      'name'                 => 'broadband service',
      'table'                => 'svc_broadband',
@@ -7,8 +7,7 @@
      'dummy'                => $cgi->query_string,
      'onsubmit'             => 'validate_coords',
      'html_foot'            => $js,
-     )
-%>
+&>
 <%init>
 
 die "access denied"
@@ -100,9 +99,19 @@ END
 my @fields = (
   qw( description ip_addr speed_down speed_up blocknum ),
   { field=>'block_label', type=>'fixed' },
-  qw( mac_addr latitude longitude altitude vlan_profile performance_profile authkey plan_id )
+  qw( mac_addr latitude longitude altitude vlan_profile 
+      performance_profile authkey plan_id ),
 );
 
+if ( $conf->exists('svc_broadband-radius') ) {
+  push @fields,
+  { field     => 'usergroup',
+    type      => 'select-radius_group',
+    multiple  => 1,
+  }
+}
+
+
 my $fixedblock = '';
 
 my $callback = sub {
@@ -116,10 +125,17 @@ my $callback = sub {
 
   my $columndef = $part_svc->part_svc_column($fieldref->{'field'});
   if ($columndef->columnflag eq 'F') {
-    $fieldref->{'type'} = 'fixed';
+    $fieldref->{'type'} = length($columndef->columnvalue)
+                            ? 'fixed'
+                            : 'hidden';
     $fieldref->{'value'} = $columndef->columnvalue;
     $fixedblock = $fieldref->{value}
       if $fieldref->{field} eq 'blocknum';
+
+    if ( $fieldref->{field} eq 'usergroup' ) {
+      $fieldref->{'formatted_value'} = 
+        [ $object->radius_groups('long_description') ];
+    }
   }
 
   if ($object->svcnum) { 
index e1e3c59..06972b5 100644 (file)
@@ -1,17 +1,6 @@
-<SELECT MULTIPLE NAME = "<% $opt{'element_name'} || $opt{'field'} || 'usergroup' %>"
-        <% $opt{'element_etc'} %>
->
-% foreach my $selopt ( keys %groups ) {
-%  my $selected = (grep{ $_ eq $selopt } @sel_groups) ? 'SELECTED' : '';
-    <OPTION VALUE="<%$selopt%>" <% $selected %>><% $groups{$selopt} %></OPTION>
-% }
-</SELECT>
-<%init>
-
-my %opt = @_;
-
-my %groups = map { $_->groupnum => $_->long_description }
-                                                    qsearch('radius_group', {});
-my @sel_groups = split(/,/,$opt{'curr_value'});
-
-</%init>
+<& /elements/select-table.html,
+    table       => 'radius_group',
+    name_col    => 'long_description',
+    order_by    => 'groupname', # better idea?
+    @_ 
+&>
index 095e1bc..f358343 100644 (file)
@@ -1,6 +1,6 @@
 <% include('tr-td-label.html', @_ ) %>
 
-  <TD BGCOLOR="#dddddd" <% $style %>><% $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'} |h %></TD>
+  <TD BGCOLOR="#dddddd" <% $style %>><% $value %></TD>
 
 </TR>
 
@@ -12,4 +12,14 @@ my %opt = @_;
 
 my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
 
+my $value = $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'};
+#compatibility with select-table and friends
+if ( $opt{'multiple'} ) {
+  $value = [ split(/\s*,\s*/, $value) ] if !ref $value;
+  $value = join('<BR>', map {encode_entities($_)} @$value);
+}
+else {
+  $value = encode_entities($value)
+}
+
 </%init>
diff --git a/httemplate/elements/tr-select-radius_group.html b/httemplate/elements/tr-select-radius_group.html
new file mode 100644 (file)
index 0000000..2992527
--- /dev/null
@@ -0,0 +1,11 @@
+<% include('tr-td-label.html', label => emt('RADIUS groups'), %opt ) %>
+  <TD <% $style %>>
+  <% include( '/elements/select-radius_group.html', %opt ) %>
+  </TD>
+</TR>
+<%init>
+
+my( %opt ) = @_;
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
index 38fa90e..a822412 100644 (file)
@@ -159,13 +159,13 @@ my($label, $value, $svcdb) = $cust_svc->label;
 
 my $part_svc = $cust_svc->part_svc;
 
-  #false laziness w/edit/svc_Common.html
-  #override default labels with service-definition labels if applicable
-  my $labels = $opt{labels}; #not -> here
-  foreach my $field ( keys %$labels ) {
-    my $col = $part_svc->part_svc_column($field);
-    $labels->{$field} = $col->columnlabel if $col->columnlabel !~ /^\S*$/;
-  }
+#false laziness w/edit/svc_Common.html
+#override default labels with service-definition labels if applicable
+my $labels = $opt{labels}; #not -> here
+foreach my $field ( keys %$labels ) {
+  my $col = $part_svc->part_svc_column($field);
+  $labels->{$field} = $col->columnlabel if $col->columnlabel !~ /^\s*$/;
+}
 
 my $pkgnum = $cust_svc->pkgnum;
 
index 6a0ed92..a253e3b 100644 (file)
 % } 
 
 <& /view/elements/tr.html, label=>mt('RADIUS groups'),
-                      value=>join('<BR>', $svc_acct->radius_groups('COMBINED')) &>
+    value=>join('<BR>', $svc_acct->radius_groups('long_description')) &>
 
 %# Can this be abstracted further?  Maybe a library function like
 %# widget('HTML', 'view', $svc_acct) ?  It would definitely make UI 
index dead70b..6277759 100644 (file)
-<%include("/elements/header.html",'Broadband Service View', menubar(
-  ( ( $custnum )
-    ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
-      )                                                                       
-    : ( "Cancel this (unaudited) website" =>
-          "${p}misc/cancel-unaudited.cgi?$svcnum" )
-  )
-))
-%>
-
-<% include('/elements/init_overlib.html') %>
-
-<% include('/view/elements/svc_edit_link.html', 'svc'=>$svc_broadband) %>
-<BR>
-<%ntable("#cccccc")%>
-  <TR>
-    <TD>
-      <%ntable("#cccccc",2)%>
-        <TR>
-          <TD ALIGN="right">Service number</TD>
-          <TD BGCOLOR="#ffffff"><%$svcnum%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">Description</TD>
-          <TD BGCOLOR="#ffffff"><%$description%></TD>
-        </TR>
-
-%       if ( $router ) {
-          <TR>
-            <TD ALIGN="right">Router</TD>
-            <TD BGCOLOR="#ffffff"><%$router->routernum%>: <%$router->routername%></TD>
-          </TR>
-%       }
-
-        <TR>
-          <TD ALIGN="right">Download Speed</TD>
-          <TD BGCOLOR="#ffffff"><%$speed_down%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">Upload Speed</TD>
-          <TD BGCOLOR="#ffffff"><%$speed_up%></TD>
-        </TR>
-
-%       if ( $ip_addr ) { 
-          <TR>
-            <TD ALIGN="right">IP Address</TD>
-            <TD BGCOLOR="#ffffff">
-              <%$ip_addr%>
-              (<% include('/elements/popup_link-ping.html', 'ip'=>$ip_addr ) %>)
-            </TD>
-          </TR>
-          <TR>
-            <TD ALIGN="right">IP Netmask</TD>
-            <TD BGCOLOR="#ffffff"><%$addr_block->NetAddr->mask%></TD>
-          </TR>
-          <TR>
-            <TD ALIGN="right">IP Gateway</TD>
-            <TD BGCOLOR="#ffffff"><%$addr_block->ip_gateway%></TD>
-          </TR>
-%       }
-
-        <TR>
-          <TD ALIGN="right">MAC Address</TD>
-          <TD BGCOLOR="#ffffff"><%$mac_addr%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">Latitude</TD>
-          <TD BGCOLOR="#ffffff"><%$latitude%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">Longitude</TD>
-          <TD BGCOLOR="#ffffff"><%$longitude%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">Altitude</TD>
-          <TD BGCOLOR="#ffffff"><%$altitude%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">VLAN Profile</TD>
-          <TD BGCOLOR="#ffffff"><%$vlan_profile%></TD>
-        </TR>
-        <TR>
-          <TD ALIGN="right">Authentication Key</TD>
-          <TD BGCOLOR="#ffffff"><%$auth_key%></TD>
-        </TR>
-       <TR>
-          <TD ALIGN="right">Service Plan Id</TD>
-          <TD BGCOLOR="#ffffff"><%$plan_id%></TD>
-        </TR>
-        <TR COLSPAN="2"><TD></TD></TR>
-%
-%foreach (sort { $a cmp $b } $svc_broadband->virtual_fields) {
-%  print $svc_broadband->pvf($_)->widget('HTML', 'view',
-%                                        $svc_broadband->getfield($_)), "\n";
-%}
-%
-%
-
-      </TABLE>
-    </TD>
-  </TR>
-</TABLE>
-
-<BR>
-<%ntable("#cccccc", 2)%>
-%
-%  my $sb_router = qsearchs('router', { svcnum => $svcnum });
-%  if ($sb_router) {
-%  
-
-  <B>Router associated: <%$sb_router->routername%> </B>
-  <A HREF="<%popurl(2)%>edit/router.cgi?<%$sb_router->routernum%>">
-    (details)
-  </A>
-  <BR>
-% my @sb_addr_block;
-%     if (@sb_addr_block = $sb_router->addr_block) {
-%     
-
-  <B>Address space </B>
-  <A HREF="<%popurl(2)%>browse/addr_block.cgi">
-    (edit)
-  </A>
-  <BR>
-%   print ntable("#cccccc", 1);
-%       foreach (@sb_addr_block) { 
-
-    <TR>
-      <TD><%$_->ip_gateway%>/<%$_->ip_netmask%></TD>
-    </TR>
-% } 
-
-  </TABLE>
-% } else { 
-
-  <B>No address space allocated.</B>
-% } 
-
-  <BR>
-%
-%  } else {
-%
-
-
-<FORM METHOD="GET" ACTION="<%popurl(2)%>edit/router.cgi">
-  <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
-Add router named 
-  <INPUT TYPE="text" NAME="routername" SIZE="32" VALUE="Broadband router (<%$svcnum%>)">
-  <INPUT TYPE="submit" VALUE="Add router">
-</FORM>
-%
-%}
-%
-
-
-<BR>
-<%joblisting({'svcnum'=>$svcnum}, 1)%>
-
-<% include('/elements/footer.html') %>
+<& elements/svc_Common.html,
+  table   => 'svc_broadband',
+  labels  => \%labels,
+  fields  => \@fields,
+&>
 <%init>
 
-die "access denied"
-  unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
-
-my($query) = $cgi->keywords;
-$query =~ /^(\d+)$/;
-my $svcnum = $1;
-my $svc_broadband = qsearchs({
-  'select'    => 'svc_broadband.*',
-  'table'     => 'svc_broadband',
-  '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' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql(
-                            'null_right' => 'View/link unlinked services'
-                          ),
-}) or die "svc_broadband: Unknown svcnum $svcnum";
-
-#false laziness w/all svc_*.cgi
-my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } );
-my $pkgnum = $cust_svc->getfield('pkgnum');
-my($cust_pkg, $custnum, $display_custnum);
-if ($pkgnum) {
-  $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
-  $custnum = $cust_pkg->custnum;
-  $display_custnum = $cust_pkg->cust_main->display_custnum;
-} else {
-  $cust_pkg = '';
-  $custnum = '';
+my $conf = FS::Conf->new;
+my $fields = FS::svc_broadband->table_info->{'fields'};
+my %labels = map { $_ => ( ref($fields->{$_}) 
+                            ? $fields->{$_}{'label'} 
+                            : $fields->{$_}
+                          );
+                 } keys %$fields;
+
+$labels{'router'} = emt('Router');
+$labels{'usergroup'} = emt('RADIUS groups'); #?
+
+my @fields = (
+  'description',
+  { field => 'router', value => \&router },
+  'speed_down',
+  'speed_up',
+  { field => 'ip_addr', value => \&ip_addr },
+  'mac_addr',
+  'latitude',
+  'longitude',
+  'altitude',
+  'vlan_profile',
+  'authkey',
+  'plan_id',
+);
+
+push @fields,
+  { field => 'usergroup', value => \&usergroup }
+  if $conf->exists('svc_broadband-radius');
+
+sub router {
+  my $svc = shift;
+  my $addr_block = $svc->addr_block or return '';
+  my $router = $addr_block->router or return '';
+  $router->routernum . ': ' . $router->routername;
 }
-#eofalse
 
-my $addr_block = $svc_broadband->addr_block;
-my $router = $addr_block->router if $addr_block;
-
-#if (not $router) { die "Could not lookup router for svc_broadband (svcnum $svcnum)" };
+sub ip_addr {
+  my $svc = shift;
+  my $ip_addr = $svc->ip_addr;
+  my $out = $ip_addr . ' (' . 
+    include('/elements/popup_link-ping.html', ip => $ip_addr) . ')';
+  if ( my $addr_block = $svc->addr_block ) {
+    $out .= '<br>Netmask: ' . $addr_block->NetAddr->mask .
+            '<br>Gateway: ' . $addr_block->ip_gateway;
+  }
+  $out;
+}
 
-my (
-     $speed_down,
-     $speed_up,
-     $ip_addr,
-     $mac_addr,
-     $latitude,
-     $longitude,
-     $altitude,
-     $vlan_profile,
-     $auth_key,
-     $description,
-     $plan_id,
-   ) = (
-     $svc_broadband->getfield('speed_down'),
-     $svc_broadband->getfield('speed_up'),
-     $svc_broadband->getfield('ip_addr'),
-     $svc_broadband->mac_addr,
-     $svc_broadband->latitude,
-     $svc_broadband->longitude,
-     $svc_broadband->altitude,
-     $svc_broadband->vlan_profile,
-     $svc_broadband->auth_key,
-     $svc_broadband->description,
-     $svc_broadband->plan_id,
-   );
+sub usergroup {
+  my $svc = shift;
+  my $usergroup = $svc->usergroup;
+  join('<BR>', $svc->radius_groups('long_description'));
+}
 
 </%init>