<?PHP

#
#   FILE:  Scout--EventLog.php
#
#   METHODS PROVIDED:
#       EventLog()
#           - constructor
#       SomeMethod($SomeParameter, $AnotherParameter)
#           - short description of method
#
#   AUTHOR:  Edward Almasy
#
#   Copyright 2007 Internet Scout
#   http://scout.wisc.edu
#


/**
* Class for storing and retrieving event information from database.
*/
class EventLog {

    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Object constructor.
    * @param DB Database object.
    * @param UserId ID of current user.  (OPTIONAL)
    * @param LoggingEnabled Whether event logging is currently enabled.
    *       (OPTIONAL, defaults to TRUE)
    */
    function EventLog($DB, $UserId = -1, $LoggingEnabled = TRUE)
    {
        $this->DB = $DB;
        $this->Enabled = $LoggingEnabled;
        $this->UserId = intval($UserId);
    }

    /**
    * Add event to log.
    * @param Type Type of event.
    * @param DataOne First data value for event.  (OPTIONAL)
    * @param DataTwo Second data value for event.  (OPTIONAL)
    */
    function Log($Type, $DataOne = "", $DataTwo = "")
    {
        # if logging is turned on
        if ($this->Enabled)
        {
            # write event out to log
            $this->DB->Query("INSERT INTO EventLog"
                    ." (EventType, EventDate, UserId, DataOne, DataTwo) VALUES"
                    ." (".intval($Type).", NOW(), ".$this->UserId.","
                        ." '".addslashes($DataOne)."',"
                        ." '".addslashes($DataTwo)."')");
        }
    }

    /**
    * Retrieve specified range of events.  Takes a variable number of arguments,
    * with as many EventType parameters as desired.  Dates are inclusive.
    * @param StartDate Start date of range to search in SQL-compatible format.
    *       (OPTIONAL, pass NULL to omit)
    * @param EndDate End date of range to search in SQL-compatible format.
    *       (OPTIONAL, pass NULL to omit)
    * @param EventCount Maximum number of events to retrieve.  (OPTIONAL)
    * @param EventType Type of event to find.  (OPTIONAL)
    * @return Array of event records, with each record having the indexes
    *       "EventType", "EventDate", "UserId", "DataOne", and "DataTwo".
    */
    function FindEvents(
            $StartDate = NULL, $EndDate = NULL, $EventCount = 999999999, $EventType)
    {
        # retrieve arguments (if supplied)
        $StartDate = NULL;
        $EndDate = NULL;
        $StartIndex = 0;
        $EventCount = 100;
        $Types = array();
        $Args = func_get_args();
        if (count($Args)) {  $StartDate = array_shift($Args);  }
        if (count($Args)) {  $EndDate = array_shift($Args);  }
        if (count($Args)) {  $StartIndex = array_shift($Args);  }
        if (count($Args)) {  $EventCount = array_shift($Args);  }
        while (count($Args))
        {
            $Types[] = array_shift($Args);
        }

        # add start and/or end date to query condition (if supplied)
        $Conditions = "";
        if ($StartDate)
        {  
            $Conditions .= " EventDate >= '".addslashes($StartDate)."'";
        }
        if ($EndDate) 
        {  
            $Conditions .= (strlen($Conditions) ? " AND" : "")
                    ." EventDate <= '".addslashes($EndDate)."'";
        }

        # add event types to query condition (if supplied)
        $SubCondition = "";
        foreach ($Types as $Type)
        {
            $SubCondition .= (strlen($SubCondition) ? " OR" : "")
                    ." EventType = ".intval($Type);
        }
        if (strlen($SubCondition))
        {
            $Conditions .= (strlen($Conditions) ? " AND" : "")
                    ." (".$SubCondition.")";
        }

        # if user privilege exclusions have been specified
        if (isset($this->ExcludedPrivilegesForFind))
        {
            # add beginning of exclusion conditions and subquery
            $Conditions .= (strlen($Conditions) ? " AND" : "")
                    ." UserId NOT IN (SELECT UserId FROM APUserPrivileges WHERE ";

            # add subquery condition for each exclusion
            $Connector = "";
            foreach ($this->ExcludedPrivilegesForFind as $Exclusion)
            {
                $Conditions .= $Connector."Privilege "
                        .$Exclusion["Operator"]." ".$Exclusion["Value"];
                $Connector = " OR ";
            }

            # close out subquery condition
            $Conditions .= ")";
        }

        # if SQL query conditions have been specified
        if (isset($this->ConditionsForFind))
        {
            # add conditions to condition string
            foreach ($this->ConditionsForFind as $Condition)
            {
                $Conditions .= (strlen($Conditions) ? " AND " : " ").$Condition;
            }
        }

        # build event query
        $Query = "SELECT * FROM EventLog"
                .(strlen($Conditions) ? " WHERE ".$Conditions : "")
                ." ORDER BY EventDate DESC LIMIT ".$StartIndex.", ".$EventCount;

        # run query and retrieve event information
        $this->DB->Query($Query);
        $Events = array();
        while ($EventInfo = $this->DB->FetchRow()) {  $Events[] = $EventInfo;  }

        # return event information to caller
        return $Events;
    }

    /**
    * Add privilege to exclude from FindEvents() results.
    * Operators should be SQL-compatible (i.e. "=" for equals, not "==").
    * Multiple operator/value conditions are ORed together.
    * Pass in NULL for Operator and NULL for Value to clear all current exclusions.
    * @param Operator Numerical comparison operator to use with privilege
    *       (for example: "=", ">", "<=", "!=" etc)..
    * @param Value Privilege value to compare.
    */
    function ExcludeUsersWithPrivilegesForFind($Operator, $Value)
    {
        # if caller requested clear
        if (($Operator === NULL) && ($Value === NULL))
        {
            # clear exclusions
            unset($this->ExcludedPrivilegesForFind);
        }
        else
        {
            # add specified exclusion
            $Exclusion["Operator"] = $Operator;
            $Exclusion["Value"] = $Value;
            $this->ExcludedPrivilegesForFind[] = $Exclusion;
        }
    }

    /**
    * Add SQL condition to apply to FindEvents().
    * Multiple conditions are ANDed.
    * Pass in NULL to clear all current conditions.
    * @param Conditions String with single condition or array with multiple conditions.
    */
    function AddSqlConditionForFind($Conditions)
    {
        # if caller requested clear
        if ($Conditions === NULL)
        {
            # clear all conditions
            unset($this->ConditionsForFind);
        }
        else
        {
            # convert condition to array if only one specified
            if (!is_array($Conditions)) {  $Conditions = array($Conditions);  }

            # add conditions to list
            $this->ConditionsForFind = isset($this->ConditionsForFind)
                    ? array_merge($this->ConditionsForFind, $Conditions)
                    : $Conditions;
        }
    }

    /**
    * Limit FindEvents() results to user with specified ID.
    * @param UserId User ID to return results for.  (OPTIONAL, defaults
    *       to NULL which uses user ID passed in to EventLog())
    */
    function LimitFindToUser($UserId = NULL)
    {
        if ($UserId === NULL)
        {
            $UserId = $this->UserId;
        }
        $this->AddSqlConditionForFind("UserId = ".intval($UserId));
    }
    
    /**
    * Modify existing events.
    * All parameters are ANDed to select which events are modified.
    * @param EventType Code for type of events to modify.
    * @param EventDate SQL-compatible date for events to modify.
    * @param UserId ID for user for events to modify.
    * @param DataOne New value for DataOne field.  (OPTIONAL, pass in NULL to not modify field)
    * @param DataTwo New value for DataTwo field.  (OPTIONAL, pass in NULL to not modify field)
    * @param Condition SQL condition to apply to select events to modify. 
    */
    function ModifyEvents($EventType, $EventDate, $UserId, 
            $DataOne = NULL, $DataTwo = NULL, $Condition = NULL)
    {
        if ($DataOne || $DataTwo)
        {
            $this->DB->Query("UPDATE EventLog SET"
                    .($DataOne ? " DataOne = '".addslashes($DataOne)."'" : "")
                    .(($DataOne && $DataTwo) ? ", " : "")
                    .($DataTwo ? " DataTwo = '".addslashes($DataTwo)."'" : "")
                    ." WHERE EventType = '".addslashes($EventType)."'"
                        ." AND EventDate = '".addslashes($EventDate)."'"
                        ." AND UserId = '".addslashes($UserId)."'");
        }
    }


    # ---- PRIVATE INTERFACE -------------------------------------------------

    private $DB;
    private $Enabled;
    private $UserId;
    private $ExcludedPrivilegesForFind;
    private $ConditionsForFind;

}


?>
