VbzCart/code/files/ckout.php

Code
<?php /* FILE: ckout.php HISTORY: 2010-02-21 split off from shop.php (via checkout/index.php) 2010-12-24 Fixed call to Update -- array is now required 2013-02-20 mostly working, but "ship to card" and "ship to self" logic is becoming unmanageable. Going to gut this and significantly rework it as a single form. 2013-04-12 ended up with two forms still, but somewhat simplified logic

clsLibMgr::Add('vbz.page.cart',	KFP_LIB_VBZ.'/page-cart.php',__FILE__,__LINE__); clsLibMgr::AddClass('clsPageCart', 'vbz.page.cart');

/*===== CLASS: clsPageCkOut PURPOSE: subclass for generating checkout pages class clsPageCkout extends clsPageCart { protected $doesCartMatchShip;

protected static $intColumns; /*   protected $objCust;	// person who placed the order protected $objShip;	// person who is receiving the order (may be the same)

/*     NOTE: needs to be public so clsPerson can access it    */ public function CartObj { return $this->objCart;	// document where this is set! }   /*      OUTPUT: $inCkout: if TRUE, it's ok to use a cart whose order has been placed. INPUT: $pgData: The page whose data we're receiving (blank if there is no data) $pgShow: The page whose form we're wanting to display */   protected function ParseInput {

$gotPgDest = FALSE; $gotPgSrce = FALSE; if (empty($_GET[KSQ_ARG_PAGE_DEST])) { if (empty($_POST[KSQ_ARG_PAGE_DATA])) { $this->pgData = KSQ_PAGE_CART;	// only the cart doesn't specify itself } else { $this->pgData = $_POST[KSQ_ARG_PAGE_DATA]; $gotPgSrce = TRUE; }	} else { $gotPgDest = TRUE; $this->pgData = NULL;	// Currently not expecting any data from a "GET" query $this->pgShow = $_GET[KSQ_ARG_PAGE_DEST]; }	if (!$gotPgDest) { // destination page unknown, so calculate it from data/source page: if (nz($_POST['btn-go-prev'])) { switch ($this->pgData) { case KSQ_PAGE_CART: $this->pgShow = KSQ_PAGE_CART;	// can't go back any further break; case KSQ_PAGE_SHIP: $this->pgShow = KSQ_PAGE_CART; break; case KSQ_PAGE_PAY: $this->pgShow = KSQ_PAGE_SHIP; break; case KSQ_PAGE_CONF: $this->pgShow = KSQ_PAGE_PAY; break; default:	// source page name not recognized; default to cart $this->pgShow = KSQ_PAGE_CART;	// can't go back any further }	   } elseif (nz($_POST['btn-go-next'])) { switch ($this->pgData) { case KSQ_PAGE_CART: $this->pgShow = KSQ_PAGE_SHIP; break; case KSQ_PAGE_SHIP: $this->pgShow = KSQ_PAGE_PAY; break; case KSQ_PAGE_PAY: $this->pgShow = KSQ_PAGE_CONF; break; case KSQ_PAGE_CONF: $this->pgShow = KSQ_PAGE_RCPT; default:	// source page name not recognized; default to cart $this->pgShow = KSQ_PAGE_CART;	// can't go back any further }	   } elseif (nz($_POST['btn-go-same'])) { $this->pgShow = $this->pgData; } elseif (nz($_POST['btn-go-order'])) { $this->pgShow = KSQ_PAGE_RCPT;	// receipt page - submits order too } else { $this->pgShow = KSQ_PAGE_DEFAULT;	// default page to display }	}
 * 1) which page were we just on

switch ($this->pgShow) { case KSQ_PAGE_CART: $this->inCkout = FALSE; break; case KSQ_PAGE_SHIP: $this->inCkout = TRUE; break; case KSQ_PAGE_PAY: $this->inCkout = TRUE; break; case KSQ_PAGE_CONF: $this->inCkout = TRUE; break; default:	// we shouldn't have to do this... something isn't properly rigorous here. $this->inCkout = TRUE; throw new exception('This should never happen.'); break; }

$this->GetObjects(__METHOD__);	// needs to know what phase we;re in	if (!($this->CartObj->ID > 0)) { //	if (!is_object($this->objCart)) { // if cart is not set, don't go past cart display $this->pgShow = KSQ_PAGE_CART; // this could happen if the user loads the checkout URL directly without a cart -- so log the error, but don't raise an exception $this->Data->LogEvent('ckout.parsequery','pgShow='.$this->pgShow,'cart ID not set','!cart',TRUE,FALSE); http_redirect(KWP_CKOUT_IF_NO_CART,'No cart set; returning to store.'); }	$this->CapturePage;

}   protected function HandleInput { $this->strWikiPg	= ''; $this->strTitle	= 'Checkout';	// Displayed title (page header) $this->strName	= 'checkout';	// HTML title $this->strTitleContext	= 'this is the secure'; // 'Tomb of the...'; $this->strHdrXtra	= ''; $this->strSideXtra	= ''; //'Cat #: '.$this->strReq; $this->strSheet	= 'ckout';	// default

$this->CartObj->LogEvent('page','showing page "'.$this->pgShow.'"'); $this->formShow = $this->pgShow; $this->doNavBar = TRUE; $this->doBackBtn = TRUE; $this->doRefrBtn = FALSE; }   /*      OUTPUT: $doBackBtn: if TRUE, show the BACK navigation button $doRefrBtn: $doNavBar: */   public function DoContent { $out = NULL;

// default options $this->doFixedCard = FALSE; $this->doFixedCountry = FALSE;

$sPageShow = $this->pgShow;

switch ($sPageShow) { case KSQ_PAGE_CART:	// shopping cart $this->doBackBtn = FALSE; $this->doRefrBtn = TRUE; $out .= $this->RenderCart; break; case KSQ_PAGE_SHIP:	// shipping information $out .= $this->RenderShipping; $out .= $this->RenderPayType; break; case KSQ_PAGE_PAY:	// billing information $out .= $this->RenderBilling; break; case KSQ_PAGE_CONF:	// confirm order $this->doNavBar = FALSE; $out .= $this->RenderConfirm; break; case KSQ_PAGE_RCPT:	// order receipt $this->doNavBar = FALSE; $out .= $this->ReceiveOrder; break; default: //$out .= $this->RenderCart;	// cart seems like a safe fallback - avoids showing any customer data // more likely to be a hacking attempt than an internal error: //$this->LogEvent('ckout.render','pgShow='.$this->pgShow,'unknown form type','FUNK',FALSE,TRUE); // The normal shopping cart does not specify a target within the checkout sequence // ...so show the first page which follows the cart page: $out .= $this->RenderShipping; }	echo $out; }   protected static function SelectIf($iFlag,$iText) { if ($iFlag) { return "$iText"; } else { return $iText; }   }    protected function RenderNavBar { $pg = $this->pgShow; $out = ' ' .self::SelectIf($pg == KSQ_PAGE_CART,'Cart')		.' ... '	 .self::SelectIf($pg == KSQ_PAGE_SHIP,'Shipping')	.' ... '	 .self::SelectIf($pg == KSQ_PAGE_PAY,'Payment')	.' ... '	 .self::SelectIf($pg == KSQ_PAGE_CONF,'Final Check')	.' ... '	 .self::SelectIf($pg == KSQ_PAGE_RCPT,'Receipt') .' ';	return $out; }   /*      RETURNS: not sure anymore; probably HTML to be displayed with message identifying which piece(s) of information were mis-entered REQUIRES: Input needs to have been parsed so we know what page's data we're capturing */   public function CapturePage { if (empty($this->pgData)) { $out = ''; } else { $this->objCart->LogEvent('save','saving data from page '.$this->pgData); switch ($this->pgData) { case KSQ_PAGE_CART:	// shopping cart $out = $this->CaptureCart; $this->formSeqData = KI_SEQ_CART;	// 0 break; case KSQ_PAGE_SHIP:	// shipping information $out = $this->CaptureShipping; $this->formSeqData = KI_SEQ_SHIP;	// 1 break; case KSQ_PAGE_PAY:	// billing information $out = $this->CaptureBilling; $this->formSeqData = KI_SEQ_PAY;	// 2 break; case KSQ_PAGE_CONF:	// confirmation $out = NULL;	// no processing needed $this->formSeqData = KI_SEQ_CONF;	// 3 break; default: // more likely to be a hacking attempt than an internal error: $out = 'Cannot save data from unknown page: ['.$this->pgData.']'; $this->Data->LogEvent('ckout.capture','pgData='.$this->pgData,'unknown form ','UNKF',FALSE,TRUE); }	}	switch ($this->pgShow) { case KSQ_PAGE_CART:	$formSeqShow = KI_SEQ_CART;	break; case KSQ_PAGE_SHIP:	$formSeqShow = KI_SEQ_SHIP;	break; case KSQ_PAGE_PAY:	$formSeqShow = KI_SEQ_PAY;	break; case KSQ_PAGE_CONF:	$formSeqShow = KI_SEQ_CONF;	break; case KSQ_PAGE_RCPT:	$formSeqShow = KI_SEQ_RCPT;	break; //	 default: $formSeqShow = 0;	break; default: throw new exception('Does this ever happen?'); }	if ($this->FieldsMissing) { // don't advance until all required fields are entered // ok to back up, however if ($formSeqShow > $this->formSeqData) { $this->pgShow = $this->pgData; }	}	return $out; }   /*      PURPOSE: Render top part of {form and outer table, including } */   protected function RenderContentHdr { $intColumns = self::$intColumns = 4; $htNavBar = $this->RenderNavBar; $dq = '"';	$out = "\n\n"	 ." \n\n \n  ";	} else {	    $htMissing = '';	}

if ($this->doNavBar) { if ($this->doBackBtn) { $htBackBtn = ''; } else { $htBackBtn = ''; }	   if ($this->doRefrBtn) { $htRefrBtn = ''; } else { $htRefrBtn = ''; }	   $out = $htMissing .' ' .'pgShow.'">' .$htBackBtn.$htRefrBtn .''; } else { $out = ''; }

echo ' ';	// close table row opened by RenderContentHdr echo $out; echo "\n \n \n";	// close outer table and form $oCart = $this->Cart; $idSess = $this->objSess->KeyValue; $idCart = $oCart->KeyValue; $idOrd = $oCart->Value('ID_Order'); $sOrd = ($idOrd == 0)?:' Order ID: '.$idOrd.; echo "\nCart ID: $idCart Session ID: $idSess$sOrd " ."\n " ."\n\n"; }   public function DoSidebar { echo "\n\n"; }   /*==========    // cart-to-order conversion // */   /*      ACTION: Receive the order: * convert the cart data to an order record * send confirmation email * display order receipt page */   public function ReceiveOrder { // convert the cart to an order

$objOrd = $this->MakeOrder; $arVars = $objOrd->TemplateVars;		// fetch variables for email $arHdrs = $objOrd->EmailParams($arVars);	// fill in email template with variables

// email the receipt, if email address is available // do this before displaying the receipt -- if the customer sees the receipt, the email is sent // args: $iReally, $iSendToSelf, $iSendToCust, $iAddrSelf, $iAddrCust, $iSubject, $iMessage $arEmail = array(	 'to-self'	=> TRUE,	  'to-cust'	=> TRUE,	  'addr-self'	=> $arHdrs['addr.self'],	  'addr-cust'	=> $arHdrs['addr.cust'],	  'subject'	=> $arHdrs['subj'],	  'message'	=> $arHdrs['msg.body']	  );

$objOrd->EmailConfirm(TRUE,$arEmail,$objOrd->Log); $objOrd->ShowReceipt; }   /*      ACTION: Create an order from this cart HISTORY: 2011-03-27 fixed bug which was preventing order number from being written to cart. Looking at the cart data, this bug apparently happened on 2010-10-28. */   protected function MakeOrder { $objCart = $this->CartObj;

assert('is_object($objCart);'); assert('$objCart->ID > 0;');

$objOrders = $this->Data->Orders; $idOrd = $objCart->Value('ID_Order'); if ($idOrd > 0) { // this may or may not be a problem, but it's a bit unexpected; log it: $arEv = array(	     'code'	=> 'UNX',	      'descr'	=> 'ID_Order already set in cart',	      'params'	=> '\cart='.$objCart->KeyValue.'\ord='.$idOrd,	      'where'	=> __METHOD__,	      ); $objCart->StartEvent($arEv); $idOrd = $objCart->ID_Order; $this->objCart->Update(array('WhenUpdated'=>'NOW')); } else { $idOrd = $objOrders->Create; $arEv = array(	     'code'	=> 'ORD',	      'descr'	=> 'assigning order to cart',	      'params'	=> '\ord='.$idOrd,	      'where'	=> __METHOD__	      ); $objCart->StartEvent($arEv); $arUpd = array(	     'WhenOrdered'	=> 'NOW',	      'ID_Order'	=> $idOrd	      ); $objCart->Update($arUpd); $objCart->FinishEvent;

$objCart->Value('ID_Order',$idOrd); }	return $objOrders->CopyCart($idOrd,$objCart);	// return new order object }   /*==========    // different pages // */   public function RenderCart { unset($this->strMissing); if ($this->CartObj->HasLines) { $out = "\n \n"; return $out; } else { return 'No items in cart!'; // TO DO: log this as a critical error - how did the user get to the checkout with no items? }   } /*    private function RenderForm_IsCard_ckbox($iOn) { $htIsCardVal = $iOn?' checked':''; $out = ' The above &uarr; is also my credit card billing address. ';	return $out; }   private function RenderForm_ToSelf_ckbox($iOn) { $htToSelfVal = $iOn?' checked':''; $out = ' I am ordering this for myself (not for someone else -- so use shipping contact info for buyer) '; return $out; }   /*      INPUT: $iOn = TRUE if shipping address is same as card address */ /*   private function RenderForm_IsCard_button($iOn) { if ($iOn) { // shipping and card addresses are SAME $txtIsCard = 'Enter Different Address for Card'; $txtExpl = " (currently shipping to card's billing address)"; } else { // shipping and card addresses are DIFFERENT $txtIsCard = 'Use Shipping Address for Card'; $txtExpl = ''; }

$out = ' '.$txtExpl.'  '; return $out; }   private function RenderForm_ToSelf_button($iOn) { if ($iOn) { $txtToSelf = 'Shipping to Someone Else'; } else { $txtToSelf = 'Shipping To Myself'; }

$out = '   '; return $out; }   protected static function RenderSectionHdr($iTitle) { $out = "\n "; return $out; }   protected static function RenderPaymentIcons { $out = '' .'' .'' .''; return $out; }   /*      ACTION: Render the form controls where user can enter shipping information */   public function RenderShipping {

$objCartData = $this->CartData; if (!$objCartData->IsLoaded) { throw new exception('Internal error: cart data not loaded.'); }

$hrefForShipping = '<a href="'.KWP_WIKI.'Shipping_Policies">';

// copy any needed constants over to variables for parsing: $ksShipMsg	= KSF_SHIP_MESSAGE;

/*	$htIsCard = $this->RenderForm_IsCard_ckbox($objCartData->ShipToCard); $htToSelf = $this->RenderForm_ToSelf_ckbox($objCartData->ShipToSelf); $strCustShipMsg = $this->CartData->ShipMsg;

$this->htmlBeforeAddress = NULL; $this->htmlAfterAddress = NULL; //	$this->htmlAfterAddress = $htIsCard; //	$this->htmlBeforeContact = $htToSelf;

$this->doFixedCard = FALSE; $this->doFixedSelf = FALSE; $this->doFixedName = FALSE;

//$out = ' <td colspan=2 class=section-title>Ship-to information: '; $out = self::RenderSectionHdr('Shipping information:');

$objAddrShip = $objCartData->ShipObj(FALSE);

$out .= $this->RenderAddress($objAddrShip,array('do.ship.zone'=>TRUE));

//	$out .= $htToSelf;

$out .= <<<__END__ <td colspan=2 align=left> <font color=#880000> Note: If you need any part of your order by a particular date, please tell us in the space below. See our {$hrefForShipping}shipping policies</a> for details. <td align=right valign=top> Special Instructions: <textarea name="$ksShipMsg" cols=50 rows=5>$strCustShipMsg __END__;

$out .= self::RenderSectionFtr; return $out; }   /*      ACTION: Render the form which lets the user choose how to pay */   public function RenderPayType { $out = self::RenderSectionHdr('Payment type:');

$isShipCardSame = $this->CartData->ShipToCard; $htChecked = $isShipCardSame?' checked':'';

$out .= "\n \n" .self::RenderPaymentIcons ." \n" ." More payment options will be available soon.\n" ." ";

$out .= self::RenderSectionFtr; return $out; }   /*      ACTION: looks at "card is shipping" flag and copies shipping address to billing address if flag is set and billing is blank. RETURNS: TRUE if the two addresses match (after copying if necessary). FALSE if they don't match -- card address was not blank and didn't match USAGE: Should be called before displaying any payment page with a billing address, and (I think) whenever processing data from such a page. (Currently, this is only	the "payment" page.) */   protected function ReconcileCardAndShip { $isCardAddrBlank = $this->CartData->CardAddrBlank; $doesMatch = TRUE;	// 3 possible conditions, but only one where they will not match afterwards if ($isCardAddrBlank) { // copy shipping address to card address $this->CartData->CopyShipToCust; } elseif (!$this->CartData->CardMatchesShip) { // clear the "use shipping address as card address" flag $this->CartData->ShipToCard(FALSE); $doesMatch = FALSE;	 // not blank and doesn't match }	$this->doesCartMatchShip = $doesMatch; return $doesMatch; } /* NOTE TO SELF: The problem right now is that we need to make sure the shipping address gets SAVED to the db when it gets copied to billing. I'm starting off trying to make this happen by moving the copying-phase into the CaptureShipping stage. That means $doesMatch needs to be saved to a class member, because RenderBilling needs to know the result. public function RenderBilling { $objCartData = $this->CartData;

// copy any needed constants over to variables for parsing: $ksfCustCardNum = KSF_CUST_CARD_NUM; $ksfCustCardExp = KSF_CUST_CARD_EXP; $ksfCardIsShip = KSF_SHIP_IS_CARD;

$custCardNum = $this->CartData->CardNum; $custCardExp = $this->CartData->CardExp; //	$isShipCardSame = $this->CartData->ShipToCard;	// request to use shipping address for card billing address $doesShipCardMatch = $this->doesCartMatchShip;

$out = self::RenderSectionHdr('Payment information:');

$out .= <<<__END__ <input type=hidden name="$ksfCardIsShip" value="$doesShipCardMatch"> <td align=right valign=middle>We accept: <img align=absmiddle src="/tools/img/cards/logo_ccVisa.gif" title="Visa"> <img align=absmiddle src="/tools/img/cards/logo_ccMC.gif" title="MasterCard"> <img align=absmiddle src="/tools/img/cards/logo_ccAmex.gif" title="American Express"> <img align=absmiddle src="/tools/img/cards/logo_ccDiscover.gif" title="Discover / Novus"> <td align=right valign=middle>Card Number: <input id="cardnum" name="$ksfCustCardNum" value="$custCardNum" size=24> Expires: <input id="cardexp" name="$ksfCustCardExp" value="$custCardExp" size=6> (mm/yy) Tip: It's okay to use dashes or spaces in the card number - reduces typing errors! __END__;

$custShipIsCard = $this->CartData->ShipToCard; $custShipToSelf = $this->CartData->ShipToSelf;

$objCont = $this->CartData->BillObj;

$this->htmlBeforeAddress = NULL; $this->htmlBeforeContact = NULL; $this->msgAfterAddr = ' Note : please check your most recent credit card statement for exact address! '; //	$this->useButtons = TRUE; /*	$this->doFixedCard = $custShipIsCard; $this->doFixedSelf = $custShipToSelf; $this->doFixedCard = FALSE; $this->doFixedSelf = FALSE; $this->doFixedName = FALSE;

$out .= $this->RenderAddress($objCont,array('do.ship.zone'=>FALSE));

$out .= ' '; $out .= self::RenderSectionFtr; return $out; }   /*      ACTION: Render the "confirm this order" page (last page before cart is finalized into order) */   public function RenderConfirm { $out = $this->RenderOrder(TRUE); return $out; } // PAGE DISPLAY ELEMENTS // // -- common display functions /*-     ACTION: Display what will be going into the order Based on cart contents, not order record. This is formatted to fit within the checkout sequence. INPUT: $iEditable: if TRUE, displays buttons to go back to earlier screens for editing; does not actually edit in place. NOTE: Don't use this to display order confirmation. Use the order object so we only show what really went into the order record. */   public function RenderOrder($iEditable) { $objCart = $this->CartObj;

assert('is_object($objCart)'); assert('$objCart->ID != 0; /* ID='.$objCart->ID.' */');

$objCD = $this->CartData;

$isShipCard = $objCD->ShipToCard; $isShipSelf = $objCD->ShipToSelf; $strCustShipMsg = $objCD->ShipMsg; $custCardNum = $objCD->CardNum; $custCardExp = $objCD->CardExp; $isShipCardReally = $this->ReconcileCardAndShip;

$idCart = $objCart->ID; $out = "\n\n"; $htLink = '';

if ($iEditable) { $htLink = $this->HtmlEditLink(KSQ_PAGE_CART); }	$out .= ' <td class=section-title>ITEMS ORDERED: <td class=section-title align=right>'.$htLink.' ';

$out .= " \n \n ";

if ($iEditable) { $htLink = $this->HtmlEditLink(KSQ_PAGE_SHIP); }	$out .= ' <td class=section-title>SHIP TO: <td class=section-title align=right>'.$htLink.' ';

$this->doFixedCard = TRUE; $this->doFixedSelf = TRUE; $this->doFixedName = TRUE; $this->htmlBeforeAddress = ''; $this->htmlBeforeContact = '';

$out .= $this->RenderAddress($objCD->ShipObj(FALSE),array('do.ship.zone'=>TRUE));

if ($iEditable) { $htLink = $this->HtmlEditLink(KSQ_PAGE_PAY); }	$out .= <<<__END__ <td align=right valign=top> Special Instructions: $strCustShipMsg <td class=section-title>PAYMENT: <td class=section-title align=right>$htLink <td align=right valign=middle>Card Number: $custCardNum - Expires: $custCardExp __END__; // if card address is different from shipping, then show it too: // if not shipping to self, then show recipient's phone and/or email: if ($isShipCardReally) { $this->strInsteadOfAddr = 'Credit card address same as shipping address'; }	if ($isShipSelf) { $this->strInsteadOfCont = 'Recipient contact information same as buyer\'s -- shipping to self'; }	// TODO 2012-05-21: this probably won't look right, and will need fixing //	also, are strInsteadOf* strings ^ used in confirmation? $out .= $this->RenderAddress($objCD->CustObj,array('do.ship.zone'=>FALSE));

if ($iEditable) { $sPgName = KSQ_ARG_PAGE_DATA; $sPgShow = $this->pgShow; $out .= <<<__END__ <td colspan=2 align=center bgcolor=ffffff class=section-title> <input type=hidden name="$sPgName" value="$sPgShow"> <input type=submit name="btn-go-prev" value="&lt;&lt; Make Changes"> <input type=submit name="btn-go-order" value="Place the Order!"> __END__; }	//$out .= "\n \n \n";

return $out; }   /*    ARGUMENTS: $iAddr PROPERTY INPUTS: RULES - this documentation is obsolete: Pages displayed: On page 1 (shipping), all fields are editable. On page 2 (payment), some fields may be read-only depending on which "same as" flags the user has checked On page 3 (confirmation), all fields are read-only */   protected function RenderAddress(clsPerson $iAddr, array $iOpts) { $objCart = $this->CartObj; $objZone = $objCart->ShipZoneObj; assert('is_object($objCart)'); assert('is_object($objZone)'); assert('is_object($iAddr)');

$out = '';

if (isset($this->strInsteadOfAddr)) { $out .= ' '.$this->strInsteadOfAddr.' '; } else { $strStateLabel = $objZone->StateLabel;	// "province" etc.	   if ($objZone->hasState) { $htStateAfter = ''; $lenStateInput = 3; // later, we'll also include how many chars the actual abbr is and use this to say "(use n-letter abbreviation)" } else { $htStateAfter = ' (if needed)'; $lenStateInput = 10; }

$isCountryFixed = FALSE; if (empty($strCountry)) { if (!$objZone->isDomestic) { // this code may need some checking $idxCountry = $iAddr->CountryNode->DataType; $this->DataItem($idxCountry,$objZone->Country); }		$isCountryFixed = !empty($strCountry); } /*	   if ($this->doFixedCard) { // make country fixed, regardless of country code, if it has been set earlier: $isCountryFixed = TRUE; }

$hrefForSpam = '<a href="'.KWP_WIKI.'Anti-Spam_Policy">'; $arOpts = array_merge($iOpts,array( 'ht.before.addr'	=> $this->htmlBeforeAddress, 'ht.after.addr'	=> nz($this->htmlAfterAddress), 'ht.after.email'	=> $hrefForSpam.'anti-spam policy</a>', 'ht.ship.combo'	=> $objZone->ComboBox, 'ht.after.state'	=> $htStateAfter, 'str.zip.label'	=> $objZone->PostalCodeName,	// US='Zip Code', otherwise="postal code" 'str.state.label'	=> $strStateLabel, 'len.state.inp'	=> $lenStateInput, 'do.fixed.all'	=> $this->doFixedCard,	// TRUE = disable editing 'do.fixed.name'	=> $this->doFixedName, 'do.fixed.ctry'	=> $this->doFixedCountry, )); /*	   $iAddr->htmlBeforeAddress = $this->htmlBeforeAddress; $iAddr->doFixed = $this->doFixedCard; $iAddr->htShipCombo = $objZone->ComboBox; $iAddr->doFixedName = $this->doFixedName; $out .= $iAddr->Render($this,$arOpts); }

$out .= ' '; if (isset($this->msgAfterAddr)) { $out .= ' '.$this->msgAfterAddr.' '; }

return $out; } /*===== SECTION: input/data management stuff TO DO: Shouldn't all the DetailObj stuff be in the ShopCart class? NOTES: This code could be optimized for more speed, as it creates objects which are sometimes discarded without being used, but I have chosen to optimize instead for clarity and maintainability. /*===================*\ * FORM/DATA METHODS * \*===================*/   public function GetFormItem($iName) { if (isset($_POST[$iName])) { return $_POST[$iName]; } else { $this->objCart->LogEvent('!FLD','Missing form field: '.$iName); return NULL; }   }    public function CartData { return $this->objCart->CartData; } /*   public function DataItem($iType,$iVal=NULL,$iForce=FALSE) { assert('is_numeric($iType)'); return $this->objCart->DataItem($iType,$iVal,$iForce); }   public function DataItem_HTML($iType) { return htmlspecialchars($this->DataItem($iType)); }   /*      DEPRECATED -- use clsShopCart->GetDetailObjs instead */ /*   public function GetDetailObjs { $objAddrShip = $this->AddrShipObj; $objAddrCard = $this->AddrCardObj; $objContDest = $this->ContDestObj; $objContCust = $this->ContCustObj;

$this->objCust = new clsPerson('cust','buyer'); $this->objShip = new clsPerson('ship','recipient');

$arFields = array(	 'num'		=> new clsCartField($this, KSI_CUST_CARD_NUM,	KSF_CUST_CARD_NUM),	  'exp'		=> new clsCartField($this, KSI_CUST_CARD_EXP,	KSF_CUST_CARD_EXP)	  ); $objPayment = new clsPayment($arFields);

$this->objCust->Node('payment', $objPayment);	// the buyer always has the credit card $this->objShip->Node('contact', $objContDest);	// shipping information is always specified

$objContDest->Addr = $objAddrShip;	// shipping address

$this->custShipIsCard	= $this->DataItem(KSI_SHIP_IS_CARD); $this->custShipToSelf	= $this->DataItem(KSI_SHIP_TO_SELF);

if ($this->custShipIsCard) { $objPayment->Node('addr', $objAddrShip);	// use shipping address for card } else { $objPayment->Node('addr', $objAddrCard);	// use separate address for card }

$this->objShip->Node('name', $objAddrShip->Name);

if ($this->custShipToSelf) { // don't use separate person data; re-use buyer contact info plus shipping address $this->objShip->Node('payment', $objPayment);	// the only buyer field the recipient doesn't have $this->objCust = $this->objShip; //$objContDest->Node('addr', $objAddrShip);			// shipping address $this->objShip->Node('contact', $objContDest); $objContDest->Node('addr', $objAddrShip); } else { $this->objShip->Node('contact', $objContDest); $this->objCust->Node('contact', $objContCust); $this->objCust->Node('name', $objAddrCard->Name); }   } /* 2012-05-19 who uses this? public function AddrShip { // REQUIRES: GetDetailObjs must be called first //	return $this->objAddrShip; //	return $this->objShip->Contact->Addr;	// commented 2010-02-18 return $this->AddrShipObj;	// trying this 2010-02-18 }   public function AddrCard { // REQUIRES: GetDetailObjs must be called first /*	if ($this->custShipIsCard) { return $this->objAddrShip; } else { return $this->objAddrCard; } //	return $this->objCust->Payment->Addr;	// commented 2010-02-18 return $this->AddrCardObj; } /*   public function WhoCust { return $this->objCust; }   public function WhoShip { return $this->objShip; } /* SECTION: methods for capturing form data public function CaptureCart { return $this->Cart->CheckData;	// check for any cart data changed }   /*      ACTION: Receive user form input, and update the database */   public function CaptureShipping { $objCD = $this->CartData; $out = $objCD->CaptureData($this->pgData); $this->ReconcileCardAndShip;	// not sure if this puts the flag in the right place, but it's a start. TODO: verify. $objCD->SaveCart;	// update the db from form data

/*	$objAddr = $this->CartData->ShipObj(FALSE); $objAddr->Capture($this); $objShipZone = $this->Cart->ShipZoneObj;

$custName	= $objCD->FormValue(KSI_ADDR_SHIP_NAME); $custStreet	= $objCD->FormValue(KSI_ADDR_SHIP_STREET); $custState	= $objCD->FormValue(KSI_ADDR_SHIP_STATE); $custCity	= $objCD->FormValue(KSI_ADDR_SHIP_CITY); $custCountry	= $objCD->FormValue(KSI_ADDR_SHIP_COUNTRY); $custEmail	= $objCD->FormValue(KSI_CUST_SHIP_EMAIL);

$shipZone	= $objCD->FormValue(KSI_SHIP_ZONE); $objShipZone->Abbr($shipZone); $custShipToSelf	= $objCD->FormValueNz(KSI_SHIP_TO_SELF); $custShipIsCard	= $objCD->FormValueNz(KSI_SHIP_IS_CARD); $custZip	= $this->GetFormItem(KSF_ADDR_SHIP_ZIP); $custPhone	= $this->GetFormItem(KSF_CUST_SHIP_PHONE); $custMessage	= $this->GetFormItem(KSF_SHIP_MESSAGE);

$objCD = $this->CartData;

/*     // load up fields that we need to validate (there's got to be a better way...) $objCD->ShipZone($shipZone); $objCD->ShipAddrName($custName); $objCD->ShipAddrStreet($custStreet); $objCD->ShipAddrTown($custCity); $objCD->ShipAddrState($custState); $objCD->ShipAddrZip($custZip); $objCD->ShipAddrCountry($custCountry);

$objCD->ShipToSelf($custShipToSelf); $objCD->ShipToCard($custShipIsCard); $objCD->ShipEmail($custEmail); $objCD->ShipPhone($custPhone); $objCD->ShipMessage($custMessage); $this->CheckField('name',$custName); $this->CheckField('street address',$custStreet); $this->CheckField('city',$custCity); if (($custState == '') && ($objShipZone->hasState)) { $this->AddMissing($objShipZone->StateLabel); }	if (!$objShipZone->isDomestic) { $this->CheckField('country',$custCountry); }	if (!is_null($custEmail)) {	// if we received a value... $this->CheckField('email',$custEmail);	// ...make sure it's not blank }   }    public function CaptureBilling { $objCD = $this->CartData; $out = $objCD->CaptureData($this->pgData); $objCD->SaveCart;	// update the db from form data // TODO: make sure all necessary fields were filled in

/*	$objAddr = $this->CartData->CustObj; $objAddr->Capture($this); $objPay = $this->CartData->PayObj; $objPay->Capture($this);

/*	$this->custShipToSelf	= $this->DataItem(KSI_SHIP_TO_SELF); $this->custShipIsCard	= $this->DataItem(KSI_SHIP_IS_CARD); $custCardNum	= $this->GetFormItem(KSF_CUST_CARD_NUM); $custCardExp	= $this->GetFormItem(KSF_CUST_CARD_EXP);

# check for missing data $this->CheckField("card number",$custCardNum); $this->CheckField("expiration date",$custCardExp);

if (!$this->CartData->ShipToCard) { $custCardName	= $this->GetFormItem(KSF_CUST_CARD_NAME); $custCardStreet	= $this->GetFormItem(KSF_CUST_CARD_STREET); $custCardCity	= $this->GetFormItem(KSF_CUST_CARD_CITY); $custCardState	= $this->GetFormItem(KSF_CUST_CARD_STATE); $custCardZip	= $this->GetFormItem(KSF_CUST_CARD_ZIP); $custCardCountry	= $this->GetFormItem(KSF_CUST_CARD_COUNTRY);

# check for missing data $this->CheckField("cardholder's name",$custCardName); $this->CheckField("card's billing address",$custCardStreet); $this->CheckField("card's billing address - city",$custCardCity); }	if (!$this->CartData->ShipToSelf) { $custEmail	= $this->GetFormItem(KSF_CUST_PAY_EMAIL); $custPhone	= $this->GetFormItem(KSF_CUST_PAY_PHONE); }	$custCheckNum	= $this->GetFormItem(KSF_CUST_CHECK_NUM); } //-   protected function HtmlEditLink($iPage,$iText='edit',$iPfx='[',$iSfx=']') { $out = $iPfx.'<a href="?'.KSQ_ARG_PAGE_DEST.'='.$iPage.'">'.$iText.'</a>'.$iSfx; return $out; }   private function Order { return $this->objCart->Order; } //=== handling of missing fields public function AddMissing($iText) { if (!$this->FieldsMissing) { $this->strMissing = $iText; } else { $this->strMissing .= ', '.$iText; }   }    public function CheckField($iText,$iValue) { if ($iValue == '') { $this->AddMissing($iText); }   }    public function FieldsMissing { return isset($this->strMissing); } }