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

# catalog.cfg

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

# 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: THEME_CSS, 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 Safe::Hole     "Need %s %s for embedded perl object access."

Require UserTag   email var loc table_editor button component 

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

# 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
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

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

# Database setup

ParseVariables  Yes

ifndef SQLDSN
Variable  SQLDSN  __MVC_SQLDSN__
endif

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

ifdef MYSQL
DatabaseDefault NO_ASCII_INDEX 1
include dbconf/mysql/mysql.cfg
endif

ifdef PGSQL
DatabaseDefault NO_ASCII_INDEX 1
include dbconf/pgsql/pgsql.cfg
endif

ifdef SQLITE
DatabaseDefault NO_ASCII_INDEX 1
include dbconf/sqlite/sqlite.cfg
endif

# Default DBM if nothing else defined
ifndef SOME_DATABASE
Require We_need_a_database  "%s, must have either MySQL or Postgres"
endif

### Uncomment these for DBI sessions
# SessionType DBI
# SessionDB   sessions
# Database sessions sessions.txt __SQLDSN__
# NoImport sessions

### 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__/
ImageDirInternal  http://__SERVER_NAME____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.
##
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

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

# Various catalog settings.

ScratchDefault  mv_add_dot_html   1
ScratchDefault  mv_no_session_id  1
ScratchDefault  mv_no_count       1

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

## 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
# We usually don't for users, so we can mail them their password
# We DO in admin, that is set in catalog_after.cfg
UserDB    default    crypt         0

# 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"

# 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
# Uncomment to use
# UserDB    default    indirect_login  email
# UserDB    default    assign_username 1

# 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.
CookieLogin  Yes

# Limit the time your customers can CookieLogin
SaveExpire   30 days

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

# Cart, order, and route settings.

ParseVariables  Yes

MailOrderTo     __ORDERS_TO__

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

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

ifndef COMMON_ORDER_PROFILE
Variable COMMON_ORDER_PROFILE <<EOV
	mv_same_billing=always_pass
	fname=required
	lname=required
	address1=required
	address2=always_pass
	city=required
	country=required
	state=multistate
	zip=multizip
	&or phone_day=phone, phone_night=phone Must have day or evening phone number
	email=email_only
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
SalesTax        __TAXFIELD__
TaxShipping     __TAXSHIPPING__

OrderCounter    etc/order.number
OrderLineLimit  200
OrderProfile    etc/profiles.order etc/profiles.login etc/profiles.misc

## 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>';
	}
	$opt->{type} ||= 2;
	my $class = "button$opt->{type}";
	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

## Tags to help us determine whether the user has a DOM-compliant browser
## for flyout/flydown/tree things
UserTag dhtml HasEndTag
UserTag dhtml Interpolate
UserTag dhtml Routine <<EOR
sub {
	$Variable->{MV_DHTML_BROWSER} ||= 'MSIE.*[5-9]\..*Windows|Mozilla.*Gecko|Opera.*[7-9]\.';
	return '' unless $Session->{browser} =~ m{$Variable->{MV_DHTML_BROWSER}};
	return shift(@_);
}
EOR

UserTag no-dhtml HasEndTag
UserTag no-dhtml Interpolate
UserTag no-dhtml Routine <<EOR
sub {
	$Variable->{MV_DHTML_BROWSER} ||= 'MSIE.*[5-9]\..*Windows|Mozilla.*Gecko|Opera.*[7-9]\.';
	return '' if $Session->{browser} =~ m{$Variable->{MV_DHTML_BROWSER}};
	return shift(@_);
}
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

## Subroutine that looks for a prod_group and category in
## a missing page and delivers them
Sub ncheck_category <<EOS
sub {
    my ($name) = @_;
    return unless $name =~ m{^[A-Z]};
    $name =~ s,_, ,g;
    my ($prod_group, $category) = split m{/}, $name; 

    my $search;
    $search->{co} = 1;
    $search->{fi} = 'products';
    $search->{st} = 'db';
    $search->{sf} = join "\0", 'prod_group', 'category';
    $search->{op} = join "\0", 'eq', 'eq';
    $search->{se} = join "\0", $prod_group, $category;
    $search->{sp} = 'results';
    $search->{mv_todo} = 'search';
    $Tag->search({ search => $search });
    if (($o = $Search->{''}) && @{$o->{mv_results}}) {
        return (1,  $Config->{Special}->{results});
    }

    return;
}
EOS
