communigate pro provisioning, RT#7083
authorivan <ivan>
Wed, 17 Feb 2010 08:32:53 +0000 (08:32 +0000)
committerivan <ivan>
Wed, 17 Feb 2010 08:32:53 +0000 (08:32 +0000)
FS/FS/Conf.pm
FS/FS/Schema.pm
FS/FS/cust_svc.pm
FS/FS/part_export.pm
FS/FS/part_export/communigate_pro.pm
FS/FS/svc_Common.pm
FS/FS/svc_domain.pm
httemplate/edit/process/svc_domain.cgi
httemplate/edit/svc_domain.cgi
httemplate/view/svc_domain.cgi

index 0bb59dc..f4f70f5 100644 (file)
@@ -3539,6 +3539,13 @@ worry that config_items is freeside-specific and icky.
     'type'        => 'checkbox',
   },
 
+  {
+    'key'         => 'svc_domain-edit_domain',
+    'section'     => '',
+    'description' => 'Enable domain renaming',
+    'type'        => 'checkbox',
+  },
+
   { key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
   { key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
   { key => "apachemachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
index fdb4a94..6a25fab 100644 (file)
@@ -1673,9 +1673,10 @@ sub tables_hashref {
        'parent_svcnum',    'int', 'NULL',       '', '', '',
        'registrarnum',     'int', 'NULL',       '', '', '',
        'registrarkey', 'varchar', 'NULL',      512, '', '',
-       'setup_date',  @date_type, '', '',
+       'setup_date',      @date_type, '', '',
        'renewal_interval', 'int', 'NULL',       '', '', '',
        'expiration_date', @date_type, '', '',
+        'max_accounts',     'int', 'NULL',       '', '', '',
       ],
       'primary_key' => 'svcnum',
       'unique' => [ ],
index 3c28204..3ce1314 100644 (file)
@@ -251,6 +251,18 @@ sub replace {
     }
   }
 
+#  #trigger a re-export on pkgnum changes?
+#  # (of prepaid packages), for Expiration RADIUS attribute
+#  if ( $new->pkgnum != $old->pkgnum && $new->cust_pkg->part_pkg->is_prepaid ) {
+#    my $svc_x = $new->svc_x;
+#    local($FS::Record::nowarn_identical) = 1;
+#    my $error = $svc_x->export('replace');
+#    if ( $error ) {
+#      $dbh->rollback if $oldAutoCommit;
+#      return $error if $error;
+#    }
+#  }
+
   #my $error = $new->SUPER::replace($old, @_);
   my $error = $new->SUPER::replace($old);
   if ( $error ) {
@@ -411,7 +423,7 @@ sub _svc_label {
 
 =item export_links
 
-Returns a list of html elements associated with this services exports.
+Returns a listref of html elements associated with this service's exports.
 
 =cut
 
@@ -423,6 +435,21 @@ sub export_links {
   $svc_x->export_links;
 }
 
+=item export_getsettings
+
+Returns two hashrefs of settings associated with this service's exports.
+
+=cut
+
+sub export_getsettings {
+  my $self = shift;
+  my $svc_x = $self->svc_x
+    or return "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum;
+
+  $svc_x->export_getsettings;
+}
+
+
 =item svc_x
 
 Returns the FS::svc_XXX object for this service (i.e. an FS::svc_acct object or
index d533db8..588606d 100644 (file)
@@ -376,6 +376,15 @@ Adds a list of web elements to ARRAYREF specific to this export and SVC_OBJECT.
 The elements are displayed in the UI to lead the the operator to external
 configuration, monitoring, and similar tools.
 
+=item export_getsettings SVC_OBJECT SETTINGS_HASHREF DEFAUTS_HASHREF
+
+Adds a hashref of settings to SETTINGSREF specific to this export and
+SVC_OBJECT.  The elements can be displayed in the UI on the service view.
+
+DEFAULTSREF is a hashref with the same keys where true values indicate the
+setting is a default (and thus can be displayed in the UI with less emphasis,
+or hidden by default).
+
 =cut
 
 =back
index ecb3780..c4201c0 100644 (file)
@@ -1,35 +1,43 @@
 package FS::part_export::communigate_pro;
 
-use vars qw(@ISA %info %options);
+use strict;
+use vars qw(@ISA %info %options $DEBUG);
+use Data::Dumper;
 use Tie::IxHash;
 use FS::part_export;
 use FS::queue;
 
 @ISA = qw(FS::part_export);
 
+$DEBUG = 1;
+
 tie %options, 'Tie::IxHash',
-  'port'     => { label=>'Port number', default=>'106', },
-  'login'    => { label=>'The administrator account name.  The name can contain a domain part.', },
-  'password' => { label=>'The administrator account password.', },
-  'accountType' => { label=>'Type for newly-created accounts',
-                     type=>'select',
-                     options=>[qw( MultiMailbox TextMailbox MailDirMailbox )],
-                     default=>'MultiMailbox',
-                   },
-  'externalFlag' => { label=> 'Create accounts with an external (visible for legacy mailers) INBOX.',
-                      type=>'checkbox',
-                    },
-  'AccessModes' => { label=>'Access modes',
-                     default=>'Mail POP IMAP PWD WebMail WebSite',
-                   },
+  'port'          => { label   =>'Port number', default=>'106', },
+  'login'         => { label   =>'The administrator account name.  The name can contain a domain part.', },
+  'password'      => { label   =>'The administrator account password.', },
+  'accountType'   => { label   => 'Type for newly-created accounts',
+                       type    => 'select',
+                       options => [qw(MultiMailbox TextMailbox MailDirMailbox)],
+                       default => 'MultiMailbox',
+                     },
+  'externalFlag'  => { label   => 'Create accounts with an external (visible for legacy mailers) INBOX.',
+                       type    => 'checkbox',
+                     },
+  'AccessModes'   => { label   => 'Access modes',
+                       default => 'Mail POP IMAP PWD WebMail WebSite',
+                     },
+  'create_domain' => { label   => 'Domain creation API call',
+                       type    => 'select',
+                       options => [qw( CreateDomain CreateSharedDomain )],
+                     }
 ;
 
 %info = (
-  'svc'     => 'svc_acct',
-  'desc'    => 'Real-time export to a CommuniGate Pro mail server',
+  'svc'     => [qw( svc_acct svc_domain )],
+  'desc'    => 'Real-time export of accounts and domains to a CommuniGate Pro mail server',
   'options' => \%options,
   'notes'   => <<'END'
-Real time export to a
+Real time export of accounts and domains to a
 <a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a>
 mail server.  The
 <a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a>
@@ -45,77 +53,199 @@ sub export_username {
 }
 
 sub _export_insert {
-  my( $self, $svc_acct ) = (shift, shift);
-  my @options = ( $svc_acct->svcnum, 'CreateAccount',
-    'accountName'    => $self->export_username($svc_acct),
-    'accountType'    => $self->option('accountType'),
-    'AccessModes'    => $self->option('AccessModes'),
-    'RealName'       => $svc_acct->finger,
-    'Password'       => $svc_acct->_password,
-  );
-  push @options, 'MaxAccountSize' => $svc_acct->quota if $svc_acct->quota;
-  push @options, 'externalFlag'   => $self->option('externalFlag')
-    if $self->option('externalFlag');
+  my( $self, $svc_x ) = (shift, shift);
+
+  my @options;
+
+  if ( $svc_x->isa('FS::svc_acct') ) {
+
+    @options = ( $svc_x->svcnum, 'CreateAccount',
+      'accountName'    => $self->export_username($svc_x),
+      'accountType'    => $self->option('accountType'),
+      'AccessModes'    => $self->option('AccessModes'),
+      'RealName'       => $svc_x->finger,
+      'Password'       => $svc_x->_password,
+    );
+    push @options, 'MaxAccountSize' => $svc_x->quota if $svc_x->quota;
+    push @options, 'externalFlag'   => $self->option('externalFlag')
+      if $self->option('externalFlag');
+
+  } elsif ( $svc_x->isa('FS::svc_domain') ) {
+
+    my $create = $self->option('create_domain') || 'CreateDomain';
+
+    @options = ( $svc_x->svcnum, $create, $svc_x->domain,
+      #other domain creation options?
+    );
+    push @options, 'AccountsLimit' => $svc_x->max_accounts
+      if $svc_x->max_accounts;
+
+  } else {
+    die "guru meditation #14: $svc_x is not FS::svc_acct, or FS::svc_domain";
+  }
 
   $self->communigate_pro_queue( @options );
 }
 
 sub _export_replace {
   my( $self, $new, $old ) = (shift, shift, shift);
-  return "can't (yet) change username with CommuniGate Pro"
-    if $old->username ne $new->username;
-  return "can't (yet) change domain with CommuniGate Pro"
-    if $self->export_username($old) ne $self->export_username($new);
-  return "can't (yet) change GECOS with CommuniGate Pro"
-    if $old->finger ne $new->finger;
-  return "can't (yet) change quota with CommuniGate Pro"
-    if $old->quota ne $new->quota;
-  return '' unless $old->username ne $new->username
-                || $old->_password ne $new->_password
-                || $old->finger ne $new->finger
-                || $old->quota ne $new->quota;
-
-  return '' if '*SUSPENDED* '. $old->_password eq $new->_password;
-
-  #my $err_or_queue = $self->communigate_pro_queue( $new->svcnum,'RenameAccount',
-  #  $old->email, $new->email );
-  #return $err_or_queue unless ref($err_or_queue);
-  #my $jobnum = $err_or_queue->jobnum;
-
-  $self->communigate_pro_queue( $new->svcnum, 'SetAccountPassword',
-                                $self->export_username($new), $new->_password        )
-    if $new->_password ne $old->_password;
+
+  if ( $new->isa('FS::svc_acct') ) {
+
+    #XXX they probably need the ability to change some of these
+    return "can't (yet) change username with CommuniGate Pro"
+      if $old->username ne $new->username;
+    return "can't (yet) change domain with CommuniGate Pro"
+      if $self->export_username($old) ne $self->export_username($new);
+    return "can't (yet) change GECOS with CommuniGate Pro"
+      if $old->finger ne $new->finger;
+    return "can't (yet) change quota with CommuniGate Pro"
+      if $old->quota ne $new->quota;
+    return '' unless $old->username ne $new->username
+                  || $old->_password ne $new->_password
+                  || $old->finger ne $new->finger
+                  || $old->quota ne $new->quota;
+
+    return '' if '*SUSPENDED* '. $old->_password eq $new->_password;
+
+    #my $err_or_queue = $self->communigate_pro_queue( $new->svcnum,'RenameAccount',
+    #  $old->email, $new->email );
+    #return $err_or_queue unless ref($err_or_queue);
+    #my $jobnum = $err_or_queue->jobnum;
+
+    $self->communigate_pro_queue( $new->svcnum, 'SetAccountPassword',
+                                  $self->export_username($new), $new->_password        )
+      if $new->_password ne $old->_password;
+
+  }  elsif ( $new->isa('FS::svc_domain') ) {
+
+    if ( $old->domain ne $new->domain ) {
+      $self->communigate_pro_queue( $new->svcnum, 'RenameDomain',
+        $old->domain, $new->domain,
+      );
+    }
+
+    if ( $old->max_accounts ne $new->max_accounts ) {
+      $self->communigate_pro_queue( $new->svcnum, 'UpdateDomainSettings',
+        $new->domain, 'AccountsLimit' => ($new->max_accounts || 'default'),
+      );
+    }
+
+    #other kinds of changes?
+
+  } else {
+    die "guru meditation #15: $new is not FS::svc_acct, or FS::svc_domain";
+  }
 
 }
 
 sub _export_delete {
-  my( $self, $svc_acct ) = (shift, shift);
-  $self->communigate_pro_queue( $svc_acct->svcnum, 'DeleteAccount',
-    $self->export_username($svc_acct),
-  );
+  my( $self, $svc_x ) = (shift, shift);
+
+  if ( $svc_x->isa('FS::svc_acct') ) {
+
+    $self->communigate_pro_queue( $svc_x->svcnum, 'DeleteAccount',
+      $self->export_username($svc_x),
+    );
+
+  } elsif ( $svc_x->isa('FS::svc_domain') ) {
+
+    $self->communigate_pro_queue( $svc_x->svcnum, 'DeleteDomain',
+      $svc_x->domain,
+      #XXX turn on force option for domain deletion?
+    );
+
+  } else {
+    die "guru meditation #16: $svc_x is not FS::svc_acct, or FS::svc_domain";
+  }
+
 }
 
 sub _export_suspend {
-  my( $self, $svc_acct ) = (shift, shift);
-  $self->communigate_pro_queue( $svc_acct->svcnum, 'UpdateAccountSettings',
-    'accountName' => $self->export_username($svc_acct),
-    'AccessModes' => 'Mail',
-  );
+  my( $self, $svc_x ) = (shift, shift);
+
+  if ( $svc_x->isa('FS::svc_acct') ) {
+
+     $self->communigate_pro_queue( $svc_x->svcnum, 'UpdateAccountSettings',
+      'accountName' => $self->export_username($svc_x),
+      'AccessModes' => 'Mail',
+    );
+
+  } elsif ( $svc_x->isa('FS::svc_domain') ) {
+
+    #XXX domain operations
+  } else {
+    die "guru meditation #17: $svc_x is not FS::svc_acct, or FS::svc_domain";
+  }
+
 }
 
 sub _export_unsuspend {
-  my( $self, $svc_acct ) = (shift, shift);
-  $self->communigate_pro_queue( $svc_acct->svcnum, 'UpdateAccountSettings',
-    'accountName' => $self->export_username($svc_acct),
-    'AccessModes' => $self->option('AccessModes'),
-  );
+  my( $self, $svc_x ) = (shift, shift);
+
+  if ( $svc_x->isa('FS::svc_acct') ) {
+
+    $self->communigate_pro_queue( $svc_x->svcnum, 'UpdateAccountSettings',
+      'accountName' => $self->export_username($svc_x),
+      'AccessModes' => $self->option('AccessModes'),
+    );
+  } elsif ( $svc_x->isa('FS::svc_domain') ) {
+
+    #XXX domain operations
+  } else {
+    die "guru meditation #18: $svc_x is not FS::svc_acct, or FS::svc_domain";
+  }
+
+}
+
+sub export_getsettings {
+  my($self, $svc_x, $settingsref, $defaultref ) = @_;
+
+  my $settings = eval { $self->communigate_pro_runcommand(
+    'GetDomainSettings',
+    $svc_x->domain
+  ) };
+  return $@ if $@;
+
+  my $effective_settings = eval { $self->communigate_pro_runcommand(
+    'GetDomainEffectiveSettings',
+    $svc_x->domain
+  ) };
+  return $@ if $@;
+
+  my %defaults = map { $_ => 1 }
+                   grep !exists(${$settings}{$_}), keys %$effective_settings;
+
+  foreach my $key ( grep ref($effective_settings->{$_}),
+                    keys %$effective_settings )
+  {
+    my $value = $effective_settings->{$key};
+    if ( ref($value) eq 'ARRAY' ) {
+      $effective_settings->{$key} = join(', ', @$value);
+    } else {
+      #XXX
+      warn "serializing ". ref($value). " for table display not yet handled";
+    }
+  }
+
+  %{$settingsref} = %$effective_settings;
+  %{$defaultref} = %defaults;
+
+  '';
 }
 
 sub communigate_pro_queue {
   my( $self, $svcnum, $method ) = (shift, shift, shift);
-  my @kludge_methods = qw(CreateAccount UpdateAccountSettings);
-  my $sub = 'communigate_pro_command';
-  $sub = $method if grep { $method eq $_ } @kludge_methods;
+  my %kludge_methods = (
+    'CreateAccount'         => 'CreateAccount',
+    'UpdateAccountSettings' => 'UpdateAccountSettings',
+    'CreateDomain'          => 'cp_Scalar_Hash',
+    'CreateSharedDomain'    => 'cp_Scalar_Hash',
+    'UpdateDomainSettings'  => 'UpdateDomainSettings',
+  );
+  my $sub = exists($kludge_methods{$method})
+              ? $kludge_methods{$method}
+              : 'communigate_pro_command';
   my $queue = new FS::queue {
     'svcnum' => $svcnum,
     'job'    => "FS::part_export::communigate_pro::$sub",
@@ -131,6 +261,41 @@ sub communigate_pro_queue {
 
 }
 
+sub communigate_pro_runcommand {
+  my( $self, $method ) = (shift, shift);
+
+  communigate_pro_command(
+    $self->machine,
+    $self->option('port'),
+    $self->option('login'),
+    $self->option('password'),
+    $method,
+    @_,
+  );
+
+}
+
+#XXX one sub per arg prototype is lame.  more magic?  i suppose queue needs
+# to store data strctures properly instead of just an arg list.  right.
+
+sub cp_Scalar_Hash {
+  my( $machine, $port, $login, $password, $method, $scalar, %hash ) = @_;
+  my @args = ( $scalar, \%hash );
+  communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+}
+
+#sub cp_Hash {
+#  my( $machine, $port, $login, $password, $method, %hash ) = @_;
+#  my @args = ( \%hash );
+#  communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+#}
+
+sub UpdateDomainSettings {
+  my( $machine, $port, $login, $password, $method, $domain, %settings ) = @_;
+  my @args = ( 'domain' => $domain, 'settings' => \%settings );
+  communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+}
+
 sub CreateAccount {
   my( $machine, $port, $login, $password, $method, %args ) = @_;
   my $accountName  = delete $args{'accountName'};
@@ -152,7 +317,7 @@ sub UpdateAccountSettings {
   my( $machine, $port, $login, $password, $method, %args ) = @_;
   my $accountName  = delete $args{'accountName'};
   $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
-  @args = ( $accountName, \%args );
+  my @args = ( $accountName, \%args );
   communigate_pro_command( $machine, $port, $login, $password, $method, @args );
 }
 
@@ -168,10 +333,15 @@ sub communigate_pro_command { #subroutine, not method
     'password' => $password,
   } ) or die "Can't login to CGPro: $CGP::ERR_STRING\n";
 
-  $cli->$method(@args) or die "CGPro error: ". $cli->getErrMessage;
+  #warn "$method ". Dumper(@args) if $DEBUG;
+
+  my $return = $cli->$method(@args)
+    or die "Communigate Pro error: ". $cli->getErrMessage;
 
   $cli->Logout; # or die "Can't logout of CGPro: $CGP::ERR_STRING\n";
 
+  $return;
+
 }
 
 1;
index 368e936..ee270ca 100644 (file)
@@ -854,6 +854,25 @@ sub export_links {
   $return;
 }
 
+=item export_getsettings
+
+Runs export_getsettings callbacks and returns the two hashrefs.
+
+=cut
+
+sub export_getsettings {
+  my $self = shift;
+  my %settings = ();
+  my %defaults = ();
+  my $error = $self->export('getsettings', \%settings, \%defaults);
+  if ( $error ) {
+    #XXX bubble this up better
+    warn "error running export_getsetings: $error";
+    return ( {}, {} );
+  }
+  ( \%settings, \%defaults );
+}
+
 =item export HOOK [ EXPORT_ARGS ]
 
 Runs the provided export hook (i.e. "suspend", "unsuspend") for this service.
index 8ca30c2..89ee26a 100644 (file)
@@ -89,6 +89,8 @@ FS::svc_Common.  The following fields are currently supported:
 
 =item expiration_date - UNIX timestamp
 
+=item max_accounts
+
 =back
 
 =head1 METHODS
@@ -109,6 +111,9 @@ sub table_info {
     'cancel_weight'  => 60,
     'fields' => {
       'domain' => 'Domain',
+      'max_accounts' => { label => 'Maximum number of accounts',
+                          'disable_inventory' => 1,
+                        },
     },
   };
 }
@@ -290,7 +295,8 @@ sub replace {
               : $new->replace_old;
 
   return "Can't change domain - reorder."
-    if $old->getfield('domain') ne $new->getfield('domain'); 
+    if $old->getfield('domain') ne $new->getfield('domain')
+    && ! $conf->exists('svc_domain-edit_domain'); 
 
   # Better to do it here than to force the caller to remember that svc_domain is weird.
   $new->setfield(action => 'I');
@@ -335,6 +341,7 @@ sub check {
 
   my $error = $self->ut_numbern('svcnum')
               || $self->ut_numbern('catchall')
+              || $self->ut_numbern('max_accounts')
   ;
   return $error if $error;
 
@@ -429,6 +436,7 @@ sub domain_record {
     'PTR'   => sub { $_[0]->reczone <=> $_[1]->reczone },
   );
 
+  map { $_ } #return $self->num_domain_record( PARAMS ) unless wantarray;
   sort {    $order{$a->rectype} <=> $order{$b->rectype}
          or &{ $sort{$a->rectype} || sub { 0; } }($a, $b)
        }
index 59b5180..d8c1a12 100755 (executable)
@@ -24,10 +24,10 @@ my $new = new FS::svc_domain ( {
 
 my $error = '';
 if ($cgi->param('svcnum')) {
-  $error="Can't modify a domain!";
+  $error  = $new->replace();
 } else {
-  $error=$new->insert;
-  $svcnum=$new->svcnum;
+  $error  = $new->insert;
+  $svcnum = $new->svcnum;
 }
 
 </%init>
index 10079ce..369d6a0 100755 (executable)
@@ -8,10 +8,18 @@
 <INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
 
 <% ntable("#cccccc",2) %>
+
 <TR>
-<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63>
-<BR>
+  <TD ALIGN="right">Domain</TD>
+  <TD>
+%   if ( !$svcnum || $conf->exists('svc_domain-edit_domain') ) {
+      <INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63>
+%   } else {
+      <B><% $domain %></B>
+%   }
+
 % if ($export) {
+<BR>
 Available top-level domains: <% $export->option('tlds') %>
 </TR>
 
@@ -27,11 +35,25 @@ Available top-level domains: <% $export->option('tlds') %>
 </TR>
 
 % }
-
-<TR>
-<P><INPUT TYPE="submit" VALUE="Submit">
+  </TD>
 </TR>
+
+% if ( $part_svc->part_svc_column('max_accounts')->columnflag =~ /^[FA]$/ ) {
+    <INPUT TYPE="hidden" NAME="max_accounts" VALUE="<% $svc_domain->max_accounts %>">
+% } else {
+    <TR>
+      <TD ALIGN="right">Maximum number of accounts</TD>
+      <TD>
+        <INPUT TYPE="text" NAME="max_accounts" SIZE=5 MAXLENGTH=6 VALUE="<% $svc_domain->max_accounts %>">
+      </TD>
+    </TR>
+% }
+
 </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Submit">
+
 </FORM>
 
 <% include('/elements/footer.html') %>
@@ -41,6 +63,8 @@ Available top-level domains: <% $export->option('tlds') %>
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific?
 
+my $conf = new FS::Conf;
+
 my($svcnum, $pkgnum, $svcpart, $kludge_action, $part_svc,
    $svc_domain);
 if ( $cgi->param('error') ) {
index a9fc775..3f2d473 100755 (executable)
@@ -1,19 +1,48 @@
-<% include("/elements/header.html",'Domain View', menubar(
-  ( ( $pkgnum || $custnum )
-    ? ( "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
-      )
-    : ( "Delete this (unaudited) domain" =>
-          "javascript:areyousure('${p}misc/cancel-unaudited.cgi?$svcnum', 'Delete $domain and all records?' )" )
-  )
-)) %>
+% if ( $custnum ) {
+
+%#  <% include("/elements/header.html","View $svcdomain") %>
+  <% include("/elements/header.html","View domain") %>
+  <% include( '/elements/small_custview.html', $custnum, '', 1,
+     "${p}view/cust_main.cgi") %>
+  <BR>
+
+% } else {
+
+  <% include("/elements/header.html",'View domain', menubar(
+       "Cancel this (unaudited) domain" =>
+         "javascript:areyousure('${p}misc/cancel-unaudited.cgi?$svcnum', 'Delete $domain and all records?')",
+     ))
+  %>
+
+% }
 
 <% include('/elements/error.html') %>
 
-Service #<% $svcnum %>
-<BR>Service: <B><% $part_svc->svc %></B>
-<BR>Domain name: <B><% $domain %></B>
+Service #<B><% $svcnum %></B>
+% #if ( $conf->exists('svc_domain-edit_domain') ) {
+  | <A HREF="<%$p%>edit/svc_domain.cgi?<%$svcnum%>">Edit this domain</A>
+% #}
+
+<% &ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
+
+<TR>
+  <TD ALIGN="right">Service</TD>
+  <TD BGCOLOR="#ffffff"><% $part_svc->svc %></TD>
+</TR>
+
+<TR>
+  <TD ALIGN="right">Domain</TD>
+  <TD BGCOLOR="#ffffff">
+    <B><% $domain %></B>
+    <A HREF="<% ${p} %>misc/whois.cgi?custnum=<%$custnum%>;svcnum=<%$svcnum%>;domain=<%$domain%>">(view whois information)</A>
+  </TD>
+</TR>
+
 % if ($export) {
-<BR>Status: <B><% $status %></B>
+  <TR>
+    <TD ALIGN="right">Registration status</TD>
+    <TD BGCOLOR="#ffffff"><B><% $status %></B>
+
 %   if ( $FS::CurrentUser::CurrentUser->access_right('Manage domain registration') ) {
 %     if ( defined($ops{'register'}) ) {
     <A HREF="<% ${p} %>edit/process/domreg.cgi?op=register&svcnum=<% $svcnum %>">Register at <% $registrar->{'name'} %></A>&nbsp;
@@ -28,17 +57,30 @@ Service #<% $svcnum %>
     <A HREF="<% ${p} %>edit/process/domreg.cgi?op=revoke&svcnum=<% $svcnum %>">Revoke</A>
 %     }
 %   }
+
+    </TD>
+  </TR>
+% }
+
+% if ( $svc_domain->max_accounts ) {
+  <TR>
+    <TD ALIGN="right">Maximum number of Accounts</TD>
+    <TD BGCOLOR="#ffffff"><% $svc_domain->max_accounts %></TD>
+  </TR>
 % }
 
+<TR>
+  <TD ALIGN="right">Catch all email</TD>
+  <TD BGCOLOR="#ffffff"><% $email ? "<B>$email</B>" : '<I>(none)</I>' %>
 % if ( $FS::CurrentUser::CurrentUser->access_right('Edit domain catchall') ) {
-    <BR>Catch all email <A HREF="<% ${p} %>misc/catchall.cgi?<% $svcnum %>">(change)</A>:
-% } else {
-    <BR>Catch all email:
+     <A HREF="<% ${p} %>misc/catchall.cgi?<% $svcnum %>">(change)</A>
 % }
+  </TD>
+</TR>
+
+</TABLE></TD></TR></TABLE>
+<BR>
 
-<% $email ? "<B>$email</B>" : "<I>(none)<I>" %>
-<BR><BR><A HREF="<% ${p} %>misc/whois.cgi?custnum=<%$custnum%>;svcnum=<%$svcnum%>;domain=<%$domain%>">View whois information.</A>
-<BR><BR>
 <SCRIPT>
   function areyousure(href, message) {
     if ( confirm(message) == true )
@@ -49,6 +91,7 @@ Service #<% $svcnum %>
   }
 </SCRIPT>
 
+DNS records
 % my @records; if ( @records = $svc_domain->domain_record ) { 
 
   <% include('/elements/table-grid.html') %>
@@ -96,7 +139,6 @@ Service #<% $svcnum %>
 % } 
 
 % if ( $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice') ) {
-    <BR>
     <FORM METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
       <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
       <INPUT TYPE="text" NAME="reczone"> 
@@ -110,16 +152,12 @@ Service #<% $svcnum %>
       <INPUT TYPE="submit" VALUE="Add record">
     </FORM>
 
-    <BR><BR>
-    or
-    <BR><BR>
-
     <FORM NAME="SlaveForm" METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
       <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
 %     if ( @records ) { 
          Delete all records and 
 %     } 
-      Slave from nameserver IP 
+      Or slave from nameserver IP 
       <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
       <INPUT TYPE="hidden" NAME="reczone" VALUE="@"> 
       <INPUT TYPE="hidden" NAME="recaf" VALUE="IN">
@@ -129,8 +167,30 @@ Service #<% $svcnum %>
     </FORM>
 
 % }
+<BR>
+
+% my ( $settings, $defaults ) = $svc_domain->export_getsettings;
+% if ( keys %$settings ) {
+
+%# XXX a way to label this "Communigate pro settings".. just a config maybe
+  External settings
+  <% ntable('#cccccc',2) %>
+
+%   foreach my $key ( keys %$settings ) {
+      <TR>
+        <TD ALIGN="right"><% $key |h %></TD>
+        <TD BGCOLOR="<% $defaults->{$key} ? '#eeeeee' : '#ffffff' %>">
+          <% $defaults->{$key} ? '<I>' : '<B>' %>
+          <% $settings->{$key} |h %>
+          <% $defaults->{$key} ? '</I>' : '</B>' %>
+        </TD>
+      </TR>
+%   }
 
-<BR><BR>
+  </TABLE>
+  <BR>
+
+% }
 
 <% joblisting({'svcnum'=>$svcnum}, 1) %>
 
@@ -140,6 +200,8 @@ Service #<% $svcnum %>
 die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('View customer services');
 
+my $conf = new FS::Conf;
+
 my($query) = $cgi->keywords;
 $query =~ /^(\d+)$/;
 my $svcnum = $1;