VbzCart/docs/archive/code/files/base.cat.php
<php> <?php /*
FILE: base.cat.php -- VbzCart catalog classes HISTORY: 2012-05-08 split off from store.php 2012-05-13 moved clsCatPages here from pages.php
- /
clsLibMgr::Add('vbz.cat.page', KFP_LIB_VBZ.'/page-cat.php',__FILE__,__LINE__);
clsLibMgr::AddClass('clsSuppliers_StoreUI','vbz.cat.page');
class clsCatPages extends clsVbzTable {
public function __construct($iDB) {
parent::__construct($iDB); $this->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 {
/*---- RETURNS: an object of the appropriate type, as determined by what the current page information record indicates */ public function ItemObj() {
$id = $this->Row['ID_Row']; $objData = $this->Engine(); switch ($this->Type) { case 'S': $rs = $objData->Suppliers(); break; case 'D': $rs = $objData->Depts(); break; case 'T': $rs = $objData->Titles(); break; case 'I': $rs = $objData->Images(); break; default: $rs = NULL; } if (is_null($rs)) { $rc = NULL; } else { $rc = $rs->GetItem($id); } return $rc;
}
}
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 = '<a href="'.$this->URL().'">'.$strText.'</a>'; return $out;
} public function Link() { return $this->ShopLink(); } public function URL() {
return KWP_CAT_REL.strtolower($this->CatKey).'/';
}
}
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);
}
- /
} 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(); $objNoImgSect = new clsRTDoc_HTML(); $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 '<a href="'.$strURL.'">'.$this->Name.'</a>';
} /*----- 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;
}
} 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;
} /*---- 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 '<a'.$htAttr.' href="'.$strURL.'">';
} public function LinkAbs() {
$strURL = $this->URL(KWP_CAT); return '<a href="'.$strURL.'">';
} public function LinkName() {
return $this->Link().$this->Name.'</a>';
}
}
/*====
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'
.'List
Price'
.'Our
Price'
.'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 = ''; $out .= ' '.$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 = '<a title="explanation..." href="'.KWP_HELP_NO_STOCK_BUT_AVAIL.'">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 .= '<option>'.$iChoose.'</option>'; } while ($this->NextRow()) { $id = $this->Row['ID']; if ($id == $iDefault) { $htSelect = " selected"; } else { $htSelect = ; } $out .= '<option'.$htSelect.' value="'.$id.'">'.$this->Name().'</option>'; } $out .= '</select>'; } 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'
.'Price'
.'Order
Qty.'
.'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 = ''; $out .= ' '.$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 = '<a title="explanation..." href="'.KWP_HELP_NO_STOCK_BUT_AVAIL.'">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().'</option>'; } $out .= '</select>'; } 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; } 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 '<a href="'.$strFldr.'/">';
}
} </php>