package FS::Conf;
-use vars qw($base_dir @config_items @card_types $DEBUG );
+use vars qw($base_dir @config_items @base_items @card_types $DEBUG);
+use IO::File;
+use File::Basename;
use MIME::Base64;
use FS::ConfItem;
use FS::ConfDefaults;
+use FS::Conf_compat17;
use FS::conf;
use FS::Record qw(qsearch qsearchs);
-use FS::UID qw(dbh);
+use FS::UID qw(dbh datasrc use_confcompat);
$base_dir = '%%%FREESIDE_CONF%%%';
-
$DEBUG = 0;
=head1 NAME
=cut
+sub _usecompat {
+ my ($self, $method) = (shift, shift);
+ warn "NO CONFIGURATION RECORDS FOUND -- USING COMPATIBILITY MODE"
+ if use_confcompat;
+ my $compat = new FS::Conf_compat17 ("$base_dir/conf." . datasrc);
+ $compat->$method(@_);
+}
+
sub _config {
my($self,$name,$agent)=@_;
my $hashref = { 'name' => $name };
}
sub config {
- my($self,$name,$agent)=@_;
+ my $self = shift;
+ return $self->_usecompat('config', @_) if use_confcompat;
+
+ my($name,$agent)=@_;
my $cv = $self->_config($name, $agent) or return;
if ( wantarray ) {
- split "\n", $cv->value;
+ my $v = $cv->value;
+ chomp $v;
+ (split "\n", $v, -1);
} else {
(split("\n", $cv->value))[0];
}
=cut
sub config_binary {
- my($self,$name,$agent)=@_;
+ my $self = shift;
+ return $self->_usecompat('config_binary', @_) if use_confcompat;
+
+ my($name,$agent)=@_;
my $cv = $self->_config($name, $agent) or return;
decode_base64($cv->value);
}
=cut
sub exists {
- my($self,$name,$agent)=@_;
+ my $self = shift;
+ return $self->_usecompat('exists', @_) if use_confcompat;
+
+ my($name,$agent)=@_;
defined($self->_config($name, $agent));
}
=cut
sub config_orbase {
- my( $self, $name, $suffix ) = @_;
+ my $self = shift;
+ return $self->_usecompat('config_orbase', @_) if use_confcompat;
+
+ my( $name, $suffix ) = @_;
if ( $self->exists("${name}_$suffix") ) {
$self->config("${name}_$suffix");
} else {
=cut
sub touch {
- my($self, $name, $agent) = @_;
+ my $self = shift;
+ return $self->_usecompat('touch', @_) if use_confcompat;
+
+ my($name, $agent) = @_;
$self->set($name, '', $agent);
}
=cut
sub set {
- my($self, $name, $value, $agent) = @_;
+ my $self = shift;
+ return $self->_usecompat('set', @_) if use_confcompat;
+
+ my($name, $value, $agent) = @_;
$value =~ /^(.*)$/s;
$value = $1;
- warn "[FS::Conf] SET $file\n" if $DEBUG;
+ warn "[FS::Conf] SET $name\n" if $DEBUG;
my $old = FS::Record::qsearchs('conf', {name => $name, agent => $agent});
my $new = new FS::conf { $old ? $old->hash
=cut
sub set_binary {
- my($self,$name, $value, $agent)=@_;
+ my $self = shift;
+ return if use_confcompat;
+
+ my($name, $value, $agent)=@_;
$self->set($name, encode_base64($value), $agent);
}
=cut
sub delete {
- my($self, $name, $agent) = @_;
+ my $self = shift;
+ return $self->_usecompat('delete', @_) if use_confcompat;
+
+ my($name, $agent) = @_;
if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agent => $agent}) ) {
warn "[FS::Conf] DELETE $file\n";
}
}
+=item import_config_item CONFITEM DIR
+
+ Imports the item specified by the CONFITEM (see L<FS::ConfItem>) into
+the database as a conf record (see L<FS::conf>). Imports from the file
+in the directory DIR.
+
+=cut
+
+sub import_config_item {
+ my ($self,$item,$dir) = @_;
+ my $key = $item->key;
+ if ( -e "$dir/$key" && ! use_confcompat ) {
+ warn "Inserting $key\n" if $DEBUG;
+ local $/;
+ my $value = readline(new IO::File "$dir/$key");
+ if ($item->type eq 'binary') {
+ $self->set_binary($key, $value);
+ }else{
+ $self->set($key, $value);
+ }
+ }else {
+ warn "Not inserting $key\n" if $DEBUG;
+ }
+}
+
+=item verify_config_item CONFITEM DIR
+
+ Compares the item specified by the CONFITEM (see L<FS::ConfItem>) in
+the database to the legacy file value in DIR.
+
+=cut
+
+sub verify_config_item {
+ return '' if use_confcompat;
+ my ($self,$item,$dir) = @_;
+ my $key = $item->key;
+ my $type = $item->type;
+
+ my $compat = new FS::Conf_compat17 $dir;
+ my $error = '';
+
+ $error .= "$key fails existential comparison; "
+ if $self->exists($key) xor $compat->exists($key);
+
+ unless ($type eq 'binary') {
+ {
+ no warnings;
+ $error .= "$key fails scalar comparison; "
+ unless scalar($self->config($key)) eq scalar($compat->config($key));
+ }
+
+ my (@new) = $self->config($key);
+ my (@old) = $compat->config($key);
+ unless ( scalar(@new) == scalar(@old)) {
+ $error .= "$key fails list comparison; ";
+ }else{
+ my $r=1;
+ foreach (@old) { $r=0 if ($_ cmp shift(@new)); }
+ $error .= "$key fails list comparison; "
+ unless $r;
+ }
+ }
+
+ if ($type eq 'binary') {
+ $error .= "$key fails binary comparison; "
+ unless scalar($self->config_binary($key)) eq scalar($compat->config_binary($key));
+ }
+
+ if ($error =~ /existential comparison/ && $item->section eq 'deprecated') {
+ my $proto;
+ for ( @config_items ) { $proto = $_; last if $proto->key eq $key; }
+ unless ($proto->key eq $key) {
+ warn "removed config item $error\n" if $DEBUG;
+ $error = '';
+ }
+ }
+
+ $error;
+}
+
+#item _orbase_items OPTIONS
+#
+#Returns all of the possible extensible config items as FS::ConfItem objects.
+#See #L<FS::ConfItem>. OPTIONS consists of name value pairs. Possible
+#options include
+#
+# dir - the directory to search for configuration option files instead
+# of using the conf records in the database
+#
+#cut
+
+#quelle kludge
+sub _orbase_items {
+ my ($self, %opt) = @_;
+
+ my $listmaker = sub { my $v = shift;
+ $v =~ s/_/!_/g;
+ if ( $v =~ /\.(png|eps)$/ ) {
+ $v =~ s/\./!_%./;
+ }else{
+ $v .= '!_%';
+ }
+ map { $_->name }
+ FS::Record::qsearch( 'conf',
+ {},
+ '',
+ "WHERE name LIKE '$v' ESCAPE '!'"
+ );
+ };
+
+ if (exists($opt{dir}) && $opt{dir}) {
+ $listmaker = sub { my $v = shift;
+ if ( $v =~ /\.(png|eps)$/ ) {
+ $v =~ s/\./_*./;
+ }else{
+ $v .= '_*';
+ }
+ map { basename $_ } glob($opt{dir}. "/$v" );
+ };
+ }
+
+ ( map {
+ my $proto;
+ my $base = $_;
+ for ( @config_items ) { $proto = $_; last if $proto->key eq $base; }
+ die "don't know about $base items" unless $proto->key eq $base;
+
+ map { new FS::ConfItem {
+ 'key' => $_,
+ 'section' => $proto->section,
+ 'description' => 'Alternate ' . $proto->description . ' See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => $proto->type,
+ };
+ } &$listmaker($base);
+ } @base_items,
+ );
+}
+
=item config_items
-Returns all of the possible configuration items as FS::ConfItem objects. See
-L<FS::ConfItem>.
+Returns all of the possible global/default configuration items as
+FS::ConfItem objects. See L<FS::ConfItem>.
=cut
sub config_items {
my $self = shift;
- #quelle kludge
- @config_items,
- ( map {
- new FS::ConfItem {
- 'key' => $_->name,
- 'section' => 'billing',
- 'description' => 'Alternate template file for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
- 'type' => 'textarea',
- }
- } 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 {
- new FS::ConfItem {
- 'key' => $_->name,
- 'section' => 'billing',
- 'description' => 'Alternate HTML template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
- 'type' => 'textarea',
- }
- } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_html!_%' ESCAPE '!'")
- ),
- ( map {
- ($latexname = $_->name ) =~ s/latex/html/;
- new FS::ConfItem {
- 'key' => $_->name,
- 'section' => 'billing',
- 'description' => "Alternate Notes section for HTML invoices. Defaults to the same data in $latexname if not specified.",
- 'type' => 'textarea',
- }
- } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_htmlnotes!_%' ESCAPE '!'")
- ),
- ( map {
- new FS::ConfItem {
- 'key' => $_->name,
- 'section' => 'billing',
- 'description' => 'Alternate LaTeX template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
- 'type' => 'textarea',
- }
- } 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 {
- new FS::ConfItem {
- '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',
- }
- } FS::Record::qsearch('conf', {}, '', "WHERE name LIKE 'invoice!_latexnotes!_%' ESCAPE '!'")
- );
+ return $self->_usecompat('config_items', @_) if use_confcompat;
+
+ ( @config_items, $self->_orbase_items(@_) );
+}
+
+=back
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item init-config DIR
+
+Imports the non-deprecated configuration items from DIR (1.7 compatible)
+to conf records in the database.
+
+=cut
+
+sub init_config {
+ my $dir = shift;
+
+ {
+ local $FS::UID::use_confcompat = 0;
+ my $conf = new FS::Conf;
+ foreach my $item ( $conf->config_items(dir => $dir) ) {
+ $conf->import_config_item($item, $dir);
+ my $error = $conf->verify_config_item($item, $dir);
+ return $error if $error;
+ }
+
+ my $compat = new FS::Conf_compat17 $dir;
+ foreach my $item ( $compat->config_items ) {
+ my $error = $conf->verify_config_item($item, $dir);
+ return $error if $error;
+ }
+ }
+
+ $FS::UID::use_confcompat = 0;
+ ''; #success
}
=back
"Solo",
);
+@base_items = qw (
+ invoice_template
+ invoice_latex
+ invoice_latexreturnaddress
+ invoice_latexfooter
+ invoice_latexsmallfooter
+ invoice_latexnotes
+ invoice_html
+ invoice_htmlreturnaddress
+ invoice_htmlfooter
+ invoice_htmlnotes
+ logo.png
+ logo.eps
+ );
+
@config_items = map { new FS::ConfItem $_ } (
{
},
{
+ 'key' => 'welcome_letter',
+ 'section' => '',
+ 'description' => 'Optional LaTex template file for a printed welcome letter. A welcome letter is printed the first time a cust_pkg record is created. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation and the billing documentation for details on the template substitution language. A variable exists for each fieldname in the customer record (<code>$first, $last, etc</code>). The following additional variables are available<ul><li><code>$payby</code> - a friendler represenation of the field<li><code>$payinfo</code> - the masked payment information<li><code>$expdate</code> - the time at which the payment method expires (a UNIX timestamp)<li><code>$returnaddress</code> - the invoice return address for this customer\'s agent</ul>',
+ 'type' => 'textarea',
+ },
+
+ {
'key' => 'warning_email',
'section' => '',
'description' => 'Template file for warning email. Warning emails are sent to the customer email invoice destination(s) each time a svc_acct record has its usage drop below a threshold or 0. See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the template substitution language. The following variables are available<ul><li><code>$username</code> <li><code>$password</code> <li><code>$first</code> <li><code>$last</code> <li><code>$pkg</code> <li><code>$column</code> <li><code>$amount</code> <li><code>$threshold</code></ul>',
'type' => 'text',
},
+ {
+ 'key' => 'disable_line_item_date_ranges',
+ 'section' => 'billing',
+ 'description' => 'Prevent freeside from automatically generating date ranges on invoice line items.',
+ 'type' => 'checkbox',
+ },
+
);
1;
-