pluf2

pluf2 Git Source Tree


Root/src/Pluf/Calendar.php

<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of Plume Framework, a simple PHP Application Framework.
# Copyright (C) 2001-2007 Loic d'Anterroches and contributors.
#
# Plume Framework is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Plume Framework 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# ***** END LICENSE BLOCK ***** */

/**
 * Calendar to display a list of events in a calendar table.
 *
 * The calendar is independent of other elements of Pluf, you can use
 * it standalone if you want.
 *
 * The principle is that you set options and feed the calendar with a
 * list of events. Based on the options, the render() method will
 * produce different views of the calendar.
 */
class Pluf_Calendar
{
    /**
     * The list of events to display.
     */
    var $events = array();
    var $summary = '';

    /**
     * The display options of the calendar.
     */
    var $opts = array();

    // When updating an interval, if a col span more rows and columns,
    // store the info for the next rows to compensate as needed.
    var $bspans = array();

    /**
     * List of events without the events not between the start/end
     * days.
     */
    var $_events = array();

    /**
     * List of time intervals in the $_events list.
     */
    var $_time_intervals = array();

    /**
     * Simultaneous events at a given time slot, for a given group.
     *
     * array('2007-03-25' => 
     *       array(array('time' => '10:15',
     *                   'start' => 4 , 
     *                   'continued' => 5),
     *             array('time' => '11:30',
     *                   'start' => 3 , 
     *                   'continued' => 0),
     *             )
     *       '2007-03-24' =>
     *       array(array('time' => '11:30',
     *                   'start' => 2 , 
     *                   'continued' => 3),
     *            )
     *       )
     *
     */
    var $_simultaneous = array();
    var $_max_simultaneous = array();
    var $_groups = array();

    /**
     * Render the calendar based on the options.
     */
    public function render()
    {
        if (count($this->events) == 0) {
            return '';
        }
        $this->cleanEventList();
        $this->getTimeIntervals();
        $this->getSimultaneous();
        $this->getMaxSimultaneous();
        $s = '';
        if ($this->summary) {
            $s = 'summary="'.htmlspecialchars($this->summary).'" ';
        }
        $out = '<table '.$s.'cellspacing="0" class="px-calendar">'."\n";
        $out .= $this->getHead();
        $out .= $this->getBody();
        $out .= '</table>'."\n";
        return Pluf_Template_SafeString::markSafe($out);
    }

    /**
     * Event are grouped by day per default, you can group as you
     * want, just subclass this method. Groups are used to make
     * columns in the table with the headings.
     */
    function getEventGroup($event)
    {
        return substr($event['start'], 0, 10);
    }

    /**
     * Get all the available groups.
     */
    function getGroups()
    {
        if (count($this->_groups)) {
            return $this->_groups;
        }
        foreach ($this->_events as $event) {
            $group = $this->getEventGroup($event);
            if (!in_array($group, $this->_groups)) {
                $this->_groups[] = $group;
            }
        }
        return $this->_groups;
    }

    /**
     * Get the name of a group to print in the headers.
     */
    function getGroupName($group)
    {
        $dw = $this->daysOfWeek();
        $days = date('w', strtotime($group));
        return htmlspecialchars($dw[$days%7]);
    }

    /**
     * Generate the body of the calendar.
     */
    function getBody()
    {
        $out = '<tbody>'."\n";
        $inters = $this->getTimeIntervals();
        $groups = $this->getGroups();
        for ($i=0;$i<(count($inters)-1);$i++) {
            $out .= '<tr>'."\n";
            $out .= '  <th scope="row">'.$inters[$i].' - '.$inters[$i+1].'</th>'."\n";
            foreach ($groups as $group) {
                $out .= $this->getEventCell($group, $inters[$i]);
            }
            $out .= '</tr>'."\n";
        }
        $out .= '</tbody>'."\n";
        return $out;
    }


    /**
     * Get the value to print for the given cell
     *
     * @param string Current group
     * @param string Current interval
     * @return string Table cells
     */
    function getEventCell($group, $inter)
    {
        $out = '';
        $max = $this->getMaxSimultaneous();
        $fullspanevent = false;
        foreach ($this->_events as $event) {
            // Get the start time of the event
            $e_start = substr($event['start'], 11, 5);
            if ($e_start != $inter) {
                // If the event does not start at the current time,
                // skip it
                continue;
            }
            if ($group != $this->getEventGroup($event)) {
                // Check if full span even at this time interval
                if (!empty($event['fullspan'])) {
                    $fullspanevent = true;
                }
                continue;
            }
            // Find how many rows the event will span
            $extra = '';
            $content = '';
            if (!isset($event['content'])) $event['content'] = '';
            $row_span = $this->getEventRowSpanning($event, $this->_time_intervals);
            if ($row_span > 1) {
                $extra .= ' rowspan="'.$row_span.'"';
            } 
            if (!empty($event['fullspan'])) {
                $colspan = 0;
                foreach ($max as $_s) {
                    $colspan += $_s;
                }
                $extra .= ' colspan="'.$colspan.'"';
                $fullspanevent = true;
            }
            if (strlen($event['color']) > 0) {
                $extra .= ' style="background-color: '.$event['color'].';"';
            }
            if (strlen($event['content']) > 0) {
                $content .= $event['content'];
            } 
            if (strlen($event['url']) > 0) {
                $content .= '<a href="'.$event['url'].'">'.htmlspecialchars($event['title']).'</a>';
            } 
            if (strlen($event['content']) == 0 and strlen($event['url']) == 0) {
                $content .= htmlspecialchars($event['title']);
            }
            $out .= '  <td'.$extra.'>'.$content.'</td>'."\n";
        }
        if (!$fullspanevent) {
            $sim = null;
            foreach ($this->_simultaneous[$group] as $_sim) {
                if ($_sim['time'] == $inter) {
                    $sim = $_sim;
                    break;
                }
            }
            $diff = $max[$group] - ($sim['start'] + $sim['continued']);
            for ($k=0; $k<$diff; $k++) {
                $out .= '  <td class="empty"> </td>'."\n";
            }
        }
        return $out;
    }

    /**
     * Get event spanning over the rows.
     *
     * @param array Event
     * @param array Intervals
     * @return int Spanning
     */
    function getEventRowSpanning($event, $inters)
    {
        $start = substr($event['start'], 11, 5);
        $end = substr($event['end'], 11, 5);
        $span = 1;
        foreach ($inters as $inter) {
            if ($inter < $end and $inter > $start) {
                $span++;
            }
        }
        return $span;
    }

    /**
     * Generate the head of the calendar.
     */
    function getHead()
    {
        $out = '<thead>'."\n".'<tr>'."\n".'  <th> </th>'."\n";
        // Print the groups.
        $groups = $this->getGroups();
        $max = $this->getMaxSimultaneous();
        foreach ($groups as $group) {
            if ($max[$group] > 1) {
                $span = ' colspan="'.$max[$group].'"';
            } else {
                $span = '';
            }
            $out .= '  <th scope="col"'.$span.'>'.$this->getGroupName($group).'</th>'."\n";
        }
        $out .= '</tr>'."\n".'</thead>'."\n";
        return $out;
    }

    /**
     * Get the rowspan for each day.
     */
    function getDaySpanning()
    {
        list($start, $end) = $this->getStartEndDays();
        $inters = $this->getTimeIntervals($start, $end);
        $n = $this->getDayNumber($start, $end);
        $inter_n = array_fill(0, count($inters), 0);
        $day_span = array_fill(0, $n+1, $inter_n);
        foreach ($this->events as $event) {
            // The event must be between $start and $end
            $e_dstart = substr($event['start'], 0, 10);
            $e_dend = substr($event['end'], 0, 10);
            if ($e_dend < $start or $e_dstart > $end) {
                continue;
            }
            
            $day = $this->getDayNumber($start, substr($event['end'], 0, 10));
            $e_start = substr($event['start'], 11, 5);
            $e_end = substr($event['end'], 11, 5);
            $i = 0;
            foreach ($inters as $inter) {
                if ($inter < $e_end and $inter >= $e_start) {
                    $day_span[$day][$i]++;
                }
                $i++;
            }
        }
        return $day_span;
    }

    /**
     * Get an array with the days of the week.
     */
    function daysOfWeek()
    {
        return array(
                     __('Sunday'),
                     __('Monday'),
                     __('Tuesday'),
                     __('Wednesday'),
                     __('Thursday'),
                     __('Friday'),
                     __('Saturday'),
                     );
    }

    /**
     * Get the number of days to list.
     *
     * @param string Start date
     * @param string End date
     * @return int Number of days
     */
    function getDayNumber($start, $end)
    {
        Pluf::loadFunction('Pluf_Date_Compare');
        $diff = Pluf_Date_Compare($start.' 00:00:00', $end.' 00:00:00');
        return (int) $diff/86400;
    }

    /**
     * Get the start and end dates based on the event list.
     *
     * @return array (start day, end day)
     */
    function getStartEndDays()
    {
        $start = '9999-12-31';
        $end = '0000-01-01';
        if (!isset($this->opts['start-day']) 
            or !isset($this->opts['end-day'])) {
            foreach ($this->events as $event) {
                $t_start = substr($event['start'], 0, 10);
                $t_end = substr($event['end'], 0, 10);
                if ($t_start < $start) {
                    $start = $t_start;
                }
                if ($t_end > $end) {
                    $end = $t_end;
                }
            }
        }
        if (isset($this->opts['start-day'])) {
            $start = $this->opts['start-day'];
        } else {
            $this->opts['start-day'] = $start;
        }
        if (isset($this->opts['end-day'])) {
            $end = $this->opts['end-day'];
        } else {
            $this->opts['end-day'] = $end;
        }
        return array($start, $end);
    }

    /**
     * Clean event list.
     */
    function cleanEventList()
    {
        list($start, $end) = $this->getStartEndDays();
        $this->_events = array();
        foreach ($this->events as $event) {
            $e_dstart = substr($event['start'], 0, 10);
            $e_dend = substr($event['end'], 0, 10);
            if ($e_dend < $start or $e_dstart > $end) {
                continue;
            }
            $this->_events[] = $event;
        }
        return true;
    }

    /**
     * Get the time intervals. They span all the groups.
     */
    function getTimeIntervals($start='', $end='')
    {
        if (count($this->_time_intervals)) {
            return $this->_time_intervals;
        }
        $intervals = array();
        foreach ($this->_events as $event) {
            $t = substr($event['start'], 11, 5);
            if (!in_array($t, $intervals)) {
                $intervals[] = $t;
            }
            $t = substr($event['end'], 11, 5);
            if (!in_array($t, $intervals)) {
                $intervals[] = $t;
            }
        }
        sort($intervals);
        $this->_time_intervals = $intervals;
        return $intervals;
    }

    /**
     * Get simultaneous events at the same time slot and same group.
     */
    function getSimultaneous()
    {
        foreach ($this->getGroups() as $group) {
            $this->_simultaneous[$group] = array();
            foreach ($this->_time_intervals as $inter) {
                $this->_simultaneous[$group][] = array('time' => $inter,
                                                       'start' => 0,
                                                       'continued' => 0);
            }
        }
        foreach ($this->_events as $event) {
            $group = $this->getEventGroup($event);
            $e_tstart = substr($event['start'], 11, 5);
            $e_tend = substr($event['end'], 11, 5);
            foreach ($this->_simultaneous[$group] as $index=>$inter) {
                if ($e_tstart == $inter['time']) {
                    $inter['start'] += 1;
                    $this->_simultaneous[$group][$index] = $inter;
                    continue;
                }
                if ($e_tstart < $inter['time'] and $e_tend > $inter['time']) {
                    $inter['continued'] += 1;
                    $this->_simultaneous[$group][$index] = $inter;
                    continue;
                }
            }
        }
        return $this->_simultaneous;
    }

    /**
     * Get maximum simultaneous events
     */
    function getMaxSimultaneous()
    {
        if (count($this->_max_simultaneous) > 0) {
            return $this->_max_simultaneous;
        }
        foreach ($this->getGroups() as $group) {
            $this->_max_simultaneous[$group] = 0;
        }
        foreach ($this->_simultaneous as $group=>$choices) {
            foreach ($choices as $count) {
                if ($this->_max_simultaneous[$group] < $count['start'] + $count['continued']) {
                    $this->_max_simultaneous[$group] = $count['start'] + $count['continued'];
                }
            }
        }
        return $this->_max_simultaneous;
    }


    /**
     * Overloading of the get method.
     *
     * @param string Property to get
     */
    function __get($prop)
    {
        if ($prop == 'render') return $this->render();
        return $this->$prop;
    }

}

Archive Download this file

Branches

Number of commits:
Page rendered in 0.05924s using 11 queries.