[freeside-commits] branch master updated. 6c43da6ed9eb43f937c98618e7cfce036118f60c

Jonathan Prykop jonathan at 420.am
Thu Mar 24 22:11:31 PDT 2016


The branch, master has been updated
       via  6c43da6ed9eb43f937c98618e7cfce036118f60c (commit)
       via  a472d8ff6bf5c87d7181c4a3f1757bae487a6ddf (commit)
      from  028e089bd3219330418ce40ab07926a320584e7e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 6c43da6ed9eb43f937c98618e7cfce036118f60c
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Fri Mar 25 00:10:00 2016 -0500

    RT#37912: Service Provisioning Export for ISPConfig 3 [bug fixes]

diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
index c06b302..9d9ecdd 100644
--- a/FS/FS/cust_svc.pm
+++ b/FS/FS/cust_svc.pm
@@ -16,6 +16,7 @@ use FS::domain_record;
 use FS::part_export;
 use FS::cdr;
 use FS::UI::Web;
+use FS::export_cust_svc;
 
 #most FS::svc_ classes are autoloaded in svc_x emthod
 use FS::svc_acct;  #this one is used in the cache stuff
diff --git a/FS/FS/part_export/ispconfig3.pm b/FS/FS/part_export/ispconfig3.pm
index 2878c51..3345b3f 100644
--- a/FS/FS/part_export/ispconfig3.pm
+++ b/FS/FS/part_export/ispconfig3.pm
@@ -141,21 +141,22 @@ sub _mail_user_params {
 
 sub _export_insert {
   my ($self, $svc_acct) = @_;
+  return $self->api_error || 'Error logging in'
+    unless $self->api_login;
   my $params = $self->_mail_user_params($svc_acct);
-  $self->api_login;
   my $remoteid = $self->api_call('mail_user_add',$self->option('client_id'),$params);
   return $self->api_error_logout if $self->api_error;
   my $error = $self->set_remoteid($svc_acct,$remoteid);
   $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
     if $error;
   $self->api_logout;
-  $error ||= "Systems updated, but error logging out: ".$self->api_error
-    if $self->api_error;
   return $error;
 }
 
 sub _export_replace {
   my ($self, $svc_acct, $svc_acct_old) = @_;
+  return $self->api_error || 'Error logging in'
+    unless $self->api_login;
   my $remoteid = $self->get_remoteid($svc_acct_old);
   return "Could not load remoteid for old service" unless $remoteid;
   my $params = $self->_mail_user_params($svc_acct);
@@ -170,23 +171,32 @@ sub _export_replace {
       if $error;
   }
   $self->api_logout;
-  $error ||= "Systems updated, but error logging out: ".$self->api_error
-    if $self->api_error;
   return $error;
 }
 
 sub _export_delete {
   my ($self, $svc_acct) = @_;
+  return $self->api_error || 'Error logging in'
+    unless $self->api_login;
   my $remoteid = $self->get_remoteid($svc_acct);
-  return "Could not load remoteid for old service" unless $remoteid;
+  #don't abort deletion--
+  #  might have been provisioned before export was implemented,
+  #  still need to be able to delete from freeside
+  unless ($remoteid) {
+    warn "Could not load remoteid for svcnum ".$svc_acct->svcnum.", unprovisioning anyway";
+    return '';
+  }
   #API docs claim "Returns the number of deleted records"
   my $success = $self->api_call('mail_user_delete',$remoteid);
   return $self->api_error_logout if $self->api_error;
-  my $error = $success ? '' : "Server returned no records deleted";
+  #don't abort deletion--
+  #  if it's already been deleted remotely,
+  #  still need to be able to delete from freeside
+  warn "Server returned no records deleted for svcnum ".$svc_acct->svcnum.
+       " remoteid $remoteid, unprovisioning anyway"
+    unless $success;
   $self->api_logout;
-  $error ||= "Systems updated, but error logging out: ".$self->api_error
-    if $self->api_error;
-  return $error;
+  return '';
 }
 
 sub _export_suspend {
@@ -219,7 +229,6 @@ automatically.  Must run L</api_login> first.
 
 sub api_call {
   my ($self,$method, at params) = @_;
-  $self->{'__ispconfig_response'} = undef;
   # This does get used by api_login,
   # to retrieve the session id after it sets the client,
   # so we only check for existence of client,
@@ -244,7 +253,6 @@ sub api_call {
   $self->{'__ispconfig_error'} = $response->fault
                                ? "Error from server: " . $response->faultstring
                                : '';
-  $self->{'__ispconfig_response'} = $response;
   return $response->result;
 }
 

commit a472d8ff6bf5c87d7181c4a3f1757bae487a6ddf
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Thu Mar 24 21:41:04 2016 -0500

    RT#37912: Service Provisioning Export for ISPConfig 3

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index dadb26d..bd63af5 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4453,6 +4453,26 @@ sub tables_hashref {
                         ],
     },
 
+    'export_cust_svc' => {
+      'columns' => [
+        'exportcustsvcnum', 'serial', '', '', '', '', 
+        'exportnum', 'int', '', '', '', '', 
+        'svcnum', 'int', '', '', '', '', 
+        'remoteid', 'varchar', '', 512, '', '', 
+      ],
+      'primary_key'  => 'exportcustsvcnum',
+      'unique'       => [ [ 'exportnum', 'svcnum' ] ],
+      'index'        => [ [ 'exportnum', 'svcnum' ] ],
+      'foreign_keys' => [
+                          { columns    => [ 'exportnum' ],
+                            table      => 'part_export',
+                          },
+                          { columns    => [ 'svcnum' ],
+                            table      => 'cust_svc',
+                          },
+                        ],
+    },
+
     'export_device' => {
       'columns' => [
         'exportdevicenum' => 'serial', '', '', '', '', 
diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
index d91fa0d..c06b302 100644
--- a/FS/FS/cust_svc.pm
+++ b/FS/FS/cust_svc.pm
@@ -169,6 +169,17 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
+  # delete associated export_cust_svc
+  foreach my $export_cust_svc (
+    qsearch('export_cust_svc',{ 'svcnum' => $self->svcnum })
+  ) {
+    my $error = $export_cust_svc->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
   my $error = $self->SUPER::delete;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/export_cust_svc.pm b/FS/FS/export_cust_svc.pm
new file mode 100644
index 0000000..7cfdcc6
--- /dev/null
+++ b/FS/FS/export_cust_svc.pm
@@ -0,0 +1,134 @@
+package FS::export_cust_svc;
+use base qw(FS::Record);
+
+use strict;
+use FS::Record qw( qsearchs );
+
+=head1 NAME
+
+FS::export_cust_svc - Object methods for export_cust_svc records
+
+=head1 SYNOPSIS
+
+  use FS::export_cust_svc;
+
+  $record = new FS::export_cust_svc \%hash;
+  $record = new FS::export_cust_svc { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::export_cust_svc object represents information unique
+to a given part_export and cust_svc pair.
+FS::export_cust_svc inherits from FS::Record.  
+The following fields are currently supported:
+
+=over 4
+
+=item exportcustsvcnum - primary key
+
+=item exportnum - export (see L<FS::part_export>)
+
+=item svcnum - service (see L<FS::cust_svc>)
+
+=item remoteid - id for accessing service on export remote system
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new export_cust_svc object.  To add the object to the database, see
+L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to.  You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'export_cust_svc'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub insert {
+  my $self = shift;
+  return "export_cust_svc for exportnum ".$self->exportnum.
+         " svcnum ".$self->svcnum." already exists"
+    if qsearchs('export_cust_svc',{ 'exportnum' => $self->exportnum,
+                                    'svcnum'    => $self->svcnum });
+  $self->SUPER::insert;
+}
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid export option.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('exportcustsvcnum')
+    || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
+    || $self->ut_foreign_key('svcnum', 'cust_svc', 'svcnum')
+    || $self->ut_text('remoteid')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+Possibly.
+
+=head1 SEE ALSO
+
+L<FS::part_export>, L<FS::cust_svc>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm
index 182f476..1b6c55a 100644
--- a/FS/FS/part_export.pm
+++ b/FS/FS/part_export.pm
@@ -10,6 +10,7 @@ use FS::part_svc;
 use FS::part_export_option;
 use FS::part_export_machine;
 use FS::svc_export_machine;
+use FS::export_cust_svc;
 
 #for export modules, though they should probably just use it themselves
 use FS::queue;
@@ -162,6 +163,17 @@ sub delete {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
+  # delete associated export_cust_svc
+  foreach my $export_cust_svc ( 
+    qsearch('export_cust_svc',{ 'exportnum' => $self->exportnum })
+  ) {
+    my $error = $export_cust_svc->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
   # clean up export_nas records
   my $error = $self->process_m2m(
     'link_table'    => 'export_nas',
@@ -637,6 +649,81 @@ sub _export_unsuspend {
   $self->_export_replace( $svc_x, $old );
 }
 
+=item get_remoteid SVC
+
+Returns the remote id for this export for the given service.
+
+=cut
+
+sub get_remoteid {
+  my ($self, $svc_x) = @_;
+
+  my $export_cust_svc = qsearchs('export_cust_svc',{
+    'exportnum' => $self->exportnum,
+    'svcnum' => $svc_x->svcnum
+  });
+
+  return $export_cust_svc ? $export_cust_svc->remoteid : '';
+}
+
+=item set_remoteid SVC VALUE
+
+Sets the remote id for this export for the given service.
+See L<FS::export_cust_svc>.
+
+If value is true, inserts or updates export_cust_svc record.
+If value is false, deletes any existing record.
+
+Returns error message, blank on success.
+
+=cut
+
+sub set_remoteid {
+  my ($self, $svc_x, $value) = @_;
+
+  my $export_cust_svc = qsearchs('export_cust_svc',{
+    'exportnum' => $self->exportnum,
+    'svcnum' => $svc_x->svcnum
+  });
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $error = '';
+  if ($value) {
+    if ($export_cust_svc) {
+      $export_cust_svc->set('remoteid',$value);
+      $error = $export_cust_svc->replace;
+    } else {
+      $export_cust_svc = new FS::export_cust_svc {
+        'exportnum' => $self->exportnum,
+        'svcnum' => $svc_x->svcnum,
+        'remoteid' => $value
+      };
+      $error = $export_cust_svc->insert;
+    }
+  } else {
+    if ($export_cust_svc) {
+      $error = $export_cust_svc->delete;
+    } #otherwise, it already doesn't exist
+  }
+
+  if ($oldAutoCommit) {
+    $dbh->rollback if $error;
+    $dbh->commit unless $error;
+  }
+
+  return $error;  
+}
+
 =item export_links SVC_OBJECT ARRAYREF
 
 Adds a list of web elements to ARRAYREF specific to this export and SVC_OBJECT.
diff --git a/FS/FS/part_export/ispconfig3.pm b/FS/FS/part_export/ispconfig3.pm
new file mode 100644
index 0000000..2878c51
--- /dev/null
+++ b/FS/FS/part_export/ispconfig3.pm
@@ -0,0 +1,361 @@
+package FS::part_export::ispconfig3;
+
+use strict;
+
+use base qw( FS::part_export );
+
+use Data::Dumper;
+use SOAP::Lite;
+
+=pod
+
+=head1 NAME
+
+FS::part_export::ispconfig3
+
+=head1 SYNOPSIS
+
+ISPConfig 3 integration for Freeside
+
+=head1 DESCRIPTION
+
+This export offers basic svc_acct provisioning for ISPConfig 3.
+All email accounts will be assigned to a single specified client.
+
+This module also provides generic methods for working through the L</ISPConfig3 API>.
+
+=cut
+
+use vars qw( %info );
+
+my @yesno = (
+  options => ['y','n'],
+  option_labels => { 'y' => 'yes', 'n' => 'no' },
+);
+
+tie my %options, 'Tie::IxHash',
+  'soap_location'      => { label   => 'SOAP Location' },
+  'username'           => { label   => 'User Name',
+                            default => '' },
+  'password'           => { label   => 'Password',
+                            default => '' },
+  'debug'              => { type    => 'checkbox',
+                            label   => 'Enable debug warnings' },
+  'subheading'         => { type    => 'title',
+                            label   => 'Account defaults' },
+  'client_id'          => { label   => 'Client ID' },
+  'server_id'          => { label   => 'Server ID' },
+  'maildir'            => { label   => 'Maildir (substitutions from svc_acct, e.g. /mail/$domain/$username)', },
+  'cc'                 => { label   => 'Cc' },
+  'autoresponder_text' => { label   => 'Autoresponder text', 
+                            default => 'Out of Office Reply' },
+  'move_junk'          => { type    => 'select',
+                            options => ['y','n'],
+                            option_labels => { 'y' => 'yes', 'n' => 'no' },
+                            label   => 'Move junk' },
+  'postfix'            => { type    => 'select',
+                            @yesno,
+                            label   => 'Postfix' },
+  'access'             => { type    => 'select',
+                            @yesno,
+                            label   => 'Access' },
+  'disableimap'        => { type    => 'select',
+                            @yesno,
+                            label   => 'Disable IMAP' },
+  'disablepop3'        => { type    => 'select',
+                            @yesno,
+                            label   => 'Disable POP3' },
+  'disabledeliver'     => { type    => 'select',
+                            @yesno,
+                            label   => 'Disable deliver' },
+  'disablesmtp'        => { type    => 'select',
+                            @yesno,
+                            label   => 'Disable SMTP' },
+;
+
+%info = (
+  'svc'             => 'svc_acct',
+  'desc'            => 'Export email account to ISPConfig 3',
+  'options'         => \%options,
+  'no_machine'      => 1,
+  'notes'           => <<'END',
+All email accounts will be assigned to a single specified client and server.
+END
+);
+
+sub _mail_user_params {
+  my ($self, $svc_acct) = @_;
+  # all available api fields are in comments below, even if we don't use them
+  return {
+    #server_id  (int(11))
+    'server_id' => $self->option('server_id'),
+    #email  (varchar(255))
+    'email' => $svc_acct->username.'@'.$svc_acct->domain,
+    #login  (varchar(255))
+    'login' => $svc_acct->username.'@'.$svc_acct->domain,
+    #password  (varchar(255))
+    'password' => $svc_acct->_password,
+    #name  (varchar(255))
+    'name' => $svc_acct->finger,
+    #uid  (int(11))
+    'uid' => $svc_acct->uid,
+    #gid  (int(11))
+    'gid' => $svc_acct->gid,
+    #maildir  (varchar(255))
+    'maildir' => $self->_substitute($self->option('maildir'),$svc_acct),
+    #quota  (bigint(20))
+    'quota' => $svc_acct->quota,
+    #cc  (varchar(255))
+    'cc' => $self->option('cc'),
+    #homedir  (varchar(255))
+    'homedir' => $svc_acct->dir,
+
+    ## initializing with autoresponder off, but this could become an export option...
+    #autoresponder  (enum('n','y'))
+    'autoresponder' => 'n',
+    #autoresponder_start_date  (datetime)
+    #autoresponder_end_date  (datetime)
+    #autoresponder_text  (mediumtext)
+    'autoresponder_text' => $self->option('autoresponder_text'),
+
+    #move_junk  (enum('n','y'))
+    'move_junk' => $self->option('move_junk'),
+    #postfix  (enum('n','y'))
+    'postfix' => $self->option('postfix'),
+    #access  (enum('n','y'))
+    'access' => $self->option('access'),
+
+    ## not needed right now, not sure what it is
+	#custom_mailfilter  (mediumtext)
+
+    #disableimap  (enum('n','y'))
+    'disableimap' => $self->option('disableimap'),
+    #disablepop3  (enum('n','y'))
+    'disablepop3' => $self->option('disablepop3'),
+    #disabledeliver  (enum('n','y'))
+    'disabledeliver' => $self->option('disabledeliver'),
+    #disablesmtp  (enum('n','y'))
+    'disablesmtp' => $self->option('disablesmtp'),
+  };
+}
+
+sub _export_insert {
+  my ($self, $svc_acct) = @_;
+  my $params = $self->_mail_user_params($svc_acct);
+  $self->api_login;
+  my $remoteid = $self->api_call('mail_user_add',$self->option('client_id'),$params);
+  return $self->api_error_logout if $self->api_error;
+  my $error = $self->set_remoteid($svc_acct,$remoteid);
+  $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
+    if $error;
+  $self->api_logout;
+  $error ||= "Systems updated, but error logging out: ".$self->api_error
+    if $self->api_error;
+  return $error;
+}
+
+sub _export_replace {
+  my ($self, $svc_acct, $svc_acct_old) = @_;
+  my $remoteid = $self->get_remoteid($svc_acct_old);
+  return "Could not load remoteid for old service" unless $remoteid;
+  my $params = $self->_mail_user_params($svc_acct);
+  #API docs claim "Returns the number of affected rows"
+  my $success = $self->api_call('mail_user_update',$self->option('client_id'),$remoteid,$params);
+  return $self->api_error_logout if $self->api_error;
+  return "Server returned no rows updated, but no other error message" unless $success;
+  my $error = '';
+  unless ($svc_acct->svcnum eq $svc_acct_old->svcnum) { # are these ever not equal?
+    $error = $self->set_remoteid($svc_acct,$remoteid);
+    $error = "Remote system updated, but error setting remoteid ($remoteid): $error"
+      if $error;
+  }
+  $self->api_logout;
+  $error ||= "Systems updated, but error logging out: ".$self->api_error
+    if $self->api_error;
+  return $error;
+}
+
+sub _export_delete {
+  my ($self, $svc_acct) = @_;
+  my $remoteid = $self->get_remoteid($svc_acct);
+  return "Could not load remoteid for old service" unless $remoteid;
+  #API docs claim "Returns the number of deleted records"
+  my $success = $self->api_call('mail_user_delete',$remoteid);
+  return $self->api_error_logout if $self->api_error;
+  my $error = $success ? '' : "Server returned no records deleted";
+  $self->api_logout;
+  $error ||= "Systems updated, but error logging out: ".$self->api_error
+    if $self->api_error;
+  return $error;
+}
+
+sub _export_suspend {
+  my ($self, $svc_acct) = @_;
+  return '';
+}
+
+sub _export_unsuspend {
+  my ($self, $svc_acct) = @_;
+  return '';
+}
+
+=head1 ISPConfig3 API
+
+These methods allow access to the ISPConfig3 API using the credentials
+set in the export options.
+
+=cut
+
+=head2 api_call
+
+Accepts I<$method> and I<@params>.  Places an api call to the specified
+method with the specified params.  Returns the result of that call
+(empty on failure.)  Retrieve error messages using L</api_error>.
+
+Do not include session id in list of params;  it will be included
+automatically.  Must run L</api_login> first.
+
+=cut
+
+sub api_call {
+  my ($self,$method, at params) = @_;
+  $self->{'__ispconfig_response'} = undef;
+  # This does get used by api_login,
+  # to retrieve the session id after it sets the client,
+  # so we only check for existence of client,
+  # and we only include session id if we have one
+  my $client = $self->{'__ispconfig_client'};
+  unless ($client) {
+    $self->{'__ispconfig_error'} = 'Not logged in';
+    return;
+  }
+  if ($self->{'__ispconfig_session'}) {
+    unshift(@params,$self->{'__ispconfig_session'});
+  }
+  # Contact server in eval, to trap connection errors
+  warn "Calling SOAP method $method with params:\n".Dumper(\@params)."\n"
+    if $self->option('debug');
+  my $response = eval { $client->$method(@params) };
+  unless ($response) {
+    $self->{'__ispconfig_error'} = "Error contacting server: $@";
+    return;
+  }
+  # Set results and return
+  $self->{'__ispconfig_error'} = $response->fault
+                               ? "Error from server: " . $response->faultstring
+                               : '';
+  $self->{'__ispconfig_response'} = $response;
+  return $response->result;
+}
+
+=head2 api_error
+
+Returns the error string set by L</ISPConfig3 API> methods,
+or a blank string if most recent call produced no errors.
+
+=cut
+
+sub api_error {
+  my $self = shift;
+  return $self->{'__ispconfig_error'} || '';
+}
+
+=head2 api_error_logout
+
+Attempts L</api_logout>, but returns L</api_error> message from
+before logout was attempted.  Useful for logging out
+properly after an error.
+
+=cut
+
+sub api_error_logout {
+  my $self = shift;
+  my $error = $self->api_error;
+  $self->api_logout;
+  return $error;
+}
+
+=head2 api_login
+
+Initializes an api session using the credentials for this export.
+Returns true on success, false on failure.
+Retrieve error messages using L</api_error>.
+
+=cut
+
+sub api_login {
+  my $self = shift;
+  if ($self->{'__ispconfig_session'} || $self->{'__ispconfig_client'}) {
+    $self->{'__ispconfig_error'} = 'Already logged in';
+    return;
+  }
+  $self->{'__ispconfig_session'} = undef;
+  $self->{'__ispconfig_client'} =
+    SOAP::Lite->proxy($self->option('soap_location'), ssl_opts => [ verify_hostname => 0 ] )
+    || undef;
+  unless ($self->{'__ispconfig_client'}) {
+    $self->{'__ispconfig_error'} = 'Error creating SOAP client';
+    return;
+  }
+  $self->{'__ispconfig_session'} = 
+    $self->api_call('login',$self->option('username'),$self->option('password'))
+    || undef;
+  return unless $self->{'__ispconfig_session'};
+  return 1;
+}
+
+=head2 api_logout
+
+Ends the current api session established by L</api_login>.
+Returns true on success, false on failure.
+
+=cut
+
+sub api_logout {
+  my $self = shift;
+  unless ($self->{'__ispconfig_session'}) {
+    $self->{'__ispconfig_error'} = 'Not logged in';
+    return;
+  }
+  my $result = $self->api_call('logout');
+  # clear these even if there was a failure to logout
+  $self->{'__ispconfig_client'} = undef;
+  $self->{'__ispconfig_session'} = undef;
+  return if $self->api_error;
+  return 1;
+}
+
+# false laziness with portaone export
+sub _substitute {
+  my ($self, $string, @objects) = @_;
+  return '' unless $string;
+  foreach my $object (@objects) {
+    next unless $object;
+    my @fields = $object->fields;
+    push(@fields,'domain') if $object->table eq 'svc_acct';
+    foreach my $field (@fields) {
+      next unless $field;
+      my $value = $object->$field;
+      $string =~ s/\$$field/$value/g;
+    }
+  }
+  # strip leading/trailing whitespace
+  $string =~ s/^\s//g;
+  $string =~ s/\s$//g;
+  return $string;
+}
+
+=head1 SEE ALSO
+
+L<FS::part_export>
+
+=head1 AUTHOR
+
+Jonathan Prykop 
+jonathan at freeside.biz
+
+=cut
+
+1;
+
+

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Schema.pm                                    |   20 ++
 FS/FS/cust_svc.pm                                  |   12 +
 .../{part_export_option.pm => export_cust_svc.pm}  |   51 +--
 FS/FS/part_export.pm                               |   87 +++++
 FS/FS/part_export/ispconfig3.pm                    |  369 ++++++++++++++++++++
 5 files changed, 515 insertions(+), 24 deletions(-)
 copy FS/FS/{part_export_option.pm => export_cust_svc.pm} (56%)
 create mode 100644 FS/FS/part_export/ispconfig3.pm




More information about the freeside-commits mailing list