config in database cleanup, editing, and agent-specific config (452, 1419)
[freeside.git] / FS / FS / Conf.pm
1 package FS::Conf;
2
3 use vars qw($base_dir @config_items @base_items @card_types $DEBUG);
4 use IO::File;
5 use File::Basename;
6 use MIME::Base64;
7 use FS::ConfItem;
8 use FS::ConfDefaults;
9 use FS::Conf_compat17;
10 use FS::conf;
11 use FS::Record qw(qsearch qsearchs);
12 use FS::UID qw(dbh datasrc use_confcompat);
13
14 $base_dir = '%%%FREESIDE_CONF%%%';
15
16 $DEBUG = 0;
17
18 =head1 NAME
19
20 FS::Conf - Freeside configuration values
21
22 =head1 SYNOPSIS
23
24   use FS::Conf;
25
26   $conf = new FS::Conf;
27
28   $value = $conf->config('key');
29   @list  = $conf->config('key');
30   $bool  = $conf->exists('key');
31
32   $conf->touch('key');
33   $conf->set('key' => 'value');
34   $conf->delete('key');
35
36   @config_items = $conf->config_items;
37
38 =head1 DESCRIPTION
39
40 Read and write Freeside configuration values.  Keys currently map to filenames,
41 but this may change in the future.
42
43 =head1 METHODS
44
45 =over 4
46
47 =item new
48
49 Create a new configuration object.
50
51 =cut
52
53 sub new {
54   my($proto) = @_;
55   my($class) = ref($proto) || $proto;
56   my($self) = { 'base_dir' => $base_dir };
57   bless ($self, $class);
58 }
59
60 =item base_dir
61
62 Returns the base directory.  By default this is /usr/local/etc/freeside.
63
64 =cut
65
66 sub base_dir {
67   my($self) = @_;
68   my $base_dir = $self->{base_dir};
69   -e $base_dir or die "FATAL: $base_dir doesn't exist!";
70   -d $base_dir or die "FATAL: $base_dir isn't a directory!";
71   -r $base_dir or die "FATAL: Can't read $base_dir!";
72   -x $base_dir or die "FATAL: $base_dir not searchable (executable)!";
73   $base_dir =~ /^(.*)$/;
74   $1;
75 }
76
77 =item config KEY [ AGENTNUM ]
78
79 Returns the configuration value or values (depending on context) for key.
80 The optional agent number selects an agent specific value instead of the
81 global default if one is present.
82
83 =cut
84
85 sub _usecompat {
86   my ($self, $method) = (shift, shift);
87   warn "NO CONFIGURATION RECORDS FOUND -- USING COMPATIBILITY MODE"
88     if use_confcompat;
89   my $compat = new FS::Conf_compat17 ("$base_dir/conf." . datasrc);
90   $compat->$method(@_);
91 }
92
93 sub _config {
94   my($self,$name,$agentnum)=@_;
95   my $hashref = { 'name' => $name };
96   $hashref->{agentnum} = $agentnum;
97   local $FS::Record::conf = undef;  # XXX evil hack prevents recursion
98   my $cv = FS::Record::qsearchs('conf', $hashref);
99   if (!$cv && defined($agentnum)) {
100     $hashref->{agentnum} = '';
101     $cv = FS::Record::qsearchs('conf', $hashref);
102   }
103   return $cv;
104 }
105
106 sub config {
107   my $self = shift;
108   return $self->_usecompat('config', @_) if use_confcompat;
109
110   my($name,$agentnum)=@_;
111   my $cv = $self->_config($name, $agentnum) or return;
112
113   if ( wantarray ) {
114     my $v = $cv->value;
115     chomp $v;
116     (split "\n", $v, -1);
117   } else {
118     (split("\n", $cv->value))[0];
119   }
120 }
121
122 =item config_binary KEY [ AGENTNUM ]
123
124 Returns the exact scalar value for key.
125
126 =cut
127
128 sub config_binary {
129   my $self = shift;
130   return $self->_usecompat('config_binary', @_) if use_confcompat;
131
132   my($name,$agentnum)=@_;
133   my $cv = $self->_config($name, $agentnum) or return;
134   decode_base64($cv->value);
135 }
136
137 =item exists KEY [ AGENTNUM ]
138
139 Returns true if the specified key exists, even if the corresponding value
140 is undefined.
141
142 =cut
143
144 sub exists {
145   my $self = shift;
146   return $self->_usecompat('exists', @_) if use_confcompat;
147
148   my($name,$agentnum)=@_;
149   defined($self->_config($name, $agentnum));
150 }
151
152 #=item config_orbase KEY SUFFIX
153 #
154 #Returns the configuration value or values (depending on context) for 
155 #KEY_SUFFIX, if it exists, otherwise for KEY
156 #
157 #=cut
158
159 # outmoded as soon as we shift to agentnum based config values
160 sub config_orbase {
161   my $self = shift;
162   return $self->_usecompat('config_orbase', @_) if use_confcompat;
163
164   my( $name, $suffix ) = @_;
165   if ( $self->exists("${name}_$suffix") ) {
166     $self->config("${name}_$suffix");
167   } else {
168     $self->config($name);
169   }
170 }
171
172 =item touch KEY [ AGENT ];
173
174 Creates the specified configuration key if it does not exist.
175
176 =cut
177
178 sub touch {
179   my $self = shift;
180   return $self->_usecompat('touch', @_) if use_confcompat;
181
182   my($name, $agentnum) = @_;
183   unless ( $self->exists($name, $agentnum) ) {
184     $self->set($name, '', $agentnum);
185   }
186 }
187
188 =item set KEY VALUE [ AGENTNUM ];
189
190 Sets the specified configuration key to the given value.
191
192 =cut
193
194 sub set {
195   my $self = shift;
196   return $self->_usecompat('set', @_) if use_confcompat;
197
198   my($name, $value, $agentnum) = @_;
199   $value =~ /^(.*)$/s;
200   $value = $1;
201
202   warn "[FS::Conf] SET $name\n" if $DEBUG;
203
204   my $old = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum});
205   my $new = new FS::conf { $old ? $old->hash 
206                                 : ('name' => $name, 'agentnum' => $agentnum)
207                          };
208   $new->value($value);
209
210   my $error;
211   if ($old) {
212     $error = $new->replace($old);
213   } else {
214     $error = $new->insert;
215   }
216
217   die "error setting configuration value: $error \n"
218     if $error;
219
220 }
221
222 =item set_binary KEY VALUE [ AGENTNUM ]
223
224 Sets the specified configuration key to an exact scalar value which
225 can be retrieved with config_binary.
226
227 =cut
228
229 sub set_binary {
230   my $self  = shift;
231   return if use_confcompat;
232
233   my($name, $value, $agentnum)=@_;
234   $self->set($name, encode_base64($value), $agentnum);
235 }
236
237 =item delete KEY [ AGENTNUM ];
238
239 Deletes the specified configuration key.
240
241 =cut
242
243 sub delete {
244   my $self = shift;
245   return $self->_usecompat('delete', @_) if use_confcompat;
246
247   my($name, $agentnum) = @_;
248   if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum}) ) {
249     warn "[FS::Conf] DELETE $name\n";
250
251     my $oldAutoCommit = $FS::UID::AutoCommit;
252     local $FS::UID::AutoCommit = 0;
253     my $dbh = dbh;
254
255     my $error = $cv->delete;
256
257     if ( $error ) {
258       $dbh->rollback if $oldAutoCommit;
259       die "error setting configuration value: $error \n"
260     }
261
262     $dbh->commit or die $dbh->errstr if $oldAutoCommit;
263
264   }
265 }
266
267 =item import_config_item CONFITEM DIR 
268
269   Imports the item specified by the CONFITEM (see L<FS::ConfItem>) into
270 the database as a conf record (see L<FS::conf>).  Imports from the file
271 in the directory DIR.
272
273 =cut
274
275 sub import_config_item { 
276   my ($self,$item,$dir) = @_;
277   my $key = $item->key;
278   if ( -e "$dir/$key" && ! use_confcompat ) {
279     warn "Inserting $key\n" if $DEBUG;
280     local $/;
281     my $value = readline(new IO::File "$dir/$key");
282     if ($item->type eq 'binary') {
283       $self->set_binary($key, $value);
284     }else{
285       $self->set($key, $value);
286     }
287   }else {
288     warn "Not inserting $key\n" if $DEBUG;
289   }
290 }
291
292 =item verify_config_item CONFITEM DIR 
293
294   Compares the item specified by the CONFITEM (see L<FS::ConfItem>) in
295 the database to the legacy file value in DIR.
296
297 =cut
298
299 sub verify_config_item { 
300   return '' if use_confcompat;
301   my ($self,$item,$dir) = @_;
302   my $key = $item->key;
303   my $type = $item->type;
304
305   my $compat = new FS::Conf_compat17 $dir;
306   my $error = '';
307   
308   $error .= "$key fails existential comparison; "
309     if $self->exists($key) xor $compat->exists($key);
310
311   unless ($type eq 'binary') {
312     {
313       no warnings;
314       $error .= "$key fails scalar comparison; "
315         unless scalar($self->config($key)) eq scalar($compat->config($key));
316     }
317
318     my (@new) = $self->config($key);
319     my (@old) = $compat->config($key);
320     unless ( scalar(@new) == scalar(@old)) { 
321       $error .= "$key fails list comparison; ";
322     }else{
323       my $r=1;
324       foreach (@old) { $r=0 if ($_ cmp shift(@new)); }
325       $error .= "$key fails list comparison; "
326         unless $r;
327     }
328   }
329
330   if ($type eq 'binary') {
331     $error .= "$key fails binary comparison; "
332       unless scalar($self->config_binary($key)) eq scalar($compat->config_binary($key));
333   }
334
335   if ($error =~ /existential comparison/ && $item->section eq 'deprecated') {
336     my $proto;
337     for ( @config_items ) { $proto = $_; last if $proto->key eq $key;  }
338     unless ($proto->key eq $key) { 
339       warn "removed config item $error\n" if $DEBUG;
340       $error = '';
341     }
342   }
343
344   $error;
345 }
346
347 #item _orbase_items OPTIONS
348 #
349 #Returns all of the possible extensible config items as FS::ConfItem objects.
350 #See #L<FS::ConfItem>.  OPTIONS consists of name value pairs.  Possible
351 #options include
352 #
353 # dir - the directory to search for configuration option files instead
354 #       of using the conf records in the database
355 #
356 #cut
357
358 #quelle kludge
359 sub _orbase_items {
360   my ($self, %opt) = @_; 
361
362   my $listmaker = sub { my $v = shift;
363                         $v =~ s/_/!_/g;
364                         if ( $v =~ /\.(png|eps)$/ ) {
365                           $v =~ s/\./!_%./;
366                         }else{
367                           $v .= '!_%';
368                         }
369                         map { $_->name }
370                           FS::Record::qsearch( 'conf',
371                                                {},
372                                                '',
373                                                "WHERE name LIKE '$v' ESCAPE '!'"
374                                              );
375                       };
376
377   if (exists($opt{dir}) && $opt{dir}) {
378     $listmaker = sub { my $v = shift;
379                        if ( $v =~ /\.(png|eps)$/ ) {
380                          $v =~ s/\./_*./;
381                        }else{
382                          $v .= '_*';
383                        }
384                        map { basename $_ } glob($opt{dir}. "/$v" );
385                      };
386   }
387
388   ( map { 
389           my $proto;
390           my $base = $_;
391           for ( @config_items ) { $proto = $_; last if $proto->key eq $base;  }
392           die "don't know about $base items" unless $proto->key eq $base;
393
394           map { new FS::ConfItem { 
395                                    'key' => $_,
396                                    'section' => $proto->section,
397                                    'description' => 'Alternate ' . $proto->description . '  See the <a href="../docs/billing.html">billing documentation</a> for details.',
398                                    'type' => $proto->type,
399                                  };
400               } &$listmaker($base);
401         } @base_items,
402   );
403 }
404
405 =item config_items
406
407 Returns all of the possible global/default configuration items as
408 FS::ConfItem objects.  See L<FS::ConfItem>.
409
410 =cut
411
412 sub config_items {
413   my $self = shift; 
414   return $self->_usecompat('config_items', @_) if use_confcompat;
415
416   ( @config_items, $self->_orbase_items(@_) );
417 }
418
419 =back
420
421 =head1 SUBROUTINES
422
423 =over 4
424
425 =item init-config DIR
426
427 Imports the non-deprecated configuration items from DIR (1.7 compatible)
428 to conf records in the database.
429
430 =cut
431
432 sub init_config {
433   my $dir = shift;
434
435   {
436     local $FS::UID::use_confcompat = 0;
437     my $conf = new FS::Conf;
438     foreach my $item ( $conf->config_items(dir => $dir) ) {
439       $conf->import_config_item($item, $dir);
440       my $error = $conf->verify_config_item($item, $dir);
441       return $error if $error;
442     }
443   
444     my $compat = new FS::Conf_compat17 $dir;
445     foreach my $item ( $compat->config_items ) {
446       my $error = $conf->verify_config_item($item, $dir);
447       return $error if $error;
448     }
449   }
450
451   $FS::UID::use_confcompat = 0;
452   '';  #success
453 }
454
455 =back
456
457 =head1 BUGS
458
459 If this was more than just crud that will never be useful outside Freeside I'd
460 worry that config_items is freeside-specific and icky.
461
462 =head1 SEE ALSO
463
464 "Configuration" in the web interface (config/config.cgi).
465
466 httemplate/docs/config.html
467
468 =cut
469
470 #Business::CreditCard
471 @card_types = (
472   "VISA card",
473   "MasterCard",
474   "Discover card",
475   "American Express card",
476   "Diner's Club/Carte Blanche",
477   "enRoute",
478   "JCB",
479   "BankCard",
480   "Switch",
481   "Solo",
482 );
483
484 @base_items = qw (
485                    invoice_template
486                    invoice_latex
487                    invoice_latexreturnaddress
488                    invoice_latexfooter
489                    invoice_latexsmallfooter
490                    invoice_latexnotes
491                    invoice_html
492                    invoice_htmlreturnaddress
493                    invoice_htmlfooter
494                    invoice_htmlnotes
495                    logo.png
496                    logo.eps
497                  );
498
499 @config_items = map { new FS::ConfItem $_ } (
500
501   {
502     'key'         => 'address',
503     'section'     => 'deprecated',
504     'description' => 'This configuration option is no longer used.  See <a href="#invoice_template">invoice_template</a> instead.',
505     'type'        => 'text',
506   },
507
508   {
509     'key'         => 'alerter_template',
510     'section'     => 'billing',
511     'description' => 'Template file for billing method expiration alerts.  See the <a href="../docs/billing.html#invoice_template">billing documentation</a> for details.',
512     'type'        => 'textarea',
513   },
514
515   {
516     'key'         => 'apacheip',
517     'section'     => 'deprecated',
518     'description' => '<b>DEPRECATED</b>, add an <i>apache</i> <a href="../browse/part_export.cgi">export</a> instead.  Used to be the current IP address to assign to new virtual hosts',
519     'type'        => 'text',
520   },
521
522   {
523     'key'         => 'encryption',
524     'section'     => 'billing',
525     'description' => 'Enable encryption of credit cards.',
526     'type'        => 'checkbox',
527   },
528
529   {
530     'key'         => 'encryptionmodule',
531     'section'     => 'billing',
532     'description' => 'Use which module for encryption?',
533     'type'        => 'text',
534   },
535
536   {
537     'key'         => 'encryptionpublickey',
538     'section'     => 'billing',
539     'description' => 'Your RSA Public Key - Required if Encryption is turned on.',
540     'type'        => 'textarea',
541   },
542
543   {
544     'key'         => 'encryptionprivatekey',
545     'section'     => 'billing',
546     'description' => 'Your RSA Private Key - Including this will enable the "Bill Now" feature.  However if the system is compromised, a hacker can use this key to decode the stored credit card information.  This is generally not a good idea.',
547     'type'        => 'textarea',
548   },
549
550   {
551     'key'         => 'business-onlinepayment',
552     'section'     => 'billing',
553     'description' => '<a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support, at least three lines: processor, login, and password.  An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\').    Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
554     'type'        => 'textarea',
555   },
556
557   {
558     'key'         => 'business-onlinepayment-ach',
559     'section'     => 'billing',
560     'description' => 'Alternate <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> support for ACH transactions (defaults to regular <b>business-onlinepayment</b>).  At least three lines: processor, login, and password.  An optional fourth line specifies the action or actions (multiple actions are separated with `,\': for example: `Authorization Only, Post Authorization\').    Optional additional lines are passed to Business::OnlinePayment as %processor_options.',
561     'type'        => 'textarea',
562   },
563
564   {
565     'key'         => 'business-onlinepayment-description',
566     'section'     => 'billing',
567     'description' => 'String passed as the description field to <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a>.  Evaluated as a double-quoted perl string, with the following variables available: <code>$agent</code> (the agent name), and <code>$pkgs</code> (a comma-separated list of packages for which these charges apply)',
568     'type'        => 'text',
569   },
570
571   {
572     'key'         => 'business-onlinepayment-email-override',
573     'section'     => 'billing',
574     'description' => 'Email address used instead of customer email address when submitting a BOP transaction.',
575     'type'        => 'text',
576   },
577
578   {
579     'key'         => 'countrydefault',
580     'section'     => 'UI',
581     'description' => 'Default two-letter country code (if not supplied, the default is `US\')',
582     'type'        => 'text',
583   },
584
585   {
586     'key'         => 'date_format',
587     'section'     => 'UI',
588     'description' => 'Format for displaying dates',
589     'type'        => 'select',
590     'select_hash' => [
591                        '%m/%d/%Y' => 'MM/DD/YYYY',
592                        '%Y/%m/%d' => 'YYYY/MM/DD',
593                      ],
594   },
595
596   {
597     'key'         => 'deletecustomers',
598     'section'     => 'UI',
599     'description' => 'Enable customer deletions.  Be very careful!  Deleting a customer will remove all traces that this customer ever existed!  It should probably only be used when auditing a legacy database.  Normally, you cancel all of a customers\' packages if they cancel service.',
600     'type'        => 'checkbox',
601   },
602
603   {
604     'key'         => 'deletepayments',
605     'section'     => 'billing',
606     'description' => 'Enable deletion of unclosed payments.  Really, with voids this is pretty much not recommended in any situation anymore.  Be very careful!  Only delete payments that were data-entry errors, not adjustments.  Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.',
607     'type'        => [qw( checkbox text )],
608   },
609
610   {
611     'key'         => 'deletecredits',
612     'section'     => 'deprecated',
613     'description' => '<B>DEPRECATED</B>, now controlled by ACLs.  Used to enable deletion of unclosed credits.  Be very careful!  Only delete credits that were data-entry errors, not adjustments.  Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.',
614     'type'        => [qw( checkbox text )],
615   },
616
617   {
618     'key'         => 'deleterefunds',
619     'section'     => 'billing',
620     'description' => 'Enable deletion of unclosed refunds.  Be very careful!  Only delete refunds that were data-entry errors, not adjustments.',
621     'type'        => 'checkbox',
622   },
623
624   {
625     'key'         => 'dirhash',
626     'section'     => 'shell',
627     'description' => 'Optional numeric value to control directory hashing.  If positive, hashes directories for the specified number of levels from the front of the username.  If negative, hashes directories for the specified number of levels from the end of the username.  Some examples: <ul><li>1: user -> <a href="#home">/home</a>/u/user<li>2: user -> <a href="#home">/home</a>/u/s/user<li>-1: user -> <a href="#home">/home</a>/r/user<li>-2: user -> <a href="#home">home</a>/r/e/user</ul>',
628     'type'        => 'text',
629   },
630
631   {
632     'key'         => 'disable_customer_referrals',
633     'section'     => 'UI',
634     'description' => 'Disable new customer-to-customer referrals in the web interface',
635     'type'        => 'checkbox',
636   },
637
638   {
639     'key'         => 'editreferrals',
640     'section'     => 'UI',
641     'description' => 'Enable advertising source modification for existing customers',
642     'type'       => 'checkbox',
643   },
644
645   {
646     'key'         => 'emailinvoiceonly',
647     'section'     => 'billing',
648     'description' => 'Disables postal mail invoices',
649     'type'       => 'checkbox',
650   },
651
652   {
653     'key'         => 'disablepostalinvoicedefault',
654     'section'     => 'billing',
655     'description' => 'Disables postal mail invoices as the default option in the UI.  Be careful not to setup customers which are not sent invoices.  See <a href ="#emailinvoiceauto">emailinvoiceauto</a>.',
656     'type'       => 'checkbox',
657   },
658
659   {
660     'key'         => 'emailinvoiceauto',
661     'section'     => 'billing',
662     'description' => 'Automatically adds new accounts to the email invoice list',
663     'type'       => 'checkbox',
664   },
665
666   {
667     'key'         => 'emailinvoiceautoalways',
668     'section'     => 'billing',
669     'description' => 'Automatically adds new accounts to the email invoice list even when the list contains email addresses',
670     'type'       => 'checkbox',
671   },
672
673   {
674     'key'         => 'exclude_ip_addr',
675     'section'     => '',
676     'description' => 'Exclude these from the list of available broadband service IP addresses. (One per line)',
677     'type'        => 'textarea',
678   },
679   
680   {
681     'key'         => 'hidecancelledpackages',
682     'section'     => 'UI',
683     'description' => 'Prevent cancelled packages from showing up in listings (though they will still be in the database)',
684     'type'        => 'checkbox',
685   },
686
687   {
688     'key'         => 'hidecancelledcustomers',
689     'section'     => 'UI',
690     'description' => 'Prevent customers with only cancelled packages from showing up in listings (though they will still be in the database)',
691     'type'        => 'checkbox',
692   },
693
694   {
695     'key'         => 'home',
696     'section'     => 'required',
697     'description' => 'For new users, prefixed to username to create a directory name.  Should have a leading but not a trailing slash.',
698     'type'        => 'text',
699   },
700
701   {
702     'key'         => 'invoice_from',
703     'section'     => 'required',
704     'description' => 'Return address on email invoices',
705     'type'        => 'text',
706   },
707
708   {
709     'key'         => 'invoice_template',
710     'section'     => 'required',
711     'description' => 'Required template file for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
712     'type'        => 'textarea',
713   },
714
715   {
716     'key'         => 'invoice_html',
717     'section'     => 'billing',
718     'description' => 'Optional HTML template for invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
719
720     'type'        => 'textarea',
721   },
722
723   {
724     'key'         => 'invoice_htmlnotes',
725     'section'     => 'billing',
726     'description' => 'Notes section for HTML invoices.  Defaults to the same data in invoice_latexnotes if not specified.',
727     'type'        => 'textarea',
728   },
729
730   {
731     'key'         => 'invoice_htmlfooter',
732     'section'     => 'billing',
733     'description' => 'Footer for HTML invoices.  Defaults to the same data in invoice_latexfooter if not specified.',
734     'type'        => 'textarea',
735   },
736
737   {
738     'key'         => 'invoice_htmlreturnaddress',
739     'section'     => 'billing',
740     'description' => 'Return address for HTML invoices.  Defaults to the same data in invoice_latexreturnaddress if not specified.',
741     'type'        => 'textarea',
742   },
743
744   {
745     'key'         => 'invoice_latex',
746     'section'     => 'billing',
747     'description' => 'Optional LaTeX template for typeset PostScript invoices.  See the <a href="../docs/billing.html">billing documentation</a> for details.',
748     'type'        => 'textarea',
749   },
750
751   {
752     'key'         => 'invoice_latexnotes',
753     'section'     => 'billing',
754     'description' => 'Notes section for LaTeX typeset PostScript invoices.',
755     'type'        => 'textarea',
756   },
757
758   {
759     'key'         => 'invoice_latexfooter',
760     'section'     => 'billing',
761     'description' => 'Footer for LaTeX typeset PostScript invoices.',
762     'type'        => 'textarea',
763   },
764
765   {
766     'key'         => 'invoice_latexreturnaddress',
767     'section'     => 'billing',
768     'description' => 'Return address for LaTeX typeset PostScript invoices.',
769     'type'        => 'textarea',
770   },
771
772   {
773     'key'         => 'invoice_latexsmallfooter',
774     'section'     => 'billing',
775     'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
776     'type'        => 'textarea',
777   },
778
779   {
780     'key'         => 'invoice_email_pdf',
781     'section'     => 'billing',
782     'description' => 'Send PDF invoice as an attachment to emailed invoices.  By default, includes the plain text invoice as the email body, unless invoice_email_pdf_note is set.',
783     'type'        => 'checkbox'
784   },
785
786   {
787     'key'         => 'invoice_email_pdf_note',
788     'section'     => 'billing',
789     'description' => 'If defined, this text will replace the default plain text invoice as the body of emailed PDF invoices.',
790     'type'        => 'textarea'
791   },
792
793
794   { 
795     'key'         => 'invoice_default_terms',
796     'section'     => 'billing',
797     'description' => 'Optional default invoice term, used to calculate a due date printed on invoices.',
798     'type'        => 'select',
799     'select_enum' => [ '', 'Payable upon receipt', 'Net 0', 'Net 10', 'Net 15', 'Net 30', 'Net 45', 'Net 60' ],
800   },
801
802   {
803     'key'         => 'payment_receipt_email',
804     'section'     => 'billing',
805     'description' => 'Template file for payment receipts.  Payment receipts are sent to the customer email invoice destination(s) when a payment is received.  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>$date</code> <li><code>$name</code> <li><code>$paynum</code> - Freeside payment number <li><code>$paid</code> - Amount of payment <li><code>$payby</code> - Payment type (Card, Check, Electronic check, etc.) <li><code>$payinfo</code> - Masked credit card number or check number <li><code>$balance</code> - New balance</ul>',
806     'type'        => [qw( checkbox textarea )],
807   },
808
809   {
810     'key'         => 'lpr',
811     'section'     => 'required',
812     'description' => 'Print command for paper invoices, for example `lpr -h\'',
813     'type'        => 'text',
814   },
815
816   {
817     'key'         => 'lpr-postscript_prefix',
818     'section'     => 'billing',
819     'description' => 'Raw printer commands prepended to the beginning of postscript print jobs (evaluated as a double-quoted perl string - backslash escapes are available)',
820     'type'        => 'text',
821   },
822
823   {
824     'key'         => 'lpr-postscript_suffix',
825     'section'     => 'billing',
826     'description' => 'Raw printer commands added to the end of postscript print jobs (evaluated as a double-quoted perl string - backslash escapes are available)',
827     'type'        => 'text',
828   },
829
830   {
831     'key'         => 'money_char',
832     'section'     => '',
833     'description' => 'Currency symbol - defaults to `$\'',
834     'type'        => 'text',
835   },
836
837   {
838     'key'         => 'defaultrecords',
839     'section'     => 'BIND',
840     'description' => 'DNS entries to add automatically when creating a domain',
841     'type'        => 'editlist',
842     'editlist_parts' => [ { type=>'text' },
843                           { type=>'immutable', value=>'IN' },
844                           { type=>'select',
845                             select_enum=>{ map { $_=>$_ } qw(A CNAME MX NS TXT)} },
846                           { type=> 'text' }, ],
847   },
848
849   {
850     'key'         => 'passwordmin',
851     'section'     => 'password',
852     'description' => 'Minimum password length (default 6)',
853     'type'        => 'text',
854   },
855
856   {
857     'key'         => 'passwordmax',
858     'section'     => 'password',
859     'description' => 'Maximum password length (default 8) (don\'t set this over 12 if you need to import or export crypt() passwords)',
860     'type'        => 'text',
861   },
862
863   {
864     'key' => 'password-noampersand',
865     'section' => 'password',
866     'description' => 'Disallow ampersands in passwords',
867     'type' => 'checkbox',
868   },
869
870   {
871     'key' => 'password-noexclamation',
872     'section' => 'password',
873     'description' => 'Disallow exclamations in passwords (Not setting this could break old text Livingston or Cistron Radius servers)',
874     'type' => 'checkbox',
875   },
876
877   {
878     'key'         => 'referraldefault',
879     'section'     => 'UI',
880     'description' => 'Default referral, specified by refnum',
881     'type'        => 'text',
882   },
883
884 #  {
885 #    'key'         => 'registries',
886 #    'section'     => 'required',
887 #    'description' => 'Directory which contains domain registry information.  Each registry is a directory.',
888 #  },
889
890   {
891     'key'         => 'maxsearchrecordsperpage',
892     'section'     => 'UI',
893     'description' => 'If set, number of search records to return per page.',
894     'type'        => 'text',
895   },
896
897   {
898     'key'         => 'session-start',
899     'section'     => 'session',
900     'description' => 'If defined, the command which is executed on the Freeside machine when a session begins.  The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
901     'type'        => 'text',
902   },
903
904   {
905     'key'         => 'session-stop',
906     'section'     => 'session',
907     'description' => 'If defined, the command which is executed on the Freeside machine when a session ends.  The contents of the file are treated as a double-quoted perl string, with the following variables available: <code>$ip</code>, <code>$nasip</code> and <code>$nasfqdn</code>, which are the IP address of the starting session, and the IP address and fully-qualified domain name of the NAS this session is on.',
908     'type'        => 'text',
909   },
910
911   {
912     'key'         => 'shells',
913     'section'     => 'required',
914     'description' => 'Legal shells (think /etc/shells).  You probably want to `cut -d: -f7 /etc/passwd | sort | uniq\' initially so that importing doesn\'t fail with `Illegal shell\' errors, then remove any special entries afterwords.  A blank line specifies that an empty shell is permitted.',
915     'type'        => 'textarea',
916   },
917
918   {
919     'key'         => 'showpasswords',
920     'section'     => 'UI',
921     'description' => 'Display unencrypted user passwords in the backend (employee) web interface',
922     'type'        => 'checkbox',
923   },
924
925   {
926     'key'         => 'signupurl',
927     'section'     => 'UI',
928     'description' => 'if you are using customer-to-customer referrals, and you enter the URL of your <a href="../docs/signup.html">signup server CGI</a>, the customer view screen will display a customized link to the signup server with the appropriate customer as referral',
929     'type'        => 'text',
930   },
931
932   {
933     'key'         => 'smtpmachine',
934     'section'     => 'required',
935     'description' => 'SMTP relay for Freeside\'s outgoing mail',
936     'type'        => 'text',
937   },
938
939   {
940     'key'         => 'soadefaultttl',
941     'section'     => 'BIND',
942     'description' => 'SOA default TTL for new domains.',
943     'type'        => 'text',
944   },
945
946   {
947     'key'         => 'soaemail',
948     'section'     => 'BIND',
949     'description' => 'SOA email for new domains, in BIND form (`.\' instead of `@\'), with trailing `.\'',
950     'type'        => 'text',
951   },
952
953   {
954     'key'         => 'soaexpire',
955     'section'     => 'BIND',
956     'description' => 'SOA expire for new domains',
957     'type'        => 'text',
958   },
959
960   {
961     'key'         => 'soamachine',
962     'section'     => 'BIND',
963     'description' => 'SOA machine for new domains, with trailing `.\'',
964     'type'        => 'text',
965   },
966
967   {
968     'key'         => 'soarefresh',
969     'section'     => 'BIND',
970     'description' => 'SOA refresh for new domains',
971     'type'        => 'text',
972   },
973
974   {
975     'key'         => 'soaretry',
976     'section'     => 'BIND',
977     'description' => 'SOA retry for new domains',
978     'type'        => 'text',
979   },
980
981   {
982     'key'         => 'statedefault',
983     'section'     => 'UI',
984     'description' => 'Default state or province (if not supplied, the default is `CA\')',
985     'type'        => 'text',
986   },
987
988   {
989     'key'         => 'unsuspendauto',
990     'section'     => 'billing',
991     'description' => 'Enables the automatic unsuspension of suspended packages when a customer\'s balance due changes from positive to zero or negative as the result of a payment or credit',
992     'type'        => 'checkbox',
993   },
994
995   {
996     'key'         => 'unsuspend-always_adjust_next_bill_date',
997     'section'     => 'billing',
998     'description' => 'Global override that causes unsuspensions to always adjust the next bill date under any circumstances.  This is now controlled on a per-package bases - probably best not to use this option unless you are a legacy installation that requires this behaviour.',
999     'type'        => 'checkbox',
1000   },
1001
1002   {
1003     'key'         => 'usernamemin',
1004     'section'     => 'username',
1005     'description' => 'Minimum username length (default 2)',
1006     'type'        => 'text',
1007   },
1008
1009   {
1010     'key'         => 'usernamemax',
1011     'section'     => 'username',
1012     'description' => 'Maximum username length',
1013     'type'        => 'text',
1014   },
1015
1016   {
1017     'key'         => 'username-ampersand',
1018     'section'     => 'username',
1019     'description' => 'Allow the ampersand character (&amp;) in usernames.  Be careful when using this option in conjunction with <a href="../browse/part_export.cgi">exports</a> which execute shell commands, as the ampersand will be interpreted by the shell if not quoted.',
1020     'type'        => 'checkbox',
1021   },
1022
1023   {
1024     'key'         => 'username-letter',
1025     'section'     => 'username',
1026     'description' => 'Usernames must contain at least one letter',
1027     'type'        => 'checkbox',
1028   },
1029
1030   {
1031     'key'         => 'username-letterfirst',
1032     'section'     => 'username',
1033     'description' => 'Usernames must start with a letter',
1034     'type'        => 'checkbox',
1035   },
1036
1037   {
1038     'key'         => 'username-noperiod',
1039     'section'     => 'username',
1040     'description' => 'Disallow periods in usernames',
1041     'type'        => 'checkbox',
1042   },
1043
1044   {
1045     'key'         => 'username-nounderscore',
1046     'section'     => 'username',
1047     'description' => 'Disallow underscores in usernames',
1048     'type'        => 'checkbox',
1049   },
1050
1051   {
1052     'key'         => 'username-nodash',
1053     'section'     => 'username',
1054     'description' => 'Disallow dashes in usernames',
1055     'type'        => 'checkbox',
1056   },
1057
1058   {
1059     'key'         => 'username-uppercase',
1060     'section'     => 'username',
1061     'description' => 'Allow uppercase characters in usernames',
1062     'type'        => 'checkbox',
1063   },
1064
1065   { 
1066     'key'         => 'username-percent',
1067     'section'     => 'username',
1068     'description' => 'Allow the percent character (%) in usernames.',
1069     'type'        => 'checkbox',
1070   },
1071
1072   {
1073     'key'         => 'safe-part_bill_event',
1074     'section'     => 'UI',
1075     'description' => 'Validates invoice event expressions against a preset list.  Useful for webdemos, annoying to powerusers.',
1076     'type'        => 'checkbox',
1077   },
1078
1079   {
1080     'key'         => 'show_ss',
1081     'section'     => 'UI',
1082     'description' => 'Turns on display/collection of social security numbers in the web interface.  Sometimes required by electronic check (ACH) processors.',
1083     'type'        => 'checkbox',
1084   },
1085
1086   {
1087     'key'         => 'show_stateid',
1088     'section'     => 'UI',
1089     'description' => "Turns on display/collection of driver's license/state issued id numbers in the web interface.  Sometimes required by electronic check (ACH) processors.",
1090     'type'        => 'checkbox',
1091   },
1092
1093   {
1094     'key'         => 'show_bankstate',
1095     'section'     => 'UI',
1096     'description' => "Turns on display/collection of state for bank accounts in the web interface.  Sometimes required by electronic check (ACH) processors.",
1097     'type'        => 'checkbox',
1098   },
1099
1100   { 
1101     'key'         => 'agent_defaultpkg',
1102     'section'     => 'UI',
1103     'description' => 'Setting this option will cause new packages to be available to all agent types by default.',
1104     'type'        => 'checkbox',
1105   },
1106
1107   {
1108     'key'         => 'legacy_link',
1109     'section'     => 'UI',
1110     'description' => 'Display options in the web interface to link legacy pre-Freeside services.',
1111     'type'        => 'checkbox',
1112   },
1113
1114   {
1115     'key'         => 'legacy_link-steal',
1116     'section'     => 'UI',
1117     'description' => 'Allow "stealing" an already-audited service from one customer (or package) to another using the link function.',
1118     'type'        => 'checkbox',
1119   },
1120
1121   {
1122     'key'         => 'queue_dangerous_controls',
1123     'section'     => 'UI',
1124     'description' => 'Enable queue modification controls on account pages and for new jobs.  Unless you are a developer working on new export code, you should probably leave this off to avoid causing provisioning problems.',
1125     'type'        => 'checkbox',
1126   },
1127
1128   {
1129     'key'         => 'security_phrase',
1130     'section'     => 'password',
1131     'description' => 'Enable the tracking of a "security phrase" with each account.  Not recommended, as it is vulnerable to social engineering.',
1132     'type'        => 'checkbox',
1133   },
1134
1135   {
1136     'key'         => 'locale',
1137     'section'     => 'UI',
1138     'description' => 'Message locale',
1139     'type'        => 'select',
1140     'select_enum' => [ qw(en_US) ],
1141   },
1142
1143   {
1144     'key'         => 'signup_server-payby',
1145     'section'     => '',
1146     'description' => 'Acceptable payment types for the signup server',
1147     'type'        => 'selectmultiple',
1148     'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB PREPAY BILL COMP) ],
1149   },
1150
1151   {
1152     'key'         => 'signup_server-default_agentnum',
1153     'section'     => '',
1154     'description' => 'Default agent for the signup server',
1155     'type'        => 'select-sub',
1156     'options_sub' => sub { require FS::Record;
1157                            require FS::agent;
1158                            map { $_->agentnum => $_->agent }
1159                                FS::Record::qsearch('agent', { disabled=>'' } );
1160                          },
1161     'option_sub'  => sub { require FS::Record;
1162                            require FS::agent;
1163                            my $agent = FS::Record::qsearchs(
1164                              'agent', { 'agentnum'=>shift }
1165                            );
1166                            $agent ? $agent->agent : '';
1167                          },
1168   },
1169
1170   {
1171     'key'         => 'signup_server-default_refnum',
1172     'section'     => '',
1173     'description' => 'Default advertising source for the signup server',
1174     'type'        => 'select-sub',
1175     'options_sub' => sub { require FS::Record;
1176                            require FS::part_referral;
1177                            map { $_->refnum => $_->referral }
1178                                FS::Record::qsearch( 'part_referral', 
1179                                                     { 'disabled' => '' }
1180                                                   );
1181                          },
1182     'option_sub'  => sub { require FS::Record;
1183                            require FS::part_referral;
1184                            my $part_referral = FS::Record::qsearchs(
1185                              'part_referral', { 'refnum'=>shift } );
1186                            $part_referral ? $part_referral->referral : '';
1187                          },
1188   },
1189
1190   {
1191     'key'         => 'signup_server-default_pkgpart',
1192     'section'     => '',
1193     'description' => 'Default pakcage for the signup server',
1194     'type'        => 'select-sub',
1195     'options_sub' => sub { require FS::Record;
1196                            require FS::part_pkg;
1197                            map { $_->pkgpart => $_->pkg.' - '.$_->comment }
1198                                FS::Record::qsearch( 'part_pkg',
1199                                                     { 'disabled' => ''}
1200                                                   );
1201                          },
1202     'option_sub'  => sub { require FS::Record;
1203                            require FS::part_pkg;
1204                            my $part_pkg = FS::Record::qsearchs(
1205                              'part_pkg', { 'pkgpart'=>shift }
1206                            );
1207                            $part_pkg
1208                              ? $part_pkg->pkg.' - '.$part_pkg->comment
1209                              : '';
1210                          },
1211   },
1212
1213   {
1214     'key'         => 'show-msgcat-codes',
1215     'section'     => 'UI',
1216     'description' => 'Show msgcat codes in error messages.  Turn this option on before reporting errors to the mailing list.',
1217     'type'        => 'checkbox',
1218   },
1219
1220   {
1221     'key'         => 'signup_server-realtime',
1222     'section'     => '',
1223     'description' => 'Run billing for signup server signups immediately, and do not provision accounts which subsequently have a balance.',
1224     'type'        => 'checkbox',
1225   },
1226   {
1227     'key'         => 'signup_server-classnum2',
1228     'section'     => '',
1229     'description' => 'Package Class for first optional purchase',
1230     'type'        => 'select-sub',
1231     'options_sub' => sub { require FS::Record;
1232                            require FS::pkg_class;
1233                            map { $_->classnum => $_->classname }
1234                                FS::Record::qsearch('pkg_class', {} );
1235                          },
1236     'option_sub'  => sub { require FS::Record;
1237                            require FS::pkg_class;
1238                            my $pkg_class = FS::Record::qsearchs(
1239                              'pkg_class', { 'classnum'=>shift }
1240                            );
1241                            $pkg_class ? $pkg_class->classname : '';
1242                          },
1243   },
1244
1245   {
1246     'key'         => 'signup_server-classnum3',
1247     'section'     => '',
1248     'description' => 'Package Class for second optional purchase',
1249     'type'        => 'select-sub',
1250     'options_sub' => sub { require FS::Record;
1251                            require FS::pkg_class;
1252                            map { $_->classnum => $_->classname }
1253                                FS::Record::qsearch('pkg_class', {} );
1254                          },
1255     'option_sub'  => sub { require FS::Record;
1256                            require FS::pkg_class;
1257                            my $pkg_class = FS::Record::qsearchs(
1258                              'pkg_class', { 'classnum'=>shift }
1259                            );
1260                            $pkg_class ? $pkg_class->classname : '';
1261                          },
1262   },
1263
1264   {
1265     'key'         => 'backend-realtime',
1266     'section'     => '',
1267     'description' => 'Run billing for backend signups immediately.',
1268     'type'        => 'checkbox',
1269   },
1270
1271   {
1272     'key'         => 'declinetemplate',
1273     'section'     => 'billing',
1274     'description' => 'Template file for credit card decline emails.',
1275     'type'        => 'textarea',
1276   },
1277
1278   {
1279     'key'         => 'emaildecline',
1280     'section'     => 'billing',
1281     'description' => 'Enable emailing of credit card decline notices.',
1282     'type'        => 'checkbox',
1283   },
1284
1285   {
1286     'key'         => 'emaildecline-exclude',
1287     'section'     => 'billing',
1288     'description' => 'List of error messages that should not trigger email decline notices, one per line.',
1289     'type'        => 'textarea',
1290   },
1291
1292   {
1293     'key'         => 'cancelmessage',
1294     'section'     => 'billing',
1295     'description' => 'Template file for cancellation emails.',
1296     'type'        => 'textarea',
1297   },
1298
1299   {
1300     'key'         => 'cancelsubject',
1301     'section'     => 'billing',
1302     'description' => 'Subject line for cancellation emails.',
1303     'type'        => 'text',
1304   },
1305
1306   {
1307     'key'         => 'emailcancel',
1308     'section'     => 'billing',
1309     'description' => 'Enable emailing of cancellation notices.',
1310     'type'        => 'checkbox',
1311   },
1312
1313   {
1314     'key'         => 'require_cardname',
1315     'section'     => 'billing',
1316     'description' => 'Require an "Exact name on card" to be entered explicitly; don\'t default to using the first and last name.',
1317     'type'        => 'checkbox',
1318   },
1319
1320   {
1321     'key'         => 'enable_taxclasses',
1322     'section'     => 'billing',
1323     'description' => 'Enable per-package tax classes',
1324     'type'        => 'checkbox',
1325   },
1326
1327   {
1328     'key'         => 'require_taxclasses',
1329     'section'     => 'billing',
1330     'description' => 'Require a taxclass to be entered for every package',
1331     'type'        => 'checkbox',
1332   },
1333
1334   {
1335     'key'         => 'welcome_email',
1336     'section'     => '',
1337     'description' => 'Template file for welcome email.  Welcome emails are sent to the customer email invoice destination(s) each time a svc_acct record is created.  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></ul>',
1338     'type'        => 'textarea',
1339     'per_agent'   => 1,
1340   },
1341
1342   {
1343     'key'         => 'welcome_email-from',
1344     'section'     => '',
1345     'description' => 'From: address header for welcome email',
1346     'type'        => 'text',
1347     'per_agent'   => 1,
1348   },
1349
1350   {
1351     'key'         => 'welcome_email-subject',
1352     'section'     => '',
1353     'description' => 'Subject: header for welcome email',
1354     'type'        => 'text',
1355     'per_agent'   => 1,
1356   },
1357   
1358   {
1359     'key'         => 'welcome_email-mimetype',
1360     'section'     => '',
1361     'description' => 'MIME type for welcome email',
1362     'type'        => 'select',
1363     'select_enum' => [ 'text/plain', 'text/html' ],
1364     'per_agent'   => 1,
1365   },
1366
1367   {
1368     'key'         => 'welcome_letter',
1369     'section'     => '',
1370     '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>',
1371     'type'        => 'textarea',
1372   },
1373
1374   {
1375     'key'         => 'warning_email',
1376     'section'     => '',
1377     '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>',
1378     'type'        => 'textarea',
1379   },
1380
1381   {
1382     'key'         => 'warning_email-from',
1383     'section'     => '',
1384     'description' => 'From: address header for warning email',
1385     'type'        => 'text',
1386   },
1387
1388   {
1389     'key'         => 'warning_email-cc',
1390     'section'     => '',
1391     'description' => 'Additional recipient(s) (comma separated) for warning email when remaining usage reaches zero.',
1392     'type'        => 'text',
1393   },
1394
1395   {
1396     'key'         => 'warning_email-subject',
1397     'section'     => '',
1398     'description' => 'Subject: header for warning email',
1399     'type'        => 'text',
1400   },
1401   
1402   {
1403     'key'         => 'warning_email-mimetype',
1404     'section'     => '',
1405     'description' => 'MIME type for warning email',
1406     'type'        => 'select',
1407     'select_enum' => [ 'text/plain', 'text/html' ],
1408   },
1409
1410   {
1411     'key'         => 'payby',
1412     'section'     => 'billing',
1413     'description' => 'Available payment types.',
1414     'type'        => 'selectmultiple',
1415     'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD COMP) ],
1416   },
1417
1418   {
1419     'key'         => 'payby-default',
1420     'section'     => 'UI',
1421     'description' => 'Default payment type.  HIDE disables display of billing information and sets customers to BILL.',
1422     'type'        => 'select',
1423     'select_enum' => [ '', qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD COMP HIDE) ],
1424   },
1425
1426   {
1427     'key'         => 'paymentforcedtobatch',
1428     'section'     => 'UI',
1429     'description' => 'Causes per customer payment entry to be forced to a batch processor rather than performed realtime.',
1430     'type'        => 'checkbox',
1431   },
1432
1433   {
1434     'key'         => 'svc_acct-notes',
1435     'section'     => 'UI',
1436     'description' => 'Extra HTML to be displayed on the Account View screen.',
1437     'type'        => 'textarea',
1438   },
1439
1440   {
1441     'key'         => 'radius-password',
1442     'section'     => '',
1443     'description' => 'RADIUS attribute for plain-text passwords.',
1444     'type'        => 'select',
1445     'select_enum' => [ 'Password', 'User-Password' ],
1446   },
1447
1448   {
1449     'key'         => 'radius-ip',
1450     'section'     => '',
1451     'description' => 'RADIUS attribute for IP addresses.',
1452     'type'        => 'select',
1453     'select_enum' => [ 'Framed-IP-Address', 'Framed-Address' ],
1454   },
1455
1456   {
1457     'key'         => 'svc_acct-alldomains',
1458     'section'     => '',
1459     'description' => 'Allow accounts to select any domain in the database.  Normally accounts can only select from the domain set in the service definition and those purchased by the customer.',
1460     'type'        => 'checkbox',
1461   },
1462
1463   {
1464     'key'         => 'dump-scpdest',
1465     'section'     => '',
1466     'description' => 'destination for scp database dumps: user@host:/path',
1467     'type'        => 'text',
1468   },
1469
1470   {
1471     'key'         => 'dump-pgpid',
1472     'section'     => '',
1473     'description' => "Optional PGP public key user or key id for database dumps.  The public key should exist on the freeside user's public keyring, and the gpg binary and GnuPG perl module should be installed.",
1474     'type'        => 'text',
1475   },
1476
1477   {
1478     'key'         => 'cvv-save',
1479     'section'     => 'billing',
1480     'description' => 'Save CVV2 information after the initial transaction for the selected credit card types.  Enabling this option may be in violation of your merchant agreement(s), so please check them carefully before enabling this option for any credit card types.',
1481     'type'        => 'selectmultiple',
1482     'select_enum' => \@card_types,
1483   },
1484
1485   {
1486     'key'         => 'allow_negative_charges',
1487     'section'     => 'billing',
1488     'description' => 'Allow negative charges.  Normally not used unless importing data from a legacy system that requires this.',
1489     'type'        => 'checkbox',
1490   },
1491   {
1492       'key'         => 'auto_unset_catchall',
1493       'section'     => '',
1494       'description' => 'When canceling a svc_acct that is the email catchall for one or more svc_domains, automatically set their catchall fields to null.  If this option is not set, the attempt will simply fail.',
1495       'type'        => 'checkbox',
1496   },
1497
1498   {
1499     'key'         => 'system_usernames',
1500     'section'     => 'username',
1501     'description' => 'A list of system usernames that cannot be edited or removed, one per line.  Use a bare username to prohibit modification/deletion of the username in any domain, or username@domain to prohibit modification/deletetion of a specific username and domain.',
1502     'type'        => 'textarea',
1503   },
1504
1505   {
1506     'key'         => 'cust_pkg-change_svcpart',
1507     'section'     => '',
1508     'description' => "When changing packages, move services even if svcparts don't match between old and new pacakge definitions.",
1509     'type'        => 'checkbox',
1510   },
1511
1512   {
1513     'key'         => 'disable_autoreverse',
1514     'section'     => 'BIND',
1515     'description' => 'Disable automatic synchronization of reverse-ARPA entries.',
1516     'type'        => 'checkbox',
1517   },
1518
1519   {
1520     'key'         => 'svc_www-enable_subdomains',
1521     'section'     => '',
1522     'description' => 'Enable selection of specific subdomains for virtual host creation.',
1523     'type'        => 'checkbox',
1524   },
1525
1526   {
1527     'key'         => 'svc_www-usersvc_svcpart',
1528     'section'     => '',
1529     'description' => 'Allowable service definition svcparts for virtual hosts, one per line.',
1530     'type'        => 'textarea',
1531   },
1532
1533   {
1534     'key'         => 'selfservice_server-primary_only',
1535     'section'     => '',
1536     'description' => 'Only allow primary accounts to access self-service functionality.',
1537     'type'        => 'checkbox',
1538   },
1539
1540   {
1541     'key'         => 'card_refund-days',
1542     'section'     => 'billing',
1543     'description' => 'After a payment, the number of days a refund link will be available for that payment.  Defaults to 120.',
1544     'type'        => 'text',
1545   },
1546
1547   {
1548     'key'         => 'agent-showpasswords',
1549     'section'     => '',
1550     'description' => 'Display unencrypted user passwords in the agent (reseller) interface',
1551     'type'        => 'checkbox',
1552   },
1553
1554   {
1555     'key'         => 'global_unique-username',
1556     'section'     => 'username',
1557     'description' => 'Global username uniqueness control: none (usual setting - check uniqueness per exports), username (all usernames are globally unique, regardless of domain or exports), or username@domain (all username@domain pairs are globally unique, regardless of exports).  disabled turns off duplicate checking completely and is STRONGLY NOT RECOMMENDED unless you REALLY need to turn this off.',
1558     'type'        => 'select',
1559     'select_enum' => [ 'none', 'username', 'username@domain', 'disabled' ],
1560   },
1561
1562   {
1563     'key'         => 'svc_external-skip_manual',
1564     'section'     => 'UI',
1565     'description' => 'When provisioning svc_external services, skip manual entry of id and title fields in the UI.  Usually used in conjunction with an export that populates these fields (i.e. artera_turbo).',
1566     'type'        => 'checkbox',
1567   },
1568
1569   {
1570     'key'         => 'svc_external-display_type',
1571     'section'     => 'UI',
1572     'description' => 'Select a specific svc_external type to enable some UI changes specific to that type (i.e. artera_turbo).',
1573     'type'        => 'select',
1574     'select_enum' => [ 'generic', 'artera_turbo', ],
1575   },
1576
1577   {
1578     'key'         => 'ticket_system',
1579     'section'     => '',
1580     'description' => 'Ticketing system integration.  <b>RT_Internal</b> uses the built-in RT ticketing system (see the <a href="../docs/install-rt">integrated ticketing installation instructions</a>).   <b>RT_External</b> accesses an external RT installation in a separate database (local or remote).',
1581     'type'        => 'select',
1582     #'select_enum' => [ '', qw(RT_Internal RT_Libs RT_External) ],
1583     'select_enum' => [ '', qw(RT_Internal RT_External) ],
1584   },
1585
1586   {
1587     'key'         => 'ticket_system-default_queueid',
1588     'section'     => '',
1589     'description' => 'Default queue used when creating new customer tickets.',
1590     'type'        => 'select-sub',
1591     'options_sub' => sub {
1592                            my $conf = new FS::Conf;
1593                            if ( $conf->config('ticket_system') ) {
1594                              eval "use FS::TicketSystem;";
1595                              die $@ if $@;
1596                              FS::TicketSystem->queues();
1597                            } else {
1598                              ();
1599                            }
1600                          },
1601     'option_sub'  => sub { 
1602                            my $conf = new FS::Conf;
1603                            if ( $conf->config('ticket_system') ) {
1604                              eval "use FS::TicketSystem;";
1605                              die $@ if $@;
1606                              FS::TicketSystem->queue(shift);
1607                            } else {
1608                              '';
1609                            }
1610                          },
1611   },
1612
1613   {
1614     'key'         => 'ticket_system-custom_priority_field',
1615     'section'     => '',
1616     'description' => 'Custom field from the ticketing system to use as a custom priority classification.',
1617     'type'        => 'text',
1618   },
1619
1620   {
1621     'key'         => 'ticket_system-custom_priority_field-values',
1622     'section'     => '',
1623     'description' => 'Values for the custom field from the ticketing system to break down and sort customer ticket lists.',
1624     'type'        => 'textarea',
1625   },
1626
1627   {
1628     'key'         => 'ticket_system-custom_priority_field_queue',
1629     'section'     => '',
1630     'description' => 'Ticketing system queue in which the custom field specified in ticket_system-custom_priority_field is located.',
1631     'type'        => 'text',
1632   },
1633
1634   {
1635     'key'         => 'ticket_system-rt_external_datasrc',
1636     'section'     => '',
1637     'description' => 'With external RT integration, the DBI data source for the external RT installation, for example, <code>DBI:Pg:user=rt_user;password=rt_word;host=rt.example.com;dbname=rt</code>',
1638     'type'        => 'text',
1639
1640   },
1641
1642   {
1643     'key'         => 'ticket_system-rt_external_url',
1644     'section'     => '',
1645     'description' => 'With external RT integration, the URL for the external RT installation, for example, <code>https://rt.example.com/rt</code>',
1646     'type'        => 'text',
1647   },
1648
1649   {
1650     'key'         => 'company_name',
1651     'section'     => 'required',
1652     'description' => 'Your company name',
1653     'type'        => 'text',
1654   },
1655
1656   {
1657     'key'         => 'address2-search',
1658     'section'     => 'UI',
1659     'description' => 'Enable a "Unit" search box which searches the second address field',
1660     'type'        => 'checkbox',
1661   },
1662
1663   { 'key'         => 'referral_credit',
1664     'section'     => 'billing',
1665     'description' => "Enables one-time referral credits in the amount of one month <i>referred</i> customer's recurring fee (irregardless of frequency).",
1666     'type'        => 'checkbox',
1667   },
1668
1669   { 'key'         => 'selfservice_server-cache_module',
1670     'section'     => '',
1671     'description' => 'Module used to store self-service session information.  All modules handle any number of self-service servers.  Cache::SharedMemoryCache is appropriate for a single database / single Freeside server.  Cache::FileCache is useful for multiple databases on a single server, or when IPC::ShareLite is not available (i.e. FreeBSD).', #  _Database stores session information in the database and is appropriate for multiple Freeside servers, but may be slower.',
1672     'type'        => 'select',
1673     'select_enum' => [ 'Cache::SharedMemoryCache', 'Cache::FileCache', ], # '_Database' ],
1674   },
1675
1676   {
1677     'key'         => 'hylafax',
1678     'section'     => '',
1679     'description' => 'Options for a HylaFAX server to enable the FAX invoice destination.  They should be in the form of a space separated list of arguments to the Fax::Hylafax::Client::sendfax subroutine.  You probably shouldn\'t override things like \'docfile\'.  *Note* Only supported when using typeset invoices (see the invoice_latex configuration option).',
1680     'type'        => [qw( checkbox textarea )],
1681   },
1682
1683   {
1684     'key'         => 'svc_acct-usage_suspend',
1685     'section'     => 'billing',
1686     'description' => 'Suspends the package an account belongs to when svc_acct.seconds or a bytecount is decremented to 0 or below (accounts with an empty seconds and up|down|totalbytes value are ignored).  Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
1687     'type'        => 'checkbox',
1688   },
1689
1690   {
1691     'key'         => 'svc_acct-usage_unsuspend',
1692     'section'     => 'billing',
1693     'description' => 'Unuspends the package an account belongs to when svc_acct.seconds or a bytecount is incremented from 0 or below to a positive value (accounts with an empty seconds and up|down|totalbytes value are ignored).  Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
1694     'type'        => 'checkbox',
1695   },
1696
1697   {
1698     'key'         => 'svc_acct-usage_threshold',
1699     'section'     => 'billing',
1700     'description' => 'The threshold (expressed as percentage) of acct.seconds or acct.up|down|totalbytes at which a warning message is sent to a service holder.  Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.  Defaults to 80.',
1701     'type'        => 'text',
1702   },
1703
1704   {
1705     'key'         => 'cust-fields',
1706     'section'     => 'UI',
1707     'description' => 'Which customer fields to display on reports by default',
1708     'type'        => 'select',
1709     'select_hash' => [ FS::ConfDefaults->cust_fields_avail() ],
1710   },
1711
1712   {
1713     'key'         => 'cust_pkg-display_times',
1714     'section'     => 'UI',
1715     'description' => 'Display full timestamps (not just dates) for customer packages.  Useful if you are doing real-time things like hourly prepaid.',
1716     'type'        => 'checkbox',
1717   },
1718
1719   {
1720     'key'         => 'svc_acct-edit_uid',
1721     'section'     => 'shell',
1722     'description' => 'Allow UID editing.',
1723     'type'        => 'checkbox',
1724   },
1725
1726   {
1727     'key'         => 'svc_acct-edit_gid',
1728     'section'     => 'shell',
1729     'description' => 'Allow GID editing.',
1730     'type'        => 'checkbox',
1731   },
1732
1733   {
1734     'key'         => 'zone-underscore',
1735     'section'     => 'BIND',
1736     'description' => 'Allow underscores in zone names.  As underscores are illegal characters in zone names, this option is not recommended.',
1737     'type'        => 'checkbox',
1738   },
1739
1740   {
1741     'key'         => 'echeck-nonus',
1742     'section'     => 'billing',
1743     'description' => 'Disable ABA-format account checking for Electronic Check payment info',
1744     'type'        => 'checkbox',
1745   },
1746
1747   {
1748     'key'         => 'voip-cust_cdr_spools',
1749     'section'     => '',
1750     'description' => 'Enable the per-customer option for individual CDR spools.',
1751     'type'        => 'checkbox',
1752   },
1753
1754   {
1755     'key'         => 'svc_forward-arbitrary_dst',
1756     'section'     => '',
1757     'description' => "Allow forwards to point to arbitrary strings that don't necessarily look like email addresses.  Only used when using forwards for weird, non-email things.",
1758     'type'        => 'checkbox',
1759   },
1760
1761   {
1762     'key'         => 'tax-ship_address',
1763     'section'     => 'billing',
1764     'description' => 'By default, tax calculations are done based on the billing address.  Enable this switch to calculate tax based on the shipping address instead.  Note: Tax reports can take a long time when enabled.',
1765     'type'        => 'checkbox',
1766   },
1767
1768   {
1769     'key'         => 'batch-enable',
1770     'section'     => 'billing',
1771     'description' => 'Enable credit card and/or ACH batching - leave disabled for real-time installations.',
1772     'type'        => 'checkbox',
1773   },
1774
1775   {
1776     'key'         => 'batch-default_format',
1777     'section'     => 'billing',
1778     'description' => 'Default format for batches.',
1779     'type'        => 'select',
1780     'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch',
1781                        'csv-chase_canada-E-xactBatch', 'BoM', 'PAP',
1782                        'ach-spiritone',
1783                     ]
1784   },
1785
1786   {
1787     'key'         => 'batch-fixed_format-CARD',
1788     'section'     => 'billing',
1789     'description' => 'Fixed (unchangeable) format for credit card batches.',
1790     'type'        => 'select',
1791     'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP' ,
1792                        'csv-chase_canada-E-xactBatch', 'BoM', 'PAP' ]
1793   },
1794
1795   {
1796     'key'         => 'batch-fixed_format-CHEK',
1797     'section'     => 'billing',
1798     'description' => 'Fixed (unchangeable) format for electronic check batches.',
1799     'type'        => 'select',
1800     'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM', 'PAP',
1801                        'ach-spiritone',
1802                      ]
1803   },
1804
1805   {
1806     'key'         => 'batch-increment_expiration',
1807     'section'     => 'billing',
1808     'description' => 'Increment expiration date years in batches until cards are current.  Make sure this is acceptable to your batching provider before enabling.',
1809     'type'        => 'checkbox'
1810   },
1811
1812   {
1813     'key'         => 'batchconfig-BoM',
1814     'section'     => 'billing',
1815     'description' => 'Configuration for Bank of Montreal batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account',
1816     'type'        => 'textarea',
1817   },
1818
1819   {
1820     'key'         => 'batchconfig-PAP',
1821     'section'     => 'billing',
1822     'description' => 'Configuration for PAP batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account',
1823     'type'        => 'textarea',
1824   },
1825
1826   {
1827     'key'         => 'batchconfig-csv-chase_canada-E-xactBatch',
1828     'section'     => 'billing',
1829     'description' => 'Gateway ID for Chase Canada E-xact batching',
1830     'type'        => 'text',
1831   },
1832
1833   {
1834     'key'         => 'payment_history-years',
1835     'section'     => 'UI',
1836     'description' => 'Number of years of payment history to show by default.  Currently defaults to 2.',
1837     'type'        => 'text',
1838   },
1839
1840   {
1841     'key'         => 'cust_main-use_comments',
1842     'section'     => 'UI',
1843     'description' => 'Display free form comments on the customer edit screen.  Useful as a scratch pad.',
1844     'type'        => 'checkbox',
1845   },
1846
1847   {
1848     'key'         => 'cust_main-disable_notes',
1849     'section'     => 'UI',
1850     'description' => 'Disable new style customer notes - timestamped and user identified customer notes.  Useful in tracking who did what.',
1851     'type'        => 'checkbox',
1852   },
1853
1854   {
1855     'key'         => 'cust_main_note-display_times',
1856     'section'     => 'UI',
1857     'description' => 'Display full timestamps (not just dates) for customer notes.',
1858     'type'        => 'checkbox',
1859   },
1860
1861   {
1862     'key'         => 'cust_main-ticket_statuses',
1863     'section'     => 'UI',
1864     'description' => 'Show tickets with these statuses on the customer view page.',
1865     'type'        => 'selectmultiple',
1866     'select_enum' => [qw( new open stalled resolved rejected deleted )],
1867   },
1868
1869   {
1870     'key'         => 'cust_main-max_tickets',
1871     'section'     => 'UI',
1872     'description' => 'Maximum number of tickets to show on the customer view page.',
1873     'type'        => 'text',
1874   },
1875
1876   {
1877     'key'         => 'cust_main-skeleton_tables',
1878     'section'     => '',
1879     'description' => 'Tables which will have skeleton records inserted into them for each customer.  Syntax for specifying tables is unfortunately a tricky perl data structure for now.',
1880     'type'        => 'textarea',
1881   },
1882
1883   {
1884     'key'         => 'cust_main-skeleton_custnum',
1885     'section'     => '',
1886     'description' => 'Customer number specifying the source data to copy into skeleton tables for new customers.',
1887     'type'        => 'text',
1888   },
1889
1890   {
1891     'key'         => 'cust_main-enable_birthdate',
1892     'section'     => 'UI',
1893     'descritpion' => 'Enable tracking of a birth date with each customer record',
1894     'type'        => 'checkbox',
1895   },
1896
1897   {
1898     'key'         => 'support-key',
1899     'section'     => '',
1900     'description' => 'A support key enables access to commercial services delivered over the network, such as the payroll module, access to the internal ticket system, priority support and optional backups.',
1901     'type'        => 'text',
1902   },
1903
1904   {
1905     'key'         => 'card-types',
1906     'section'     => 'billing',
1907     'description' => 'Select one or more card types to enable only those card types.  If no card types are selected, all card types are available.',
1908     'type'        => 'selectmultiple',
1909     'select_enum' => \@card_types,
1910   },
1911
1912   {
1913     'key'         => 'dashboard-toplist',
1914     'section'     => 'UI',
1915     'description' => 'List of items to display on the top of the front page',
1916     'type'        => 'textarea',
1917   },
1918
1919   {
1920     'key'         => 'impending_recur_template',
1921     'section'     => 'billing',
1922     'description' => 'Template file for alerts about looming first time recurrant billing.  See the <a href="http://search.cpan.org/~mjd/Text-Template.pm">Text::Template</a> documentation for details on the template substitition language.  Also see packages with a <a href="../browse/part_pkg.cgi">flat price plan</a>  The following variables are available<ul><li><code>$packages</code> allowing <code>$packages->[0]</code> thru <code>$packages->[n]</code> <li><code>$package</code> the first package, same as <code>$packages->[0]</code> <li><code>$recurdates</code> allowing <code>$recurdates->[0]</code> thru <code>$recurdates->[n]</code> <li><code>$recurdate</code> the first recurdate, same as <code>$recurdate->[0]</code> <li><code>$first</code> <li><code>$last</code></ul>',
1923 # <li><code>$payby</code> <li><code>$expdate</code> most likely only confuse
1924     'type'        => 'textarea',
1925   },
1926
1927   {
1928     'key'         => 'logo.png',
1929     'section'     => 'billing',  #? 
1930     'description' => 'An image to include in some types of invoices',
1931     'type'        => 'binary',
1932   },
1933
1934   {
1935     'key'         => 'logo.eps',
1936     'section'     => 'billing',  #? 
1937     'description' => 'An image to include in some types of invoices',
1938     'type'        => 'binary',
1939   },
1940
1941   {
1942     'key'         => 'selfservice-ignore_quantity',
1943     'section'     => '',
1944     'description' => 'Ignores service quantity restrictions in self-service context.  Strongly not recommended - just set your quantities correctly in the first place.',
1945     'type'        => 'checkbox',
1946   },
1947
1948   {
1949     'key'         => 'disable_setup_suspended_pkgs',
1950     'section'     => 'billing',
1951     'description' => 'Disables charging of setup fees for suspended packages.',
1952     'type'       => 'checkbox',
1953   },
1954
1955   {
1956     'key' => 'password-generated-allcaps',
1957     'section' => 'password',
1958     'description' => 'Causes passwords automatically generated to consist entirely of capital letters',
1959     'type' => 'checkbox',
1960   },
1961
1962   {
1963     'key'         => 'datavolume-forcemegabytes',
1964     'section'     => 'UI',
1965     'description' => 'All data volumes are expressed in megabytes',
1966     'type'        => 'checkbox',
1967   },
1968
1969   {
1970     'key'         => 'datavolume-significantdigits',
1971     'section'     => 'UI',
1972     'description' => 'number of significant digits to use to represent data volumes',
1973     'type'        => 'text',
1974   },
1975
1976   {
1977     'key'         => 'disable_void_after',
1978     'section'     => 'billing',
1979     'description' => 'Number of seconds after which freeside won\'t attempt to VOID a payment first when performing a refund.',
1980     'type'        => 'text',
1981   },
1982
1983   {
1984     'key'         => 'disable_line_item_date_ranges',
1985     'section'     => 'billing',
1986     'description' => 'Prevent freeside from automatically generating date ranges on invoice line items.',
1987     'type'        => 'checkbox',
1988   },
1989
1990 );
1991
1992 1;