VbzCart/archive/code/admin.cache
Jump to navigation
Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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 .= '<br>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 = '<s>'.$ftName.'</s>';
}
$out .= $vgOut->TblCell($ftName);
$out .= $vgOut->TblCell($isActive?'√':'-');
$out .= $vgOut->TblCell(($objRows->Value('doesClear'))?'√':'-');
$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.'→'.$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.'→'.$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 .= ' [['.kwp_DocTblPfx.$strName.'|'.$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 .= ' [['.kwp_DocTblPfx.$strName.'|'.$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 .= '<br>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 .= '<br>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 .= '<ul>';
$out .= '<li> <b>ID</b>: '.$this->KeyValue();
$out .= '<li> <b>Name</b>: '.$this->DocLink();
$out .= '<li> <b>Active</b>: '.NoYes($this->Value('isActive'));
$out .= '<li> <b>Clears</b>: '.NoYes($this->Value('doesClear'));
$out .= '</ul>';
$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 .= '<ul>';
foreach ($arSrce as $id => $objTbl) {
$dtThis = $objTbl->Value('WhenUpdated');
if ($dtThis > $dtNewest) {
$dtNewest = $dtThis;
}
$strTime = (is_null($dtThis)?'<i>never updated</i>':('updated '.$dtThis));
$out .= '<li>'.$objTbl->AdminLink_Name().' - '.$strTime;
}
$out .= '</ul>';
} else {
$out .= 'No sources found.';
}
$out .= $vgOut->Header('Target Tables',3);
if (isset($arTarg)) {
$out .= '<ul>';
foreach ($arTarg as $id => $objTbl) {
$dtThis = $objTbl->Value('WhenUpdated');
if (($dtThis < $dtOldest) || (is_null($dtOldest))) {
$dtOldest = $dtThis;
}
$strTime = (is_null($dtThis)?'<i>never updated</i>':('updated '.$dtThis));
$out .= '<li>'.$objTbl->AdminLink_Name().' - '.$strTime;
}
$out .= '</ul>';
} else {
$out .= 'No targets found.';
}
$out .= $vgOut->Header('Status',3);
if ($dtOldest < $dtNewest) {
$out .= '<b>Update needed.</b>';
} 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 .= '<form method=POST>';
$out .= $vgOut->TblRowOpen();
$out .= $vgOut->TblCell($objProcs->DropDown('ID_Proc',NULL,$arProcs));
$out .= $vgOut->TblCell('<input type=submit name="'.$iAddBtn.'" value="Add">');
$out .= $vgOut->TblCell('<input name="notes">');
$out .= $vgOut->TblRowShut();
$out .= '</form>';
}
$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);
}
}