#==========================================================================#

# catalog.cfg

#==========================================================================#

## We want to hide the .gdbm, .autonumber, .numeric, .sql, .db files
## Needs to go before VariableDatabase
DatabaseDefault HIDE_AUTO_FILES 1

# Read in some initial default variables from a directory. These are
# larger ones -- they could conceivably be maintained in the
# VariableDatabase but might be easier to maintain in a file, especially
# for development.
#
# Notable settings: COMMON_MENU, COPYRIGHT
#
DirConfig Variable variables

# Read in large variables or dynamic variables from a series
# of files in a directory
VariableDatabase variable

# Read in site-specific variables from a database (if any).
# Used to create a information for a temporarily transferred
# catalog, i.e. for development.
#
# Ignored if file doesn't exist.
VariableDatabase site

# Warn if any important modules or usertags are missing.

Require module Digest::MD5    "Need %s %s for better cache keys."
Require module Digest::Bcrypt "Required if you are going to use bcrypt passwords for UserDB."
Require module Crypt::Random  "Required if you are going to use bcrypt passwords for UserDB."
Require module Safe::Hole     "Need %s %s for embedded perl object access."

# Encoding
Variable MV_UTF8 1
Variable MV_HTTP_CHARSET UTF-8

#==========================================================================#

# Can send various files outside the catalog directory, for disk partition
# reasons, for example.

ParseVariables  Yes

ifdef LOGDIR
ErrorFile       __LOGDIR__/error.log
AsciiTrack      __LOGDIR__/tracking.asc
#TrackFile       __LOGDIR__/usertrack
ErrorDestination  "Run jobs group=%s pid=%s"       __LOGDIR__/jobs_run.log
ErrorDestination  "Finished jobs group=%s pid=%s"  __LOGDIR__/jobs_run.log
endif

ifndef LOGDIR
AsciiTrack      logs/tracking.asc
#TrackFile       logs/usertrack
endif

ifdef CACHEDIR
SessionDatabase __CACHEDIR__/session
ScratchDir      __CACHEDIR__/tmp
endif

ifndef CACHEDIR
SessionDatabase session
ScratchDir      tmp
endif

ifdef RUNDIR
RunDir  __RUNDIR__
endif

#==========================================================================#

# Database setup

ParseVariables  Yes

ifndef INDEV
DatabaseDefault LOG_ERROR_SESSION 0
endif

## Quote all identifiers to avoid name clash issues and SQL injection attacks
DatabaseDefault QUOTE_IDENTIFIERS 1

ifdef MYSQL
DatabaseDefault NO_ASCII_INDEX 1
include dbconf/mysql/mysql.cfg
Variable SESSIONDB_ENGINE mysql
Variable MOREDB_EXT sessiondb
endif

ifdef PGSQL
DatabaseDefault NO_ASCII_INDEX 1
include dbconf/pgsql/pgsql.cfg
Variable SESSIONDB_ENGINE pgsql
Variable MOREDB_EXT sessiondb
endif

ifdef SQLITE
DatabaseDefault NO_ASCII_INDEX 1
include dbconf/sqlite/sqlite.cfg
Variable SESSIONDB_ENGINE sqlite
Variable MOREDB_EXT moredb
endif

ifndef SOME_DATABASE
Require We_need_a_database  "Must select MySQL, PostgreSQL, or SQLite database"
endif

### Uncomment these for DBI sessions
### Not available to SQLite due to absence of row locks
# include dbconf/__SESSIONDB_ENGINE__/sessions.__SESSIONDB_ENGINE__.sessiondb
# SessionType DBI
# SessionDB   sessions

### Uncomment these for Redis sessions
### Replace with your Redis server if not running on localhost
# Variable REDIS_SERVER 127.0.0.1:6379
# SessionType Redis
# SessionDB __REDIS_SERVER__

### Compression optional: one of Zstd, Gzip, or Brotli
# SessionDBCompression Zstd

### Uncomment for MoreDB. Used in conjunction with DBI or Redis sessions
### With Redis, can be used with SQLite
# MoreDB yes
# ifdef REDIS_SERVER
#    include dbconf/__SESSIONDB_ENGINE__/sessions.__SESSIONDB_ENGINE__.__MOREDB_EXT__
#    MoreDBTable sessions
# endif

### You can change the length of the session ID. Must be integer between
### 8 and 32.
# Limit  session_id_length  8

#==========================================================================#

# The URLs which are written to refer back to our catalog.

<ParseVariables Yes>

VendURL    http://__SERVER_NAME____CGI_URL__
SecureURL  __SECURE_SERVER____CGI_URL__

ifndef SECURE_ENABLE
SecureURL  http://__SERVER_NAME____CGI_URL__
endif

# Set the image path for relative images
ImageDir          __IMAGE_DIR__/

</ParseVariables>

#==========================================================================#

# Here we set up the catalog theme.

<ParseVariables Yes>

##
## Here we pick up the region variables like LEFTONLY_TOP and LEFTONLY_BOTTOM.
##
## Commenting; new variables TOP and BOTTOM will be defined in variable directory.
## No longer going to need multiple leftright_top, leftonly_top, etc; will be 
## defined in "display_class" in each page.

#DirConfig Variable templates/regions

# TRAFFIC is defined system-wide in interchange.cfg. When in low-traffic
# mode, variables are dynamic -- i.e. when you change the region file
# the page changes. Otherwise a reconfig must be done to change the
# variable.
#
# Careful, this can change the way *other* variables are interpreted.
ifdef @TRAFFIC =~ /low/i
Pragma dynamic_variables
Pragma dynamic_variables_file_only
endif

# Provide some links for the admin to edit pages if we have the UI in
ifdef @UI
Autoload admin_links
endif

#==========================================================================#

# Internationalization
LocaleDatabase  locale

# Default locale
ifdef DEFAULT_LOCALE
DefaultLocale  __DEFAULT_LOCALE__
endif

ifdef DEFAULT_LOCALE
# Database definitions for localized databases
include dbconf/locales/{__LOCALES__}.cfg
endif

Locale en_US  <<EOL
{
    "Username already exists (indirect).",
    "Sorry, that email is already associated with an account.",
}
EOL

#==========================================================================#

# Various catalog settings.

ScratchDefault  mv_add_dot_html   0
ScratchDefault  mv_no_session_id  1
ScratchDefault  mv_no_count       1
Pragma url_no_session_id

ifdef DEFAULT_COUNTRY
ValuesDefault country __DEFAULT_COUNTRY__
ValuesDefault b_country __DEFAULT_COUNTRY__
endif

DirectoryIndex  index.html
SpecialPage  catalog      index
SpecialPage  violation    ../special_pages/violation
SpecialPage  put_handler  admin_publish
SpecialPage  report       ../etc/report
SpecialPage  receipt      ../etc/receipt

# Allow others in our group to read/write files by default
ReadPermission  group
WritePermission group

# Don't send X-Track HTTP response header
UserTrack no

# If a specific user session accesses this catalog more than this many
# times with no pauses of 30 seconds or more, the LockoutCommand (if set)
# is executed. Set this to 0 if you're getting links to 127.0.0.1
# during your testing.
RobotLimit  100

# We also count the number of accesses from the same IP address. More than 
# RobotLimit accesses within a one hour period will cause a Forbidden result.
# Uncomment the following line to change the 1 hour period to 30 minutes.
# Limit  ip_session_expire  30

# The default lockout time is one day.  Uncomment the following line to change
# it to just under an hour.
# Limit  robot_expire  0.04

# Strip whitespace from top of pages so you don't have to scroll down
# before the HTML starts when doing 'view source' in a browser.
Pragma  strip_white

# Don't allow <!--[itl tag]-->
Pragma  no_html_comment_embed

# do not allow orders without a profile
Pragma require_order_profile

## DATA INSERTION PRAGMAS for form database inserts or updates

## Restricts inserts to insert-only, but allows the fall-through
## behavior from update to insert. No existing data will be clobbered.
# Pragma dml=preserve

## Pragma 'dml=strict' forces update or insert to only perform the
## requested action.
# Pragma dml=strict

## Pragma 'dml=upsert' (the default) preserves Interchange's original
## method of doing an update if the record exists, otherwise doing an
## insert. This can clobber an existing key.
# Pragma dml=upsert

## END DATA INSERTION PRAGMAS

#==========================================================================#

# User session related settings.

# Whether to encrypt passwords in UserDB
UserDB    default    crypt         1
UserDB    default    bcrypt        1

# These 2 lines are needed for query/pw_reset
UserDB    default    promote       1
UserDB    default    from_plain    1

# The pepper should be unique for your site, but note that if you change this,
# it will make previously-crypted passwords inaccessible --
# so set this before you start adding users, or don't set at all.
UserDB    default    bcrypt_pepper  __BCRYPT_PEPPER__

# Set to 1 to make the username and password case-insensitive
UserDB    default    ignore_case   0

# Change a field to something that doesn't conflict in MySQL
UserDB    default    time_field    mod_time

# Don't want people setting their credit limit or dealer status directly
UserDB    default    scratch       "dealer price_level credit_limit usernick"
UserDB    default    expire_field  expiration

# Update the database with the date when creating a user
UserDB    default    created_date_iso   created

# Update the database with the date when updating a user via set_values
UserDB    default    updated_date_iso   updated

# Set some other things you want to retain but which don't deserve their
# own field -- stored in serial field preferences
UserDB    default    extra_fields  <<EOU
"
	mv_same_billing
	mv_credit_card_exp_type
	mv_credit_card_exp_month
	mv_credit_card_exp_year
	fax_order
	phone_cell
	account_id
	po_number
"
EOU

# Log user login/logout etc in separate logfile
UserDB    default    logfile       logs/userdb.log

# This makes the login username the user's email address
UserDB    default    indirect_login  usernick
UserDB    default    assign_username 1

# for indirect login to work with admin
UserDB    autocreate   crypt        0
UserDB    autocreate   time_field   mod_time
UserDB    autocreate   scratch      "dealer price_level credit_limit"

# This makes the password be inserted in an insert-only table.
#
# UserDB      default     enclair_db  enclair
#
# You can set the following, which have the defaults shown in the
# setting. You can also insert %M, which is the MD5 of the password, or
# %D which is a datetime localtime value in the form YYYYmmddHHMMSS.
#UserDB   default  enclair_key_field   username
#UserDB   default  enclair_field       password
#UserDB   default  enclair_query_template "INSERT INTO %t (%U,%P) values (%u,%p)"

# minimal login stuff for affiliate
UserDB    affiliate  user_field    affiliate
UserDB    affiliate  database      affiliate
UserDB    affiliate  time_field    none
UserDB    affiliate  crypt         0

# Set this to Yes if you want auto-login capability for users.
# You must be careful about malicious JavaScripts in your embedded code.
## this is not secure at all, since it saves user's password.
## can use alternate cookie to just save username.
CookieLogin  No

# Limit the time your customers can retain a session
SessionExpire   4 hours

# Limit the time your customers can CookieLogin
SaveExpire   30 days

#==========================================================================#

# Cart, order, and route settings.

ParseVariables  Yes

MailOrderTo     __ORDERS_TO__

AlwaysSecure   <<EOD
	login
	member/account
	member/change_email
	member/change_password
	new_account
	ord/billing
	ord/checkout
	ord/finalize
	ord/shipping
	query/order_detail
EOD

AlwaysSecureGlob   <<EOD
	admin*,
	cert*,
	ui*,
EOD

## Set this if you have a different secure server
#AlwaysSecure   order ord/basket process

ifndef COMMON_ORDER_PROFILE
Variable COMMON_ORDER_PROFILE <<EOV
	[calc]
		if ($CGI->{state_cs_in} && !$CGI->{state}){
			$CGI->{state} = $CGI->{state_cs_in};
		}
		return;
	[/calc]
	mv_same_billing=always_pass
	fname=required
	lname=required
	address1=required
	address2=always_pass
	city=required
	country=required
	state=multistate
	zip=multizip
	phone_day=phone
	[if !session logged_in]email=email_only[/if]
EOV
endif

## Payment-related stuff

EncryptKey  __PGP_KEY__

## These routes are not order routes, but payment routes
Route  authorizenet  id           "__MV_PAYMENT_ID__"
Route  authorizenet  secret       "__MV_PAYMENT_SECRET__"
Route  authorizenet  host         "__MV_PAYMENT_HOST__"
Route  authorizenet  referer      "__MV_PAYMENT_REFERER__"

Route  itransact     id           "__MV_PAYMENT_ID__"

Route  netbilling    id           "__MV_PAYMENT_ID__"
Route  netbilling    secret       "__MV_PAYMENT_SECRET__"

Route  signio        id           "__MV_PAYMENT_ID__"
Route  signio        secret       "__MV_PAYMENT_SECRET__"
Route  signio        partner      "__MV_PAYMENT_PARTNER__"
Route  signio        vendor       "__MV_PAYMENT_VENDOR__"
Route  signio        host         "__MV_PAYMENT_HOST__"

Route  skipjack      id           "__MV_PAYMENT_ID__"
Route  skipjack      partner      "__MV_PAYMENT_PARTNER__"

Route  linkpoint     id           "__MV_PAYMENT_ID__"
Route  linkpoint     host         "__MV_PAYMENT_HOST__"
Route  linkpoint     keyfile      "__MV_PAYMENT_SECRET__"

# This prevents a user from setting this value, you may want to unset
# this if you have user-selectable handling charges like insurance
FormIgnore   mv_handling
#
#

# Along these lines further, for better integrity and less chance of a 
# user screwing up your order routes:
# Note that __ORDER_ROUTES__ is empty by default, default Route "default"
# is used with cascades
FormIgnore      mv_order_route 

## This route places the order entry in the database. If you don't
## have an inventory table (or a userdb table for that matter) make
## sure you remove it from the list of "transactions" tables.

Route log  <<EOF
	empty        1
	encrypt      0
	increment    0
	report       etc/log_transaction
	supplant     0
	track        logs/log
EOF

ifdef TRANSACTION_TABLES
Route log       transactions '__TRANSACTION_TABLES__'
endif

## This route copies the user if they requested that. We don't
## care (much) if it fails, so error_ok is set and failure will
## not cause the order to fail
Route copy_user  <<EOF
	empty        1
	error_ok     1
	encrypt      0
	increment    0
	report       etc/mail_receipt
	supplant     0
	track        logs/log
EOF

ParseVariables Yes
## This route emails the order to you unless email is set to "",
## and failsafe-logs the order report a couple of places
Route main  <<EOF
	attach            0
	credit_card       1
	default           1
	email             '__ORDERS_TO__'
	encrypt           0
	errors_to         '__ORDERS_TO__'
	pgp_cc_key        "__PGP_KEY__"
	pgp_key           "__PGP_KEY__"
	receipt           etc/receipt.html
	report            etc/report
	supplant          1
	individual_track  orders
	track             logs/tracking.asc
EOF

## This route emails the order to you unless email is set to "",
## and failsafe-logs the order report a couple of places
Route main_entry  <<EOF
	attach            0
	credit_card       1
	email             '__ORDERS_TO__'
	encrypt           0
	errors_to         '__ORDERS_TO__'
	pgp_cc_key        "__PGP_KEY__"
	pgp_key           "__PGP_KEY__"
	report            etc/report
	supplant          1
	individual_track  orders
	track             logs/tracking.asc
EOF


# Order desk route run if entered from admin
# always
Route   entry   master          1
Route   entry   cascade         "log main_entry copy_user"
Route   entry   empty           1
Route   entry   supplant        1
Route   entry   no_receipt      1
Route   entry   counter_tid     logs/tid.counter
Route   entry   write_tables    "inventory userdb transactions orderline"
Route   entry   transactions    '__TRANSACTION_TABLES__'
Route   entry   email           '__ORDERS_TO__'

# Default route is run if no routes set, this should be last Route
# always
Route   default   master          1
Route   default   cascade         "log main copy_user"
Route   default   empty           1
Route   default   supplant        1
Route   default   counter_tid     logs/tid.counter
Route   default   write_tables    "inventory userdb transactions orderline"
Route   default   transactions    '__TRANSACTION_TABLES__'
Route   default   email           '__ORDERS_TO__'

## Uncomment this if you want Routes read dynamically from DB
#Route   default   dynamic_routes  1
## Uncomment this if you want ITL allowed in routes
#Route   default   expandable      1


## Sest
# For 3rd-party tax API, require module for TAXSERVICE in interchange.cfg
# - E.g. `Require module Vend::Tax::TaxJar`
# Uncomment below to switch to using TAXSERVICE. Set TAXTOKEN in variable.txt
#Variable        TAXFIELD [tax-lookup service=__TAXSERVICE__]
SalesTax        __TAXFIELD__
TaxShipping     __TAXSHIPPING__
AutoModifier    nontaxable
ScratchDefault  tag_tax_lookup_estimate_mode 1


OrderCounter    etc/order.number
OrderLineLimit  200
Profiles        include/profiles/*.*

## Sets the default as to whether items are aggregated or ordered
## on separate lines. Can be overridden with mv_separate_items=0 in
## URL or form.
SeparateItems   Yes

#==========================================================================#

ifdef UI_TRAFFIC_STATS
TrackFile __UI_TRAFFIC_STATS__
endif

# Deal with customer click history. For example, after adding an item to
# the cart, the user can return to a specific search results page.
History 10

# Allow delivery of soft goods (downloadable files).
ActionMap  deliver   <<EOR
sub {
	$Scratch->{deliverable} = $CGI->{mv_arg};
	$CGI->{mv_nextpage} = 'deliver';
	if(! $Session->{username} and $CGI->{mv_username}) {
		$Tag->userdb('login');
	}
	return 1;
}
EOR

# Pricing setup
#
# If the user is logged in and is marked as a "dealer" (1 in the dealer
# field in the userdb database) then they are given quantity discounts
# based on price groups. (All products are in price group 1 as
# distributed.) If the quantity is 1, then pricing comes from the
# "wholesale" field in the products database.

AutoModifier pricing:price_group

# 
# If the user is not a dealer (or not logged in) then pricing just comes
# from "price". Any quantity discounts will be set in the qN fields in the
# database, and are separate from dealer quantity discounts.
# 

Profile dealer <<EOR
{ 
	CommonAdjust => <<EOF,

		pricing:w5,w10:,
		;:wholesale,
		;:wholesale:mv_sku,
		;$,
		==:options
EOF
	NonTaxableField => 'nontaxable',
}
EOR

Profile distributor <<EOR
{ 
	CommonAdjust => <<EOF,

		pricing:w5,w10:,
		;:wholesale,
		;:wholesale:mv_sku,
		;$,
		==:options,
		-10%
EOF
	NonTaxableField => 'nontaxable',
}
EOR

## This should match the default direct setting below this paragraph
Profile default CommonAdjust   "pricing:q5,q10 ;:sale_price, ;:price, ;$, :related, ==:options"
Profile default NonTaxableField 
Profile default PriceField 0

CommonAdjust   pricing:q5,q10 ;:sale_price, ;:price, ;$, :related, ==:options
PriceField     0

## This sets the type of options pertaining to the product
OptionsEnable option_type

## Finally, set which tables products can be ordered from
ProductFiles   products variants

#==========================================================================#

## Tag which provides a CSS button-looking link
UserTag b-link Order href form
UserTag b-link HasEndTag
UserTag b-link addAttr
UserTag b-link Routine <<EOR
sub {
	my ($page, $form, $opt, $anchor) = @_;
	# use vars qw/$Tag $Scratch/;
	if($Scratch->{no_javascript}) {
		$opt->{extra} ||= 'style="font-size: smaller"';
		return $Tag->page($opt) . $anchor . '</a>';
	}
	my $class = $opt->{class} || 'btn';
	my $url = $Tag->area($opt);
	my $string = $Tag->filter('encode_entities', $anchor);
	my @properties;
	push @properties, "type=button";
	push @properties, qq{value="$string"};
	push @properties, qq{class="$class"};
	for(qw/style id name/) {
		next unless $opt->{$_};
		push @properties, qq{$_="$opt->{$_}"};
	}
	push @properties, qq{onClick="window.location='$url'"};
	push @properties, qq{onMouseOver="saveblink=window.status;window.status='$url'"};
	push @properties, qq{onMouseOut="window.status=saveblink"};
	if($opt->{title}) {
		$opt->{title} = $Tag->filter('encode_entities', $opt->{title});
	}
	else {
		$opt->{title} = $string;
	}
	push @properties, qq{title="$opt->{title}"};
	push @properties, $opt->{extra} if $opt->{extra};

	return qq{<input } . join(" ", @properties) . '>';
}
EOR

### Shipping setup. See Interchange docs -- you will want to change
### the origin postal code and default destination postal codes, for sure.
Shipping   Postal     default_geo   45056
Shipping   QueryUPS   default_geo   45056
Shipping   default    dir           products/ship

Database   2ndDayAir        ship/2ndDayAir.csv         CSV
Database   2ndDayAirAM      ship/2ndDayAirAM.csv       CSV
Database   3DaySelect       ship/3DaySelect.csv        CSV
Database   Expedited        ship/Expedited.csv         CSV
Database   Express          ship/Express.csv           CSV
Database   Ground           ship/Ground.csv            CSV
Database   NextDayAir       ship/NextDayAir.csv        CSV
Database   NextDayAirSaver  ship/NextDayAirSaver.csv   CSV
Database   Xarea            ship/Xarea.csv             CSV
Database   Zone             ship/Zone.csv              CSV
Database   usps             ship/usps.txt              TAB
Database   Book             ship/Book.txt              TAB
Database   Priority         ship/Priority.txt          TAB
Database   air_pp           ship/air_pp.txt            TAB
Database   surf_pp          ship/surf_pp.txt           TAB
Database   ups_cache        ship/ups_cache.txt         __SQLDSN__
Database   ups_cache        AUTO_SEQUENCE  ups_cache_seq
Database   ups_cache        DEFAULT_TYPE varchar(12)
Database   ups_cache        INDEX  weight origin zip shipmode country

## Uncomment
MaxQuantityField  inventory:quantity

## Builds a series of hidden variables given an IC-style form
## specification, i.e.
##
## [hiddens]
##    mv_action=refresh
##    mv_nextpage=foo
## [/hiddens]
##
UserTag hiddens Interpolate 1
UserTag hiddens HasEndTag
UserTag hiddens Routine <<EOR
sub {
	my $block = shift;
	my @lines = grep /\S/, split /\n/, $block;

	my @out;
	for(@lines) {
		s/\s+$//;
		s/^\s+//;
		my ($k,$v) = split /=/, $_, 2;
		$v = $Tag->filter('encode_entities', $v);
		push @out, qq{<input type=hidden name="$k" value="$v">};
	}
	return join "\n", @out;
}
EOR

## When uncommented, delivers a redirect if we get a request for an image
## DeliverImage Yes

## Map a subroutine to happen if the page is not there
SpecialSub  missing  ncheck_category

UserTag edisplay Alias error auto=1 class="alert alert-danger list-unstyled"
UserTag wdisplay Alias warnings auto=1 list_class="alert alert-success list-unstyled"
UserTag ecgi     Alias cgi keep=1 filter=encode_entities name=

CodeDef string2uri Filter
CodeDef string2uri Description Sanitize a string for use in a URL
CodeDef string2uri Routine <<EOR
sub {
	my $val = shift;
	$val =~ s|/|::|g;
	$val =~ s|-|_|g;
	$val =~ s|\s+|-|g;
	return $val;
}
EOR

CodeDef uri2string Filter
CodeDef uri2string Description Revert sanitized URL back to a string
CodeDef uri2string Routine <<EOR
sub {
	my $val = shift;
	$val =~ s|-| |g;
	$val =~ s|_|-|g;
	$val =~ s|::|/|g;
	return $val;
}
EOR

SearchProfile  include/profiles/searchprofiles

Jobs  log    __LOGDIR__/jobs.log
Jobs  base_directory  etc/jobs
