From fbfffdabe931d704aae420984058e61dc4196b01 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 27 Feb 2007 01:51:52 +0000 Subject: [PATCH] config goes in database --- FS/FS/Conf.pm | 244 +++++++++++++++++++--------------- FS/FS/Schema.pm | 12 ++ FS/FS/UID.pm | 29 ++-- FS/FS/conf.pm | 114 ++++++++++++++++ FS/FS/cust_bill.pm | 28 +++- FS/FS/cust_main.pm | 2 +- FS/FS/svc_acct.pm | 43 +++--- FS/MANIFEST | 2 + FS/bin/freeside-init-config | 92 +++++++++++++ FS/bin/freeside-setup | 27 +++- FS/bin/freeside-upgrade | 8 +- FS/t/conf.t | 5 + conf/invoice_latex | 4 +- httemplate/config/config-download.cgi | 14 ++ httemplate/config/config-process.cgi | 12 +- httemplate/config/config-view.cgi | 8 ++ httemplate/config/config.cgi | 6 +- 17 files changed, 494 insertions(+), 156 deletions(-) create mode 100644 FS/FS/conf.pm create mode 100755 FS/bin/freeside-init-config create mode 100644 FS/t/conf.t create mode 100644 httemplate/config/config-download.cgi diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 2f01e1f40..594f3b3eb 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -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 billing documentation 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 billing documentation 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 billing documentation 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 billing documentation 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.', diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index bae7522f8..d9d5f5a03 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -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' => { diff --git a/FS/FS/UID.pm b/FS/FS/UID.pm index 8dd928ec7..da573a698 100644 --- a/FS/FS/UID.pm +++ b/FS/FS/UID.pm @@ -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 index 000000000..6126372cc --- /dev/null +++ b/FS/FS/conf.pm @@ -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 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, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 844d1b867..13174487d 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -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 and L 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 () { @@ -2212,7 +2231,7 @@ L and L 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 () { diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index d775e75d6..fe6aa50a7 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -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 diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index 48116d761..a06f4d797 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -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'; diff --git a/FS/MANIFEST b/FS/MANIFEST index 82f106412..597cd366b 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -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 index 000000000..a186d1a85 --- /dev/null +++ b/FS/bin/freeside-init-config @@ -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; diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup index ddc210f50..bce5a0aeb 100755 --- a/FS/bin/freeside-setup +++ b/FS/bin/freeside-setup @@ -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 } diff --git a/FS/bin/freeside-upgrade b/FS/bin/freeside-upgrade index 3a4e4f8e3..5c646fec2 100755 --- a/FS/bin/freeside-upgrade +++ b/FS/bin/freeside-upgrade @@ -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 index 000000000..5e52079f6 --- /dev/null +++ b/FS/t/conf.t @@ -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"; diff --git a/conf/invoice_latex b/conf/invoice_latex index d1b471a4b..cf557f4e4 100644 --- a/conf/invoice_latex +++ b/conf/invoice_latex @@ -86,7 +86,7 @@ \returninset \makebox{ \begin{tabular}{ll} - \includegraphics{[@-- $conf_dir --@]/logo.eps} & + \includegraphics{[@-- $logo_file --@]} & \begin{minipage}[b]{5.5cm} [@-- $returnaddress --@] \end{minipage} @@ -94,7 +94,7 @@ } } { % ... pages - %\includegraphics{[@-- $conf_dir --@]/logo.eps} % Uncomment if you want the logo on all pages. + %\includegraphics{[@-- $logo_file --@]} % Uncomment if you want the logo on all pages. } } diff --git a/httemplate/config/config-download.cgi b/httemplate/config/config-download.cgi new file mode 100644 index 000000000..d4b88ded9 --- /dev/null +++ b/httemplate/config/config-download.cgi @@ -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); diff --git a/httemplate/config/config-process.cgi b/httemplate/config/config-process.cgi index d8f0d8e93..3e49b4f99 100644 --- a/httemplate/config/config-process.cgi +++ b/httemplate/config/config-process.cgi @@ -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; } - <% $cgi->redirect("config-view.cgi") %> diff --git a/httemplate/config/config-view.cgi b/httemplate/config/config-view.cgi index 91ba33769..7f2a1b293 100644 --- a/httemplate/config/config-view.cgi +++ b/httemplate/config/config-view.cgi @@ -42,6 +42,14 @@ no type +% } elsif ( $type eq 'binary' ) { + + + <% $conf->exists($i->key) + ? qq!download! + : 'empty' + %> + % } elsif ( $type eq 'textarea' % || $type eq 'editlist' % || $type eq 'selectmultiple' ) { diff --git a/httemplate/config/config.cgi b/httemplate/config/config.cgi index 6c3a51aca..df9af47a6 100644 --- a/httemplate/config/config.cgi +++ b/httemplate/config/config.cgi @@ -21,7 +21,7 @@ function SafeOnsubmit() { % my $conf = new FS::Conf; my @config_items = $conf->config_items; -
+ % foreach my $section ( qw(required billing username password UI session % shell BIND % ), @@ -61,6 +61,10 @@ function SafeOnsubmit() { no type +% } elsif ( $type eq 'binary' ) { + + + Filename % } elsif ( $type eq 'textarea' ) { -- 2.11.0