VbzCart/code/lib/base.cat

Code
Name('cat_pages');	// cache //$this->Name('qryCat_pages');	// live data $this->KeyName('AB'); $this->ClassSng('clsCatPage'); }   public function GetItem_byKey($iKey) { CallEnter($this,__LINE__,__CLASS__.'.GetItem_byKey('.$iKey.')'); $strKey = trim($iKey,'/'); $strKey = str_replace('-','/',$strKey); $sqlCatKey = $this->objDB->SafeParam($strKey); // This function is named wrong, and needs to be rewritten anyway //	$this->Touch('clsCatPages.GetItem_byKey('.$iKey.')'); $objItem = $this->GetData('Path="'.$sqlCatKey.'"'); //   $objRec = $this->objDB->Query($sql); assert('is_object($objItem)'); if ($objItem->NextRow) { DumpValue('objItem NumRows',$objItem->hasRows); CallExit('clsCatPages.GetItem_byKey('.$iKey.') -> Page '.$objItem->AB); } else { CallExit('clsCatPages.GetItem_byKey('.$iKey.') -> no data'); }	return $objItem; } } // just for paral;ellism, at this point class clsCatPage extends clsDataSet { }

class clsSuppliers extends clsVbzTable { // ==== STATIC SECTION

// ==== DYNAMIC SECTION public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_supp'); $this->KeyName('ID'); $this->ClassSng('clsSupplier'); $this->ActionKey('supp'); }   public function GetItem_byKey($iKey) { CallEnter($this,__LINE__,__CLASS__.'.GetItem_byKey('.$iKey.')'); $sqlCatKey = $this->objDB->SafeParam($iKey); $objItem = $this->GetData('CatKey="'.$sqlCatKey.'"'); CallExit(__CLASS__.'.GetItem_byKey('.$iKey.') -> new supplier'); return $objItem; }   /*      HISTORY 2010-11-12 disabled automatic cache update */   protected function DataSet_forStore($iClass=NULL) { //$objCache = $this->objDB->CacheMgr; //$objCache->Update_byName('_supplier_ittyps','clsSuppliers.DoHomePage'); $sql = 'SELECT * FROM _supplier_ittyps ORDER BY Name, ItemCount DESC'; $objRows = $this->objDB->DataSet($sql,$iClass);

return $objRows; } }

class clsSupplier extends clsDataSet { /*     ACTION: Finds the Item for this Supplier with the given supplier CatNum RETURNS: object of type requested by user; defaults to clsItem. NULL if not found. DEPRECATED -- use GetItem_bySCatNum */   public function GetItem_bySuppCatNum($iCatNum,$iClass=NULL) { return $this->GetItem_bySCatNum($iCatNum); }   /*      ACTION: Checks the given catalog number to see if it corresponds to a given item for the current supplier INPUT: supplier catalog number OUTPUT: item object (if found) or NULL (if not found) HISTORY: 2011-01-09 Moved here from VbzAdminSupplier; replaces GetItem_bySuppCatNum */   public function GetItem_bySCatNum($iSCat) { $objTblItems = $this->objDB->Items;

$sqlFind = '(ID_Supp='.$this->ID.') AND (Supp_CatNum="'.$iSCat.'")'; $objItem = $objTblItems->GetData($sqlFind); if ($objItem->HasRows) { $objItem->NextRow; return $objItem; } else { return NULL; }   }    /*      ACTION: Finds the Title for this Supplier with the given CatKey RETURNS: object of type requested by user; defaults to clsVbzTitle. NULL if not found. HISTORY: 2010-11-07 Created for Title editing page -- need to check for duplicate CatKey before saving. */   public function GetTitle_byCatKey($iCatKey,$iClass='clsVbzTitle') { $sqlCatKey = $this->objDB->SafeParam($iCatKey); $sqlFilt = '(ID_Supplier='.$this->ID.') AND (CatKey="'.$sqlCatKey.'")'; $objTitle = $this->objDB->Titles_Cat->GetData($sqlFilt,$iClass); if ($objTitle->HasRows) { $objTitle->NextRow; return $objTitle; } else { return NULL; }   }    /*      ACTION: Searches for Titles whose CatKeys include the given string PURPOSE: used during renaming of supplier-recycled catalog numbers, so we can see if that number has been recycled before and avoid having to repeatedly try new names HISTORY: 2010-11-08 Created for title editing page */   public function GetTitles_byCatKey($iCatKey,$iClass='clsVbzTitle') { $sqlCatKey = $this->objDB->SafeParam($iCatKey); $sqlFilt = '(ID_Supplier='.$this->ID.') AND (CatKey LIKE "%'.$sqlCatKey.'%")'; $objTitle = $this->objDB->Titles_Cat->GetData($sqlFilt,$iClass); if ($objTitle->HasRows) { $objTitle->NextRow; return $objTitle; } else { return NULL; }   }    protected function DeptsData_forCount($iClass='clsDept') { $objTbl = $this->objDB->Depts; $objRows = $objTbl->GetData('isActive AND (ID_Supplier='.$this->ID.')',$iClass,'Sort'); return $objRows; }   /*      ACTION: builds an array of item type data for the supplier, broken down by department. Caches the results in memory. USED BY: $this->DoPiece_ItTyp_Summary, $this->DoPiece_Dept_ItTyps RETURNS: array of data for the current supplier array[rows] = source dataset -- each row is an ItTyp within a Department array[depts][ID_Dept][ID_ItTyp] = count of items for sale by department and item type array[supp][ID_ItTyp] = count of items for sale by item type HISTORY: 2011-02-02 switched data source from qryItTypsDepts_ItTyps to _dept_ittyps Page was not displaying at all. Some additional changes were necessary. */   protected function DeptsData_forStore { if (is_null($this->arDeptsData)) { //$objRows = $this->objDB->DataSet('SELECT * FROM qryItTypsDepts_ItTyps WHERE ID_Supplier='.$this->ID); $objRows = $this->objDB->DataSet('SELECT * FROM _dept_ittyps WHERE ID_Supp='.$this->ID); while ($objRows->NextRow) { $idItTyp = $objRows->ID_ItTyp; $intCntForSale = $objRows->cntForSale;

if (!isset($arObjs[$idItTyp])) { $objItTyp = $this->Engine->ItTyps->SpawnItem; $arObjs[$idItTyp] = $objItTyp;

$objItTyp->Row['NameSng'] = $objRows->Value('ItTypNameSng'); $objItTyp->Row['NamePlr'] = $objRows->Value('ItTypNamePlr'); $objItTyp->Row['cntForSale'] = 0;	// initialize the count }   // accumulate the list of everything this supplier has: $idSupp = $objRows->ID_Supplier; $objItTyp->Row['cntForSale'] += $intCntForSale; // accumulate the department listing: $idDept = $objRows->Value('ID_Dept'); $arDeptCntForSale[$idDept][$idItTyp] = $intCntForSale; }	   $arOut['rows'] = $objRows; $arOut['depts'] = $arDeptCntForSale; $arOut['supp'] = $arObjs; $this->arDeptsData = $arOut; }	return $this->arDeptsData; }   /*      ACTION: Generates the item-type-count summary for the Supplier's index page */   public function DoPiece_ItTyp_Summary { $arData = $this->DeptsData_forStore; $arObjs = $arData['supp'];

$outRow = ''; foreach ($arObjs as $id=>$obj) { $objTyp = $obj; $cnt = $objTyp->Value('cntForSale'); if ($cnt > 0) { $strType = $objTyp->Name($cnt); if ($outRow != '') { $outRow .= ', '; }		$outRow .= ''.$cnt.' '.$strType; }	}	$out = ' '.$outRow.' '; return $out; }   public function DoPage { $out = ''; assert('$this->ID');

// first, check how many departments supplier has: //$objDeptTbl = $this->objDB->Depts; //$objDepts = $objDeptTbl->GetData('isActive AND (ID_Supplier='.$this->ID.')','clsDept','Sort'); $objDepts = $this->DeptsData_forCount; $intRows = $objDepts->RowCount;

if ($intRows == 1) { // if there's only one department, display that instead of a department listing $objDepts->NextRow;	// get the first/only dept $out = $objDepts->DoPage; } else { $out .= $this->DeptsPage_forStore; }

return $out; }

public function ShopLink($iText=NULL) { if (is_null($iText)) { $strText = $this->Name; } else { $strText = $iText; }	$out = 'URL.'">'.$strText.''; return $out; }   public function Link { return $this->ShopLink; } public function URL { return KWP_CAT_REL.strtolower($this->CatKey).'/'; } }

/*%%%% PURPOSE: extends clsSuppliers to handle store UI interactions class clsSuppliers_StoreUI extends clsSuppliers { private $objPage;

public function __construct($iDB) { parent::__construct($iDB); $this->ClassSng('clsSupplier_StoreUI'); }   public function Page(clsVbzSkin $iPage=NULL) { if (!is_null($iPage)) { $this->objPage = $iPage; }	if (!is_object($this->objPage)) { throw new exception('Internal error: must set page before trying to read it.'); }	return $this->objPage; }   public function DoHomePage { $rc = $this->DataSet_forStore; if ($rc->hasRows) { $objPage = $this->Page; $objPage->NewSection('Suppliers'); $objTbl = $objPage->NewTable; $objTbl->ClassName('catalog-summary'); $strKeyLast = $outCell = ''; while ($rc->NextRow) { $strKey = $rc->Value('CatKey'); if ($strKey != $strKeyLast) { // supplier has changed $strKeyLast = $strKey; $strKeyLink = strtolower($strKey).'/'; if ($outCell) { // dump accumulated list in 2nd column $objRow->NewCell($outCell); $outCell = ''; }		   // start a new row $objRow = $objTbl->NewRow; $objRow->NewCell(''.$rc->Value('Name').''); $isFirst = true; }		if ($isFirst) { $isFirst = false; } else { $outCell .= ', '; }		$strItType = $rc->Value('ItemType'); if ($strItType == '') { $strItType = '?id'.$rc->KeyString; }		$outCell .= ' '.$rc->Value('ItemCount').' '.$strItType; }	   $objRow->NewCell($outCell); }   } }

class clsSupplier_StoreUI extends clsSupplier { /*     HISTORY: 2012-05-10 extracted from clsSuppliers and renamed from DeptsPage_forStore to DoDeptsPage 2012-05-11 no longer returns rendered output, but leaves it in Doc object */   public function DoDeptsPage { $this->DoPiece_ItTyp_Summary; $this->Table->Page->NewSection('Departments:'); $this->DoPiece_Dept_ItTyps; }   /*      ACTION: Generates the table of departments and the summary of items available for each */   public function DoPiece_Dept_ItTyps { $arData = $this->DeptsData_forStore; $arObjs = $arData['supp']; $arDeptCntForSale = $arData['depts']; $objPage = $this->Table->Page;

$objTbl = $objPage->NewTable; $objTbl->ClassName('depts'); $isOdd = FALSE; $fpSupp = KWP_CAT_REL.strtolower($this->Value('CatKey')).'/'; $arAttrCell = array('valign' => 'top'); foreach ($arDeptCntForSale as $idDept=>$arCnts) { $isOdd = !$isOdd;

$outRow = ''; foreach ($arCnts as $id=>$cnt) { if ($cnt > 0) { $objTyp = $arObjs[$id]; $strType = $objTyp->Name($cnt); if ($outRow != '') { $outRow .= ', '; }		   $outRow .= ''.$cnt.' '.$strType; }	   }	    if ($outRow != '') { $objDept = $this->objDB->Depts->GetItem($idDept); $strPageKey = $objDept->PageKey; $strName = $objDept->Name;

$objRow = $objTbl->NewRow; if ($isOdd) { $objRow->ClassName('catalog-stripe'); } else { $objRow->ClassName('catalog'); }

$objCell = $objRow->NewCell(''.$strName.''); $objCell->SetAttrs($arAttrCell); $objCell = $objRow->NewCell($outRow); $objCell->SetAttrs($arAttrCell); }	}   } }

class clsDepts extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_depts'); $this->KeyName('ID'); $this->ClassSng('clsDept'); $this->ActionKey('dept'); }   protected function _newItem { CallStep('clsDepts._newItem'); return new clsDept($this); }

// THIS FUNCTION IS DEPRECATED /*   public function DoListing_forSupp($iSuppID) { CallEnter($this,__LINE__,'clsDepts.DoListing_forSupp('.$iSuppID.')'); //$objRows = $this->objDB->DataSet('SELECT * FROM qryItTypsDepts_ItTyps WHERE ID_Supplier='.$iSuppID); $objRows = $this->Data_forStore; while ($objRows->NextRow) { $idItTyp = $objRows->ID_ItTyp; $intCntForSale = $objRows->cntForSale; $arTypSng[$idItTyp] = $objRows->ItTyp_Sng; $arTypPlr[$idItTyp] = $objRows->ItTyp_Plr;

// accumulate the list of everything this supplier has: $idSupp = $objRows->ID_Supplier; if (isset($arSuppCntForSale[$idItTyp])) { $arSuppCntForSale[$idItTyp] += $intCntForSale; } else { $arSuppCntForSale[$idItTyp] = $intCntForSale; }

// accumulate the department listing: $idDept = $objRows->ID_Dept; $arDeptCntForSale[$idDept][$idItTyp] = $intCntForSale; }

// Now present the accumulated data: // - SUPPLIER: $outRow = ''; foreach ($arSuppCntForSale as $idType=>$cnt) { if ($cnt == 1) { $strType = $arTypSng[$idType]; } else { $strType = $arTypPlr[$idType]; }	   if ($outRow != '') { $outRow .= ', '; }	   $outRow .= ''.$cnt.' '.$strType; }	$out = ' '.$outRow.' ';

// - DEPARTMENTS: $out .= ' Departments: ';

CallExit('clsDepts.DoListing_forSupp'); return $out; } /**/ } class clsDept extends clsDataSet { // object cache private $objSupp;

public function SuppObj { if (is_object($this->objSupp)) { return $this->objSupp; } else { $idSupp = $this->ID_Supplier; if ($idSupp) { $this->objSupp = $this->objDB->Suppliers->GetItem($idSupp); return $this->objSupp; } else { return NULL; }	}   }    // DEPRECATED -- use SuppObj public function Supplier { return $this->SuppObj; }   public function PageKey { if ($this->PageKey) { return $this->PageKey; } else { return $this->CatKey; }   }    /*      PURPOSE: loads data needed to display catalog views for this department HISTORY 2010-11-12 disabled automatic cache update 2010-11-16 changed sorting field from cntInPrint to cntForSale 2011-02-02 using _dept_ittyps now instead of qryItTypsDepts_ItTyps Also added "AND (cntForSale)" to WHERE clause -- not listing titles with nothing to sell */   protected function Data_forStore {	// was GetDeptData //$objCache = $this->objDB->CacheMgr; //$objCache->Update_byName('_dept_ittyps','clsDept.DoListing for ID='.$this->ID); //$sql = 'SELECT * FROM qryItTypsDepts_ItTyps WHERE (ID_Dept='.$this->ID.') ORDER BY cntForSale DESC'; $sql = 'SELECT * FROM _dept_ittyps WHERE (ID_Dept='.$this->ID.') AND (cntForSale) ORDER BY cntForSale DESC'; $objItTyps = $this->objDB->DataSet($sql,'clsItTyp'); return $objItTyps; }   /*-      PURPOSE: Print this department's information as part of department list HISTORY: 2010-11-16 $cntAvail is now cntForSale, not cntInPrint+qtyInStock */   public function DoListing { assert('$this->ID'); $objItTyps = $this->Data_forStore; $isFirst = true; $out = ''; while ($objItTyps->NextRow) { if ($isFirst) { $isFirst = false; } else { $out .= ', '; }	   $cntInPrint = $objItTyps->cntInPrint; $qtyInStock = $objItTyps->qtyForSale; //$cntAvail = $cntInPrint + $qtyInStock; $cntForSale = $objItTyps->cntForSale; if ($cntAvail == 1) { $strName = $objItTyps->ItTyp_Sng; } else { $strName = $objItTyps->ItTyp_Plr; }	   $out .= ' '.$cntAvail.' '.$strName; }	return $out; }   /*      PURPOSE: Print page for current department ACTION: * Iterates through item types available for this department. * For each item type, prints header and then a list of titles. HISTORY: 2010-11-?? Started using cached table _title_ittyps instead of qryTitles_ItTyps_Titles 2010-11-16 $cntAvail is now cntForSale, not cntInPrint+qtyInStock 2011-02-02 $qtyInStock now set to Row['qtyInStock'], not Row['qtyForSale'] which didn't make sense anyway */   public function DoPage { $out = ''; $idDept = $this->ID; if (empty($idDept)) { throw new exception('Department object has no ID'); }	$objSection = new clsPageOutput; // calculate the list of item types available in this department $objItTyps = $this->Data_forStore; $objTitles = new clsVbzTitle($this->objDB); $objNoImgSect = new clsPageOutput; $cntSections = 0; while ($objItTyps->NextRow) { $cntInPrint = $objItTyps->Row['cntInPrint']; $qtyInStock = $objItTyps->Row['qtyInStock']; $cntAvail = $objItTyps->Row['cntForSale']; if ($cntAvail) { $cntSections++; $idItTyp = $objItTyps->Row['ID_ItTyp']; $sql = 'SELECT *, ID_Title AS ID, TitleName AS Name, cntInStock FROM _title_ittyps WHERE ((cntForSale) AND (ID_ItTyp='.$idItTyp.') AND (ID_Dept='.$idDept.'));'; //$sql = 'SELECT t.ID_Title AS ID, t.* FROM qryTitles_ItTyps_Titles AS t WHERE (ID_ItTyp='.$idItTyp.') AND (ID_Dept='.$idDept.');'; $objTitles->Query($sql); $arTitles = NULL; if ($objTitles->hasRows) { while ($objTitles->NextRow) { // add title to display list $arTitles[] = $objTitles->Values;	// save it in a list }		   assert('is_array($arTitles)');

// We've generated the list of titles for this section; now display the section header and titles: $out .= $this->objDB->ShowTitles($objItTyps->Row['ItTypNamePlr'].':',$arTitles,$objNoImgSect); } else { echo 'ERROR: No titles found! SQL='.$sql; }		$objSection->Clear; } else { $out .= ' Small coding error: this line should never happen. '; // TO DO: log an error }	}	if (!$cntSections) { $out .= ' This department appears to have been emptied of all leftover stock. (Eventually there will be a way to see what items used to be here.) '; }	if ($objNoImgSect->inTbl) { $objNoImgSect->EndTable; $objSection->AddText($objNoImgSect->out); $objSection->EndTable; $out .= $objSection->out; }	return $out; }   public function URL_Rel { $strURL = $this->Supplier->URL; $strKey = $this->PageKey; if ($strKey) { $strURL .= strtolower($strKey).'/'; }	return $strURL; }   public function URL_Abs { return KWP_ROOT.$this->URL_Rel; }   public function LinkName { $strURL = $this->URL_Rel; return ''.$this->Name.''; }   /*-      RETURNS: The string which, when prepended to a Title's CatKey, would form the Title's catalog number */   public function CatPfx { $strFull = strtoupper($this->Supplier->CatKey); if ($this->AffectsCatNum) { $strFull .= '-'.strtoupper($this->CatKey); }	return $strFull.'-'; }   /*-      RETURNS: TRUE if this department affects the catalog number (i.e. if CatKey is non-blank) */   public function AffectsCatNum { return ($this->CatKey != ''); } }

class clsVbzTitles extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_titles'); $this->KeyName('ID'); $this->ClassSng('clsVbzTitle'); }   public function Search_forText_SQL($iFind) { return '(Name LIKE "%'.$iFind.'%") OR (`Desc` LIKE "%'.$iFind.'%")'; }   public function Search_forText($iFind) { $sqlFilt = $this->Search_forText_SQL($iFind); $rs = $this->GetData($sqlFilt); return $rs; } /* ACTION: Finds a Title from a CatNum and returns an object for it TO DO: Rename to Get_byCatNum Stop using v_titles /* 2010-11-07 Is anything actually using this? public function GetItem_byCatNum($iCatNum) { global $objDataMgr;

CallEnter($this,__LINE__,__CLASS__.'.GetItem_byCatNum('.$iCatNum.')'); assert('is_object($this->objDB)'); $sqlCatNum = strtoupper(str_replace('.','-',$iCatNum)); $sqlCatNum = $this->objDB->SafeParam($sqlCatNum); $sql = 'SELECT * FROM v_titles WHERE CatNum="'.$sqlCatNum.'"'; //   $objTitle = new clsVbzTitleExt($this); $objTitle = new clsVbzTitle($this); // make sure _titles (part of v_titles) is up-to-date //$objDataMgr->Update_byName('_titles','GetItem_byCatNum('.$iCatNum.')'); $objDataMgr->Update_byName('_depts','GetItem_byCatNum('.$iCatNum.')'); // get data from v_titles $objTitle->Query($sql); $idTitle = $objTitle->ID; assert('is_resource($objTitle->Res)'); if ($objTitle->RowCount) { assert('$idTitle'); $sql = 'SELECT * FROM titles WHERE ID='.$idTitle; $objTitle->dontLoadBasic = true; $objTitle->Query($sql); CallExit('clsVbzTitles.GetItem_byCatNum -> ok'); return $objTitle; } else { CallExit('clsVbzTitles.GetItem_byCatNum -> NULL'); return NULL; } } } class clsVbzTitle extends clsDataSet { // object cache private $objDept; private $objSupp; // options public $hideImgs;

public function Dept { $doLoad = FALSE; if (empty($this->objDept)) { $doLoad = TRUE; } else if (is_object($this->objDept)) { if ($this->ID_Dept != $this->objDept->ID) { $doLoad = TRUE; }	} else { $doLoad = TRUE; }	if ($doLoad) { $idDept = $this->ID_Dept; if (empty($idDept)) { $objDept = NULL; } else { $objDept = $this->objDB->Depts->GetItem($idDept); assert('is_object($objDept)'); }	   $this->objDept = $objDept; }	return $this->objDept; }   /*      RETURNS: ID of this title's supplier HISTORY: 2011-09-28 revised to get ID directly from the new ID_Supp field instead of having to look up the Dept and get it from there. */   public function Supplier_ID { /*	$objDept = $this->Dept; $idSupp = $objDept->ID_Supplier; $idSupp = $this->Value('ID_Supp'); return $idSupp; }   // DEPRECATED -- use SuppObj public function Supplier { return $this->SuppObj; }   public function SuppObj { $doLoad = FALSE; if (empty($this->objSupp)) { $doLoad = TRUE; } else if (is_object($this->objSupp)) { if ($this->ID_Supplier != $this->objSupp->ID) { $doLoad = TRUE; }	} else { $doLoad = TRUE; }	if ($doLoad) { $idSupp = $this->Supplier_ID; if (empty($idSupp)) { $objSupp = NULL; } else { $objSupp = $this->objDB->Suppliers->GetItem($idSupp); assert('is_object($objSupp)'); }	   $this->objSupp = $objSupp; }	return $this->objSupp; }   public function Items { $sqlFilt = 'ID_Title='.$this->ID; $objTbl = $this->objDB->Items; $objRows = $objTbl->GetData($sqlFilt); return $objRows; }   public function Topics { $objTbl = $this->Engine->TitleTopic_Topics; $objRows = $objTbl->GetTitle($this->KeyValue); return $objRows; }   public function DoPage { $idTitle = $this->KeyValue; assert('$idTitle'); $objSection = new clsPageOutput;

// show "small" images for this title if (!$this->hideImgs) { $ht = $this->ShopPage_Images; if (is_null($ht)) { $objSection->ShowImgUnavail; } else { $objSection->AddText($ht); }	}

// list topics for this title $out = $this->ShopPage_Topics; $objSection->AddText($out);

// list available items as table $out = $this->ShopPage_Items; $objSection->AddText($out);

/*	if (is_null($out)) { $objSection->SectionHdr('This title is currently unavailable'); }	return $objSection->out."\n\n"; }   protected function ShopPage_Topics { $db = $this->Engine; $tbl = $db->TitleTopic_Topics; $tbl->doBranch(TRUE); $rs = $tbl->GetTitle($this->KeyValue); if ($rs->hasRows) { $txt = ' '; return $txt; } else { return NULL; }   }    protected function ShopPage_Images { $objImgs = $this->ListImages('sm'); $out = NULL; if ($objImgs->hasRows) { while ($objImgs->NextRow) { $strImgTag = $objImgs->AttrDispl; $urlRel = $objImgs->Spec; $idImg = $objImgs->ID; //$strImg = 'WebSpec.'"'; if ($strImgTag) { $strImg .= ' title="'.$strImgTag.'"'; }		$strImg .= '>'; $objImgBig = $objImgs->ImgForSize('big'); if (is_object($objImgBig)) { if ($objImgBig->FirstRow) { $strImg = $objImgBig->Href.$strImg.''; }		}		$out .= $strImg; }	}	return $out; }   /*      HISTORY: 2012-02-10 began rewriting to go straight to data (no cache) */   protected function ShopPage_Items { $idTitle = $this->KeyValue; $out = NULL;

//$sql = 'SELECT * FROM qryTitles_ItTyps_ItTyps WHERE (ID_Title='.$idTitle.') ORDER BY ItTyp_Sort IS NULL, ItTyp_Sort;'; //$sql = 'SELECT * FROM _title_ittyps WHERE (ID_Title='.$idTitle.') ORDER BY ItTypSort IS NULL, ItTypSort;'; $sql = 'SELECT * FROM' .' (cat_items AS i'	 .' LEFT JOIN cat_ittyps AS it ON i.ID_ItTyp=it.ID)' .' LEFT JOIN cat_ioptns AS io ON i.ID_ItOpt=io.ID' .' WHERE isForSale AND i.ID_Title='.$idTitle .' ORDER BY it.Sort,GrpSort,GrpDescr,i.ItOpt_Sort, io.Sort;';	// sorting may need to be tweaked $tblItems = $this->Engine->Items; $rs = $tblItems->DataSQL($sql); $isItem = FALSE; if ($rs->hasRows) { if (KF_CART_ABSOLUTE) { $urlCart = KWP_CART_ABS; } else { $urlCart = KWP_CART_REL; }	   $out .= '';

$flagDisplayTogether = false;	// hard-coded for now

$txtTblHdr = $tblItems->Render_TableHdr; $txtInStock = $txtOutStock = $txtBoth = '';

$idItTyp_old = NULL; $strGrp_old = NULL; $this->cntInStk = 0; $this->cntOutStk = 0;

while ($rs->NextRow) { $idItTyp = $rs->Value('ID_ItTyp'); if ($idItTyp_old != $idItTyp) { $idItTyp_old = $idItTyp; // new type -- render type headers $ar = $this->ShopPage_Items_TypeSection($rs); $txtInStock .= NzArray($ar,'in'); $txtOutStock .= NzArray($ar,'out'); $txtBoth .= NzArray($ar,'both'); }

$strGrp = $rs->Value('GrpDescr'); $qtyStk = $rs->Value('QtyIn_Stk'); if ($strGrp != $strGrp_old) { $strGrp_old = $strGrp; // new group -- render group header

$strGrpCode = $rs->Value('GrpCode'); $out = ' '; $out .= ' &mdash; '.$strGrp; if ($strGrpCode) { $out .= ' ('.$strGrpCode.' ) '; }		   $out .= ' '; $out .= ' '; // this should probably be a subroutine... if ($flagDisplayTogether) { $txtBoth .= $out; } else { if ($qtyStk > 0) { $txtInStock .= $out; } else { $txtOutStock .= $out; }		   }		} // END new group $txtLine = $rs->Render_TableRow;

if ($flagDisplayTogether) { $txtBoth .= $txtLine; } else { if ($qtyStk > 0) { $txtInStock .= $txtLine; } else { $txtOutStock .= $txtLine; }		}	   } // END while next row

// format all the accumulated bits of text into one large string: $txtTblOpen = ' ';

if ($flagDisplayTogether) { // Display in-stock and backordered items together $out .= $txtTblOpen.$txtTblHdr.$txtBoth.$txtTblFtr.$txtTblShut; } else { if ($this->cntInStk) { $txtClause = Pluralize($qtyStk,'This item is','These items are'); $out .= $txtTblOpen; $out .= ' '.$txtClause.' in stock: '; $out .= $txtTblHdr.$txtInStock.$txtTblFtr.$txtTblShut; }

if ($this->cntOutStk) { if (!empty($txtInStock)) { $out .= ' '; }		   $txtClause = Pluralize($this->cntOutStk,'This item is','These '.$this->cntOutStk.' items are');

$out .= $txtTblOpen; $out .= ' '.$txtClause.' not in stock</a>';

$txtClause = Pluralize($this->cntOutStk,'it','them');

$out .= ', but we can (probably) get '.$txtClause.'</a>: '; $out .= $txtTblHdr.$txtOutStock.$txtTblFtr.$txtTblShut; }	   }	    $out .= ' '; } else { $out .= clsPageOutput::SectionHeader('This title is currently unavailable'); }	return $out; }   protected function ShopPage_Items_TypeSection($rs) { $txtTypPlur = $rs->Value('NamePlr');

$txtLine = "\n<tr class=typeHdr> $txtTypPlur: ";

$flagDisplayTogether = FALSE;	// hard-coded for now if ($flagDisplayTogether) { // displaying all items in a single listing $arOut['both'] = $txtLine; } else { // set flags to determine which stock-status sections to show $qtyInStk = $rs->Value('QtyIn_Stk'); $isForSale = $rs->Value('isForSale'); $isOutStk = (($qtyInStk == 0) && $isForSale);

if ($qtyInStk > 0) { // list in the "in stock" section $arOut['in'] = $txtLine; // add to count of lines in stock $this->cntInStk++; }	   if ($isOutStk) { // list in the "out of stock" section $arOut['out'] = $txtLine; // add to count of lines out of stock $this->cntOutStk++; }	}	return $arOut; }   protected function ShopPage_Items_OLD { $idTitle = $this->KeyValue; $out = NULL;

//$sql = 'SELECT * FROM qryTitles_ItTyps_ItTyps WHERE (ID_Title='.$idTitle.') ORDER BY ItTyp_Sort IS NULL, ItTyp_Sort;'; $sql = 'SELECT * FROM _title_ittyps WHERE (ID_Title='.$idTitle.') ORDER BY ItTypSort IS NULL, ItTypSort;'; $rsTypes = $this->Engine->DataSet($sql); $isItem = FALSE; if ($rsTypes->hasRows) { if (KF_CART_ABSOLUTE) { $urlCart = KWP_CART_ABS; } else { $urlCart = KWP_CART_REL; }	   $out .= '<form method=post action="'.$urlCart.'"><input type=hidden name=from value=browse-multi>';

$flagDisplayTogether = false;	// hard-coded for now

//$objStk = $this->Engine->Items_Stock; $tblItems = $this->Engine->Items; //$txtTblHdr = ' Option Status <th align=right class=title-price>Price <th align=center class=orderQty>Order Qty. <i>list price '; $txtTblHdr = $tblItems->Render_TableHdr; $txtInStock = $txtOutStock = '';

while ($rsTypes->NextRow) { $idItTyp = $rsTypes->Value('ID_ItTyp'); assert('$idItTyp'); $sqlFilt = '(ID_Title='.$idTitle.') AND (ID_ItTyp='.$idItTyp.')'; $sqlSort = 'GrpSort,GrpDescr,ItOpt_Sort'; $rsItems = $tblItems->GetData($sqlFilt,NULL,$sqlSort); //$idItType = 0;

$txtLine = '<tr class=typeHdr> '.$rsTypes->Value('ItTypNamePlr').': ';

if ($flagDisplayTogether) { // displaying all items in a single listing $txtBoth .= $txtLine; } else { // set flags to determine which stock-status sections to show $cntInStock = $rsTypes->Value('cntInStock'); $cntForSale = $rsTypes->Value('cntForSale'); $cntOutStock = $cntForSale - $cntInStock;

if ($cntInStock > 0) { $txtInStock .= $txtLine; }		   if ($cntOutStock > 0) { $txtOutStock .= $txtLine; }		} // iterate through items for this type: $strGrpLast = ''; while ($rsItems->NextRow) { $strGrp = $rsItems->Value('GrpDescr'); $qtyStk = $rsItems->Value('QtyIn_Stk'); if ($strGrp != $strGrpLast) { $strGrpLast = $strGrp; $strGrpCode = $rsItems->Value('GrpCode'); $out = ' '; $out .= ' &mdash; '.$strGrp; if ($strGrpCode) { $out .= ' <font color=#666666>(<font color=#666699>'.$strGrpCode.' ) '; }			$out .= ' '; $out .= ' '; // this should probably be a subroutine... if ($flagDisplayTogether) { $txtBoth .= $out; } else { if ($qtyStk > 0) { $txtInStock .= $out; } else { $txtOutStock .= $out; }			}		   } //echo ' GOT TO #1 - qty='.$qtyStk; if ($rsItems->Value('isForSale')) { $isItem = TRUE; $txtLine = $rsItems->Render_TableRow;

if ($flagDisplayTogether) { $txtBoth .= $txtLine; } else { if ($qtyStk > 0) { $txtInStock .= $txtLine; } else { $txtOutStock .= $txtLine; }			}		   }		}	    }

if ($isItem) { // format all the accumulated bits of text into one large string: $txtTblOpen = ' ';

if ($flagDisplayTogether) { // Display in-stock and backordered items together $out .= $txtTblOpen.$txtTblHdr.$txtBoth.$txtTblFtr.$txtTblShut; } else { if ($txtInStock != '') { $txtClause = Pluralize($cntInStock,'This item is','These items are'); $out .= $txtTblOpen; $out .= '<tr class=inStock> '.$txtClause.' in stock: '; $out .= $txtTblHdr.$txtInStock.$txtTblFtr.$txtTblShut; }

if (!empty($txtOutStock)) { if (!empty($txtInStock)) { $out .= ' '; }			$txtClause = Pluralize($cntOutStock,'This item is','These items are');

$out .= $txtTblOpen; $out .= ' '.$txtClause.' not in stock</a>';

$txtClause = Pluralize($cntOutStock,'it','them');

$out .= ', but we can (probably) get '.$txtClause.'</a>: '; $out .= $txtTblHdr.$txtOutStock.$txtTblFtr.$txtTblShut; } /**/		}	   } else { $out .= clsPageOutput::SectionHeader('This title is currently unavailable'); }	   $out .= ' '; }	return $out; }   /*      RETURNS: Array containing summary information about this title */   public function Indicia(array $iarAttr=NULL) { $objItems = $this->Items; $intActive = 0; $intRetired = 0; if ($objItems->HasRows) { while ($objItems->NextRow) { if ($objItems->isForSale) { $intActive++; } else { $intRetired++; }	   }	}	// "dark-bg" brings up link colors for a dark background $arLink = array('class'=>'dark-bg'); // merge in any overrides or additions from iarAttr: if (is_array($iarAttr)) { $arLink = array_merge($arLink,$iarAttr); }	$htLink = $this->Link($arLink); $txtCatNum = $this->CatNum; $txtName = $this->Name;

$arOut['cnt.active'] = $intActive; $arOut['cnt.retired'] = $intRetired; $arOut['txt.cat.num'] = $txtCatNum; $arOut['ht.link.open'] = $htLink; $arOut['ht.cat.line'] = $htLink.$txtCatNum.'</a> '.$txtName;

return $arOut; }   /*      RETURNS: Array containing summaries of ItTyps in which this Title is available array['text.!num'] = plaintext version with no numbers (types only) array['text.cnt'] = plaintext version with line counts array['html.cnt'] = HTML version with line counts array['html.qty'] = HTML version with stock quantities HISTORY: 2011-01-23 written */   public function Summary_ItTyps($iSep=', ') { $dsRows = $this->DataSet_ItTyps; $outTextNoQ = $outTextType = $outTextCnt = $outHTMLCnt = $outHTMLQty = NULL; if ($dsRows->HasRows) { $isFirst = TRUE; while ($dsRows->NextRow) { $cntType = $dsRows->Value('cntForSale'); if ($cntType > 0) { $qtyStk = $dsRows->Value('qtyInStock'); $txtSng = $dsRows->Value('ItTypNameSng'); $txtPlr = $dsRows->Value('ItTypNamePlr'); $strType = Pluralize($cntType,$txtSng,$txtPlr); if ($isFirst) { $isFirst = FALSE; } else { $outTextType .= $iSep; $outTextCnt .= $iSep; $outHTMLCnt .= $iSep; if (!is_null($outHTMLQty)) { $outHTMLQty .= $iSep; }		   }		    $outTextType .= $txtSng; $outTextCnt .= $cntType.' '.$strType; $outHTMLCnt .= ''.$cntType.' '.$strType; if (!empty($qtyStk)) { $outHTMLQty .= ''.$qtyStk.' '.Pluralize($qtyStk,$txtSng,$txtPlr); }		}	   }	}	$arOut['text.!num'] = $outTextType; $arOut['text.cnt'] = $outTextCnt; $arOut['html.cnt'] = $outHTMLCnt; $arOut['html.qty'] = $outHTMLQty; return $arOut; } // LATER: change name to DataSet_Images to clarify that this returns a dataset, not a text list or array public function ListImages($iSize) { $sqlFilt = '(ID_Title='.$this->ID.') AND (Ab_Size="'.$iSize.'") AND isActive'; $objImgs = $this->objDB->Images->GetData($sqlFilt,'clsImage','AttrSort'); return $objImgs; }   /*      RETURNS: dataset of item types for this title USES: _title_ittyps (cached table) HISTORY: 2011-01-19 written */   public function DataSet_ItTyps { $sql = 'SELECT * FROM _title_ittyps WHERE ID_Title='.$this->KeyValue; $obj = $this->Engine->DataSet($sql,'clsTitleIttyp'); return $obj; }   /*      HISTORY: 2010-10-19 added optimization to fetch answer from CatKey field if it exists. This may cause future problems. Remove $iSep field and create individual functions if so. 2012-02-02 allowed bypass of Dept if it isn't set */   public function CatNum($iSep='-') { if (empty($this->Row['CatNum'])) {

$objDept = $this->Dept; $objSupp = $this->SuppObj; if (is_object($objDept)) { $strDeptKey = $objDept->CatKey; $strOut = $objSupp->CatKey; if ($strDeptKey) { $strOut .= $iSep.$strDeptKey; }	 } else { if (is_object($objSupp)) { $strOut = $objSupp->CatKey; } else { $strOut = '?'; }	 }	  $strOut .= $iSep.$this->CatKey; } else { $strOut = $this->CatNum; }     return strtoupper($strOut); } public function URL_part { return strtolower($this->CatNum('/')); } public function URL($iBase=KWP_CAT_REL) { return $iBase.$this->URL_part; }   public function Link(array $iarAttr=NULL) { $strURL = $this->URL; $htAttr = ArrayToAttrs($iarAttr); return ''; } public function LinkAbs { $strURL = $this->URL(KWP_CAT); return ''; } public function LinkName { return $this->Link.$this->Name.'</a>'; } /* 2010-11-06 if this is needed, use a method in SpecialVbzAdmin public function LinkName_wt { // TO DO: make this more configurable $out = .$this->Name.; return $out; } }

/*==== PURPOSE: TITLE/ITTYP hybrid TABLE: _title_ittyps class clsTitleIttyp extends clsDataSet { // object cache private $objIttyp;

public function Ittyp { if (is_null($this->objIttyp)) { $this->objIttyp = VbzClasses::ItTyps->GetItem($this->ID_ItTyp); }   return $this->objIttyp; } } /* *\    ITEM classes \* */ class clsItems extends clsVbzTable {

public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_items'); $this->KeyName('ID'); $this->ClassSng('clsItem'); }   /*      ACTION: Finds the Item with the given CatNum, and returns a clsItem object */   public function Get_byCatNum($iCatNum) { $sqlCatNum = $this->objDB->SafeParam(strtoupper($iCatNum)); $objItem = $this->GetData('CatNum="'.$sqlCatNum.'"'); if ($objItem->HasRows) { $objItem->NextRow; return $objItem; } else { return NULL; }   }    public function Search_byCatNum($iCatNum) { $sqlCatNum = $this->objDB->SafeParam(strtoupper($iCatNum)); $objItem = $this->GetData('CatNum LIKE "%'.$sqlCatNum.'%"'); if ($objItem->HasRows) { return $objItem; } else { return NULL; }   }    /*      RETURNS: Table header for list of available items on catalog Title pages HISTORY: 2011-01-24 created/corrected from code in Title page-display function */   public function Render_TableHdr { return ' ' .' Option ' .' Status ' .' <i>List Price ' .'<th align=center class=title-price>Our Price ' .'<th align=center class=orderQty>Order Qty. '	 .' ';    } } /* =============== CLASS: clsItem NOTES: * "in stock" always refers to stock for sale, not stock which has already been purchased * 2009-12-03: The above note does not clarify anything. * Four methods were moved here from clsShopCartLine in shop.php: ItemSpecs, ItemDesc, ItemDesc_ht, ItemDesc_wt They are used for displaying a full description of an item, in both shop.php and SpecialVbzAdmin class clsItem extends clsDataSet { // object cache private $objTitle; private $objItTyp; private $objItOpt;

public function CatNum { return $this->Value('CatNum'); }   public function DescSpecs(array $iSpecs=NULL) { if (is_null($iSpecs)) { $this->objTitle	= $this->Title; $this->objItTyp	= $this->ItTyp; $this->objItOpt	= $this->ItOpt;

$out['tname']	= $this->objTitle->Name; $out['ittyp']	= $this->objItTyp->Name($this->Qty); $out['itopt']	= $this->objItOpt->Descr; return $out; } else { return $iSpecs; }   }    public function DescLong(array $iSpecs=NULL) {	// plaintext if (is_null($this->Value('Descr'))) { $sp = $this->DescSpecs($iSpecs);

$strItOpt = $sp['itopt'];

$out = '"'.$sp['tname'].'" ('.$sp['ittyp'];	   if (!is_null($strItOpt)) {		$out .= ' - '.$strItOpt;	    }	    $out .= ')'; } else { $out = $this->Value('Descr'); }

return $out; }   public function DescLong_ht(array $iSpecs=NULL) {	// as HTML $sp = $this->DescSpecs($iSpecs);

$htTitleName = '.$this->Title->LinkName.'; $strItOpt = $sp['itopt'];

$out = $htTitleName.' ('.$sp['ittyp'];	if (!is_null($strItOpt)) {	   $out .= ' - '.$strItOpt;	}	$out .= ')';

return $out; }   /*-      ASSUMES: This item is ForSale, so isForSale = true and (qtyForSale>0 || isInPrint) = true HISTORY: 2011-01-24 Renamed Print_TableRow -> Render_TableRow; corrected to match header */   public function Render_TableRow { $arStat = $this->AvailStatus; $strCls = $arStat['cls'];

$out = '<tr class='.$strCls.'>'; $out .= ' &emsp;'.$this->Value('ItOpt_Descr').' '; $out .= ' '.$arStat['html'].' '; $out .= ' .DataCurr($this->Value('PriceList')). '; $out .= ' '.DataCurr($this->Value('PriceSell')).' '; $out .= ' '.'<input size=3 name="qty-'.$this->Value('CatNum').'"> '; $out .= ' '; return $out; }   /*      ACTION: Returns an array with human-friendly text about the item's availability status RETURNS: array['html']: status text, in HTML format array['cls']: class to use for displaying item row in a table USED BY: Render_TableRow NOTE: This probably does not actually need to be a separate method; I thought I could reuse it to generate status for titles, but that doesn't make sense. Maybe it will be easier to adapt, though, as a separate method. HISTORY: 2010-11-16 Modified truth table for in-print status so that if isInPrint=FALSE, then status always shows "out of print" even if isCurrent=FALSE. What happens when a supplier has been discontinued? Maybe we need to check that separately. Wait for an example to come up, for easier debugging. 2011-01-24 Corrected to use cat_items fields */   private function AvailStatus { //echo 'SQL=['.$this->sqlMake.']'; //echo ' '.print_r($this->Row,TRUE).' '; $qtyInStock = $this->Value('QtyIn_Stk'); if ($qtyInStock) { $strCls = 'inStock'; $strStk = $qtyInStock.' in stock'; } else { $strCls = 'noStock'; $strStk = 'none in stock'; }	$isInPrint = $this->Value('isInPrint'); if ($isInPrint) { if ($this->Value('isCurrent')) { if ($qtyInStock) { $txt = $strStk.'; more available'; } else { $txt = 'available, not in stock</a>'; }	   } else { if ($qtyInStock) { $txt = $strStk.'; in-print status uncertain'; } else { $txt = $strStk.'; availability uncertain'; }	   }	} else { if (is_null($isInPrint)) { $txt = .$strStk.' - possibly out of print'''; } else { $txt = .$strStk.' - out of print!'''; }	}	$arOut['html'] = $txt; $arOut['cls'] = $strCls; return $arOut; }

// DEPRECATED - use TitleObj public function Title { return $this->TitleObj; }   public function TitleObj { $doLoad = TRUE; if (is_object($this->objTitle)) { if ($this->objTitle->ID == $this->ID_Title) { $doLoad = FALSE; }	}	if ($doLoad) { $this->objTitle = $this->objDB->Titles->GetItem($this->ID_Title); }	return $this->objTitle; } public function Supplier { return $this->TitleObj->Supplier; } public function ItTyp { $doLoad = TRUE; if (is_object($this->objItTyp)) { if ($this->objItTyp->ID == $this->ID_ItTyp) { $doLoad = FALSE; }     }      if ($doLoad) { $this->objItTyp = $this->objDB->ItTyps->GetItem($this->ID_ItTyp); }     return $this->objItTyp; } public function ItOpt { $doLoad = TRUE; if (is_object($this->objItOpt)) { if ($this->objItOpt->ID == $this->ID_ItOpt) { $doLoad = FALSE; }   }    if ($doLoad) { $this->objItOpt = $this->objDB->ItOpts->GetItem($this->ID_ItOpt); }   return $this->objItOpt; }   // DEPRECATED - use ShipCostObj public function ShCost { return $this->ShipCostObj; }   /*      HISTORY: 2010-10-19 created from contents of ShCost */   public function ShipCostObj { $doLoad = FALSE; if (empty($this->objShCost)) { $doLoad = TRUE; } elseif ($this->objShCost->ID != $this->ID_ShipCost) { $doLoad = TRUE; }	if ($doLoad) { $this->objShCost = $this->objDB->ShipCosts->GetItem($this->ID_ShipCost); }	return $this->objShCost; }   /*      RETURNS: The item's per-item shipping price for the given shipping zone FUTURE: Rename to ShPerItm_forZone */   public function ShipPriceItem($iZone) { global $listItmFactors;

$fltZoneFactor = $listItmFactors[$iZone]; $objSh = $this->ShipCostObj; return $objSh->PerItem * $fltZoneFactor; }   /*      RETURNS: The item's per-package shipping price for the given shipping zone FUTURE: Rename to ShPerPkg_forZone */   public function ShipPricePkg($iZone) { global $listPkgFactors;

$fltZoneFactor = $listPkgFactors[$iZone]; return $this->ShipCostObj->PerPkg * $fltZoneFactor; }   /*      RETURNS: The item's per-item shipping price, with no zone calculations FUTURE: need to handle shipping zone more gracefully and rigorously This function is currently only used in the admin area, so does not need to be infallible. */   public function ShPerItm { return $this->ShipCostObj->Value('PerItem'); }   /*      RETURNS: The item's per-package shipping price, with no zone calculations FUTURE: need to handle shipping zone more gracefully and rigorously This function is currently only used in the admin area, so does not need to be infallible. */   public function ShPerPkg { return $this->ShipCostObj->Value('PerPkg'); } } /*==== PURPOSE: clsItems with additional catalog information class clsItems_info_Cat extends clsItems { public function __construct($iDB) { parent::__construct($iDB); $this->Name('qryCat_Items'); //$this->ClassSng('clsItem_info_Cat'); } } /* *\    ITEM TYPE classes \* */ class clsItTyps extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_ittyps'); $this->KeyName('ID'); $this->ClassSng('clsItTyp'); }   // BOILERPLATE - cache protected $objCache; protected function Cache { if (!isset($this->objCache)) { $this->objCache = new clsCache_Table($this); }	return $this->objCache; }   public function GetItem_Cached($iID=NULL,$iClass=NULL) { return $this->Cache->GetItem($iID,$iClass); } /*   public function GetData_Cached($iWhere=NULL,$iClass=NULL,$iSort=NULL) { return $this->Cache->GetData($iWhere,$iClass,$iSort); }   /*      FUTURE: * This method really belongs with Admin functions, since it will never be used in the standalone store * If table ever grows to a significant size, we might end up changing the filtering criteron. HISTORY: 2010-11-21 Adapted from clsFolders. */   public function DropDown($iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; $arRows = $this->Cache->GetData_array('isType',NULL,'Sort, NameSng'); $out = $this->DropDown_for_array($arRows,$strName,$iDefault,$iChoose); return $out; }   /*      ACTION: same as clsItTyp::DropDown_for_rows, but takes an array HISTORY: 2011-02-11 wrote */   public function DropDown_for_array(array $iRows,$iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; $objRow = $this->SpawnItem; foreach($iRows as $key => $row) { $objRow->Values($row); $arList[$key] = $objRow->Name; }	return DropDown_arr($strName,$arList,$iDefault,$iChoose); } } /*==== CLASS: Item Type (singular) class clsItTyp extends clsDataSet_bare { /*     HISTORY: 2011-02-02 removed the IsNew check because sometimes we want to use this on data which has not been associated with an ID   */ public function Name($iCount=NULL) { if (is_null($iCount)) { if (isset($this->Row['cntInPrint'])) { $iCount = $this->Row['cntInPrint']; } else { $iCount = 1;	// default: use singular }	}	$strSng = NzArray($this->Row,'NameSng'); if ($iCount == 1) { $out = $strSng; } else { $out = NzArray($this->Row,'NamePlr',$strSng); }	return $out; }   /*      ACTION: Shows a drop-down selection box contining the rows in the current dataset FUTURE: * This method really belongs with Admin functions, since it will never be used in the standalone store */   public function DropDown_for_rows($iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; if ($this->HasRows) { $out = '<select name="'.$strName.'">'; if (!is_null($iChoose)) { $out .= ' '.$iChoose.' '; }	   while ($this->NextRow) { $id = $this->Row['ID']; if ($id == $iDefault) { $htSelect = " selected"; } else { $htSelect = ''; }		$out .= '<option'.$htSelect.' value="'.$id.'">'.$this->Name.' '; }	   $out .= ' '; } else { $out = 'No item types found.'; }	return $out; } } /* *\    ITEM OPTION classes \* */ class clsItOpts extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_ioptns'); $this->KeyName('ID'); $this->ClassSng('clsItOpt'); }   // ==BOILERPLATE - cache protected $objCache; protected function Cache { if (!isset($this->objCache)) { $this->objCache = new clsCache_Table($this); }	return $this->objCache; }   public function GetItem_Cached($iID=NULL,$iClass=NULL) { return $this->Cache->GetItem($iID,$iClass); }   // ==/BOILERPLATE /*     FUTURE: * This method really belongs with Admin functions, since it will never be used in the standalone store * If table ever grows to a significant size, we might end up changing the filtering criteron. * Actually, this should be a boilerplate function with a helper class. The only change from clsItTyps is the GetData filter and sorting. HISTORY: 2010-11-21 Adapted from clsItTyps */   public function DropDown($iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; $arRows = $this->Cache->GetData_array(NULL,NULL,'Sort'); $out = $this->DropDown_for_array($arRows,$strName,$iDefault,$iChoose); return $out; }   /*      ACTION: same as clsItTyp::DropDown_for_rows, but takes an array HISTORY: 2011-02-11 wrote */   public function DropDown_for_array(array $iRows,$iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; $objRow = $this->SpawnItem; foreach($iRows as $key => $row) { $objRow->Values($row); $arList[$key] = $objRow->ChoiceLine; }	return DropDown_arr($strName,$arList,$iDefault,$iChoose); } } class clsItOpt extends clsDataSet { /*     RETURNS: Approximately as much description as will fit nicely into a choice line for a drop-down or selection box */   public function ChoiceLine { return $this->Value('CatKey'); }   /*      RETURNS: A longer description for when horizontal space is not tight */   public function DescrFull { return $this->Value('CatKey').' - '.$this->Value('Descr'); } } /* *\    SHIP COST classes \* */ class clsShipCosts extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_ship_cost'); $this->KeyName('ID'); $this->ClassSng('clsShipCost'); }   // ==BOILERPLATE - cache protected $objCache; protected function Cache { if (!isset($this->objCache)) { $this->objCache = new clsCache_Table($this); }	return $this->objCache; }   public function GetItem_Cached($iID=NULL,$iClass=NULL) { return $this->Cache->GetItem($iID,$iClass); }   // ==/BOILERPLATE /*     FUTURE: * This method really belongs with Admin functions, since it will never be used in the standalone store * If table ever grows to a significant size, we might end up changing the filtering criteron. * Actually, this should be a boilerplate function with a helper class. The only change from clsItTyps is the GetData filter and sorting. HISTORY: 2010-11-21 Adapted from clsItTyps */   public function DropDown($iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; $arRows = $this->Cache->GetData_array(NULL,NULL,'Sort'); $out = $this->DropDown_for_array($arRows,$strName,$iDefault,$iChoose); return $out; }   /*      ACTION: same as clsItTyp::DropDown_for_rows, but takes an array HISTORY: 2011-02-11 wrote */   public function DropDown_for_array(array $iRows,$iName=NULL,$iDefault=NULL,$iChoose=NULL) { $strName = is_null($iName)?($this->Table->ActionKey):$iName; $objRow = $this->SpawnItem; foreach($iRows as $key => $row) { $objRow->Values($row); $arList[$key] = $objRow->ChoiceLine; }	return DropDown_arr($strName,$arList,$iDefault,$iChoose); } } class clsShipCost extends clsDataSet { /*     RETURNS: Approximately as much description as will fit nicely into a choice line for a drop-down or selection box */   public function ChoiceLine { return $this->Value('Descr'); } } /* *\    STOCK ITEM classes \* */ /*====  CLASS PAIR: Items_Stock PURPOSE: items with stock information (from a query or cache) Similar to clsItem, but with different fields HISTORY: 2011-01-24 disabled -- use cat_items (has all necessary fields cached) /* class clsItems_Stock extends clsItems { public function __construct($iDB) { parent::__construct($iDB); $this->Name('qryCat_Items_Stock'); //$this->Name('_title_ittyps'); // WRONG! This GROUPS the data, no Items. $this->KeyName('ID'); $this->ClassSng('clsItem_Stock'); }   public function Render_TableHdr { return ' ' .' Option ' .' Status ' .'<th align=right class=title-price>Price ' .'<th align=center class=orderQty>Order Qty. '	 .' <i>list price ' .' ';   } } //class clsItem_Stock extends clsItem { /*-     ASSUMES: This item is ForSale, so isForSale = true and (qtyForSale>0 || isInPrint) = true This item's data was generated by clsItems_Stock TO DO: create a separate clsItem_Stock class and move this method there. TO DO: this method isn't named or structured canonically; consider refactoring. Whatever that means. HISTORY: 2011-01-24 moved from clsItem to clsItem_Stock */ /*   public function Render_TableRow { $arStat = $this->AvailStatus; $strCls = $arStat['cls'];

$out = '<tr class='.$strCls.'>'; $out .= ' &emsp;'.$this->ItOpt_Descr.' '; $out .= ' '.$arStat['html'].' '; $out .= ' '.DataCurr($this->PriceSell).' '; $out .= ' '.'<input size=3 name="qty-'.$this->CatNum.'"> '; if ($this->PriceList) { $out .= ' .DataCurr($this->PriceList). '; }	$out .= ' '; return $out; }   /*      ACTION: Returns an array with human-friendly text about the item's availability status RETURNS: array['html']: status text, in HTML format array['cls']: class to use for displaying item row in a table USED BY: Print_TableRow NOTE: This probably does not actually need to be a separate method; I thought I could reuse it to generate status for titles, but that doesn't make sense. Maybe it will be easier to adapt, though, as a separate method. HISTORY: 2010-11-16 Modified truth table for in-print status so that if isInPrint=FALSE, then status always shows "out of print" even if isCurrent=FALSE. What happens when a supplier has been discontinued? Maybe we need to check that separately. Wait for an example to come up, for easier debugging. 2011-01-24 Adapted from clsItem (was it being used there, for real?) to clsItem_Stock */ /*   private function AvailStatus { //echo 'SQL=['.$this->sqlMake.']'; //echo ' '.print_r($this->Row,TRUE).' '; $qtyInStock = $this->Value('qtyForSale'); if ($qtyInStock) { $strCls = 'inStock'; $strStk = $qtyInStock.' in stock'; } else { $strCls = 'noStock'; $strStk = 'none in stock'; }	if ($this->Value('isInPrint')) { if ($this->Value('isCurrent')) { if ($qtyInStock) { $txt = $strStk.'; more available'; } else { $txt = 'available, not in stock</a>'; }	   } else { if ($qtyInStock) { $txt = $strStk.'; in-print status uncertain'; } else { $txt = $strStk.'; availability uncertain'; }	   }	} else { $txt = .$strStk.' - out of print!'''; }	$arOut['html'] = $txt; $arOut['cls'] = $strCls; return $arOut; } }

/* *\    IMAGE classes \* */ class clsVbzFolders extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_folders'); $this->KeyName('ID'); $this->ClassSng('clsVbzFolder'); }   /*      FUTURE: * This method really belongs with Admin functions, since it will never be used in the standalone store * If table ever grows to a significant size, we might end up changing the filtering criteron. */   public function DropDown($iName,$iDefault=NULL) { $dsRows = $this->GetData('Descr IS NOT NULL'); return $dsRows->DropDown_for_rows($iName,$iDefault); }   /*      PURPOSE: Finds the folder record which matches as much of the given URL as possible RETURNS: object for that folder, or NULL if no match found ASSUMES: folder list is not empty TO DO: does not yet handle adding new folders does not recursively check subfolders for improved match HISTORY: 2011-01-30 created -- subfolders not implemented yet because no data to test with */   public function FindBest($iURL) { if (strlen($iURL) > 0) { $slURL = strlen($iURL); $rs = $this->GetData('ID_Parent IS NULL');	// start with root folders $arrBest = NULL; $slBest = 0; while ($rs->NextRow) { $fp = $rs->Value('PathPart'); $pos = strpos($iURL,$fp);	// does the folder appear in the URL? if ($pos === 0) { $slFldr = strlen($fp); if ($slFldr > $slBest) { $arrBest = $rs->Values; $slBest = $slFldr; }		}	   }	    if (is_array($arrBest)) { $rsFldr = $this->SpawnItem; $rsFldr->Values($arrBest); return $rsFldr; }	}	return NULL; } } class clsVbzFolder extends clsDataSet { public function Spec { $out = ''; if (!is_null($this->ID_Parent)) { $out = $this->ParentObj->Spec; }	$out .= $this->PathPart; return $out; }   protected function ParentObj { return $this->Table->GetItem($this->ID_Parent); }   /*      ACTION: Shows a drop-down selection box contining the rows in the current dataset FUTURE: * This method really belongs with Admin functions, since it will never be used in the standalone store */   public function DropDown_for_rows($iName,$iDefault=NULL) { if ($this->HasRows) { $out = '<select name="'.$iName.'">'; while ($this->NextRow) { if ($this->ID == $iDefault) { $htSelect = " selected"; } else { $htSelect = ''; }		$out .= '<option'.$htSelect.' value="'.$this->ID.'">'.$this->Spec.' '; }	   $out .= ' '; } else { $out = 'No shipments matching filter'; }	return $out; }   /*      RETURNS: The rest of the URL after this folder's PathPart is removed from the beginning USED BY: bulk image entry admin routine */   public function Remainder($iSpec) { $fsFldr = $this->Value('PathPart'); $slFldr = strlen($fsFldr); $fsRest = substr($iSpec,$slFldr); return $fsRest; } } class clsImages extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('cat_images'); $this->KeyName('ID'); $this->ClassSng('clsImage'); }   public function Update(array $iSet,$iWhere) { $iSet['WhenEdited'] = 'NOW'; parent::Update($iSet,$iWhere); $this->Touch(__METHOD__.' WHERE='.$iWhere); }   public function Insert(array $iData) { $iData['WhenAdded'] = 'NOW'; parent::Insert($iData); $this->Touch(__METHOD__); }   public function Thumbnails($iTitle,array $iarAttr=NULL) { $sqlFilt = '(ID_Title='.$iTitle.') AND (Ab_Size="th") AND isActive'; $objTbl = $this->objDB->Images; $objRows = $objTbl->GetData($sqlFilt,NULL,'AttrSort'); return $objRows->Images_HTML($iarAttr); } } class clsImage extends clsVbzRecs { // object cache protected $objTitle;

/*     HISTORY: 2010-11-16 Modified to use new cat_folders data via ID_Folder */   public function WebSpec { //return KWP_IMG_MERCH.$this->Spec; return $this->FolderPath.$this->Spec; }   /*      HISTORY: 2010-11-16 Created */   public function FolderObj { return $this->objDB->Folders->GetItem($this->ID_Folder); }   /*      HISTORY: 2010-11-16 Created */   public function FolderPath { return $this->FolderObj->Spec; }   /*-      ACTION: Generate the HTML code to display all images in the current dataset */   public function Images_HTML(array $iarAttr=NULL) { if ($this->HasRows) { $out = ''; while ($this->NextRow) { $out .= $this->Image_HTML($iarAttr); }	   return $out; } else { return NULL; }   }    /*-      ACTION: Generate the HTML code to display an image for the current row */   public function Image_HTML(array $iarAttr=NULL) { $htDispl = $this->AttrDispl; if (!empty($htDispl)) { nzApp($iarAttr['title'],' - '.$htDispl); }	$iarAttr['src'] = $this->WebSpec; $htAttr = ArrayToAttrs($iarAttr); return '<img'.$htAttr.'>'; }   /*-      ACTION: Get the image with the same title and attribute but with the given size */   public function ImgForSize($iSize) { if ($this->AttrFldr) { $sqlAttr = '="'.$this->AttrFldr.'"'; } else { $sqlAttr = ' IS NULL'; }	$sqlFilt = '(ID_Title='.$this->ID_Title.') AND (AttrFldr'.$sqlAttr.') AND (Ab_Size="'.$iSize.'")'; $objImgOut = $this->objDB->Images->GetData($sqlFilt); return $objImgOut; }   public function Title { if (!is_object($this->objTitle)) { $this->objTitle = $this->objDB->Titles->GetItem($this->ID_Title); }     return $this->objTitle; }   /*-      ACTION: outputs a standalone page for larger image sizes - does not use skin */   public function DoPage { global $vbgImgSize;

$objTitle = $this->Title; $strCatNum = $objTitle->CatNum; $strTitle = $objTitle->Name; $htTitleHref = $objTitle->Link; $htmlTitle = KS_STORE_NAME.' - '.$strCatNum.' &ldquo;'.$strTitle.'&rdquo;'; echo '  '.$htmlTitle.'  '; echo "\n<body" ."\n bgcolor=000044" ."\n TEXT=CCFFFF" ."\n LINK=33FF33" ."\n VLINK=33CCFF" ."\n ALINK=FFCC00" ."\n TOPMARGIN=0" ."\n LEFTMARGIN=0" ."\n MARGINWIDTH=0" ."\n MARGINHEIGHT=0" .'>';	echo ' '; echo ' '.$htTitleHref.$strTitle.'</a> '.$strCatNum.' - title ID #'.$this->ID_Title.' ';

// show list of available image sizes (except th and sm) $objImgs = $this->ListImages_sameAttr; $strSizes = NULL; if ($objImgs->hasRows) { $strImgCount = 0; while ($objImgs->NextRow) { $strImgType = $objImgs->Ab_Size; if (!empty($strImgType)) { if (($strImgType != 'th') && ($strImgType != 'sm')) { $strImgCount++; $strDesc = $vbgImgSize[$strImgType]; if ($objImgs->ID == $this->ID) { $strImgTag = .$strDesc.; } else { $strImgTag = $objImgs->Href(TRUE).$strDesc.'</a>'; }			if (!empty($strSizes)) { $strSizes .= ' .. ';			}			$strSizes .= $strImgTag; }		}	   }	    if ($strImgCount > 1) { $ftSizes = ' ' .' <font color=#aaaaaa>sizes : ' .' '.$strSizes.' ' .' : <font color=#aaaaaa>sizes ' .' ';	   } else { $ftSizes = NULL; }	}

// show list of available images for this title at this size $strAttrs = NULL; $objImgs = $this->ListImages_sameSize; //echo 'test'; if ($objImgs->NextRow) { $intImgs = 0; if ($objImgs->hasRows) { $idx = 0; while ($objImgs->NextRow) { $idx++; $strImgFldr = $objImgs->AttrFldr; $strImgDescr = $objImgs->AttrDispl.'('.$idx.')'; $intImgs++; if (!empty($out)) { $strAttrs .= ' .. ';		   }		    if ($objImgs->ID == $this->ID) { $strAttrs .= .$strImgDescr.; } else { $strAttrs .= $objImgs->Href(TRUE).$strImgDescr.'</a>'; }		}		if ($intImgs > 1) { $ftAttrs = ' ' .' <font color=#aaaaaa>views : ' .' '.$strAttrs.' ' .' : <font color=#aaaaaa>views ' .' ';		} else { $ftAttrs = NULL; }	   }	}	if ((!empty($ftSizes)) || (!empty($ftAttrs))) { echo '  '; } else { echo ' '; }	echo $htTitleHref.'ordering page</a> '; echo $this->Image_HTML; echo "\n \n "; } public function ListImages_sameAttr { $sqlFilt = 'isActive AND (ID_Title='.$this->ID_Title.')'; if ($this->AttrFldr) { $sqlFilt .= ' AND (AttrFldr="'.$this->AttrFldr.'")'; }   $objImgOut = $this->objDB->Images->GetData($sqlFilt);

return $objImgOut; }   public function ListImages_sameSize { $sqlFilt = 'isActive AND (ID_Title='.$this->ID_Title.') AND (Ab_Size="'.$this->Ab_Size.'")'; //echo 'SQL: '.$sqlFilt; $objImgOut = $this->objDB->Images->GetData($sqlFilt); return $objImgOut; }   public function Href($iAbs=false) { $strFldrRel = $this->AttrFldr; if ($strFldrRel) { $strFldrRel .= '-'; }	$strFldrRel .= $this->Ab_Size;

if ($iAbs) { $strFldr = $this->Title->URL.'/'.$strFldrRel; } else { $strFldr = $strFldrRel; }	return ''; } }