<?PHP
#
#   FILE:  Scout--PopupWindow.php
#   Copyright 2009 Edward Almasy and Internet Scout
#   http://scout.wisc.edu
#

/**
* lightboxed pop-up window with repeat prevention
*/
class PopupWindow {

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

    /**
    * object constructor
    * @param PopupId Integer ID of popup window.
    * @param DB Database object to use for storage and retrieval of window information.
    * @param UserId User ID for repeat prevention.  Defaults to NULL, which indicates no user ID is available.
    */
    function PopupWindow($PopupId, $DB, $UserId = NULL)
    {
        # save our window ID and database handle
        $this->Id = intval($PopupId);
        $this->DB = $DB;
        $this->UserId = $UserId ? intval($UserId) : NULL;

        # set defaults
        $this->Width = 400;
        $this->Height = 200;
        $this->ForceDisplay = FALSE;
        $this->SeenCountThreshold = 5;
        $this->SeenTimeThreshold = 60;
        $this->CookieLifetimeInDays = 90;
    }

    /**
    * initialize popup window tracking (must be called before any HTML output)
    * @param CountThreshold Number of times user must been seen before window displays.
    * @param TimeThreshold Minimum number of seconds from when user was first seen before window displays.
    */
    function Initialize($CountThreshold, $TimeThreshold)
    {
        $this->SeenCountThreshold = $CountThreshold;
        $this->SeenTimeThreshold = $TimeThreshold;
        $this->ShouldDisplay();
    }

    /**
    * set width of window (must be called before any code output method)
    */
    function Width($NewWidth) {  $this->Width = intval($NewWidth);  }
    /**
    * set height of window (must be called before any code output method)
    */
    function Height($NewHeight) {  $this->Height = intval($NewHeight);  }

    /**
    * get integer ID of window
    * @return ID value.
    */
    function Id() {  return $this->Id;  }

    /**
    * report whether pop-up window will display if code printing methods are called
    * @return Returns TRUE if window will display.
    */
    function WillDisplay() {  return $this->ShouldDisplay();  }

    /**
    * sets flag that forces window to display
    * @param Display TRUE to force window to display.
    */
    function AlwaysDisplay($Display) {  $this->ForceDisplay = $Display;  }

    /**
    * print header code (if appropriate)
    */
    function PrintHeaderCode()
    {
        # if we should display the window
        if ($this->ShouldDisplay())
        {
            ?>
            <style type="text/css">@import 'include/thickbox.css';</style>
            <script type="text/javascript" src="include/SPT--jQuery.js"></script>
            <script type="text/javascript" src="include/thickbox-compressed.js"></script>
            <script type="text/javascript">
                $(document).ready(function(){
                    tb_show('', '#TB_inline?inlineId=PopupWindowContent<?PHP  
                            print($this->Id());  ?>&width=<?PHP
                            print($this->Width);  ?>&height=<?PHP
                            print($this->Height);  ?>&modal=true', 'null');
                });
            </script>
            <?PHP
        }
    }

    /**
    * print begin code for window content section in page body (if appropriate)
    */
    function PrintBeginContentCode()
    {
        # if we should display the window
        if ($this->ShouldDisplay())
        {
            # display code for beginning of content section
            ?><div id="PopupWindowContent<?PHP  print($this->Id());  
                    ?>" style="display: none;"><span><?PHP
        }
    }

    /**
    * print end code for window content section in page body (if appropriate)
    */
    function PrintEndContentCode()
    {
        # if we should display the window
        if ($this->ShouldDisplay())
        {
            # display code for end of content section
            ?></span></div><?PHP
        }
    }


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

    private $Id;
    private $DB;
    private $UserId;
    private $Width;
    private $Height;
    private $ForceDisplay;
    private $SeenCountThreshold;
    private $SeenTimeThreshold;
    private $CookieLifetimeInDays;
    private $DisplayStatus;             # local to ShouldDisplay()
    private $UserIdSeenCount;           # local to SeenCountForUserId()
    private $UserIdFirstSeen;
    private $IPAddressSeenCount;        # local to SeenCountForIPAddress()
    private $IPAddressFirstSeen;

    # determine whether pop-up window should be displayed
    private function ShouldDisplay()
    {
        # if user requested always display return TRUE to caller
        if ($this->ForceDisplay) {  return TRUE;  }

        # if we have already determined status for this window
        if (isset($this->DisplayStatus))
        {
            # return status to caller
            return $this->DisplayStatus;
        }

        # if cookie is available
        if (isset($_COOKIE["ScoutPopupCount".$this->Id])
                && isset($_COOKIE["ScoutPopupFirstSeen".$this->Id]))
        {
            # if cookie seen count is below threshold 
            $Count = $_COOKIE["ScoutPopupCount".$this->Id];
            if ($Count < $this->SeenCountThreshold)
            {
                # increase cookie seen count
                setcookie("ScoutPopupCount".$this->Id, ($Count + 1), 
                        (time() + (60*60*24 * $this->CookieLifetimeInDays)));
                setcookie("ScoutPopupFirstSeen".$this->Id, 
                        $_COOKIE["ScoutPopupFirstSeen".$this->Id], 
                        (time() + (60*60*24 * $this->CookieLifetimeInDays)));
            }
            else
            {
                # if enough time has elapsed and we are not sure about displaying window
                if ((time() - $_COOKIE["ScoutPopupFirstSeen".$this->Id])
                            >= $this->SeenTimeThreshold)
                {
                    # if cookie seen count is at threshold 
                    if ($Count == $this->SeenCountThreshold)
                    {
                        # display the window
                        $Display = TRUE;

                        # increase cookie seen count
                        setcookie("ScoutPopupCount".$this->Id, ($Count + 1), 
                                (time() + (60*60*24 * $this->CookieLifetimeInDays)));
                        setcookie("ScoutPopupFirstSeen".$this->Id, 
                                $_COOKIE["ScoutPopupFirstSeen".$this->Id], 
                                (time() + (60*60*24 * $this->CookieLifetimeInDays)));
                    }
                    else
                    {
                        # do not display the window
                        $Display = FALSE;
                    }
                }
            }
        }
        else
        {
            # set cookie
            setcookie("ScoutPopupFirstSeen".$this->Id, time(), 
                    (time() + (60*60*24 * $this->CookieLifetimeInDays)));
            setcookie("ScoutPopupCount".$this->Id, 1, 
                    (time() + (60*60*24 * $this->CookieLifetimeInDays)));
        }

        # if we know the user ID
        if ($this->UserId !== NULL)
        {
            # if we have seen this user ID before
            $Count = $this->SeenCountForUserId();
            if ($Count !== NULL)
            {
                # if user ID seen count is below threshold 
                if ($Count < $this->SeenCountThreshold)
                {
                    # increase user ID seen count
                    $Count = $this->SeenCountForUserId($Count + 1);
                }
                else
                {
                    # if enough time has elapsed
                    if ($this->SecondsSinceUserIdFirstSeen() 
                                    >= $this->SeenTimeThreshold)
                    {
                        # if user ID seen count is at threshold 
                        if ($Count == $this->SeenCountThreshold)
                        {
                            # display the window (if not previously disallowed)
                            if (!isset($Display)) {  $Display = TRUE;  }

                            # increase user ID seen count
                            $Count = $this->SeenCountForUserId($Count + 1);
                        }
                        else
                        {
                            # do not display the window
                            $Display = FALSE;
                        }
                    }
                }
            }
            else
            {
                # add user ID to database
                $Count = $this->SeenCountForUserId(1);
            }
        }

        # if we have seen this IP address before
        $Count = $this->SeenCountForIPAddress();
        if ($Count !== NULL)
        {
            # if IP address seen count is below threshold 
            if ($Count < $this->SeenCountThreshold)
            {
                # increase IP address seen count
                $Count = $this->SeenCountForIPAddress($Count + 1);
            }
            else
            {
                # if enough time has elapsed
                if ($this->SecondsSinceIPAddressFirstSeen() >= $this->SeenTimeThreshold)
                {
                    # if IP address seen count is at threshold 
                    if ($Count == $this->SeenCountThreshold)
                    {
                        # display the window (if not previously disallowed)
                        if (!isset($Display)) {  $Display = TRUE;  }

                        # increase IP address seen count
                        $Count = $this->SeenCountForIPAddress($Count + 1);
                    }
                    else
                    {
                        # do not display the window
                        $Display = FALSE;
                    }
                }
            }
        }
        else
        {
            # add IP address to database
            $Count = $this->SeenCountForIPAddress(1);
        }

        # if we are still not sure whether to display the window
        if (!isset($Display))
        {
            # do not display the window
            $Display = FALSE;
        }

        # save window display status
        $this->DisplayStatus = $Display;

        # return window display status to caller
        return $Display;
    }

    # get/set seen count for current user ID (returns NULL if not set)
    private function SeenCountForUserId($NewSeenCount = NULL)
    {
        # attempt to retrieve count from database
        if (!isset($this->UserIdSeenCount))
        {
            $this->DB->Query("SELECT SeenCount, FirstSeen FROM PopupLog"
                    ." WHERE PopupId = ".$this->Id
                    ." AND SigOne = ".$this->UserId
                    ." AND SigTwo <= 0");
            if( $this->DB->NumRowsSelected() )
            {
                $Tmp = $this->DB->FetchRow();
                $this->UserIdSeenCount = $Tmp["SeenCount"];
                $this->UserIdFirstSeen = $Tmp["FirstSeen"];
            }
            else
            {
                $this->UserIdSeenCount = NULL;
                $this->UserIdFirstSeen = NULL;
            }
        }
        $Count = $this->UserIdSeenCount;

        # if new count supplied
        if ($NewSeenCount !== NULL)
        {
            # if count is already in database
            if ($Count !== NULL)
            {
                # update count in database
                $this->DB->Query("UPDATE PopupLog SET SeenCount = ".$NewSeenCount
                        ." WHERE PopupId = ".intval($this->Id)
                        ." AND SigOne = ".$this->UserId
                        ." AND SigTwo <= 0");
            }
            else
            {
                # add count to database
                $this->DB->Query("INSERT INTO PopupLog"
                        ." (PopupId, SigOne, SigTwo, FirstSeen, SeenCount) VALUES "
                        ." (".$this->Id.", ".$this->UserId.", -1, NOW(), "
                                .$NewSeenCount.")");
            }

            # set current count to new count
            $Count = $NewSeenCount;
        }
        
        # return current count to caller
        return $Count;
    }

    # get/set seen count for IP address of current user (returns NULL if not set)
    private function SeenCountForIPAddress($NewSeenCount = NULL)
    {
        # attempt to retrieve count from database
        if (!isset($this->IPAddressSeenCount))
        {
            $this->DB->Query("SELECT SeenCount, FirstSeen FROM PopupLog"
                    ." WHERE PopupId = ".$this->Id
                    ." AND SigOne = ".$this->UserIPSigOne()
                    ." AND SigTwo = ".$this->UserIPSigTwo());
            if( $this->DB->NumRowsSelected() )
            {
                 $Tmp = $this->DB->FetchRow();
                 $this->IPAddressSeenCount = $Tmp["SeenCount"];
                 $this->IPAddressFirstSeen = $Tmp["FirstSeen"];
            }
            else
            {
                 $this->IPAddressSeenCount = NULL;
                 $this->IPAddressFirstSeen = NULL;
            }
        }
        $Count = $this->IPAddressSeenCount;

        # if new count supplied
        if ($NewSeenCount !== NULL)
        {
            # if count is already in database
            if ($Count !== NULL)
            {
                # update count in database
                $this->DB->Query("UPDATE PopupLog SET SeenCount = ".$NewSeenCount
                        ." WHERE SigOne = ".$this->UserIPSigOne()
                        ." AND SigTwo = ".$this->UserIPSigTwo());
            }
            else
            {
                # add count to database
                $this->DB->Query("INSERT INTO PopupLog"
                        ." (PopupId, SigOne, SigTwo, SeenCount, FirstSeen)"
                        ." VALUES (".$this->Id.", "
                                .$this->UserIPSigOne().", "
                                .$this->UserIPSigTwo().", "
                                .$NewSeenCount.", NOW())");
            }

            # set current count to new count
            $Count = $NewSeenCount;
        }
        
        # return current count to caller
        return $Count;
    }

    # get seconds since current user ID first seen
    private function SecondsSinceUserIdFirstSeen()
    {
        if (!isset($this->UserIdFirstSeen)) {  $this->SeenCountForUserId();  }
        return time() - strtotime($this->UserIdFirstSeen);
    }

    # get seconds since IP address of current user first seen
    private function SecondsSinceIPAddressFirstSeen()
    {
        if (!isset($this->IPAddressFirstSeen)) {  $this->SeenCountForIPAddress();  }
        return time() - strtotime($this->IPAddressFirstSeen);
    }

    # return signature values for IP address of current user
    private function UserIPSigOne()
    {
        $Bytes = explode(".", $_SERVER["REMOTE_ADDR"]);
        return (count($Bytes) != 4) ? 0 
                : (intval($Bytes[0]) * 256) + intval($Bytes[1]);
    }
    private function UserIPSigTwo()
    {
        $Bytes = explode(".", $_SERVER["REMOTE_ADDR"]);
        return (count($Bytes) != 4) ? 0 
                : (intval($Bytes[2]) * 256) + intval($Bytes[3]);
    }
}


?>
