RADIUS groups for svc_broadband, #14695
authormark <mark>
Thu, 10 Nov 2011 21:40:05 +0000 (21:40 +0000)
committermark <mark>
Thu, 10 Nov 2011 21:40:05 +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)
@@ -2296,6 +2296,13 @@ and customer address. Include units.',
     'type'        => 'checkbox',
   },
 
+  {
+    'key'         => 'svc_broadband-radius',
+    'section'     => '',
+    'description' => 'Enable RADIUS groups for broadband services.',
+    'type'        => 'checkbox',
+  },
+
   {
     'key'         => 'svc_acct-alldomains',
     'section'     => '',
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 249a2b4..cc30dbb 100644 (file)
@@ -192,6 +192,8 @@ sub insert {
     }
   }
 
+  # XXX shouldn't this update fixed values?
+
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
   '';
@@ -714,11 +716,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
@@ -737,14 +734,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>