i18n, RT#12515
authorivan <ivan>
Wed, 11 May 2011 16:20:13 +0000 (16:20 +0000)
committerivan <ivan>
Wed, 11 May 2011 16:20:13 +0000 (16:20 +0000)
23 files changed:
FS/FS.pm
FS/FS/Conf.pm
FS/FS/L10N.pm [new file with mode: 0644]
FS/FS/Locales.pm [new file with mode: 0644]
FS/FS/Mason.pm
FS/FS/Mason/Request.pm
FS/FS/Msgcat.pm
FS/FS/Setup.pm
FS/FS/TicketSystem.pm
FS/MANIFEST
FS/t/Locales.t [new file with mode: 0644]
README
httemplate/L10N [new file with mode: 0644]
httemplate/autohandler
httemplate/edit/elements/edit.html
httemplate/edit/process/elements/process.html
httemplate/elements/header-minimal.html
httemplate/elements/header-popup.html
httemplate/elements/menu.html
httemplate/index.html
httemplate/pref/pref-process.html
httemplate/pref/pref.html
httemplate/search/elements/search.html

index 60f4cd1..c8c58bd 100644 (file)
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -39,6 +39,8 @@ L<FS::CurrentUser> -  Package representing the current user
 
 L<FS::CGI> - Non OO-subroutines for the web interface.
 
 
 L<FS::CGI> - Non OO-subroutines for the web interface.
 
+L<FS::Locales> - Locales
+
 L<FS::Msgcat> - Message catalog
 
 L<FS::SearchCache> - Search cache
 L<FS::Msgcat> - Message catalog
 
 L<FS::SearchCache> - Search cache
index f7b68b2..d5828f0 100644 (file)
@@ -8,6 +8,7 @@ use MIME::Base64;
 use FS::ConfItem;
 use FS::ConfDefaults;
 use FS::Conf_compat17;
 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::payby;
 use FS::conf;
 use FS::Record qw(qsearch qsearchs);
@@ -1800,9 +1801,9 @@ and customer address. Include units.',
   {
     'key'         => 'locale',
     'section'     => 'UI',
   {
     'key'         => 'locale',
     'section'     => 'UI',
-    'description' => 'Message locale',
+    'description' => 'Default locale',
     'type'        => 'select',
     'type'        => 'select',
-    'select_enum' => [ qw(en_US) ],
+    'select_enum' => [ FS::Locales->locales ],
   },
 
   {
   },
 
   {
diff --git a/FS/FS/L10N.pm b/FS/FS/L10N.pm
new file mode 100644 (file)
index 0000000..c48261f
--- /dev/null
@@ -0,0 +1,6 @@
+package FS::L10N;
+use base qw(Locale::Maketext);
+
+our %Lexicon = ( _AUTO=>1 );
+
+1;
diff --git a/FS/FS/Locales.pm b/FS/FS/Locales.pm
new file mode 100644 (file)
index 0000000..607f2be
--- /dev/null
@@ -0,0 +1,61 @@
+package FS::Locales;
+
+use strict;
+use Tie::IxHash;
+
+=head1 NAME
+
+FS::Locales - Supported locales
+
+=head1 SYNOPSIS
+
+  use FS::Locales;
+
+  my @locales = FS::Locales->locales;
+
+=head1 DESCRIPTION
+
+FS::Locales provides a list of supported locales.
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item locales
+
+Returns a list of the available locales.
+
+=cut
+
+tie our %locales, 'Tie::IxHash',
+  'en_US', { name => 'English', country => 'United States', },
+  'iw_IL', { name => 'Hebrew',  country => 'Israel', rtl=>1, },
+;
+
+sub locales {
+  keys %locales;
+}
+
+=item locale_info LOCALE
+
+Returns a hash of information about a locale.
+
+=cut
+
+sub locale_info {
+  my($class, $locale) = @_;
+  %{ $locales{$locale} };
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Msgcat>
+
+=cut
+
+1;
+
index 67c1d9f..e74f44e 100644 (file)
@@ -137,6 +137,8 @@ if ( -e $addl_handler_use_file ) {
   use FS::TicketSystem;
   use FS::NetworkMonitoringSystem;
   use FS::Tron qw( tron_lint );
   use FS::TicketSystem;
   use FS::NetworkMonitoringSystem;
   use FS::Tron qw( tron_lint );
+  use FS::Locales;
+  use FS::L10N;
 
   use FS::agent;
   use FS::agent_type;
 
   use FS::agent;
   use FS::agent_type;
@@ -406,6 +408,11 @@ if ( -e $addl_handler_use_file ) {
     $m->comp('/elements/errorpage-popup.html', @_);
   }
 
     $m->comp('/elements/errorpage-popup.html', @_);
   }
 
+  sub mt {
+    use vars qw($lh);
+    $lh->maketext(@_);
+  }
+
   sub redirect {
     my( $location ) = @_;
     use vars qw($m);
   sub redirect {
     my( $location ) = @_;
     use vars qw($m);
@@ -526,13 +533,15 @@ sub mason_interps {
     ${$_[0]} = "'". ${$_[0]}. "'";
   };
 
     ${$_[0]} = "'". ${$_[0]}. "'";
   };
 
+  my $defang_sub = sub {
+    ${$_[0]} = $html_defang->defang(${$_[0]});
+  };
+
   my $fs_interp = new HTML::Mason::Interp (
     %interp,
     comp_root    => $fs_comp_root,
   my $fs_interp = new HTML::Mason::Interp (
     %interp,
     comp_root    => $fs_comp_root,
-    escape_flags => { 'js_string' => $js_string_sub,
-                      'defang'    => sub {
-                        ${$_[0]} = $html_defang->defang(${$_[0]});
-                      },
+    escape_flags => { 'js_string'   => $js_string_sub,
+                      'defang'      => $defang_sub,
                     },
     compiler     => HTML::Mason::Compiler::ToObject->new(
                       allow_globals        => [qw(%session)],
                     },
     compiler     => HTML::Mason::Compiler::ToObject->new(
                       allow_globals        => [qw(%session)],
index a5ec21f..bf704bd 100644 (file)
@@ -51,7 +51,7 @@ sub freeside_setup {
     } else {
 
       package HTML::Mason::Commands;
     } else {
 
       package HTML::Mason::Commands;
-      use vars qw( $cgi $p $fsurl );
+      use vars qw( $cgi $p $fsurl $lh );
       use Encode;
       use FS::UID qw( cgisuidsetup );
       use FS::CGI qw( popurl rooturl );
       use Encode;
       use FS::UID qw( cgisuidsetup );
       use FS::CGI qw( popurl rooturl );
index 70933b2..e111f4e 100644 (file)
@@ -1,7 +1,7 @@
 package FS::Msgcat;
 
 use strict;
 package FS::Msgcat;
 
 use strict;
-use vars qw( @ISA @EXPORT_OK $conf $locale $debug );
+use vars qw( @ISA @EXPORT_OK $conf $def_locale $debug );
 use Exporter;
 use FS::UID;
 #use FS::Record qw( qsearchs ); # wtf?  won't import...
 use Exporter;
 use FS::UID;
 #use FS::Record qw( qsearchs ); # wtf?  won't import...
@@ -16,7 +16,7 @@ FS::UID->install_callback( sub {
   eval "use FS::Conf;";
   die $@ if $@;
   $conf = new FS::Conf;
   eval "use FS::Conf;";
   die $@ if $@;
   $conf = new FS::Conf;
-  $locale = $conf->config('locale') || 'en_US';
+  $def_locale = $conf->config('locale') || 'en_US';
   $debug = $conf->exists('show-msgcat-codes')
 });
 
   $debug = $conf->exists('show-msgcat-codes')
 });
 
@@ -52,17 +52,28 @@ sub gettext {
   $debug ? geterror(@_) : _gettext(@_);
 }
 
   $debug ? geterror(@_) : _gettext(@_);
 }
 
+#though i guess we don't really have to cache here since we do it in
+# FS::L10N::DBI
+our %cache;
+
 sub _gettext {
   my $msgcode = shift;
 sub _gettext {
   my $msgcode = shift;
+  my $locale =  (@_ && shift)
+             || $FS::CurrentUser::CurrentUser->option('locale')
+             || $def_locale;
+
+  return $cache{$locale}->{$msgcode} if exists $cache{$locale}->{$msgcode};
+
   my $msgcat = FS::Record::qsearchs('msgcat', {
     'msgcode' => $msgcode,
   my $msgcat = FS::Record::qsearchs('msgcat', {
     'msgcode' => $msgcode,
-    'locale' => $locale
+    'locale'  => $locale,
   } );
   if ( $msgcat ) {
   } );
   if ( $msgcat ) {
-    $msgcat->msg;
+    $cache{$locale}->{$msgcode} = $msgcat->msg;
   } else {
   } else {
-    warn "WARNING: message for msgcode $msgcode in locale $locale not found";
-    $msgcode;
+    warn "WARNING: message for msgcode $msgcode in locale $locale not found"
+      unless $locale eq 'en_US';
+    $cache{$locale}->{$msgcode} = $msgcode;
   }
 
 }
   }
 
 }
@@ -78,6 +89,7 @@ sub geterror {
   my $msgcode = shift;
   my $msg = _gettext($msgcode);
   if ( $msg eq $msgcode ) {
   my $msgcode = shift;
   my $msg = _gettext($msgcode);
   if ( $msg eq $msgcode ) {
+    my $locale = $FS::CurrentUser::CurrentUser->option('locale') || $def_locale;
     "Error code $msgcode (message for locale $locale not found)";
   } else {
     "$msg (error code $msgcode)";
     "Error code $msgcode (message for locale $locale not found)";
   } else {
     "$msg (error code $msgcode)";
@@ -92,7 +104,7 @@ i18n/l10n, eek
 
 =head1 SEE ALSO
 
 
 =head1 SEE ALSO
 
-L<FS::msgcat>, L<FS::Record>, schema.html from the base documentation.
+L<FS::Locales>, L<FS::msgcat>
 
 =cut
 
 
 =cut
 
index 13d6f60..fec1c2d 100644 (file)
@@ -103,7 +103,7 @@ sub populate_addl_locales {
 
 sub _add_country {
 
 
 sub _add_country {
 
-  use Locale::SubCountry;
+  use Locale::SubCountry 1.44;
 
   my( $country ) = shift;
 
 
   my( $country ) = shift;
 
index 8ea4b03..1020a8c 100644 (file)
@@ -28,7 +28,7 @@ sub AUTOLOAD {
 }
 
 sub _upgrade_data {
 }
 
 sub _upgrade_data {
-  return if $system ne 'RT_Internal';
+  return if !defined($system) || $system ne 'RT_Internal';
   my ($class, %opts) = @_;
 
   # go ahead and use the RT API for this
   my ($class, %opts) = @_;
 
   # go ahead and use the RT API for this
index 009c40d..2851801 100644 (file)
@@ -594,3 +594,6 @@ FS/msa.pm
 t/msa.t
 FS/rate_center.pm
 t/rate_center.t
 t/msa.t
 FS/rate_center.pm
 t/rate_center.t
+FS/Locales.pm
+t/Locales.t
+FS/L10N.pm
diff --git a/FS/t/Locales.t b/FS/t/Locales.t
new file mode 100644 (file)
index 0000000..38dff7f
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Locales;
+$loaded=1;
+print "ok 1\n";
diff --git a/README b/README
index 41ae52d..a2babf3 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 Freeside is a billing and administration package for Internet Service 
 Providers, VoIP providers and other online businesses.
 
 Freeside is a billing and administration package for Internet Service 
 Providers, VoIP providers and other online businesses.
 
-Copyright (C) 2005-2008 Freeside Internet Services, Inc.
+Copyright (C) 2005-2011 Freeside Internet Services, Inc.
 Copyright (C) 2000-2005 Ivan Kohler
 Copyright (C) 1999 Silicon Interactive Software Design
 Additional copyright holders may be found in the docs/license.html file.
 Copyright (C) 2000-2005 Ivan Kohler
 Copyright (C) 1999 Silicon Interactive Software Design
 Additional copyright holders may be found in the docs/license.html file.
diff --git a/httemplate/L10N b/httemplate/L10N
new file mode 100644 (file)
index 0000000..242fede
--- /dev/null
@@ -0,0 +1,675 @@
+2
+M ./pref/pref-process.html
+M ./pref/pref.html
+
+6
+L ./config/config-process.cgi
+L ./config/config-download.cgi
+L ./config/config.cgi
+L ./config/config-image.cgi
+L ./config/config-delete.cgi
+L ./config/config-view.cgi
+
+6
+L ./docs/signup.html
+H ./docs/cvv2.html
+L ./docs/about.html
+L ./docs/license.html
+L ./docs/ach.html
+L ./docs/credits.html
+
+70
+H ./view/cust_refund.html
+H ./view/svc_acct.cgi
+H ./view/svc_phone.cgi
+M ./view/svc_domain.cgi
+M ./view/svc_domain/basics.html
+M ./view/svc_domain/acct_defaults.html
+M ./view/svc_domain/dns.html
+L ./view/svc_dsl.cgi
+L ./view/svc_forward.cgi
+L ./view/svc_hardware.cgi
+H ./view/cust_pay.html
+H ./view/cust_bill.cgi
+L ./view/cust_statement-pdf.cgi
+H ./view/cust_main/notes.html
+H ./view/cust_main/contacts.html
+L ./view/cust_main/qual_link.html
+H ./view/cust_main/payment_history.html
+H ./view/cust_main/locations.html
+H ./view/cust_main/tickets.html
+L ./view/cust_main/payment_history/statement.html
+H ./view/cust_main/payment_history/voided_payment.html
+H ./view/cust_main/payment_history/pending_payment.html
+H ./view/cust_main/payment_history/refund.html
+H ./view/cust_main/payment_history/attempted_payment.html
+H ./view/cust_main/payment_history/credit.html
+H ./view/cust_main/payment_history/payment.html
+H ./view/cust_main/payment_history/invoice.html
+H ./view/cust_main/billing.html
+H ./view/cust_main/misc.html
+L ./view/cust_main/custom.html
+H ./view/cust_main/packages.html
+H ./view/cust_main/order_pkg_link.html
+H ./view/cust_main/packages/services.html
+H ./view/cust_main/packages/section.html
+H ./view/cust_main/packages/package.html
+H ./view/cust_main/packages/status.html
+H ./view/cust_main/packages/location.html
+L ./view/cust_main/contacts_new.html
+H ./view/cust_main/one_time_charge_link.html
+H ./view/cust_main/attachments.html
+M ./view/cust_main/change_history.html
+L ./view/svc_acct/usage.html
+H ./view/svc_acct/basics.html
+L ./view/svc_acct/radius_usage.html
+H ./view/svc_acct/change_svc.html
+H ./view/svc_acct/change_svc_form.html
+L ./view/svc_acct/hosting.html
+L ./view/svc_acct/cardfortress.html
+L ./view/svc_acct/communigate.html
+H ./view/cust_svc.cgi
+L ./view/svc_broadband.cgi
+L ./view/svc_www.cgi
+L ./view/svc_dish.cgi
+L ./view/cust_statement.html
+L ./view/svc_external.cgi
+H ./view/cust_main.cgi
+H ./view/elements/svc_edit_link.html
+H ./view/elements/tr.html
+L ./view/elements/svc_export_settings.html
+H ./view/elements/svc_Common.html
+H ./view/cust_pay_void.html
+L ./view/qual.cgi
+L ./view/svc_mailinglist.cgi
+L ./view/svc_pbx.cgi
+L ./view/svc_port.cgi
+L ./view/bill_batch.cgi
+L ./view/prospect_main.html
+L ./view/svc_cert.cgi
+L ./view/port_graph.html
+H ./view/svc_Common.html
+
+16
+L ./graph/report_cust_pkg.html
+L ./graph/cust_bill_pkg.cgi
+L ./graph/money_time.cgi
+L ./graph/report_signupdate.html
+L ./graph/report_cust_bill_pkg.html
+L ./graph/elements/report.html
+L ./graph/elements/monthly.html
+L ./graph/report_money_time.html
+L ./graph/cust_pkg.cgi
+L ./graph/report_cust_bill_pkg_detail.html
+L ./graph/report_cust_bill_pkg_discount.html
+L ./graph/cust_bill_pkg_detail.cgi
+L ./graph/cust_bill_pkg_discount.html
+L ./graph/cust_pkg_cost.cgi
+L ./graph/signupdate.cgi
+L ./graph/report_cust_pkg_cost.html
+
+121
+M ./search/cust_pkg_summary.html
+H ./search/cust_main.html
+M ./search/report_cust_pkg.html
+M ./search/cust_pkg_svc.html
+L ./search/report_477.html
+L ./search/timeworked.html
+L ./search/sqlradius.html
+L ./search/report_cust_main-zip.html
+L ./search/477partV.html
+L ./search/cdr.html
+L ./search/h_cust_pay.html
+M ./search/unprovisioned_services.html
+H ./search/cust_refund.html
+H ./search/cust_svc.html
+H ./search/svc_acct.cgi
+L ./search/svc_phone.cgi
+L ./search/report_phone_avail.html
+L ./search/svc_domain.cgi
+L ./search/cust_tax_exempt.cgi
+L ./search/svc_forward.cgi
+L ./search/cust_pay_batch.cgi
+L ./search/report_rt_ticket.html
+L./search/svc_hardware.cgi
+L ./search/report_cust_pay_batch.html
+L ./search/reg_code.html
+H ./search/cust_pay.html
+L ./search/477.html
+M ./search/unapplied_cust_pay.html
+H ./search/cust_bill_pkg.cgi
+M ./search/report_unapplied_cust_pay.html
+M ./search/cust_pkg_susp.html
+M ./search/cust_main-otaker.cgi
+L ./search/report_prepaid_income.cgi
+L ./search/report_rt_transaction.html
+L ./search/rt_transaction.html
+L ./search/customer_accounting_summary.html
+L ./search/477partIA_summary.html
+L ./search/report_newtax.html
+M ./search/cust_pkg_discount.html
+L ./search/report_timeworked.html
+L ./search/report_cdr.html
+L ./search/report_sql.html
+H ./search/cust_credit.html
+L ./search/report_h_cust_pay.html
+M ./search/cust_pkg_summary.cgi
+L ./search/sqlradius.cgi
+L ./search/svc_broadband.cgi
+L ./search/477partIV.html
+L ./search/phone_avail.html
+L ./search/svc_www.cgi
+L ./search/cust_tax_exempt_pkg.cgi
+L ./search/inventory_item.html
+H ./search/report_cust_refund.html
+L ./search/svc_dish.cgi
+L ./search/sql.html
+L ./search/report_prepaid_income.html
+H ./search/report_svc_acct.html
+H ./search/report_receivables.cgi
+L ./search/cust_bill_pay.html
+L ./search/report_employee_commission.html
+L ./search/svc_external.cgi
+H ./search/cust_main.cgi
+L ./search/report_queued_newtax.cgi
+L ./search/report_newtax.cgi
+L ./search/report_tax.html
+H ./search/report_cust_bill.html
+L ./search/elements/search-xls.html
+L ./search/elements/search-html.html
+L ./search/elements/search-xml.html
+L ./search/elements/search-csv.html
+H ./search/elements/cust_pay_or_refund.html
+H ./search/elements/cust_main_dayranges.html
+L ./search/elements/cust_pay_batch_top.html
+H ./search/elements/search.html
+H ./search/elements/report_cust_pay_or_refund.html
+H ./search/report_cust_main.html
+L ./search/report_svc_broadband.html
+L ./search/cust_main-zip.html
+H ./search/cust_pay_void.html
+M ./search/cust_event.html
+L ./search/477partIIB.html
+L ./search/pay_batch.html
+H ./search/cust_pkg.cgi
+H ./search/report_receivables.html
+L ./search/report_svc_phone.html
+L ./search/qual.cgi
+L ./search/report_h_inventory_item.html
+L ./search/agent_inventory.html
+H ./search/cust_bill.html
+M ./search/cust_pay_pending.html
+H ./search/report_cust_credit.html
+L ./search/report_agent_inventory.html
+L ./search/report_tax.cgi
+M ./search/report_unprovisioned_services.html
+L ./search/cust_credit_bill.html
+L ./search/report_cust_bill_pkg_discount.html
+L ./search/pay_batch.cgi
+H ./search/report_cust_pay.html
+L ./search/cust_bill_pkg_discount.html
+L ./search/prepay_credit.html
+L ./search/phone_inventory_provisioned.html
+L ./search/477partVI_census.html
+L ./search/part_pkg.html
+L ./search/report_cust_pkg_discount.html
+L ./search/report_prospect_main.html
+M ./search/cust_pkg_susp.cgi
+M ./search/queue.html
+L ./search/cust_credit_refund.html
+L ./search/report_customer_accounting_summary.html
+L ./search/h_inventory_item.html
+L ./search/cust_credit_bill_pkg.html
+L ./search/cust_tax_exempt.html
+L ./search/cust_tax_adjustment.html
+L ./search/477partIA_detail.html
+L ./search/bill_batch.cgi
+L ./search/prospect_main.html
+L ./search/rt_ticket.html
+M ./search/report_cust_event.html
+L ./search/477partIIA.html
+L ./search/report_svc_hardware.html
+L ./search/mailinglistmember.html
+
+147
+L ./elements/about_rt.html
+L ./elements/small_prospect_view.html
+H ./elements/tr-select-part_pkg.html
+L ./elements/tr-select-hardware_type.html
+M ./elements/progress-init.html
+L ./elements/popup_link-ping.html
+H ./elements/tr-select-agent.html
+L ./elements/tr-select-discount.html
+L ./elements/tr-select-state.html
+L ./elements/select-taxproduct.html
+H ./elements/tr-select-cust-part_pkg.html
+H ./elements/tr-select-otaker.html
+H ./elements/tr-select-payby.html
+L ./elements/select-cdrbatch.html
+L ./elements/searchbar-address2.html
+L ./elements/tr-select-part_event.html
+H ./elements/select-otaker.html
+L ./elements/checkboxes-table.html
+M ./elements/select-user.html
+M ./elements/select-table.html
+H ./elements/tr-password.html
+M ./elements/tr-part_pkg_freq.html
+L ./elements/select-cust_class.html
+L ./elements/tr-select-svc_pbx.html
+L ./elements/tr-select-taxproduct.html
+M ./elements/select-pkg_class.html
+H ./elements/tr-input-date-field.html
+L ./elements/select-mac.html
+L ./elements/tr-select-agent_type.html
+H ./elements/tr-cust_svc.html
+L ./elements/email-link.html
+M ./elements/tr-freq.html
+L ./elements/tr-select-did.html
+L ./elements/tr-select-agent_types.html
+L ./elements/select-discount.html
+H ./elements/small_custview.html
+H ./elements/searchbar-cust_svc.html
+L ./elements/dashboard-install_welcome.html
+H ./elements/select-part_pkg.html
+M ./elements/checkboxes.html
+L ./elements/select-cgp_rule_condition.html
+L ./elements/select-torrus_serviceid.html
+L ./elements/tr-select-discount_term.html
+L ./elements/tr-contact.html
+H ./elements/tr-td-label.html
+M ./elements/tr-input-beginning_ending.html
+M ./elements/tr-select-domain.html
+L ./elements/select-lnp_status.html
+H ./elements/tr-select-part_referral.html
+H ./elements/header.html
+H ./elements/searchbar-cust_bill.html
+L ./elements/customer-table.html
+M ./elements/select-svc_acct-domain.html
+L ./elements/select-did.html
+H ./elements/select-cust-part_pkg.html
+L ./elements/tr-cust_svc_cancel.html
+H ./elements/header-popup.html
+L ./elements/communigate_pro-accessmodes.html
+M ./elements/popup_link-cust_main.html
+L ./elements/select-phonenum.html
+M ./elements/progress-popup.html
+L ./elements/select-taxclass.html
+H ./elements/select-payby.html
+M ./elements/select-cust_main-status.html
+H ./elements/tr-select-reason.html
+H ./elements/select-month_year.html
+L ./elements/tr-select-rate.html
+H ./elements/menu.html
+L ./elements/select-agent_type.html
+L ./elements/tr-select-taxclass.html
+L ./elements/mcp_lint.html
+L ./elements/select-cust_pkg-balances.html
+L ./elements/about_freeside.html
+L ./elements/select-part_event.html
+L ./elements/select-discount_term.html
+L ./elements/tr-select-mac.html
+M ./elements/errorpage-popup.html
+L ./elements/select-exchange.html
+M ./elements/select-access_group.html
+M ./elements/tr-select-access_group.html
+L ./elements/tr-select-cust_location.html
+H ./elements/input-date-field.html
+M ./elements/tr-select-cust_main-status.html
+M ./elements/popup_link-cust_pkg.html
+L ./elements/select-agent_types.html
+L ./elements/pickcolor.html
+L ./elements/tr-fixed-country.html
+L ./elements/select-cust_pkg-status.html
+L ./elements/contact.html
+M ./elements/popup_link-cust_svc.html
+L ./elements/tr-did_order_item.html
+H ./elements/tr-select-svc_acct-domain.html
+L ./elements/form-file_upload.html
+M ./elements/tr-select-user.html
+M ./elements/tr-pkg_svc.html
+L ./elements/select-cdr_type.html
+M ./elements/tr-input-percentage.html
+M ./elements/tr-select-cust_pkg-status.html
+L ./elements/select-hardware_type.html
+M ./elements/searchbar-prospect.html
+H ./elements/searchbar-cust_main.html
+H ./elements/searchbar-ticket.html
+L ./elements/select-svc_pbx.html
+M ./elements/tr-select-pkg_class.html
+L ./elements/did_order_item.html
+M ./elements/search-cust_main.html
+L ./elements/phonenumber.html
+H ./elements/error.html
+M ./elements/popup_link_onclick.html
+L ./elements/header-minimal.html
+L ./elements/select-areacode.html
+L ./elements/tr-select-cust_pkg-balances.html
+L ./elements/popup_link-prospect_main.html
+H ./elements/select-part_referral.html
+L ./elements/select-rate.html
+H ./elements/tr-input-money.html
+L ./elements/tr-select-part_svc.html
+L ./elements/tr-select-cust_tag.html
+H ./elements/tr-select-from_to.html
+L ./elements/tr-select-cdrbatch.html
+L ./elements/tr-select-lnp_status.html
+L ./elements/select-cust-fields.html
+L ./elements/tr-select-invoice_template.html
+L ./elements/tr-select-cust-fields.html
+M ./elements/select-state.html
+M ./elements/tr-select-svc-domain.html
+L ./elements/tr-select-torrus_serviceid.html
+L ./elements/select-cust_tag.html
+M ./elements/pager.html
+L ./elements/standardize_locations.js
+H ./elements/select-agent.html
+L ./elements/file-upload.html
+L ./elements/tr-pickcolor.html
+L ./elements/location.html
+L ./elements/tr-select-cust_class.html
+L ./elements/tr-select-taxoverride.html
+H ./elements/dashboard-toplist.html
+H ./elements/select-terms.html
+M ./elements/select-cust-pkg_class.html
+M ./elements/tr-selectmultiple-part_pkg.html
+H ./elements/errorpage.html
+M ./elements/select-country.html
+L ./elements/select-taxoverride.html
+L ./elements/select-cgp_rule_action.html
+H ./elements/tr-search-cust_main.html
+L ./elements/select-hardware_class.html
+L ./elements/tr-input-lessthan_greaterthan.html
+
+H ./index.html
+
+48
+M ./browse/part_export.cgi
+L ./browse/part_pkg_taxclass.html
+L ./browse/rate.cgi
+L ./browse/pkg_class.html
+L ./browse/rate_time.html
+L ./browse/reason_type.html
+L ./browse/part_virtual_field.cgi
+M ./browse/cust_attachment.html
+L ./browse/hardware_status.html
+M ./browse/part_referral.html
+M ./browse/cust_main_county.cgi
+L ./browse/nas.cgi
+L ./browse/did_vendor.html
+L ./browse/part_pkg_taxproduct.cgi
+L ./browse/agent_type.cgi
+M ./browse/msg_template.html
+M ./browse/reason.html
+L ./browse/invoice_template.html
+L ./browse/hardware_class.html
+L ./browse/router.cgi
+L ./browse/cust_note_class.html
+L ./browse/part_device.html
+L ./browse/svc_acct_pop.cgi
+L ./browse/usage_class.html
+L ./browse/part_tag.html
+L ./browse/cust_class.html
+L ./browse/cust_category.html
+L ./browse/torrus_srvderive.html
+L ./browse/rate_region.html
+L ./browse/elements/browse.html
+L ./browse/tax_class.html
+L ./browse/part_pkg_report_option.html
+H ./browse/msgcat.cgi
+L ./browse/part_event.html
+L ./browse/addr_block.cgi
+L ./browse/payment_gateway.html
+M ./browse/part_svc.cgi
+M ./browse/part_pkg.cgi
+L ./browse/agent.cgi
+M ./browse/access_group.html
+L ./browse/tax_rate.cgi
+L ./browse/cgp_rule.html
+L ./browse/inventory_class.html
+L ./browse/pkg_category.html
+L ./browse/acct_snarf.html
+L ./browse/discount.html
+L ./browse/did_order.html
+M ./browse/access_user.html
+
+78
+L ./misc/bulk_change_pkg.cgi
+L ./misc/cdr-post.cgi
+L ./misc/timeworked.html
+L ./misc/inventory_item-import.html
+L ./misc/queued_report.html
+L ./misc/ping.html
+L ./misc/delete-rate_detail.html
+L ./misc/copy-rate_detail.html
+L ./misc/tax-import.cgi
+L ./misc/cust_pay-import.cgi
+L ./misc/delete-mailinglistmember.html
+L ./misc/recharge_svc.html
+H ./misc/payment.cgi
+H ./misc/cancel_cust.html
+L ./misc/tax-fetch_and_replace.cgi
+L ./misc/qual.html
+L ./misc/enable_or_disable_tax.html
+L ./misc/cust_main-import.cgi
+H ./misc/order_pkg.html
+M ./misc/batch-cust_pay.html
+L ./misc/catchall.cgi
+L ./misc/link.cgi
+L ./misc/did_order_confirmed.html
+L ./misc/cust_pkg-import.html
+L ./misc/did_order_provision.html
+L ./misc/email-customers.html
+L ./misc/process/bulk_change_pkg.cgi
+L ./misc/process/timeworked.html
+L ./misc/process/copy-rate_detail.html
+L ./misc/process/recharge_svc.html
+L ./misc/process/payment.cgi
+L ./misc/process/pay_batch-approve.cgi
+L ./misc/process/tax-fetch_and_replace.cgi
+L ./misc/process/enable_or_disable_tax.html
+M ./misc/process/batch-cust_pay.cgi
+L ./misc/process/nms-add_iface.html
+L ./misc/process/bulk_pkg_increment_bill.cgi
+L ./misc/process/tax-upgrade.cgi
+M ./misc/process/delay_susp_pkg.html
+L ./misc/process/nms-add_router.html
+L ./misc/process/cust_main_note-import.cgi
+L ./misc/process/cdr-import.html
+H ./misc/process/cancel_pkg.html
+L ./misc/process/cust_main-import_charges.cgi
+L ./misc/process/rate_edit_excel.html
+L ./misc/process/rate-import.html
+L ./misc/process/meta-import.cgi
+L ./misc/process/delete-customer.cgi
+L ./misc/phone_avail-import.html
+L ./misc/part_device-import.html
+L ./misc/nms-add_iface.html
+L ./misc/bulk_pkg_increment_bill.cgi
+L ./misc/choose_tax_location.html
+L ./misc/tax-fetch_and_import.cgi
+L ./misc/delay_susp_pkg.html
+L ./misc/merge_cust.html
+L ./misc/did_order_confirm.html
+L ./misc/nms-add_router.html
+H ./misc/cust_main-cancel.cgi
+L ./misc/whois.cgi
+L ./misc/cust_main_note-import.cgi
+L ./misc/cdr-import.html
+L ./misc/phone_device_config.html
+H ./misc/cancel_pkg.html
+L ./misc/cust_main-import_charges.cgi
+L ./misc/rate_edit_excel.html
+L ./misc/file-upload.html
+L ./misc/cust_main-merge.html
+L ./misc/clone-cgp_rule.html
+L ./misc/cust_main_note-import.html
+L ./misc/disable-cust_location.cgi
+H ./misc/change_pkg.cgi
+L ./misc/rate-import.html
+L ./misc/meta-import.cgi
+L ./misc/delete-cust_pkg_discount.html
+L ./misc/delete-customer.cgi
+L ./misc/delete-domain_record.cgi
+L ./misc/cdr.cgi
+
+242
+L ./edit/part_export.cgi
+L ./edit/rate_detail.html
+L ./edit/part_pkg_taxclass.html
+L ./edit/prospect_main-upload.html
+M ./edit/cust_pkg_detail.html
+L ./edit/rate.cgi
+H ./edit/cust_pay.cgi
+H ./edit/quick-charge.html
+M ./edit/cust_credit_refund.cgi
+M ./edit/cust_credit_bill.cgi
+L ./edit/phone_device.html
+H ./edit/svc_acct.cgi
+L ./edit/svc_phone.cgi
+L ./edit/svc_domain.cgi
+M ./edit/pkg_class.html
+L ./edit/svc_domain/communigate-basics.html
+L ./edit/svc_domain/communigate-acct_defaults.html
+L ./edit/svc_dsl.cgi
+L ./edit/reason_type.html
+L ./edit/svc_forward.cgi
+M ./edit/REAL_cust_pkg.cgi
+L ./edit/svc_hardware.cgi
+L ./edit/part_virtual_field.cgi
+L ./edit/hardware_status.html
+M ./edit/part_referral.html
+L ./edit/did_vendor.html
+H ./edit/cust_bill_pay.cgi
+H ./edit/cust_main/first_pkg/svc_acct.html
+H ./edit/cust_main/first_pkg/select-part_pkg.html
+L ./edit/cust_main/first_pkg/svc_dsl.html
+L ./edit/cust_main/first_pkg/svc_phone.html
+H ./edit/cust_main/billing.html
+H ./edit/cust_main/contact.html
+L ./edit/cust_main/choose_tax_location.html
+H ./edit/cust_main/top_misc.html
+H ./edit/cust_main/first_pkg.html
+L ./edit/cust_main/birthdate.html
+L ./edit/bulk-cust_svc.html
+L ./edit/agent_type.cgi
+M ./edit/msg_template.html
+M ./edit/reason.html
+L ./edit/agent_payment_gateway.html
+L ./edit/cust_main_county-expand.cgi
+L ./edit/svc_acct/acct_snarf.html
+L ./edit/svc_acct/communigate.html
+L ./edit/bulk-cust_pkg.html
+L ./edit/rate_region.cgi
+L ./edit/cust_pkg_discount.html
+H ./edit/cust_main_note.cgi
+L ./edit/invoice_template.html
+L ./edit/hardware_class.html
+L ./edit/domain_record.html
+L ./edit/reg_code.cgi
+L ./edit/cust_main_county-add.cgi
+L ./edit/router.cgi
+L ./edit/cust_note_class.html
+L ./edit/svc_broadband.cgi
+L ./edit/part_device.html
+L ./edit/allocate.html
+L ./edit/svc_www.cgi
+L ./edit/part_pkg_taxoverride.html
+L ./edit/svc_dish.cgi
+L ./edit/svc_acct_pop.cgi
+L ./edit/cgp_rule-vacation.html
+L ./edit/usage_class.html
+L ./edit/part_tag.html
+L ./edit/process/rate_detail.html
+H ./edit/process/quick-cust_pkg.cgi
+H ./edit/process/change-cust_pkg.html
+M ./edit/process/cust_pkg_detail.html
+H ./edit/process/cust_pay.cgi
+M ./edit/process/cust_credit_refund.cgi
+M ./edit/process/cust_credit_bill.cgi
+L ./edit/process/svc_acct.cgi
+M ./edit/process/cust_bill_pay.cgi
+L ./edit/process/addr_block/allocate.cgi
+L ./edit/process/domain_record.cgi
+L ./edit/process/cust_main_county-expand.cgi
+L ./edit/process/cust_pkg_discount.html
+M ./edit/process/cust_main_note.cgi
+L ./edit/process/reg_code.cgi
+L ./edit/process/cust_main_county-add.cgi
+H ./edit/process/cust_main.cgi
+M ./edit/process/cust_refund.cgi
+M ./edit/process/elements/ApplicationCommon.html
+L ./edit/process/cgp_rule-simplified.html
+L ./edit/process/tax_rate.html
+L ./edit/process/cust_location.cgi
+L ./edit/process/cust_main_county.html
+H ./edit/process/quick-charge.cgi
+L ./edit/process/qual.cgi
+L ./edit/process/part_event.html
+M ./edit/process/cust_pay_pending.html
+L ./edit/process/part_pkg.cgi
+M ./edit/process/cust_main_attach.cgi
+L ./edit/process/bulk-cust_main_county.html
+L ./edit/process/cust_tax_adjustment.html
+H ./edit/process/cust_credit.cgi
+L ./edit/process/prepay_credit.cgi
+L ./edit/process/svc_cert.cgi
+M ./edit/process/access_user.html
+L ./edit/process/mailinglistmember.html
+M ./edit/process/cust_pay_refund.cgi
+L ./edit/rate_time.cgi
+L ./edit/cgp_rule-redirect_all.html
+L ./edit/svc_external.cgi
+L ./edit/cust_class.html
+L ./edit/cust_category.html
+L ./edit/torrus_srvderive.html
+H ./edit/cust_main.cgi
+M ./edit/cust_refund.cgi
+L ./edit/elements/rate_detail.html
+M ./edit/elements/edit.html
+L ./edit/elements/class_Common.html
+M ./edit/elements/ApplicationCommon.html
+L ./edit/elements/category_Common.html
+L ./edit/elements/svc_Common.html
+L ./edit/tax_rate.html
+L ./edit/tax_class.html
+L ./edit/cust_location.cgi
+L ./edit/svc_cert/import_certificate.html
+L ./edit/svc_cert/import_cacert.html
+L ./edit/svc_cert/import_privatekey.html
+L ./edit/svc_cert/generate_privatekey.html
+M ./edit/cust_main_county.html
+L ./edit/cdr_type.cgi
+L ./edit/part_pkg_report_option.html
+M ./edit/cust_pkg.cgi
+L ./edit/svc_mailinglist.cgi
+H ./edit/msgcat.cgi
+L ./edit/part_event.html
+M ./edit/cust_pay_pending.html
+L ./edit/payment_gateway.html
+M ./edit/part_svc.cgi
+M ./edit/part_pkg.cgi
+L ./edit/agent.cgi
+M ./edit/access_group.html
+L ./edit/cgp_rule.html
+L ./edit/hardware_type.html
+L ./edit/svc_port.cgi
+M ./edit/cust_main_attach.cgi
+L ./edit/inventory_class.html
+L ./edit/pkg_category.html
+L ./edit/acct_snarf.html
+L ./edit/discount.html
+L ./edit/did_order.html
+L ./edit/bulk-cust_main_county.html
+L ./edit/prospect_main-ocr.html
+L ./edit/cust_tax_adjustment.html
+H ./edit/cust_credit.cgi
+L ./edit/prepay_credit.cgi
+L ./edit/invoice_logo.html
+L ./edit/prospect_main.html
+L ./edit/svc_cert.cgi
+M ./edit/access_user.html
+M ./edit/svc_Common.html
+L ./edit/mailinglistmember.html
+M ./edit/cust_pay_refund.cgi
+
+H ./loginout/logout.html
index d52b791..c676d3d 100644 (file)
@@ -1,12 +1,21 @@
 % $m->call_next;
 <%init>
 % $m->call_next;
 <%init>
+
   dbh->{'private_profile'} = {} if UNIVERSAL::can(dbh, 'sprintProfile');
   dbh->{'private_profile'} = {} if UNIVERSAL::can(dbh, 'sprintProfile');
+
+  my $locale =  $FS::CurrentUser::CurrentUser->option('locale')
+             || FS::Conf->new->config('locale')
+             || 'en_US';
+  $locale =~ s/_/-/g;
+  $lh = FS::L10N->get_handle($locale) || die "Unknown locale $locale";
+
 </%init>
 <%filter>
 
 </%init>
 <%filter>
 
-my $profile = '';
 if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
 
 if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
 
+  my $profile = '';
+
   if ( lc($r->content_type) =~ /^text\/html/
        && $FS::CurrentUser::CurrentUser->option('show_db_profile')
      )
   if ( lc($r->content_type) =~ /^text\/html/
        && $FS::CurrentUser::CurrentUser->option('show_db_profile')
      )
@@ -35,9 +44,10 @@ if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
   }
 
   dbh->{'private_profile'} = {};
   }
 
   dbh->{'private_profile'} = {};
+
+  s/(<\/BODY>[\s\n]*<\/HTML>[\s\n]*)$/$profile$1/i;
 }
 
 }
 
-s/(<\/BODY>[\s\n]*<\/HTML>[\s\n]*)$/$profile$1/i;
 </%filter>
 <%cleanup>
    dbh->commit();
 </%filter>
 <%cleanup>
    dbh->commit();
index f5698d9..d984347 100644 (file)
@@ -2,7 +2,7 @@
 
 Example:
 
 
 Example:
 
-  include( 'elements/edit.html',  
+  <& elements/edit.html,  
     'name_singular' =>  #singular name for the record
                         # (preferred, will be pluralized automatically)
     'name'          =>  #name for the record
     'name_singular' =>  #singular name for the record
                         # (preferred, will be pluralized automatically)
     'name'          =>  #name for the record
@@ -182,7 +182,7 @@ Example:
     #run before display to manipulate element of the 'fields' arrayref
     'field_callback' => sub { my( $cgi, $object, $field_hashref ) = @_; },
 
     #run before display to manipulate element of the 'fields' arrayref
     'field_callback' => sub { my( $cgi, $object, $field_hashref ) = @_; },
 
-  );
+  &>
 
 </%doc>
 
 
 </%doc>
 
index 107b3f2..f7558e8 100644 (file)
@@ -2,7 +2,7 @@
 
 Example:
 
 
 Example:
 
include( 'elements/process.html',
<& elements/process.html,
 
    ###
    # required
 
    ###
    # required
@@ -78,7 +78,7 @@ Example:
    'agent_virt'       => 1,
    'agent_null_right' => 'Access Right Name',
 
    'agent_virt'       => 1,
    'agent_null_right' => 'Access Right Name',
 
- )
+ &>
 
 </%doc>
 %if ( $error ) {
 
 </%doc>
 %if ( $error ) {
index 28a78b4..a36457b 100644 (file)
@@ -9,7 +9,7 @@
 <HTML>
   <HEAD>
     <TITLE>
 <HTML>
   <HEAD>
     <TITLE>
-      <% $title %>
+      <% $title |h %>
     </TITLE>
     <META HTTP-Equiv="Cache-Control" Content="no-cache">
     <META HTTP-Equiv="Pragma" Content="no-cache">
     </TITLE>
     <META HTTP-Equiv="Cache-Control" Content="no-cache">
     <META HTTP-Equiv="Pragma" Content="no-cache">
index 2bd4a96..bd17d2f 100644 (file)
@@ -20,7 +20,7 @@ Example:
 <HTML>
   <HEAD>
     <TITLE>
 <HTML>
   <HEAD>
     <TITLE>
-      <% $title %>
+      <% $title |h %>
     </TITLE>
     <META HTTP-Equiv="Cache-Control" Content="no-cache">
     <META HTTP-Equiv="Pragma" Content="no-cache">
     </TITLE>
     <META HTTP-Equiv="Cache-Control" Content="no-cache">
     <META HTTP-Equiv="Pragma" Content="no-cache">
@@ -30,7 +30,7 @@ Example:
   <BODY BGCOLOR="#f8f8f8" <% $etc %>>
     <link href="<%$fsurl%>elements/freeside.css" type="text/css" rel="stylesheet">
     <FONT SIZE=6>
   <BODY BGCOLOR="#f8f8f8" <% $etc %>>
     <link href="<%$fsurl%>elements/freeside.css" type="text/css" rel="stylesheet">
     <FONT SIZE=6>
-      <CENTER><% $title %></CENTER>
+      <CENTER><% $title |h %></CENTER>
     </FONT>
 
 % unless ( $nobr ) {
     </FONT>
 
 % unless ( $nobr ) {
index 9471656..c186aa2 100644 (file)
@@ -541,7 +541,7 @@ $config_misc{'Advertising sources'} = [ $fsurl.'browse/part_referral.html', 'Whe
   || $curuser->access_right('Edit global advertising sources');
 if ( $curuser->access_right('Configuration') ) {
   $config_misc{'Virtual fields'} = [ $fsurl.'browse/part_virtual_field.cgi', 'Locally defined fields', ];
   || $curuser->access_right('Edit global advertising sources');
 if ( $curuser->access_right('Configuration') ) {
   $config_misc{'Virtual fields'} = [ $fsurl.'browse/part_virtual_field.cgi', 'Locally defined fields', ];
-  $config_misc{'Error catalog'} = [ $fsurl.'browse/msgcat.cgi', 'Change error messages and other customizable labels' ];
+  $config_misc{'Message catalog'} = [ $fsurl.'browse/msgcat.cgi', 'Change error messages and other customizable labels for each locale' ];
 }
 $config_misc{'Inventory classes and inventory'} = [ $fsurl.'browse/inventory_class.html', 'Setup inventory classes and stock inventory' ]
   if $curuser->access_right('Edit inventory')
 }
 $config_misc{'Inventory classes and inventory'} = [ $fsurl.'browse/inventory_class.html', 'Setup inventory classes and stock inventory' ]
   if $curuser->access_right('Edit inventory')
index 5b550db..c44880d 100644 (file)
@@ -1,6 +1,4 @@
-% my $conf = new FS::Conf; 
-
-<% include('/elements/header.html', 'Billing Main' ) %>
+<& /elements/header.html, mt('Billing Main') &>
 
 <% include('/elements/dashboard-install_welcome.html') %>
 
 
 <% include('/elements/dashboard-install_welcome.html') %>
 
@@ -53,4 +51,4 @@
 
 % } 
 
 
 % } 
 
-<% include('/elements/footer.html') %>
+<& /elements/footer.html &>
index 897be25..0ee48fd 100644 (file)
@@ -21,7 +21,7 @@ if ( grep { $cgi->param($_) !~ /^\s*$/ }
 
   $access_user = qsearchs( 'access_user', {
     'username'  => getotaker,
 
   $access_user = qsearchs( 'access_user', {
     'username'  => getotaker,
-    '_password' => $cgi->param('_password'),
+    '_password' => scalar($cgi->param('_password')),
   } );
 
   $error = 'Current password incorrect; password not changed'
   } );
 
   $error = 'Current password incorrect; password not changed'
@@ -48,7 +48,7 @@ unless ( $error ) { # if ($access_user) {
   my %param = $access_user->options;
 
   #XXX autogen
   my %param = $access_user->options;
 
   #XXX autogen
-  my @paramlist = qw( menu_position default_customer_view
+  my @paramlist = qw( locale menu_position default_customer_view
                       disable_html_editor
                       email_address
                       snom-ip snom-username snom-password
                       disable_html_editor
                       email_address
                       snom-ip snom-username snom-password
index c7083e9..da8b428 100644 (file)
@@ -31,6 +31,20 @@ Interface
 <% ntable("#cccccc",2) %>
 
   <TR>
 <% ntable("#cccccc",2) %>
 
   <TR>
+    <TH ALIGN="right">Locale: </TH>
+    <TD>
+      <SELECT NAME="locale">
+%       foreach my $locale ( FS::Locales->locales ) {
+%         my %info = FS::Locales->locale_info($locale);
+%         my $selected = ($locale eq $curuser->option('locale'))
+%                          ? 'SELECTED' : '';
+          <OPTION VALUE="<% $locale %>" <%$selected%>><% $info{name} %> (<% $info{country} %>)
+%       }
+      </SELECT>
+    </TD>
+  </TR>
+
+  <TR>
     <TH ALIGN="right">Menu location: </TH>
     <TD>
       <INPUT TYPE="radio" NAME="menu_position" VALUE="left" onClick="document.images['menu_example'].src='../images/menu-left-example.png';" <% $menu_position eq 'left' ? ' CHECKED' : ''%>> Left<BR>
     <TH ALIGN="right">Menu location: </TH>
     <TD>
       <INPUT TYPE="radio" NAME="menu_position" VALUE="left" onClick="document.images['menu_example'].src='../images/menu-left-example.png';" <% $menu_position eq 'left' ? ' CHECKED' : ''%>> Left<BR>
index a8e9f08..81ec4d0 100644 (file)
@@ -2,7 +2,7 @@
 
 Example:
 
 
 Example:
 
-  include( 'elements/search.html',
+  <& elements/search.html,
 
     ###
     # required
 
     ###
     # required
@@ -163,7 +163,7 @@ Example:
     # each hashref: http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm#Format_methods_and_Format_properties
     'xls_format' => => [],
     
     # each hashref: http://search.cpan.org/dist/Spreadsheet-WriteExcel/lib/Spreadsheet/WriteExcel.pm#Format_methods_and_Format_properties
     'xls_format' => => [],
     
-  )
+  &>
 
 </%doc>
 % if ( $type eq 'csv' ) {
 
 </%doc>
 % if ( $type eq 'csv' ) {