VbzCart/docs/archive/code/files/store.php: Difference between revisions

From Woozle Writes Code
< VbzCart‎ | docs‎ | archive‎ | code‎ | files
Jump to navigation Jump to search
imported>Woozle
(New page: ==About== * '''Purpose''': Classes for displaying different types of catalog display pages * '''History''': ** '''2009-03-07''' Transcribed from working code at vbz.net * '''To Do''': ** s...)
 
m (Woozle moved page VbzCart/VbzCart/archive/code/files/store.php to VbzCart/docs/archive/code/files/store.php without leaving a redirect: correct naming (was no way to import directly to this name))
 
(6 intermediate revisions by one other user not shown)
Line 3: Line 3:
* '''History''':
* '''History''':
** '''2009-03-07''' Transcribed from working code at vbz.net
** '''2009-03-07''' Transcribed from working code at vbz.net
** '''2009-10-06''' Latest code - large chunks working, focus of development no longer on this module
** '''2011-12-18''' long-overdue update
* '''To Do''':
* '''To Do''':
** should be split up into auto-loadable class files, e.g. vbz.title.php, vbz.dept.php, etc.
** should be split up into auto-loadable class files, e.g. vbz.title.php, vbz.dept.php, etc.
Line 8: Line 10:
==Code - store.php==
==Code - store.php==
<php><?php
<php><?php
# PURPOSE: vbz class library
/*
 
FILE: store.php
define('kfpHostAcctRoot','/hsphere/local/home/hypertwi/');
PURPOSE: vbz class library - should eventually be subdivided
define('kfpMediaWiki',kfpHostAcctRoot.'wiki.vbz.net/');
HISTORY:
  2009-07-11 (Wzl) Lots of cleanup; added types and methods needed for shopping cart page
  2009-10-06 (Wzl) Changed most clsTitle* classes to clsVbzTitle* (conflict with something - w3tpl?)
    Additional changes needed so it would work on Rizzo
  2010-06-12 (Wzl) A little cleanup; clsTopic::Titles()
  2010-06-14 (Wzl) clsVbzTable::GetItem() handles non-numeric IDs by creating new object and passing along ID
  2011-01-18 (Wzl) moved clsTopic(s) to topic.php
*/
define('kEmbeddedPagePrefix','embed:');
define('kEmbeddedPagePrefix','embed:');


require('datamgr.php');
/*
if (KF_USE_WIKI) {
if (defined( '__DIR__' )) {
  define(KW_WIKI_ROOT,'http://wiki.vbz.net/');
  $fpThis = __DIR__;
  include('extract.php');
} else {
  $fpThis = dirname(__FILE__);
}
*/
if (!defined('LIBMGR')) {
    require(KFP_LIB.'/libmgr.php');
}
}
clsLibMgr::Add('strings', KFP_LIB.'/strings.php',__FILE__,__LINE__);
clsLibMgr::Add('string.tplt', KFP_LIB.'/StringTemplate.php',__FILE__,__LINE__);
clsLibMgr::Add('cache' , KFP_LIB.'/cache.php',__FILE__,__LINE__);
clsLibMgr::Add('dtree', KFP_LIB.'/dtree.php',__FILE__,__LINE__);
clsLibMgr::Add('events', KFP_LIB.'/events.php',__FILE__,__LINE__);
clsLibMgr::Add('vbz.data', KFP_LIB_VBZ.'/vbz-data.php',__FILE__,__LINE__);
clsLibMgr::Add('topic', KFP_LIB_VBZ.'/topic.php',__FILE__,__LINE__);
clsLibMgr::Load('strings',__FILE__,__LINE__);
clsLibMgr::Load('string.tplt',__FILE__,__LINE__);
clsLibMgr::Load('cache',__FILE__,__LINE__);
clsLibMgr::Load('dtree',__FILE__,__LINE__);
clsLibMgr::Load('events',__FILE__,__LINE__);
clsLibMgr::Load('vbz.data',__FILE__,__LINE__);
clsLibMgr::Load('topic',__FILE__,__LINE__);


define('EN_PGTYPE_NOTFND',-1); // requested item (supp/dept/title) not found
define('EN_PGTYPE_NOTFND',-1); // requested item (supp/dept/title) not found
Line 25: Line 54:
define('EN_PGTYPE_DEPT',3); // department page, or possibly title for keyless dept
define('EN_PGTYPE_DEPT',3); // department page, or possibly title for keyless dept
define('EN_PGTYPE_TITLE',4); // title page
define('EN_PGTYPE_TITLE',4); // title page
// table names
// - stock
define('ksTbl_stock_places','stk_places');
define('ksTbl_stock_bins','stk_bins');
define('ksQry_stock_bins_wInfo','qryStk_Bins_w_info');
define('ksTbl_stock_items','stk_items');
define('ksTbl_stock_hist_items','stk_history');
define('ksTbl_stock_hist_bins','stk_bin_history');
// - browsing
define('ksTbl_title_topics','brs_titles_x_topics');


$imgSize['th'] = 'thumbnail';
global $vbgImgSize;
$imgSize['sm'] = 'small';
$vbgImgSize = array(
$imgSize['big'] = 'large';
    'th' => 'thumbnail',
$imgSize['huge'] = 'huge';
    'sm' => 'small',
$imgSize['zoom'] = 'detail';
    'big' => 'large',
    'huge' => 'huge',
    'zoom' => 'detail');
 
// shipping zone data:
global $listShipListDesc,$listItmFactors,$listPkgFactors; // there's got to be a better way to do this...
$listShipListDesc['US'] = 'United States';
$listShipListDesc['CA'] = 'Canada';
$listShipListDesc['INT'] = 'International';
// -- shipping charge adjustment factors:
$listPkgFactors['US'] = 1.0;
$listItmFactors['US'] = 1.0;
$listPkgFactors['CA'] = 2.0;
$listItmFactors['CA'] = 2.0;
$listPkgFactors['INT'] = 4.0;
$listItmFactors['INT'] = 4.0;
// The above is just until we have something more exact and dynamic


$intCallDepth = 0;
$intCallDepth = 0;
Line 40: Line 95:
$fwpAbsTools = 'http://'.KS_TOOLS_SERVER.$fpTools;
$fwpAbsTools = 'http://'.KS_TOOLS_SERVER.$fpTools;
$fwpCart = $fwpAbsPages.'/cart/';
$fwpCart = $fwpAbsPages.'/cart/';
$strCurServer = $_ENV['SERVER_NAME'];
$strCurServer = $_SERVER['SERVER_NAME'];


// SET UP DEPENDENT VALUES
// SET UP DEPENDENT VALUES
Line 51: Line 106:
$fwpLogo = $fpTools.'/img/logos/v/';
$fwpLogo = $fpTools.'/img/logos/v/';


/*====
  NOTES:
    2011-02-02 This could use some rewriting. A lot of it is kind of pointless,
      and other parts duplicate RichText objects.
*/
class clsPageOutput {
  public $out;
  private $isOdd;
  public $inTbl;


function InitData($iSpec) {
  function __construct() {
  global $objFactory,$objDataMgr;
    $this->out = '';
 
    $this->isOdd = false;
  $objDb = new clsDatabase($iSpec);
     $this->inTbl = 0;
  $objFactory = new clsFactory($objDb);
  $objDataMgr = new clsDataMgr($objDb,'data_tables','data_procs','v_data_flow','data_log');
  return $objDb;
}
 
class clsFactory {
  protected $objDB;
  private $objPages;
  private $objSupps;
  private $objDepts;
  private $objTitles;
  private $objTitlesExt;
  private $objItems;
  private $objItTyps;
  private $objImages;
  private $objStkItems;
  private $objTopics;
 
  public function __construct($iDB) {
     $this->objDB = $iDB;
   }
   }
// generic functions
   function Clear() {
   public function DB() {
     $this->out = '';
     return $this->objDB;
   }
   }
   public function Table($iName) {
   function AddText($iText) {
     return new clsDataTable($this->objDB,$iName);
     $this->out .= $iText;
   }
   }
   public function Query($iSQL) {
   function SectionHdr($iTitle) { // this is specifically duplicated
     return new clsDataQuery($this->objDB,$iSQL);
    $this->out .= '<p class="hdr-sub">'.$iTitle.'</p>';
     return $this->out;
   }
   }
// table-specific functions
   function StartTable($iTitle) {
   public function Pages() {
     if ($iTitle) {
     if (!is_object($this->objPages)) {
      $this->SectionHdr($iTitle);
       $this->objPages = new clsCatPages($this->objDB);
       $this->out .= '<table class=content>';
      $this->inTbl++;
     }
     }
    return $this->objPages;
   }
   }
   public function Suppliers() {
   function RowStart($iClass='') {
     if (!is_object($this->objSupps)) {
     if ($iClass) {
       $this->objSupps = new clsSuppliers($this->objDB);
       $this->out .= '<tr class="'.$iClass.'">';
    } else {
      $this->out .= '<tr>';
     }
     }
    return $this->objSupps;
   }
   }
   public function Depts() {
   function RowStop() {
     if (!is_object($this->objDepts)) {
     $this->out .= '</tr>';
      $this->objDepts = new clsDepts($this->objDB);
    $this->isOdd = !$this->isOdd;
  }
  function ColAdd($iText) {
    if ($this->isOdd) {
      $cellOpen = '<td class=catalog-stripe valign=top>';
    } else {
      $cellOpen = '<td class=catalog valign=top>';
     }
     }
     return $this->objDepts;
     $this->out .= $cellOpen.$iText.'</td>';
   }
   }
   public function Titles() {
   function EndTable() {
     if (!is_object($this->objTitles)) {
     if ($this->inTbl) {
       $this->objTitles = new clsTitles($this->objDB);
       $this->out .= '</table>';
      $this->inTbl--;
     }
     }
     return $this->objTitles;
     return $this->out;
   }
   }
   public function TitlesExt() {
   function ShowImgUnavail() {
     if (!is_object($this->objTitlesExt)) {
    $this->out .= '<table class=border cellpadding="5"><tbody><tr><td><table class="hdr" cellpadding="2"><tbody><tr><td align="center"><span class="page-title">No Images<br>Available<br></span>for this item<br><b>:-(</b></td></tr></tbody></table></td></tr></tbody></table>';
      $this->objTitlesExt = new clsTitlesExt($this->objDB);
    return $this->out;
  }
}
class clsCacheFile_vbz extends clsCacheFile {
    public function __construct() {
parent::__construct(KFP_CACHE);
    }
}
class clsVbzData extends clsDatabase {
    protected $objPages;
 
    public function __construct($iSpec) {
parent::__construct($iSpec);
$this->Open();
    }
// cache manager
    protected function CacheMgr_empty() {
return new clsCacheMgr($this);
     }
    public function CacheMgr() {
if (empty($this->objCacheMgr)) {
    $objCache = $this->CacheMgr_empty();
    $objCache->SetTables('cache_tables','cache_queries','cache_flow','cache_log');
    $this->objCacheMgr = $objCache;
}
return $this->objCacheMgr;
    }
// table-specific functions
    public function Pages() {
return $this->Make('clsCatPages');
    }
    public function Suppliers() {
return $this->Make('clsSuppliers');
    }
    public function Depts() {
return $this->Make('clsDepts');
    }
    public function Titles($iID=NULL) {
return $this->Make('clsVbzTitles',$iID);
    }
    public function Items() {
return $this->Make('clsItems');
    }
    public function Items_Stock() {
return $this->Make('clsItems_Stock');
    }
    public function Items_Cat() {
return $this->Make('clsItems_info_Cat');
    }
    public function ItTyps() {
return $this->Make('clsItTyps');
    }
    public function ItOpts() {
return $this->Make('clsItOpts');
    }
    public function ShipCosts() {
return $this->Make('clsShipCosts');
    }
    public function Folders() {
return $this->Make('clsVbzFolders');
    }
    public function Images() {
return $this->Make('clsImages');
    }
    public function StkItems() {
return $this->Make('clsStkItems');
    }
    public function Topics($iID=NULL) {
return $this->Make('clsTopics',$iID);
    }
    public function TitleTopic_Titles() {
return $this->Make('clsTitleTopic_Titles');
    }
    public function TitleTopic_Topics() {
return $this->Make('clsTitleTopic_Topics');
     }
     }
     return $this->objTitlesExt;
     public function VarsGlobal() {
  }
return $this->Make('clsGlobalVars');
  public function Items() {
    }
    if (!is_object($this->objItems)) {
    public function Events() {
      $this->objItems = new clsItems($this->objDB);
return $this->Make('clsEvents');
     }
     }
     return $this->objItems;
     public function LogEvent($iWhere,$iParams,$iDescr,$iCode,$iIsError,$iIsSevere) {
  }
return $this->Events()->LogEvent($iWhere,$iParams,$iDescr,$iCode,$iIsError,$iIsSevere);
  public function ItTyps() {
    if (!is_object($this->objItTyps)) {
      $this->objItTyps = new clsItTyps($this->objDB);
     }
     }
     return $this->objItTyps;
// Page output routines
  }
     public function Formatter() {
  public function Images() {
if (!isset($this->objFmt)) {
    if (!is_object($this->objImages)) {
    $this->objFmt = new clsPageOutput();
      $this->objImages = new clsImages($this->objDB);
}
return $this->objFmt;
     }
     }
     return $this->objImages;
     public function SectionHdr($iTitle) {
  }
/*
  public function StkItems() {
$out = '<p class="hdr-sub">'.$iTitle.'</p>';
    if (!is_object($this->objStkItems)) {
return $out;
      $this->objStkItems = new clsStkItems($this->objDB);
*/
$this->Formatter()->Clear();
return $this->Formatter()->SectionHdr($iTitle);
     }
     }
     return $this->objStkItems;
     /*----
  }
      NOTES:
  public function Topics() {
2011-02-02 This really should somehow provide a more general service. Right now, it's only used for displaying
    if (!is_object($this->objTopics)) {
  department pages -- but topic pages and searches could make use of it as well, if there were some tidy way
      $this->objTopics = new clsTopics($this->objDB);
  to abstract the collecting of the price and stock data. This will probably necessitate adding hi/low price
  data to _titles and possibly _dept_ittyps (it's already in _title_ittyps).
      HISTORY:
2011-02-02
  * Removed assertion that $objImgs->Res is a resource, because if there are no thumbnails for this title,
    then apparently it isn't, and apparently RowCount() handles this properly.
  * Also fixed references to qtyForSale -- should be qtyInStock.
    */
    public function ShowTitles($iHdrText,$iList,$objNoImgSect) {
$cntImgs = 0;
$outImgs = '';
foreach ($iList as $i => $row) {
    $objTitle = $this->Titles()->GetItem($row['ID']);
    $objImgs = $objTitle->ListImages('th');
    //assert('is_resource($objImgs->Res); /* TYPE='.get_class($objImgs).' SQL='.$objImgs->sqlMake.' */');
    $currMinPrice = $row['currMinPrice'];
    $currMaxPrice = $row['currMaxPrice'];
    $strPrice = DataCurr($currMinPrice);
    if ($currMinPrice != $currMaxPrice) {
      $strPrice .= '-'.DataCurr($currMaxPrice);
    }
    if ($objImgs->RowCount()) {
      $cntImgs++;
      $strCatNum = $objTitle->CatNum();
      $strTitleTag = '&quot;'.$objTitle->Name.'&quot; ('.$strCatNum.')';
      $strTitleLink = $objTitle->Link();
      while ($objImgs->NextRow()) {
$strImgTag = $strTitleTag.' - '.$strPrice;
$qtyStk = $row['qtyInStock'];
if ($qtyStk) {
  $strImgTag .= ' - '.$qtyStk.' in stock';
}
$outImgs .= $strTitleLink.'<img class="thumb" src="'.$objImgs->WebSpec().'" title="'.$strImgTag.'"></a>';
      }
    } else {
if (!$objNoImgSect->inTbl) {
    $objNoImgSect->StartTable('titles without images:');
    $objNoImgSect->AddText('<tr class=main><th>Cat. #</th><th>Title</th><th>Price<br>Range</th><th>to<br>order</th><th>status</th></tr>');
}
$objNoImgSect->RowStart();
$objNoImgSect->ColAdd('<b>'.$objTitle->CatNum().'</b>');
$objNoImgSect->ColAdd($objTitle->Name);
$objNoImgSect->ColAdd($strPrice);
$objNoImgSect->ColAdd('<b>[</b>'.$objTitle->Link().'order</a><b>]</b>');
$qtyStk = $row['qtyInStock'];
if ($qtyStk) {
    $strStock = '<b>'.$qtyStk.'</b> in stock';
    if ($row['cntInPrint'] == 0) {
$strStock .= ' - OUT OF PRINT!';
    }
    $objNoImgSect->ColAdd($strStock);
} else {
    $objNoImgSect->ColAdd('<a title="explanation..." href="'.KWP_HELP_NO_STOCK_BUT_AVAIL.'">available, not in stock</a>');
// Debugging:
//          $objNoImgSect->ColAdd('ID_Title='.$objTitle->ID.' ID_ItTyp='.$objTitle->idItTyp);
}
$objNoImgSect->RowStop();
    }
}
$out = '';
if ($cntImgs) {
    $out .= $this->SectionHdr($iHdrText);
    $out .= $outImgs;
}
return $out;
     }
     }
    return $this->objTopics;
  }
  public function Table_noID($iName) {
    return new clsDataTable_noID($objDb,$iName);
  }
}
}


Line 162: Line 347:
   }
   }
   public function Output($iPfx, $iSep, $iSfx) {
   public function Output($iPfx, $iSep, $iSfx) {
    $out = NULL;
     if (is_array($this->List)) {
     if (is_array($this->List)) {
       foreach ($this->List as $objItem) {
       foreach ($this->List as $objItem) {
Line 183: Line 369:
   }
   }
}
}
/* ===================
/* ===================
   CLASS: clsPage
   CLASS: clsVbzSkin
   PURPOSE: Handles display of different page types
   PURPOSE: Abstract skin class
*/
*/
class clsPage {
abstract class clsVbzSkin extends clsVbzData {
// query
    /*-----
  protected $strReq; // requested page
      USAGE: Normal main entry point -- should be called from index.php
// page definition
    */
  protected $strAbbr; // page's abbreviation for wiki embedding lookups (if blank, suppress embedding)
    public function DoPage() {
  protected $strName; // short title: {item name} (goes into html title, prefixed with store name)
try {
  protected $strTitle; // longer, descriptive title: {"item name" by Supplier} (goes at top of page)
    $this->DoPreContent();
  protected $strSheet; // name of style sheet to use (without the .css)
    $this->DoContent();
  protected $strTitleContext; // context of short title, in HTML: {Supplier: Department:} (goes above title, in small print)
    $this->DoPostContent();
  protected $strHdrXtra; // any extra stuff (HTML) for the header
} catch(exception $e) {
  protected $strSideXtra; // any extra stuff for the sidebar
    $this->EmailException($e);
  protected $lstTop; // stuff listed at the top of the sidebar
}
// calculated fields
    }
  protected $strCalcTitle;
    /*----
  protected $strContText;
      HISTORY:
// flags set by wiki contents
2011-03-31 added Page and Cookie to list of reported variables
  protected $hideImgs;
    */
/* OLD
    protected function EmailException(exception $e) {
// query parsing
$msg = $e->getMessage();
  public $CatNum;
$out = 'Description: '.($e->getMessage());
// status
$out .= "\nStack trace:\n".($e->getTraceAsString());
  var $enPgType;
 
/**/
$guest_ip = $_SERVER['REMOTE_ADDR'];
  public function __construct() {
$guest_browser = $_SERVER['HTTP_USER_AGENT'];
    $this->lstTop = new clsList();
$guest_referer = NzArray($_SERVER,'HTTP_REFERER');
  }
$guest_page = $_SERVER['REQUEST_URI'];
$guest_cookie = $_SERVER['HTTP_COOKIE'];
 
$out .= <<<__END__


  public function GetQuery() {
Client information:
// ACTION: Retrieves request from URL and parses it
- IP Addr: $guest_ip
    $this->strSheet = 'browse'; // default
- Browser: $guest_browser
    $strReq = $_SERVER['PATH_INFO'];
- Page  : $guest_page
    $this->strReq = $strReq;
- Cookie : $guest_cookie
//    $pathinfo = $_SERVER['REQUEST_URI'];
- Referer: $guest_referer
    if (strrpos($strReq,'/')+1 < strlen($strReq)) {
__END__;
      $strRedir = KWP_CAT_REL.substr($strReq,1).'/';
      header('Location: '.$strRedir);
      exit; // retry with new URL
    }
    $this->ParseQuery();
  }
  public function ParseQuery() {
// This is essentially an abstract function
// Define any additional parsing of the query (store member vars etc.)


//    global $objFactory;
$subj = 'vbz error from IP '.$guest_ip;
$ok = mail(KS_TEXT_EMAIL_ADDR_ERROR,$subj,$out);


//    $strReq = $this->strReq;
echo <<<__END__
<b>Ack!</b> We seem to have a small problem here. (If it was a large problem, you wouldn't be seeing this message.)
The webmaster is being alerted about this.
<br>Meanwhile, you might try reloading the page -- a lot of errors are transient,
which makes them hard to fix, which is why there are more of them than the other kind.
<br><br>
We apologize for the nuisance.
<br><br>
<b>Error Message</b>: $msg
__END__;


// FUTURE: log the error and whether the email was successful
    }
    /*-----
      ACTION: Displays everything *before* the main page contents
    */
    protected function DoPreContent() {
$this->ParseInput();
$this->HandleInput();
$this->RenderHdrBlocks();
    }
    /*-----
      ACTION: Displays everything *after* the main page contents
    */
    protected function DoPostContent() {
$this->RenderFtrBlocks();
    }


//   $this->objCatPage = $objFactory->Pages()->GetItem_byKey($strReq);
// ABSTRACT section //
//print 'REQ='.$strReq.' ABBR='.$this->objCatPage->AB;
    /*-----
  }
      ACTION: Displays the main page contents
// DIFFERENT TYPES OF PAGES
    */
  protected function DoNotFound() {
    protected abstract function DoContent();
//    $this->Setup('','Unknown Title','unknown title in catalog','browse','Tomb of the...');
    /*-----
     $this->strAbbr = '';
      ACTION: Grab any expected input and interpret it
     $this->strTitle = 'Unknown Page';
    */
     $this->strName = 'unknown title in catalog';
    protected abstract function ParseInput();
    $this->strTitleContext = 'Tomb of the...';
    /*-----
    $this->strHdrXtra = '';
      ACTION: Take the parsed input and do any needed processing (e.g. looking up data)
     $this->strSideXtra = '<dt><b>Cat #</b>: '.$this->strReq;
    */
  }
    protected abstract function HandleInput();
// UTILITY
     /*-----
  protected function AddText($iText) {
      ACTION: Render any output that appears *before* the main content
    $this->strContText .= $iText;
    */
  }
    protected abstract function RenderHdrBlocks();
     /*-----
      ACTION: Render any output that appears *after* the main content
    */
    protected abstract function RenderFtrBlocks();
}
/* ===================
  CLASS: clsVbzSkin_Standard
  PURPOSE: Standard skin class
    Will later be replaceable with other skins
*/
abstract class clsVbzSkin_Standard extends clsVbzSkin {
/*
    protected function DoContent() {
    }
    protected function ParseInput() {
    }
    protected function HandleInput() {
    }
*/
     protected function RenderHdrBlocks() {
$this->RenderHtmlStart();
$this->RenderContentHdr();
$this->DoSidebar();
     }
    protected function RenderFtrBlocks() {
$this->RenderContentFtr();
$this->RenderHtmlStop();
    }
    protected function RenderContentFtr() {
global $didPage,$fltStart;


// PAGE COMPONENTS
echo '<div style="clear: both;" align=right>';
// -- HEADER COMPONENTS
$this->DoSepBar();
  protected function DoPreamble() {
echo '<table width=100%><tr><td align=right><small><i>';
# Framework
$fltExecTime = microtime(true)-$fltStart;
    $this->DoHeader();
$dat = getrusage();
    $this->DoSidebar();
$fltUserTime = $dat["ru_utime.tv_usec"]/1000000;
    $this->DoWikiContent();
$strServer = $_SERVER['SERVER_SOFTWARE'];
  }
echo $strServer.' .. ';
  protected function DoPostamble() {
echo 'PHP '.phpversion().' .. Generated in <b>'.$fltUserTime.'</b> seconds (script execution '.$fltExecTime.' sec.) .. ';
    global $didPage,$fltStart,$strPageAbbr;
$strWikiPg = $this->strWikiPg;
    print '<div style="clear: both;" align=right>';
if ($strWikiPg) {
    $this->DoSepBar();
    echo 'wiki: <a href="'.KWP_WIKI.kEmbeddedPagePrefix.$this->strWikiPg.'">'.$strWikiPg.'</a> .. ';
    print '<table width=100%><tr><td><small>'.$txtFooter.'</small></td><td align=right><small><i>';
}
    $fltExecTime = microtime(true)-$fltStart;
echo date('Y-m-d H:i:s');
    $dat = getrusage();
echo '</i></small></td></tr></table>';
    $fltUserTime = $dat["ru_utime.tv_usec"]/1000000;
echo '</div>';
    $strServer = $_SERVER['SERVER_SOFTWARE'];
$didPage = true;
    print $strServer.' .. ';
    print 'PHP '.phpversion().' .. Generated in <b>'.$fltUserTime.'</b> seconds (script execution '.$fltExecTime.' sec.) .. ';
    if ($strPageAbbr) {
      print 'wiki: <a href="'.KWP_WIKI_ROOT.kEmbeddedPagePrefix.$strPageAbbr.'">'.$strPageAbbr.'</a> .. ';
     }
     }
     print date('Y-m-d H:i:s');
     protected function RenderHtmlStop() {
    print '</i></small></td></tr></table>';
echo "\n</body>\n</html>";
    print '</i></small></div></body></html>';
    $didPage = true;
  }
  private function DoWikiContent() {
# WIKI CONTENTS
# $txtPage = GetEmbedPage('cat');
    if (KF_USE_WIKI) {
      $txtWiki = GetWikiPage($this->strAbbr);
      if ($txtWiki) {
        if (strpos($txtWiki,'__NOIMG__') != -1) {
          $txtWiki = str_replace('__NOIMG__','',$txtWiki);
          $this->hideImgs = true;
        }
      }
      if ($txtWiki) {
//        print '<span class=main>'.$txtPage.'</span><br>';
        print '<table class=main><tr><td>'.$txtWiki.'</td></tr></table>';
      }
     }
     }
  }
 
  protected function DoSidebar() {
// NEW METHODS for this class //
    global $fpTools,$objDataMgr;
    protected function DoSidebar() {
// later: these will be pulled from the [stats] table
$objCache = $this->CacheMgr();
if ($objDataMgr->dtNewest) {
// TO DO: these should be pulled from the [stats] table
  $timeSidebarBuild=$objDataMgr->dtNewest;
/*
if ($objCache->dtNewest) {
    $timeSidebarBuild=$objCache->dtNewest;
} else {
    $timeSidebarBuild = NULL;
}
}
*/
    $timeSidebarBuild = NULL;
$statsQtyTitlesAvail = 2245;
$statsQtyTitlesAvail = 2245;
$statsQtyStockPieces = 1395;
$statsQtyStockPieces = 1395;
Line 310: Line 529:
$statsQtyArtists = 136;
$statsQtyArtists = 136;
$statsQtyTopics = 1048;
$statsQtyTopics = 1048;
//---------


    print '<table align=left background="'.$fpTools.'/img/bg/lines/" cellpadding=3 bgcolor="#000000"><tr><td>';
echo '<table class=border align=left cellpadding=3 bgcolor="#000000"><tr><td>';
?>
echo '<table class=sidebar bgcolor="#ffffff" cellpadding=5><tr><td>';
<table bgcolor="#ffffff" cellpadding=5><tr><td class=xxmenu>
echo '<table border=0 class=menu-title width="100%"><tr><td class=menu-title><a href="/">Home</a></td></tr></table>';
<table border=0 class=menu-title width="100%"><tr><td class=menu-title><a href="/">Home</a></td></tr></table>
echo '<span class=menu-text><dl>';
<span class=menu-text><dl>
<?php
/*
/*
<span class=menu-text><p style="background: #eeeeee;"><dl>
<span class=menu-text><p style="background: #eeeeee;"><dl>
*/
*/
  echo $this->lstTop->Output('<dt><b>','</b>: ','');
echo $this->lstTop->Output('<dt><b>','</b>: ','');
//  echo '</p></span></dl>';
//  echo '</p></span></dl>';
  echo '</dl>';
echo '</dl>';
  if ($this->strSideXtra) {
if ($this->strSideXtra) {
    echo '<dl style="background: #eeeeee;">'.$this->strSideXtra.'</dl>';
    echo '<dl style="background: #eeeeee;">'.$this->strSideXtra.'</dl>';
  }
}
  echo '<form action="/search/">';
echo '<form action="/search/">';
  echo 'Search '.$statsQtyTitlesAvail.' items:<br>';
echo 'Search '.$statsQtyTitlesAvail.' items:<br>';
?>
?>
<input size=10 name=search><input type=submit value="Go"><br>
<input size=10 name=search><input type=submit value="Go"><br>
Line 333: Line 551:
</form>
</form>
<b>Indexes</b>
<b>Indexes</b>
<br> ...<a href="/cat/"><b>C</b>atalog <b>H</b>ome</a>
<?php
<?php
echo '<br> ...<a href="'.KWP_SHOP_SUPP.'" title="list of suppliers and what we carry from each one"><b>S</b>uppliers</a>';
/*
echo '<br> ...<a href="/stock/" title="'
  .$statsQtyStockPieces.' pieces, '
  .$statsQtyStockItems.'. items"><b>S</b>tock</a> ('.$statsQtyStockPieces.')';
*/
echo '<br> ...<a href="'.KWP_SHOP_STOCK.'" title="what\'s currently in stock"><b>S</b>tock</a>';
echo '<br> ...<a href="'.KWP_SHOP_TOPICS.'" title="topic master index (topics are like category tags)"><b>T</b>opics</a>';
// echo '<br> ...<a href="/artists/" title="'.$statsQtyArtists.'.artists"><b>A</b>rtists</a> ('.$statsQtyArtists.')';
echo '<p>';
echo '[[ <a href="'.KWP_WIKI.'" title="vbz wiki homepage"><b>wiki</b></a> ]]<br>';
echo '-- [[ <a href="'.KWP_HELP_HOME.'" title="help main index"><b>Help</b></a> ]]<br>';
echo '-- [[ <a href="'.KWP_HELP_ABOUT.'" title="about vbz.net (probably more than you want to know)"><b>About</b></a> ]]<br>';
echo '-- [[ <a href="'.KWP_HELP_CONTACT.'" title="contact vbz.net (several different methods)"><b>Contact</b></a> ]]<br>';
echo '-- [[ <a href="'.KWP_WIKI.'VBZwiki_talk:Community_portal" title="leave your comments and suggestions"><b>Comments</b></a> ]]<br>';
echo '<p>';
echo '<a href="/email/" title="web form for sending us email">email form</a><br>';
echo '<a href="/cart/" title="your shopping cart">shopping cart</a><p>';
echo '</span></td></tr></table></td></tr></table>';
    }
    protected function DoSepBar() {
      echo $this->Render_HLine();
    }
    public function Render_HLine($iHeight=NULL) {
      global $fpTools;


    print '<br> ...<a href="/stock/" title="'.$statsQtyStockPieces.' pieces, '.$statsQtyStockItems.'. items"><b>S</b>tock</a> ('.$statsQtyStockPieces.')';
      $htHt = is_null($iHeight)?'':('height='.$iHeight);
    print '<br> ...<a href="/artists/" title="'.$statsQtyArtists.'.artists"><b>A</b>rtists</a> ('.$statsQtyArtists.')';
      return '<img src="'.$fpTools.'/img/bg/hlines/"'.$htHt.' alt="-----" width="100%">';
    print '<br> ...<a href="/topics/" title="'.$statsQtyTopics.'.topics"><b>T</b>opics</a> ('.$statsQtyTopics.')';
    }
    print '<p>';
    private function ToolbarItem($iURL,$iIcon,$iTitle,$iAlt) {
    print '[[ <a href="'.KWP_WIKI.'/" title="vbz wiki homepage"><b>wiki</b></a> ]]<br>';
global $fpTools;
    print '-- [[ <a href="'.KWP_WIKI.'/help" title="help-related pages on the wiki"><b>Help</b></a> ]]<br>';
return '<a href="'.$iURL.'"><img border=0 src="'.$fpTools.'/img/icons/'.$iIcon.'.050pxh.png" title="'.$iTitle.'" alt="'.$iAlt.'"></a>';
    print '-- [[ <a href="'.KWP_WIKI.'/about" title="about vbz.net (probably more than you want to know)"><b>About</b></a> ]]<br>';
     }
    print '-- [[ <a href="'.KWP_WIKI.'/contact" title="contact vbz.net (several different methods)"><b>Contact</b></a> ]]<br>';
     protected function DoToolbar() {
     print '<p>';
global $fpPages,$fwpCart;
     print '<a href="/email/" title="web form for sending us email">email form</a><br>';
 
    print '<a href="/cart/" title="your shopping cart">shopping cart</a><p>';
echo $this->ToolbarItem($fpPages.'/','home',KS_STORE_NAME.' home page','home page');
# == END Sidebar B ==
echo $this->ToolbarItem($fpPages.'/search/','search','search page','search page');
    print '<table background="'.$fpTools.'/img/bg/hdr/" width="100%"><tr><td>';
echo $this->ToolbarItem($fwpCart,'cart','shopping cart','shopping cart');
    print '<table width="100%"><tr><td valign=top class=menu-status>Updated:</td><td>';
echo $this->ToolbarItem(KWP_HELP_HOME,'help','help!','help');
    print '<td align=right class=menu-status><b>'.$timeSidebarBuild.'</b>';
     }
    print '</td></tr></table>';
    print '</td></tr></table></span></td></tr></table></td></tr></table>';
  }
  private function DoSepBar() {
     global $fpTools;


    print '<img src="'.$fpTools.'/img/bg/hlines/" alt="-----" width="100%">';
// NEW METHODS for this class //
  }
    /*----
  private function ToolbarItem($iURL,$iIcon,$iTitle,$iAlt) {
      PURPOSE: Renders HTML inside <head></head> section
    global $fpTools;
      HISTORY:
    return '<a href="'.$iURL.'"><img border=0 src="'.$fpTools.'/img/icons/'.$iIcon.'.050pxh.png" title="'.$iTitle.'" alt="'.$iAlt.'"></a>';
2011-01-11 Created
  }
    */
  protected function DoToolbar() {
    protected function RenderHtmlHeaderSection() {
    global $fpPages,$fwpCart;
$strTitle = KS_STORE_NAME.' - '.$this->strName;
    print $this->ToolbarItem($fpPages.'/','home',KS_STORE_NAME.' home page','home page');
$out = "\n<title>$strTitle</title>";
    print $this->ToolbarItem($fpPages.'/search/','search','search page','search page');
    print $this->ToolbarItem($fwpCart,'cart','shopping cart','shopping cart');
    print $this->ToolbarItem(KWP_WIKI.'/help','help','help!','help');
  }
// -- HEADER
  protected function DoHeader() {
    global $fpTools, $fwpLogo,$strPageAbbr;;


    $strPageAbbr = $this->strAbbr;
$arVars = array('sheet' => $this->strSheet);
    $this->strCalcTitle = KS_STORE_NAME.' - '.$this->strName;
$objStrTplt = new clsStringTemplate_array(NULL,NULL,$arVars);
    $htmlHead = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
$objStrTplt->MarkedValue(KHT_PAGE_STYLE);
    $htmlHead .= '<html><head><title>'.$this->strCalcTitle.'</title>';
$out .= $objStrTplt->Replace();
    if ($this->strSheet) {
//$out .= KHT_PAGE_STYLE;
      $htmlHead .= '<link rel="StyleSheet" href="'.$fpTools.'/styles/'.$this->strSheet.'.css">';
if (!empty($this->strName)) {
    $ftName = ': '.htmlspecialchars($this->strName);
} else {
    $ftName = '';
}
$strContent = KS_STORE_NAME_META.$ftName;
$out .= "\n<meta name=description content=\"$strContent\">";
return $out;
     }
     }
# remove any quotes from $pageName:
    /*----
     $htmlName = str_replace('"','&quot;',$this->strName);
      PURPOSE: Renders HTML up to beginning of BODY.
    if ($htmlName) {
      HISTORY:
      $htmlName = ': '.$htmlName;
2011-01-11 Extracted everything between <head> and </head> into RenderHtmlHeaderSection()
    */
     protected function RenderHtmlStart() {
//$this->strCalcTitle = KS_STORE_NAME.' - '.$this->strName;
$out = KHT_PAGE_DOCTYPE;
$out .= "\n<html>\n<head>";
$out .= $this->RenderHtmlHeaderSection();
$out .= "\n</head>";
$out .= KHT_PAGE_BODY_TAG;
echo $out;
     }
     }
     $htmlHead .= '<meta name=description content="'.KS_STORE_NAME_META.$htmlName.'">';
     protected function RenderContentHdr() {
    $htmlHead .= '</head>';
global $fwpLogo;
    print $htmlHead;
//$strWikiPg = $this->strWikiPg;
?><body
 
bgcolor=000044
      // begin content header
TEXT=CCFFFF
echo '<table width="100%" class=border cellpadding=5><tr><td>';
LINK=33FF33
echo '<table width="100%" class=hdr cellpadding=2><tr>';
VLINK=33CCFF
      // === LEFT HEADER: Title ===
ALINK=FFCC00
echo '<td>';
TOPMARGIN=0
echo '<a href="'.KWP_HOME_ABS.'"><img align=left border=0 src="'.$fwpLogo.'" title="'.KS_STORE_NAME.' home" alt="'.KS_SMALL_LOGO_ALT.'"></a>';
LEFTMARGIN=0
if ($this->strTitleContext) {
MARGINWIDTH=0
  echo '<span class=pretitle><b><a href="/">'.KS_STORE_NAME.'</a></b>: '.$this->strTitleContext.'</span><br>';
MARGINHEIGHT=0
}
>
echo '<span class=page-title>'.$this->strTitle.'</span></td>';
<?
      // === END LEFT HEADER ===
// begin content header
 
    print '<table width="100%" background="'.$fpTools.'/img/bg/lines/" cellpadding=5 bgcolor=000000><tr><td>';
      // === RIGHT HEADER: nav icons ===
    print '<table width="100%" class=hdr cellpadding=2><tr>';
echo '<td align=right>';
// === LEFT HEADER: Title ===
$this->DoToolbar();
    print '<td>';
echo '</td>';
    print '<a href="'.KWP_HOME_ABS.'"><img align=left border=0 src="'.$fwpLogo.'" title="'.KS_STORE_NAME.' home" alt="'.KS_SMALL_LOGO_ALT.'"></a>';
      // === END RIGHT HEADER ===
    if ($this->strTitleContext) {
echo "\n</tr></table>\n</td></tr></table><!-- end html header -->";
      print '<span class=pretitle><b><a href="/">'.KS_STORE_NAME.'</a></b>: '.$this->strTitleContext.'</span><br>';
     }
     }
     print '<span class=page-title>'.$this->strTitle.'</span></td>';
}
// === END LEFT HEADER ===
/* ===================
  CLASS: clsPage
  PURPOSE: Handles display of different page types
  TO DO: These classes (clsPage and descendants) need to be reorganized (they're a mess)
     and also set up so we can create different skins.
*/
abstract class clsPage extends clsVbzSkin_Standard {
// query
    protected $strReq; // requested page
// page definition
    protected $strName; // short title: {item name} (goes into html title, prefixed with store name)
    protected $strTitle; // longer, descriptive title: {"item name" by Supplier} (goes at top of page)
    protected $strSheet; // name of style sheet to use (without the .css)
    protected $strWikiPg; // name of wiki page to embed, if any (blank = suppress embedding)
    protected $strTitleContext; // context of short title, in HTML: {Supplier: Department:} (goes above title, in small print)
    protected $strHdrXtra; // any extra stuff (HTML) for the header
    protected $strSideXtra; // any extra stuff for the sidebar
    protected $lstTop; // stuff listed at the top of the sidebar
// calculated fields
//   protected $strCalcTitle;
    protected $strContText;
// flags set by wiki contents
    protected $hideImgs;


// === RIGHT HEADER: nav icons ===
    public function __construct($iSpec) {
    print '<td align=right>';
parent::__construct($iSpec);
    $this->DoToolbar();
$this->lstTop = new clsList();
    print '</td>';
    }
// === END RIGHT HEADER ===
    protected function HandleInput() {
?>
$this->strSheet = 'browse'; // default
</tr></table>
    }
</td></tr></table>
    /*-----
<!-- end html header -->
      IMPLEMENTATION: Retrieves request from URL and parses it
<?
URL data identifies page, keyed to cat_pages data
  }
    */
    protected function ParseInput() {
if (isset($_SERVER['PATH_INFO'])) {
    $strReq = $_SERVER['PATH_INFO'];
} else {
    $strReq = '';
}
$this->strReq = $strReq;
if (strrpos($strReq,'/')+1 < strlen($strReq)) {
    $strRedir = KWP_CAT_REL.substr($strReq,1).'/';
    header('Location: '.$strRedir);
    exit; // retry with new URL
}
    }
// DIFFERENT TYPES OF PAGES
    protected function DoNotFound() {
$this->strWikiPg = '';
$this->strTitle = 'Unknown Page';
$this->strName = 'unknown title in catalog';
$this->strTitleContext = 'Tomb of the...';
$this->strHdrXtra = '';
$this->strSideXtra = '<dt><b>Cat #</b>: '.$this->strReq;
    }
// UTILITY
    protected function AddText($iText) {
$this->strContText .= $iText;
    }
    private function DoWikiContent() {
# WIKI CONTENTS
# $txtPage = GetEmbedPage('cat');
if (KF_USE_WIKI) {
    $txtWiki = GetWikiPage($this->strWikiPg);
    if ($txtWiki) {
if (strpos($txtWiki,'__NOIMG__') != -1) {
    $txtWiki = str_replace('__NOIMG__','',$txtWiki);
    $this->hideImgs = true;
}
    }
    if ($txtWiki) {
echo '<table class=main><tr><td>'.$txtWiki.'</td></tr></table>';
    }
}
    }
}
}


class clsPageCat extends clsPage {
class clsPageCat extends clsPage {
  private $objCatPage; // object for identifying page to display
    private $objCatPage; // object for identifying page to display
 
    protected function RenderHdrBlocks() {
  public function ParseQuery() {
if ($this->useSkin) {
    global $objFactory;
    parent::RenderHdrBlocks();
 
}
     $strReq = $this->strReq;
    }
    $this->objCatPage = $objFactory->Pages()->GetItem_byKey($strReq);
    protected function RenderFtrBlocks() {
//print 'REQ='.$strReq.' ABBR='.$this->objCatPage->AB;
if ($this->useSkin) {
  }
    parent::RenderFtrBlocks();
}
    }
     protected function HandleInput() {
parent::HandleInput();
$strReq = $this->strReq;
$this->objCatPage = $this->Pages()->GetItem_byKey($strReq);
$objPage = $this->objCatPage;
$this->useSkin = TRUE;


  public function DoRequest() {
if ($this->strReq) {
    if ($this->strReq) {
    if (is_object($objPage)) {
      if (is_object($this->objCatPage)) {
switch ($objPage->Type) {
        switch ($this->objCatPage->Type) {
case 'S':
        case 'S':
  $this->DoCatSupp();
          $this->DoCatSupp();
  break;
          break;
case 'D':
        case 'D':
  $this->DoCatDept();
          $this->DoCatDept();
  break;
          break;
case 'T':
        case 'T':
  $this->DoCatTitle();
          $this->DoCatTitle();
  break;
          break;
case 'I':
        case 'I':
  $this->useSkin = FALSE;
          $doRawHTML = TRUE;
  $this->DoCatImage();
          $this->DoCatImage();
  break;
          break;
}
        }
    } else {
      } else {
$this->DoNotFound();
        $this->DoNotFound();
    }
      }
} else {
    } else {
    $this->DoCatHome();
      $this->DoCatHome();
}
     }
     }
     if ($doRawHTML) {
     public function DoContent() {
      echo $this->strContText;
  echo $this->strContText;
    } else {
      $this->DoPreamble(); // everything before the contents
      echo $this->strContText;
      $this->DoPostamble(); // everything after the contents
     }
     }
  }
// SIDEBAR INFO for different types of pages
// SIDEBAR INFO for different types of pages
   private function DoCatIndicia() {
   private function DoCatIndicia() {
Line 477: Line 789:
     if ($isFinal) {
     if ($isFinal) {
       $this->lstTop->Add('Supplier',$iSupp->Name);
       $this->lstTop->Add('Supplier',$iSupp->Name);
       $this->lstTop->Add('<a href="'.KWP_WIKI_ROOT.$iSupp->Name.'">more info</a>');
       $this->lstTop->Add('<a href="'.KWP_WIKI.$iSupp->Name.'">more info</a>');
     } else {
     } else {
       $this->lstTop->Add('Supplier',$iSupp->Link());
       $this->lstTop->Add('Supplier',$iSupp->Link());
Line 498: Line 810:


   private function DoCatHome() {
   private function DoCatHome() {
    global $objFactory;
    $objSuppTbl = $objFactory->Suppliers();
//    $this->Setup('cat','Catalog Home','Catalog main page','browse','hello and welcome to the...');
     $this->DoCatIndicia();
     $this->DoCatIndicia();
     $this->strAbbr = 'cat';
     $this->strWikiPg = 'cat';
     $this->strTitle = 'Catalog Home';
     $this->strTitle = 'Catalog Home';
     $this->strName = 'Catalog main page';
     $this->strName = 'Catalog main page';
     $this->strTitleContext = 'hello and welcome to the...';
     $this->strTitleContext = 'hello and welcome to the...';
     $this->AddText($objSuppTbl->DoHomePage());
     $this->AddText($this->Suppliers()->DoHomePage());
   }
   }
   private function DoCatSupp() {
   private function DoCatSupp() {
    global $objFactory;
     $objSuppTbl = $this->Suppliers();
    CallEnter('clsPage.DoCatSupp()');
 
     $objSuppTbl = $objFactory->Suppliers();
     $objSupp = $objSuppTbl->GetItem($this->objCatPage->ID_Row);
     $objSupp = $objSuppTbl->GetItem($this->objCatPage->ID_Row);
     assert(is_object($objSupp));
     assert('is_object($objSupp)');
     $strSuppName = $objSupp->GetValue('Name');
     $strSuppName = $objSupp->Name;


     $this->DoSuppIndicia($objSupp);
     $this->DoSuppIndicia($objSupp);
     $this->strAbbr = 'supp:'.strtoupper($this->CatKey);
     $this->strWikiPg = 'supp:'.strtoupper($objSupp->CatKey);
     $this->strTitle = $strSuppName;
     $this->strTitle = $strSuppName;
     $this->strName = 'listing for '.$strSuppName;
     $this->strName = 'listing for '.$strSuppName;
     $this->strTitleContext = '<a href="'.KWP_CAT_REL.'">Suppliers</a>: <b>'.$strSuppName.'</b>:';
     $this->strTitleContext = '<a href="'.KWP_CAT_REL.'">Suppliers</a>: <b>'.$strSuppName.'</b>:';
     $this->AddText($objSupp->DoPage());
     $this->AddText($objSupp->DoPage());
    CallExit('clsPage.DoCatSupp()');
   }
   }
   private function DoCatDept() {
   private function DoCatDept() {
    global $objFactory;
     CallEnter($this,__LINE__,'clsPage.DoCatDept()');
     CallEnter('clsPage.DoCatDept()');


     $objDeptTbl = $objFactory->Depts();
//    $objDeptTbl = VbzClasses::Depts();
     $objDeptTbl = $this->Depts();
     $objDept = $objDeptTbl->GetItem($this->objCatPage->ID_Row);
     $objDept = $objDeptTbl->GetItem($this->objCatPage->ID_Row);
     assert(is_object($objDept));
     assert('is_object($objDept)');
     $objSupp = $objDept->Supplier();
     $objSupp = $objDept->Supplier();
     assert(is_object($objSupp));
     assert('is_object($objSupp)');
     $strDeptName = $objDept->Name;
     $strDeptName = $objDept->Name;
     $strSuppName = $objSupp->Name;
     $strSuppName = $objSupp->Name;
Line 542: Line 845:


     $this->DoDeptIndicia($objDept);
     $this->DoDeptIndicia($objDept);
     $this->strAbbr = 'dept:'.strtoupper($this->CatNum);
     $this->strWikiPg = 'dept:'.strtoupper($objDept->PageKey);
 
     $this->strTitle = $strSuppName;
     $this->strTitle = $strSuppName;
     $this->strName = $strDeptName.' dept. of '.$strSuppName;
     $this->strName = $strDeptName.' dept. of '.$strSuppName;
Line 550: Line 854:
   }
   }
   private function DoCatTitle() {
   private function DoCatTitle() {
    global $objFactory;
     CallEnter($this,__LINE__,'clsPage.DoCatTitle()');
     CallEnter('clsPage.DoCatTitle()');


     $strCatNum = $this->CatNum;
     $strPageKey = $this->objCatPage->Path;
     $objTitleTbl = $objFactory->Titles();
//    $objTitleTbl = VbzClasses::Titles();
     $objTitleTbl = $this->Titles();


     $objTitle = $objTitleTbl->GetItem($this->objCatPage->ID_Row);
    $idRow = $this->objCatPage->ID_Row;
     assert(is_object($objTitle));
     $objTitle = $objTitleTbl->GetItem($idRow);
     assert('is_object($objTitle)');
     $objDept = $objTitle->Dept();
     $objDept = $objTitle->Dept();
     assert(is_object($objDept));
     assert('is_object($objDept)');
     $objSupp = $objDept->Supplier();
     $objSupp = $objDept->Supplier();
     assert(is_object($objSupp));
     assert('is_object($objSupp)');
     $strTitleName = $objTitle->Name;
     $strTitleName = $objTitle->Name;


Line 567: Line 872:


//    $this->strAbbr = 'title:'.strtoupper($strCatNum);
//    $this->strAbbr = 'title:'.strtoupper($strCatNum);
     $this->strAbbr = 'title:'.$objTitle->CatNum();
     $this->strWikiPg = 'title:'.$objTitle->CatNum();
//print 'ABBR='.$this->strAbbr;
//print 'ABBR='.$this->strAbbr;
     $this->strTitle = $strTitleName;
     $this->strTitle = $strTitleName;
     $this->strName = $strCatNum.' "'.$strTitleName.'" from '.$objSupp->Name;
     $this->strName = $strPageKey.' "'.$strTitleName.'" from '.$objSupp->Name;
     $this->strTitleContext =  
     $this->strTitleContext =  
       'items <a href="'.KWP_CAT_REL.
       'items <a href="'.KWP_CAT_REL.
Line 580: Line 885:
   }
   }
   private function DoCatImage() {
   private function DoCatImage() {
    global $objFactory;
     CallEnter($this,__LINE__,'clsPage.DoCatImage()');
     CallEnter('clsPage.DoCatImage()');
//    $objImageTbl = VbzClasses::Images();
     $objImageTbl = $objFactory->Images();
     $objImageTbl = $this->Images();
     $objImage = $objImageTbl->GetItem($this->objCatPage->ID_Row);
     $objImage = $objImageTbl->GetItem($this->objCatPage->ID_Row);
     $objImage->DoPage();
     $objImage->DoPage();
Line 589: Line 894:
}
}


class clsPageTopic extends clsPage {
/* ===========================
  public function DoRequest() {
*** CATALOG DATA CLASSES ***
    global $objFactory;
*/


    $strReq = $this->strReq;
class clsCatPages extends clsVbzTable {
     if (is_numeric($strReq)) {
     public function __construct($iDB) {
      $idTopic = (int)$strReq;
parent::__construct($iDB);
      $objTopic = $objFactory->Topics()->GetItem($idTopic);
  $this->Name('cat_pages'); // cache
      $this->strAbbr = 'topic.'.$objTopic->WebName();
  //$this->Name('qryCat_pages'); // live data
      $this->strTitle = 'Topic Index';
  $this->KeyName('AB');
      $this->strName = 'catalog topic index';
  $this->ClassSng('clsCatPage');
      $this->AddText($objTopic->DoPage());
    }
    } else {
    public function GetItem_byKey($iKey) {
      $this->strAbbr = 'topics';
CallEnter($this,__LINE__,__CLASS__.'.GetItem_byKey('.$iKey.')');
      $this->strTitle = 'Topic Index';
$strKey = trim($iKey,'/');
      $this->strName = 'catalog topic index';
$strKey = str_replace('-','/',$strKey);
      $this->AddText($objFactory->Topics()->DoIndex());
$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;
     }
     }
    $this->DoPreamble(); // everything before the contents
    echo '<span class=main>'.$this->strContText.'</span>';
    $this->DoPostamble(); // everything after the contents
  }
}
}


class clsPageOutput {
class clsCatPage extends clsDataSet {
  public $out;
// == STATIC SECTION
  private $isOdd;
//  const cksIDName = 'AB';
  public $inTbl;
 
}
 
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);


  function __construct() {
return $objRows;
    $this->out = '';
    }
    $this->isOdd = false;
    public function DoHomePage() {
    $this->inTbl = 0;
$out = '';
  }
$objRec = $this->DataSet_forStore();
  function Clear() {
if ($objRec->hasRows()) {
    $this->out = '';
    $objTbl = new clsPageOutput;
  }
    $objTbl->StartTable('Suppliers');
  function AddText($iText) {
    $strKeyLast = $outCell = '';
    $this->out .= $iText;
    while ($objRec->NextRow()) {
  }
$strKey = $objRec->CatKey;
  function SectionHdr($iTitle) {
if ($strKey != $strKeyLast) {
    $this->out .= '<p class=main><big>'.$iTitle.'</big></p>';
    $strKeyLast = $strKey;
    return $this->out;
    $strKeyLink = strtolower($strKey).'/';
  }
    if ($outCell) {
  function StartTable($iTitle) {
$objTbl->ColAdd($outCell);
    if ($iTitle) {
$objTbl->RowStop();
      $this->SectionHdr($iTitle);
$outCell = '';
      $this->out .= '<table>';
    }
      $this->inTbl++;
    $objTbl->RowStart();
    $objTbl->ColAdd('<b><a href="'.$strKeyLink.'">'.$objRec->Name.'</a></b>');
    $isFirst = true;
}
if ($isFirst) {
    $isFirst = false;
} else {
    $outCell .= ', ';
}
$strItType = $objRec->ItemType;
if ($strItType == '') {
    $strItType = '?id'.$objRec->ID;
}
$outCell .= ' <b>'.$objRec->ItemCount.'</b> '.$strItType;
    }
    $objTbl->ColAdd($outCell);
    $objTbl->RowStop();
    $out .= $objTbl->EndTable();
}
return $out;
     }
     }
  }
}
  function RowStart($iClass='') {
 
     if ($iAttr) {
class clsSupplier extends clsDataSet {
       $this->out .= '<tr class="'.$iAttr.'">';
     /*----
     } else {
       ACTION: Finds the Item for this Supplier with the given supplier CatNum
      $this->out .= '<tr>';
      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);
     }
     }
  }
    /*----
  function RowStop() {
      ACTION: Checks the given catalog number to see if it corresponds to a given item for the current supplier
    $this->out .= '</tr>';
      INPUT: supplier catalog number
    $this->isOdd = !$this->isOdd;
      OUTPUT: item object (if found) or NULL (if not found)
  }
      HISTORY:
  function ColAdd($iText) {
2011-01-09 Moved here from VbzAdminSupplier; replaces GetItem_bySuppCatNum()
    if ($this->isOdd) {
    */
      $cellOpen = '<td class=catalog-stripe valign=top>';
    public function GetItem_bySCatNum($iSCat) {
    } else {
$objTblItems = $this->objDB->Items();
      $cellOpen = '<td class=catalog valign=top>';
 
$sqlFind = '(ID_Supp='.$this->ID.') AND (Supp_CatNum="'.$iSCat.'")';
$objItem = $objTblItems->GetData($sqlFind);
if ($objItem->HasRows()) {
    $objItem->NextRow();
    return $objItem;
} else {
    return NULL;
}
     }
     }
     $this->out .= $cellOpen.$iText.'</td>';
     /*----
  }
      ACTION: Finds the Title for this Supplier with the given CatKey
  function EndTable() {
      RETURNS: object of type requested by user; defaults to clsVbzTitle. NULL if not found.
    if ($this->inTbl) {
      HISTORY:
      $this->out .= '</table>';
2010-11-07 Created for Title editing page -- need to check for duplicate CatKey before saving.
      $this->inTbl--;
    */
    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;
}
     }
     }
     return $this->out;
     /*----
  }
      ACTION: Searches for Titles whose CatKeys include the given string
  function ShowTitles($iHdrText,$iList,$objNoImgSect) {
      PURPOSE: used during renaming of supplier-recycled catalog numbers, so we can see if that number
    foreach ($iList as $i => $objTitle) {
has been recycled before and avoid having to repeatedly try new names
       $objImgs = $objTitle->ListImages('th');
       HISTORY:
      $currMinPrice = $objTitle->currMinPrice;
2010-11-08 Created for title editing page
      $currMaxPrice = $objTitle->currMaxPrice;
    */
      $strPrice = DataCurr($currMinPrice);
    public function GetTitles_byCatKey($iCatKey,$iClass='clsVbzTitle') {
      if ($currMinPrice != $currMaxPrice) {
$sqlCatKey = $this->objDB->SafeParam($iCatKey);
        $strPrice .= '-'.DataCurr($currMaxPrice);
$sqlFilt = '(ID_Supplier='.$this->ID.') AND (CatKey LIKE "%'.$sqlCatKey.'%")';
      }
$objTitle = $this->objDB->Titles_Cat()->GetData($sqlFilt,$iClass);
      if ($objImgs->RowCount()) {
if ($objTitle->HasRows()) {
        $cntImgs++;
    $objTitle->NextRow();
        $strTitleTag = '&quot;'.$objTitle->Name.'&quot; ('.$objTitle->CatNum.')';
    return $objTitle;
        $strTitleLink = $objTitle->Link();
} else {
        while ($objImgs->HasData()) {
    return NULL;
          $strImgTag = $strTitleTag.' - '.$strPrice;
}
          if ($objTitle->qtyInStock) {
            $strImgTag .= ' - '.$objTitle->qtyInStock.' in stock';
          }
          $outImgs .= $strTitleLink.'<img class="thumb" src="'.KWP_IMG_MERCH.'/'.$objImgs->GetValue('Spec').'" title="'.$strImgTag.'"></a>';
          $objImgs->NextRow();
        }
      } else {
        if (!$objNoImgSect->inTbl) {
          $objNoImgSect->StartTable('titles without images:');
          $objNoImgSect->AddText('<tr class=main><th>Cat. #</th><th>Title</th><th>Price<br>Range</th><th>to<br>order</th><th>status</th></tr>');
        }
        $objNoImgSect->RowStart();
        $objNoImgSect->ColAdd('<b>'.$objTitle->CatNum.'</b>');
        $objNoImgSect->ColAdd($objTitle->Name);
        $objNoImgSect->ColAdd($strPrice);
        $objNoImgSect->ColAdd('<b>[</b>'.$objTitle->Link().'order</a><b>]</b>');
        $qtyInStock = $objTitle->GetValue('qtyInStock');
        if ($qtyInStock) {
          $strStock = '<b>'.$qtyInStock.'</b> in stock';
          $objNoImgSect->ColAdd($strStock);
          if ($objTitle->GetValue('cntInPrint') == 0) {
            $objNoImgSect->ColAdd('OUT OF PRINT!');
          }
        } else {
          $objNoImgSect->ColAdd('<a title="explanation..." href="'.KWP_WIKI.'/Available_but_not_in_stock">available, not in stock</a>');
// Debugging:
//          $objNoImgSect->ColAdd('ID_Title='.$objTitle->ID.' ID_ItTyp='.$objTitle->idItTyp);
        }
        $objNoImgSect->RowStop();
      }
     }
     }
     if ($cntImgs) {
     protected function DeptsData_forCount($iClass='clsDept') {
      $this->SectionHdr($iHdrText);
$objTbl = $this->objDB->Depts();
      $this->AddText($outImgs);
$objRows = $objTbl->GetData('isActive AND (ID_Supplier='.$this->ID.')',$iClass,'Sort');
return $objRows;
     }
     }
     return $this->out;
     /*----
  }
      USED BY: $this->DeptsPage_forStore()
  function ShowImgUnavail() {
      RETURNS: array of data for the current supplier
    $this->out .= '<table background="/tools/img/bg/lines/" bgcolor="000000" cellpadding="5"><tbody><tr><td><table class="hdr" cellpadding="2"><tbody><tr><td align="center"><span class="page-title">No Images<br>Available<br></span>for this item<br><b>:-(</b></td></tr></tbody></table></td></tr></tbody></table>';
array[rows] = source dataset -- each row is an ItTyp within a Department
    return $this->out;
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])) {
*** CATALOG DATA CLASSES ***
    $objItTyp = $this->Engine()->ItTyps()->SpawnItem();
*/
    $arObjs[$idItTyp] = $objItTyp;


class clsCatPages extends clsDataTable {
    $objItTyp->Row['NameSng'] = $objRows->Value('ItTypNameSng');
  public function __construct($iDB) {
    $objItTyp->Row['NamePlr'] = $objRows->Value('ItTypNamePlr');
     parent::__construct($iDB,'cat_pages','AB');
    $objItTyp->Row['cntForSale'] = 0; // initialize the count
  }
}
  protected function _newItem() {
     // accumulate the list of everything this supplier has:
    return new clsCatPage($this);
$idSupp = $objRows->ID_Supplier;
  }
$objItTyp->Row['cntForSale'] += $intCntForSale;
  public function GetItem_byKey($iKey) {
    // accumulate the department listing:
    global $objDataMgr;
$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'];


    CallEnter('clsCatPages.GetItem_byKey('.$iKey.')');
$outRow = '';
    $strKey = trim($iKey,'/');
foreach ($arObjs as $id=>$obj) {
    $strKey = str_replace('-','/',$strKey);
    $objTyp = $obj;
    $sqlCatKey = $this->objDB->SafeParam($strKey);
    $cnt = $objTyp->Value('cntForSale');
//    $sql = 'SELECT * FROM '.$this->strName.' WHERE CatKey="'.$sqlCatKey.'"';
    if ($cnt > 0) {
    $objDataMgr->Update($this->Name(),'clsCatPages.GetItem_byKey('.$iKey.')');
$strType = $objTyp->Name($cnt);
    $objItem = $this->GetData('Path="'.$sqlCatKey.'"');
if ($outRow != '') {
//    $objRec = $this->objDB->Query($sql);
    $outRow .= ', ';
     DumpValue('objItem NumRows',$objItem->Res->num_rows);
}
     CallExit('clsCatPages.GetItem_byKey('.$iKey.') -> Page '.$this->strAbbr);
$outRow .= '<b>'.$cnt.'</b> '.$strType;
    return $objItem;
    }
  }
}
}
$out = '<span class=catalog-summary>'.$outRow.'</span>';
class clsCatPage extends clsDataItem {
return $out;
  public $AB;
     }
  public $Type;
    /*----
  public $ID_Row;
      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'];


  protected function LoadResults() {
$out = '<table class=depts>';
    CallEnter('clsCatPage.LoadResults()');
$isOdd = FALSE;
    parent::LoadResults();
$fpSupp = KWP_CAT_REL.strtolower($this->Value('CatKey')).'/';
    $this->AB = $this->GetValue('AB');
foreach ($arDeptCntForSale as $idDept=>$arCnts) {
    $this->Type = $this->GetValue('Type');
    $isOdd = !$isOdd;
    $this->ID_Row = $this->GetValue('ID_Row');
    if ($isOdd) {
    assert($this->AB);
$cellOpen = '<td class=catalog-stripe valign=top>';
    CallExit('clsCatPage.LoadResults()');
    } else {
  }
$cellOpen = '<td class=catalog valign=top>';
}
    }


class clsSuppliers extends clsDataTable {
    $outRow = '';
  public function __construct($iDB) {
    foreach ($arCnts as $id=>$cnt) {
    parent::__construct($iDB,'suppliers');
if ($cnt > 0) {
  }
    $objTyp = $arObjs[$id];
  protected function _newItem() {
    $strType = $objTyp->Name($cnt);
    return new clsSupplier($this);
    if ($outRow != '') {
  }
$outRow .= ', ';
/*
    }
  public function GetItem($iID) {
    $outRow .= '<b>'.$cnt.'</b> '.$strType;
    CallEnter('clsSuppliers.GetItem('.$iID.')');
}
    $objItem = parent::GetItem($iID,$objItem);
    }
    CallExit('clsSuppliers.GetItem('.$iID.')');
    if ($outRow != '') {
    return $objItem;
$objDept = $this->objDB->Depts()->GetItem($idDept);
  }
$strPageKey = $objDept->PageKey();
/**/
$strName = $objDept->Name;
  public function GetItem_byKey($iKey) {
    CallEnter('clsSuppliers.GetItem_byKey('.$iKey.')');
    $sqlCatKey = $this->objDB->SafeParam($iKey);
//    $sql = 'SELECT * FROM '.$this->strName.' WHERE CatKey="'.$sqlCatKey.'"';
    $objItem = $this->GetData('CatKey="'.$sqlCatKey.'"');
//    $objRec = $this->objDB->Query($sql);
    CallExit('clsSuppliers.GetItem_byKey('.$iKey.') -> new supplier');
    return $objItem;
  }
  public function DoHomePage() {
    global $objDataMgr;


    $sql = 'SELECT * FROM _supplier_ittyps ORDER BY Name, ItemCount DESC';
$out .= '<tr>'.$cellOpen.'<a href="'.$fpSupp.strtolower($strPageKey).'/">'.$strName.'</a></td>';
    $objRec = new clsDataItem($this);
$out .= $cellOpen.$outRow.'</td></tr>';
    $objDataMgr->Update('_supplier_ittyps','clsSuppliers.DoHomePage()');
    }
    $objRec->Query($sql);
}
    if ($objRec->RowCount()) {
$out .= '</table>';
      $objTbl = new clsPageOutput;
return $out;
      $objTbl->StartTable('Suppliers');
    }
      while ($objRec->HasData()) {
    protected function DeptsPage_forStore() {
        $strKey = $objRec->GetValue('CatKey');
$out = $this->DoPiece_ItTyp_Summary();
        if ($strKey != $strKeyLast) {
$out .= $this->Engine()->SectionHdr('Departments:');
          $strKeyLast = $strKey;
$out .= $this->DoPiece_Dept_ItTyps();
          $strKeyLink = strtolower($strKey).'/';
return $out;
          if ($outCell) {
            $objTbl->ColAdd($outCell);
            $objTbl->RowStop();
            $outCell = '';
          }
          $objTbl->RowStart();
          $objTbl->ColAdd('<b><a href="'.$strKeyLink.'">'.$objRec->GetValue('Name').'</a></b>');
          $isFirst = true;
        }
        if ($isFirst) {
          $isFirst = false;
        } else {
          $outCell .= ', ';
        }
        $outCell .= ' <b>'.$objRec->GetValue('ItemCount').'</b> '.$objRec->GetValue('ItemType');
        $objRec->NextRow();
      }
      $objTbl->ColAdd($outCell);
      $objTbl->RowStop();
      $out .= $objTbl->EndTable();
     }
     }
     return $out;
     public function DoPage() {
  }
$out = '';
}
assert('$this->ID');


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


  protected function LoadResults() {
if ($intRows == 1) {
     CallEnter('clsSupplier.LoadResults()');
     // if there's only one department, display that instead of a department listing
    parent::LoadResults();
    $objDepts->NextRow(); // get the first/only dept
    $this->ID = $this->GetValue('ID');
    $out = $objDepts->DoPage();
    $this->Name = $this->GetValue('Name');
} else {
    $this->CatKey = $this->GetValue('CatKey');
    $out .= $this->DeptsPage_forStore();
    assert($this->ID);
}
    CallExit('clsSupplier.LoadResults()');
  }
  public function DoPage() {
    global $objFactory,$objDataMgr;


    CallEnter('clsSupplier.DoPage()');
return $out;
    assert($this->ID);
// first, check how many departments supplier has:
    $objDeptTbl = $objFactory->Depts();
    $objDepts = new clsDept($objDeptTbl);
    $sql = 'SELECT * FROM _depts WHERE (cntForSale>0) AND (ID_Supp='.$this->ID.')';
    $objDataMgr->Update('_depts','clsSupplier.DoPage() for '.$this->CatKey);
    $objDepts->Query($sql);
    if ($objDepts->RowCount() == 1) {
// if there's only one department, display that instead of a department listing
      $out = $objDepts->DoPage();
    } else {
      $sql = 'SELECT * FROM _supplier_ittyps WHERE ID='.$this->ID.' ORDER BY Name, ItemCount DESC';
      $objDataMgr->Update('_supplier_ittyps','clsSupplier.DoPage() for '.$this->CatKey);
      $objItTyps = new clsDataItem($this->Table);
      $objItTyps->Query($sql);
      $isFirst = true;
      $out .= '<span class=catalog-summary>';
      while ($objItTyps->HasData()) {
        if ($isFirst) {
          $isFirst = false;
        } else {
          $out .= ', ';
        }
        $out .= ' <b>'.$objItTyps->GetValue('ItemCount').'</b> '.$objItTyps->GetValue('ItemType');
        $objItTyps->NextRow();
      }
      $out .= '</span>';
      $out .= $objDeptTbl->DoListing_forSupp($this->ID);
     }
     }


     CallExit('clsSupplier.DoPage()');
     public function ShopLink($iText=NULL) {
    return $out;
if (is_null($iText)) {
  }
    $strText = $this->Name;
  public function Link() {
} else {
    $out = '<a href="'.$this->URL().'">'.$this->Name.'</a>';
    $strText = $iText;
    return $out;
}
  }
$out = '<a href="'.$this->URL().'">'.$strText.'</a>';
  public function URL() {
return $out;
    return KWP_CAT_REL.strtolower($this->CatKey).'/';
    }
  }
    public function Link() { return $this->ShopLink(); }
    public function URL() {
return KWP_CAT_REL.strtolower($this->CatKey).'/';
    }
}
}
class clsDepts extends clsDataTable {
class clsDepts extends clsVbzTable {
  public function __construct($iDB) {
    public function __construct($iDB) {
    parent::__construct($iDB,'depts');
parent::__construct($iDB);
  }
  $this->Name('cat_depts');
  protected function _newItem() {
  $this->KeyName('ID');
    CallStep('clsDepts._newItem()');
  $this->ClassSng('clsDept');
    return new clsDept($this);
  $this->ActionKey('dept');
  }
    }
  public function DoListing_forSupp($iSuppID) {
    protected function _newItem() {
    CallEnter('clsDepts.DoListing_forSupp('.$iSuppID.')');
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 .= '<b>'.$cnt.'</b> '.$strType;
}
$out = '<span class=catalog-summary>'.$outRow.'</span>';
 
  // - DEPARTMENTS:
$out .= '<p class=main><big>Departments:</big></p><table class=depts>';
$isOdd = FALSE;
foreach ($arDeptCntForSale as $idDept=>$arCnts) {
      $isOdd = !$isOdd;
      if ($isOdd) {
  $cellOpen = '<td class=catalog-stripe valign=top>';
      } else {
  $cellOpen = '<td class=catalog valign=top>';
      }


    $out .= '<p class=main><big>Departments:</big></p><table>';
      $objDept = $this->objDB->Depts()->GetItem($idDept);
    $objDepts = $this->GetData('ID_Supplier='.$iSuppID,'Name');
      $strPageKey = $objDept->PageKey();
    $isFirst = true;
      $strName = $objDept->Name;
      $out .= '<tr>'.$cellOpen.'<a href="'.strtolower($strPageKey).'/">'.$strName.'</a></td>';
      $outRow = '';
      foreach ($arCnts as $idType=>$cnt) {
  if ($cnt == 1) {
      $strType = $arTypSng[$idType];
  } else {
      $strType = $arTypPlr[$idType];
  }
  if ($outRow != '') {
      $outRow .= ', ';
  }
  $outRow .= '<b>'.$cnt.'</b> '.$strType;
      }
      $out .= $cellOpen.$outRow.'</td></tr>';
}
$out .= '</table>';


    while ($objDepts->HasData()) {
CallExit('clsDepts.DoListing_forSupp()');
      $outDept = $objDepts->DoListing();
return $out;
      if ($outDept) { // only show departments with something in them
        if ($isOdd) {
          $cellOpen = '<td class=catalog-stripe valign=top>';
        } else {
          $cellOpen = '<td class=catalog valign=top>';
        }
        $keyDept = $objDepts->PageKey();
        $out .= '<tr>'.$cellOpen.'<a href="'.strtolower($keyDept).'/">'.$objDepts->Name.'</a></td>';
        $isOdd = !$isOdd;
        $out .= $cellOpen.$outDept.'</td></tr>';
      }
      $objDepts->NextRow();
     }
     }
    $out .= '</table>';
    CallExit('clsDepts.DoListing_forSupp()');
    return $out;
/**/
/**/
  }
  public function GetItem_byCatPage($iCatPage) {
    global $objDataMgr;
    CallEnter('clsDepts.GetItem_byCatPage('.$iCatPage.')');
    assert(is_object($this->objDB));
    $sqlCatPage = strtolower(str_replace('.','/',$iCatPage));
    $sqlCatPage = $this->objDB->SafeParam($sqlCatPage);
    $sql = 'SELECT * FROM _depts WHERE CatWeb_Dept="'.$sqlCatPage.'"';
    $objRec = new clsDataItem($this);
    $objDataMgr->Update('_depts','clsDepts.GetItem_byCatPage('.$iCatPage.')');
    $objRec->Query($sql);
    if ($objRec->HasData()) {
      assert($objRec->RowCount() == 1);
      $idDept = $objRec->GetValue('ID');
      $objDept = $this->GetData('ID='.$idDept);
      assert($objDept);
      return $objDept;
    } else {
      return NULL;
    }
    CallExit('clsDepts.GetItem_byCatPage()');
  }
}
}
class clsDept extends clsDataItem {
class clsDept extends clsDataSet {
  public $ID;
  public $Name;
  public $CatKey;
  public $PageKey;
  public $idSupp;
  public $isActive;
// object cache
// object cache
  private $objSupp;
    private $objSupp;


  protected function LoadResults() {
    public function SuppObj() {
    CallEnter('clsDept.LoadResults()');
if (is_object($this->objSupp)) {
    $this->ID = $this->GetValue('ID');
    return $this->objSupp;
    $this->Name = $this->GetValue('Name');
} else {
    $this->CatKey = $this->GetValue('CatKey');
    $idSupp = $this->ID_Supplier;
    $this->PageKey = $this->GetValue('PageKey');
    if ($idSupp) {
    $this->idSupp = $this->GetValue('ID_Supplier');
$this->objSupp = $this->objDB->Suppliers()->GetItem($idSupp);
    $this->isActive = $this->GetValue('isActive');
return $this->objSupp;
    $objSupp = NULL;
    } else {
     assert($this->ID);
return NULL;
     CallExit('clsDept.LoadResults()');
    }
  }
}
  public function Supplier() {
     }
    global $objFactory;
     // DEPRECATED -- use SuppObj()
 
    public function Supplier() {
    if (is_object($this->objSupp)) {
return $this->SuppObj();
      return $this->objSupp;
     }
     } else {
    public function PageKey() {
      if ($this->idSupp) {
if ($this->PageKey) {
        $this->objSupp = $objFactory->Suppliers()->GetItem($this->idSupp);
    return $this->PageKey;
        return $this->objSupp;
} else {
      } else {
    return $this->CatKey;
        return NULL;
}
      }
     }
     }
  }
    /*----
  public function PageKey() {
      PURPOSE: loads data needed to display catalog views for this department
    if ($this->PageKey) {
      HISTORY
      return $this->PageKey;
2010-11-12 disabled automatic cache update
    } else {
2010-11-16 changed sorting field from cntInPrint to cntForSale
      return $this->CatKey;
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;
     }
     }
  }
     /*-----
  protected function GetDeptData() {
      PURPOSE: Print this department's information as part of department list
     global $objDataMgr;
      HISTORY:
 
2010-11-16 $cntAvail is now cntForSale, not cntInPrint+qtyInStock
//   $sql = 'SELECT * FROM _dept_ittyps WHERE (ID_Dept='.$this->ID.') AND cntForSale ORDER BY cntInPrint DESC';
    */
    $sql = 'SELECT * FROM qryItTypsDepts_ItTyps WHERE (ID_Dept='.$this->ID.') ORDER BY cntInPrint DESC';
     public function DoListing() {
    $objItTyps = new clsTitleIttyp($this->Table);
assert('$this->ID');
    $objDataMgr->Update('_dept_ittyps','clsDept.DoListing() for ID='.$this->ID);
$objItTyps = $this->Data_forStore();
    $objItTyps->Query($sql);
$isFirst = true;
    return $objItTyps;
$out = '';
  }
while ($objItTyps->NextRow()) {
  public function DoListing() {
    if ($isFirst) {
// PURPOSE: Print this department's information as part of department list
$isFirst = false;
 
    } else {
     assert($this->ID);
$out .= ', ';
    $objItTyps = $this->GetDeptData();
    }
 
    $cntInPrint = $objItTyps->cntInPrint;
    $isFirst = true;
    $qtyInStock = $objItTyps->qtyForSale;
    while ($objItTyps->HasData()) {
    //$cntAvail = $cntInPrint + $qtyInStock;
      if ($isFirst) {
    $cntForSale = $objItTyps->cntForSale;
        $isFirst = false;
    if ($cntAvail == 1) {
      } else {
$strName = $objItTyps->ItTyp_Sng;
        $out .= ', ';
    } else {
      }
$strName = $objItTyps->ItTyp_Plr;
      $cntInPrint = $objItTyps->cntInPrint;
    }
      $qtyInStock = $objItTyps->qtyInStock;
    $out .= ' <b>'.$cntAvail.'</b> '.$strName;
// TO FIX: This is wrong. Need cntForSale field
}
      $cntAvail = $cntInPrint + $qtyInStock;
return $out;
      if ($cntAvail == 1) {
        $strName = $objItTyps->ItTypNameSng;
      } else {
        $strName = $objItTyps->ItTypNamePlr;
      }
      $out .= ' <b>'.$cntAvail.'</b> '.$strName;
      $objItTyps->NextRow();
     }
     }
     return $out;
     /*----
  }
      PURPOSE: Print page for current department
  public function DoPage() {
      ACTION:
// PURPOSE: Print page for current department
* Iterates through item types available for this department.
// ACTION:
* For each item type, prints header and then a list of titles.
// * Iterates through item types available for this department.
      HISTORY:
// * For each item type, prints header and then a list of titles.
2010-11-?? Started using cached table _title_ittyps instead of qryTitles_ItTyps_Titles
    global $objDataMgr;
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
     assert($this->ID);
    */
    $idDept = $this->ID;
     public function DoPage() {
    $objSection = new clsPageOutput();
$out = '';
    $objItTyps = $this->GetDeptData();
$idDept = $this->ID;
    $objTitles = new clsTitleExt($this->Table);
if (empty($idDept)) {
//    $objTitles = new clsDataItem($this->Table);   TRY THIS NEXT
    throw new exception('Department object has no ID');
    $objNoImgSect = new clsPageOutput();
}
    while ($objItTyps->HasData()) {
$objSection = new clsPageOutput();
      $cntInPrint = $objItTyps->cntInPrint;
// calculate the list of item types available in this department
      $qtyInStock = $objItTyps->qtyInStock;
$objItTyps = $this->Data_forStore();
// TO FIX: This is wrong. Need cntForSale field
$objTitles = new clsVbzTitle($this->objDB);
      $cntAvail = $cntInPrint + $qtyInStock;
$objNoImgSect = new clsPageOutput();
      if ($cntAvail) {
$cntSections = 0;
        $cntSections++;
while ($objItTyps->NextRow()) {
        $idItTyp = $objItTyps->ID_ItTyp;
    $cntInPrint = $objItTyps->Row['cntInPrint'];
//        $objSection->SectionHdr();
    $qtyInStock = $objItTyps->Row['qtyInStock'];
//        $sql = 'SELECT *, ID_Title AS ID, TitleName AS Name FROM _title_ittyps WHERE ((cntForSale) AND (ID_ItTyp='.$idItTyp.') AND (ID_Dept='.$idDept.'));';
    $cntAvail = $objItTyps->Row['cntForSale'];
        $sql = 'SELECT t.ID_Title AS ID, t.* FROM qryTitles_ItTyps_Titles AS t WHERE (ID_ItTyp='.$idItTyp.') AND (ID_Dept='.$idDept.');';
    if ($cntAvail) {
//        $objDataMgr->Update('_title_ittyps','clsDept.DoPage() for ID_ItTyp='.$idItTyp.', ID_Dept='.$idDept);
$cntSections++;
        $objTitles->Query($sql);
$idItTyp = $objItTyps->Row['ID_ItTyp'];
        $idTitle = $objTitles->ID;
        $sql = 'SELECT *, ID_Title AS ID, TitleName AS Name, cntInStock FROM _title_ittyps WHERE ((cntForSale) AND (ID_ItTyp='.$idItTyp.') AND (ID_Dept='.$idDept.'));';
//        $idTitle = $objTitles->GetValue('ID_Title');
//$sql = 'SELECT t.ID_Title AS ID, t.* FROM qryTitles_ItTyps_Titles AS t WHERE (ID_ItTyp='.$idItTyp.') AND (ID_Dept='.$idDept.');';
//        $objTitles->ID = $idTitle;
$objTitles->Query($sql);
        if ($objTitles->HasData()) {
$arTitles = NULL;
          while ($objTitles->HasData()) {
if ($objTitles->hasRows()) {
            $objTitle = clone $objTitles; // get a copy with current values
    while ($objTitles->NextRow()) {
            $objTitle->Res = NULL;
// add title to display list
            $lstTitles[] = $objTitle; // save it in a list
$arTitles[] = $objTitles->Values(); // save it in a list
            $objTitles->NextRow();
    }
          }
    assert('is_array($arTitles)');
          assert(is_array($lstTitles));


// We've generated the list of titles for this section; now display the section header and titles:
  // 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);
//          $out .= $objSection->ShowTitles($objItTyps->ItTypNamePlr.':',$lstTitles,$objNoImgSect);
} else {
          $out .= $objSection->ShowTitles($objItTyps->GetValue('ItTyp_Plr').':',$lstTitles,$objNoImgSect);
    echo 'ERROR: No titles found! SQL='.$sql;
        } else {
}
  echo 'ERROR: No titles found! SQL='.$sql;
$objSection->Clear();
        }
    } else {
        unset($lstTitles);
$out .= '<span class=main>Small coding error: this line should never happen.</span>'; // TO DO: log an error
        $objSection->Clear();
    }
      } else {
}
        $out .= '<span class=main>Small coding error: this line should never happen.</span>'; // TO DO: log an error
if (!$cntSections) {
      }
    $out .= '<span class=main>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.)</span>';
      $objItTyps->NextRow();
}
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();
     }
     }
     if (!$cntSections) {
     public function LinkName() {
        $out .= '<span class=main>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.)</span>';
$strURL = $this->URL_Rel();
return '<a href="'.$strURL.'">'.$this->Name.'</a>';
     }
     }
     if ($objNoImgSect->inTbl) {
     /*-----
      $objNoImgSect->EndTable();
      RETURNS: The string which, when prepended to a Title's CatKey, would form the Title's catalog number
      $objSection->AddText($objNoImgSect->out);
    */
      $objSection->EndTable();
    public function CatPfx() {
      $out .= $objSection->out;
$strFull = strtoupper($this->Supplier()->CatKey);
if ($this->AffectsCatNum()) {
    $strFull .= '-'.strtoupper($this->CatKey);
}
return $strFull.'-';
     }
     }
     return $out;
     /*-----
  }
      RETURNS: TRUE if this department affects the catalog number (i.e. if CatKey is non-blank)
  public function LinkName() {
     */
     $strURL = $this->Supplier()->URL();
     public function AffectsCatNum() {
     if ($this->PageKey) {
return ($this->CatKey != '');
      $strURL .= strtolower($this->PageKey).'/';
     }
     }
//    return '<a href="'.KWP_CAT_REL.$strURL.'">'.$this->Name.'</a>';
    return '<a href="'.$strURL.'">'.$this->Name.'</a>';
  }
}
}


class clsTitles extends clsDataTable {
class clsVbzTitles extends clsVbzTable {
  public function __construct($iDB,$iTable='titles') {
    public function __construct($iDB) {
    parent::__construct($iDB,$iTable);
parent::__construct($iDB);
  }
  $this->Name('cat_titles');
  protected function _newItem() {
  $this->KeyName('ID');
    CallStep('clsTitles._newItem()');
  $this->ClassSng('clsVbzTitle');
     return new clsTitle($this);
    }
  }
    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) {
   public function GetItem_byCatNum($iCatNum) {
     global $objDataMgr;
     global $objDataMgr;


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


  protected function LoadResults() {
    // show "small" images
    CallEnter('clsTitle.LoadResults()');
if (!$this->hideImgs) {
    $this->ID = $this->GetValue('ID');
    $objImgs = $this->ListImages('sm');
    $this->Name = $this->GetValue('Name');
    if ($objImgs->hasRows()) {
    $this->CatKey = $this->GetValue('CatKey');
while ($objImgs->NextRow()) {
    $this->idDept = $this->GetValue('ID_Dept');
    $strImgTag = $objImgs->AttrDispl;
    assert($this->ID);
    $urlRel = $objImgs->Spec;
    CallExit('clsTitle.LoadResults()');
    $idImg = $objImgs->ID;
  }
    //$strImg = '<img src="'.KWP_IMG_MERCH.$urlRel.'"';
  protected function LoadResStandard() {
    $strImg = '<img src="'.$objImgs->WebSpec().'"';
  }
    if ($strImgTag) {
  protected function LoadResExtended() {
$strImg .= ' title="'.$strImgTag.'"';
// extended fields
    }
  }
    $strImg .= '>';
  public function Dept() {
    $objImgBig = $objImgs->ImgForSize('big');
    global $objFactory;
    if (is_object($objImgBig)) {
if ($objImgBig->FirstRow()) {
    $strImg = $objImgBig->Href().$strImg.'</a>';
}
    }
    $objSection->AddText($strImg);
}
    } else {
      $objSection->ShowImgUnavail();
    }
}


    if (is_object($this->objDept)) {
// list topics for this title
      return $this->objDept;
$db = $this->Engine();
    } else {
$tbl = $db->TitleTopic_Topics();
      if ($this->idDept) {
$tbl->doBranch(TRUE);
        $objDept = $objFactory->Depts()->GetItem($this->idDept);
$rs = $tbl->GetTitle($idTitle);
        $this->objDept = $objDept;
if ($rs->hasRows()) {
        assert(is_object($objDept));
    $txt = '<table align=right style="border: solid 1px; background: black;"><tr><td bgcolor=#333333>';
        return $objDept;
    $txt .= '<b>'.$db->Topics()->IndexLink('Topics').'</b>:';
      } else {
    while ($rs->NextRow()) {
        return NULL;
$txt .= '<br>- '.$rs->ShopLink();
      }
    }
    }
    $txt .= '</td></tr></table>';
  }
    $objSection->AddText($txt);
  public function DoPage() {
}
    global $objFactory,$objDataMgr;


    $idTitle = $this->ID;
    assert($idTitle);
    $objSection = new clsPageOutput();
// show small-size images
    if (!$this->hideImgs) {
      $objImgs = $this->ListImages('sm');
      if ($objImgs->HasData()) {
        while ($objImgs->HasData()) {
          $strImgTag = $objImgs->GetValue('AttrDispl');
          $urlRel = $objImgs->GetValue('Spec');
          $idImg = $objImgs->ID;
          $strImg = '<img src="'.KWP_IMG_MERCH.$urlRel.'"';
          if ($strImgTag) {
            $strImg .= ' title="'.$strImgTag.'"';
          }
          $strImg .= '>';
          $objImgBig = $objImgs->ImgForSize('big');
          if (is_object($objImgBig)) {
            if ($objImgBig->HasData()) {
              $strImg = $objImgBig->Href().$strImg.'</a>';
            }
          }
          $objSection->AddText($strImg);
          $objImgs->NextRow();
        }
      } else {
        $objSection->ShowImgUnavail();
      }
    }
// now list available items as table
// now list available items as table
//   $sql = 'SELECT * FROM _title_ittyps WHERE (ID_Title='.$idTitle.') AND cntForSale ORDER BY IFNULL(ItTypSort,"ZZ")';
//$sql = 'SELECT * FROM qryTitles_ItTyps_ItTyps WHERE (ID_Title='.$idTitle.') ORDER BY ItTyp_Sort IS NULL, ItTyp_Sort;';
    $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;';
// echo '<!-- SQL for grouping: '.$sql.' -->';
$rsTypes = $this->Engine()->DataSet($sql);
    $objTypes = new clsTitleIttyp($this->Table);
$isItem = FALSE;
//    $objDataMgr->Update('_title_ittyps','clsTitle.DoPage()');
if ($rsTypes->hasRows()) {
    $objTypes->Query($sql); // list of all item types available for this title
    if (KF_CART_ABSOLUTE) {
    if ($objTypes->HasRows()) {
      $urlCart = KWP_CART_ABS;
// $objSection->AddText('<br>Found '.$objTypes->RowCount().' item(s):');
    } else {
      if (KF_CART_ABSOLUTE) {
      $urlCart = KWP_CART_REL;
        $urlCart = KWP_CART_ABS;
    }
      } else {
    $objSection->AddText('<form method=post action="'.$urlCart.'"><input type=hidden name=from value=browse-multi>');
        $urlCart = KWP_CART_REL;
 
      }
    $flagDisplayTogether = false; // hard-coded for now
      $objSection->AddText('<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 = '<tr><th align=left>Option</th><th>Status</th><th align=right class=title-price>Price</th><th align=center class=orderQty>Order<br>Qty.</th><th><i>list<br>price</th></tr>';
    $txtTblHdr = $tblItems->Render_TableHdr();
    $txtInStock = $txtOutStock = '';


      while ($objTypes->HasData()) {
    while ($rsTypes->NextRow()) {
        $idItTyp = $objTypes->ID_ItTyp;
$idItTyp = $rsTypes->Value('ID_ItTyp');
        assert($idItTyp);
assert('$idItTyp');
$sql = '(ID_Title='.$idTitle.') AND (ID_ItTyp='.$idItTyp.')';
$sqlFilt = '(ID_Title='.$idTitle.') AND (ID_ItTyp='.$idItTyp.')';
// $strGrpCode = $objTypes->GetValue('GrpCode');
$sqlSort = 'GrpSort,GrpDescr,ItOpt_Sort';
// $strGrpDescr = $objTypes->GetValue('GrpDescr');
$rsItems = $tblItems->GetData($sqlFilt,NULL,$sqlSort);
        $objItems = $objFactory->Items()->GetData($sql,'GrpSort,GrpDescr,ItOpt_Sort');
//$idItType = 0;
        assert($objItems->HasData());
        $idItType = 0;
//        $txtLine = '<tr class=typeHdr><td colspan=3><b>'.$objTypes->Ittyp()->Name().'</b>:</td></tr>';


        $txtLine = '<tr class=typeHdr><td colspan=3><b>'.$objTypes->GetValue('ItTyp_Plr').'</b>:</td></tr>';
$txtLine = '<tr class=typeHdr><td colspan=3><b>'.$rsTypes->Value('ItTypNamePlr').'</b>:</td></tr>';


        if ($flagDisplayTogether) {
if ($flagDisplayTogether) {
// displaying all items in a single listing
// displaying all items in a single listing
          $txtBoth .= $txtLine;
    $txtBoth .= $txtLine;
        } else {
} else {
// set flags to determine which stock-status sections to show
// set flags to determine which stock-status sections to show
          $cntInStock = $objTypes->cntInStock;
    $cntInStock = $rsTypes->Value('cntInStock');
          $cntForSale = $objTypes->cntForSale;
    $cntForSale = $rsTypes->Value('cntForSale');
          $cntOutStock = $cntForSale - $cntInStock;
    $cntOutStock = $cntForSale - $cntInStock;
// DEBUG:
//$objSection->AddText("<!--\n\nfor sale: $cntForSale .. in stock: $cntInStock .. outta stock: $cntOutStock\n\n-->");
          if ($cntInStock > 0) {
            $txtInStock .= $txtLine;
          }
          if ($cntOutStock > 0) {
            $txtOutStock .= $txtLine;
          }
        }


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


            if ($flagDisplayTogether) {
if ($flagDisplayTogether) {
              $txtBoth .= $txtLine;
    $txtBoth .= $txtLine;
            } else {
} else {
              if ($objItems->qtyInStock > 0) {
    if ($qtyStk > 0) {
                $txtInStock .= $txtLine;
$txtInStock .= $txtLine;
              }
    } else {
              if ($objItems->qtyInStock == 0) {
$txtOutStock .= $txtLine;
                $txtOutStock .= $txtLine;
    }
              }
}
            }
    }
          }
}
          $objItems->NextRow();
    }
        }
        $objTypes->NextRow();
      }


// DO the actual display of the accumulated text
// DO the actual display of the accumulated text
    $txtTblOpen = '<table class=main><tbody>';
    $txtTblFtr = '<tr><td colspan="4" align="right"><input value="Add to Cart" type="submit"></td></tr>';
    $txtTblShut = '</tbody></table>';
    if ($flagDisplayTogether) {
// Display in-stock and backordered items together
$objSection->AddText($txtTblOpen);
$objSection->AddText($txtTblHdr);
$objSection->AddText($txtBoth);
$objSection->AddText($txtTblFtr);
$objSection->AddText($txtTblShut);
    } else {
if ($txtInStock != '') {
    $txtClause = Pluralize($cntInStock,'This item is','These items are');
    $objSection->AddText($txtTblOpen);
    $objSection->AddText('<tr class=inStock><td colspan=5>'.$txtClause.' in stock:</td></tr>');
    $objSection->AddText($txtTblHdr);
    $objSection->AddText($txtInStock);
    $objSection->AddText($txtTblFtr);
    $objSection->AddText($txtTblShut);
}


      $txtTblOpen = '<table class=main><tbody>';
if (!empty($txtOutStock)) {
      $txtTblHdr = '<tr><th align=left>Option</th><th>Status</th><th align=right class=title-price>Price</th><th align=center class=orderQty>Order<br>Qty.</th><th><i>list<br>price</th></tr>';
    if (!empty($txtInStock)) {
      $txtTblFtr = '<tr><td colspan="4" align="right"><input value="Add to Cart" type="submit"></td></tr>';
$objSection->AddText('<p>');
      $txtTblShut = '</tbody></table>';
    }
    $txtClause = Pluralize($cntOutStock,'This item is','These items are');
    $objSection->AddText($txtTblOpen);
    $objSection->AddText('<tr><td colspan=5>'.$txtClause.' <a href="'.KWP_HELP_NO_STOCK_BUT_AVAIL.'"><b>not in stock</b></a>');
    $txtClause = Pluralize($cntOutStock,'it','them');
    $objSection->AddText(', but we can (probably) <a href="'.KWP_HELP_POLICY_SHIP.'">get '.$txtClause.'</a>:</td></tr>');
    $objSection->AddText($txtTblHdr);
    $objSection->AddText($txtOutStock);
    $objSection->AddText($txtTblFtr);
    $objSection->AddText($txtTblShut);
} /**/
    }
    $objSection->AddText('</form>');
}
if (!$isItem) {
    $objSection->SectionHdr('This title is currently unavailable');
}
return $objSection->out."\n<!-- TITLE ID=$idTitle -->\n";
    }
    /*----
      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;


       if ($flagDisplayTogether) {
$arOut['cnt.active'] = $intActive;
// Display in-stock and backordered items together
$arOut['cnt.retired'] = $intRetired;
        $objSection->AddText($txtTblOpen);
$arOut['txt.cat.num'] = $txtCatNum;
        $objSection->AddText($txtTblHdr);
$arOut['ht.link.open'] = $htLink;
        $objSection->AddText($txtBoth);
$arOut['ht.cat.line'] = $htLink.$txtCatNum.'</a> '.$txtName;
        $objSection->AddText($txtTblFtr);
 
        $objSection->AddText($txtTblShut);
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 .= '<b>'.$cntType.'</b> '.$strType;
    if (!empty($qtyStk)) {
$outHTMLQty .= '<b>'.$qtyStk.'</b> '.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.
    */
    public function CatNum($iSep='-') {
      if (empty($this->Row['CatNum'])) {
  $objDept = $this->Dept();
  if (is_object($objDept)) {
      $objSupp = $objDept->Supplier();
      $strDeptKey = $objDept->CatKey;
      $strOut = $objSupp->CatKey;
      if ($strDeptKey) {
$strOut .= $iSep.$strDeptKey;
      }
  } else {
      $strOut = '?';
  }
  $strOut .= $iSep.$this->CatKey;
       } else {
       } else {
        if ($txtInStock != '') {
  $strOut = $this->CatNum;
          $txtClause = Pluralize($cntInStock,'This item is','These items are');
          $objSection->AddText($txtTblOpen);
          $objSection->AddText('<tr class=inStock><td colspan=5>'.$txtClause.' in stock:</td></tr>');
          $objSection->AddText($txtTblHdr);
          $objSection->AddText($txtInStock);
          $objSection->AddText($txtTblFtr);
          $objSection->AddText($txtTblShut);
        }
        if ($txtOutStock != '') {
          if ($txtInStock != '') {
            $objSection->AddText('<p>');
          }
          $txtClause = Pluralize($cntOutStock,'This item is','These items are');
          $objSection->AddText($txtTblOpen);
          $objSection->AddText('<tr><td colspan=5>'.$txtClause.' <a href='.'"http://wiki.vbz.net/Available_but_not_in_stock"><b>not in stock</b></a>');
          $txtClause = Pluralize($cntOutStock,'it','them');
          $objSection->AddText(', but we can (probably) <a href='.'"http://wiki.vbz.net/Shipping_Policies">get '.$txtClause.'</a>:</td></tr>');
          $objSection->AddText($txtTblHdr);
          $objSection->AddText($txtOutStock);
          $objSection->AddText($txtTblFtr);
          $objSection->AddText($txtTblShut);
        } /**/
       }
       }
//      $objSection->AddText('<tr><td colspan="4" align="right"><input value="Add to Cart" type="submit"></td></tr></tbody></table></form>');
       return strtoupper($strOut);
       $objSection->AddText('</form>');
    } else {
      $objSection->SectionHdr('This title is currently unavailable');
     }
     }
     return $objSection->out;
  public function URL_part() {
     return strtolower($this->CatNum('/'));
   }
   }
   public function ListImages($iSize) {
   public function URL($iBase=KWP_CAT_REL) {
     global $objFactory;
     return $iBase.$this->URL_part();
 
    $objImgs = $objFactory->Images()->GetData('(ID_Title='.$this->ID.') AND (Ab_Size="'.$iSize.'")','AttrSort');
    return $objImgs;
   }
   }
  public function CatNum($iSep='-') {
    public function Link(array $iarAttr=NULL) {
    $objDept = $this->Dept();
$strURL = $this->URL();
    $objSupp = $objDept->Supplier();
$htAttr = ArrayToAttrs($iarAttr);
    $strDeptKey = $objDept->CatKey;
return '<a'.$htAttr.' href="'.$strURL.'">';
    $strOut = $objSupp->CatKey;
    if ($strDeptKey) {
      $strOut .= $iSep.$strDeptKey;
     }
     }
    $strOut .= $iSep.$this->CatKey;
   public function LinkAbs() {
    return strtoupper($strOut);
     $strURL = $this->URL(KWP_CAT);
  }
   public function Link() {
     $strRel = $this->CatNum('/');
     return '<a href="'.$strURL.'">';
     return '<a href="'.$strURL.'">';
   }
   }
Line 1,374: Line 1,932:
     return $this->Link().$this->Name.'</a>';
     return $this->Link().$this->Name.'</a>';
   }
   }
}
/* 2010-11-06 if this is needed, use a method in SpecialVbzAdmin
// extended Title data from v_titles
   public function LinkName_wt() {
class clsTitlesExt extends clsTitles {
// TO DO: make this more configurable
   public function __construct($iDB,$iTable='v_titles') {
     $out = '[[vbznet:cat/'.$this->URL_part().'|'.$this->Name.']]';
     parent::__construct($iDB,$iTable);
     return $out;
  }
  protected function _newItem() {
    CallStep('clsTitlesExt._newItem()');
     return new clsTitleExt($this);
   }
   }
*/
}
}
// this encapsulates [_titles] instead of [titles]
class clsTitleExt extends clsTitle {
  public $CatNum;
  public $CatWeb;
  public $curMinPrice;
  public $curMaxPrice;
  public $idItTyp;
  public $qtyInStock;
// action flags
  public $dontLoadBasic;
  protected function LoadResults() {
    CallEnter('clsTitleExt.LoadResults()');
    if (!$this->dontLoadBasic) {
      parent::LoadResults();
    }


    $this->CatNum = $this->GetValue('CatNum');
/*====
    $this->CatWeb = $this->GetValue('CatWeb');
   PURPOSE: TITLE/ITTYP hybrid
    $this->currMinPrice = $this->GetValue('currMinPrice');
  TABLE: _title_ittyps
    $this->currMaxPrice = $this->GetValue('currMaxPrice');
*/
    $this->idItTyp = $this->GetValue('ID_ItTyp');
class clsTitleIttyp extends clsDataSet {
    $this->qtyInStock = $this->GetValue('qtyInStock');
 
    CallExit('clsTitle.LoadResults()');
  }
  public function Link() {
    $out = '<a class="thumb" href="'.$this->URL().'">';
    return $out;
  }
  public function URL() {
    return KWP_CAT_REL.$this->CatWeb.'/';
   }
}
/* -------------------- *\
    TITLE/ITTYP hybrid
\* -------------------- */
class clsTitleIttyp extends clsDataItem_noID {
  public $ID_ItTyp;
  public $ID_Dept;
  public $cntForSale;
  public $cntInPrint;
  public $cntInStock;
  public $qtyInStock;
  public $ItTypNameSng;
  public $ItTypNamePlr;
// object cache
// object cache
   private $objIttyp;
   private $objIttyp;


  protected function LoadResults() {
    CallEnter('clsTitleIttyp.LoadResults()');
    $idItTyp = $this->GetValue('ID_ItTyp');
    if ($idItTyp != $this->ID_ItTyp) {
      $this->ID_ItTyp = $this->GetValue('ID_ItTyp');
      $this->objIttyp = NULL;
    }
    $this->ID_Title = $this->GetValue('ID_Title');
    $this->ID_Dept = $this->GetValue('ID_Dept');
    $this->cntForSale = $this->GetValue('cntForSale');
    $this->cntInPrint = $this->GetValue('cntInPrint');
    $this->cntInStock = $this->GetValue('cntInStock');
    $this->qtyInStock = $this->GetValue('qtyInStock');
    $this->ItTypNameSng = $this->GetValue('ItTypNameSng');
    $this->ItTypNamePlr = $this->GetValue('ItTypNamePlr');
    if ($this->ID_ItTyp) { } else {
      echo 'ERROR: ID_ItTyp has no value! data = ';
      DumpArray($this->Row,TRUE);
    }
    CallExit('clsTitleIttyp.LoadResults()');
  }
   public function Ittyp() {
   public function Ittyp() {
    global $objFactory;
     if (is_null($this->objIttyp)) {
     if (is_null($this->objIttyp)) {
       $this->objIttyp = $objFactory->ItTyps()->GetItem($this->ID_ItTyp);
       $this->objIttyp = VbzClasses::ItTyps()->GetItem($this->ID_ItTyp);
     }
     }
     return $this->objIttyp;
     return $this->objIttyp;
Line 1,466: Line 1,959:
     ITEM classes
     ITEM classes
\* -------------------- */
\* -------------------- */
class clsItems extends clsDataTable {
class clsItems extends clsVbzTable {
  public function __construct($iDB,$iTable='cat_items') {
 
    global $objDataMgr;
    public function __construct($iDB) {
    $objDataMgr->Update('cat_items','clsItems()');
parent::__construct($iDB);
    parent::__construct($iDB,$iTable);
  $this->Name('cat_items');
  }
  $this->KeyName('ID');
  protected function _newItem() {
  $this->ClassSng('clsItem');
    CallStep('clsTitles._newItem()');
    }
     return new clsItem($this);
    /*----
  }
      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 '<tr>'
  .'<th align=left>Option</th>'
  .'<th>Status</th>'
  .'<th align=center><i>List<br>Price</th>'
  .'<th align=center class=title-price>Our<br>Price</th>'
  .'<th align=center class=orderQty>Order<br>Qty.</th>'
  .'</tr>';
    }
}
}
class clsItem extends clsDataItem {
/* ===============
// NOTE: "in stock" always refers to stock for sale, not stock which has already been purchased
CLASS: clsItem
   public $CatNum;
NOTES:
   public $isForSale;
  * "in stock" always refers to stock for sale, not stock which has already been purchased
  public $isInPrint;
   * 2009-12-03: The above note does not clarify anything.
  public $qtyInStock;
   * Four methods were moved here from clsShopCartLine in shop.php: ItemSpecs(), ItemDesc(), ItemDesc_ht(), ItemDesc_wt()
  public $idTitle;
    They are used for displaying a full description of an item, in both shop.php and SpecialVbzAdmin
  public $idItTyp;
*/
  public $idItOpt;
class clsItem extends clsDataSet {
  public $ItOptDescr;
  public $PriceSell;
  public $PriceList;
// object cache
// object cache
  private $objTitle;
    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();


  protected function LoadResults() {
    $out['tname'] = $this->objTitle->Name;
    CallEnter('clsItem.LoadResults()');
    $out['ittyp'] = $this->objItTyp->Name($this->Qty);
    $this->ID = $this->GetValue('ID');
    $out['itopt'] = $this->objItOpt->Descr;
    $this->CatNum = $this->GetValue('CatNum');
    return $out;
    $this->isForSale = $this->GetValue('isForSale');
} else {
    $this->isInPrint = $this->GetValue('isInPrint');
    return $iSpecs;
     $this->qtyInStock = $this->GetValue('qtyInStock');
}
    $this->idTitle = $this->GetValue('ID_Title');
     }
    $this->idItTyp = $this->GetValue('ID_ItTyp');
    public function DescLong(array $iSpecs=NULL) { // plaintext
    $this->idItOpt = $this->GetValue('ID_ItOpt');
if (is_null($this->Value('Descr'))) {
    $this->ItOptDescr = $this->GetValue('ItOpt_Descr');
    $sp = $this->DescSpecs($iSpecs);
    $this->PriceSell = $this->GetValue('PriceSell');
 
    $this->PriceList = $this->GetValue('PriceList');
    $strItOpt = $sp['itopt'];
    assert($this->ID);
 
     CallExit('clsItem.LoadResults()');
    $out = '"'.$sp['tname'].'" ('.$sp['ittyp'];
  }
    if (!is_null($strItOpt)) {
  public function Print_TableRow() {
$out .= ' - '.$strItOpt;
// ASSUMES: This item is ForSale, so isForSale = true and (qtyForSale>0 || isInPrint) = true
    }
    $qtyInStock = $this->QtyInStock(true);
    $out .= ')';
    if ($qtyInStock) {
} else {
      $strClass = 'inStock';
    $out = $this->Value('Descr');
      $strStock = $qtyInStock.' in stock';
}
    } else {
 
      $strClass = 'noStock';
return $out;
     }
    public function DescLong_ht(array $iSpecs=NULL) { // as HTML
$sp = $this->DescSpecs($iSpecs);
 
$htTitleName = '<i>'.$this->Title()->LinkName().'</i>';
$strItOpt = $sp['itopt'];
 
$out = $htTitleName.' ('.$sp['ittyp'];
if (!is_null($strItOpt)) {
    $out .= ' - '.$strItOpt;
}
$out .= ')';
 
return $out;
     }
     }
     $out = '<tr class='.$strClass.'><!-- ID='.$this->ID.' -->';
     /*-----
    $out .= '<td>&emsp;'.$this->ItOptDescr;
      ASSUMES:
    if ($this->isInPrint) {
  This item is ForSale, so isForSale = true and (qtyForSale>0 || isInPrint) = true
      if ($qtyInStock) {
      HISTORY:
        $strStatus = $strStock.'; more available';
  2011-01-24 Renamed Print_TableRow() -> Render_TableRow; corrected to match header
      } else {
    */
        $strStatus = '<a title="explanation..." href="'.KWP_WIKI.'/Available_but_not_in_stock">available, not in stock</a>';
    public function Render_TableRow() {
      }
$arStat = $this->AvailStatus();
    } else {
$strCls = $arStat['cls'];
      $strStatus = '<b>'.$strStock.'</b> - <i>out of print!</i>';
 
$out = '<tr class='.$strCls.'><!-- ID='.$this->ID.' -->';
$out .= '<td>&emsp;'.$this->Value('ItOpt_Descr').'</td>';
$out .= '<td>'.$arStat['html'].'</td>';
$out .= '<td align=right><i>'.DataCurr($this->Value('PriceList')).'</i></td>';
$out .= '<td align=right>'.DataCurr($this->Value('PriceSell')).'</td>';
$out .= '<td>'.'<input size=3 name="qty-'.$this->Value('CatNum').'"></td>';
$out .= '</tr>';
return $out;
     }
     }
     $out .= '<td>'.$strStatus.'</td>';
     /*----
    $out .= '<td>'.DataCurr($this->PriceSell).'</td>';
      ACTION: Returns an array with human-friendly text about the item's availability status
    $out .= '<td>'.'<input size=3 name="qty-'.$this->CatNum.'"></td>';
      RETURNS:
    if ($this->PriceList) {
array['html']: status text, in HTML format
      $out .= '<td><i>'.DataCurr($this->PriceList).'</i></td>';
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 '<pre>'.print_r($this->Row,TRUE).'</pre>';
      $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 = '<b>'.$strStk.'</b> - <i>possibly out of print</i>';
    } else {
$txt = '<b>'.$strStk.'</b> - <i>out of print!</i>';
    }
}
$arOut['html'] = $txt;
$arOut['cls'] = $strCls;
return $arOut;
     }
     }
    $out .= '</tr>';
 
    return $out;
  public function Title() {
      $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->Title()->Supplier();
   }
   }
   public function QtyInStock($iNeedQty) {
   public function ItTyp() {
// DEPRECATED
      $doLoad = TRUE;
// Originally, this field might have contained a "-1" to indicate
      if (is_object($this->objItTyp)) {
// that some items were in stock but the number hadn't been
  if ($this->objItTyp->ID == $this->ID_ItTyp) {
// calculated. We're not doing that anymore.
      $doLoad = FALSE;
/*
  }
    if (is_null($this->qtyInStock)) {
      }
      $doFigure = true;
       if ($doLoad) {
    } else {
  $this->objItTyp = $this->objDB->ItTyps()->GetItem($this->ID_ItTyp);
       if ($this->qtyInStock == -1) {
        if ($iNeedQty) {
          $doFigure = true;
        }
       }
       }
    }
       return $this->objItTyp;
    if ($doFigure) {
       $this->DoFigureStock();
    }
*/
    return $this->qtyInStock;
   }
   }
   public function Title() {
   public function ItOpt() {
    global $objFactory;
 
     $doLoad = TRUE;
     $doLoad = TRUE;
     if (is_object($this->objTitle)) {
     if (is_object($this->objItOpt)) {
       if ($this->objTitle == $this->idTitle) {
       if ($this->objItOpt->ID == $this->ID_ItOpt) {
         $doLoad = FALSE;
         $doLoad = FALSE;
       }
       }
     }
     }
     if ($doLoad) {
     if ($doLoad) {
       $this->objTitle = $objFactory->Titles()->GetItem($this->idTitle);
       $this->objItOpt = $this->objDB->ItOpts()->GetItem($this->ID_ItOpt);
     }
     }
     return $this->objTitle;
     return $this->objItOpt;
   }
   }
   public function StoreLink() {
    // 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);
    }
/*
/*
RETURNS: HTML suitable for use within the store
    public function GetData_Cached($iWhere=NULL,$iClass=NULL,$iSort=NULL) {
NOTE: Since the store doesn't yet have pages for each item,
return $this->Cache()->GetData($iWhere,$iClass,$iSort);
this returns the store's Title link
    }
*/
*/
     return $this->Title-Link();
     /*----
  }
      FUTURE:
  public function AdminLink() {
* This method really belongs with Admin functions, since it will never be used in the standalone store
    return '<a href="item='.$this->ID.'">';
* If table ever grows to a significant size, we might end up changing the filtering criteron.
  }
      HISTORY:
/*
2010-11-21 Adapted from clsFolders.
  private function DoFigureStock() {
    */
    global $objFactory;
    public function DropDown($iName=NULL,$iDefault=NULL,$iChoose=NULL) {
 
$strName = is_null($iName)?($this->Table->ActionKey()):$iName;
    $qtyInStock = $objFactory->StkItems()->QtyInStock_forItem($this->ID);
$arRows = $this->Cache()->GetData_array('isType',NULL,'Sort, NameSng');
//    assert(!is_null($qtyInStock));
$out = $this->DropDown_for_array($arRows,$strName,$iDefault,$iChoose);
//   $sql = 'UPDATE cat_items SET qtyInStock='.$qtyInStock.' WHERE ID='.$this->ID;
return $out;
    assert(is_object($this->Table));
    }
    assert(is_object($this->Table->DB()));
    /*----
    $this->Table->DB()->Exec($sql);
      ACTION: same as clsItTyp::DropDown_for_rows, but takes an array
     $this->qtyInStock = $qtyInStock;
      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
    }
}
if ($iCount == 1) {
    $out = array_key_exists('NameSng',$this->Row)?$this->Row['NameSng']:NULL;
} else {
    $out = array_key_exists('NamePlr',$this->Row)?$this->Row['NamePlr']:NULL;
}
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;
    }
}
}
class clsItemsExt extends clsItems {
/* -------------------- *\
   public function __construct($iDB,$iTable='v_items') {
    ITEM OPTION classes
     parent::__construct($iDB,$iTable);
\* -------------------- */
  }
class clsItOpts extends clsVbzTable {
  protected function _newItem() {
   public function __construct($iDB) {
    CallStep('clsItemsExt._newItem()');
     parent::__construct($iDB);
    return new clsItemExt($this);
      $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 clsItemExt extends clsItem {
class clsItOpt extends clsDataSet {
  public $OptSort;
     /*----
 
      RETURNS: Approximately as much description as will fit nicely into a choice line for a drop-down or selection box
  protected function LoadResults() {
     */
     CallEnter('clsItemExt.LoadResults()');
     public function ChoiceLine() {
    parent::LoadResults();
return $this->Value('CatKey');
 
     }
    $this->OptSort = $this->GetValue('OptSort');
    $this->CatWeb = $this->GetValue('CatWeb');
    $this->currMinPrice = $this->GetValue('currMinPrice');
     $this->currMaxPrice = $this->GetValue('currMaxPrice');
     $this->idItTyp = $this->GetValue('ID_ItTyp');
    $this->qtyInStock = $this->GetValue('qtyInStock');
 
     CallExit('clsItemExt.LoadResults()');
  }
}
}
/* -------------------- *\
/* -------------------- *\
     ITEM TYPE classes
     SHIP COST classes
\* -------------------- */
\* -------------------- */
class clsItTyps extends clsDataTable {
class clsShipCosts extends clsVbzTable {
   public function __construct($iDB) {
   public function __construct($iDB) {
     global $objDataMgr;
     parent::__construct($iDB);
    $objDataMgr->Update('cat_ittyps','clsItems()');
      $this->Name('cat_ship_cost');
    parent::__construct($iDB,'cat_ittyps');
      $this->KeyName('ID');
  }
      $this->ClassSng('clsShipCost');
  protected function _newItem() {
    CallStep('clsItTyps._newItem()');
    return new clsItTyp($this);
   }
   }
    // ==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 clsItTyp extends clsDataItem {
class clsShipCost extends clsDataSet {
  public $ID;
     /*----
  public $NameSng;
       RETURNS: Approximately as much description as will fit nicely into a choice line for a drop-down or selection box
  public $NamePlr;
     */
//  public $cntInPrint;
     public function ChoiceLine() {
//  public $qtyInStock;
return $this->Value('Descr');
//  public $usePrefix;
 
  protected function LoadResults() {
     CallEnter('clsItTyp.LoadResults()');
/*
    if ($this->usePrefix) {
      $this->ID = $this->GetValue('ID_ItTyp');
      $this->NameSng = $this->GetValue('ItTypNameSng');
      $this->NamePlr = $this->GetValue('ItTypNamePlr');
       $this->cntInPrint = $this->GetValue('cntInPrint');
     } else {
/**/
      $this->ID = $this->GetValue('ID');
      $this->NameSng = $this->GetValue('NameSng');
      $this->NamePlr = $this->GetValue('NamePlr');
//    }
    assert($this->ID);
     CallExit('clsItTyp.LoadResults()');
  }
  public function Name($iCount=-1) {
    if ($iCount == -1) {
      $iCount = $this->cntInPrint;
    }
    if ($iCount == 1) {
      return $this->NameSng;
    } else {
      return $this->NamePlr;
     }
     }
  }
}
}
/* -------------------- *\
/* -------------------- *\
     STOCK ITEM classes
     STOCK ITEM classes
\* -------------------- */
\* -------------------- */
class clsStkItems extends clsDataTable {
/*====
  public function __construct($iDB) {
  CLASS PAIR: Items_Stock
    parent::__construct($iDB,'stk_items');
  PURPOSE: items with stock information (from a query or cache)
  }
    Similar to clsItem, but with different fields
  public function QtyInStock_forItem($iItemID) {
  HISTORY:
    $sql = 'SELECT SUM(s.Qty) AS Qty FROM stk_items AS s LEFT JOIN stk_bins AS sb ON s.ID_Bin=sb.ID WHERE (s.ID_Item='.$iItemID.') AND (s.WhenRemoved IS NULL) AND (sb.WhenVoided IS NULL) AND (sb.isForSale) GROUP BY s.ID_Item';
    2011-01-24 disabled -- use cat_items (has all necessary fields cached)
// ** TO DO: maybe this can use a view or one of the calculated tables now?
*/
/*
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 '<tr>'
  .'<th align=left>Option</th>'
  .'<th>Status</th>'
  .'<th align=right class=title-price>Price</th>'
  .'<th align=center class=orderQty>Order<br>Qty.</th>'
  .'<th><i>list<br>price</th>'
  .'</tr>';
    }
}
*/
//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'];


    $objStock = new clsDataItem($this);
$out = '<tr class='.$strCls.'><!-- ID='.$this->ID.' -->';
    $objStock->Query($sql);
$out .= '<td>&emsp;'.$this->ItOpt_Descr.'</td>';
    if ($objStock->HasData()) {
$out .= '<td>'.$arStat['html'].'</td>';
      if ($objStock->RowCount()) {
$out .= '<td>'.DataCurr($this->PriceSell).'</td>';
        assert($objStock->RowCount() == 1);
$out .= '<td>'.'<input size=3 name="qty-'.$this->CatNum.'"></td>';
        return $objStock->GetValue('Qty');
if ($this->PriceList) {
      }
    $out .= '<td><i>'.DataCurr($this->PriceList).'</i></td>';
}
$out .= '</tr>';
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
    */
/*
/*
  protected function _newItem() {
    private function AvailStatus() {
     CallStep('clsStkItems._newItem()');
//echo 'SQL=['.$this->sqlMake.']';
     return new clsStkItem($this);
//echo '<pre>'.print_r($this->Row,TRUE).'</pre>';
  }
      $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 = '<b>'.$strStk.'</b> - <i>out of print!</i>';
}
$arOut['html'] = $txt;
$arOut['cls'] = $strCls;
return $arOut;
    }
}
*/
class clsStkItems extends clsVbzTable {
    public function __construct($iDB) {
parent::__construct($iDB);
  $this->Name('stk_items');
  $this->KeyName('ID');
     }
    /*-----
      RETURNS: Recordset containing list of stock for the given item (qtys, bin, bin name, place, place name, notes)
    */
    public function List_forItem($iItemID) {
$sql = 'SELECT '
  .'ID, QtyForSale, QtyForShip, QtyExisting, ID_Bin, ID_Place, BinCode, WhName, Notes '
  .'FROM qryStk_lines_remaining WHERE (ID_Item='.$iItemID.');';
$objStock = $this->objDB->DataSet($sql,$this->ClassSng());
$objStock->Table = $this;
return $objStock;
    }
/* (2010-06-15) This doesn't seem to be used anywhere, and possibly does not work.
     public function QtyInStock_forItem($iItemID) {
$sql = 'SELECT SUM(s.Qty) AS Qty FROM stk_items AS s LEFT JOIN stk_bins AS sb ON s.ID_Bin=sb.ID WHERE (s.ID_Item='.$iItemID.') AND (s.WhenRemoved IS NULL) AND (sb.WhenVoided IS NULL) AND (sb.isForSale) GROUP BY s.ID_Item';
 
$objStock = new clsDataItem($this);
$objStock->Query($sql);
if ($objStock->NextRow()) {
    assert('is_resource($objStock->Res)');
    if ($objStock->RowCount()) {
assert('$objStock->RowCount() == 1');
return $objStock->Qty;
    }
} else {
    return NULL;
}
    }
*/
*/
}
}
Line 1,704: Line 2,616:
     IMAGE classes
     IMAGE classes
\* -------------------- */
\* -------------------- */
class clsImages extends clsDataTable {
class clsVbzFolders extends clsVbzTable {
  public function __construct($iDB) {
    public function __construct($iDB) {
    parent::__construct($iDB,'cat_images');
parent::__construct($iDB);
  }
  $this->Name('cat_folders');
  protected function _newItem() {
  $this->KeyName('ID');
     CallStep('clsImages._newItem()');
  $this->ClassSng('clsVbzFolder');
     return new clsImage($this);
    }
  }
    /*----
      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 clsDataItem {
class clsImage extends clsVbzRecs {
  public $idTitle;
  public $Ab_Size;
  public $AttrFldr;
  public $Spec;
// object cache
// object cache
  private $objTitle;
    protected $objTitle;


  protected function LoadResults() {
    /*----
    CallEnter('clsImage.LoadResults()');
      HISTORY:
    $this->ID = $this->GetValue('ID');
2010-11-16 Modified to use new cat_folders data via ID_Folder
     $this->idTitle = $this->GetValue('ID_Title');
    */
     $this->AbSize = $this->GetValue('Ab_Size');
    public function WebSpec() {
     $this->AttrFldr = $this->GetValue('AttrFldr');
//return KWP_IMG_MERCH.$this->Spec;
    $this->AttrDispl = $this->GetValue('AttrDispl');
return $this->FolderPath().$this->Spec;
     $this->Spec = $this->GetValue('Spec');
     }
    assert($this->ID);
    /*----
    CallExit('clsImage.LoadResults()');
      HISTORY:
  }
2010-11-16 Created
  public function ImgForSize($iSize) {
    */
// ACTION: Get the image with the same title and attribute but with the given size
    public function FolderObj() {
     if ($this->AttrFldr) {
return $this->objDB->Folders()->GetItem($this->ID_Folder);
      $sqlAttr = '="'.$this->AttrFldr.'"';
     }
    } else {
    /*----
      $sqlAttr = ' IS NULL';
      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;
     }
     }
     $sql = 'SELECT * FROM cat_images WHERE (ID_Title='.$this->idTitle.') AND (AttrFldr'.$sqlAttr.') AND (Ab_Size="'.$iSize.'");';
     public function Title() {
    $objImgOut = new clsImage($this->Table);
      if (!is_object($this->objTitle)) {
    $objImgOut->Query($sql);
  $this->objTitle = $this->objDB->Titles()->GetItem($this->ID_Title);
    return $objImgOut;
      }
  }
      return $this->objTitle;
  public function Title() {
    global $objFactory;
 
    if (!is_object($this->objTitle)) {
      $objTitleTbl = $objFactory->TitlesExt();
      $this->objTitle = $objTitleTbl->GetItem($this->idTitle);
     }
     }
     return $this->objTitle;
     /*-----
  }
      ACTION: outputs a standalone page for larger image sizes - does not use skin
  public function DoPage() {
    */
    global $objFactory;
    public function DoPage() {
global $vbgImgSize;


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


// show list of available image sizes (except th and sm)
// show list of available image sizes (except th and sm)
    $objImgs = $this->ListImages_sameAttr();
$objImgs = $this->ListImages_sameAttr();
    if ($objImgs->HasData()) {
$strSizes = NULL;
      $strImgCount = 0;
if ($objImgs->hasRows()) {
      while ($objImgs->HasData()) {
    $strImgCount = 0;
        $strImgType = $objImgs->AbSize;
    while ($objImgs->NextRow()) {
        if (($strImgType != 'th') && ($strImgType != 'sm')) {
$strImgType = $objImgs->Ab_Size;
          $strImgCount++;
if (!empty($strImgType)) {
          $strImgTag = $imgSize[$strImgType];
    if (($strImgType != 'th') && ($strImgType != 'sm')) {
          if ($strOut) {
$strImgCount++;
            $strOut .= ' .. ';
$strDesc = $vbgImgSize[$strImgType];
          }
if ($objImgs->ID == $this->ID) {
          $strOut .= $strImgTag;
    $strImgTag = '<b>'.$strDesc.'</b>';
        }
} else {
        $objImgs->NextRow();
    $strImgTag = $objImgs->Href(TRUE).$strDesc.'</a>';
      }
}
      if ($strImgCount > 1) {
if (!empty($strSizes)) {
        echo $strOut.'<br>';
    $strSizes .= ' .. ';
      }
}
    }
$strSizes .= $strImgTag;
    }
}
    }
    if ($strImgCount > 1) {
$ftSizes = '<tr>'
  .'<td><font color=#aaaaaa>sizes</font> :</td>'
  .'<td align=center>'.$strSizes.'</td>'
  .'<td>: <font color=#aaaaaa>sizes</font></td>'
  .'</tr>';
    } else {
$ftSizes = NULL;
    }
}
 
// show list of available images for this title at this size
// show list of available images for this title at this size
    $objImgs = $this->ListImages_sameSize();
$strAttrs = NULL;
    if ($objImgs->HasData()) {
$objImgs = $this->ListImages_sameSize();
      $strImgCount = 0;
//echo 'test';
      $strOut .= '';
if ($objImgs->NextRow()) {
      if ($objImgs->HasData()) {
    $intImgs = 0;
        while ($objImgs->HasData()) {
    if ($objImgs->hasRows()) {
          $strImgFldr = $objImgs->AttrFldr;
$idx = 0;
          $strImgDescr = $objImgs->AttrDispl;
while ($objImgs->NextRow()) {
          $strImgCount++;
$idx++;
          if ($strOut) {
    $strImgFldr = $objImgs->AttrFldr;
            $strOut .= ' .. ';
    $strImgDescr = $objImgs->AttrDispl.'('.$idx.')';
          }
    $intImgs++;
          if ($objImgs->ID == $this->ID) {
    if (!empty($out)) {
            $strOut .= '<b>'.$strImgDescr.'</b>';
$strAttrs .= ' .. ';
          } else {
    }
            $strOut .= $objImgs->Href(TRUE).$strImgDescr.'</a>';
    if ($objImgs->ID == $this->ID) {
          }
$strAttrs .= '<b>'.$strImgDescr.'</b>';
          $objImgs->NextRow();
    } else {
        }
$strAttrs .= $objImgs->Href(TRUE).$strImgDescr.'</a>';
        if ($strImgCount > 1) {
    }
          echo $strOut.'<br>';
}
        }
if ($intImgs > 1) {
      }
    $ftAttrs = '<tr>'
      .'<td><font color=#aaaaaa>views</font> :</td>'
      .'<td align=center>'.$strAttrs.'</td>'
      .'<td>: <font color=#aaaaaa>views</font></td>'
      .'</tr>';
} else {
    $ftAttrs = NULL;
}
    }
}
if ((!empty($ftSizes)) || (!empty($ftAttrs))) {
    echo '<table border=1><tr><td><table>';
    echo $ftSizes.$ftAttrs;
    echo '</table></td></tr></table>';
} else {
    echo '<br>';
}
echo $htTitleHref.'ordering page</a><br>';
echo $this->Image_HTML();
echo "\n</body>\n</html>";
     }
     }
    echo $objTitle->Link().'ordering page</a><br>';
    echo $this->ImgSrc();
    echo '</body></html>';
  }
   public function ListImages_sameAttr() {
   public function ListImages_sameAttr() {
    $sqlFilt = 'isActive AND (ID_Title='.$this->ID_Title.')';
     if ($this->AttrFldr) {
     if ($this->AttrFldr) {
       $sqlFilt = '(ID_Title='.$this->idTitle.') AND (AttrFldr="'.$this->AttrFldr.'")';
       $sqlFilt .= ' AND (AttrFldr="'.$this->AttrFldr.'")';
    } else {
      $sqlFilt = '(ID_Title='.$this->idTitle.')';
     }
     }
     $objImgOut = $this->Table->GetData($sqlFilt);
     $objImgOut = $this->objDB->Images()->GetData($sqlFilt);
 
     return $objImgOut;
     return $objImgOut;
   }
   }
  public function ListImages_sameSize() {
    public function ListImages_sameSize() {
    $sqlFilt = '(ID_Title='.$this->idTitle.') AND (Ab_Size="'.$this->AbSize.'")';
$sqlFilt = 'isActive AND (ID_Title='.$this->ID_Title.') AND (Ab_Size="'.$this->Ab_Size.'")';
    $objImgOut = $this->Table->GetData($sqlFilt);
//echo 'SQL: '.$sqlFilt;
    return $objImgOut;
$objImgOut = $this->objDB->Images()->GetData($sqlFilt);
  }
return $objImgOut;
  public function Href($iAbs=false) {
    if ($iAbs) {
      $strFldr = $this->Title()->URL();
     }
     }
     $strFldr .= $this->AttrFldr;
     public function Href($iAbs=false) {
    if ($strFldr) {
$strFldrRel = $this->AttrFldr;
      $strFldr .= '-';
if ($strFldrRel) {
    $strFldrRel .= '-';
}
$strFldrRel .= $this->Ab_Size;
 
if ($iAbs) {
    $strFldr = $this->Title()->URL().'/'.$strFldrRel;
} else {
    $strFldr = $strFldrRel;
}
return '<a href="'.$strFldr.'/">';
     }
     }
    $strFldr .= $this->AbSize;
    return '<a href="'.$strFldr.'/">';
  }
  public function ImgSrc() {
    return '<img src="'.KWP_IMG_MERCH.$this->Spec.'">';
  }
}
}


/* ------------------ *\
/* ======================
    CATALOG BROWSING
GLOBAL VARIABLES CLASS
\* ------------------ */
*/
class clsTopics extends clsDataTable {
class clsGlobalVars extends clsTable {
  public function __construct($iDB) {
     const TableName='var_global';
     parent::__construct($iDB,'brs_topics');
     private $strOld;
  }
  protected function _newItem() {
     CallStep('clsTopics._newItem()');
    return new clsTopic($this);
  }
  public function DoIndex() {
    global $objFactory,$objDataMgr;


     CallEnter('clsTopics.DoIndex()');
     public function __construct($iDB) {
    $objSection = new clsPageOutput();
parent::__construct($iDB);
 
  $this->Name(self::TableName);
    $objTopics = $this->GetData('ID_Parent IS NULL','Sort,Name,NameTree');
  $this->KeyName('Name');
    while ($objTopics->HasData()) {
//   $this->ClassSng('clsGlobalVar');
      if ($isFirst) {
    }
        $isFirst = false;
    public function Exists($iName) {
        $objSection->SectionHdr('Root Topics');
$sqlFilt = 'Name="'.$iName.'"';
      } else {
$objRow = $this->GetData($sqlFilt);
        $objSection->AddText($objTopics->Name.'<br>');
if ($objRow->hasRows()) {
      }
    $objRow->NextRow();
      $objTopics->NextRow();
    $this->strOld = $objRow->Value;
    return TRUE;
} else {
    unset($this->strOld);
    return FALSE;
}
    }
    private function GetVar($iName) {
if ($this->Exists($iName)) {
    return $this->strOld;
} else {
    return NULL;
}
     }
     }
    private function SetVar($iName, $iValue) {
$strWhere = __METHOD__; // should return class::function
$sqlVal = SQLValue($iValue);
$sqlName = SQLValue($iName);


    CallExit('clsTopic.DoIndex()');
if ($this->Exists($iName)) {
    return $objSection->out;
    $this->objDB->LogEvent($strWhere,
  }
      '|name='.$iName.'|old='.$this->strOld.'|new='.$sqlVal,
}
      'global updated: '.$iName,
class clsTopic extends clsDataItem {
      'VAR-U',FALSE,FALSE);
  public $ID_Parent;
  public $Name;
  public $NameTree;
  public $NameFull;
  public $Variants;
  public $Mispeled;


  protected function LoadResults() {
    $arUpd = array(
    CallEnter('clsTopic.LoadResults()');
      'Value' => $sqlVal,
    $this->ID = $this->GetValue('ID');
      'WhenUpdated' => 'NOW()'
    $this->ID_Parent = $this->GetValue('ID_Parent');
      );
    $this->Name = $this->GetValue('Name');
    $this->Update($arUpd,'Name='.$sqlName);
    $this->NameTree = $this->GetValue('NameTree');
} else {
    $this->NameFull = $this->GetValue('NameFull');
    $this->objDB->LogEvent($strWhere,
    $this->Variants = $this->GetValue('Variants');
      '|name='.$iName.'|val='.$sqlVal,
    $this->Mispeled = $this->GetValue('Mispeled');
      'global added: '.$iName,
    assert($this->ID);
      'VAR-I',FALSE,FALSE);
    CallExit('clsTopic.LoadResults()');
    $arIns = array(
  }
      'Name' => $sqlName,
 
      'Value' => $sqlVal,
  public function DoPage() {
      'WhenCreated' => 'NOW()'
    global $objFactory,$objDataMgr;
      );
 
    $this->Insert($arIns);
    CallEnter('clsTopic.DoPage()');
}
    assert($this->ID);
    }
    $objSection = new clsPageOutput();
    public function Val($iName,$iValue=NULL) {
 
if (is_null($iValue)) {
    $objTbl = $objFactory->Table_noID('brs_titles_x_topics');
    return $this->GetVar($iName);
     $objTitles = $objTbl->GetData('ID_Topic='.$this->ID);
} else {
     while ($objTitles->HasData()) {
    return $this->SetVar($iName,$iValue);
      if ($isFirst) {
}
        $isFirst = false;
     }
        $objSection->SectionHdr('Titles');
/*
      } else {
     public function __set($iName, $iValue) {
        $objSection->AddText($objTitles->Name.'<br>');
$this->SetVar($iName,$iValue);
      }
    }
      $objTitles->NextRow();
    public function __get($iName) {
return $this->GetVar($iName);
     }
     }
 
*/
    CallExit('clsTopic.DoPage()');
    return $out;
  }
  public function WebName() {
    return sprintf(KS_FMT_TOPICID,$this->ID);
  }
}
}
/* ==================== *\
/* ==================== *\
     UTILITY FUNCTIONS
     UTILITY FUNCTIONS
Line 1,943: Line 3,029:
}
}
function DataCurr($iCurr,$iPfx='$') {
function DataCurr($iCurr,$iPfx='$') {
  $out = $iPfx.sprintf("%01.2f",$iCurr);
    if (is_null($iCurr)) {
  return $out;
return NULL;
    } else {
$out = $iPfx.sprintf("%01.2f",$iCurr);
return $out;
    }
}
}
function DataDate($iDate) {
function DataDate($iDate) {
Line 1,957: Line 3,047:
       $out = '';
       $out = '';
     }
     }
  return $out;
    return $out;
}</php>
}
function DataDateTime($iDate) {
    if (is_string($iDate)) {
if ($iDate == '') {
    $out = '-';
} else {
    $objDate = new DateTime($iDate);
    $out = $objDate->format('Y-m-d H:i');
}
    } else {
      $out = '';
    }
    return $out;
}
function IsEmail($iAddr) {
    $ok = preg_match('/^[0-9,a-z,\-,\.]{1,}@([0-9,a-z,\-][\.]{0,1}){2,}$/i', $iAddr );
    return $ok;
}
function ArrayToAttrs(array $iarAttr=NULL) {
    $htAttr = '';
    if (is_array($iarAttr)) {
foreach ($iarAttr as $key => $val) {
    $htAttr .= ' '.$key.'="'.$val.'"';
}
    }
    return $htAttr;
}
 
/* ==================== *\
    MISSING FUNCTIONS
\* ==================== */
if (!function_exists('http_redirect')) {
    function http_redirect($iURL) {
header('Status: 301 Moved Permanently',TRUE);
header('Location: '.$iURL,TRUE);
    }
}
</php>

Latest revision as of 01:53, 25 February 2024

About

  • Purpose: Classes for displaying different types of catalog display pages
  • History:
    • 2009-03-07 Transcribed from working code at vbz.net
    • 2009-10-06 Latest code - large chunks working, focus of development no longer on this module
    • 2011-12-18 long-overdue update
  • To Do:
    • should be split up into auto-loadable class files, e.g. vbz.title.php, vbz.dept.php, etc.
    • "clsFactory" should be eliminated in favor of static functions for each class

Code - store.php

<php><?php /*

FILE: store.php
PURPOSE: vbz class library - should eventually be subdivided
HISTORY:
 2009-07-11 (Wzl) Lots of cleanup; added types and methods needed for shopping cart page
 2009-10-06 (Wzl) Changed most clsTitle* classes to clsVbzTitle* (conflict with something - w3tpl?)
   Additional changes needed so it would work on Rizzo
 2010-06-12 (Wzl) A little cleanup; clsTopic::Titles()
 2010-06-14 (Wzl) clsVbzTable::GetItem() handles non-numeric IDs by creating new object and passing along ID
 2011-01-18 (Wzl) moved clsTopic(s) to topic.php
  • /

define('kEmbeddedPagePrefix','embed:');

/* if (defined( '__DIR__' )) {

 $fpThis = __DIR__;

} else {

 $fpThis = dirname(__FILE__);

}

  • /

if (!defined('LIBMGR')) {

   require(KFP_LIB.'/libmgr.php');

} clsLibMgr::Add('strings', KFP_LIB.'/strings.php',__FILE__,__LINE__); clsLibMgr::Add('string.tplt', KFP_LIB.'/StringTemplate.php',__FILE__,__LINE__); clsLibMgr::Add('cache' , KFP_LIB.'/cache.php',__FILE__,__LINE__); clsLibMgr::Add('dtree', KFP_LIB.'/dtree.php',__FILE__,__LINE__); clsLibMgr::Add('events', KFP_LIB.'/events.php',__FILE__,__LINE__); clsLibMgr::Add('vbz.data', KFP_LIB_VBZ.'/vbz-data.php',__FILE__,__LINE__); clsLibMgr::Add('topic', KFP_LIB_VBZ.'/topic.php',__FILE__,__LINE__);

clsLibMgr::Load('strings',__FILE__,__LINE__); clsLibMgr::Load('string.tplt',__FILE__,__LINE__); clsLibMgr::Load('cache',__FILE__,__LINE__); clsLibMgr::Load('dtree',__FILE__,__LINE__); clsLibMgr::Load('events',__FILE__,__LINE__); clsLibMgr::Load('vbz.data',__FILE__,__LINE__); clsLibMgr::Load('topic',__FILE__,__LINE__);

define('EN_PGTYPE_NOTFND',-1); // requested item (supp/dept/title) not found define('EN_PGTYPE_HOME',1); // catalog home page define('EN_PGTYPE_SUPP',2); // supplier page define('EN_PGTYPE_DEPT',3); // department page, or possibly title for keyless dept define('EN_PGTYPE_TITLE',4); // title page // table names // - stock define('ksTbl_stock_places','stk_places'); define('ksTbl_stock_bins','stk_bins'); define('ksQry_stock_bins_wInfo','qryStk_Bins_w_info'); define('ksTbl_stock_items','stk_items'); define('ksTbl_stock_hist_items','stk_history'); define('ksTbl_stock_hist_bins','stk_bin_history'); // - browsing define('ksTbl_title_topics','brs_titles_x_topics');

global $vbgImgSize; $vbgImgSize = array(

   'th'	=> 'thumbnail',
   'sm'	=> 'small',
   'big' 	=> 'large',
   'huge'	=> 'huge',
   'zoom'	=> 'detail');

// shipping zone data: global $listShipListDesc,$listItmFactors,$listPkgFactors; // there's got to be a better way to do this... $listShipListDesc['US'] = 'United States'; $listShipListDesc['CA'] = 'Canada'; $listShipListDesc['INT'] = 'International'; // -- shipping charge adjustment factors: $listPkgFactors['US'] = 1.0; $listItmFactors['US'] = 1.0; $listPkgFactors['CA'] = 2.0; $listItmFactors['CA'] = 2.0; $listPkgFactors['INT'] = 4.0; $listItmFactors['INT'] = 4.0; // The above is just until we have something more exact and dynamic

$intCallDepth = 0;

// CALCULATED GLOBALS $fpTools = '/tools'; $fpPages = ; $fwpAbsPages = 'http://'.KS_PAGE_SERVER.$fpPages; $fwpAbsTools = 'http://'.KS_TOOLS_SERVER.$fpTools; $fwpCart = $fwpAbsPages.'/cart/'; $strCurServer = $_SERVER['SERVER_NAME'];

// SET UP DEPENDENT VALUES /* if ($strCurServer != KS_TOOLS_SERVER) {

 $fpTools = $fwpAbsTools;
 $fpPages = $fwpAbsPages;

}

  • /

$fwpLogo = $fpTools.'/img/logos/v/';

/*====

 NOTES:
   2011-02-02 This could use some rewriting. A lot of it is kind of pointless,
     and other parts duplicate RichText objects.
  • /

class clsPageOutput {

 public $out;
 private $isOdd;
 public $inTbl;
 function __construct() {
   $this->out = ;
   $this->isOdd = false;
   $this->inTbl = 0;
 }
 function Clear() {
   $this->out = ;
 }
 function AddText($iText) {
   $this->out .= $iText;
 }
 function SectionHdr($iTitle) {	// this is specifically duplicated

$this->out .= '

'.$iTitle.'

';

   return $this->out;
 }
 function StartTable($iTitle) {
   if ($iTitle) {
     $this->SectionHdr($iTitle);

$this->out .= '

'; $this->inTbl++; } } function RowStart($iClass=) { if ($iClass) { $this->out .= ''; } else { $this->out .= ''; } } function RowStop() { $this->out .= ''; $this->isOdd = !$this->isOdd; } function ColAdd($iText) { if ($this->isOdd) { $cellOpen = ''; } function EndTable() { if ($this->inTbl) { $this->out .= '
';
   } else {
$cellOpen = '
';
   }
$this->out .= $cellOpen.$iText.'

';

     $this->inTbl--;
   }
   return $this->out;
 }
 function ShowImgUnavail() {

$this->out .= '

<tbody></tbody>
<tbody></tbody>
No Images
Available
for this item
:-(

';

   return $this->out;
 }

} class clsCacheFile_vbz extends clsCacheFile {

   public function __construct() {

parent::__construct(KFP_CACHE);

   }

} class clsVbzData extends clsDatabase {

   protected $objPages;
   public function __construct($iSpec) {

parent::__construct($iSpec); $this->Open();

   }

// cache manager

   protected function CacheMgr_empty() {

return new clsCacheMgr($this);

   }
   public function CacheMgr() {

if (empty($this->objCacheMgr)) { $objCache = $this->CacheMgr_empty(); $objCache->SetTables('cache_tables','cache_queries','cache_flow','cache_log'); $this->objCacheMgr = $objCache; } return $this->objCacheMgr;

   }

// table-specific functions

   public function Pages() {

return $this->Make('clsCatPages');

   }
   public function Suppliers() {

return $this->Make('clsSuppliers');

   }
   public function Depts() {

return $this->Make('clsDepts');

   }
   public function Titles($iID=NULL) {

return $this->Make('clsVbzTitles',$iID);

   }
   public function Items() {

return $this->Make('clsItems');

   }
   public function Items_Stock() {

return $this->Make('clsItems_Stock');

   }
   public function Items_Cat() {

return $this->Make('clsItems_info_Cat');

   }
   public function ItTyps() {

return $this->Make('clsItTyps');

   }
   public function ItOpts() {

return $this->Make('clsItOpts');

   }
   public function ShipCosts() {

return $this->Make('clsShipCosts');

   }
   public function Folders() {

return $this->Make('clsVbzFolders');

   }
   public function Images() {

return $this->Make('clsImages');

   }
   public function StkItems() {

return $this->Make('clsStkItems');

   }
   public function Topics($iID=NULL) {

return $this->Make('clsTopics',$iID);

   }
   public function TitleTopic_Titles() {

return $this->Make('clsTitleTopic_Titles');

   }
   public function TitleTopic_Topics() {

return $this->Make('clsTitleTopic_Topics');

   }
   public function VarsGlobal() {

return $this->Make('clsGlobalVars');

   }
   public function Events() {

return $this->Make('clsEvents');

   }
   public function LogEvent($iWhere,$iParams,$iDescr,$iCode,$iIsError,$iIsSevere) {

return $this->Events()->LogEvent($iWhere,$iParams,$iDescr,$iCode,$iIsError,$iIsSevere);

   }

// Page output routines

   public function Formatter() {

if (!isset($this->objFmt)) { $this->objFmt = new clsPageOutput(); } return $this->objFmt;

   }
   public function SectionHdr($iTitle) {

/*

$out = '

'.$iTitle.'

';

return $out;

  • /

$this->Formatter()->Clear(); return $this->Formatter()->SectionHdr($iTitle);

   }
   /*----
     NOTES:

2011-02-02 This really should somehow provide a more general service. Right now, it's only used for displaying department pages -- but topic pages and searches could make use of it as well, if there were some tidy way to abstract the collecting of the price and stock data. This will probably necessitate adding hi/low price data to _titles and possibly _dept_ittyps (it's already in _title_ittyps).

     HISTORY:

2011-02-02 * Removed assertion that $objImgs->Res is a resource, because if there are no thumbnails for this title, then apparently it isn't, and apparently RowCount() handles this properly. * Also fixed references to qtyForSale -- should be qtyInStock.

   */
   public function ShowTitles($iHdrText,$iList,$objNoImgSect) {

$cntImgs = 0; $outImgs = ; foreach ($iList as $i => $row) { $objTitle = $this->Titles()->GetItem($row['ID']); $objImgs = $objTitle->ListImages('th'); //assert('is_resource($objImgs->Res); /* TYPE='.get_class($objImgs).' SQL='.$objImgs->sqlMake.' */'); $currMinPrice = $row['currMinPrice']; $currMaxPrice = $row['currMaxPrice']; $strPrice = DataCurr($currMinPrice); if ($currMinPrice != $currMaxPrice) { $strPrice .= '-'.DataCurr($currMaxPrice); } if ($objImgs->RowCount()) { $cntImgs++; $strCatNum = $objTitle->CatNum(); $strTitleTag = '"'.$objTitle->Name.'" ('.$strCatNum.')'; $strTitleLink = $objTitle->Link(); while ($objImgs->NextRow()) { $strImgTag = $strTitleTag.' - '.$strPrice; $qtyStk = $row['qtyInStock']; if ($qtyStk) { $strImgTag .= ' - '.$qtyStk.' in stock'; } $outImgs .= $strTitleLink.'<img class="thumb" src="'.$objImgs->WebSpec().'" title="'.$strImgTag.'"></a>'; } } else { if (!$objNoImgSect->inTbl) { $objNoImgSect->StartTable('titles without images:');

$objNoImgSect->AddText('Cat. #TitlePrice
Rangeto
orderstatus');

} $objNoImgSect->RowStart(); $objNoImgSect->ColAdd(''.$objTitle->CatNum().''); $objNoImgSect->ColAdd($objTitle->Name); $objNoImgSect->ColAdd($strPrice); $objNoImgSect->ColAdd('['.$objTitle->Link().'order</a>]'); $qtyStk = $row['qtyInStock']; if ($qtyStk) { $strStock = ''.$qtyStk.' in stock'; if ($row['cntInPrint'] == 0) { $strStock .= ' - OUT OF PRINT!'; } $objNoImgSect->ColAdd($strStock); } else { $objNoImgSect->ColAdd('<a title="explanation..." href="'.KWP_HELP_NO_STOCK_BUT_AVAIL.'">available, not in stock</a>'); // Debugging: // $objNoImgSect->ColAdd('ID_Title='.$objTitle->ID.' ID_ItTyp='.$objTitle->idItTyp); } $objNoImgSect->RowStop(); } } $out = ; if ($cntImgs) { $out .= $this->SectionHdr($iHdrText); $out .= $outImgs; } return $out;

   }

}

class clsList {

 public $List;
 public function Add($iName, $iValue=NULL) {
   $objItem = new clsListItem($iName,$iValue);
   $this->List[] = $objItem;
   return $objItem;
 }
 public function Output($iPfx, $iSep, $iSfx) {
   $out = NULL;
   if (is_array($this->List)) {
     foreach ($this->List as $objItem) {
       if (is_null($objItem->value)) {
         $out .= $iPfx.$iSep.$objItem->name.$iSfx;
       } else {
         $out .= $iPfx.$objItem->name.$iSep.$objItem->value.$iSfx;
       }
     }
   }
   return $out;
 }

} class clsListItem {

 public $name;
 public $value;
 public function __construct($iName, $iValue=NULL) {
   $this->name = $iName;
   $this->value = $iValue;
 }

} /* ===================

 CLASS: clsVbzSkin
 PURPOSE: Abstract skin class
  • /

abstract class clsVbzSkin extends clsVbzData {

   /*-----
     USAGE: Normal main entry point -- should be called from index.php
   */
   public function DoPage() {

try { $this->DoPreContent(); $this->DoContent(); $this->DoPostContent(); } catch(exception $e) { $this->EmailException($e); }

   }
   /*----
     HISTORY:

2011-03-31 added Page and Cookie to list of reported variables

   */
   protected function EmailException(exception $e) {

$msg = $e->getMessage(); $out = 'Description: '.($e->getMessage()); $out .= "\nStack trace:\n".($e->getTraceAsString());

$guest_ip = $_SERVER['REMOTE_ADDR']; $guest_browser = $_SERVER['HTTP_USER_AGENT']; $guest_referer = NzArray($_SERVER,'HTTP_REFERER'); $guest_page = $_SERVER['REQUEST_URI']; $guest_cookie = $_SERVER['HTTP_COOKIE'];

$out .= <<<__END__

Client information:

- IP Addr: $guest_ip
- Browser: $guest_browser
- Page   : $guest_page
- Cookie : $guest_cookie
- Referer: $guest_referer

__END__;

$subj = 'vbz error from IP '.$guest_ip; $ok = mail(KS_TEXT_EMAIL_ADDR_ERROR,$subj,$out);

echo <<<__END__ Ack! We seem to have a small problem here. (If it was a large problem, you wouldn't be seeing this message.) The webmaster is being alerted about this.
Meanwhile, you might try reloading the page -- a lot of errors are transient, which makes them hard to fix, which is why there are more of them than the other kind.

We apologize for the nuisance.

Error Message: $msg __END__;

// FUTURE: log the error and whether the email was successful

   }
   /*-----
     ACTION: Displays everything *before* the main page contents
   */
   protected function DoPreContent() {

$this->ParseInput(); $this->HandleInput(); $this->RenderHdrBlocks();

   }
   /*-----
     ACTION: Displays everything *after* the main page contents
   */
   protected function DoPostContent() {

$this->RenderFtrBlocks();

   }

// ABSTRACT section //

   /*-----
     ACTION: Displays the main page contents
   */
   protected abstract function DoContent();
   /*-----
     ACTION: Grab any expected input and interpret it
   */
   protected abstract function ParseInput();
   /*-----
     ACTION: Take the parsed input and do any needed processing (e.g. looking up data)
   */
   protected abstract function HandleInput();
   /*-----
     ACTION: Render any output that appears *before* the main content
   */
   protected abstract function RenderHdrBlocks();
   /*-----
     ACTION: Render any output that appears *after* the main content
   */
   protected abstract function RenderFtrBlocks();

} /* ===================

 CLASS: clsVbzSkin_Standard
 PURPOSE: Standard skin class
   Will later be replaceable with other skins
  • /

abstract class clsVbzSkin_Standard extends clsVbzSkin { /*

   protected function DoContent() {
   }
   protected function ParseInput() {
   }
   protected function HandleInput() {
   }
  • /
   protected function RenderHdrBlocks() {

$this->RenderHtmlStart(); $this->RenderContentHdr(); $this->DoSidebar();

   }
   protected function RenderFtrBlocks() {

$this->RenderContentFtr(); $this->RenderHtmlStop();

   }
   protected function RenderContentFtr() {

global $didPage,$fltStart;

echo '

';

$this->DoSepBar();

echo '
';

$fltExecTime = microtime(true)-$fltStart; $dat = getrusage(); $fltUserTime = $dat["ru_utime.tv_usec"]/1000000; $strServer = $_SERVER['SERVER_SOFTWARE']; echo $strServer.' .. '; echo 'PHP '.phpversion().' .. Generated in '.$fltUserTime.' seconds (script execution '.$fltExecTime.' sec.) .. '; $strWikiPg = $this->strWikiPg; if ($strWikiPg) { echo 'wiki: <a href="'.KWP_WIKI.kEmbeddedPagePrefix.$this->strWikiPg.'">'.$strWikiPg.'</a> .. '; } echo date('Y-m-d H:i:s');

echo '
'; echo '

';

$didPage = true;

   }
   protected function RenderHtmlStop() {

echo "\n</body>\n</html>";

   }

// NEW METHODS for this class //

   protected function DoSidebar() {

$objCache = $this->CacheMgr(); // TO DO: these should be pulled from the [stats] table /* if ($objCache->dtNewest) {

   $timeSidebarBuild=$objCache->dtNewest;

} else {

   $timeSidebarBuild = NULL;

}

  • /
   $timeSidebarBuild = NULL;

$statsQtyTitlesAvail = 2245; $statsQtyStockPieces = 1395; $statsQtyStockItems = 753; $statsQtyArtists = 136; $statsQtyTopics = 1048; //---------

echo '

'; echo '

';

   }
   protected function DoSepBar() {
     echo $this->Render_HLine();
   }
   public function Render_HLine($iHeight=NULL) {
     global $fpTools;
     $htHt = is_null($iHeight)?:('height='.$iHeight);
     return '<img src="'.$fpTools.'/img/bg/hlines/"'.$htHt.' alt="-----" width="100%">';
   }
   private function ToolbarItem($iURL,$iIcon,$iTitle,$iAlt) {

global $fpTools; return '<a href="'.$iURL.'"><img border=0 src="'.$fpTools.'/img/icons/'.$iIcon.'.050pxh.png" title="'.$iTitle.'" alt="'.$iAlt.'"></a>';

   }
   protected function DoToolbar() {

global $fpPages,$fwpCart;

echo $this->ToolbarItem($fpPages.'/','home',KS_STORE_NAME.' home page','home page'); echo $this->ToolbarItem($fpPages.'/search/','search','search page','search page'); echo $this->ToolbarItem($fwpCart,'cart','shopping cart','shopping cart'); echo $this->ToolbarItem(KWP_HELP_HOME,'help','help!','help');

   }

// NEW METHODS for this class //

   /*----
     PURPOSE: Renders HTML inside <head></head> section
     HISTORY:

2011-01-11 Created

   */
   protected function RenderHtmlHeaderSection() {

$strTitle = KS_STORE_NAME.' - '.$this->strName; $out = "\n<title>$strTitle</title>";

$arVars = array('sheet' => $this->strSheet); $objStrTplt = new clsStringTemplate_array(NULL,NULL,$arVars); $objStrTplt->MarkedValue(KHT_PAGE_STYLE); $out .= $objStrTplt->Replace(); //$out .= KHT_PAGE_STYLE; if (!empty($this->strName)) { $ftName = ': '.htmlspecialchars($this->strName); } else { $ftName = ; } $strContent = KS_STORE_NAME_META.$ftName; $out .= "\n<meta name=description content=\"$strContent\">"; return $out;

   }
   /*----
     PURPOSE: Renders HTML up to beginning of BODY.
     HISTORY:

2011-01-11 Extracted everything between <head> and </head> into RenderHtmlHeaderSection()

   */
   protected function RenderHtmlStart() {

//$this->strCalcTitle = KS_STORE_NAME.' - '.$this->strName; $out = KHT_PAGE_DOCTYPE; $out .= "\n\n"; $out .= $this->RenderHtmlHeaderSection(); $out .= "\n"; $out .= KHT_PAGE_BODY_TAG; echo $out; } protected function RenderContentHdr() { global $fwpLogo; //$strWikiPg = $this->strWikiPg; // begin content header echo '

'; echo ''; // === LEFT HEADER: Title === echo ''; // === END LEFT HEADER === // === RIGHT HEADER: nav icons === echo ''; // === END RIGHT HEADER === echo "\n
'; echo ''.KS_SMALL_LOGO_ALT.''; if ($this->strTitleContext) { echo ''.KS_STORE_NAME.': '.$this->strTitleContext.'
'; } echo ''.$this->strTitle.'
'; $this->DoToolbar(); echo '
\n

"; } } /* =================== CLASS: clsPage PURPOSE: Handles display of different page types TO DO: These classes (clsPage and descendants) need to be reorganized (they're a mess) and also set up so we can create different skins. */ abstract class clsPage extends clsVbzSkin_Standard { // query protected $strReq; // requested page // page definition protected $strName; // short title: {item name} (goes into html title, prefixed with store name) protected $strTitle; // longer, descriptive title: {"item name" by Supplier} (goes at top of page) protected $strSheet; // name of style sheet to use (without the .css) protected $strWikiPg; // name of wiki page to embed, if any (blank = suppress embedding) protected $strTitleContext; // context of short title, in HTML: {Supplier: Department:} (goes above title, in small print) protected $strHdrXtra; // any extra stuff (HTML) for the header protected $strSideXtra; // any extra stuff for the sidebar protected $lstTop; // stuff listed at the top of the sidebar // calculated fields // protected $strCalcTitle; protected $strContText; // flags set by wiki contents protected $hideImgs; public function __construct($iSpec) { parent::__construct($iSpec); $this->lstTop = new clsList(); } protected function HandleInput() { $this->strSheet = 'browse'; // default } /*----- IMPLEMENTATION: Retrieves request from URL and parses it URL data identifies page, keyed to cat_pages data */ protected function ParseInput() { if (isset($_SERVER['PATH_INFO'])) { $strReq = $_SERVER['PATH_INFO']; } else { $strReq = ''; } $this->strReq = $strReq; if (strrpos($strReq,'/')+1 < strlen($strReq)) { $strRedir = KWP_CAT_REL.substr($strReq,1).'/'; header('Location: '.$strRedir); exit; // retry with new URL } } // DIFFERENT TYPES OF PAGES protected function DoNotFound() { $this->strWikiPg = ''; $this->strTitle = 'Unknown Page'; $this->strName = 'unknown title in catalog'; $this->strTitleContext = 'Tomb of the...'; $this->strHdrXtra = ''; $this->strSideXtra = '

Cat #: '.$this->strReq; } // UTILITY protected function AddText($iText) { $this->strContText .= $iText; } private function DoWikiContent() { # WIKI CONTENTS # $txtPage = GetEmbedPage('cat'); if (KF_USE_WIKI) { $txtWiki = GetWikiPage($this->strWikiPg); if ($txtWiki) { if (strpos($txtWiki,'__NOIMG__') != -1) { $txtWiki = str_replace('__NOIMG__','',$txtWiki); $this->hideImgs = true; } } if ($txtWiki) { echo '
'.$txtWiki.'
'; } } } } class clsPageCat extends clsPage { private $objCatPage; // object for identifying page to display protected function RenderHdrBlocks() { if ($this->useSkin) { parent::RenderHdrBlocks(); } } protected function RenderFtrBlocks() { if ($this->useSkin) { parent::RenderFtrBlocks(); } } protected function HandleInput() { parent::HandleInput(); $strReq = $this->strReq; $this->objCatPage = $this->Pages()->GetItem_byKey($strReq); $objPage = $this->objCatPage; $this->useSkin = TRUE; if ($this->strReq) { if (is_object($objPage)) { switch ($objPage->Type) { case 'S': $this->DoCatSupp(); break; case 'D': $this->DoCatDept(); break; case 'T': $this->DoCatTitle(); break; case 'I': $this->useSkin = FALSE; $this->DoCatImage(); break; } } else { $this->DoNotFound(); } } else { $this->DoCatHome(); } } public function DoContent() { echo $this->strContText; } // SIDEBAR INFO for different types of pages private function DoCatIndicia() { $this->lstTop->Add('Section','by supplier'); } private function DoSuppIndicia($iSupp,$isFinal=true) { $this->DoCatIndicia(); if ($isFinal) { $this->lstTop->Add('Supplier',$iSupp->Name); $this->lstTop->Add('more info'); } else { $this->lstTop->Add('Supplier',$iSupp->Link()); } } private function DoDeptIndicia($iDept,$isFinal=true) { $this->DoSuppIndicia($iDept->Supplier(),false); if ($isFinal) { $this->lstTop->Add('Dept.',$iDept->Name); } else { $this->lstTop->Add('Dept.',$iDept->LinkName()); } } private function DoTitleIndicia($iTitle) { $this->DoDeptIndicia($iTitle->Dept(),false); $this->lstTop->Add('Title',$iTitle->Name); $this->lstTop->Add(' - catalog #',$iTitle->CatNum()); } private function DoCatHome() { $this->DoCatIndicia(); $this->strWikiPg = 'cat'; $this->strTitle = 'Catalog Home'; $this->strName = 'Catalog main page'; $this->strTitleContext = 'hello and welcome to the...'; $this->AddText($this->Suppliers()->DoHomePage()); } private function DoCatSupp() { $objSuppTbl = $this->Suppliers(); $objSupp = $objSuppTbl->GetItem($this->objCatPage->ID_Row); assert('is_object($objSupp)'); $strSuppName = $objSupp->Name; $this->DoSuppIndicia($objSupp); $this->strWikiPg = 'supp:'.strtoupper($objSupp->CatKey); $this->strTitle = $strSuppName; $this->strName = 'listing for '.$strSuppName; $this->strTitleContext = 'Suppliers: '.$strSuppName.':'; $this->AddText($objSupp->DoPage()); } private function DoCatDept() { CallEnter($this,__LINE__,'clsPage.DoCatDept()'); // $objDeptTbl = VbzClasses::Depts(); $objDeptTbl = $this->Depts(); $objDept = $objDeptTbl->GetItem($this->objCatPage->ID_Row); assert('is_object($objDept)'); $objSupp = $objDept->Supplier(); assert('is_object($objSupp)'); $strDeptName = $objDept->Name; $strSuppName = $objSupp->Name; $strDeptLink = $objDept->LinkName(); $strSuppLink = $objSupp->Link(); $this->DoDeptIndicia($objDept); $this->strWikiPg = 'dept:'.strtoupper($objDept->PageKey); $this->strTitle = $strSuppName; $this->strName = $strDeptName.' dept. of '.$strSuppName; $this->strTitleContext = 'items supplied by '.$strSuppLink.'\'s '.$strDeptName.' department:'; $this->AddText($objDept->DoPage()); CallExit('clsPage.DoCatDept()'); } private function DoCatTitle() { CallEnter($this,__LINE__,'clsPage.DoCatTitle()'); $strPageKey = $this->objCatPage->Path; // $objTitleTbl = VbzClasses::Titles(); $objTitleTbl = $this->Titles(); $idRow = $this->objCatPage->ID_Row; $objTitle = $objTitleTbl->GetItem($idRow); assert('is_object($objTitle)'); $objDept = $objTitle->Dept(); assert('is_object($objDept)'); $objSupp = $objDept->Supplier(); assert('is_object($objSupp)'); $strTitleName = $objTitle->Name; $this->DoTitleIndicia($objTitle); // $this->strAbbr = 'title:'.strtoupper($strCatNum); $this->strWikiPg = 'title:'.$objTitle->CatNum(); //print 'ABBR='.$this->strAbbr; $this->strTitle = $strTitleName; $this->strName = $strPageKey.' "'.$strTitleName.'" from '.$objSupp->Name; $this->strTitleContext = 'items supplied by '.$objSupp->Link().'\'s '. $objDept->LinkName().' department:'; $objTitle->hideImgs = $this->hideImgs; $this->AddText($objTitle->DoPage()); CallExit('clsPage.DoCatTitle()'); } private function DoCatImage() { CallEnter($this,__LINE__,'clsPage.DoCatImage()'); // $objImageTbl = VbzClasses::Images(); $objImageTbl = $this->Images(); $objImage = $objImageTbl->GetItem($this->objCatPage->ID_Row); $objImage->DoPage(); CallExit('clsPage.DoCatImage()'); } } /* =========================== *** CATALOG DATA CLASSES *** */ 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; } } class clsCatPage extends clsDataSet { // == STATIC SECTION // const cksIDName = 'AB'; } 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; } public function DoHomePage() { $out = ''; $objRec = $this->DataSet_forStore(); if ($objRec->hasRows()) { $objTbl = new clsPageOutput; $objTbl->StartTable('Suppliers'); $strKeyLast = $outCell = ''; while ($objRec->NextRow()) { $strKey = $objRec->CatKey; if ($strKey != $strKeyLast) { $strKeyLast = $strKey; $strKeyLink = strtolower($strKey).'/'; if ($outCell) { $objTbl->ColAdd($outCell); $objTbl->RowStop(); $outCell = ''; } $objTbl->RowStart(); $objTbl->ColAdd(''.$objRec->Name.''); $isFirst = true; } if ($isFirst) { $isFirst = false; } else { $outCell .= ', '; } $strItType = $objRec->ItemType; if ($strItType == '') { $strItType = '?id'.$objRec->ID; } $outCell .= ' '.$objRec->ItemCount.' '.$strItType; } $objTbl->ColAdd($outCell); $objTbl->RowStop(); $out .= $objTbl->EndTable(); } return $out; } } 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; } /*---- USED BY: $this->DeptsPage_forStore() 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; } /*---- 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']; $out = ''; $isOdd = FALSE; $fpSupp = KWP_CAT_REL.strtolower($this->Value('CatKey')).'/'; foreach ($arDeptCntForSale as $idDept=>$arCnts) { $isOdd = !$isOdd; if ($isOdd) { $cellOpen = ''.$cellOpen.''.$strName.''; $out .= $cellOpen.$outRow.''; } } $out .= '
'; } else { $cellOpen = ''; } $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; $out .= '
'; return $out; } protected function DeptsPage_forStore() { $out = $this->DoPiece_ItTyp_Summary(); $out .= $this->Engine()->SectionHdr('Departments:'); $out .= $this->DoPiece_Dept_ItTyps(); 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 = ''.$strText.''; 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); } // 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:

'; $isOdd = FALSE; foreach ($arDeptCntForSale as $idDept=>$arCnts) { $isOdd = !$isOdd; if ($isOdd) { $cellOpen = ''.$cellOpen.''.$strName.''; $outRow = ''; foreach ($arCnts as $idType=>$cnt) { if ($cnt == 1) { $strType = $arTypSng[$idType]; } else { $strType = $arTypPlr[$idType]; } if ($outRow != '') { $outRow .= ', '; } $outRow .= ''.$cnt.' '.$strType; } $out .= $cellOpen.$outRow.''; } $out .= '
'; } else { $cellOpen = ''; } $objDept = $this->objDB->Depts()->GetItem($idDept); $strPageKey = $objDept->PageKey(); $strName = $objDept->Name; $out .= '
'; 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 if (!$this->hideImgs) { $objImgs = $this->ListImages('sm'); 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.''; } } $objSection->AddText($strImg); } } else { $objSection->ShowImgUnavail(); } } // list topics for this title $db = $this->Engine(); $tbl = $db->TitleTopic_Topics(); $tbl->doBranch(TRUE); $rs = $tbl->GetTitle($idTitle); if ($rs->hasRows()) { $txt = '
'; $txt .= ''.$db->Topics()->IndexLink('Topics').':'; while ($rs->NextRow()) { $txt .= '
- '.$rs->ShopLink(); } $txt .= '
'; $objSection->AddText($txt); } // now list available items as table //$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; } $objSection->AddText('
'); $flagDisplayTogether = false; // hard-coded for now //$objStk = $this->Engine()->Items_Stock(); $tblItems = $this->Engine()->Items(); //$txtTblHdr = 'OptionStatusPriceOrder
Qty.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 = ''.$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 .= ' — '.$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; } } } //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; } } } } } // DO the actual display of the accumulated text $txtTblOpen = ''; $txtTblFtr = ''; $txtTblShut = '
'; if ($flagDisplayTogether) { // Display in-stock and backordered items together $objSection->AddText($txtTblOpen); $objSection->AddText($txtTblHdr); $objSection->AddText($txtBoth); $objSection->AddText($txtTblFtr); $objSection->AddText($txtTblShut); } else { if ($txtInStock != '') { $txtClause = Pluralize($cntInStock,'This item is','These items are'); $objSection->AddText($txtTblOpen); $objSection->AddText(''.$txtClause.' in stock:'); $objSection->AddText($txtTblHdr); $objSection->AddText($txtInStock); $objSection->AddText($txtTblFtr); $objSection->AddText($txtTblShut); } if (!empty($txtOutStock)) { if (!empty($txtInStock)) { $objSection->AddText('

'); } $txtClause = Pluralize($cntOutStock,'This item is','These items are'); $objSection->AddText($txtTblOpen); $objSection->AddText(''.$txtClause.' not in stock'); $txtClause = Pluralize($cntOutStock,'it','them'); $objSection->AddText(', but we can (probably) get '.$txtClause.':'); $objSection->AddText($txtTblHdr); $objSection->AddText($txtOutStock); $objSection->AddText($txtTblFtr); $objSection->AddText($txtTblShut); } /**/ } $objSection->AddText('

'); } if (!$isItem) { $objSection->SectionHdr('This title is currently unavailable'); } return $objSection->out."\n\n"; } /*---- 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.' '.$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. */ public function CatNum($iSep='-') { if (empty($this->Row['CatNum'])) { $objDept = $this->Dept(); if (is_object($objDept)) { $objSupp = $objDept->Supplier(); $strDeptKey = $objDept->CatKey; $strOut = $objSupp->CatKey; if ($strDeptKey) { $strOut .= $iSep.$strDeptKey; } } 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.''; } /* 2010-11-06 if this is needed, use a method in SpecialVbzAdmin public function LinkName_wt() { // TO DO: make this more configurable $out = '[[vbznet:cat/'.$this->URL_part().'|'.$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' .'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 .= ''.''; $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'; } } 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; } public function Title() { $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->Title()->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 } } if ($iCount == 1) { $out = array_key_exists('NameSng',$this->Row)?$this->Row['NameSng']:NULL; } else { $out = array_key_exists('NamePlr',$this->Row)?$this->Row['NamePlr']:NULL; } 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 = ''; } 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'); } } /* -------------------- *\ 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 .= ''.''; 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'; } } 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; } } */ class clsStkItems extends clsVbzTable { public function __construct($iDB) { parent::__construct($iDB); $this->Name('stk_items'); $this->KeyName('ID'); } /*----- RETURNS: Recordset containing list of stock for the given item (qtys, bin, bin name, place, place name, notes) */ public function List_forItem($iItemID) { $sql = 'SELECT ' .'ID, QtyForSale, QtyForShip, QtyExisting, ID_Bin, ID_Place, BinCode, WhName, Notes ' .'FROM qryStk_lines_remaining WHERE (ID_Item='.$iItemID.');'; $objStock = $this->objDB->DataSet($sql,$this->ClassSng()); $objStock->Table = $this; return $objStock; } /* (2010-06-15) This doesn't seem to be used anywhere, and possibly does not work. public function QtyInStock_forItem($iItemID) { $sql = 'SELECT SUM(s.Qty) AS Qty FROM stk_items AS s LEFT JOIN stk_bins AS sb ON s.ID_Bin=sb.ID WHERE (s.ID_Item='.$iItemID.') AND (s.WhenRemoved IS NULL) AND (sb.WhenVoided IS NULL) AND (sb.isForSale) GROUP BY s.ID_Item'; $objStock = new clsDataItem($this); $objStock->Query($sql); if ($objStock->NextRow()) { assert('is_resource($objStock->Res)'); if ($objStock->RowCount()) { assert('$objStock->RowCount() == 1'); return $objStock->Qty; } } else { return NULL; } } */ } /* -------------------- *\ 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 = ''; } 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 ''; } /*----- 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.' “'.$strTitle.'”'; echo ''.$htmlTitle.''; echo "\n'; echo '
'; echo ''.$htTitleHref.$strTitle.'
'.$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.''; } if (!empty($strSizes)) { $strSizes .= ' .. '; } $strSizes .= $strImgTag; } } } if ($strImgCount > 1) { $ftSizes = '' .'sizes :' .''.$strSizes.'' .': 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.''; } } if ($intImgs > 1) { $ftAttrs = '' .'views :' .''.$strAttrs.'' .': views' .''; } else { $ftAttrs = NULL; } } } if ((!empty($ftSizes)) || (!empty($ftAttrs))) { echo '
'; echo $ftSizes.$ftAttrs; echo '
'; } else { echo '
'; } echo $htTitleHref.'ordering page
'; 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 '<a href="'.$strFldr.'/">';

   }

}

/* ======================

GLOBAL VARIABLES CLASS
  • /

class clsGlobalVars extends clsTable {

   const TableName='var_global';
   private $strOld;
   public function __construct($iDB) {

parent::__construct($iDB); $this->Name(self::TableName); $this->KeyName('Name'); // $this->ClassSng('clsGlobalVar');

   }
   public function Exists($iName) {

$sqlFilt = 'Name="'.$iName.'"'; $objRow = $this->GetData($sqlFilt); if ($objRow->hasRows()) { $objRow->NextRow(); $this->strOld = $objRow->Value; return TRUE; } else { unset($this->strOld); return FALSE; }

   }
   private function GetVar($iName) {

if ($this->Exists($iName)) { return $this->strOld; } else { return NULL; }

   }
   private function SetVar($iName, $iValue) {

$strWhere = __METHOD__; // should return class::function $sqlVal = SQLValue($iValue); $sqlName = SQLValue($iName);

if ($this->Exists($iName)) { $this->objDB->LogEvent($strWhere, '|name='.$iName.'|old='.$this->strOld.'|new='.$sqlVal, 'global updated: '.$iName, 'VAR-U',FALSE,FALSE);

$arUpd = array( 'Value' => $sqlVal, 'WhenUpdated' => 'NOW()' ); $this->Update($arUpd,'Name='.$sqlName); } else { $this->objDB->LogEvent($strWhere, '|name='.$iName.'|val='.$sqlVal, 'global added: '.$iName, 'VAR-I',FALSE,FALSE); $arIns = array( 'Name' => $sqlName, 'Value' => $sqlVal, 'WhenCreated' => 'NOW()' ); $this->Insert($arIns); }

   }
   public function Val($iName,$iValue=NULL) {

if (is_null($iValue)) { return $this->GetVar($iName); } else { return $this->SetVar($iName,$iValue); }

   }

/*

   public function __set($iName, $iValue) {

$this->SetVar($iName,$iValue);

   }
   public function __get($iName) {

return $this->GetVar($iName);

   }
  • /

} /* ==================== *\

   UTILITY FUNCTIONS

\* ==================== */

function DataCents($iCents,$iPfx='$') {

 $out = $iPfx.sprintf("%01.2f",$iCents/100);
 return $out;

} function DataCurr($iCurr,$iPfx='$') {

   if (is_null($iCurr)) {

return NULL;

   } else {

$out = $iPfx.sprintf("%01.2f",$iCurr); return $out;

   }

} function DataDate($iDate) {

   if (is_string($iDate)) {
     $objDate = new DateTime($iDate);

// if ($iDate == 0) { // $out = ; // } else { // $out = date('Y-m-d',$iDate);

     $out = $objDate->format('Y-m-d');
   } else {
     $out = ;
   }
   return $out;

} function DataDateTime($iDate) {

   if (is_string($iDate)) {

if ($iDate == ) { $out = '-'; } else { $objDate = new DateTime($iDate); $out = $objDate->format('Y-m-d H:i'); }

   } else {
     $out = ;
   }
   return $out;

} function IsEmail($iAddr) {

   $ok = preg_match('/^[0-9,a-z,\-,\.]{1,}@([0-9,a-z,\-][\.]{0,1}){2,}$/i', $iAddr );
   return $ok;

} function ArrayToAttrs(array $iarAttr=NULL) {

   $htAttr = ;
   if (is_array($iarAttr)) {

foreach ($iarAttr as $key => $val) { $htAttr .= ' '.$key.'="'.$val.'"'; }

   }
   return $htAttr;

}

/* ==================== *\

   MISSING FUNCTIONS

\* ==================== */ if (!function_exists('http_redirect')) {

   function http_redirect($iURL) {

header('Status: 301 Moved Permanently',TRUE); header('Location: '.$iURL,TRUE);

   }

} </php>