Location: PHPKode > scripts > bstCountdown > bstcountdown/class.bstCoundown.php
<?

/*

by Bill Lazar, hide@address.com
Copyright (c) 2002, 2003 William Lazar.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

   * Redistributions in binary form must reproduce the above
     copyright notice, this list of conditions and the following
     disclaimer in the documentation and/or other materials provided
     with the distribution.

   * Neither the name of the "XML-RPC for PHP" nor the names of its
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.

How to use this class:

include_once("class.bstCountdown.php");

$cd = new bstCountdown(1); // where 1 is a category ID
$fcd = $cd->formatCDowns(); // returns the formatted string of events
print($fcd);

Styles used in formatter:
#cdTitle       {text-align: left; font-size: large; font-weight: bolder; margin-top: 0; margin-bottom: 0; margin-left: 5px;}
#cdSpacer      {color: black; font-size: 10px; margin: 10px;}
#cdEvents      {color: black; font-size: 10px; font-weight: bold; margin: 10px;}

Database setup:

#
# Table structure for table `cdEvents`
#

CREATE TABLE cdEvents (
  id smallint(6) NOT NULL auto_increment,
  name varchar(75) NOT NULL default '',
  url varchar(120) default NULL,
  tip varchar(255) NULL,
  occurs_on date NOT NULL default '0000-00-00',
  category smallint(6) NOT NULL default '1',
  active enum('y','n') NOT NULL default 'y',
  PRIMARY KEY  (id),
  KEY name (name,occurs_on,category,active)
) TYPE=MyISAM;

--------------------------------------------------------

#
# Table structure for table `cdCategories`
#

CREATE TABLE cdCategories (
  id smallint(6) NOT NULL auto_increment,
  category varchar(50) NOT NULL default '',
  PRIMARY KEY  (id)
) TYPE=MyISAM COMMENT='Categories for the Countdowns table';

Here are my categories:

# Dumping data for table `cdCategories`
#

INSERT INTO cdCategories VALUES (1, 'main');
INSERT INTO cdCategories VALUES (2, 'birthdays');
INSERT INTO cdCategories VALUES (3, 'anniversaries');

VERSION 1.1 NOTE:

- Field TIP was added to TABLE cdEvents
- Tip is optionally used to provide a value for the title field of the HREF/URL and
  is therefore only used if a URL is supplied.

*/

// comment this out and use the version of function setdb below
include_once("site_utils.php");


class bstCountdown {

    var $aCDowns;   // array of events returned
    var $useDB     = "";   // database connection array
    var $cdRows    = 0;   // number of events returned from database
    var $curCat    = "";   // category to use for querying table
    var $fmtCDowns = ""; // holds the formatted string of events

    // if no category is specified, returns all events
    function bstCountdown($cat = "ALL")
    {
        $this->curCat = $cat;
        $this->useDB  = setDB(); // utility db function, see below

        $this->getCDowns();

        return $this;
    }

    // queries the database for the requested category
    function getCDowns()
    {

        $this->setEvents("EMPTY"); // zero out the array for each use
        $cdQuery      = $this->setSelect();
        $cdResult     = mysql_db_query($this->useDB[db],$cdQuery,$this->useDB[conn]) or die("Error in cdEvents query");
        $this->cdRows = mysql_num_rows($cdResult);
        if($this->cdRows > 0)
        {
            $this->buildList($cdResult);
        }
        mysql_free_result($cdResult);
    }

    // lets us customize the query in subclasses
    function setSelect()
    {
        $r = "SELECT *, TO_DAYS(cdEvents.occurs_on) - TO_DAYS(NOW()) as daysUntil FROM cdEvents ORDER BY category, id";
        return $r;
    }

    function buildList($cdResult)
    {
        while($myrow = mysql_fetch_array($cdResult))
        {
            $aCD["name"]      = $myrow["name"];
            $aCD["url"]       = $myrow["url"];
            $aCD["tip"]       = $myrow["tip"];
            $aCD["daysUntil"] = $myrow["daysUntil"];
            $aCD["id"]        = $myrow["id"];
            $aCD["dO"]        = $myrow["occurs_on"];
            $aCD["active"]    = $myrow["active"];
            $aCD["category"]  = $myrow["category"];

            // calling a method here allows subclasses to customize the filter used!
            // also pushes the row onto the class list
            $this->filterEvents($aCD);
        }
    }

    // in this instance we simply return everything in the table
    // and add it to the master list
    function filterEvents($aCD)
    {
        $this->setEvents($aCD);
    }

    // returns a formatted string of the events
    // note: this would be the function to override in a subclass
    //       to customize class for your site while avoiding
    //       version problems if the base class is updated
    // This version is not pretty!
    function formatCDowns()
    {
        $r = "<div id=\"cdTitle\">Events</div><br>\n";

        // make sure it's not an empty table
        if(!$this->aCDowns)
        {
            $r .= "<div id=\"cdEvents\">No Events in any category!</div>\n";
        }
        else
        {
            $r .= "<div style=\"margin-left: 10px; font-weight: bold;\">
                   Cat - ID - Name - Date - Until - Active
                   </div>";
            $r .= "<div style=\"margin-left: 10px\">\n";
            foreach($this->aCDowns as $aCD)
            {
                $nU      = $aCD["daysUntil"];
                $nUntil  = ($nU > 0)? $nU:"($nU)";
                $cURL    = $aCD["url"];
                $cName   = $aCD["name"];
                $cTip    = $aCD["tip"];
                $nID     = $aCD["id"];
                $dOn     = $aCD["dO"];
                $bActive = $aCD["active"];
                $nCat    = $aCD["category"];

                if($cURL == "")  // is there a URL?
                {
                    $r .= "$nCat - $nID - $cName - $dOn - $nUntil - $bActive<br>\n";
                }
                else
                {
                    $r .= "$nCat - $nID - <a href=\"$cURL\" title=\"$cTip\">$cName</a> - $dOn - $nUntil - $bActive<br>\n";
                }
            }
            $r .= "</div>\n";
        }

        $this->fmtCDowns = $r;
        return $r;
    }

    function setEvents($oEv)
    {
        if($oEV != "EMPTY")
        {
            if($oEv["daysUntil"] != "E")  // I have no clue why this is happening
            {
                $this->aCDowns[] = $oEv;
            }
        }
        else
        {
            $this->aCDowns = NULL;
        }
    }
}

/*

This is a library function for me, to simplify MySQL access.
Remember to use your real database values.

Returns a two element array:
[conn] is the MySQL connection
[db] is the MySQL database

function setDB()
{
    // set up some variables
    // server name
    $server = "your host";
    // username
    $user   = "your account";
    // password
    $pass   = "your password";
    // database to query
    $db     = "your db";

    // open a connection to the database
    $connection    = mysql_connect($server, $user, $pass) or die("Invalid server or user");
    $retData[conn] = $connection;
    $retData[db]   = $db;

    return $retData;
}

*/

// handles the needs of my front page, a simple example
class mainpageEvents extends bstCountdown
{
    function mainpageEvents()
    {
        parent::bstCountdown(1);
        return $this;
    }

    // lets us customize the query in subclasses
    function setSelect()
    {
        $r = "SELECT *, TO_DAYS(cdEvents.occurs_on) - TO_DAYS(NOW()) as daysUntil FROM cdEvents WHERE cdEvents.category = '" .$this->curCat. "' AND active = 'y' ORDER BY occurs_on";

        return $r;
    }

    function buildList($cdResult)
    {
        while($myrow = mysql_fetch_array($cdResult))
        {
            $aCD["name"]      = $myrow["name"];
            $aCD["url"]       = $myrow["url"];
            $aCD["tip"]       = $myrow["tip"];
            $aCD["dO"]        = $myrow["occurs_on"];
            $aCD["daysUntil"] = $myrow["daysUntil"];

            $this->filterEvents($aCD);
        }
    }

    function filterEvents($aCD)
    {
        $dOccurs_on = $aCD["daysUntil"];
        if($dOccurs_on > 0)  // make sure this event is not today or in the past
        {
            $this->setEvents($aCD);
        }
    }

    function formatCDowns()
    {
        $r = "<div id=\"cdTitle\">Countdowns</div>\n
              <span id=\"cdSpacer\">(Days Until)</span>\n";

        if(!$this->aCDowns)
        {
            $r .= "<div id=\"cdEvents\">No Events in $this->curCat</div>\n";
        }
        else
        {
            $r .= "<div id=\"cdEvents\">";
            foreach($this->aCDowns as $aCD)
            {
                $nUntil = $aCD["daysUntil"];
                $cURL   = $aCD["url"];
                $cName  = $aCD["name"];
                $cTip   = $aCD["tip"];

                if($cURL == "")  // is there a URL?
                {
                    $r .= "$nUntil - $cName<br>\n";
                }
                else
                {
                    $r .= "$nUntil - <a href=\"$cURL\" title=\"$cTip\">$cName</a><br>\n";
                }
            }
            $r .= "</div>\n";
        }

        $this->fmtCDowns = $r;
        return $r;
    }
}

// only here to be a parent to all lists that handle Perennial events
class perennialCountdown extends bstCountdown
{
    function perennialCountdown($cat)
    {
        parent::bstCountdown($cat);
        return $this;
    }

    function setSelect()
    {
        $r = "SELECT *, DATE_FORMAT(cdEvents.occurs_on, '%M %e') as frmDate, DAYOFYEAR(cdEvents.occurs_on) as doyDO FROM cdEvents WHERE category = '" .$this->curCat. "' AND active = 'y' ORDER BY DAYOFYEAR(cdEvents.occurs_on)";

        return $r;
    }

    function buildList($cdResult)
    {
        while($myrow = mysql_fetch_array($cdResult))
        {
            $aCD["name"]    = $myrow["name"];
            $aCD["url"]     = $myrow["url"];
            $aCD["dO"]      = $myrow["occurs_on"];
            $aCD["frmDate"] = $myrow["frmDate"];
            $aCD["doy"]     = $myrow["doyDO"];

            $this->filterEvents($aCD); // calling a method here allows subclasses to customize the filter used!
        }
    }

    function calcNext($dOn)
    {
        $dToday = time(void);

        $today = getdate();
        $dy    = substr($dOn, 8,2);
        $mo    = substr($dOn, 5,2);
        $yr    = $today['year'];
        $dTarg = mktime(0,0,0,$mo,$dy,$yr);

        // this block is needed in case the event has
        // already passed for the current year
        if(date($dTarg, "z") < date($dToday, "z"))
        {
            $yr    = $today['year'] + 1;
            $dTarg = mktime(0,0,0,$mo,$dy,$yr);  // make it next year
        }

        $r = (int)(($dTarg - $dToday)/86400) + 1;
        // $r = ($r == 0)? 0:($r + 1);

        return $r;
    }
}

// sample of a subclass
class birthdaylistCountdown extends perennialCountdown
{

    function birthdaylistCountdown($cat)
    {
        // category 2 is birthdays in my cat table
        parent::perennialCountdown($cat);

        return $this;
    }

    function formatCDowns()
    {
        // we could make two separate child classes but that seems silly
        // just for one line of code
        if($this->curCat == 2)
        {
            $r = "<div id=\"cdTitle\">Birthdays</div>\n";
        }
        elseif($this->curCat == 3)
        {
            $r = "<div id=\"cdTitle\">Anniversaries</div>\n";
        }

        if(!$this->aCDowns)
        {
            $r .= "<div id=\"cdEvents\">No Events in $this->curCat</div>\n";
        }
        else
        {
            $r .= "<div>\n<ul>";
            foreach($this->aCDowns as $aCD)
            {
                $cName = $aCD["name"];
                $fDate = $aCD["frmDate"];
                $dU    = $aCD["daysUntil"];

                if($aCD["urgent"])
                {
                    $days = ($dU == 1)? "day":"days";
                    $r   .= "<li><strong>$cName - $fDate, $dU $days away</strong></li>\n";
                }
                else
                {
                    $r .= "<li><em>$cName</em> - $fDate</li>\n";
                }
            }
            $r .= "</ul></div>\n";
        }

        $this->fmtCDowns = $r;
        return $r;
    }

    // we don't really want to filter but we do want to set the
    // number of days until the birthday
    function filterEvents($aCD)
    {
        $nUntil           = $this->calcNext($aCD["dO"]);
        $aCD["daysUntil"] = $nUntil;
        $aCD["urgent"]    = ($nUntil < 31);

        $this->setEvents($aCD);
    }
}

?>
Return current item: bstCountdown