config goes in database
authorjeff <jeff>
Tue, 27 Feb 2007 01:51:52 +0000 (01:51 +0000)
committerjeff <jeff>
Tue, 27 Feb 2007 01:51:52 +0000 (01:51 +0000)
17 files changed:
FS/FS/Conf.pm
FS/FS/Schema.pm
FS/FS/UID.pm
FS/FS/conf.pm [new file with mode: 0644]
FS/FS/cust_bill.pm
FS/FS/cust_main.pm
FS/FS/svc_acct.pm
FS/MANIFEST
FS/bin/freeside-init-config [new file with mode: 0755]
FS/bin/freeside-setup
FS/bin/freeside-upgrade
FS/t/conf.t [new file with mode: 0644]
conf/invoice_latex
httemplate/config/config-download.cgi [new file with mode: 0644]
httemplate/config/config-process.cgi
httemplate/config/config-view.cgi
httemplate/config/config.cgi

index 2f01e1f..594f3b3 100644 (file)
@@ -1,13 +1,14 @@
 package FS::Conf;
 
-use vars qw($default_dir $base_dir @config_items @card_types $DEBUG );
-use IO::File;
-use File::Basename;
+use vars qw($base_dir @config_items @card_types $DEBUG );
+use MIME::Base64;
 use FS::ConfItem;
 use FS::ConfDefaults;
+use FS::conf;
+use FS::Record qw(qsearch qsearchs);
+use FS::UID qw(dbh);
 
 $base_dir = '%%%FREESIDE_CONF%%%';
-$default_dir = '%%%FREESIDE_CONF%%%';
 
 
 $DEBUG = 0;
@@ -20,13 +21,8 @@ FS::Conf - Freeside configuration values
 
   use FS::Conf;
 
-  $conf = new FS::Conf "/config/directory";
-
-  $FS::Conf::default_dir = "/config/directory";
   $conf = new FS::Conf;
 
-  $dir = $conf->dir;
-
   $value = $conf->config('key');
   @list  = $conf->config('key');
   $bool  = $conf->exists('key');
@@ -46,39 +42,19 @@ but this may change in the future.
 
 =over 4
 
-=item new [ DIRECTORY ]
+=item new
 
-Create a new configuration object.  A directory arguement is required if
-$FS::Conf::default_dir has not been set.
+Create a new configuration object.
 
 =cut
 
 sub new {
-  my($proto,$dir) = @_;
+  my($proto) = @_;
   my($class) = ref($proto) || $proto;
-  my($self) = { 'dir'      => $dir || $default_dir,
-                'base_dir' => $base_dir,
-              };
+  my($self) = { 'base_dir' => $base_dir };
   bless ($self, $class);
 }
 
-=item dir
-
-Returns the conf directory.
-
-=cut
-
-sub dir {
-  my($self) = @_;
-  my $dir = $self->{dir};
-  -e $dir or die "FATAL: $dir doesn't exist!";
-  -d $dir or die "FATAL: $dir isn't a directory!";
-  -r $dir or die "FATAL: Can't read $dir!";
-  -x $dir or die "FATAL: $dir not searchable (executable)!";
-  $dir =~ /^(.*)$/;
-  $1;
-}
-
 =item base_dir
 
 Returns the base directory.  By default this is /usr/local/etc/freeside.
@@ -102,20 +78,29 @@ Returns the configuration value or values (depending on context) for key.
 
 =cut
 
+sub _config {
+  my($self,$name,$agent)=@_;
+  my $hashref = { 'name' => $name };
+  if (defined($agent) && $agent) {
+    $hashref->{agent} = $agent;
+  }
+  local $FS::Record::conf = undef;  # XXX evil hack prevents recursion
+  my $cv = FS::Record::qsearchs('conf', $hashref);
+  if (!$cv && exists($hashref->{agent})) {
+    delete($hashref->{agent});
+    $cv = FS::Record::qsearchs('conf', $hashref);
+  }
+  return $cv;
+}
+
 sub config {
-  my($self,$file)=@_;
-  my($dir)=$self->dir;
-  my $fh = new IO::File "<$dir/$file" or return;
+  my($self,$name,$agent)=@_;
+  my $cv = $self->_config($name, $agent) or return;
+
   if ( wantarray ) {
-    map {
-      /^(.*)$/
-        or die "Illegal line (array context) in $dir/$file:\n$_\n";
-      $1;
-    } <$fh>;
+    split "\n", $cv->value;
   } else {
-    <$fh> =~ /^(.*)$/
-      or die "Illegal line (scalar context) in $dir/$file:\n$_\n";
-    $1;
+    (split("\n", $cv->value))[0];
   }
 }
 
@@ -126,12 +111,9 @@ Returns the exact scalar value for key.
 =cut
 
 sub config_binary {
-  my($self,$file)=@_;
-  my($dir)=$self->dir;
-  my $fh = new IO::File "<$dir/$file" or return;
-  local $/;
-  my $content = <$fh>;
-  $content;
+  my($self,$name,$agent)=@_;
+  my $cv = $self->_config($name, $agent) or return;
+  decode_base64($cv->value);
 }
 
 =item exists KEY
@@ -142,9 +124,8 @@ is undefined.
 =cut
 
 sub exists {
-  my($self,$file)=@_;
-  my($dir) = $self->dir;
-  -e "$dir/$file";
+  my($self,$name,$agent)=@_;
+  defined($self->_config($name, $agent));
 }
 
 =item config_orbase KEY SUFFIX
@@ -155,11 +136,11 @@ KEY_SUFFIX, if it exists, otherwise for KEY
 =cut
 
 sub config_orbase {
-  my( $self, $file, $suffix ) = @_;
-  if ( $self->exists("${file}_$suffix") ) {
-    $self->config("${file}_$suffix");
+  my( $self, $name, $suffix ) = @_;
+  if ( $self->exists("${name}_$suffix") ) {
+    $self->config("${name}_$suffix");
   } else {
-    $self->config($file);
+    $self->config($name);
   }
 }
 
@@ -170,12 +151,8 @@ Creates the specified configuration key if it does not exist.
 =cut
 
 sub touch {
-  my($self, $file) = @_;
-  my $dir = $self->dir;
-  unless ( $self->exists($file) ) {
-    warn "[FS::Conf] TOUCH $file\n" if $DEBUG;
-    system('touch', "$dir/$file");
-  }
+  my($self, $name, $agent) = @_;
+  $self->set($name, '', $agent);
 }
 
 =item set KEY VALUE
@@ -185,23 +162,49 @@ Sets the specified configuration key to the given value.
 =cut
 
 sub set {
-  my($self, $file, $value) = @_;
-  my $dir = $self->dir;
+  my($self, $name, $value, $agent) = @_;
   $value =~ /^(.*)$/s;
   $value = $1;
-  unless ( join("\n", @{[ $self->config($file) ]}) eq $value ) {
-    warn "[FS::Conf] SET $file\n" if $DEBUG;
-#    warn "$dir" if is_tainted($dir);
-#    warn "$dir" if is_tainted($file);
-    chmod 0644, "$dir/$file";
-    my $fh = new IO::File ">$dir/$file" or return;
-    chmod 0644, "$dir/$file";
-    print $fh "$value\n";
+
+  warn "[FS::Conf] SET $file\n" if $DEBUG;
+
+  my $old = FS::Record::qsearchs('conf', {name => $name, agent => $agent});
+  my $new = new FS::conf { $old ? $old->hash 
+                                : ('name' => $name, 'agent' => $agent)
+                         };
+  $new->value($value);
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $error;
+  if ($old) {
+    $error = $new->replace($old);
+  }else{
+    $error = $new->insert;
+  }
+
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    die "error setting configuration value: $error \n"
   }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+}
+
+=item set_binary KEY VALUE
+
+Sets the specified configuration key to an exact scalar value which
+can be retrieved with config_binary.
+
+=cut
+
+sub set_binary {
+  my($self,$name, $value, $agent)=@_;
+  $self->set($name, encode_base64($value), $agent);
 }
-#sub is_tainted {
-#             return ! eval { join('',@_), kill 0; 1; };
-#         }
 
 =item delete KEY
 
@@ -210,11 +213,23 @@ Deletes the specified configuration key.
 =cut
 
 sub delete {
-  my($self, $file) = @_;
-  my $dir = $self->dir;
-  if ( $self->exists($file) ) {
+  my($self, $name, $agent) = @_;
+  if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agent => $agent}) ) {
     warn "[FS::Conf] DELETE $file\n";
-    unlink "$dir/$file";
+
+    my $oldAutoCommit = $FS::UID::AutoCommit;
+    local $FS::UID::AutoCommit = 0;
+    my $dbh = dbh;
+
+    my $error = $cv->delete;
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      die "error setting configuration value: $error \n"
+    }
+
+    $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
   }
 }
 
@@ -230,65 +245,68 @@ sub config_items {
   #quelle kludge
   @config_items,
   ( map { 
-        my $basename = basename($_);
-        $basename =~ /^(.*)$/;
-        $basename = $1;
         new FS::ConfItem {
-                           'key'         => $basename,
+                           'key'         => $_->name,
                            'section'     => 'billing',
                            'description' => 'Alternate template file for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
                            'type'        => 'textarea',
                          }
-      } glob($self->dir. '/invoice_template_*')
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_template!_%' ESCAPE '!'")
+  ),
+  ( map { 
+        new FS::ConfItem {
+                           'key'         => '$_->name',
+                           'section'     => 'billing',  #? 
+                           'description' => 'An image to include in some types of invoices',
+                           'type'        => 'binary',
+                         }
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'logo!_%.png' ESCAPE '!'")
   ),
   ( map { 
-        my $basename = basename($_);
-        $basename =~ /^(.*)$/;
-        $basename = $1;
         new FS::ConfItem {
-                           'key'         => $basename,
+                           'key'         => $_->name,
                            'section'     => 'billing',
                            'description' => 'Alternate HTML template for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
                            'type'        => 'textarea',
                          }
-      } glob($self->dir. '/invoice_html_*')
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_html!_%' ESCAPE '!'")
   ),
   ( map { 
-        my $basename = basename($_);
-        $basename =~ /^(.*)$/;
-        $basename = $1;
-        ($latexname = $basename ) =~ s/latex/html/;
+        ($latexname = $_->name ) =~ s/latex/html/;
         new FS::ConfItem {
-                           'key'         => $basename,
+                           'key'         => $_->name,
                            'section'     => 'billing',
                            'description' => "Alternate Notes section for HTML invoices.  Defaults to the same data in $latexname if not specified.",
                            'type'        => 'textarea',
                          }
-      } glob($self->dir. '/invoice_htmlnotes_*')
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_htmlnotes!_%' ESCAPE '!'")
   ),
   ( map { 
-        my $basename = basename($_);
-        $basename =~ /^(.*)$/;
-        $basename = $1;
         new FS::ConfItem {
-                           'key'         => $basename,
+                           'key'         => $_->name,
                            'section'     => 'billing',
                            'description' => 'Alternate LaTeX template for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
                            'type'        => 'textarea',
                          }
-      } glob($self->dir. '/invoice_latex_*')
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_latex!_%' ESCAPE '!'")
+  ),
+  ( map { 
+        new FS::ConfItem {
+                           'key'         => '$_->name',
+                           'section'     => 'billing',  #? 
+                           'description' => 'An image to include in some types of invoices',
+                           'type'        => 'binary',
+                         }
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'logo!_%.eps' ESCAPE '!'")
   ),
   ( map { 
-        my $basename = basename($_);
-        $basename =~ /^(.*)$/;
-        $basename = $1;
         new FS::ConfItem {
-                           'key'         => $basename,
+                           'key'         => $_->name,
                            'section'     => 'billing',
                            'description' => 'Alternate Notes section for LaTeX typeset PostScript invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
                            'type'        => 'textarea',
                          }
-      } glob($self->dir. '/invoice_latexnotes_*')
+      } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_latexnotes!_%' ESCAPE '!'")
   );
 }
 
@@ -2046,6 +2064,20 @@ httemplate/docs/config.html
   },
 
   {
+    'key'         => 'logo.png',
+    'section'     => 'billing',  #? 
+    'description' => 'An image to include in some types of invoices',
+    'type'        => 'binary',
+  },
+
+  {
+    'key'         => 'logo.eps',
+    'section'     => 'billing',  #? 
+    'description' => 'An image to include in some types of invoices',
+    'type'        => 'binary',
+  },
+
+  {
     'key'         => 'selfservice-ignore_quantity',
     'section'     => '',
     'description' => 'Ignores service quantity restrictions in self-service context.  Strongly not recommended - just set your quantities correctly in the first place.',
index bae7522..d9d5f5a 100644 (file)
@@ -1687,6 +1687,18 @@ sub tables_hashref {
       'index' => [],
     },
 
+    'conf' => {
+      'columns' => [
+        'confnum',  'serial',  '', '', '', '', 
+        'agentnum', 'int',  'NULL', '', '', '', 
+        'name',     'varchar', '', $char_d, '', '', 
+        'value',    'varchar', 'NULL', '', '', '',       # Pg specific
+      ],
+      'primary_key' => 'confnum',
+      'unique' => [ [ 'agentnum', 'name' ]],
+      'index' => [],
+    },
+
     # name type nullability length default local
 
     #'new_table' => {
index 8dd928e..da573a6 100644 (file)
@@ -4,7 +4,7 @@ use strict;
 use vars qw(
   @ISA @EXPORT_OK $cgi $dbh $freeside_uid $user 
   $conf_dir $secrets $datasrc $db_user $db_pass %callback @callback
-  $driver_name $AutoCommit
+  $driver_name $AutoCommit $callback_hack
 );
 use subs qw(
   getsecrets cgisetotaker
@@ -12,7 +12,7 @@ use subs qw(
 use Exporter;
 use Carp qw(carp croak cluck confess);
 use DBI;
-use FS::Conf;
+use IO::File;
 use FS::CurrentUser;
 
 @ISA = qw(Exporter);
@@ -24,6 +24,7 @@ $freeside_uid = scalar(getpwnam('freeside'));
 $conf_dir = "%%%FREESIDE_CONF%%%/";
 
 $AutoCommit = 1; #ours, not DBI
+$callback_hack = 0;
 
 =head1 NAME
 
@@ -104,12 +105,14 @@ sub forksuidsetup {
 
   FS::CurrentUser->load_user($user);
 
-  foreach ( keys %callback ) {
-    &{$callback{$_}};
-    # breaks multi-database installs # delete $callback{$_}; #run once
-  }
+  unless($callback_hack) {
+    foreach ( keys %callback ) {
+      &{$callback{$_}};
+      # breaks multi-database installs # delete $callback{$_}; #run once
+    }
 
-  &{$_} foreach @callback;
+    &{$_} foreach @callback;
+  }
 
   $dbh;
 }
@@ -275,11 +278,11 @@ the `/usr/local/etc/freeside/mapsecrets' file.
 sub getsecrets {
   my($setuser) = shift;
   $user = $setuser if $setuser;
-  my($conf) = new FS::Conf $conf_dir;
 
-  if ( $conf->exists('mapsecrets') ) {
+  if ( -e "$conf_dir/mapsecrets" ) {
     die "No user!" unless $user;
-    my($line) = grep /^\s*($user|\*)\s/, $conf->config('mapsecrets');
+    my($line) = grep /^\s*($user|\*)\s/,
+      map { /^(.*)$/; $1 } readline(new IO::File "$conf_dir/mapsecrets");
     confess "User $user not found in mapsecrets!" unless $line;
     $line =~ /^\s*($user|\*)\s+(.*)$/;
     $secrets = $2;
@@ -289,9 +292,9 @@ sub getsecrets {
     $secrets = 'secrets';
   }
 
-  ($datasrc, $db_user, $db_pass) = $conf->config($secrets)
-    or die "Can't get secrets: $secrets: $!\n";
-  $FS::Conf::default_dir = $conf_dir. "/conf.$datasrc";
+  ($datasrc, $db_user, $db_pass) = 
+    map { /^(.*)$/; $1 } readline(new IO::File "$conf_dir/$secrets")
+      or die "Can't get secrets: $secrets: $!\n";
   undef $driver_name;
   ($datasrc, $db_user, $db_pass);
 }
diff --git a/FS/FS/conf.pm b/FS/FS/conf.pm
new file mode 100644 (file)
index 0000000..6126372
--- /dev/null
@@ -0,0 +1,114 @@
+package FS::conf;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::conf - Object methods for conf records
+
+=head1 SYNOPSIS
+
+  use FS::conf;
+
+  $record = new FS::conf \%hash;
+  $record = new FS::conf { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::conf object represents a configuration value.  FS::conf inherits from
+FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item confnum - primary key
+
+=item agentnum - the agent to which this configuration value applies
+
+=item name - the name of the configuration value
+
+=item value - the configuration value
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new configuration value.  To add the example 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
+
+sub table { 'conf'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+=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
+
+=item check
+
+Checks all fields to make sure this is a valid configuration value.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('confnum')
+    || $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
+    || $self->ut_text('name')
+    || $self->ut_anything('value')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
index 844d1b8..1317448 100644 (file)
@@ -1827,7 +1827,8 @@ sub print_text {
 =item print_latex [ TIME [ , TEMPLATE ] ]
 
 Internal method - returns a filename of a filled-in LaTeX template for this
-invoice (Note: add ".tex" to get the actual filename).
+invoice (Note: add ".tex" to get the actual filename), and a filename of
+an associated logo (with the .eps extension included).
 
 See print_ps and print_pdf for methods that return PostScript and PDF output.
 
@@ -1909,6 +1910,7 @@ sub print_latex {
     'quantity'     => 1,
     'terms'        => $conf->config('invoice_default_terms') || 'Payable upon receipt',
     #'notes'        => join("\n", $conf->config('invoice_latexnotes') ),
+    # better hang on to conf_dir for a while
     'conf_dir'     => "$FS::UID::conf_dir/conf.$FS::UID::datasrc",
   );
 
@@ -2134,6 +2136,22 @@ sub print_latex {
   }
 
   my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+  my $lh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX',
+                           DIR      => $dir,
+                           SUFFIX   => '.eps',
+                           UNLINK   => 0,
+                         ) or die "can't open temp file: $!\n";
+
+  if ($template && $conf->exists("logo_${template}.eps")) {
+    print $lh $conf->config_binary("logo_${template}.eps")
+      or die "can't write temp file: $!\n";
+  }else{
+    print $lh $conf->config_binary('logo.eps')
+      or die "can't write temp file: $!\n";
+  }
+  close $lh;
+  $invoice_data{'logo_file'} = $lh->filename;
+
   my $fh = new File::Temp( TEMPLATE => 'invoice.'. $self->invnum. '.XXXXXXXX',
                            DIR      => $dir,
                            SUFFIX   => '.tex',
@@ -2149,7 +2167,7 @@ sub print_latex {
   close $fh;
 
   $fh->filename =~ /^(.*).tex$/ or die "unparsable filename: ". $fh->filename;
-  return $1;
+  return ($1, $invoice_data{'logo_file'});
 
 }
 
@@ -2167,7 +2185,7 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 sub print_ps {
   my $self = shift;
 
-  my $file = $self->print_latex(@_);
+  my ($file, $lfile) = $self->print_latex(@_);
 
   my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
   chdir($dir);
@@ -2186,6 +2204,7 @@ sub print_ps {
     or die "can't open $file.ps: $! (error in LaTeX template?)\n";
 
   unlink("$file.dvi", "$file.log", "$file.aux", "$file.ps", "$file.tex");
+  unlink("$lfile");
 
   my $ps = '';
   while (<POSTSCRIPT>) {
@@ -2212,7 +2231,7 @@ L<Time::Local> and L<Date::Parse> for conversion functions.
 sub print_pdf {
   my $self = shift;
 
-  my $file = $self->print_latex(@_);
+  my ($file, $lfile) = $self->print_latex(@_);
 
   my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
   chdir($dir);
@@ -2240,6 +2259,7 @@ sub print_pdf {
     or die "can't open $file.pdf: $! (error in LaTeX template?)\n";
 
   unlink("$file.dvi", "$file.log", "$file.aux", "$file.pdf", "$file.tex");
+  unlink("$lfile");
 
   my $pdf = '';
   while (<PDF>) {
index d775e75..fe6aa50 100644 (file)
@@ -417,7 +417,7 @@ sub start_copy_skel {
   #'mg_watchlist_header.watchlist_header_id' => { 'mg_watchlist_details.watchlist_details_id' },
   #'mg_user_grid_header.grid_header_id' => { 'mg_user_grid_details.user_grid_details_id' },
   #'mg_portfolio_header.portfolio_header_id' => { 'mg_portfolio_trades.portfolio_trades_id' => { 'mg_portfolio_trades_positions.portfolio_trades_positions_id' } },
-  my @tables = eval($conf->config_binary('cust_main-skeleton_tables'));
+  my @tables = eval(join('\n',$conf->config('cust_main-skeleton_tables')));
   die $@ if $@;
 
   _copy_skel( 'cust_main',                                 #tablename
index 48116d7..a06f4d7 100644 (file)
@@ -8,8 +8,6 @@ use vars qw( @ISA $DEBUG $me $conf $skip_fuzzyfiles
              $username_noperiod $username_nounderscore $username_nodash
              $username_uppercase $username_percent
              $password_noampersand $password_noexclamation
-             $welcome_template $welcome_from
-             $welcome_subject $welcome_subject_template $welcome_mimetype
              $warning_template $warning_from $warning_subject $warning_mimetype
              $warning_cc
              $smtpmachine
@@ -66,24 +64,6 @@ $FS::UID::callback{'FS::svc_acct'} = sub {
   $password_noampersand = $conf->exists('password-noexclamation');
   $password_noexclamation = $conf->exists('password-noexclamation');
   $dirhash = $conf->config('dirhash') || 0;
-  if ( $conf->exists('welcome_email') ) {
-    $welcome_template = new Text::Template (
-      TYPE   => 'ARRAY',
-      SOURCE => [ map "$_\n", $conf->config('welcome_email') ]
-    ) or warn "can't create welcome email template: $Text::Template::ERROR";
-    $welcome_from = $conf->config('welcome_email-from'); # || 'your-isp-is-dum'
-    $welcome_subject = $conf->config('welcome_email-subject') || 'Welcome';
-    $welcome_subject_template = new Text::Template (
-      TYPE   => 'STRING',
-      SOURCE => $welcome_subject,
-    ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
-    $welcome_mimetype = $conf->config('welcome_email-mimetype') || 'text/plain';
-  } else {
-    $welcome_template = '';
-    $welcome_from = '';
-    $welcome_subject = '';
-    $welcome_mimetype = '';
-  }
   if ( $conf->exists('warning_email') ) {
     $warning_template = new Text::Template (
       TYPE   => 'ARRAY',
@@ -467,6 +447,7 @@ sub insert {
 
   if ( $cust_pkg ) {
     my $cust_main = $cust_pkg->cust_main;
+    my $agentnum = $cust_main->agentnum;
 
     if (   $conf->exists('emailinvoiceautoalways')
         || $conf->exists('emailinvoiceauto')
@@ -478,7 +459,25 @@ sub insert {
     }
 
     #welcome email
-    my $to = '';
+    my ($to,$welcome_template,$welcome_from,$welcome_subject,$welcome_subject_template,$welcome_mimetype)
+      = ('','','','','','');
+
+    if ( $conf->exists('welcome_email', $agentnum) ) {
+      $welcome_template = new Text::Template (
+        TYPE   => 'ARRAY',
+        SOURCE => [ map "$_\n", $conf->config('welcome_email', $agentnum) ]
+      ) or warn "can't create welcome email template: $Text::Template::ERROR";
+      $welcome_from = $conf->config('welcome_email-from', $agentnum);
+        # || 'your-isp-is-dum'
+      $welcome_subject = $conf->config('welcome_email-subject', $agentnum)
+        || 'Welcome';
+      $welcome_subject_template = new Text::Template (
+        TYPE   => 'STRING',
+        SOURCE => $welcome_subject,
+      ) or warn "can't create welcome email subject template: $Text::Template::ERROR";
+      $welcome_mimetype = $conf->config('welcome_email-mimetype', $agentnum)
+        || 'text/plain';
+    }
     if ( $welcome_template && $cust_pkg ) {
       my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
       if ( $to ) {
@@ -949,7 +948,7 @@ sub check {
         $recref->{shell} = (grep $_ eq $recref->{shell}, @shells)[0];
       } else {
         return "Illegal shell \`". $self->shell. "\'; ".
-               $conf->dir. "/shells contains: @shells";
+               "shells configuration value contains: @shells";
       }
     } else {
       $recref->{shell} = '/bin/sync';
index 82f1064..597cd36 100644 (file)
@@ -371,3 +371,5 @@ FS/reason_type.pm
 t/reason_type.t
 FS/cust_pkg_option.pm
 t/cust_pkg_option.t
+FS/conf.pm
+t/conf.t
diff --git a/FS/bin/freeside-init-config b/FS/bin/freeside-init-config
new file mode 100755 (executable)
index 0000000..a186d1a
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw($opt_u $opt_f $opt_v);
+use Getopt::Std;
+use IO::File;
+use FS::UID qw(adminsuidsetup checkeuid dbh);
+use FS::CurrentUser;
+use FS::Record qw(qsearch);
+
+
+die "Not running uid freeside!" unless checkeuid();
+
+getopts("u:vf");
+my $dir = shift or die &usage;
+
+$FS::CurrentUser::upgrade_hack = 1;
+adminsuidsetup $opt_u; #$user;
+
+$|=1;
+
+my $conf = new FS::Conf;
+if (!scalar(qsearch('conf', {})) || $opt_f) {
+
+  foreach my $item ( $conf->config_items() ) {
+    insert_config_item($item,$dir);
+  }
+
+  # ugly pseudo false laziness with Conf.pm 
+  foreach my $item ( map { my $basename = basename($_);
+                           $basename =~ /^(.*)$/;
+                           $basename = $1;
+                           new FS::ConfItem {
+                             'key'         => $basename,
+                             'type'        => '',
+                           }
+                         } glob($dir. '/invoice_template_*'),
+                           glob($dir. '/invoice_html_*'),
+                           glob($dir. '/invoice_htmlnotes_*'),
+                           glob($dir. '/invoice_latex_*'),
+                           glob($dir. '/invoice_latexnotes_*')
+                   ) {
+
+    insert_config_item($item,$dir);
+
+  }
+
+  foreach my $item ( map { my $basename = basename($_);
+                           $basename =~ /^(.*)$/;
+                           $basename = $1;
+                           new FS::ConfItem {
+                             'key'         => $basename,
+                             'type'        => 'binary',
+                           }
+                         } glob($dir. '/logo_*.png'),
+                           glob($dir. '/logo_*.eps')
+                   ) {
+
+    insert_config_item($item,$dir);
+
+  }
+
+}
+
+warn "Freeside database initialized - committing transaction\n" if $opt_v;
+
+dbh->commit or die dbh->errstr;
+dbh->disconnect or die dbh->errstr;
+
+warn "Configuration initialization committed successfully\n" if $opt_v;
+
+sub insert_config_item {
+  local $/;
+  my ($item,$dir) = @_;
+  my $key = $item->key;
+  if (-e "$dir/$key") {
+    warn "Inserting $key\n" if $opt_v;
+    my $value = readline(new IO::File "$dir/$key");
+    if ($item->type eq 'binary'){
+      $conf->set_binary($key, $value);
+    }else{
+      $conf->set($key, $value);
+    }
+  }
+}
+
+sub usage {
+  die "Usage:\n  freeside-init-config directory [ -v ] [ -f ]\n"
+  # [ -u user ] for devel/multi-db installs
+}
+
+1;
index ddc210f..bce5a0a 100755 (executable)
@@ -19,12 +19,15 @@ die "Not running uid freeside!" unless checkeuid();
 #  map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib;
 
 getopts("u:vd:");
-#my $user = shift or die &usage;
+my $config_dir = shift || 'conf' ;
+$config_dir =~ /^([\w.:=]+)$/
+  or die "unacceptable configuration directory name";
+$config_dir = $1;
 
-getsecrets($opt_u); #$user);
+getsecrets($opt_u);
 
 #needs to match FS::Record
-my($dbdef_file) = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
+my($dbdef_file) = "/usr/local/etc/newtest/dbdef.". datasrc;
 
 ###
 
@@ -88,7 +91,9 @@ $dbdef->save($dbdef_file);
 ###
 
 $FS::CurrentUser::upgrade_hack = 1;
+$FS::UID::callback_hack = 1;
 my $dbh = adminsuidsetup $opt_u; #$user;
+$FS::UID::callback_hack = 0;
 
 #create tables
 $|=1;
@@ -105,6 +110,20 @@ dbdef_create($dbh, $dbdef_file);
 delete $FS::Schema::dbdef_cache{$dbdef_file}; #force an actual reload
 reload_dbdef($dbdef_file);
 
+warn "Freeside schema initialized - commiting transaction\n" if $opt_v;
+
+$dbh->commit or die $dbh->errstr;
+$dbh->disconnect or die $dbh->errstr;
+
+warn "Database schema committed successfully\n" if $opt_v;
+
+my $init_config = "freeside-init-config";
+$init_config .= " -v" if $opt_v;
+$init_config .= " -u $opt_u" if $opt_u;
+$init_config .= " $config_dir";
+system "$init_config" ;
+
+$dbh = adminsuidsetup $opt_u;
 create_initial_data('domain' => $opt_d);
 
 warn "Freeside database initialized - commiting transaction\n" if $opt_v;
@@ -121,7 +140,7 @@ sub dbdef_create { # reverse engineer the schema from the DB and save to file
 }
 
 sub usage {
-  die "Usage:\n  freeside-setup -d domain.name [ -v ]\n"
+  die "Usage:\n  freeside-setup -d domain.name [ -v ] [ config/dir ]\n"
   # [ -u user ] for devel/multi-db installs
 }
 
index 3a4e4f8..5c646fe 100755 (executable)
@@ -46,6 +46,12 @@ dbdef_create($dbh, $dbdef_file);
 
 $dbh->disconnect or die $dbh->errstr;
 
+unless ( $DRY_RUN ) {
+  my $init_config = "freeside-init-config -u $user ";
+  $init_config .= "%%%FREESIDE_CONF%%%/conf.". datasrc;
+  system "$init_config" ;
+}
+
 ###
 
 sub dbdef_create { # reverse engineer the schema from the DB and save to file
@@ -64,7 +70,7 @@ freeside-upgrade - Upgrades database schema for new freeside verisons.
 
 =head1 SYNOPSIS
 
-  freeside-adduser [ -d ] [ -q | -v ]
+  freeside-upgrade [ -d ] [ -q | -v ]
 
 =head1 DESCRIPTION
 
diff --git a/FS/t/conf.t b/FS/t/conf.t
new file mode 100644 (file)
index 0000000..5e52079
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::conf;
+$loaded=1;
+print "ok 1\n";
index d1b471a..cf557f4 100644 (file)
@@ -86,7 +86,7 @@
     \returninset\r
     \makebox{\r
       \begin{tabular}{ll}\r
-        \includegraphics{[@-- $conf_dir --@]/logo.eps} &\r
+        \includegraphics{[@-- $logo_file --@]} &\r
         \begin{minipage}[b]{5.5cm}\r
 [@-- $returnaddress --@]\r
         \end{minipage}\r
@@ -94,7 +94,7 @@
     }\r
   }\r
   { % ... pages\r
-    %\includegraphics{[@-- $conf_dir --@]/logo.eps}    % Uncomment if you want the logo on all pages.\r
+    %\includegraphics{[@-- $logo_file --@]}    % Uncomment if you want the logo on all pages.\r
   }\r
 }\r
 \r
diff --git a/httemplate/config/config-download.cgi b/httemplate/config/config-download.cgi
new file mode 100644 (file)
index 0000000..d4b88de
--- /dev/null
@@ -0,0 +1,14 @@
+%
+%
+%my $conf=new FS::Conf;
+%
+%http_header('Content-Type' => 'application/x-unknown' );
+%
+%die "No configuration variable specified (bad URL)!" # umm
+%  unless $cgi->keywords;
+%my($query) = $cgi->keywords;
+%$query =~  /^([\w -\)+-\/@;:?=[\]]+)$/;
+%my $name = $1;
+%
+%http_header('Content-Disposition' => "attachment; filename=$name" );
+% print $conf->config_binary($name);
index d8f0d8e..3e49b4f 100644 (file)
@@ -1,5 +1,4 @@
 <%init>
-
 die "access denied\n"
   unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
 
@@ -28,6 +27,16 @@ foreach my $i ( @config_items ) {
       } else {
         $conf->delete($i->key);
       }
+    } elsif ( $type eq 'binary' ) {
+      if ( defined($cgi->param($i->key. $n)) && $cgi->param($i->key. $n) ) {
+        my $fh = $cgi->upload($i->key. $n);
+        if (defined($fh)) {
+          local $/;
+          $conf->set_binary($i->key, <$fh>);
+        }
+      }else{
+        warn "Condition failed for " . $i->key;
+      }
     } elsif ( $type eq 'checkbox' ) {
 #        if ( defined($cgi->param($i->key. $n)) && $cgi->param($i->key. $n) ) {
       if ( defined $cgi->param($i->key. $n) ) {
@@ -57,6 +66,5 @@ foreach my $i ( @config_items ) {
   $conf->touch($_) foreach @touch;
   $conf->delete($_) foreach @delete;
 }
-
 </%init>
 <% $cgi->redirect("config-view.cgi") %>
index 91ba337..7f2a1b2 100644 (file)
             <tr>
               <td><font color="#ff0000">no type</font></td>
             </tr>
+% } elsif (   $type eq 'binary' ) {
+
+            <tr>
+              <% $conf->exists($i->key)
+                   ? qq!<a href="config-download.cgi?!. $i->key. qq!">download</a>!
+                   : 'empty'
+              %>
+            </tr>
 % } elsif (   $type eq 'textarea'
 %                      || $type eq 'editlist'
 %                      || $type eq 'selectmultiple' ) { 
index 6c3a51a..df9af47 100644 (file)
@@ -21,7 +21,7 @@ function SafeOnsubmit() {
 % my $conf = new FS::Conf; my @config_items = $conf->config_items; 
 
 
-<form name="OneTrueForm" action="config-process.cgi" METHOD="POST" onSubmit="SafeOnsubmit()">
+<form name="OneTrueForm" action="config-process.cgi" METHOD="POST" enctype="multipart/form-data" onSubmit="SafeOnsubmit()">
 % foreach my $section ( qw(required billing username password UI session
 %                            shell BIND
 %                           ),
@@ -61,6 +61,10 @@ function SafeOnsubmit() {
 
 
                <font color="#ff0000">no type</font>
+% } elsif ( $type eq 'binary' ) { 
+
+
+               Filename <input type="file" name="<% $i->key. $n %>">
 % } elsif ( $type eq 'textarea' ) {