package FS::Conf;
use strict;
-use vars qw($base_dir @config_items @base_items @card_types $DEBUG $conf_cache);
+use vars qw( $base_dir @config_items @base_items @card_types $DEBUG
+ $conf_cache $conf_cache_enabled
+ );
use Carp;
use IO::File;
use File::Basename;
use Locale::Currency;
use FS::ConfItem;
use FS::ConfDefaults;
-use FS::Conf_compat17;
use FS::Locales;
use FS::payby;
use FS::conf;
use FS::Record qw(qsearch qsearchs);
-use FS::UID qw(dbh datasrc use_confcompat);
+use FS::UID qw(dbh datasrc);
use FS::Misc::Invoicing qw( spool_formats );
$base_dir = '%%%FREESIDE_CONF%%%';
$DEBUG = 0;
+$conf_cache_enabled = 0;
+
=head1 NAME
FS::Conf - Freeside configuration values
=cut
-sub _usecompat {
- my ($self, $method) = (shift, shift);
- carp "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,$agentnum,$agentonly)=@_;
my $hashref = { 'name' => $name };
local $FS::Record::conf = undef; # XXX evil hack prevents recursion
+ $conf_cache = undef unless $conf_cache_enabled; # use cache only when it is
+ # safe to do so
my $cv;
my @a = (
($agentnum || ()),
my $key = join(':',$name, $a, $l);
if (! exists $conf_cache->{$key}){
$hashref->{locale} = $l;
- # $conf_cache is reset in FS::UID during myconnect, so the cache is reset per connection
+ # $conf_cache is reset in FS::UID during myconnect, so the cache is
+ # reset per connection
$conf_cache->{$key} = FS::Record::qsearchs('conf', $hashref);
}
return $conf_cache->{$key} if $conf_cache->{$key};
sub config {
my $self = shift;
- return $self->_usecompat('config', @_) if use_confcompat;
carp "FS::Conf->config(". join(', ', @_). ") called"
if $DEBUG > 1;
sub config_binary {
my $self = shift;
- return $self->_usecompat('config_binary', @_) if use_confcompat;
my $cv = $self->_config(@_) or return;
length($cv->value) ? decode_base64($cv->value) : '';
sub exists {
my $self = shift;
- return $self->_usecompat('exists', @_) if use_confcompat;
#my($name, $agentnum)=@_;
sub config_bool {
my $self = shift;
- return $self->_usecompat('exists', @_) if use_confcompat;
my($name,$agentnum,$agentonly) = @_;
# these to fall back to standard values
sub config_orbase {
my $self = shift;
- return $self->_usecompat('config_orbase', @_) if use_confcompat;
my( $name, $suffix ) = @_;
if ( $self->exists("${name}_$suffix") ) {
sub key_orbase {
my $self = shift;
- #no compat for this...return $self->_usecompat('config_orbase', @_) if use_confcompat;
my( $name, $suffix ) = @_;
if ( $self->exists("${name}_$suffix") ) {
sub touch {
my $self = shift;
- return $self->_usecompat('touch', @_) if use_confcompat;
my($name, $agentnum) = @_;
#unless ( $self->exists($name, $agentnum) ) {
sub set {
my $self = shift;
- return $self->_usecompat('set', @_) if use_confcompat;
my($name, $value, $agentnum) = @_;
$value =~ /^(.*)$/s;
sub set_binary {
my $self = shift;
- return if use_confcompat;
my($name, $value, $agentnum)=@_;
$self->set($name, encode_base64($value), $agentnum);
sub delete {
my $self = shift;
- return $self->_usecompat('delete', @_) if use_confcompat;
my($name, $agentnum) = @_;
if ( my $cv = FS::Record::qsearchs('conf', {name => $name, agentnum => $agentnum, locale => $self->{locale}}) ) {
sub delete_bool {
my $self = shift;
- return $self->_usecompat('delete', @_) if use_confcompat;
my($name, $agentnum) = @_;
sub import_config_item {
my ($self,$item,$dir) = @_;
my $key = $item->key;
- if ( -e "$dir/$key" && ! use_confcompat ) {
+ if ( -e "$dir/$key" ) {
warn "Inserting $key\n" if $DEBUG;
local $/;
my $value = readline(new IO::File "$dir/$key");
}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);
-
- if ( $type !~ /^(binary|image)$/ ) {
-
- {
- 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;
- }
-
} else {
-
- no warnings 'uninitialized';
- $error .= "$key fails binary comparison; "
- unless scalar($self->config_binary($key)) eq scalar($compat->config_binary($key));
-
+ warn "Not inserting $key\n" if $DEBUG;
}
-
-#remove deprecated config on our own terms, not freeside-upgrade's
-# 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
sub config_items {
my $self = shift;
- return $self->_usecompat('config_items', @_) if use_confcompat;
( @config_items, $self->_orbase_items(@_) );
}
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;
- }
+ my $conf = new FS::Conf;
+ foreach my $item ( $conf->config_items(dir => $dir) ) {
+ $conf->import_config_item($item, $dir);
}
- $FS::UID::use_confcompat = 0;
''; #success
}
invoice_latexsmallfooter
invoice_latexnotes
invoice_latexcoupon
+invoice_latexwatermark
invoice_html
invoice_htmlreturnaddress
invoice_htmlfooter
invoice_htmlnotes
+invoice_htmlwatermark
logo.png
logo.eps
);
}
}
+my $validate_email = sub { $_[0] =~
+ /^[^@]+\@[[:alnum:]-]+(\.[[:alnum:]-]+)+$/
+ ? '' : 'Invalid email address';
+ };
+
#Billing (81 items)
#Invoicing (50 items)
#UI (69 items)
@config_items = map { new FS::ConfItem $_ } (
- {
- 'key' => 'address',
- 'section' => 'deprecated',
- 'description' => 'This configuration option is no longer used. See <a href="#invoice_template">invoice_template</a> instead.',
- 'type' => 'text',
- },
-
{
'key' => 'event_log_level',
'section' => 'notification',
'per_locale' => 1,
},
- {
- 'key' => 'deleteinvoices',
- 'section' => 'UI',
- 'description' => 'Enable invoices deletions. Be very careful! Deleting an invoice will remove all traces that the invoice ever existed! Normally, you would void or apply a credit against the invoice instead.',
- 'type' => 'checkbox',
- },
-
{
'key' => 'deletecredits',
#not actually deprecated yet
'type' => 'checkbox',
},
- {
- 'key' => 'unapplypayments',
- 'section' => 'deprecated',
- 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable "unapplication" of unclosed payments.',
- 'type' => 'checkbox',
- },
-
- {
- 'key' => 'unapplycredits',
- 'section' => 'deprecated',
- 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable "unapplication" of unclosed credits.',
- 'type' => 'checkbox',
- },
-
{
'key' => 'dirhash',
'section' => 'shell',
'description' => 'Return address on email invoices (address only, see invoice_from_name)',
'type' => 'text',
'per_agent' => 1,
- 'validate' => sub { $_[0] =~
- /^[^@]+\@[[:alnum:]-]+(\.[[:alnum:]-]+)+$/
- ? '' : 'Invalid email address';
- }
+ 'validate' => $validate_email,
},
{
'per_locale' => 1,
},
+ {
+ 'key' => 'invoice_htmlwatermark',
+ 'section' => 'invoicing',
+ 'description' => 'Watermark for HTML invoices. Appears in a semitransparent positioned DIV overlaid on the main invoice container.',
+ 'type' => 'textarea',
+ 'per_agent' => 1,
+ 'per_locale' => 1,
+ },
+
{
'key' => 'invoice_latex',
'section' => 'invoicing',
'per_locale' => 1,
},
+ {
+ 'key' => 'invoice_latexwatermark',
+ 'section' => 'invoicing',
+ 'description' => 'Watermark for LaTeX invoices. See "texdoc background" for information on what this can contain. The content itself should be enclosed in braces, optionally followed by a comma and any formatting options.',
+ 'type' => 'textarea',
+ 'per_agent' => 1,
+ 'per_locale' => 1,
+ },
+
{
'key' => 'invoice_email_pdf',
'section' => 'invoicing',
'type' => 'checkbox',
},
- {
- 'key' => 'invoice_send_receipts',
- 'section' => 'deprecated',
- 'description' => '<b>DEPRECATED</b>, this used to send an invoice copy on payments and credits. See the payment_receipt_email and XXXX instead.',
- 'type' => 'checkbox',
- },
-
{
'key' => 'payment_receipt',
'section' => 'notification',
# 'description' => 'Directory which contains domain registry information. Each registry is a directory.',
# },
- {
- 'key' => 'report_template',
- 'section' => 'deprecated',
- 'description' => 'Deprecated template file for reports.',
- 'type' => 'textarea',
- },
-
{
'key' => 'maxsearchrecordsperpage',
'section' => 'UI',
'svc_acct' => 'Account (svc_acct)',
'svc_phone' => 'Phone number (svc_phone)',
'svc_pbx' => 'PBX (svc_pbx)',
+ 'none' => 'None - package only',
],
},
{
'key' => 'enable_taxclasses',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Enable per-package tax classes',
'type' => 'checkbox',
},
{
'key' => 'require_taxclasses',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Require a taxclass to be entered for every package',
'type' => 'checkbox',
},
{
- 'key' => 'enable_taxproducts',
- 'section' => 'billing',
+ 'key' => 'tax_data_vendor',
+ 'section' => 'taxation',
'description' => 'Tax data vendor you are using.',
'type' => 'select',
- 'select_enum' => [ 'cch', 'billsoft', 'avalara' ],
+ 'select_enum' => [ '', 'cch', 'billsoft', 'avalara', 'suretax' ],
},
{
'key' => 'taxdatadirectdownload',
- 'section' => 'billing', #well
- 'description' => 'Enable downloading tax data directly from the vendor site. at least three lines: URL, username, and password.j',
+ 'section' => 'taxation',
+ 'description' => 'Enable downloading tax data directly from CCH. at least three lines: URL, username, and password.j',
'type' => 'textarea',
},
{
'key' => 'ignore_incalculable_taxes',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Prefer to invoice without tax over not billing at all',
'type' => 'checkbox',
},
{
'key' => 'billsoft-company_code',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Billsoft tax service company code (3 letters)',
'type' => 'text',
},
{
'key' => 'avalara-taxconfig',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Avalara tax service configuration. Four lines: company code, account number, license key, test mode (1 to enable).',
'type' => 'textarea',
},
+ {
+ 'key' => 'suretax-hostname',
+ 'section' => 'taxation',
+ 'description' => 'SureTax server name; defaults to the test server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'suretax-client_number',
+ 'section' => 'taxation',
+ 'description' => 'SureTax tax service client ID.',
+ 'type' => 'text',
+ },
+ {
+ 'key' => 'suretax-validation_key',
+ 'section' => 'taxation',
+ 'description' => 'SureTax validation key (UUID).',
+ 'type' => 'text',
+ },
+ {
+ 'key' => 'suretax-business_unit',
+ 'section' => 'taxation',
+ 'description' => 'SureTax client business unit name; optional.',
+ 'type' => 'text',
+ 'per_agent' => 1,
+ },
+ {
+ 'key' => 'suretax-regulatory_code',
+ 'section' => 'taxation',
+ 'description' => 'SureTax client regulatory status.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'ILEC', 'IXC', 'CLEC', 'VOIP', 'ISP', 'Wireless' ],
+ 'per_agent' => 1,
+ },
+
+
{
'key' => 'welcome_msgnum',
'section' => 'notification',
'select_enum' => [ qw(CARD DCRD CHEK DCHK CASH WEST MCRD MCHK PPAL) ],
},
+ {
+ 'key' => 'banned_pay-pad',
+ 'section' => 'billing',
+ 'description' => 'Padding for encrypted storage of banned credit card hashes. If you already have new-style SHA512 entries in the banned_pay table, do not change as this will invalidate the old entries.',
+ 'type' => 'text',
+ },
+
{
'key' => 'payby-default',
'section' => 'deprecated',
'type' => 'checkbox',
},
- {
- 'key' => 'paymentforcedtobatch',
- 'section' => 'deprecated',
- 'description' => 'See batch-enable_payby and realtime-disable_payby. Used to (for CHEK): Cause per customer payment entry to be forced to a batch processor rather than performed realtime.',
- 'type' => 'checkbox',
- },
-
{
'key' => 'svc_acct-notes',
'section' => 'deprecated',
},
{
- 'key' => 'users-allow_comp',
- 'section' => 'deprecated',
- 'description' => '<b>DEPRECATED</b>, enable the <i>Complimentary customer</i> access right instead. Was: Usernames (Freeside users, created with <a href="../docs/man/bin/freeside-adduser.html">freeside-adduser</a>) which can create complimentary customers, one per line. If no usernames are entered, all users can create complimentary accounts.',
- 'type' => 'textarea',
+ 'key' => 'dump-email_to',
+ 'section' => '',
+ 'description' => "Optional email address to send success/failure message for database dumps.",
+ 'type' => 'text',
+ 'validate' => $validate_email,
},
{
'type' => 'checkbox',
},
+ {
+ 'key' => 'manual_process-single_invoice_amount',
+ 'section' => 'billing',
+ 'description' => 'When entering manual credit card and ACH payments, amount will not autofill if the customer has more than one open invoice',
+ 'type' => 'checkbox',
+ },
+
{
'key' => 'manual_process-pkgpart',
'section' => 'billing',
'per_agent' => 1,
},
- {
- 'key' => 'city_not_required',
- 'section' => 'required',
- 'description' => 'Turn off requirement for a City to be entered for billing & shipping addresses',
- 'type' => 'checkbox',
- 'per_agent' => 1,
- },
-
- {
- 'key' => 'echeck-void',
- 'section' => 'deprecated',
- 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable local-only voiding of echeck payments in addition to refunds against the payment gateway',
- 'type' => 'checkbox',
- },
-
- {
- 'key' => 'cc-void',
- 'section' => 'deprecated',
- 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable local-only voiding of credit card payments in addition to refunds against the payment gateway',
- 'type' => 'checkbox',
- },
-
- {
- 'key' => 'unvoid',
- 'section' => 'deprecated',
- 'description' => '<B>DEPRECATED</B>, now controlled by ACLs. Used to enable unvoiding of voided payments',
- 'type' => 'checkbox',
- },
-
{
'key' => 'address1-search',
'section' => 'UI',
'per_agent' => 1,
},
- { 'key' => 'referral_credit',
- 'section' => 'deprecated',
- 'description' => "Used to enable one-time referral credits in the amount of one month <i>referred</i> customer's recurring fee (irregardless of frequency). Replace with a billing event on appropriate packages.",
- 'type' => 'checkbox',
- },
-
{ 'key' => 'selfservice_server-cache_module',
'section' => 'self-service',
'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.',
{
'key' => 'tax-ship_address',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'By default, tax calculations are done based on the billing address. Enable this switch to calculate tax based on the shipping address instead.',
'type' => 'checkbox',
}
,
{
'key' => 'tax-pkg_address',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'By default, tax calculations are done based on the billing address. Enable this switch to calculate tax based on the package address instead (when present).',
'type' => 'checkbox',
},
{
'key' => 'batchconfig-RBC',
'section' => 'billing',
- 'description' => 'Configuration for Royal Bank of Canada PDS batching, four lines: 1. Client number, 2. Short name, 3. Long name, 4. Transaction code.',
+ 'description' => 'Configuration for Royal Bank of Canada PDS batching, five lines: 1. Client number, 2. Short name, 3. Long name, 4. Transaction code 5. (optional) set to TEST to turn on test mode.',
'type' => 'textarea',
},
'type' => 'text',
},
+ {
+ 'key' => 'batchconfig-nacha-origin_name',
+ 'section' => 'billing',
+ 'description' => 'Configuration for NACHA batching, Origin name (defaults to company name, but sometimes bank name is needed instead.)',
+ 'type' => 'text',
+ },
+
{
'key' => 'batch-manual_approval',
'section' => 'billing',
{
'key' => 'previous_balance-exclude_from_total',
'section' => 'invoicing',
- 'description' => 'Do not include previous balance in the \'Total\' line. Only meaningful when invoice_sections is false. Optionally provide text to override the Total New Charges description',
- 'type' => [ qw(checkbox text) ],
+ 'description' => 'Show separate totals for previous invoice balance and new charges. Only meaningful when invoice_sections is false.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'previous_balance-text',
+ 'section' => 'invoicing',
+ 'description' => 'Text for the label of the total previous balance, when it is shown separately. Defaults to "Previous Balance".',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'previous_balance-text-total_new_charges',
+ 'section' => 'invoicing',
+ 'description' => 'Text for the label of the total of new charges, when it is shown separately. If invoice_show_prior_due_date is enabled, the due date of current charges will be appended. Defaults to "Total New Charges".',
+ 'type' => 'text',
},
{
'section' => 'invoicing',
'description' => 'Instead of showing payments (and credits) applied to the invoice, show those received since the previous invoice date.',
'type' => 'checkbox',
+ 'uscensus' => 'U.S. Census Bureau',
},
{
'type' => 'checkbox',
},
+ {
+ 'key' => 'cust_main-no_city_in_address',
+ 'section' => 'UI',
+ 'description' => 'Turn off City for billing & shipping addresses',
+ 'type' => 'checkbox',
+ },
+
{
'key' => 'census_year',
'section' => 'UI',
{
'key' => 'tax_district_method',
- 'section' => 'UI',
+ 'section' => 'taxation',
'description' => 'The method to use to look up tax district codes.',
'type' => 'select',
#'select_hash' => [ FS::Misc::Geo::get_district_methods() ],
{
'key' => 'tax-cust_exempt-groups',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'List of grouping possibilities for tax names, for per-customer exemption purposes, one tax name per line. For example, "GST" would indicate the ability to exempt customers individually from taxes named "GST" (but not other taxes).',
'type' => 'textarea',
},
{
'key' => 'tax-cust_exempt-groups-num_req',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'When using tax-cust_exempt-groups, control whether individual tax exemption numbers are required for exemption from different taxes.',
'type' => 'select',
'select_hash' => [ '' => 'Not required',
{
'key' => 'enable_tax_adjustments',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Enable the ability to add manual tax adjustments.',
'type' => 'checkbox',
},
{
'key' => 'cust_class-tax_exempt',
- 'section' => 'billing',
+ 'section' => 'taxation',
'description' => 'Control the tax exemption flag per customer class rather than per indivual customer.',
'type' => 'checkbox',
},
'type' => 'checkbox',
},
+ {
+ 'key' => 'selfservice-enable_payment_without_balance',
+ 'section' => 'self-service',
+ 'description' => 'Allow selfservice customers to make payments even if balance is zero or below (resulting in an unapplied payment and negative balance.)',
+ 'type' => 'checkbox',
+ },
+
{
'key' => 'logout-timeout',
'section' => 'UI',
'type' => 'checkbox',
},
- { key => "apacheroot", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "apachemachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "apachemachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "bindprimary", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "bindsecondaries", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "bsdshellmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "cyrus", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "cp_app", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "erpcdmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "icradiusmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "icradius_mysqldest", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "icradius_mysqlsource", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "icradius_secrets", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "maildisablecatchall", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "mxmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "nsmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "arecords", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "cnamerecords", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "nismachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "qmailmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "radiusmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "sendmailconfigpath", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "sendmailmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "sendmailrestart", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "shellmachine", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "shellmachine-useradd", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "shellmachine-userdel", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "shellmachine-usermod", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "shellmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "radiusprepend", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "textradiusprepend", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "username_policy", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "vpopmailmachines", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "vpopmailrestart", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "safe-part_pkg", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "selfservice_server-quiet", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "signup_server-quiet", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "signup_server-email", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "vonage-username", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "vonage-password", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
- { key => "vonage-fromnumber", section => "deprecated", description => "<b>DEPRECATED</b>", type => "text" },
+ {
+ 'key' => 'default_appointment_length',
+ 'section' => 'UI',
+ 'description' => 'Default appointment length, in minutes (30 minute granularity).',
+ 'type' => 'text',
+ },
);