User:Woozle/events.php

About

 * Depends on:

Code
<?php /* FILE: events.php -- handling of generalized event logging Originally written to work with FinanceFerret, but should be compatible with standard event tables. Any app-specific code should be moved out into descendant classes. HISTORY: 2010-10-25 clsLogger_DataSet extracted from menu.php 2010-10-29 moved clsEvents and clsEvent from store.php Needs a few tweaks to be fully generalized abstract class clsEvents_abstract extends clsTable_key_single { /*   abstract public function StartEvent(array $iArgs);	// returns clsEvent* abstract public function FinishEvent($iEvent,array $iArgs=NULL); } /*===== CLASS: clsEvents - EVENT LOGGING FUTURE: There should be some way to "register" event types at the start of the code, so that: 1. this class doesn't have to be aware of every class which might want to record events 2. a conflicting name will always generate an error when the code runs, even if no events are triggered, alerting the author that a different name is needed. (Perhaps names should be prefixed     using the module name, e.g. "shop.cart", to minimize the chance of undetected conflicts      due to classes not being used at the same time -- class A using name X is used for awhile,      leaves some events of type "X" in the log, then is discontinued; later, class B uses name X      again. Unless both A and B have very similar purposes, it will be confusing to see A's events      showing up in B's log.) TO DO: Data_forType($iType) -- returns events for the given type only clsEvent::AdminList -- displays table of events //class clsEvents extends clsTable { class clsEvents extends clsEvents_abstract { protected static $arArgs = array(	'descr'		=> 'Descr',	'descrfin'	=> 'DescrFin',	'notes'		=> 'Notes',	'type'		=> 'ModType',	'id'		=> 'ModIndex',	'where'		=> 'EvWhere',	'code'		=> 'Code',	'params'	=> 'Params',	'error'		=> 'isError',	'severe'	=> 'isSevere',     ); /*-     RETURNS: event arguments translated into field names for use in Insert NOTE: This is the method which ultimately determines what the list of values is. INPUT: Array containing zero or more elements whose keys match the keys of self::arArgs, which descendant classes must define. FUTURE: This could probably be a static function. */   protected function CalcSQL(array $iArgs) { $arIns = NULL; foreach ($iArgs as $key=>$val) { if (array_key_exists($key,self::$arArgs)) { $sqlKey = self::$arArgs[$key]; $sqlVal = SQLValue($val); } else { throw new exception('Unrecognized event argument "'.$key.'".'); }	   $arIns[$sqlKey] = $sqlVal; }	return $arIns; }

public function __construct($iDB) { assert('is_object($iDB)'); parent::__construct($iDB); $this->Name('event_log'); $this->KeyName('ID'); $this->ClassSng('clsEvent'); }   /*      USAGE: This version is for logging general events with no stop/start; they are treated as "finished" only */   public function LogEvent($iWhere,$iParams,$iDescr,$iCode,$iIsError,$iIsSevere) { global $sql, $vgUserName;

if ($iIsSevere) { $txtSubj = KS_STORE_NAME.' internal error: '.$iCode; $txtBody = $iWhere.' generated an internal error:' ."\n* What: $iDescr" ."\n* Who: $vgUserName" ."\n* Address: ".$_SERVER['REMOTE_ADDR'] ."\n* Params: $iParams"; $okMail = mail(KS_EMAIL_ADMIN,$txtSubj,$txtBody); }

$sql = 'INSERT INTO `'.$this->Name.'` (WhenFinished,EvWhere,Params,Descr,Code,WhoAdmin,WhoSystem,WhoNetwork,isError,isSevere)'. 'VALUES(NOW,"'.$iWhere.'","'.$iParams.'","'.$iDescr.'",'.SQLValue($iCode).','.SQLValue($vgUserName).	 ',NULL,"'.	  $_SERVER['REMOTE_ADDR'].'",'.	  ($iIsError?'TRUE':'FALSE').','.	  ($iIsSevere?'TRUE':'FALSE').');'; $this->objDB->Exec($sql); if ($iIsSevere) { throw new exception($txtBody);	// this should send a second, more detailed email }   }    /*-      RETURNS: event arguments translated into field names for use in Insert NOTE: This is the method which ultimately determines what the list of values is. INPUT: Array containing any of the following elements: 'descr': description of event 'descrfin': description of information found during event 'where': location in code where event is taking place (usually __METHOD__ will do) 'code': event code unique to type 'params': text giving parameters used or determined 'error' (value ignored): if present, event represents an error 'severe' (value ignored): if present, event represents a severe error These fields are sometimes set automatically by subclasses: 'type': type of event - usually ActionKey (use of kType* class constants is now deprecated) 'id': ID of row in table corresponding to event type */ /*   protected function CalcSQL(array $iArgs) { $arIns = NULL; foreach ($iArgs as $key=>$val) { switch ($key) { case 'descr': $sqlKey = 'Descr'; $sqlVal = SQLValue($val); break; case 'descrfin': $sqlKey = 'DescrFin'; $sqlVal = SQLValue($val); break; case 'notes': $sqlKey = 'Notes'; $sqlVal = SQLValue($val); break; case 'type': $sqlKey = 'ModType'; $sqlVal = SQLValue($val); break; case 'id': $sqlKey = 'ModIndex'; $sqlVal = SQLValue($val);	// can be NULL break; case 'where': $sqlKey = 'EvWhere'; $sqlVal = SQLValue($val); break; case 'code': $sqlKey = 'Code'; $sqlVal = SQLValue($val);	// can be NULL break; case 'params': $sqlKey = 'Params'; $sqlVal = SQLValue($val);	// can be NULL break; case 'error': $sqlKey = 'isError'; $sqlVal = TRUE; break; case 'severe': $sqlKey = 'isSevere'; $sqlVal = TRUE; break; default: die ('UNRECOGNIZED EVENT PARAMETER: ['.$key.']'); }	   $arIns[$sqlKey] = $sqlVal; }	return $arIns; }   /*-      ACTION: Logs an event from specs in an array Possible specs are determined by CalcSQL */   public function StartEvent(array $iArgs) { global $vgUserName;

if (empty($iArgs)) { throw new exception('No arguments given for event.'); }

$arIns = $this->CalcSQL($iArgs); if (empty($arIns)) { return NULL; } else { $arIns['WhenStarted'] = 'NOW'; $arIns['WhoNetwork'] = SQLValue($_SERVER['REMOTE_ADDR']); $arIns['WhoAdmin'] = SQLValue($vgUserName); $ok = $this->Insert($arIns); if ($ok) { return $this->objDB->NewID(__METHOD__); } else { return NULL; }	}   }    public function FinishEvent($iEvent,array $iArgs=NULL) { if (is_array($iArgs)) { $arUpd = $this->CalcSQL($iArgs); //$arUpd = array_merge($arUpd,$iArgs); }	$arUpd['WhenFinished'] = 'NOW'; $this->Update($arUpd,'ID='.$iEvent); } } class clsEvent extends clsDataSet { } /*%%%% HISTORY: 2013-02-18 removing $iEdits parameter -- nothing seems to use it; may have been left over from the work that ultimately resulted in clsFxEvents abstract class clsLogger { abstract public function StartEvent(array $iarArgs); abstract public function FinishEvent(array $iarArgs=NULL); } /*%%%% PURPOSE: This is a helper class for clsDataSet and clsEvents. It is initialized with a DataSet object, and provides event logging. This is essentially multiple inheritance the hard way. This replaces clsAdminData_Logged. HISTORY: 2011-10-09 changed 1st constructor parameter from clsDataSet (deprecated) to clsRecs_key_single 2013-02-18 removing $iEdits parameter -- nothing seems to use it; may have been left over from the work that ultimately resulted in clsFxEvents class clsLogger_DataSet extends clsLogger { protected $obj; protected $log; protected $idEvent;

public function __construct(clsRecs_key_single $iObj, clsEvents_abstract $iLog) { $this->obj = $iObj; $this->log = $iLog; }   /*=====      INPUT: Array containing any of several possible elements as defined by clsEvents */   public function StartEvent(array $iarArgs) { $arArgs = $iarArgs; $obj = $this->obj;

$arArgs['type'] = $obj->Table->ActionKey;	// TO DO: de-couple this from URL keys $arArgs['id'] = $obj->KeyValue;

$this->idEvent = $this->log->StartEvent($arArgs,$obj->Values); return $this->idEvent; }   public function FinishEvent(array $iarArgs=NULL) { $this->log->FinishEvent($this->idEvent,$iarArgs); unset($this->idEvent); }   /*      RETURNS: dataset consisting of events related to the specific DatSet object given */   public function EventData($iDebug=FALSE) { $obj = $this->obj; $sql = '(ModType="'.$obj->Table->ActionKey.'") AND (ModIndex='.$obj->KeyValue.')'; if (!$iDebug) { $sql .= ' AND (NOT isDebug)'; }	return $this->log->GetData($sql); }   public function EventListing($iDebug=FALSE) { $objRows = $this->EventData($iDebug); if ($objRows->HasRows) { $out = $objRows->AdminRows; } else { $out = 'No events found here.'; }	return $out; } }

/*%%%% PURPOSE: more detailed event table class which keeps separate records for each field modified HISTORY: 2013-01-03 started class clsFxEvents extends clsEvents_abstract { protected $tblFields;

public function __construct($iDB) { $this->tblFields = NULL; parent::__construct($iDB); $this->Name('event_log'); $this->KeyName('ID'); $this->ClassSng('clsFxEvent'); }   protected function TblFields { if (is_null($this->tblFields)) { $this->tblFields = $this->Engine->EventFields; }	return $this->tblFields; }   /*-      ACTION: Logs an event from specs in an array RETURNS: ID of new event, or NULL */   public function StartEvent(array $iArgs,array $iOldVals, array $iNewVals) { if (empty($iArgs)) { throw new exception('No arguments given for event.'); }

$arIns['ModType'] = $iArgs['tbl']; $arIns['ModIndex'] = $iArgs['idx']; $arIns['ID_User'] = $iArgs['user']; $arIns['WhenStarted'] = 'NOW'; $ok = $this->Insert($arIns); if ($ok) { $idEv = $this->objDB->NewID(__METHOD__); $this->TblFields->LogEdits($idEv,$iOldVals,$iNewVals); return $idEv; } else { return NULL; }   }    public function FinishEvent($iEvent,array $iArgs=NULL) { } } class clsFxEvents_Flds extends clsTable_key_single { public function __construct($iDB) { parent::__construct($iDB); $this->Name('event_log_fields'); }   public function LogEdits($idEvent, array $iOldVals, array $iNewVals) { foreach ($iNewVals as $key => $val) { $arIns = array(	     'ID_Event'	=> $idEvent,	      'ModField'	=> SQLValue($key),	      'ValOld'		=> SQLValue($iOldVals[$key]),	      'ValNew'		=> SQLValue($val),	      ); $this->Insert($arIns); }   } }