kopia lustrzana https://github.com/friendica/SG-iCalendar
Merged tpruvot's fork PHP-iCal (http://github.com/tpruvot/PHP-iCal). Great work tpruvot!
commit
c4042c1628
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
/sgical.php
|
|
@ -0,0 +1,54 @@
|
||||||
|
A simple and fast iCal parser.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
http://github.com/fangel/SG-iCalendar
|
||||||
|
With massive help from http://github.com/tpruvot/PHP-iCal
|
||||||
|
and http://github.com/xonev/SG-iCalendar
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
A simple example :
|
||||||
|
$ical = new SG_iCalReader( "./basic.ics" );
|
||||||
|
//or
|
||||||
|
$ical = new SG_iCalReader( "http://example.com/calendar.ics" );
|
||||||
|
foreach( $ical->getEvents() As $event ) {
|
||||||
|
// Do stuff with the event $event
|
||||||
|
}
|
||||||
|
|
||||||
|
To check unit tests with phpunit, goto tests/ directory and :
|
||||||
|
phpunit AllTests
|
||||||
|
phpunit helpers/FreqTest
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
CHANGELOG :
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
current (31 oct 2010)
|
||||||
|
+ ical RDATE support (added dates in a range)
|
||||||
|
+ RDATE and EXDATE arrays support
|
||||||
|
|
||||||
|
0.7.0 (30 oct 2010)
|
||||||
|
+ ical EXDATE support (excluded dates in a range)
|
||||||
|
+ $event->isWholeDay()
|
||||||
|
+ getAllOccurrences() for repeated events
|
||||||
|
+ implemented a cache for repeated events
|
||||||
|
|
||||||
|
0.6.0 (29 oct 2010)
|
||||||
|
+ Added demo based on fullcalendar
|
||||||
|
+ Added duration unit tests
|
||||||
|
+ Support of Recurrent events in query Between()
|
||||||
|
* various fixes on actual (5) issues
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
TODO :
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
These iCal keywords are not supported for the moment :
|
||||||
|
- RECURRENCE-ID : to move one event from a recurrence
|
||||||
|
- EXRULE : to exclude multiple days by a complex rule
|
||||||
|
|
||||||
|
Also, multiple RRULE could be specified for an event,
|
||||||
|
but that is not the case for most calendar applications
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
To get more information about ical format and rules :
|
||||||
|
see http://www.ietf.org/rfc/rfc2445.txt
|
27
SG_iCal.php
27
SG_iCal.php
|
@ -1,9 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
define('SG_ICALREADER_VERSION', '0.5');
|
define('SG_ICALREADER_VERSION', '0.7.0');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple iCal parser. Should take care of most stuff for ya
|
* A simple iCal parser. Should take care of most stuff for ya
|
||||||
|
* http://github.com/fangel/SG-iCalendar
|
||||||
*
|
*
|
||||||
* Roadmap:
|
* Roadmap:
|
||||||
* * Finish FREQUENCY-parsing.
|
* * Finish FREQUENCY-parsing.
|
||||||
|
@ -11,7 +12,7 @@ define('SG_ICALREADER_VERSION', '0.5');
|
||||||
*
|
*
|
||||||
* A simple example:
|
* A simple example:
|
||||||
* <?php
|
* <?php
|
||||||
* $ical = new SG_iCal("http://example.com/calendar.ics");
|
* $ical = new SG_iCalReader("http://example.com/calendar.ics");
|
||||||
* foreach( $ical->getEvents() As $event ) {
|
* foreach( $ical->getEvents() As $event ) {
|
||||||
* // Do stuff with the event $event
|
* // Do stuff with the event $event
|
||||||
* }
|
* }
|
||||||
|
@ -19,26 +20,32 @@ define('SG_ICALREADER_VERSION', '0.5');
|
||||||
*
|
*
|
||||||
* @package SG_iCalReader
|
* @package SG_iCalReader
|
||||||
* @author Morten Fangel (C) 2008
|
* @author Morten Fangel (C) 2008
|
||||||
|
* @author xonev (C) 2010
|
||||||
|
* @author Tanguy Pruvot (C) 2010
|
||||||
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
||||||
*/
|
*/
|
||||||
class SG_iCal {
|
class SG_iCal {
|
||||||
private $information;
|
|
||||||
private $events;
|
//objects
|
||||||
private $timezones;
|
public $information; //SG_iCal_VCalendar
|
||||||
|
public $timezones; //SG_iCal_VTimeZone
|
||||||
|
|
||||||
|
protected $events; //SG_iCal_VEvent[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new iCalReader. You can supply the url now, or later using setUrl
|
* Constructs a new iCalReader. You can supply the url now, or later using setUrl
|
||||||
* @param $url string
|
* @param $url string
|
||||||
*/
|
*/
|
||||||
public function __construct($url = false) {
|
public function __construct($url = false) {
|
||||||
require_once dirname(__FILE__) . '/helpers/SG_iCal_Factory.php'; // BUILD: Remove line
|
|
||||||
require_once dirname(__FILE__) . '/helpers/SG_iCal_Line.php'; // BUILD: Remove line
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Line.php'; // BUILD: Remove line
|
||||||
require_once dirname(__FILE__) . '/helpers/SG_iCal_Query.php'; // BUILD: Remove line
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Duration.php'; // BUILD: Remove line
|
||||||
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Freq.php'; // BUILD: Remove line
|
||||||
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Recurrence.php'; // BUILD: Remove line
|
||||||
require_once dirname(__FILE__) . '/helpers/SG_iCal_Parser.php'; // BUILD: Remove line
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Parser.php'; // BUILD: Remove line
|
||||||
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Query.php'; // BUILD: Remove line
|
||||||
|
require_once dirname(__FILE__) . '/helpers/SG_iCal_Factory.php'; // BUILD: Remove line
|
||||||
|
|
||||||
if( $url !== false ) {
|
$this->setUrl($url);
|
||||||
SG_iCal_Parser::Parse($url, $this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
||||||
*/
|
*/
|
||||||
class SG_iCal_VCalendar implements IteratorAggregate {
|
class SG_iCal_VCalendar implements IteratorAggregate {
|
||||||
private $data;
|
protected $data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new SG_iCal_VCalendar.
|
* Creates a new SG_iCal_VCalendar.
|
||||||
|
|
|
@ -15,14 +15,27 @@
|
||||||
*/
|
*/
|
||||||
class SG_iCal_VEvent {
|
class SG_iCal_VEvent {
|
||||||
const DEFAULT_CONFIRMED = true;
|
const DEFAULT_CONFIRMED = true;
|
||||||
private $uid;
|
|
||||||
private $start;
|
protected $uid;
|
||||||
private $end;
|
|
||||||
private $recurrence;
|
protected $start;
|
||||||
private $summary;
|
protected $end;
|
||||||
private $description;
|
|
||||||
private $location;
|
protected $summary;
|
||||||
private $data;
|
protected $description;
|
||||||
|
protected $location;
|
||||||
|
|
||||||
|
protected $laststart;
|
||||||
|
protected $lastend;
|
||||||
|
|
||||||
|
public $recurrence; //RRULE
|
||||||
|
public $recurex; //EXRULE
|
||||||
|
public $excluded; //EXDATE(s)
|
||||||
|
public $added; //RDATE(s)
|
||||||
|
|
||||||
|
public $freq; //getFrequency() SG_iCal_Freq
|
||||||
|
|
||||||
|
public $data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new SG_iCal_VEvent. Needs the SG_iCalReader
|
* Constructs a new SG_iCal_VEvent. Needs the SG_iCalReader
|
||||||
|
@ -30,7 +43,8 @@ class SG_iCal_VEvent {
|
||||||
* @param SG_iCal_Line[] $data
|
* @param SG_iCal_Line[] $data
|
||||||
* @param SG_iCalReader $ical
|
* @param SG_iCalReader $ical
|
||||||
*/
|
*/
|
||||||
public function __construct($data, SG_iCal $ical ) {
|
public function __construct($data, SG_iCal $ical) {
|
||||||
|
|
||||||
$this->uid = $data['uid']->getData();
|
$this->uid = $data['uid']->getData();
|
||||||
unset($data['uid']);
|
unset($data['uid']);
|
||||||
|
|
||||||
|
@ -39,8 +53,13 @@ class SG_iCal_VEvent {
|
||||||
unset($data['rrule']);
|
unset($data['rrule']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( isset($data['exrule']) ) {
|
||||||
|
$this->recurex = new SG_iCal_Recurrence($data['exrule']);
|
||||||
|
unset($data['exrule']);
|
||||||
|
}
|
||||||
|
|
||||||
if( isset($data['dtstart']) ) {
|
if( isset($data['dtstart']) ) {
|
||||||
$this->start = $this->getTimestamp( $data['dtstart'], $ical );
|
$this->start = $this->getTimestamp($data['dtstart'], $ical);
|
||||||
unset($data['dtstart']);
|
unset($data['dtstart']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,23 +67,51 @@ class SG_iCal_VEvent {
|
||||||
$this->end = $this->getTimestamp($data['dtend'], $ical);
|
$this->end = $this->getTimestamp($data['dtend'], $ical);
|
||||||
unset($data['dtend']);
|
unset($data['dtend']);
|
||||||
} elseif( isset($data['duration']) ) {
|
} elseif( isset($data['duration']) ) {
|
||||||
require_once dirname(__FILE__).'/../helpers/SG_iCal_Duration.php'; // BUILD: Remove line
|
|
||||||
$dur = new SG_iCal_Duration( $data['duration']->getData() );
|
$dur = new SG_iCal_Duration( $data['duration']->getData() );
|
||||||
$this->end = $this->start + $dur->getDuration();
|
$this->end = $this->start + $dur->getDuration();
|
||||||
unset($data['duration']);
|
unset($data['duration']);
|
||||||
} elseif ( isset($this->recurrence) ) {
|
}
|
||||||
|
|
||||||
|
//google cal set dtend as end of initial event (duration)
|
||||||
|
if ( isset($this->recurrence) ) {
|
||||||
//if there is a recurrence rule
|
//if there is a recurrence rule
|
||||||
|
|
||||||
|
//exclusions
|
||||||
|
if ( isset($data['exdate']) ) {
|
||||||
|
foreach ($data['exdate'] as $exdate) {
|
||||||
|
foreach ($exdate->getDataAsArray() as $ts) {
|
||||||
|
$this->excluded[] = strtotime($ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($data['exdate']);
|
||||||
|
}
|
||||||
|
//additions
|
||||||
|
if ( isset($data['rdate']) ) {
|
||||||
|
foreach ($data['rdate'] as $rdate) {
|
||||||
|
foreach ($rdate->getDataAsArray() as $ts) {
|
||||||
|
$this->added[] = strtotime($ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($data['rdate']);
|
||||||
|
}
|
||||||
|
|
||||||
$until = $this->recurrence->getUntil();
|
$until = $this->recurrence->getUntil();
|
||||||
$count = $this->recurrence->getCount();
|
$count = $this->recurrence->getCount();
|
||||||
//check if there is either 'until' or 'count' set
|
//check if there is either 'until' or 'count' set
|
||||||
if ( $this->recurrence->getUntil() or $this->recurrence->getCount() ) {
|
|
||||||
//if until is set, set that as the end date (using getTimeStamp)
|
|
||||||
if ( $until ) {
|
if ( $until ) {
|
||||||
$this->end = strtotime( $until );
|
//ok..
|
||||||
}
|
} elseif ($count) {
|
||||||
//if count is set, then figure out the last occurrence and set that as the end date
|
//if count is set, then figure out the last occurrence and set that as the end date
|
||||||
|
$this->getFrequency();
|
||||||
|
$until = $this->freq->lastOccurrence($this->start);
|
||||||
|
} else {
|
||||||
|
//forever... limit to 3 years
|
||||||
|
$this->recurrence->setUntil('+3 years');
|
||||||
|
$until = $this->recurrence->getUntil();
|
||||||
}
|
}
|
||||||
|
//date_default_timezone_set( xx ) needed ?;
|
||||||
|
$this->laststart = strtotime($until);
|
||||||
|
$this->lastend = $this->laststart + $this->getDuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
$imports = array('summary','description','location');
|
$imports = array('summary','description','location');
|
||||||
|
@ -75,9 +122,27 @@ class SG_iCal_VEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( isset($this->previous_tz) ) {
|
||||||
|
date_default_timezone_set($this->previous_tz);
|
||||||
|
}
|
||||||
|
|
||||||
$this->data = SG_iCal_Line::Remove_Line($data);
|
$this->data = SG_iCal_Line::Remove_Line($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Event Occurrences Iterator (if recurrence set)
|
||||||
|
* @return SG_iCal_Freq
|
||||||
|
*/
|
||||||
|
public function getFrequency() {
|
||||||
|
if (! isset($this->freq)) {
|
||||||
|
if ( isset($this->recurrence) ) {
|
||||||
|
$this->freq = new SG_iCal_Freq($this->recurrence->rrule, $this->start, $this->excluded, $this->added);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->freq;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the UID of the event
|
* Returns the UID of the event
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -130,6 +195,18 @@ class SG_iCal_VEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if duration is multiple of 86400
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isWholeDay() {
|
||||||
|
$dur = $this->getDuration();
|
||||||
|
if ($dur > 0 && ($dur % 86400) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the timestamp for the beginning of the event
|
* Returns the timestamp for the beginning of the event
|
||||||
* @return int
|
* @return int
|
||||||
|
@ -146,6 +223,14 @@ class SG_iCal_VEvent {
|
||||||
return $this->end;
|
return $this->end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the timestamp for the end of the last event
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getRangeEnd() {
|
||||||
|
return max($this->end,$this->lastend);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the duration of this event in seconds
|
* Returns the duration of this event in seconds
|
||||||
* @return int
|
* @return int
|
||||||
|
@ -169,18 +254,39 @@ class SG_iCal_VEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default timezone (temporary) to get timestamps
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function setLineTimeZone(SG_iCal_Line $line) {
|
||||||
|
if( isset($line['tzid']) ) {
|
||||||
|
if (!isset($this->previous_tz)) {
|
||||||
|
$this->previous_tz = @ date_default_timezone_get();
|
||||||
|
}
|
||||||
|
$this->tzid = $line['tzid'];
|
||||||
|
date_default_timezone_set($this->tzid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the timestamp from a DT line.
|
* Calculates the timestamp from a DT line.
|
||||||
* @param $line SG_iCal_Line
|
* @param $line SG_iCal_Line
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
private function getTimestamp( SG_iCal_Line $line, SG_iCal $ical ) {
|
protected function getTimestamp( SG_iCal_Line $line, SG_iCal $ical ) {
|
||||||
$ts = strtotime($line->getData());
|
|
||||||
if( isset($line['tzid']) ) {
|
if( isset($line['tzid']) ) {
|
||||||
$tz = $ical->getTimeZoneInfo($line['tzid']);
|
$this->setLineTimeZone($line);
|
||||||
$offset = $tz->getOffset($ts);
|
//$tz = $ical->getTimeZoneInfo($line['tzid']);
|
||||||
$ts = strtotime(date('D, d M Y H:i:s', $ts) . ' ' . $offset);
|
//$offset = $tz->getOffset($ts);
|
||||||
|
//$ts = strtotime(date('D, d M Y H:i:s', $ts) . ' ' . $offset);
|
||||||
}
|
}
|
||||||
|
$ts = strtotime($line->getData());
|
||||||
|
|
||||||
return $ts;
|
return $ts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
||||||
*/
|
*/
|
||||||
class SG_iCal_VTimeZone {
|
class SG_iCal_VTimeZone {
|
||||||
private $tzid;
|
protected $tzid;
|
||||||
private $daylight;
|
protected $daylight;
|
||||||
private $standard;
|
protected $standard;
|
||||||
private $cache = array();
|
protected $cache = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new SG_iCal_VTimeZone
|
* Constructs a new SG_iCal_VTimeZone
|
||||||
|
@ -65,6 +65,16 @@ class SG_iCal_VTimeZone {
|
||||||
* @return string standard|daylight
|
* @return string standard|daylight
|
||||||
*/
|
*/
|
||||||
private function getActive( $ts ) {
|
private function getActive( $ts ) {
|
||||||
|
|
||||||
|
if (class_exists('DateTimeZone')) {
|
||||||
|
|
||||||
|
//PHP >= 5.2
|
||||||
|
$tz = new DateTimeZone( $this->tzid );
|
||||||
|
$date = new DateTime("@$ts", $tz);
|
||||||
|
return ($date->format('I') == 1) ? 'daylight' : 'standard';
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
if( isset($this->cache[$ts]) ) {
|
if( isset($this->cache[$ts]) ) {
|
||||||
return $this->cache[$ts];
|
return $this->cache[$ts];
|
||||||
}
|
}
|
||||||
|
@ -81,4 +91,5 @@ class SG_iCal_VTimeZone {
|
||||||
|
|
||||||
return $this->cache[$ts];
|
return $this->cache[$ts];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
@SET OUTPUT=.\sgical.php
|
||||||
|
|
||||||
|
cat SG_iCal.php | grep -v "BUILD: Remove line" > %OUTPUT%
|
||||||
|
DIR /B /S helpers | grep SG_iCal | grep -v svn | sed -e "s/\\/\//g" | xargs cat | grep -v "BUILD: Remove line" >> %OUTPUT%
|
||||||
|
DIR /B /S blocks | grep SG_iCal | grep -v svn | sed -e "s/\\/\//g" | xargs cat | grep -v "BUILD: Remove line" >> %OUTPUT%
|
|
@ -0,0 +1,107 @@
|
||||||
|
BEGIN:VCALENDAR
|
||||||
|
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||||
|
VERSION:2.0
|
||||||
|
CALSCALE:GREGORIAN
|
||||||
|
METHOD:PUBLISH
|
||||||
|
X-WR-CALNAME:Particuliers - Grand Tour du Bassin
|
||||||
|
X-WR-TIMEZONE:Europe/Paris
|
||||||
|
X-WR-CALDESC:
|
||||||
|
BEGIN:VTIMEZONE
|
||||||
|
TZID:Europe/Paris
|
||||||
|
X-LIC-LOCATION:Europe/Paris
|
||||||
|
BEGIN:DAYLIGHT
|
||||||
|
TZOFFSETFROM:+0100
|
||||||
|
TZOFFSETTO:+0200
|
||||||
|
TZNAME:CEST
|
||||||
|
DTSTART:19700329T020000
|
||||||
|
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||||
|
END:DAYLIGHT
|
||||||
|
BEGIN:STANDARD
|
||||||
|
TZOFFSETFROM:+0200
|
||||||
|
TZOFFSETTO:+0100
|
||||||
|
TZNAME:CET
|
||||||
|
DTSTART:19701025T030000
|
||||||
|
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||||
|
END:STANDARD
|
||||||
|
END:VTIMEZONE
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTART;TZID=Europe/Paris:20100526T143000
|
||||||
|
DTEND;TZID=Europe/Paris:20100526T171500
|
||||||
|
DTSTAMP:20101028T215738Z
|
||||||
|
UID:ptb5jqomu2vu0sr2nm24qungag@google.com
|
||||||
|
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Partic
|
||||||
|
uliers - Grand Tour du Bassin;X-NUM-GUESTS=0:mailto:l2n7ajiud0oaiua4qcdarg1
|
||||||
|
krg@group.calendar.google.com
|
||||||
|
RECURRENCE-ID;TZID=Europe/Paris:20100526T143000
|
||||||
|
CREATED:20100428T225030Z
|
||||||
|
DESCRIPTION:
|
||||||
|
LAST-MODIFIED:20100527T102538Z
|
||||||
|
LOCATION:Jetée Thiers\, Arcachon
|
||||||
|
SEQUENCE:0
|
||||||
|
STATUS:CONFIRMED
|
||||||
|
SUMMARY:Horaire
|
||||||
|
TRANSP:TRANSPARENT
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTART;TZID=Europe/Paris:20100403T143000
|
||||||
|
DTEND;TZID=Europe/Paris:20100403T171500
|
||||||
|
RRULE:FREQ=WEEKLY;WKST=MO;UNTIL=20100630T123000Z;BYDAY=SU,WE,SA
|
||||||
|
DTSTAMP:20101028T215738Z
|
||||||
|
UID:ptb5jqomu2vu0sr2nm24qungag@google.com
|
||||||
|
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Partic
|
||||||
|
uliers - Grand Tour du Bassin;X-NUM-GUESTS=0:mailto:l2n7ajiud0oaiua4qcdarg1
|
||||||
|
krg@group.calendar.google.com
|
||||||
|
CREATED:20100428T225030Z
|
||||||
|
DESCRIPTION:
|
||||||
|
LAST-MODIFIED:20100518T052328Z
|
||||||
|
LOCATION:Jetée Thiers\, Arcachon
|
||||||
|
SEQUENCE:0
|
||||||
|
STATUS:CONFIRMED
|
||||||
|
SUMMARY:Horaire
|
||||||
|
TRANSP:TRANSPARENT
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTART;TZID=Europe/Paris:20101002T143000
|
||||||
|
DTEND;TZID=Europe/Paris:20101002T171500
|
||||||
|
RRULE:FREQ=WEEKLY;BYDAY=SU,SA;WKST=MO
|
||||||
|
DTSTAMP:20101028T215738Z
|
||||||
|
UID:41v060qrjvpvsv90n6ulljjeg4@google.com
|
||||||
|
CREATED:20100428T225427Z
|
||||||
|
DESCRIPTION:
|
||||||
|
LAST-MODIFIED:20100518T052328Z
|
||||||
|
LOCATION:Jetée Thiers\, Arcachon
|
||||||
|
SEQUENCE:0
|
||||||
|
STATUS:CONFIRMED
|
||||||
|
SUMMARY:Horaire
|
||||||
|
TRANSP:TRANSPARENT
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTART;TZID=Europe/Paris:20100703T143000
|
||||||
|
DTEND;TZID=Europe/Paris:20100703T171500
|
||||||
|
RRULE:FREQ=DAILY;UNTIL=20100930T123000Z;WKST=MO
|
||||||
|
DTSTAMP:20101028T215738Z
|
||||||
|
UID:92tmcm95ktr40ofn3okf0hhr2c@google.com
|
||||||
|
CREATED:20100428T225334Z
|
||||||
|
DESCRIPTION:
|
||||||
|
LAST-MODIFIED:20100518T052327Z
|
||||||
|
LOCATION:Jetée Thiers\, Arcachon
|
||||||
|
SEQUENCE:0
|
||||||
|
STATUS:CONFIRMED
|
||||||
|
SUMMARY:Horaire
|
||||||
|
TRANSP:TRANSPARENT
|
||||||
|
END:VEVENT
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTART:20100513T123000Z
|
||||||
|
DTEND:20100513T151500Z
|
||||||
|
DTSTAMP:20101028T215738Z
|
||||||
|
UID:254jd6v58v92k7bj69sgu3ulb0@google.com
|
||||||
|
CREATED:20100428T225221Z
|
||||||
|
DESCRIPTION:
|
||||||
|
LAST-MODIFIED:20100518T052327Z
|
||||||
|
LOCATION:Jetée Thiers\, Arcachon
|
||||||
|
SEQUENCE:0
|
||||||
|
STATUS:CONFIRMED
|
||||||
|
SUMMARY:Horaire
|
||||||
|
TRANSP:TRANSPARENT
|
||||||
|
END:VEVENT
|
||||||
|
END:VCALENDAR
|
|
@ -0,0 +1,43 @@
|
||||||
|
BEGIN:VCALENDAR
|
||||||
|
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||||
|
VERSION:2.0
|
||||||
|
CALSCALE:GREGORIAN
|
||||||
|
METHOD:PUBLISH
|
||||||
|
X-WR-CALNAME:uba.gestion@gmail.com
|
||||||
|
X-WR-TIMEZONE:Europe/Paris
|
||||||
|
BEGIN:VTIMEZONE
|
||||||
|
TZID:Europe/Paris
|
||||||
|
X-LIC-LOCATION:Europe/Paris
|
||||||
|
BEGIN:DAYLIGHT
|
||||||
|
TZOFFSETFROM:+0100
|
||||||
|
TZOFFSETTO:+0200
|
||||||
|
TZNAME:CEST
|
||||||
|
DTSTART:19700329T020000
|
||||||
|
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
||||||
|
END:DAYLIGHT
|
||||||
|
BEGIN:STANDARD
|
||||||
|
TZOFFSETFROM:+0200
|
||||||
|
TZOFFSETTO:+0100
|
||||||
|
TZNAME:CET
|
||||||
|
DTSTART:19701025T030000
|
||||||
|
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
||||||
|
END:STANDARD
|
||||||
|
END:VTIMEZONE
|
||||||
|
BEGIN:VEVENT
|
||||||
|
DTSTART;VALUE=DATE:20100907
|
||||||
|
DTEND;VALUE=DATE:20100908
|
||||||
|
RRULE:FREQ=DAILY;UNTIL=20100912
|
||||||
|
EXDATE;VALUE=DATE:20100911
|
||||||
|
EXDATE;VALUE=DATE:20100909,20100910
|
||||||
|
DTSTAMP:20101031T155459Z
|
||||||
|
UID:5oo2ridecth26kcavj8elhtd4s@google.com
|
||||||
|
CREATED:00001231T000000Z
|
||||||
|
DESCRIPTION:
|
||||||
|
LAST-MODIFIED:20101030T193954Z
|
||||||
|
LOCATION:
|
||||||
|
SEQUENCE:1
|
||||||
|
STATUS:CONFIRMED
|
||||||
|
SUMMARY:occur
|
||||||
|
TRANSP:TRANSPARENT
|
||||||
|
END:VEVENT
|
||||||
|
END:VCALENDAR
|
|
@ -0,0 +1,615 @@
|
||||||
|
/*
|
||||||
|
* FullCalendar v1.4.8-IE9 Stylesheet
|
||||||
|
*
|
||||||
|
* Feel free to edit this file to customize the look of FullCalendar.
|
||||||
|
* When upgrading to newer versions, please upgrade this file as well,
|
||||||
|
* porting over any customizations afterwards.
|
||||||
|
*
|
||||||
|
* Date: Mon Oct 25 02:35:06 2010 +0200
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: make font sizes look the same in all doctypes */
|
||||||
|
|
||||||
|
|
||||||
|
.fc,
|
||||||
|
.fc .fc-header,
|
||||||
|
.fc .fc-content {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc {
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc td, .fc th {
|
||||||
|
padding: 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Header
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
table.fc-header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-left {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-left table {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-center {
|
||||||
|
width: 50%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-center table {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-right {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-right table {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-title {
|
||||||
|
margin-top: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header-space {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* right-to-left */
|
||||||
|
|
||||||
|
.fc-rtl .fc-header-title {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Buttons
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc-header .fc-state-default,
|
||||||
|
.fc-header .ui-state-default {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-default {
|
||||||
|
border-width: 1px 0;
|
||||||
|
padding: 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-default,
|
||||||
|
.fc-header .fc-state-default a {
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-default a {
|
||||||
|
display: block;
|
||||||
|
border-width: 0 1px;
|
||||||
|
margin: 0 -1px;
|
||||||
|
width: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-default span {
|
||||||
|
display: block;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px 0 1px 1px;
|
||||||
|
padding: 3px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .ui-state-default {
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-default span,
|
||||||
|
.fc-header .ui-state-default span {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for adjacent buttons */
|
||||||
|
|
||||||
|
.fc-header .fc-no-right {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-no-right a {
|
||||||
|
margin-right: 0;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .ui-no-right {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for fake rounded corners */
|
||||||
|
|
||||||
|
.fc-header .fc-corner-left {
|
||||||
|
margin-left: 1px;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-corner-right {
|
||||||
|
margin-right: 1px;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEFAULT button COLORS */
|
||||||
|
|
||||||
|
.fc-header .fc-state-default,
|
||||||
|
.fc-header .fc-state-default a {
|
||||||
|
border-color: #777; /* outer border */
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-default span {
|
||||||
|
border-color: #fff #fff #d1d1d1; /* inner border */
|
||||||
|
background: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PRESSED button COLORS (down and active) */
|
||||||
|
|
||||||
|
.fc-header .fc-state-active a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-down span,
|
||||||
|
.fc-header .fc-state-active span {
|
||||||
|
background: #888;
|
||||||
|
border-color: #808080 #808080 #909090; /* inner border */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DISABLED button COLORS */
|
||||||
|
|
||||||
|
.fc-header .fc-state-disabled a {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-disabled,
|
||||||
|
.fc-header .fc-state-disabled a {
|
||||||
|
border-color: #ccc; /* outer border */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-header .fc-state-disabled span {
|
||||||
|
border-color: #fff #fff #f0f0f0; /* inner border */
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Content Area & Global Cell Styles
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc-widget-content {
|
||||||
|
border: 1px solid #ccc; /* outer border color */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-state-default {
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #ccc; /* inner border color */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .ui-state-highlight,
|
||||||
|
.fc-content .fc-state-highlight { /* today */
|
||||||
|
background: #ffd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-not-today { /* override jq-ui highlight (TODO: ui-widget-content) */
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-before-today {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-cell-overlay { /* semi-transparent rectangle while dragging */
|
||||||
|
background: #9cf;
|
||||||
|
opacity: .2;
|
||||||
|
filter: alpha(opacity=20); /* for IE */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-view { /* prevents dragging outside of widget */
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Global Event Styles
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc-event,
|
||||||
|
.fc-agenda .fc-event-time,
|
||||||
|
.fc-event a {
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #36c; /* default BORDER color (probably the same as background-color) */
|
||||||
|
background-color: #36c; /* default BACKGROUND color */
|
||||||
|
color: #fff; /* default TEXT color */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use the 'className' CalEvent property and the following
|
||||||
|
* example CSS to change event color on a per-event basis:
|
||||||
|
*
|
||||||
|
* .myclass,
|
||||||
|
* .fc-agenda .myclass .fc-event-time,
|
||||||
|
* .myclass a {
|
||||||
|
* background-color: black;
|
||||||
|
* border-color: black;
|
||||||
|
* color: red;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
.fc-event {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event a {
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: .85em;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-editable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-time,
|
||||||
|
.fc-event-title {
|
||||||
|
padding: 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for fake rounded corners */
|
||||||
|
|
||||||
|
.fc-event a {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* right-to-left */
|
||||||
|
|
||||||
|
.fc-rtl .fc-event a {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resizable */
|
||||||
|
|
||||||
|
.fc .ui-resizable-handle {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 99999;
|
||||||
|
border: 0 !important; /* important overrides pre jquery ui 1.7 styles */
|
||||||
|
background: url(data:image/gif;base64,AAAA) !important; /* hover fix for IE */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Horizontal Events
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc-event-hori {
|
||||||
|
border-width: 1px 0;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-hori a {
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for fake rounded corners */
|
||||||
|
|
||||||
|
.fc-content .fc-corner-left {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-corner-left a {
|
||||||
|
margin-left: -1px;
|
||||||
|
border-left-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-corner-right {
|
||||||
|
margin-right: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-corner-right a {
|
||||||
|
margin-right: -1px;
|
||||||
|
border-right-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resizable */
|
||||||
|
|
||||||
|
.fc-event-hori .ui-resizable-e {
|
||||||
|
top: 0 !important; /* importants override pre jquery ui 1.7 styles */
|
||||||
|
right: -3px !important;
|
||||||
|
width: 7px !important;
|
||||||
|
height: 100% !important;
|
||||||
|
cursor: e-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-hori .ui-resizable-w {
|
||||||
|
top: 0 !important;
|
||||||
|
left: -3px !important;
|
||||||
|
width: 7px !important;
|
||||||
|
height: 100% !important;
|
||||||
|
cursor: w-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-hori .ui-resizable-handle {
|
||||||
|
_padding-bottom: 14px; /* IE6 had 0 height */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Month View, Basic Week View, Basic Day View
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc-grid table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-grid th {
|
||||||
|
border-width: 0 0 0 1px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-grid td {
|
||||||
|
border-width: 1px 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-grid th.fc-leftmost,
|
||||||
|
.fc-grid td.fc-leftmost {
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-grid .fc-day-number {
|
||||||
|
float: right;
|
||||||
|
padding: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-grid .fc-other-month .fc-day-number {
|
||||||
|
opacity: 0.3;
|
||||||
|
filter: alpha(opacity=30); /* for IE */
|
||||||
|
/* opacity with small font can sometimes look too faded
|
||||||
|
might want to set the 'color' property instead
|
||||||
|
making day-numbers bold also fixes the problem */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-grid .fc-day-content {
|
||||||
|
clear: both;
|
||||||
|
padding: 2px 2px 0; /* distance between events and day edges */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* event styles */
|
||||||
|
|
||||||
|
.fc-grid .fc-event-time {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* right-to-left */
|
||||||
|
|
||||||
|
.fc-rtl .fc-grid {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-rtl .fc-grid .fc-day-number {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-rtl .fc-grid .fc-event-time {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* week numbers */
|
||||||
|
|
||||||
|
.fc .fc-grid th.fc-weeknumber {
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
/* Agenda Week View, Agenda Day View
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc .fc-agenda th,
|
||||||
|
.fc .fc-agenda td {
|
||||||
|
border-width: 1px 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-agenda .fc-leftmost {
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-agenda tr.fc-first th,
|
||||||
|
.fc-agenda tr.fc-first td {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-agenda-head tr.fc-last th {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-agenda-head td,
|
||||||
|
.fc .fc-agenda-body td {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-agenda-head th {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the time axis running down the left side */
|
||||||
|
|
||||||
|
.fc-agenda .fc-axis {
|
||||||
|
width: 50px;
|
||||||
|
padding: 0 4px;
|
||||||
|
vertical-align: middle;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: right;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all-day event cells at top */
|
||||||
|
|
||||||
|
.fc-agenda-head tr.fc-all-day th {
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-agenda-head td {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-divider div {
|
||||||
|
font-size: 1px; /* for IE6/7 */
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-divider .fc-state-default {
|
||||||
|
background: #eee; /* color for divider between all-day and time-slot events */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* body styles */
|
||||||
|
|
||||||
|
.fc .fc-agenda-body td div {
|
||||||
|
height: 20px; /* slot height */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-agenda-body tr.fc-minor th,
|
||||||
|
.fc .fc-agenda-body tr.fc-minor td {
|
||||||
|
border-top-style: dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-agenda .fc-day-content {
|
||||||
|
padding: 2px 2px 0; /* distance between events and day edges */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vertical background columns */
|
||||||
|
|
||||||
|
.fc .fc-agenda-bg .ui-state-highlight {
|
||||||
|
background-image: none; /* tall column, don't want repeating background image */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Vertical Events
|
||||||
|
------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
.fc-event-vert {
|
||||||
|
border-width: 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-vert a {
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for fake rounded corners */
|
||||||
|
|
||||||
|
.fc-content .fc-corner-top {
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-corner-top a {
|
||||||
|
margin-top: -1px;
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-corner-bottom {
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-content .fc-corner-bottom a {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* event content */
|
||||||
|
|
||||||
|
.fc-event-vert span {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-vert span.fc-event-time {
|
||||||
|
white-space: nowrap;
|
||||||
|
_white-space: normal;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-vert span.fc-event-title {
|
||||||
|
line-height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-event-vert span.fc-event-bg { /* makes the event lighter w/ a semi-transparent overlay */
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
opacity: .3;
|
||||||
|
filter: alpha(opacity=30); /* for IE */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resizable */
|
||||||
|
|
||||||
|
.fc-event-vert .ui-resizable-s {
|
||||||
|
bottom: 0 !important; /* importants override pre jquery ui 1.7 styles */
|
||||||
|
width: 100% !important;
|
||||||
|
height: 8px !important;
|
||||||
|
line-height: 8px !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-family: monospace;
|
||||||
|
text-align: center;
|
||||||
|
cursor: s-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* JOMRES */
|
||||||
|
.gcal-blackbooking-0,
|
||||||
|
.fc-agenda .gcal-blackbooking-0 .fc-event-time,
|
||||||
|
.gcal-blackbooking-0 a {
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #400; /* default BORDER color (probably the same as background-color) */
|
||||||
|
background-color: #D60; /* default BACKGROUND color */
|
||||||
|
color: #fff; /* default TEXT color */
|
||||||
|
}
|
||||||
|
|
||||||
|
.gcal-blackbooking-1,
|
||||||
|
.fc-agenda .gcal-blackbooking-1 .fc-event-time,
|
||||||
|
.gcal-blackbooking-1 a {
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #400; /* default BORDER color (probably the same as background-color) */
|
||||||
|
background-color: #800; /* default BACKGROUND color */
|
||||||
|
color: #fff; /* default TEXT color */
|
||||||
|
}
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once('../SG_iCal.php');
|
||||||
|
|
||||||
|
function dump_t($x) {
|
||||||
|
echo "<pre>".print_r($x,true)."</pre>";
|
||||||
|
}
|
||||||
|
$ICS = "exdate.ics";
|
||||||
|
//echo dump_t(file_get_contents($ICS));
|
||||||
|
|
||||||
|
$ical = new SG_iCalReader($ICS);
|
||||||
|
$query = new SG_iCal_Query();
|
||||||
|
|
||||||
|
$evts = $ical->getEvents();
|
||||||
|
//$evts = $query->Between($ical,strtotime('20100901'),strtotime('20101131'));
|
||||||
|
|
||||||
|
|
||||||
|
$data = array();
|
||||||
|
foreach($evts as $id => $ev) {
|
||||||
|
$jsEvt = array(
|
||||||
|
"id" => ($id+1),
|
||||||
|
"title" => $ev->getProperty('summary'),
|
||||||
|
"start" => $ev->getStart(),
|
||||||
|
"end" => $ev->getEnd()-1,
|
||||||
|
"allDay" => $ev->isWholeDay()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($ev->recurrence)) {
|
||||||
|
$count = 0;
|
||||||
|
$start = $ev->getStart();
|
||||||
|
$freq = $ev->getFrequency();
|
||||||
|
if ($freq->firstOccurrence() == $start)
|
||||||
|
$data[] = $jsEvt;
|
||||||
|
while (($next = $freq->nextOccurrence($start)) > 0 ) {
|
||||||
|
if (!$next or $count >= 1000) break;
|
||||||
|
$count++;
|
||||||
|
$start = $next;
|
||||||
|
$jsEvt["start"] = $start;
|
||||||
|
$jsEvt["end"] = $start + $ev->getDuration()-1;
|
||||||
|
|
||||||
|
$data[] = $jsEvt;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
$data[] = $jsEvt;
|
||||||
|
|
||||||
|
}
|
||||||
|
//echo(date('Ymd\n',$data[0][start]));
|
||||||
|
//echo(date('Ymd\n',$data[1][start]));
|
||||||
|
//dump_t($data);
|
||||||
|
|
||||||
|
$events = "events:".json_encode($data).',';
|
||||||
|
|
||||||
|
?>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>Fullcalendar iCal Loader</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="fullcalendar.css">
|
||||||
|
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="fullcalendar.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
$('#calendar').fullCalendar({
|
||||||
|
header: {
|
||||||
|
left: 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right: 'month,agendaWeek,agendaDay'
|
||||||
|
},
|
||||||
|
|
||||||
|
year: 2010,
|
||||||
|
month: 9-1,
|
||||||
|
|
||||||
|
// US Holidays
|
||||||
|
//events: $.fullCalendar.gcalFeed('http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic'),
|
||||||
|
|
||||||
|
<?=$events ?>
|
||||||
|
|
||||||
|
eventClick: function(event) {
|
||||||
|
// opens events in a popup window
|
||||||
|
window.open(event.url, 'gcalevent', 'width=700,height=600');
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
loading: function(bool) {
|
||||||
|
if (bool) {
|
||||||
|
$('#loading').show();
|
||||||
|
}else{
|
||||||
|
$('#loading').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style type='text/css'>
|
||||||
|
body div {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
|
||||||
|
}
|
||||||
|
div#loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
div#calendar {
|
||||||
|
width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="loading" style="display:none;">loading...</div>
|
||||||
|
<div id="calendar"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -9,7 +9,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SG_iCal_Duration {
|
class SG_iCal_Duration {
|
||||||
private $dur;
|
protected $dur;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new SG_iCal_Duration from a duration-rule.
|
* Constructs a new SG_iCal_Duration from a duration-rule.
|
||||||
|
@ -20,28 +20,30 @@ class SG_iCal_Duration {
|
||||||
* @param $duration string
|
* @param $duration string
|
||||||
*/
|
*/
|
||||||
public function __construct( $duration ) {
|
public function __construct( $duration ) {
|
||||||
if( $duration{0} == 'P' || (($duration{0} == '+' || $duration{0} == '-') && $duration{1} == 'P') ) {
|
|
||||||
preg_match('/P((\d+)W)?((\d+)D)?(T)?((\d+)H)?((\d+)M)?((\d+)S)?/', $duration, $matches);
|
|
||||||
$results = array('weeks'=>(int)$matches[2],
|
|
||||||
'days'=>(int)$matches[4],
|
|
||||||
'hours'=>(int)$matches[7],
|
|
||||||
'minutes'=>(int)$matches[9],
|
|
||||||
'seconds'=>(int)$matches[11]);
|
|
||||||
|
|
||||||
$ts = 0;
|
$ts = 0;
|
||||||
|
|
||||||
|
if (preg_match('/[\\+\\-]{0,1}P((\d+)W)?((\d+)D)?(T)?((\d+)H)?((\d+)M)?((\d+)S)?/', $duration, $matches) === 1) {
|
||||||
|
$results = array(
|
||||||
|
'weeks'=> (int)@ $matches[2],
|
||||||
|
'days'=> (int)@ $matches[4],
|
||||||
|
'hours'=> (int)@ $matches[7],
|
||||||
|
'minutes'=>(int)@ $matches[9],
|
||||||
|
'seconds'=>(int)@ $matches[11]
|
||||||
|
);
|
||||||
|
|
||||||
$ts += $results['seconds'];
|
$ts += $results['seconds'];
|
||||||
$ts += 60 * $results['minutes'];
|
$ts += 60 * $results['minutes'];
|
||||||
$ts += 60 * 60 * $results['hours'];
|
$ts += 60 * 60 * $results['hours'];
|
||||||
$ts += 24 * 60 * 60 * $results['days'];
|
$ts += 24 * 60 * 60 * $results['days'];
|
||||||
$ts += 7 * 24 * 60 * 60 * $results['weeks'];
|
$ts += 7 * 24 * 60 * 60 * $results['weeks'];
|
||||||
|
} else {
|
||||||
|
// Invalid duration!
|
||||||
|
}
|
||||||
|
|
||||||
$dir = ($duration{0} == '-') ? -1 : 1;
|
$dir = ($duration{0} == '-') ? -1 : 1;
|
||||||
|
|
||||||
$this->dur = $dir * $ts;
|
$this->dur = $dir * $ts;
|
||||||
} else {
|
|
||||||
// Invalid duration!
|
|
||||||
$this->dur = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,21 +23,30 @@
|
||||||
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
||||||
*/
|
*/
|
||||||
class SG_iCal_Freq {
|
class SG_iCal_Freq {
|
||||||
private $weekdays = array('MO'=>'monday', 'TU'=>'tuesday', 'WE'=>'wednesday', 'TH'=>'thursday', 'FR'=>'friday', 'SA'=>'saturday', 'SU'=>'sunday');
|
protected $weekdays = array('MO'=>'monday', 'TU'=>'tuesday', 'WE'=>'wednesday', 'TH'=>'thursday', 'FR'=>'friday', 'SA'=>'saturday', 'SU'=>'sunday');
|
||||||
private $knownRules = array('month', 'weekno', 'day', 'monthday', 'yearday', 'hour', 'minute');
|
protected $knownRules = array('month', 'weekno', 'day', 'monthday', 'yearday', 'hour', 'minute'); //others : 'setpos', 'second'
|
||||||
private $simpleMode = true;
|
protected $ruleModifiers = array('wkst');
|
||||||
|
protected $simpleMode = true;
|
||||||
|
|
||||||
private $rules = array('freq'=>'yearly', 'interval'=>1);
|
protected $rules = array('freq'=>'yearly', 'interval'=>1);
|
||||||
private $start = 0;
|
protected $start = 0;
|
||||||
private $freq = '';
|
protected $freq = '';
|
||||||
|
|
||||||
|
protected $excluded; //EXDATE
|
||||||
|
protected $added; //RDATE
|
||||||
|
|
||||||
|
protected $cache; // getAllOccurrences()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new Freqency-rule
|
* Constructs a new Freqency-rule
|
||||||
* @param $rule string
|
* @param $rule string
|
||||||
* @param $start int Unix-timestamp (important!)
|
* @param $start int Unix-timestamp (important : Need to be the start of Event)
|
||||||
|
* @param $excluded array of int (timestamps), see EXDATE documentation
|
||||||
|
* @param $added array of int (timestamps), see RDATE documentation
|
||||||
*/
|
*/
|
||||||
public function __construct( $rule, $start ) {
|
public function __construct( $rule, $start, $excluded=array(), $added=array()) {
|
||||||
$this->start = $start;
|
$this->start = $start;
|
||||||
|
$this->excluded = array();
|
||||||
|
|
||||||
$rules = array();
|
$rules = array();
|
||||||
foreach( explode(';', $rule) AS $v) {
|
foreach( explode(';', $rule) AS $v) {
|
||||||
|
@ -50,7 +59,6 @@ class SG_iCal_Freq {
|
||||||
}
|
}
|
||||||
$this->freq = strtolower($this->rules['freq']);
|
$this->freq = strtolower($this->rules['freq']);
|
||||||
|
|
||||||
|
|
||||||
foreach( $this->knownRules AS $rule ) {
|
foreach( $this->knownRules AS $rule ) {
|
||||||
if( isset($this->rules['by' . $rule]) ) {
|
if( isset($this->rules['by' . $rule]) ) {
|
||||||
if( $this->isPrerule($rule, $this->freq) ) {
|
if( $this->isPrerule($rule, $this->freq) ) {
|
||||||
|
@ -65,14 +73,56 @@ class SG_iCal_Freq {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//set until, and cache
|
||||||
if( isset($this->rules['count']) ) {
|
if( isset($this->rules['count']) ) {
|
||||||
$n = $start;
|
|
||||||
for($i=0;$i<$this->rules['count'];$i++) {
|
$cache[$ts] = $ts = $this->start;
|
||||||
$n = $this->findNext($n);
|
for($n=1; $n < $this->rules['count']; $n++) {
|
||||||
|
$ts = $this->findNext($ts);
|
||||||
|
$cache[$ts] = $ts;
|
||||||
}
|
}
|
||||||
$this->rules['until'] = $n;
|
$this->rules['until'] = $ts;
|
||||||
|
|
||||||
|
//EXDATE
|
||||||
|
if (!empty($excluded)) {
|
||||||
|
foreach($excluded as $ts) {
|
||||||
|
unset($cache[$ts]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//RDATE
|
||||||
|
if (!empty($added)) {
|
||||||
|
$cache = $cache + $added;
|
||||||
|
asort($cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache = array_values($cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->excluded = $excluded;
|
||||||
|
$this->added = $added;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all timestamps array(), build the cache if not made before
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAllOccurrences() {
|
||||||
|
if (empty($this->cache)) {
|
||||||
|
//build cache
|
||||||
|
$next = $this->firstOccurrence();
|
||||||
|
while ($next) {
|
||||||
|
$cache[] = $next;
|
||||||
|
$next = $this->findNext($next);
|
||||||
|
}
|
||||||
|
if (!empty($this->added)) {
|
||||||
|
$cache = $cache + $this->added;
|
||||||
|
asort($cache);
|
||||||
|
}
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
return $this->cache;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the previous (most recent) occurrence of the rule from the
|
* Returns the previous (most recent) occurrence of the rule from the
|
||||||
|
@ -81,14 +131,23 @@ class SG_iCal_Freq {
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function previousOccurrence( $offset ) {
|
public function previousOccurrence( $offset ) {
|
||||||
$t1 = $this->start;
|
if (!empty($this->cache)) {
|
||||||
while( ($t2 = $this->findNext($t1)) < $offset) {
|
$t2=$this->start;
|
||||||
|
foreach($this->cache as $ts) {
|
||||||
|
if ($ts >= $offset)
|
||||||
|
return $t2;
|
||||||
|
$t2 = $ts;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$ts = $this->start;
|
||||||
|
while( ($t2 = $this->findNext($ts)) < $offset) {
|
||||||
if( $t2 == false ){
|
if( $t2 == false ){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$t1 = $t2;
|
$ts = $t2;
|
||||||
}
|
}
|
||||||
return $t1;
|
}
|
||||||
|
return $ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -97,21 +156,32 @@ class SG_iCal_Freq {
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function nextOccurrence( $offset ) {
|
public function nextOccurrence( $offset ) {
|
||||||
return $this->findNext( $this->previousOccurrence( $offset) );
|
if ($offset < $this->start)
|
||||||
|
return $this->firstOccurrence();
|
||||||
|
return $this->findNext($offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first occurrence of the rule.
|
||||||
|
* @return int timestamp
|
||||||
|
*/
|
||||||
|
public function firstOccurrence() {
|
||||||
|
$t = $this->start;
|
||||||
|
if (in_array($t, $this->excluded))
|
||||||
|
$t = $this->findNext($t);
|
||||||
|
return $t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the absolute last occurrence of the rule from the given offset.
|
* Finds the absolute last occurrence of the rule from the given offset.
|
||||||
|
* Builds also the cache, if not set before...
|
||||||
* @return int timestamp
|
* @return int timestamp
|
||||||
*/
|
*/
|
||||||
public function lastOccurrence() {
|
public function lastOccurrence() {
|
||||||
$temp_timestamp = $this->findNext($this->start);
|
//build cache if not done
|
||||||
$timestamp = 0;
|
$this->getAllOccurrences();
|
||||||
while ($temp_timestamp) {
|
//return last timestamp in cache
|
||||||
$timestamp = $temp_timestamp;
|
return end($this->cache);
|
||||||
$temp_timestamp = $this->findNext($temp_timestamp);
|
|
||||||
}
|
|
||||||
return $timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -138,11 +208,18 @@ class SG_iCal_Freq {
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function findNext($offset) {
|
public function findNext($offset) {
|
||||||
$echo = false;
|
if (!empty($this->cache)) {
|
||||||
|
foreach($this->cache as $ts) {
|
||||||
|
if ($ts > $offset)
|
||||||
|
return $ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$debug = false;
|
||||||
|
|
||||||
//make sure the offset is valid
|
//make sure the offset is valid
|
||||||
if( $offset === false || (isset($this->rules['until']) && $this->rules['until'] <= $offset) ) {
|
if( $offset === false || (isset($this->rules['until']) && $offset > $this->rules['until']) ) {
|
||||||
if($echo) echo 'STOP: ' . date('r', $offset) . "\n";
|
if($debug) echo 'STOP: ' . date('r', $offset) . "\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,25 +227,28 @@ class SG_iCal_Freq {
|
||||||
|
|
||||||
//set the timestamp of the offset (ignoring hours and minutes unless we want them to be
|
//set the timestamp of the offset (ignoring hours and minutes unless we want them to be
|
||||||
//part of the calculations.
|
//part of the calculations.
|
||||||
if($echo) echo 'O: ' . date('r', $offset) . "\n";
|
if($debug) echo 'O: ' . date('r', $offset) . "\n";
|
||||||
$hour = (in_array($this->freq, array('hourly','minutely')) && $offset > $this->start) ? date('H', $offset) : date('H', $this->start);
|
$hour = (in_array($this->freq, array('hourly','minutely')) && $offset > $this->start) ? date('H', $offset) : date('H', $this->start);
|
||||||
$minute = (($this->freq == 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date('i', $offset) : date('i', $this->start);
|
$minute = (($this->freq == 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date('i', $offset) : date('i', $this->start);
|
||||||
$t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y',$offset));
|
$t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y',$offset));
|
||||||
if($echo) echo 'START: ' . date('r', $t) . "\n";
|
if($debug) echo 'START: ' . date('r', $t) . "\n";
|
||||||
|
|
||||||
if( $this->simpleMode ) {
|
if( $this->simpleMode ) {
|
||||||
if( $offset < $t ) {
|
if( $offset < $t ) {
|
||||||
return $t;
|
$ts = $t;
|
||||||
|
if ($ts && in_array($ts, $this->excluded))
|
||||||
|
$ts = $this->findNext($ts);
|
||||||
|
} else {
|
||||||
|
$ts = $this->findStartingPoint( $t, $this->rules['interval'], false );
|
||||||
|
if( !$this->validDate( $ts ) ) {
|
||||||
|
$ts = $this->findNext($ts);
|
||||||
}
|
}
|
||||||
$next = $this->findStartingPoint( $t, $this->rules['interval'], false );
|
|
||||||
if( !$this->validDate( $next ) ) {
|
|
||||||
return $this->findNext($next);
|
|
||||||
}
|
}
|
||||||
return $next;
|
return $ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
$eop = $this->findEndOfPeriod($offset);
|
$eop = $this->findEndOfPeriod($offset);
|
||||||
if($echo) echo 'EOP: ' . date('r', $eop) . "\n";
|
if($debug) echo 'EOP: ' . date('r', $eop) . "\n";
|
||||||
|
|
||||||
foreach( $this->knownRules AS $rule ) {
|
foreach( $this->knownRules AS $rule ) {
|
||||||
if( $found && isset($this->rules['by' . $rule]) ) {
|
if( $found && isset($this->rules['by' . $rule]) ) {
|
||||||
|
@ -180,7 +260,7 @@ class SG_iCal_Freq {
|
||||||
if( $imm === false ) {
|
if( $imm === false ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if($echo) echo strtoupper($rule) . ': ' . date('r', $imm) . ' A: ' . ((int) ($imm > $offset && $imm < $eop)) . "\n";
|
if($debug) echo strtoupper($rule) . ': ' . date('r', $imm) . ' A: ' . ((int) ($imm > $offset && $imm < $eop)) . "\n";
|
||||||
if( $imm > $offset && $imm < $eop && ($_t == null || $imm < $_t) ) {
|
if( $imm > $offset && $imm < $eop && ($_t == null || $imm < $_t) ) {
|
||||||
$_t = $imm;
|
$_t = $imm;
|
||||||
}
|
}
|
||||||
|
@ -194,20 +274,24 @@ class SG_iCal_Freq {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( $this->start > $offset && $this->start < $t ) {
|
if( $offset < $this->start && $this->start < $t ) {
|
||||||
return $this->start;
|
$ts = $this->start;
|
||||||
} else if( $found && ($t != $offset)) {
|
} else if( $found && ($t != $offset)) {
|
||||||
if( $this->validDate( $t ) ) {
|
if( $this->validDate( $t ) ) {
|
||||||
if($echo) echo 'OK' . "\n";
|
if($debug) echo 'OK' . "\n";
|
||||||
return $t;
|
$ts = $t;
|
||||||
} else {
|
} else {
|
||||||
if($echo) echo 'Invalid' . "\n";
|
if($debug) echo 'Invalid' . "\n";
|
||||||
return $this->findNext($t);
|
$ts = $this->findNext($t);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if($echo) echo 'Not found' . "\n";
|
if($debug) echo 'Not found' . "\n";
|
||||||
return $this->findNext( $this->findStartingPoint( $offset, $this->rules['interval'] ) );
|
$ts = $this->findNext( $this->findStartingPoint( $offset, $this->rules['interval'] ) );
|
||||||
}
|
}
|
||||||
|
if ($ts && in_array($ts, $this->excluded))
|
||||||
|
return $this->findNext($ts);
|
||||||
|
|
||||||
|
return $ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -396,7 +480,11 @@ class SG_iCal_Freq {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validDate( $t ) {
|
private function validDate( $t ) {
|
||||||
if( isset($this->rules['until']) && $this->rules['until'] <= $t ) {
|
if( isset($this->rules['until']) && $t > $this->rules['until'] ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_array($t, $this->excluded)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
* @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
|
||||||
*/
|
*/
|
||||||
class SG_iCal_Line implements ArrayAccess, Countable, IteratorAggregate {
|
class SG_iCal_Line implements ArrayAccess, Countable, IteratorAggregate {
|
||||||
private $ident;
|
protected $ident;
|
||||||
private $data;
|
protected $data;
|
||||||
private $params = array();
|
protected $params = array();
|
||||||
|
|
||||||
private $replacements = array('from'=>array('\\,', '\\n', '\\;', '\\:', '\\"'), 'to'=>array(',', "\n", ';', ':', '"'));
|
protected $replacements = array('from'=>array('\\,', '\\n', '\\;', '\\:', '\\"'), 'to'=>array(',', "\n", ';', ':', '"'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new line.
|
* Constructs a new line.
|
||||||
|
@ -75,6 +75,18 @@ class SG_iCal_Line implements ArrayAccess, Countable, IteratorAggregate {
|
||||||
return $this->data;
|
return $this->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content of the line
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDataAsArray() {
|
||||||
|
if (strpos($this->data,",") !== false) {
|
||||||
|
return explode(",",$this->data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return array($this->data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A static helper to get a array of SG_iCal_Line's, and calls
|
* A static helper to get a array of SG_iCal_Line's, and calls
|
||||||
* getData() on each of them to lay the data "bare"..
|
* getData() on each of them to lay the data "bare"..
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?php
|
<?php // BUILD: Remove line
|
||||||
|
|
||||||
class SG_iCal_Parser {
|
class SG_iCal_Parser {
|
||||||
/**
|
/**
|
||||||
|
@ -93,6 +93,7 @@ class SG_iCal_Parser {
|
||||||
*/
|
*/
|
||||||
private static function _Parse( $content, SG_iCal $ical ) {
|
private static function _Parse( $content, SG_iCal $ical ) {
|
||||||
$main_sections = array('vevent', 'vjournal', 'vtodo', 'vtimezone', 'vcalendar');
|
$main_sections = array('vevent', 'vjournal', 'vtodo', 'vtimezone', 'vcalendar');
|
||||||
|
$array_idents = array('exdate','rdate');
|
||||||
$sections = array();
|
$sections = array();
|
||||||
$section = '';
|
$section = '';
|
||||||
$current_data = array();
|
$current_data = array();
|
||||||
|
@ -119,8 +120,13 @@ class SG_iCal_Parser {
|
||||||
if( array_search($s, $sections) !== false ) {
|
if( array_search($s, $sections) !== false ) {
|
||||||
// This section is in the main section
|
// This section is in the main section
|
||||||
if( $section == $s ) {
|
if( $section == $s ) {
|
||||||
// It _is_ the main section
|
// It _is_ the main section else
|
||||||
|
if (in_array($line->getIdent(), $array_idents))
|
||||||
|
//exdate could appears more that once
|
||||||
|
$current_data[$s][$line->getIdent()][] = $line;
|
||||||
|
else {
|
||||||
$current_data[$s][$line->getIdent()] = $line;
|
$current_data[$s][$line->getIdent()] = $line;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Sub section
|
// Sub section
|
||||||
$current_data[$s][$section][$line->getIdent()] = $line;
|
$current_data[$s][$section][$line->getIdent()] = $line;
|
||||||
|
|
|
@ -22,14 +22,14 @@ class SG_iCal_Query {
|
||||||
if( $ical instanceof SG_iCalReader ) {
|
if( $ical instanceof SG_iCalReader ) {
|
||||||
$ical = $ical->getEvents();
|
$ical = $ical->getEvents();
|
||||||
}
|
}
|
||||||
if( !is_array($evs) ) {
|
if( !is_array($ical) ) {
|
||||||
throw new Exception('SG_iCal_Query::Between called with invalid input!');
|
throw new Exception('SG_iCal_Query::Between called with invalid input!');
|
||||||
}
|
}
|
||||||
|
|
||||||
$rtn = array();
|
$rtn = array();
|
||||||
foreach( $evs AS $e ) {
|
foreach( $ical AS $e ) {
|
||||||
if( ($start <= $e->getStart() && $e->getStart() < $end)
|
if( ($start <= $e->getStart() && $e->getStart() < $end)
|
||||||
|| ($start < $e->getEnd() && $e->getEnd() <= $end) ) {
|
|| ($start < $e->getRangeEnd() && $e->getRangeEnd() <= $end) ) {
|
||||||
$rtn[] = $e;
|
$rtn[] = $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ class SG_iCal_Query {
|
||||||
|
|
||||||
$rtn = array();
|
$rtn = array();
|
||||||
foreach( $ical AS $e ) {
|
foreach( $ical AS $e ) {
|
||||||
if( $start <= $e->getStart() ) {
|
if($e->getStart() >= $start || $e->getRangeEnd() >= $start) {
|
||||||
$rtn[] = $e;
|
$rtn[] = $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
*/
|
*/
|
||||||
class SG_iCal_Recurrence {
|
class SG_iCal_Recurrence {
|
||||||
|
|
||||||
|
public $rrule;
|
||||||
|
|
||||||
protected $freq;
|
protected $freq;
|
||||||
|
|
||||||
protected $until;
|
protected $until;
|
||||||
|
@ -29,6 +31,7 @@ class SG_iCal_Recurrence {
|
||||||
protected $byyearno;
|
protected $byyearno;
|
||||||
protected $bymonth;
|
protected $bymonth;
|
||||||
protected $bysetpos;
|
protected $bysetpos;
|
||||||
|
|
||||||
protected $wkst;
|
protected $wkst;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +43,6 @@ class SG_iCal_Recurrence {
|
||||||
'byyearday', 'byyearno', 'bymonth', 'bysetpos'
|
'byyearday', 'byyearno', 'bymonth', 'bysetpos'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an recurrence object with a passed in line. Parses the line.
|
* Creates an recurrence object with a passed in line. Parses the line.
|
||||||
* @param object $line an SG_iCal_Line object which will be parsed to get the
|
* @param object $line an SG_iCal_Line object which will be parsed to get the
|
||||||
|
@ -56,6 +58,8 @@ class SG_iCal_Recurrence {
|
||||||
* @param string $line the line to be parsed
|
* @param string $line the line to be parsed
|
||||||
*/
|
*/
|
||||||
protected function parseLine($line) {
|
protected function parseLine($line) {
|
||||||
|
$this->rrule = $line;
|
||||||
|
|
||||||
//split up the properties
|
//split up the properties
|
||||||
$recurProperties = explode(';', $line);
|
$recurProperties = explode(';', $line);
|
||||||
$recur = array();
|
$recur = array();
|
||||||
|
@ -77,6 +81,18 @@ class SG_iCal_Recurrence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the $until member
|
||||||
|
* @param mixed timestamp (int) / Valid DateTime format (string)
|
||||||
|
*/
|
||||||
|
public function setUntil($ts) {
|
||||||
|
if ( is_int($ts) )
|
||||||
|
$dt = new DateTime('@'.$ts);
|
||||||
|
else
|
||||||
|
$dt = new DateTime($ts);
|
||||||
|
$this->until = $dt->format('Ymd\THisO');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the desired member variable and returns it (if it's set)
|
* Retrieves the desired member variable and returns it (if it's set)
|
||||||
* @param string $member name of the member variable
|
* @param string $member name of the member variable
|
||||||
|
@ -103,7 +119,6 @@ class SG_iCal_Recurrence {
|
||||||
* @return mixed string if the member has been set, false otherwise
|
* @return mixed string if the member has been set, false otherwise
|
||||||
*/
|
*/
|
||||||
public function getUntil() {
|
public function getUntil() {
|
||||||
|
|
||||||
return $this->getMember('until');
|
return $this->getMember('until');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,6 @@ class VEventTest extends PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
date_default_timezone_set('America/New_York');
|
date_default_timezone_set('America/New_York');
|
||||||
$event = new SG_iCal_VEvent($data, $ical);
|
$event = new SG_iCal_VEvent($data, $ical);
|
||||||
$this->assertEquals(strtotime('20091030T090000'), $event->getEnd());
|
$this->assertEquals(strtotime('20091030T090000'), $event->getProperty('laststart'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
require_once 'PHPUnit/Framework.php';
|
require_once 'PHPUnit/Framework.php';
|
||||||
require_once dirname(__FILE__).'/FreqTest.php';
|
require_once dirname(__FILE__).'/FreqTest.php';
|
||||||
require_once dirname(__FILE__).'/RecurrenceTest.php';
|
require_once dirname(__FILE__).'/RecurrenceTest.php';
|
||||||
|
require_once dirname(__FILE__).'/DurationTest.php';
|
||||||
|
|
||||||
class Helpers_AllTests {
|
class Helpers_AllTests {
|
||||||
|
|
||||||
|
@ -9,6 +10,7 @@ class Helpers_AllTests {
|
||||||
$suite = new PHPUnit_Framework_TestSuite('Helpers');
|
$suite = new PHPUnit_Framework_TestSuite('Helpers');
|
||||||
$suite->addTestSuite('FreqTest');
|
$suite->addTestSuite('FreqTest');
|
||||||
$suite->addTestSuite('RecurrenceTest');
|
$suite->addTestSuite('RecurrenceTest');
|
||||||
|
$suite->addTestSuite('DurationTest');
|
||||||
|
|
||||||
return $suite;
|
return $suite;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
require_once dirname(__FILE__) . '/../common.php';
|
||||||
|
require_once 'PHPUnit/Framework.php';
|
||||||
|
|
||||||
|
class DurationTest extends PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
$this->secsMin = 60;
|
||||||
|
$this->secsHour = 60 * 60;
|
||||||
|
$this->secsDay = 24 * 60 * 60;
|
||||||
|
$this->secsWeek = 7 * 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDurationDateTime() {
|
||||||
|
|
||||||
|
//A duration of 10 days, 6 hours and 20 seconds
|
||||||
|
$dur = new SG_iCal_Duration('P10DT6H0M20S');
|
||||||
|
$this->assertEquals($this->secsDay*10 + $this->secsHour*6 + 20, $dur->getDuration() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDurationWeek() {
|
||||||
|
|
||||||
|
//A duration of 2 weeks
|
||||||
|
$dur = new SG_iCal_Duration('P2W');
|
||||||
|
$this->assertEquals($this->secsWeek * 2, $dur->getDuration() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDurationNegative() {
|
||||||
|
|
||||||
|
//A duration of -1 day
|
||||||
|
$dur = new SG_iCal_Duration('-P1D');
|
||||||
|
$this->assertEquals(-1 * $this->secsDay, $dur->getDuration() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -605,21 +605,64 @@ class FreqTest extends PHPUnit_Framework_TestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLastOccurrence() {
|
/*
|
||||||
|
weird : in this test $start is not a matched occurrence but...
|
||||||
|
|
||||||
|
to do something like that, we need EXDATE :
|
||||||
|
DTSTART;TZID=US-Eastern:19970902T090000
|
||||||
|
EXDATE;TZID=US-Eastern:19970902T090000
|
||||||
|
RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function testFirstOccurrencesByYearDay() {
|
||||||
$rule = 'FREQ=YEARLY;INTERVAL=2;BYYEARDAY=1;COUNT=5';
|
$rule = 'FREQ=YEARLY;INTERVAL=2;BYYEARDAY=1;COUNT=5';
|
||||||
$start = strtotime('2009-10-27T090000');
|
$start = strtotime('2009-10-27T090000');
|
||||||
$freq = new SG_iCal_Freq($rule, $start);
|
$freq = new SG_iCal_Freq($rule, $start);
|
||||||
$this->assertEquals(strtotime('2018-01-01T09:00:00'), $freq->lastOccurrence());
|
$this->assertEquals(strtotime('2009-10-27T09:00:00'), $freq->firstOccurrence());
|
||||||
|
$this->assertEquals(strtotime('2011-01-01T09:00:00'), $freq->nextOccurrence($start));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: WKST rule
|
public function testFirstOccurrencesByYearDayWithoutFirstDate() {
|
||||||
|
$rule = 'FREQ=YEARLY;INTERVAL=2;BYYEARDAY=1;COUNT=5';
|
||||||
|
$start = strtotime('2009-10-27T090000');
|
||||||
|
$freq = new SG_iCal_Freq($rule, $start, array($start));
|
||||||
|
$this->assertEquals(strtotime('2011-01-01T09:00:00'), $freq->firstOccurrence());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLastOccurrenceByYearDay() {
|
||||||
|
$rule = 'FREQ=YEARLY;INTERVAL=2;BYYEARDAY=1;COUNT=5';
|
||||||
|
$start = strtotime('2011-01-01T090000');
|
||||||
|
$freq = new SG_iCal_Freq($rule, $start);
|
||||||
|
$this->assertEquals(strtotime('2019-01-01T09:00:00'), $freq->lastOccurrence());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCacheCount() {
|
||||||
|
$rule = 'FREQ=YEARLY;INTERVAL=2;BYYEARDAY=1;COUNT=5';
|
||||||
|
$start = strtotime('2011-01-01T090000');
|
||||||
|
$freq = new SG_iCal_Freq($rule, $start);
|
||||||
|
$this->assertEquals(5, count($freq->getAllOccurrences()));
|
||||||
|
$this->assertEquals(strtotime('2019-01-01T09:00:00'), $freq->lastOccurrence());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: BYSETPOS rule :
|
||||||
|
The 3rd instance into the month of one of Tuesday, Wednesday or
|
||||||
|
Thursday, for the next 3 months:
|
||||||
|
|
||||||
|
DTSTART;TZID=US-Eastern:19970904T090000
|
||||||
|
RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO: WKST rule
|
||||||
|
*/
|
||||||
|
|
||||||
|
//check a serie of dates
|
||||||
private function assertRule( $rule, $start, $dateset ) {
|
private function assertRule( $rule, $start, $dateset ) {
|
||||||
$freq = new SG_iCal_Freq($rule, $start);
|
$freq = new SG_iCal_Freq($rule, $start);
|
||||||
reset($dateset);
|
reset($dateset);
|
||||||
$n = $start - 1;
|
$n = $start - 1;
|
||||||
do {
|
do {
|
||||||
$n = $freq->findNext($n);
|
$n = $freq->findNext($n);
|
||||||
|
//echo date('Y-m-d H:i:sO ',$n);
|
||||||
$e = (current($dateset) != -1) ? current($dateset) : false;
|
$e = (current($dateset) != -1) ? current($dateset) : false;
|
||||||
$this->assertEquals($e, $n);
|
$this->assertEquals($e, $n);
|
||||||
} while( next($dateset) !== false );
|
} while( next($dateset) !== false );
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# aptitude install phpunit
|
||||||
|
|
||||||
|
phpunit AllTests.php
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
TIMESTAMP=$1
|
||||||
|
|
||||||
|
php -r "echo date('Y-m-d H:i:s O',$TIMESTAMP); echo \"\n\"; "
|
||||||
|
|
Ładowanie…
Reference in New Issue