<?php

/*
 *
 *   CWS Calendar, a PHP group calendar tool
 *   Copyright (c) 2006 Oregon State University
 *
 *   This file is part of CWS Calendar.
 *
 *   CWS Calendar is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   CWS Calendar is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with CWS Calendar; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */


/**
 * Demo display page for RSS Calendar Feed
 * @package cws-calendar
 * @subpackage examples
 */

/**
 * Creates a mini-calendar 
 *
 * @param calendar string Shortname of calendar to display (ex: 'osu'),
 *        Or a shortname + layer list:  osu+osu-lectures
 * @param start_date string    Day to start the calendar range, in yyyymmdd format, or 'today'
 * @param range string One of 'day','week', or 'month'
 * @param xsl string Filename of XSL file to use for parsing the RSS output
 * @return string HTML markup for minicalendar
 *
 */
function minical_month ($calendar='osu'$start_date='today'$range=null$xsl null)
{
    
$url MiniCal::build_url($calendar$start_date$range) . 'rss20.xml';
    
$xml MiniCal::get_events_xml($url);
    if(!
$xml){ return; }

    if(
$xsl) {
        
// if an xsl stylesheet is supplied, use that to output the html
        
$output MiniCal::month_xml($xml$xsl$calendar$start_date$range);
    } else {
        
// build our own html that can be styled with CSS
        
$output MiniCal::month_date_array($xml$calendar$start_date$range);
    }
    return 
$output;
}


function 
minical_empty_month($calendar$start_date='today'$range=null)
{
    
$output MiniCal::month_date_array(null$calendar$start_date$range);
    return 
$output;
}

/**
 * Creates a list of calendar events
 *
 * @param calendar string Shortname of calendar to display (ex: 'osu'),
 *        Or a shortname + layer list:  osu+osu-lectures
 * @param start_date string    Day to start the calendar range, in yyyymmdd format, or 'today'
 * @param range string One of 'day','week', or 'month'
 * @return string HTML markup for list of events
 *
 */
function minical_list ($calendar='osu'$start_date='today'$range=null)
{
    
$url MiniCal::build_url($calendar$start_date$range) . 'rss20.xml';

    
$xml MiniCal::get_events_xml($url);
    if(!
$xml){ return ; }

    
$output MiniCal::list_date_array($xml$calendar$start_date$range);

    return 
$output;
}

/** 
 * Wrapper class, mostly for namespace purposes
 * @package cws-calendar
 * @subpackage examples
 *
 */
class MiniCal {

    
/**
     * Fetches the RSS for a calendar, checking for a local cache first
     * @param $url string Url to fetch from
     * @return $xml SimpleXML object of parsed RSS
     */
    
function get_events_xml($url)
    {
        
// Please don't touch these settings:
        
$minical_calendar_url 'http://calendar.oregonstate.edu';
        
$minical_cache_dir '/www/virtual/calendar/cache';


        static 
$last_url;
        static 
$last_xml_obj null;
        if(
is_null($url)) {
            
$url 'http://calendar.oregonstate.edu/osu/today/month/rss20.xml';
        }

        if(!
strstr($url'rss20.xml')){ $url .= '/rss20.xml'; }

        if(
$url !== $last_url) {

            
// first we'll see if the cache file exists:
            //$cache_file = str_replace('/', '.', str_replace($minical_calendar_url, $minical_cache_dir, $url));
            
$cache_file $minical_cache_dir '/' substr(str_replace('/''.'str_replace($minical_calendar_url''$url)), 1);
            if(@
file_exists($cache_file)) {
                
$finfo stat($cache_file);
                if(
$finfo['mtime'] < (time() - 60 5) ) {
                    
// too old
                    
$location $url;
                } else {
                    
$location $cache_file;
                }
            } else {
                
$location $url;
            }

            
$handle = @fopen ($location'r');
            if (!
is_resource($handle)) {
                
//echo "Unable to open $url";
                //exit;
                
$data '';
            } else {
                
$data '';
                while (!
feof($handle)) {
                    
$data .= fgets($handle1024);
                }
                
fclose ($handle);
            }

            
// load xml into a string
            
$xml simplexml_load_string($data);
            
$last_xml_obj $xml;
        } else {
            
$xml $last_xml_obj;
        }
        
$last_url $url;
        return 
$xml;
    }

    
/**
     * Build an XML document describing a month of events, and then process it
     * @param $xml SimpleXML object containing events
     * @param $xsl String filename of XSLT stylesheet to apply to the xml
     * @param $calendar String shortname of calendar to use when generating URLs
     * @param $start_date String Beginning of date range
     * @param $start_date String Number of days from $start_date to include
     * @return $html String HTML output of the XML after it is processed with the $xsl argument
     */
    
function month_xml($xml$xsl$calendar$start_date$range)
    {
        
$r_channel $xml->channel;
        
$r_items $xml->channel->item;

        
$weekdays = array(0=>'S'1=>'M'2=>'T'3=>'W'4=>'T'5=>'F'6=>'S');

        
$date = (is_null($start_date) || strlen($start_date) == 0)?time():strtotime($start_date);

        
// today
        
list($year$month$day) = explode(':'date('Y:n:j'));

        
// Number of days
        
$days_in_month = ($month == ? ($year 28 : ($year 100 29 : ($year 400 28 29))) : (($month 1) % 30 31));

        
// Day of week the 1st falls on
        
$first_weekday weekday(date('Y-m-01'$date));// sunday = 0, saturday = 6

        // output buffering 
        
ob_start();
        
// output title bar
        
echo "\n<month>\n\t<label>".date('F Y',$date)."</label>\n";
        echo 
"\t<title>" $xml->channel->title "</title>\n";
        echo 
"\t<url>" MiniCal::date_url($calendardate('Ymd',$date) . '/month/') . "</url>\n";

        
// output first row up to first day of month
        
echo "\n\t<week>";
        for (
$wd 0$wd $first_weekday$wd++) {
            echo 
"\n\t\t<day />";
        }

        
$today date('j');
        
$this_month date('n');
        
$this_year date('Y');

        
// output all days in month
        
$day_out 1;
        while (
$day_out <= $days_in_month) {
            
// starting a new week
            
if ($wd == 7) {
                echo 
"\n\t</week>\n";
                echo 
"\n\t<week>";
                
$wd 0;
            }
            
// determine if there are any events that appear on this date
            
$entries_today 0;
            foreach (
$r_items as $object) {
                
$children $object->children('http://calendar.oregonstate.edu/');
                
$utc_start strtotime($children->dtstart);
                
$utc_end strtotime($children->dtend);
                
$utc_day_start mktime(000$month$day_out$year);
                
$utc_day_end mktime(235959$month$day_out$year);
                
// event start falls within date
                
if ($utc_start >= $utc_day_start && $utc_start <= $utc_day_end) {
                    
$entries_today++;
                    continue;
                }
                
// event end falls within date
                
if ($utc_end $utc_day_start && $utc_end <= $utc_day_end) {
                    
$entries_today++;
                    continue;
                }
                
// event completely encompasses date
                
if ($utc_start <= $utc_day_start && $utc_end >= $utc_day_end) {
                    
$entries_today++;
                    continue;
                }
            }

            if (
$day_out == $today && $month == $this_month && $year == $this_year) {
                
$extra_element "\n\t\t\t<today>true</today>";
            } else  {
                
$extra_element ="";
            }

            
$href MiniCal::date_url($calendarsprintf('%02d%02d%02d'$year$month$day_out));
            if(
$entry_today) {
                echo 
"\n\t\t<day>$extra_element\n\t\t\t<url>$href</url>\n\t\t\t<number>$day_out</number>\n\t\t</day>";
            } else {
                echo 
"\n\t\t<day hasevents=\"$entries_today\">$extra_element\n\t\t\t<url>$href</url>\n\t\t\t<number>$day_out</number>\n\t\t</day>";
            }

            
$day_out++;
            
$wd++;
        }

        
// finish up last row
        
while ($wd 7) {
            echo 
"\n\t\t<day />";
            
$wd++;
        }
        echo 
"\n\t</week>\n";
        echo 
"</month>\n";

        
$xml_str ob_get_contents();
        
ob_end_clean();

        
$handle fopen ($xsl'r');
        if (!
is_resource($handle)) {
            echo 
"not a resource, fool";
            exit;
        }

        
$xsl_data '';
        while (!
feof($handle)) {
            
$xsl_data .= fgets($handle1024);
        }
        
fclose($handle);

        
$xslt = new DomDocument();
        
$xslt->loadXML($xsl_data);

        
$xml_data = new DomDocument();
        
$xml_data->loadXML($xml_str);

        
$processor = new XsltProcessor();
        
$processor->registerPhpFunctions();
        
$processor->importStylesheet($xslt);

        
$newdom $processor->transformToDoc($xml_data);
        
$output $newdom->saveXML();

        return 
$output;
    }


    
/**
     * Build an HTML fragment describing a month of events
     * @param $xml SimpleXML object containing events
     * @param $calendar String shortname of calendar to use when generating URLs
     * @param $start_date String Beginning of date range
     * @param $range String Number of days from $start_date to include
     * @return $html String HTML output of the XML
     */
    
function month_date_array($xml$calendar$start_date$range
    {
        
$r_channel $xml->channel;
        
$r_items $xml->channel->item;

        
$weekdays = array(0=>'S'1=>'M'2=>'T'3=>'W'4=>'T'5=>'F'6=>'S');

        
$date = (is_null($start_date) || strlen($start_date) == 0)?time():strtotime($start_date);

        list(
$year$month$day) = explode(':'date('Y:n:j'$date));

        
// Number of days
        
$days_in_month = ($month == ? ($year 28 : ($year 100 29 : ($year 400 28 29))) : (($month 1) % 30 31));

        
// Day of week the 1st falls on
        
$first_weekday MiniCal::weekday(date('Y-m-01'$date)); // sunday = 0, saturday = 6

        // output title bar
        
$output['title'] = '';
        if(
$r_channel->title) {
            
$output['title'] .= '<a href="' $r_channel->link '">' .str_replace('OSU Events: '''$r_channel->title) . 
            
'</a><br />';
        }
        
$output['title'] .= date('F Y'$date);
        
$output['rows'] = array();

        
// output first row up to first day of month
        
$row 0;
        
$output['rows'] = array();
        for (
$wd 0$wd $first_weekday$wd++) {
            
$output['rows'][$row][] = "&nbsp;";
        }

        
$today date('j');
        
// output all days in month
        
$day_out 1;
        while (
$day_out <= $days_in_month) {
            
// starting a new week
            
if ($wd == 7) {
                
$row++; 
                
$wd 0;
            }

            
// determine if there are any events that appear on this date
            
$entries_today 0;
            if(
is_array($r_items) || is_object($r_items)) {
                foreach (
$r_items as $object) {
                    
$children $object->children('http://calendar.oregonstate.edu/');
                    
$utc_start strtotime($children->dtstart);
                    
$utc_end strtotime($children->dtend);
                    
$utc_day_start mktime(000$month$day_out$year);
                    
$utc_day_end mktime(235959$month$day_out$year);
                    
// event start falls within date
                    
if ($utc_start >= $utc_day_start && $utc_start <= $utc_day_end) {
                        
$entries_today++;
                        continue;
                    }
                    
// event end falls within date
                    
if ($utc_end $utc_day_start && $utc_end <= $utc_day_end) {
                        
$entries_today++;
                        continue;
                    }
                    
// event completely encompasses date
                    
if ($utc_start <= $utc_day_start && $utc_end >= $utc_day_end) {
                        
$entries_today++;
                        continue;
                    }
                }
            }

            
// keep track of what cell and row is today -> used to specify a class
            
if ($day_out == date('j'$date)) {
                
$output['today']['cell'] = $wd;
                
$output['today']['row'] = $row;

            }
            
//$href = minical_build_url('day/' . sprintf('%02d%02d%02d', $year, $month, $day_out), null, null);
            
$href MiniCal::date_url($calendarsprintf('%02d%02d%02d'$year$month$day_out));

            if (
$entries_today) {
                if(
$entries_today == 1) {
                    
$daytext "<a title=\"$entries_today event\" class=\"minical_hasevents\" href=\"$href\" >$day_out</a>";
                } else {
                    
$daytext "<a title=\"$entries_today events\" class=\"minical_hasevents\" href=\"$href\" >$day_out</a>";
                }
            } else {
                
$daytext "<a href=\"$href\">$day_out</a>";
            }

            
$output['rows'][$row][] = $daytext;
            
$day_out++;
            
$wd++;
        }

        
// finish up last row
        
while ($wd 7) {
            
$output['rows'][$row][] = "&nbsp;";
            
$wd++;
        }
        
ob_start();
        
?>
            <div class="minical_content_box">
            <div class="minical_month"> <?php echo $output['title']; ?> </div>
            <table class="minical_month" width="100%" summary="mini-calendar">
            <thead>
            <tr class="minical_weekdays"><th>S</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th></tr>
            </thead>
            <tbody>
            <?php
            $i 
$j 0;
        foreach (
$output['rows'] as $row) { 
            echo 
"<tr>";
            foreach (
$row as $cell) {
                if (
$i == $output['today']['row'] && $j == $output['today']['cell']) {
                    echo 
"\n\n<td class=\"minical_today\">$cell</td>";
                } else {
                    echo 
"\n\t<td>$cell</td>";
                }
                
$j++;
            }
            
$j 0;
            
$i++;
            echo 
"\n</tr>";
        }
        
?>
            </tbody>
            </table>
            </div>
            <?

            $output 
ob_get_contents();
        
ob_end_clean();
        return 
$output;
    }

    
/**
     * Build an HTML fragment describing a month of events
     * @param $xml SimpleXML object containing events
     * @param $calendar String shortname of calendar to use when generating URLs
     * @param $start_date String Beginning of date range
     * @param $range String Number of days from $start_date to include
     * @return $html String HTML output of the XML
     */
    
function list_date_array($xml$calendar$start_date$range
    {
        
$r_channel $xml->channel;
        
$r_items $xml->channel->item;

        
$date = (is_null($start_date) || strlen($start_date) == 0)?time():strtotime($start_date);

        
// today
        
list($year$month$day) = explode(':'date('Y:n:j'$date));

        
$events_days = array();

        foreach (
$r_items as $object) {
            
$children $object->children('http://calendar.oregonstate.edu/');
            
$pst_start strtotime($children->dtstart);
            
$pst_end strtotime($children->dtend);

            
$day_str date('l, M jS Y'$pst_start);
            if(!
array_key_exists($day_str$events_days)) {
                
$events_days[$day_str] = array();
            }
            
$events_days[$day_str][] = $object;
        }

        
ob_start();
        
//foreach ($output['rows'] as $row) { 
        
foreach ($events_days as $datestr=>$events) {
            if(
$date){ echo '<h3>' $datestr '</h3>'; }
            if(
count($events)) {
                echo 
'<dl>';
                foreach (
$events as $event) {
                    echo 
'<dt><a href="' $event->link '">' $event->title '</a></dt>';
                    echo 
'<dd>' preg_replace('#^<div>.*?<br /></div>#'''$event->description) . '</dd>';
                }
                echo 
"</dl>";
            }
        }
        
$output ob_get_contents();
        
ob_end_clean();
        return 
$output;
    }

    
/**
     * Calcuates and returns the day of the week for a specific date
     *
     * @param string date in form YYYY-MM-DD
     * @param int mode 0 returns 0-6, 1 string, 2 abbrev string
     * @return mixed
     */
    
function weekday($date$mode=0)
    {
        if( !
MiniCal::datevalid($date) ){
            
trigger_error('Invalid date format'E_USER_WARNING);
        }
        list(
$year$month$day$time) = MiniCal::dateparts($date);
        
$jd gregorianToJD($month$day$year);
        return 
jddayofweek($jd$mode);
    }

    
/**
     * Scans a date string in the format YYYY-MM-DD and returns an
     * array containing each part of the date.
     *
     * @param string date in form YYYY-MM-DD
     * @return array ((int)YEAR, (int)MONTH, (int)DAY, (string)TIME)
     */
    
function dateparts($date)
    {
        
$year $month $day 0;
        
$time '';
        if( !
MiniCal::datevalid($date) ){
            
trigger_error('Invalid date format'E_USER_WARNING);
        }
        if( 
preg_match('/^(\d\d\d\d)-(\d\d)-(\d\d)(.*)/'$date$match) ){
            
$year $match[1];
            
$month $match[2];
            
$day $match[3];
            if( isset(
$match[4]) ){
                
$time $match[4];
            }
        }

        return array(
$year$month$day$time);
    }

    
/**
     * Determines if a date is valid for this library.
     * Valid dates are in the form YYYY-MM-DD
     *
     * @param string date
     * @return boolean
     */
    
function datevalid($date)
    {
        return (
preg_match('/^\d\d\d\d-\d\d-\d\d/'$date) > 0);
    }

    
/**
     * Build a calendar URL from the specified parameters
     * @param $calendar String shortname of the calendar to link to
     * @param $start_date String Beginning of date range
     * @param $range String Number of days from $start_date to include
     * @return $url String URL to the main calendar site
     */
    
function build_url($calendar$start_date$range) {
        
$url 'http://calendar.oregonstate.edu/';

        if(! 
is_null($calendar) && strlen($calendar)) { 
            
$url .= $calendar '/'
        }

        if(! 
is_null($start_date) && strlen($start_date)) {
            
$url .= $start_date '/';
            
// and some other stuff, for titling, etc.
        
}

        if(! 
is_null($range) && strlen($range)) { 
            
$url .= $range '/'
        }

        return 
$url;
    }

    
/**
     * Utility function to generate a link to specific day on a specific calendar
     * @param $calendar String shortname of calendar to link to
     * @param $date String date to put in the URL
     * @return $url String URL to the given calendar on the given date
     */
    
function date_url($calendar$date) {
        return 
'http://calendar.oregonstate.edu/' $calendar '/' $date;
    }
}
?>