VbzCart/code/admin.cache

Code
<?php /* LIBRARY: admin.cache.php - administration of cache management tables using MW UI  HISTORY: 2010-11-08 extracting from SpecialVbzAdmin 2010-11-09 renaming "data" to "cache" Also renamed everything "Proc" to "Query", but then later decided this was not as great an idea as I originally thought because procs are really the way to do this. 2010-11-11 renaming "query" back to "proc" class clsAdminCacheMgr extends clsCacheMgr { // STATIC methods -- these should later be methods of descendent classes of the component tables /*     INPUT: iName: what to display for a page (or interwiki) link iPfx: full name of page (or interwiki) link, but without the name (iPfx+iName = full page name) */   static public function PageLink($iName,$iPfx) { global $vgOut;

return $vgOut->InternalLink($iPfx.$iName,$iName); }   static public function TableLinkDoc($iName) { return self::PageLink($iName,kwp_DocTblPfx); }   static public function ProcLinkDoc($iName) { return self::PageLink($iName,kwp_DocPrcPfx); } // OVERRIDE methods protected function NewTblTables($iName) { return new clsAdminCacheTables($this, $iName); }   protected function NewTblProcs($iName) { return new clsAdminCacheProcs($this,$iName); }   protected function NewTblFlows($iName) { return new clsAdminCacheFlows($this,$iName); }   protected function NewTblEvents($iName) { return new clsAdminCacheEvents($this,$iName); } // DYNAMIC methods public function MenuDispatch($iAction,$iID=NULL) { switch ($iAction) { case 'cache': $this->AdminPage; break; case 'cache.tbl': $obj = $this->Tables->GetItem($iID); $obj->AdminPage; break; case 'cache.proc': $obj = $this->Procs->GetItem($iID); $obj->AdminPage; break; }   }    public function AdminPage { global $wgRequest, $wgOut; global $vgPage, $vgOut; $vgPage->UseWiki;

$out = '==Cache Management=='."\n"; $wgOut->addWikiText($out,TRUE);	$out = '';

$dbVBZ = $this->Engine;

// check for action requests $strDo = $vgPage->Arg('do'); //$strTbls = $vgPage->Arg('id.table'); if ($strDo != '') { $idTbl = $vgPage->Arg('id'); if (is_numeric($idTbl)) { $objTbl = $this->Tables->GetItem($idTbl); }	   switch ($strDo) { case 'update': $out .= $objTbl->UpdateData_Verbose; $out .= ' Update of '.$objTbl->AdminLink_Name.' complete.'; break; case 'stamp': $arRes = $objTbl->UpdateTime(__METHOD__); $out .= 'Stamped '.$objTbl->AdminLink_Name; $out .= "\n* Was: ".$arRes['was']; $out .= "\n* SQL: ".$arRes['sql']; break; case 'clear': $arRes = $objTbl->ClearTime(__METHOD__); $out .= 'Cleared '.$objTbl->AdminLink_Name; $out .= "\n* Was: ".$arRes['was']; $out .= "\n* SQL: ".$arRes['sql']; break; /*	     case 'table-info': $out = $this->AdminTable($idTbl); break; case 'table-update': $out = $this->UpdateTable($idTbl,'Special:VbzAdmin'); break; case 'table-stamp'; $out = $dbVBZ->StampTable($idTbl); break; }	   $wgOut->addWikiText($out,TRUE);	$out = ''; } else { // display the current status $out = '===Cache Tables==='."\n"; $wgOut->addWikiText($out,TRUE);	$out = ''; //$out = $this->ShowTables($strTbls); $out = $this->ShowTables; $wgOut->addWikiText($out,TRUE);	$out = ''; $out = '===Cache Procedures==='."\n"; $wgOut->addWikiText($out,TRUE);	$out = ''; $this->ShowProcs; }	$dbVBZ->Shut; }

public function ShowProcs { global $vgPage,$vgOut;

$vgPage->UseHTML;

$tblProcs = $this->Procs;

$objRows = $tblProcs->DataSet;	// get all rows

if ($objRows->hasRows) { $out = $vgOut->TableOpen('class=sortable'); $out .= $vgOut->TblRowOpen(NULL,TRUE); $out .= $vgOut->TblCell('procedure name'); $out .= $vgOut->TblCell('A?'); $out .= $vgOut->TblCell('Clr?'); $out .= $vgOut->TblCell('notes'); $out .= $vgOut->TblRowShut; while ($objRows->NextRow) { $out .= $vgOut->TblRowOpen; //$ftName = $this->ProcLinkDoc($objRows->Name); $ftName = $objRows->AdminLink_Name; $isActive = $objRows->Value('isActive'); if (!$isActive) { $ftName = ' '.$ftName.' '; }		 $out .= $vgOut->TblCell($ftName); $out .= $vgOut->TblCell($isActive?'&radic;':'-'); $out .= $vgOut->TblCell(($objRows->Value('doesClear'))?'&radic;':'-'); $out .= $vgOut->TblCell($objRows->Value('Notes')); $out .= $vgOut->TblRowShut; }	   $out .= $vgOut->TableShut; } else { $out .= 'No procedures found!'; }	$vgOut->AddText($out); }   /*      FUTURE: Rename ShowTables -> AdminTables Move most of the code into clsAdminTables */   public function ShowTables { global $vgPage,$vgOut;

$objPage = $vgPage; $vgPage->UseWiki;

//$objTbls = $this->objDB->DataSet('SELECT * FROM '.$this->Tables->Name.' WHERE Name != "" ORDER BY Name'); $objTbls = $this->Tables->GetData('Name != ""',NULL,'Name');

if ($objTbls->hasRows) { $out = "{| class=sortable \n|-\n! table name || last update || sources || targets || notes"; while ($objTbls->NextRow) { if ($objTbls->Value('isActive')) { //$strTbl = $objTbls->Name; $idTbl = $objTbls->KeyValue;

$objSrces = $this->Sources($idTbl); $objTargs = $this->Targets($idTbl); $cntSrces = $objSrces->RowCount; $cntTargs = $objTargs->RowCount;

// table info open/shut calculations //		   $doInfo = isset($lstTbls[$idTbl]);

$htTblName = $objTbls->AdminLink_Name;

// common elements in action links $arLink = $vgPage->Args(array('page')); $arLink['id'] = $idTbl;

// source table list: if ($cntSrces) { /*			$arLink = array(			 'id'	=> $idTbl,			  'do'	=> 'table-update'			  ); $txtSrces .= $objPage->SelfLink($arLink,'update').' ('.$cntSrces.')'; $arLink['do'] = 'update'; $ftLink = $vgOut->SelfLink($arLink,'update','update this table from its sources'); $txtSrces = $cntSrces.'&rarr;'.$ftLink; } else { $txtSrces = ''; } // target table list:

if ($cntTargs) { $arLink['do'] = 'clear'; $ftLink1 = $vgOut->SelfLink($arLink,'un','clear the time-updated stamp for this table');

$arLink['do'] = 'stamp'; $ftLink2 = $vgOut->SelfLink($arLink,'stamp','update the time-updated stamp for this table');

$txtTargs = '('.$ftLink1.')'.$ftLink2.'&rarr;'.$cntTargs; } else { $txtTargs = ''; } $out .= "\n|-\n| ".$htTblName. ' || ' . $objTbls->Value('WhenUpdated'). ' || '.$txtSrces. ' || '.$txtTargs. ' || '.$objTbls->Value('Notes');

// if table is selected to show info, add a row for that: /*		   if ($doInfo) { $out .= "\n|-\n| colspan=5 |"; if ($objSrces->hasRows) { $out .= "\n* Sources:"; while ($objSrces->NextRow) { $objSrce = 	$objDataMgr->Tables->GetItem($objSrces->ID_Srce); if ($objSrce->isActive) { $strName = $objSrce->Name; $out .= ' '.$strName.''; } else { $out .= " N/A id=".$objSrces->ID_Srce.""; }			   }			}			if ($objTargs->hasRows) { $out .= "\n* Targets:"; while ($objTargs->NextRow) { $objTarg = 	$objDataMgr->Tables->GetItem($objTargs->ID_Dest); if ($objTarg->isActive) { $strName = $objTarg->Name; $out .= ' '.$strName.''; } else { $out .= " N/A id=".$objTargs->ID_Dest.""; }			   }			}		    }		}	    }	    $out .= "\n|}"; } else { $out .= 'ERROR: Mysterious lack of data'; }	return $out; } } class clsAdminCacheTables extends clsCacheTables { protected $arMap,$arCkd;

public function __construct(clsCacheMgr $iMgr,$iName) { parent::__construct($iMgr,$iName); $this->ClassSng('clsAdminCacheTable'); $this->ActionKey('cache.tbl'); }

// INFORMATION methods

/*     RETURNS: recordset of targets with more recently-updated sources HISTORY: 2011-12-21 created for update-cat maintenance script */   public function UpdatedTargets { // map out the cache and find out what needs to be run initially Write('Mapping the cache...'); $rcFlows = $this->Mgr->Flow->GetData;	// get table of all flows $this->arMap = $rcFlows->FullMap; $this->arCkd = array;

WriteLn(' ok'); $arRun = $this->arMap['run']; $cntRun = count($arRun); if ($cntRun > 0) { WriteLn('Starting with '.$cntRun.' update'.Pluralize($cntRun).':'); $this->RunUpdates($arRun); }   }    /*      INPUT: $arRun: array of procs that need to be run NOTE: This does not take a rigorous approach to preventing loops. It just cuts off execution after the same Proc has been executed more than a preset number of times. There's probably a very tidy algorithmic way to figure out what order everything should be executed in while rigorously preventing recursion, but I don't have time to figure it out right now. HISTORY: 2011-12-22 created for command line utility 2011-12-24 allowing limited number of duplicate Procs */   public function RunUpdates(array $arRun) { $tblT = $this->Mgr->Tables; $rcT = $tblT->SpawnItem; $arDone = array; do { $isMore = FALSE; $arRunNext = array;

// display run list WriteLn("--\nTO RUN:"); foreach ($arRun as $idP => $objP) { $txt = ' '.$objP->NameFull; $msg = NULL; if (array_key_exists($idP,$arDone)) { $cnt = $arDone[$idP]; if ($cnt > 2) { $msg = ' - LOOP DETECTED; quitting.'; die($txt.$msg."\n"); }		   $msg = ' - repeat #'.$cnt; $arDone[$idP]++; } else { $arDone[$idP] = 1; }		WriteLn($txt.$msg); }

foreach ($arRun as $idP => $objP) { Write('PROC: '.$objP->NameFull); $arEx = $objP->Execute(TRUE);	// FALSE = debug mode -- don't actually write data WriteLn(' - '.$arEx['text']);

if (array_key_exists('targ',$arEx)) { $arT = $arEx['targ'];	// target tables updated if (count($arT) > 0) { //		   WriteLn(' - updated:'); // get list of procs that supply those tables foreach ($arT as $idT => $rowT) { WriteLn(' - updated TABLE: ['.$idT.'] '.$rowT['Name']); $rcT->Values($rowT); $arP = $rcT->TargProcs; if (is_array($arP)) { foreach ($arP as $dummy => $rowP) { WriteLn(' -- used by PROC: '.$rowP->NameFull); }				$arRunNext = ArrayJoin($arRunNext,$arP,TRUE,TRUE);	// replace = yes (probably n/a), append = yes //			   $isMore = TRUE; }			}		   }		}

if (array_key_exists($idP,$arRunNext)) { WriteLn(' - removing '.$objP->NameFull.' from next run list'); unset($arRunNext[$idP]); $txt = ''; foreach ($arRunNext as $idP => $objP) { $txt .= ' '.$idP; }		   WriteLn(' - list is now:'.$txt); }

}	   // ultimately, this list may need to be sorted too: $arRun = $arRunNext; $isMore = Count($arRun) > 0; /*	   Write('TO RUN NEXT:'); foreach ($arRun as $id => $objP) { Write(' '.$objP->Name); }	   WriteLn; } while ($isMore); } /*   protected function FindFlowSources(array $iSrce) { $arOut = array; foreach ($iSrce as $id => $obj) { //	   Write(' -- source ID='.$id.' - '.$obj->Value('Name')); if ($obj->IsActive) { //		WriteLn(''); $ar = $this->FindRoots($obj); foreach ($ar as $id => $row) { if (!array_key_exists($id,$arOut)) { $arOut[$id] = $row; }		}	   } else { //		WriteLn(' - INACTIVE'); }	}	return $arOut; } } class clsAdminCacheTable extends clsCacheTable {

// BOILERPLATE methods

public function AdminLink($iText=NULL,$iPopup=NULL,array $iarArgs=NULL) { return clsAdminData::_AdminLink($this,$iText,$iPopup,$iarArgs); }   public function AdminLink_Name($iPopup=NULL,array $iarArgs=NULL) { return $this->AdminLink($this->Value('Name'),$iPopup,$iarArgs); }   public function DocLink { return $this->Mgr->TableLinkDoc($this->Value('Name')); }

// ACTION methods

/*     ACTION: Run $this->UpdateData and display results */   public function UpdateData_Verbose { global $vgPage, $vgUserName;

$out = ''; //$arRes = $this->Update_byID($iID,$vgUserName.': '.__METHOD__); $arRes = $this->UpdateData($vgUserName.': '.__METHOD__); $out .= $arRes['msgs']; if (is_array($arRes['proc'])) { $arDone = $arRes['proc']; $out .= "\n===Update Procedures Run===\n"; foreach($arDone AS $obj) { $out .= "\n* ".$obj->Value('Name'); }	} else { $out .= ' No procedures were executed.'; }	if (is_array($arRes['targ'])) { $arDone = $arRes['targ']; $out .= "\n===Tables Updated===\n"; foreach($arDone AS $id=>$row) { $out .= "\n* ".$row['Name']; }	} else { $out .= ' No tables were updated.'; }	return $out; }

// ADMIN DISPLAY methods public function AdminPage { global $wgRequest; global $vgPage,$vgOut;

$vgPage->UseWiki;

$out = '==Table: '.$this->Value('Name')."==\n";

$out .= "Note: ADD and DELETE have not yet been tested, and they do not yet log changes.\n";

$idTbl = $this->Value('ID'); $out .= '===Table Record==='; $out .= "\n* ID: $idTbl"; $out .= "\n* Name: ".$this->DocLink; $out .= "\n* Updated: ".$this->Value('WhenUpdated'); $out .= "\n* Notes: ".$this->Value('Notes');

$vgOut->AddText($out); $out='';

// check for any form input (adding flows/procs) $doAddSrce = $wgRequest->getBool('btnAddSrce'); $doAddTarg = $wgRequest->getBool('btnAddTarg'); if ($doAddSrce || $doAddTarg) { $idProc = $wgRequest->getBool('ID_Proc'); $doWrite = $doAddSrce;	// if proc is a source, then it writes to this table $txtNotes = $wgRequest->getText('Notes'); $this->Mgr->Flows->Add($iProc,$this->ID,$doWrite,$txtNotes); }

$vgPage->UseHTML;

// SOURCES cell (left): $out .= $vgOut->Header('Table Sources',3); $out .= $this->AdminSources;

// TARGETS cell (right): $out .= $vgOut->Header('Table Targets',3); $out .= $this->AdminTargets;

$vgOut->AddText($out); }   public function AdminSources { $objRows = $this->Mgr->Sources($this->KeyValue); return $objRows->AdminList('btnAddSrce'); }   public function AdminTargets { $objRows = $this->Mgr->Targets($this->KeyValue); return $objRows->AdminList('btnAddTarg'); } } class clsAdminCacheProcs extends clsCacheProcs { public function __construct(clsCacheMgr $iMgr,$iName) { parent::__construct($iMgr,$iName); $this->ClassSng('clsAdminCacheProc'); $this->ActionKey('cache.proc'); } } class clsAdminCacheProc extends clsCacheProc { public function AdminLink($iText=NULL,$iPopup=NULL,array $iarArgs=NULL) { return clsAdminData::_AdminLink($this,$iText,$iPopup,$iarArgs); }   public function AdminLink_Name($iPopup=NULL,array $iarArgs=NULL) { return $this->AdminLink($this->Value('Name'),$iPopup,$iarArgs); }   public function DocLink { return $this->Mgr->ProcLinkDoc($this->Value('Name')); }   public function AdminPage { global $vgPage,$vgOut;

$vgPage->UseHTML;

$out = $vgOut->Header('Procedure: '.$this->Value('Name'));

$out .= ''; $out .= ' ID: '.$this->KeyValue; $out .= ' Name: '.$this->DocLink; $out .= ' Active: '.NoYes($this->Value('isActive')); $out .= ' Clears: '.NoYes($this->Value('doesClear')); $out .= '';

$vgOut->AddText($out); $out='';

$rcFlows = $this->Mgr->Flow->Data_forProc($this->KeyValue); if ($rcFlows->HasRows) { $ar = $rcFlows->PoolMap; /*	   while ($rcFlows->NextRow) { $idTbl = $rcFlows->Value('ID_Table'); $objTbl = $this->Mgr->Tables->GetItem($idTbl); if ($rcFlows->Value('doWrite')) { // the proc writes to these tables $arTarg[$idTbl] = $objTbl; } else { // the proc reads from these tables $arSrce[$idTbl] = $objTbl; }	   }	    $arTarg = $ar['targ']; $arSrce = $ar['srce']; $dtNewest = NULL; $dtOldest = NULL;

$out .= $vgOut->Header('Source Tables',3); if (isset($arSrce)) { $out .= ''; foreach ($arSrce as $id => $objTbl) { $dtThis = $objTbl->Value('WhenUpdated'); if ($dtThis > $dtNewest) { $dtNewest = $dtThis; }		   $strTime = (is_null($dtThis)?never updated:('updated '.$dtThis)); $out .= ''.$objTbl->AdminLink_Name.' - '.$strTime; }		$out .= ''; } else { $out .= 'No sources found.'; }

$out .= $vgOut->Header('Target Tables',3); if (isset($arTarg)) { $out .= ''; foreach ($arTarg as $id => $objTbl) { $dtThis = $objTbl->Value('WhenUpdated'); if (($dtThis < $dtOldest) || (is_null($dtOldest))) { $dtOldest = $dtThis; }		   $strTime = (is_null($dtThis)?never updated:('updated '.$dtThis)); $out .= ''.$objTbl->AdminLink_Name.' - '.$strTime; }		$out .= ''; } else { $out .= 'No targets found.'; }	   $out .= $vgOut->Header('Status',3); if ($dtOldest < $dtNewest) { $out .= 'Update needed.'; } else { $out .= 'This table is up-to-date.'; }	} else { $out .= 'This procedure is not used.'; }

$vgOut->AddText($out); } } class clsAdminCacheFlows extends clsCacheFlows { public function __construct(clsCacheMgr $iMgr,$iName) { parent::__construct($iMgr,$iName); $this->ClassSng('clsAdminCacheFlow'); $this->ActionKey('cache.flow'); } } class clsAdminCacheFlow extends clsCacheFlow { public function AdminLink($iText=NULL,$iPopup=NULL,array $iarArgs=NULL) { return clsAdminData::_AdminLink($this,$iText,$iPopup,$iarArgs); }   /*      ACTION: Render administrative controls for the current dataset of flows INPUT: name of button for adding another row. If no name given, does not display button. RETURNS: formatted text using $vgOut */   public function AdminList($iAddBtn=NULL) { global $vgOut;

$out = NULL; if ($this->hasRows) { $out .= $vgOut->TableOpen('class=sortable'); $out .= $vgOut->TblRowOpen(NULL,TRUE); $out .= $vgOut->TblCell('Procedure'); $out .= $vgOut->TblCell('Action'); $out .= $vgOut->TblCell('notes'); $out .= $vgOut->TblRowShut;

$doBtn = !is_null($iAddBtn);

$objMgr = $this->Mgr; assert('is_object($this->objMgr)'); $objProcs = $objMgr->Procs; $arProcs = NULL;

$isOdd = TRUE; while ($this->NextRow) { $row = $this->Row;

$ftStyle = $isOdd?'background:#ffffff;':'background:#eeeeee;'; $htStyle = 'style="'.$ftStyle.'"'; $isOdd = !$isOdd;

$idProc = $this->Value('ID_Proc'); $objProc = $objProcs->GetItem($idProc); $ftProc = $objProc->AdminLink_Name;

// exclusion array for drop-down: $arProcs[$idProc] = TRUE;

$ftAct = $this->AdminLink('del');

$ftNotes = $this->Value('Notes');

$out .= $vgOut->TblRowOpen($htStyle); $out .= $vgOut->TblCell($ftProc); $out .= $vgOut->TblCell($ftAct); $out .= $vgOut->TblCell($ftNotes); $out .= $vgOut->TblRowShut; }

// display a row for adding a proc if ($doBtn) { $out .= ''; $out .= $vgOut->TblRowOpen; $out .= $vgOut->TblCell($objProcs->DropDown('ID_Proc',NULL,$arProcs)); $out .= $vgOut->TblCell(''); $out .= $vgOut->TblCell(' '); $out .= $vgOut->TblRowShut; $out .= ' '; }	   $out .= $vgOut->TableShut; } else { $out .= 'none'; }	return $out; } } class clsAdminCacheEvents extends clsCacheEvents { public function __construct(clsCacheMgr $iMgr,$iName) { parent::__construct($iMgr,$iName); $this->ClassSng('clsAdminCacheEvent'); $this->ActionKey('cache.event'); } } class clsAdminCacheEvent extends clsCacheEvent { public function AdminLink($iText=NULL,$iPopup=NULL,array $iarArgs=NULL) { return clsAdminData::_AdminLink($this,$iText,$iPopup,$iarArgs); } }