Difference between revisions of "VbzCart/archive/code/files/ckout.php"

from HTYP, the free directory anyone can edit if they can prove to me that they're not a spambot
< VbzCart‎ | archive‎ | code‎ | files
Jump to navigation Jump to search
(last edit before gutting -- *mostly* works...)
 
(One intermediate revision by the same user not shown)
Line 11: Line 11:
 
     2013-02-20 mostly working, but "ship to card" and "ship to self" logic is becoming unmanageable.
 
     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.
 
       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
 
*/
 
*/
  
Line 21: Line 22:
 
*/
 
*/
 
class clsPageCkout extends clsPageCart {
 
class clsPageCkout extends clsPageCart {
 +
    protected $doesCartMatchShip;
 +
 +
    protected static $intColumns;
 
/*
 
/*
 
     protected $objCust; // person who placed the order
 
     protected $objCust; // person who placed the order
Line 35: Line 39:
 
       OUTPUT:
 
       OUTPUT:
 
$inCkout: if TRUE, it's ok to use a cart whose order has been placed.
 
$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() {
 
     protected function ParseInput() {
# Two important concepts:
 
# 1. $pgData: The page whose data we're receiving (blank if there is no data)
 
# 2. $pgShow: The page whose form we're wanting to display
 
  
 
# which page were we just on
 
# which page were we just on
Line 68: Line 72:
 
  case KSQ_PAGE_PAY:
 
  case KSQ_PAGE_PAY:
 
    $this->pgShow = KSQ_PAGE_SHIP;
 
    $this->pgShow = KSQ_PAGE_SHIP;
 +
    break;
 +
  case KSQ_PAGE_CONF:
 +
    $this->pgShow = KSQ_PAGE_PAY;
 
    break;
 
    break;
 
  default: // source page name not recognized; default to cart
 
  default: // source page name not recognized; default to cart
Line 83: Line 90:
 
    $this->pgShow = KSQ_PAGE_CONF;
 
    $this->pgShow = KSQ_PAGE_CONF;
 
    break;
 
    break;
 +
  case KSQ_PAGE_CONF:
 +
    $this->pgShow = KSQ_PAGE_RCPT;
 
  default: // source page name not recognized; default to cart
 
  default: // source page name not recognized; default to cart
 
    $this->pgShow = KSQ_PAGE_CART; // can't go back any further
 
    $this->pgShow = KSQ_PAGE_CART; // can't go back any further
Line 110: Line 119:
 
  default: // we shouldn't have to do this... something isn't properly rigorous here.
 
  default: // we shouldn't have to do this... something isn't properly rigorous here.
 
    $this->inCkout = TRUE;
 
    $this->inCkout = TRUE;
 +
    throw new exception('This should never happen.');
 
    break;
 
    break;
 
}
 
}
Line 118: Line 128:
 
    // if cart is not set, don't go past cart display
 
    // if cart is not set, don't go past cart display
 
    $this->pgShow = KSQ_PAGE_CART;
 
    $this->pgShow = KSQ_PAGE_CART;
    $this->LogEvent('ckout.parsequery','pgShow='.$this->pgShow,'cart ID not set','!cart',TRUE,TRUE);
+
    // 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();
 
$this->CapturePage();
Line 161: Line 173:
 
  case KSQ_PAGE_SHIP: // shipping information
 
  case KSQ_PAGE_SHIP: // shipping information
 
    $out .= $this->RenderShipping();
 
    $out .= $this->RenderShipping();
 +
    $out .= $this->RenderPayType();
 
    break;
 
    break;
 
  case KSQ_PAGE_PAY: // billing information
 
  case KSQ_PAGE_PAY: // billing information
Line 199: Line 212:
 
  .self::SelectIf($pg == KSQ_PAGE_RCPT,'Receipt')
 
  .self::SelectIf($pg == KSQ_PAGE_RCPT,'Receipt')
 
  .'</center>';
 
  .'</center>';
 +
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;
 
return $out;
 
     }
 
     }
Line 205: Line 269:
 
     */
 
     */
 
     protected function RenderContentHdr() {
 
     protected function RenderContentHdr() {
$out = '<!-- +RenderContentHdr() -->';
+
$intColumns = self::$intColumns = 4;
$out .= '<center><form action="./" name=checkout method=post><table class=ckout-outer>';
+
$htNavBar = $this->RenderNavBar();
$out .= '<tr><td colspan=2>'.$this->RenderNavBar().'</td></tr>';
+
$dq = '"';
$out .= '<tr><td>';
+
$out = "\n<!-- +RenderContentHdr() -->\n"
$out .= '<!-- -RenderContentHdr() -->';
+
  ."<center>\n<form action=$dq./$dq name=checkout method=post>\n<table class=ckout-outer>"
echo $out;
+
  ."<tr><td colspan=$intColumns>$htNavBar</td></tr>"
 +
  .'<tr><td>'
 +
  ."\n<!-- -RenderContentHdr() -->\n";
 +
return $out;
 
     }
 
     }
 
     /*----
 
     /*----
Line 216: Line 283:
 
     */
 
     */
 
     protected function RenderContentFtr() {
 
     protected function RenderContentFtr() {
echo '<!-- +RenderContentFtr() -->';
+
echo "\n<!-- +RenderContentFtr() in ckout.php -->\n";
 +
$intColumns = self::$intColumns;
  
if ($this->FieldsMissing()) {
+
if ($this->FieldsMissing() && ($this->pgShow == $this->pgData)) {
    $htMissing = '<tr><td colspan=2><table><tr><td><img src="'.KWP_ICON_ALERT
+
    // NOTE: I've been unable to get the icon to align nicely with the text without using a table.
      .'"></td><td valign=middle><span class=alert><b>Please fill in the following</b></span>: '
+
    $htMissing = "<tr><td colspan=$intColumns>\n<table>\n<tr><td><img src=".'"'.KWP_ICON_ALERT
 +
      .'"></td><td valign=middle><span class=alert style="background: yellow"><b>Please fill in the following</b>: '
 
      .$this->strMissing
 
      .$this->strMissing
      .'</td></tr></table></td></tr>';
+
      ."</span></td></tr>\n</table>\n</td></tr>";
 
} else {
 
} else {
 
    $htMissing = '';
 
    $htMissing = '';
Line 228: Line 297:
  
 
if ($this->doNavBar) {
 
if ($this->doNavBar) {
    $strPgDataName = KSQ_ARG_PAGE_DATA;
 
 
    if ($this->doBackBtn) {
 
    if ($this->doBackBtn) {
 
$htBackBtn = '<input type=submit name="btn-go-prev" value="&lt; Back">';
 
$htBackBtn = '<input type=submit name="btn-go-prev" value="&lt; Back">';
Line 241: Line 309:
 
    $out =
 
    $out =
 
      $htMissing
 
      $htMissing
      .'<tr><td colspan=2 align=center bgcolor=ffffff class=section-title>'
+
      .'<tr><td colspan='.$intColumns.' align=center bgcolor=ffffff class=section-title>'
      .'<input type=hidden name="'.$strPgDataName.'" value="'.$this->pgShow.'">'
+
      .'<input type=hidden name="'.KSQ_ARG_PAGE_DATA.'" value="'.$this->pgShow.'">'
 
      .$htBackBtn.$htRefrBtn
 
      .$htBackBtn.$htRefrBtn
 
      .'<input type=submit name="btn-go-next" value="Next &gt;">';
 
      .'<input type=submit name="btn-go-next" value="Next &gt;">';
Line 251: Line 319:
 
echo '</td></tr>'; // close table row opened by RenderContentHdr()
 
echo '</td></tr>'; // close table row opened by RenderContentHdr()
 
echo $out;
 
echo $out;
echo '</table></form>'; // close outer table and form
+
echo "\n</table>\n</form>\n"; // close outer table and form
 +
$oCart = $this->Cart();
 
$idSess = $this->objSess->KeyValue();
 
$idSess = $this->objSess->KeyValue();
$idCart = $this->Cart()->KeyValue();
+
$idCart = $oCart->KeyValue();
echo "<span style=\"color: grey; font-size: 10pt;\">Cart ID: <b>$idCart</b> Session ID: <b>$idSess</b></span>";
+
$idOrd = $oCart->Value('ID_Order');
echo '<!-- END RenderContentFtr() -->';
+
$sOrd = ($idOrd == 0)?'':' Order ID: <b>'.$idOrd.'</b>';
 +
echo "\n<span style=\"color: grey; font-size: 10pt;\">Cart ID: <b>$idCart</b> Session ID: <b>$idSess</b>$sOrd</span>"
 +
  ."\n</center>"
 +
  ."\n<!-- -RenderContentFtr() -->\n";
 
     }
 
     }
 
     public function DoSidebar() {
 
     public function DoSidebar() {
echo '<!-- DoSidebar() -->';
+
echo "\n<!-- DoSidebar() -->\n";
/* maybe we're not currently using this...
 
$imgRoot = '';
 
$strNotYet = '<img src="'.$imgRoot.'/tools/img/icons/nulbox.gif">';
 
$strDone = '<img src="'.$imgRoot.'/tools/img/icons/chkbox.gif" title="done">';
 
$strDoNow = '<img src="'.$imgRoot.'/tools/img/icons/curbox.gif" title="do this now">';
 
 
 
$pfx_step2 = $strNotYet;
 
$pfx_step3 = $strNotYet;
 
if ($did_step1) {
 
$pfx_step1 = $strDone;
 
if ($did_step2) {
 
$pfx_step2 = $strDone;
 
$pfx_step3 = $strDoNow;
 
} else {
 
$pfx_step2 = $strDoNow;
 
}
 
} else {
 
$pfx_step1 = $strDoNow;
 
}
 
 
 
$out = <<<__END__
 
<script language="JavaScript1.2" src="{$imgRoot}/tools/js/lib.js"></script>
 
<table class=border align=left background="{$imgRoot}/tools/img/bg/lines/" cellpadding=5><tr><td>
 
<table background="" bgcolor=000000  cellpadding=3 bgcolor=000000><tr><td>
 
<table background="" cellpadding=5 bgcolor=ffffff><tr><td>
 
 
 
<span class=menu-text>
 
<span class=menu-dept>Steps</span>
 
 
 
<br>-- $pfx_step1 1. enter <b>shipping</b>
 
<br>-- $pfx_step2 2. enter <b>payment</b>
 
<br>-- $pfx_step3 3. print <b>receipt</b>
 
 
 
<p><span class=menu-dept>Information</span>
 
$linkToShipping
 
$linkToPrivacy
 
$linkToSecurity
 
$linkToReturns
 
<p><span class=menu-dept>Other Choices</span>
 
<br>-- <a href="http://vbz.net/cart/?action=delcart"><b>clear &amp; restart</b> your order</a>
 
<br>-- <a href="http://vbz.net/about/contact/email/"><b>send</b> us a <b>message</b></a>
 
 
 
</td></tr></table>
 
</td></tr></table>
 
</td></tr></table>
 
__END__;
 
echo $out;
 
*/
 
 
     }
 
     }
 
     /*==========
 
     /*==========
Line 321: Line 345:
  
 
$objOrd = $this->MakeOrder();
 
$objOrd = $this->MakeOrder();
 
 
$arVars = $objOrd->TemplateVars(); // fetch variables for email
 
$arVars = $objOrd->TemplateVars(); // fetch variables for email
 
$arHdrs = $objOrd->EmailParams($arVars); // fill in email template with variables
 
$arHdrs = $objOrd->EmailParams($arVars); // fill in email template with variables
Line 392: Line 415:
 
unset($this->strMissing);
 
unset($this->strMissing);
 
if ($this->CartObj()->HasLines()) {
 
if ($this->CartObj()->HasLines()) {
    $out = '<table><tr><td colspan=2>';
+
    $out = "\n<table>\n<tr><td colspan=2>";
 
    $out .= $this->CartObj()->RenderCore(TRUE);
 
    $out .= $this->CartObj()->RenderCore(TRUE);
    $out .= '</td></tr></table>';
+
    $out .= "</td></tr>\n</table>\n";
 
    return $out;
 
    return $out;
 
} else {
 
} else {
Line 401: Line 424:
 
}
 
}
 
     }
 
     }
 +
/*
 
     private function RenderForm_IsCard_ckbox($iOn) {
 
     private function RenderForm_IsCard_ckbox($iOn) {
 
$htIsCardVal = $iOn?' checked':'';
 
$htIsCardVal = $iOn?' checked':'';
Line 421: Line 445:
 
return $out;
 
return $out;
 
     }
 
     }
 +
*/
 
     /*----
 
     /*----
 
       INPUT: $iOn = TRUE if shipping address is same as card address
 
       INPUT: $iOn = TRUE if shipping address is same as card address
 
     */
 
     */
 +
/*
 
     private function RenderForm_IsCard_button($iOn) {
 
     private function RenderForm_IsCard_button($iOn) {
 
if ($iOn) {
 
if ($iOn) {
Line 458: Line 484:
 
return $out;
 
return $out;
 
     }
 
     }
 +
*/
 +
    protected static function RenderSectionHdr($iTitle) {
 +
$out = "\n<table width=100%>"
 +
  ."\n<tr><td colspan=2 class=section-title><b>$iTitle</b></td></tr>";
 +
 +
return $out;
 +
    }
 +
    protected static function RenderSectionFtr() {
 +
$out = "\n</table>";
 +
return $out;
 +
    }
 +
    protected static function RenderPaymentIcons() {
 +
$out =
 +
  '<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">';
 +
return $out;
 +
    }
 +
    /*----
 +
      ACTION: Render the form controls where user can enter shipping information
 +
    */
 
     public function RenderShipping() {
 
     public function RenderShipping() {
  
Line 470: Line 518:
 
$ksShipMsg = KSF_SHIP_MESSAGE;
 
$ksShipMsg = KSF_SHIP_MESSAGE;
  
 +
/*
 
$htIsCard = $this->RenderForm_IsCard_ckbox($objCartData->ShipToCard());
 
$htIsCard = $this->RenderForm_IsCard_ckbox($objCartData->ShipToCard());
 
$htToSelf = $this->RenderForm_ToSelf_ckbox($objCartData->ShipToSelf());
 
$htToSelf = $this->RenderForm_ToSelf_ckbox($objCartData->ShipToSelf());
 
+
*/
 
$strCustShipMsg = $this->CartData()->ShipMsg();
 
$strCustShipMsg = $this->CartData()->ShipMsg();
  
 
$this->htmlBeforeAddress = NULL;
 
$this->htmlBeforeAddress = NULL;
$this->htmlAfterAddress = $htIsCard;
+
$this->htmlAfterAddress = NULL;
 +
// $this->htmlAfterAddress = $htIsCard;
 
// $this->htmlBeforeContact = $htToSelf;
 
// $this->htmlBeforeContact = $htToSelf;
  
Line 483: Line 533:
 
$this->doFixedName = FALSE;
 
$this->doFixedName = FALSE;
  
$out = '<tr><td colspan=2 class=section-title>Ship-to information:</td></tr>';
+
//$out = '<tr><td colspan=2 class=section-title>Ship-to information:</td></tr>';
 +
$out = self::RenderSectionHdr('Shipping information:');
  
 
$objAddrShip = $objCartData->ShipObj(FALSE);
 
$objAddrShip = $objCartData->ShipObj(FALSE);
  
$out .= $this->RenderAddress($objAddrShip);
+
$out .= $this->RenderAddress($objAddrShip,array('do.ship.zone'=>TRUE));
  
$out .= $htToSelf;  
+
// $out .= $htToSelf;  
  
 
$out .= <<<__END__
 
$out .= <<<__END__
Line 503: Line 554:
 
</tr>
 
</tr>
 
__END__;
 
__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<tr><td align=center>\n"
 +
  .self::RenderPaymentIcons()
 +
  ."<table><tr><td>"
 +
  .'<input name=payType value="'.KSF_PTYP_CARD_HERE.'" type=radio checked disabled> Visa / MasterCard / Discover / American Express - pay here'
 +
  .'<br>&emsp;<input name="'.KSF_SHIP_IS_CARD.'" type=checkbox'.$htChecked.'>billing address is same as shipping address above'
 +
  ."</td></tr></table>\n"
 +
  ."<hr>More payment options will be available soon.\n"
 +
  ."</td></tr>";
 +
 +
$out .= self::RenderSectionFtr();
 
return $out;
 
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() {
 
     public function RenderBilling() {
 
$objCartData = $this->CartData();
 
$objCartData = $this->CartData();
Line 511: Line 614:
 
$ksfCustCardNum = KSF_CUST_CARD_NUM;
 
$ksfCustCardNum = KSF_CUST_CARD_NUM;
 
$ksfCustCardExp = KSF_CUST_CARD_EXP;
 
$ksfCustCardExp = KSF_CUST_CARD_EXP;
 +
$ksfCardIsShip = KSF_SHIP_IS_CARD;
  
 
$custCardNum = $this->CartData()->CardNum();
 
$custCardNum = $this->CartData()->CardNum();
 
$custCardExp = $this->CartData()->CardExp();
 
$custCardExp = $this->CartData()->CardExp();
 +
// $isShipCardSame = $this->CartData()->ShipToCard(); // request to use shipping address for card billing address
 +
$doesShipCardMatch = $this->doesCartMatchShip;
  
$out = <<<__END__
+
$out = self::RenderSectionHdr('Payment information:');
<tr><td colspan=2 class=section-title>Payment information:</td></tr>
+
 
 +
$out .= <<<__END__
 +
<input type=hidden name="$ksfCardIsShip" value="$doesShipCardMatch">
 
<tr><td align=right valign=middle>We accept:</td>
 
<tr><td align=right valign=middle>We accept:</td>
 
<td>
 
<td>
Line 534: Line 642:
 
$custShipToSelf = $this->CartData()->ShipToSelf();
 
$custShipToSelf = $this->CartData()->ShipToSelf();
  
$htIsCard = $this->RenderForm_IsCard_button($custShipIsCard);
 
$htToSelf = $this->RenderForm_ToSelf_button($custShipToSelf);
 
 
/*
 
// get shipping address object
 
if ($custShipIsCard) {
 
    $objAddr = $this->CartData()->ShipObj();
 
//     $txtIsCard = 'Enter Different Address for Card';
 
} else {
 
    $objAddr = $this->CartData()->CustObj();
 
//     $txtIsCard = 'Use Shipping Address for Card';
 
}
 
*/
 
// get contact (customer) object
 
/*
 
if ($custShipToSelf) {
 
    $objCont = $this->CartData()->DestObj(); // use shipping destination for billing address
 
} else {
 
    $objCont = $this->CartData()->CustObj(); // customer has separate billing address
 
}
 
*/
 
 
$objCont = $this->CartData()->BillObj();
 
$objCont = $this->CartData()->BillObj();
/*
 
$htIsCard =
 
  '<tr><td colspan=2><input name="'
 
  .KSF_SHIP_IS_CARD
 
  .'" type=submit value="'
 
  .$txtIsCard
 
  .'"></td></tr>';
 
$htToSelf =
 
  '<tr><td colspan=2><input name="'
 
  .KSF_SHIP_TO_SELF
 
  .'" type=submit value="'
 
  .$txtToSelf
 
  .'"></td></tr>';
 
*/
 
  
$this->htmlBeforeAddress = $htIsCard;
+
$this->htmlBeforeAddress = NULL;
$this->htmlBeforeContact = $htToSelf;
+
$this->htmlBeforeContact = NULL;
 
$this->msgAfterAddr = '<span class=note><font color=ff0000><b>Note</b></font>: please check your most recent credit card statement for exact address!</span>';
 
$this->msgAfterAddr = '<span class=note><font color=ff0000><b>Note</b></font>: please check your most recent credit card statement for exact address!</span>';
 
// $this->useButtons = TRUE;
 
// $this->useButtons = TRUE;
 +
/*
 
$this->doFixedCard = $custShipIsCard;
 
$this->doFixedCard = $custShipIsCard;
 
$this->doFixedSelf = $custShipToSelf;
 
$this->doFixedSelf = $custShipToSelf;
 +
*/
 +
$this->doFixedCard = FALSE;
 +
$this->doFixedSelf = FALSE;
 
$this->doFixedName = FALSE;
 
$this->doFixedName = FALSE;
  
$out .= $this->RenderAddress($objCont);
+
$out .= $this->RenderAddress($objCont,array('do.ship.zone'=>FALSE));
  
 
$out .= '</tr>';
 
$out .= '</tr>';
 +
$out .= self::RenderSectionFtr();
 
return $out;
 
return $out;
 
     }
 
     }
Line 617: Line 695:
 
$custCardNum = $objCD->CardNum();
 
$custCardNum = $objCD->CardNum();
 
$custCardExp = $objCD->CardExp();
 
$custCardExp = $objCD->CardExp();
 +
$isShipCardReally = $this->ReconcileCardAndShip();
  
$out = '<!-- CART ID='.$objCart->ID.' -->';
+
$idCart = $objCart->ID;
 +
$out = "\n<!-- CART ID=$idCart -->\n";
 
$htLink = '';
 
$htLink = '';
  
Line 626: Line 706:
 
$out .= '<tr><td class=section-title>ITEMS ORDERED:</td><td class=section-title align=right>'.$htLink.'</td></tr>';
 
$out .= '<tr><td class=section-title>ITEMS ORDERED:</td><td class=section-title align=right>'.$htLink.'</td></tr>';
  
$out .= '<tr><td colspan=2><table>';
+
$out .= "<tr><td colspan=2>\n<table>\n";
 
$out .= $objCart->RenderConfirm();
 
$out .= $objCart->RenderConfirm();
$out .= '</table></td></tr>';
+
$out .= "\n</table>\n</td></tr>";
  
 
if ($iEditable) {
 
if ($iEditable) {
Line 641: Line 721:
 
$this->htmlBeforeContact = '';
 
$this->htmlBeforeContact = '';
  
$out .= $this->RenderAddress($objCD->ShipObj(FALSE));
+
$out .= $this->RenderAddress($objCD->ShipObj(FALSE),array('do.ship.zone'=>TRUE));
  
 
if ($iEditable) {
 
if ($iEditable) {
Line 660: Line 740:
 
// if card address is different from shipping, then show it too:
 
// 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 not shipping to self, then show recipient's phone and/or email:
if ($isShipCard) {
+
if ($isShipCardReally) {
 
    $this->strInsteadOfAddr = 'Credit card address <b>same as shipping address</b>';
 
    $this->strInsteadOfAddr = 'Credit card address <b>same as shipping address</b>';
 
}
 
}
Line 668: Line 748:
 
// TODO 2012-05-21: this probably won't look right, and will need fixing
 
// TODO 2012-05-21: this probably won't look right, and will need fixing
 
// also, are strInsteadOf* strings ^ used in confirmation?
 
// also, are strInsteadOf* strings ^ used in confirmation?
$out .= $this->RenderAddress($objCD->CustObj());
+
$out .= $this->RenderAddress($objCD->CustObj(),array('do.ship.zone'=>FALSE));
  
 
if ($iEditable) {
 
if ($iEditable) {
/* using "btn-go-pay" is a kluge. I'm trying to go back to the payment page,
+
    $sPgName = KSQ_ARG_PAGE_DATA;
  but "btn-go-prev" goes too far. This goes back to the shipping info page,
+
    $sPgShow = $this->pgShow;
  which is acceptable for now.
 
*/
 
 
    $out .= <<<__END__
 
    $out .= <<<__END__
 
<tr><td colspan=2 align=center bgcolor=ffffff class=section-title>
 
<tr><td colspan=2 align=center bgcolor=ffffff class=section-title>
<input type=submit name="btn-go-pay" value="&lt;&lt; Make Changes">
+
<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!">
 
<input type=submit name="btn-go-order" value="Place the Order!">
</td></tr>
 
 
__END__;
 
__END__;
 
}
 
}
$out .= '</table></form>';
+
//$out .= "\n</table>\n</form>\n";
  
 
return $out;
 
return $out;
Line 690: Line 768:
 
       $iAddr
 
       $iAddr
 
     PROPERTY INPUTS:
 
     PROPERTY INPUTS:
     RULES:
+
     RULES - this documentation is obsolete:
 
       Pages displayed:
 
       Pages displayed:
 
On page 1 (shipping), all fields are editable.
 
On page 1 (shipping), all fields are editable.
Line 696: Line 774:
 
On page 3 (confirmation), all fields are read-only
 
On page 3 (confirmation), all fields are read-only
 
     */
 
     */
     protected function RenderAddress(clsPerson $iAddr) {
+
     protected function RenderAddress(clsPerson $iAddr, array $iOpts) {
 
$objCart = $this->CartObj();
 
$objCart = $this->CartObj();
 
$objZone = $objCart->ShipZoneObj();
 
$objZone = $objCart->ShipZoneObj();
Line 735: Line 813:
  
 
    $hrefForSpam = '<a href="'.KWP_WIKI.'Anti-Spam_Policy">';
 
    $hrefForSpam = '<a href="'.KWP_WIKI.'Anti-Spam_Policy">';
    $arOpts = array(
+
    $arOpts = array_merge($iOpts,array(
 
      'ht.before.addr' => $this->htmlBeforeAddress,
 
      'ht.before.addr' => $this->htmlBeforeAddress,
 
      'ht.after.addr' => nz($this->htmlAfterAddress),
 
      'ht.after.addr' => nz($this->htmlAfterAddress),
Line 747: Line 825:
 
      'do.fixed.name' => $this->doFixedName,
 
      'do.fixed.name' => $this->doFixedName,
 
      'do.fixed.ctry' => $this->doFixedCountry,
 
      'do.fixed.ctry' => $this->doFixedCountry,
      );
+
      ));
 
/*
 
/*
 
    $iAddr->htmlBeforeAddress = $this->htmlBeforeAddress;
 
    $iAddr->htmlBeforeAddress = $this->htmlBeforeAddress;
Line 757: Line 835:
 
}
 
}
  
 +
$out .= '<tr>';
 
if (isset($this->msgAfterAddr)) {
 
if (isset($this->msgAfterAddr)) {
 
    $out .= '<td colspan=2>'.$this->msgAfterAddr.'</td>';
 
    $out .= '<td colspan=2>'.$this->msgAfterAddr.'</td>';
Line 792: Line 871:
 
     }
 
     }
 
*/
 
*/
    /*
 
      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 = 0;
 
break;
 
      case KSQ_PAGE_SHIP: // shipping information
 
$out = $this->CaptureShipping();
 
$this->formSeqData = 1;
 
break;
 
      case KSQ_PAGE_PAY: // billing information
 
$out = $this->CaptureBilling();
 
$this->formSeqData = 2;
 
break;
 
      default:
 
// more likely to be a hacking attempt than an internal error:
 
$out = 'Cannot save data from unknown page: ['.$this->pgData.']';
 
$this->LogEvent('ckout.capture','pgData='.$this->pgData,'unknown form ','FUNK',FALSE,TRUE);
 
    }
 
}
 
switch ($this->pgShow) {
 
  case KSQ_PAGE_CART: $formSeqShow = 0; break;
 
  case KSQ_PAGE_SHIP: $formSeqShow = 1; break;
 
  case KSQ_PAGE_PAY: $formSeqShow = 2; break;
 
  case KSQ_PAGE_CONF: $formSeqShow = 3; break;
 
  case KSQ_PAGE_RCPT: $formSeqShow = 4; break;
 
  default: $formSeqShow = 0; break;
 
}
 
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;
 
    }
 
 
     /*----
 
     /*----
 
       DEPRECATED -- use clsShopCart->GetDetailObjs() instead
 
       DEPRECATED -- use clsShopCart->GetDetailObjs() instead
Line 927: Line 960:
 
$objCD = $this->CartData();
 
$objCD = $this->CartData();
 
$out = $objCD->CaptureData($this->pgData);
 
$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
 
$objCD->SaveCart(); // update the db from form data
  
Line 999: Line 1,033:
 
$custCardNum = $this->GetFormItem(KSF_CUST_CARD_NUM);
 
$custCardNum = $this->GetFormItem(KSF_CUST_CARD_NUM);
 
$custCardExp = $this->GetFormItem(KSF_CUST_CARD_EXP);
 
$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()) {
 
if (!$this->CartData()->ShipToCard()) {
Line 1,008: Line 1,046:
 
    $custCardCountry = $this->GetFormItem(KSF_CUST_CARD_COUNTRY);
 
    $custCardCountry = $this->GetFormItem(KSF_CUST_CARD_COUNTRY);
  
/*
 
    # save current values
 
    $this->DataItem(KSI_CUST_CARD_NAME,$custCardName);
 
    $this->DataItem(KSI_CUST_CARD_STREET,$custCardStreet);
 
    $this->DataItem(KSI_CUST_CARD_CITY,$custCardCity);
 
    $this->DataItem(KSI_CUST_CARD_STATE,$custCardState);
 
    $this->DataItem(KSI_CUST_CARD_ZIP,$custCardZip);
 
    $this->DataItem(KSI_CUST_CARD_COUNTRY,$custCardCountry);
 
*/
 
 
    # check for missing data
 
    # check for missing data
 
    $this->CheckField("cardholder's name",$custCardName);
 
    $this->CheckField("cardholder's name",$custCardName);
Line 1,026: Line 1,055:
 
    $custEmail = $this->GetFormItem(KSF_CUST_PAY_EMAIL);
 
    $custEmail = $this->GetFormItem(KSF_CUST_PAY_EMAIL);
 
    $custPhone = $this->GetFormItem(KSF_CUST_PAY_PHONE);
 
    $custPhone = $this->GetFormItem(KSF_CUST_PAY_PHONE);
 
/*
 
    # save current values
 
    $this->DataItem(KSI_CUST_PAY_EMAIL,$custEmail);
 
    $this->DataItem(KSI_CUST_PAY_PHONE,$custPhone);
 
*/
 
 
}
 
}
 
$custCheckNum = $this->GetFormItem(KSF_CUST_CHECK_NUM);
 
$custCheckNum = $this->GetFormItem(KSF_CUST_CHECK_NUM);
 
/*
 
# handle unconditional fields:
 
# save current values
 
$this->CartData()->CardNum($custCardNum);
 
$this->CartData()->CardExp($custCardExp);
 
$this->CartData()->CheckNum($custCheckNum);
 
 
$this->CheckField("credit card number",$custCardNum);
 
$this->CheckField("credit card expiration date",$custCardExp);
 
# save list of missing fields
 
// $this->DataItem(KSI_CUST_MISSING,$this->strMissing,TRUE);
 
 
// TODO: where do these get used?
 
$this->custCardNum = $custCardNum;
 
$this->custCardExp = $custCardExp;
 
$this->custCheckNum = $custCheckNum;
 
*/
 
 
     }
 
     }
 
//-----
 
//-----

Latest revision as of 02:09, 19 December 2014

Notes

This version doesn't quite work (contact data is not being saved), but I'm saving it for future reference before I start rearranging things.

Code

<php> <?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() {
  1. which page were we just on

$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 } }

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<form action=$dq./$dq name=checkout method=post>\n" ."" .'";

} else { $htMissing = ; }

if ($this->doNavBar) { if ($this->doBackBtn) { $htBackBtn = '<input type=submit name="btn-go-prev" value="< Back">'; } else { $htBackBtn = ; } if ($this->doRefrBtn) { $htRefrBtn = '<input type=submit name="btn-go-same" value="Update">'; } else { $htRefrBtn = ; } $out = $htMissing

.''; // close table row opened by RenderContentHdr() echo $out; echo "\n
$htNavBar
'

."\n\n"; return $out;

   }
   /*----
     PURPOSE: Close table row opened in RenderContentHdr(), display standard buttons, close outer table and form
   */
   protected function RenderContentFtr() {

echo "\n\n"; $intColumns = self::$intColumns;

if ($this->FieldsMissing() && ($this->pgShow == $this->pgData)) { // NOTE: I've been unable to get the icon to align nicely with the text without using a table.

$htMissing = "
\n\n\n
<img src=".'"'.KWP_ICON_ALERT .'">Please fill in the following: '

.$this->strMissing

."
\n
'

.'<input type=hidden name="'.KSQ_ARG_PAGE_DATA.'" value="'.$this->pgShow.'">' .$htBackBtn.$htRefrBtn .'<input type=submit name="btn-go-next" value="Next >">'; } else { $out = ; }

echo '
\n</form>\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\n
";

$out .= $this->CartObj()->RenderCore(TRUE);

$out .= "
\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 =

'<input name="'

.KSF_SHIP_IS_CARD .'" type=checkbox' .$htIsCardVal

.'>The above ↑ is also my credit card billing address.'; return $out; } private function RenderForm_ToSelf_ckbox($iOn) { $htToSelfVal = $iOn?' checked':; $out = '<input name="'

.KSF_SHIP_TO_SELF .'" type=checkbox' .$htToSelfVal

.'>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 = '<input name="'

.KSF_SHIP_IS_CARD .'" type=submit value="' .$txtIsCard

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

.KSF_SHIP_TO_SELF .'" type=submit value="' .$txtToSelf

.'">'; return $out; }
  • /
protected static function RenderSectionHdr($iTitle) { $out = "\n" ."\n";

return $out;

   }
   protected static function RenderSectionFtr() {
$out = "\n
$iTitle
";

return $out;

   }
   protected static function RenderPaymentIcons() {

$out = '<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">'; 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 = '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__

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.

Special Instructions:

<textarea name="$ksShipMsg" cols=50 rows=5>$strCustShipMsg</textarea> __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()

."
"

.'<input name=payType value="'.KSF_PTYP_CARD_HERE.'" type=radio checked disabled> Visa / MasterCard / Discover / American Express - pay here' .'
 <input name="'.KSF_SHIP_IS_CARD.'" type=checkbox'.$htChecked.'>billing address is same as shipping address above'

."
\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"> 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">

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 .= 'ITEMS ORDERED:'.$htLink.''; $out .= "\n\n";

$out .= $objCart->RenderConfirm();

$out .= "\n
\n";

if ($iEditable) { $htLink = $this->HtmlEditLink(KSQ_PAGE_SHIP); }

$out .= 'SHIP TO:'.$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__

Special Instructions:

$strCustShipMsg PAYMENT:$htLink 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__

<input type=hidden name="$sPgName" value="$sPgShow"> <input type=submit name="btn-go-prev" value="<< Make Changes"> <input type=submit name="btn-go-order" value="Place the Order!"> __END__; }

//$out .= "\n\n</form>\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);

   }

} </php>