New style calendar generation

This commit is contained in:
nikolark committed 2018-01-31 22:37:28 +01:00
1 parent b27a90ed82
commit 30eccd374a
50 files changed
+5097 -10535

No files matched your search

View File
Whitespace-only changes.
+5
View File
@@ -0,0 +1,5 @@
{
"require": {
"jsvrcek/ics": "^0.5.1"
}
}
+66
View File
@@ -0,0 +1,66 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "dc0b84df01241ef3d112862283e7b985",
"packages": [
{
"name": "jsvrcek/ics",
"version": "0.5.1",
"source": {
"type": "git",
"url": "https://github.com/jasvrcek/ICS.git",
"reference": "c93b8dba37b35dd0b65d7b76c7d5b8bc38ecc74c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jasvrcek/ICS/zipball/c93b8dba37b35dd0b65d7b76c7d5b8bc38ecc74c",
"reference": "c93b8dba37b35dd0b65d7b76c7d5b8bc38ecc74c",
"shasum": ""
},
"require": {
"php": "^5.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Jsvrcek\\ICS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Justin Svrcek",
"homepage": "https://github.com/jasvrcek"
}
],
"description": "abstraction layer for creating multi-byte safe RFC 5545 compliant .ics files",
"homepage": "https://github.com/jasvrcek/ICS",
"keywords": [
".ics",
"RFC 5545",
"calendar",
"export",
"ical",
"multi-byte safe"
],
"time": "2017-02-01T20:10:20+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}
File diff suppressed because it is too large. Load diff
+109 -1
View File
@@ -1,6 +1,114 @@
<?php
/*
* Template Name: Ical
*
* Generate an ical with the event schedule
* Uses https://github.com/jasvrcek/ICS
*/
require get_stylesheet_directory() . '/vendor/autoload.php';
use Jsvrcek\ICS\CalendarExport;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\Model\Calendar;
use Jsvrcek\ICS\Model\CalendarEvent;
use Jsvrcek\ICS\Model\Description\Location;
use Jsvrcek\ICS\Utility\Formatter;
function fetch_events($from = '-6 months', $to = '+1 year') {
global $post;
$cache_key = 'dns2015-ical-events';
$cache_expiry = 30*60; // 30 min in seconds
$cached_events = wp_cache_get($cache_key);
if($cached_events !== false) {
return $cached_events;
}
$meta_query = [
'key' => '_neuf_events_starttime',
'value' => [date('U', strtotime($from)), date('U', strtotime($to))],
'type' => 'numeric',
'compare' => 'BETWEEN'
];
$args = [
'post_type' => 'event',
'meta_query' => [$meta_query],
'posts_per_page' => -1,
'orderby' => 'meta_value_num',
'meta_key' => '_neuf_events_starttime',
'order' => 'ASC'
];
$events = new WP_Query($args);
$event_list = [];
while ($events->have_posts()) {
$events->the_post();
$start = new DateTime('@' . $post->neuf_events_starttime);
$end = new DateTime('@' . $post->neuf_events_endtime);
$description = strip_tags(get_the_content());
$venue = $post->neuf_events_venue;
$location = $venue == 'Annetsteds' ? $venue : $venue . ', Chateau Neuf, Slemdalsveien 15, 0313 OSLO';
array_push($event_list, [
'start' => $start,
'end' => $end,
'locations' => [$location],
'summary' => html_entity_decode(get_the_title()),
'description' => html_entity_decode($description)
]);
}
wp_cache_set($cache_key, $event_list, '', $cache_expiry);
return $event_list;
}
function add_calendar_events($events, $calendar) {
foreach ($events as $event) {
$calendar_event = new CalendarEvent();
$calendar_event->setStart($event['start'])
->setEnd($event['end'])
->setSummary($event['summary'])
->setDescription($event['description'])
->setUid('asdf');
$calendar_locations = [];
foreach ($event['locations'] as $location) {
$cal_location = new Location();
$cal_location->setName($location);
$calendar_locations[] = $cal_location;
}
$calendar_event->setLocations($calendar_locations);
$calendar->addEvent($calendar_event);
}
return $calendar;
}
$events = fetch_events();
$wp_timezone = get_option('timezone_string');
$custom_cal_headers = [
'X-WR-CALNAME' => 'Chateau Neuf / Studentersamfundet',
'X-WR-CALDESC' => 'Program for Chateau Neuf - Det Norske Studentersamfund',
'X-WR-TIMEZONE' => $wp_timezone
];
$calendar = new Calendar();
$calendar->setProdId('-//Chateau Neuf - Det Norske Studentersafund//Program for Chateau Neuf - Det Norske Studentersamfund//NB')
->setTimezone(new DateTimeZone($wp_timezone))
->setCustomHeaders($custom_cal_headers);
$calendar = add_calendar_events($events, $calendar);
//setup exporter and out .ics formatted text
$calendarExport = new CalendarExport(new CalendarStream, new Formatter());
$calendarExport->addCalendar($calendar);
header("Content-type: text/calendar; charset=utf-8");
echo file_get_contents(get_bloginfo('stylesheet_directory')."/cal/studentersamfundet.ics");
echo $calendarExport->getStream();
-62
View File
@@ -1,62 +0,0 @@
<?php
/** Usage with WP-CLI
* wp --path=/path/to/wordpress eval-file program-ical-regen.php
*/
require_once('../inc/iCalcreator.class.php');
//http://kigkonsult.se/iCalcreator/
$config = array(
"unique_id" => "studentersamfundet.no",
"directory" => "cal",
"filename" => "studentersamfundet.ics"
);
$v = new vcalendar($config);
$tz = "Europe/Oslo";
$v->setProperty("method", "PUBLISH");
$v->setProperty("x-wr-calname", "Studentersamfundet");
$v->setProperty("x-wr-caldesc", "Program for Det Norske Studentersamfund / Chateau Neuf");
$v->setProperty("x-wr-timezone", $tz);
$xprops = array("X-LIC-LOCATION" => $tz);
iCalUtilityFunctions::createTimezone($v, $tz, $xprops);
$meta_query = array(
'key' => '_neuf_events_starttime',
'value' => array(date('U', strtotime('-6 months')), date('U', strtotime('+1 year'))),
'type' => 'numeric',
'compare' => 'BETWEEN'
);
$args = array(
'post_type' => 'event',
'meta_query' => array($meta_query),
'posts_per_page' => -1,
'orderby' => 'meta_value_num',
'meta_key' => '_neuf_events_starttime',
'order' => 'ASC'
);
$events = new WP_Query($args);
if ($events->have_posts()) {
while ($events->have_posts()) {
$events->the_post();
$neuf_events_starttime = $post->neuf_events_starttime;
$neuf_events_endtime = $post->neuf_events_endtime;
$neuf_events_venue = $post->neuf_events_venue;
$dtstart = date_i18n('Ymd\THis', (int) $neuf_events_starttime);
$dtend = date_i18n('Ymd\THis', (int) $neuf_events_endtime);
$description = strip_tags(get_the_content());
$vevent = & $v->newComponent("vevent");
$vevent->setProperty("dtstart", $dtstart);
$vevent->setProperty("dtend", $dtend);
$vevent->setProperty("location", $neuf_events_venue);
$vevent->setProperty("summary", html_entity_decode(get_the_title()));
$vevent->setProperty("description", html_entity_decode($description));
}
}
$v->saveCalendar();
+7
View File
@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitc474cc171e7801a4a7be7490bdfb66aa::getLoader();
+445
View File
@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) {
foreach ($this->prefixDirsPsr4[$search] as $dir) {
$length = $this->prefixLengthsPsr4[$first][$search];
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
+56
View File
@@ -0,0 +1,56 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Composer
Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be>
Source: https://github.com/composer/composer
Files: *
Copyright: 2016, Nils Adermann <naderman@naderman.de>
2016, Jordi Boggiano <j.boggiano@seld.be>
License: Expat
Files: src/Composer/Util/TlsHelper.php
Copyright: 2016, Nils Adermann <naderman@naderman.de>
2016, Jordi Boggiano <j.boggiano@seld.be>
2013, Evan Coury <me@evancoury.com>
License: Expat and BSD-2-Clause
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+9
View File
@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);
+9
View File
@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);
+10
View File
@@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Jsvrcek\\ICS\\' => array($vendorDir . '/jsvrcek/ics/src'),
);
+52
View File
@@ -0,0 +1,52 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitc474cc171e7801a4a7be7490bdfb66aa
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitc474cc171e7801a4a7be7490bdfb66aa', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitc474cc171e7801a4a7be7490bdfb66aa', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitc474cc171e7801a4a7be7490bdfb66aa::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitc474cc171e7801a4a7be7490bdfb66aa
{
public static $prefixLengthsPsr4 = array (
'J' =>
array (
'Jsvrcek\\ICS\\' => 12,
),
);
public static $prefixDirsPsr4 = array (
'Jsvrcek\\ICS\\' =>
array (
0 => __DIR__ . '/..' . '/jsvrcek/ics/src',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitc474cc171e7801a4a7be7490bdfb66aa::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitc474cc171e7801a4a7be7490bdfb66aa::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}
+52
View File
@@ -0,0 +1,52 @@
[
{
"name": "jsvrcek/ics",
"version": "0.5.1",
"version_normalized": "0.5.1.0",
"source": {
"type": "git",
"url": "https://github.com/jasvrcek/ICS.git",
"reference": "c93b8dba37b35dd0b65d7b76c7d5b8bc38ecc74c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jasvrcek/ICS/zipball/c93b8dba37b35dd0b65d7b76c7d5b8bc38ecc74c",
"reference": "c93b8dba37b35dd0b65d7b76c7d5b8bc38ecc74c",
"shasum": ""
},
"require": {
"php": "^5.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.5"
},
"time": "2017-02-01T20:10:20+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Jsvrcek\\ICS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Justin Svrcek",
"homepage": "https://github.com/jasvrcek"
}
],
"description": "abstraction layer for creating multi-byte safe RFC 5545 compliant .ics files",
"homepage": "https://github.com/jasvrcek/ICS",
"keywords": [
".ics",
"RFC 5545",
"calendar",
"export",
"ical",
"multi-byte safe"
]
}
]
+7
View File
@@ -0,0 +1,7 @@
.buildpath
.project
.settings
.idea
vendor
composer.lock
phpunit.xml
+18
View File
@@ -0,0 +1,18 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
sudo: false
install:
- travis_retry composer install --no-interaction --prefer-source
script:
- vendor/bin/phpunit
+147
View File
@@ -0,0 +1,147 @@
[![Build status](https://travis-ci.org/jasvrcek/ICS.svg?branch=master)](https://travis-ci.org/jasvrcek/ICS)
ICS
===
Object-oriented php library for creating (and eventually reading) .ics iCal files.
* This project does not yet support all functionality of the .ics format.
## 1. Basic Usage
```php
use Jsvrcek\ICS\Model\Calendar;
use Jsvrcek\ICS\Model\CalendarEvent;
use Jsvrcek\ICS\Model\Relationship\Attendee;
use Jsvrcek\ICS\Model\Relationship\Organizer;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\CalendarExport;
//setup an event
$eventOne = new CalendarEvent();
$eventOne->setStart(new \DateTime())
->setSummary('Family reunion')
->setUid('event-uid');
//add an Attendee
$attendee = new Attendee(new Formatter());
$attendee->setValue('moe@example.com')
->setName('Moe Smith');
$eventOne->addAttendee($attendee);
//set the Organizer
$organizer = new Organizer(new Formatter());
$organizer->setValue('heidi@example.com')
->setName('Heidi Merkell')
->setLanguage('de');
$eventOne->setOrganizer($organizer);
//new event
$eventTwo = new CalendarEvent();
$eventTwo->setStart(new \DateTime())
->setSummary('Dentist Appointment')
->setUid('event-uid');
//setup calendar
$calendar = new Calendar();
$calendar->setProdId('-//My Company//Cool Calendar App//EN')
->addEvent($eventOne)
->addEvent($eventTwo);
//setup exporter
$calendarExport = new CalendarExport(new CalendarStream, new Formatter());
$calendarExport->addCalendar($calendar);
//output .ics formatted text
echo $calendarExport->getStream();
```
## 2. Batch Event Provider
The basic usage example will build the complete .ics string in memory and then echo it all
at once. This will use a lot of memory for a large calendar. The following example shows
how to make CalendarExport::getStream() output each line of the ics file as it is generated, as well as how to set a provider
for building the event list of a calendar in batches during export.
```php
use Jsvrcek\ICS\Model\Calendar;
use Jsvrcek\ICS\Model\CalendarEvent;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\CalendarExport;
//setup calendar
$calendar = new Calendar();
$calendar->setProdId('-//My Company//Cool Calendar App//EN');
//setup event provider to add events in batches during event iteration in $calendarExport->getStream()
$calendar->setEventsProvider(function ($startKey) use ($myDatabase) {
//pseudo-code to get a batch of events from database
$eventDataArray = $myDatabase->getEventsBatch($startKey);
$events = array();
foreach ($eventDataArray as $row)
{
$event = new CalendarEvent();
$event->setStart($row['start_date'])
->setSummary($row['summary'])
->setUid($row['event_uid']);
$events[] = $event;
}
return $events;
});
//setup exporter
$calendarExport = new CalendarExport(new CalendarStream, new Formatter());
$calendarExport->addCalendar($calendar);
//set exporter to send items directly to output instead of storing in memory
$calendarExport->getStreamObject()->setDoImmediateOutput(true);
//output .ics formatted text
echo $calendarExport->getStream();
```
## Todos
* Jsvrcek\ICS\Model\CalendarTodo
## Reference
* http://tools.ietf.org/html/rfc5545
## Credits
* Alex Balhatchet at [kaoru](https://github.com/kaoru) implemented CalendarAlarm.
* Thijs Wijnmaalen at [thijsw](https://github.com/thijsw/ics-large) provided inspiration on the batch provider code.
## License
The MIT License (MIT)
Copyright (c) 2013 Justin Svrcek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+29
View File
@@ -0,0 +1,29 @@
{
"name": "jsvrcek/ics",
"license": "MIT",
"description": "abstraction layer for creating multi-byte safe RFC 5545 compliant .ics files",
"keywords": ["ical", "calendar", "export", ".ics", "RFC 5545", "multi-byte safe"],
"homepage": "https://github.com/jasvrcek/ICS",
"authors": [
{
"name": "Justin Svrcek",
"homepage": "https://github.com/jasvrcek"
}
],
"require": {
"php": "^5.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.5"
},
"autoload": {
"psr-4": {
"Jsvrcek\\ICS\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Jsvrcek\\ICS\\Test\\": "src/"
}
}
}
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
failOnRisky="true"
failOnWarning="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
verbose="true"
>
<testsuites>
<testsuite name="ICS Test Suite">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>
+295
View File
@@ -0,0 +1,295 @@
<?php
namespace Jsvrcek\ICS;
use Jsvrcek\ICS\Model\CalendarAlarm;
use Jsvrcek\ICS\Model\Recurrence\RecurrenceRule;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\Model\Calendar;
use Jsvrcek\ICS\Model\CalendarEvent;
use Jsvrcek\ICS\Model\Description\Location;
use Jsvrcek\ICS\Model\Relationship\Attendee;
use Jsvrcek\ICS\Model\Relationship\Organizer;
use Jsvrcek\ICS\CalendarStream;
class CalendarExport
{
/**
*
* @var array
*/
private $calendars = array();
/**
*
* @var CalendarStream;
*/
private $stream;
/**
*
* @var Formatter
*/
private $formatter;
public function __construct(CalendarStream $stream, Formatter $formatter)
{
$this->stream = $stream;
$this->formatter = $formatter;
}
/**
* @return CalendarStream
*/
public function getStreamObject()
{
return $this->stream;
}
/**
* @return string
*/
public function getStream()
{
$this->stream->reset();
/* @var $cal Calendar */
foreach ($this->getCalendars() as $cal)
{
//start calendar
$this->stream->addItem('BEGIN:VCALENDAR')
->addItem('VERSION:'.$cal->getVersion())
->addItem('PRODID:'.$cal->getProdId())
->addItem('CALSCALE:'.$cal->getCalendarScale())
->addItem('METHOD:'.$cal->getMethod());
//custom headers
foreach ($cal->getCustomHeaders() as $key => $value)
{
$this->stream->addItem($key.':'.$value);
}
//timezone
$this->stream->addItem('BEGIN:VTIMEZONE');
$tz = $cal->getTimezone();
$transitions = $tz->getTransitions(strtotime('1970-01-01'), strtotime('1970-12-31'));
$daylightSavings = array(
'exists' => false,
'start' => '',
'offsetTo' => '',
'offsetFrom' => ''
);
$standard = array(
'start' => '',
'offsetTo' => '',
'offsetFrom' => ''
);
foreach ($transitions as $transition)
{
$varName = ($transition['isdst']) ? 'daylightSavings' : 'standard';
${$varName}['exists'] = true;
${$varName}['start'] = $this->formatter->getFormattedDateTime(new \DateTime($transition['time']));
${$varName}['offsetTo'] = $this->formatter->getFormattedTimeOffset($transition['offset']);
//get previous offset
$previousTimezoneObservance = $transition['ts'] - 100;
$tzDate = new \DateTime('now', $tz);
$tzDate->setTimestamp($previousTimezoneObservance);
$offset = $tzDate->getOffset();
${$varName}['offsetFrom'] = $this->formatter->getFormattedTimeOffset($offset);
}
$this->stream->addItem('TZID:'.$tz->getName());
$this->stream->addItem('BEGIN:STANDARD')
->addItem('DTSTART:'.$standard['start'])
->addItem('TZOFFSETTO:'.$standard['offsetTo'])
->addItem('TZOFFSETFROM:'.$standard['offsetFrom']);
if ($daylightSavings['exists'])
{
$this->stream->addItem('RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU');
}
$this->stream->addItem('END:STANDARD');
if ($daylightSavings['exists'])
{
$this->stream->addItem('BEGIN:DAYLIGHT')
->addItem('DTSTART:'.$daylightSavings['start'])
->addItem('TZOFFSETTO:'.$daylightSavings['offsetTo'])
->addItem('TZOFFSETFROM:'.$daylightSavings['offsetFrom'])
->addItem('RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU')
->addItem('END:DAYLIGHT');
}
$this->stream->addItem('END:VTIMEZONE');
//add events
/* @var $event CalendarEvent */
foreach ($cal->getEvents() as $event)
{
$dtStart = $event->isAllDay() ?
$this->formatter->getFormattedDate($event->getStart()) :
$this->formatter->getFormattedDateTime($event->getStart());
$dtEnd = $event->isAllDay() ?
$this->formatter->getFormattedDate($event->getEnd()) :
$this->formatter->getFormattedDateTime($event->getEnd());
$this->stream->addItem('BEGIN:VEVENT')
->addItem('UID:'.$event->getUid())
->addItem('DTSTART:'. $dtStart)
->addItem('DTEND:'. $dtEnd);
if ($event->getRecurrenceRule() instanceof RecurrenceRule)
$this->stream->addItem($event->getRecurrenceRule()->__toString());
foreach ($event->getExceptionDates() as $date)
{
$this->stream->addItem('EXDATE:'.$this->formatter->getFormattedDateTime($date));
}
if ($event->getSequence())
$this->stream->addItem('SEQUENCE:'.$event->getSequence());
$this->stream->addItem('STATUS:'.$event->getStatus())
->addItem('SUMMARY:'.$event->getSummary())
->addItem('DESCRIPTION:'.$event->getDescription());
if ($event->getClass())
$this->stream->addItem('CLASS:'.$event->getClass());
/* @var $location Location */
foreach ($event->getLocations() as $location)
{
$this->stream
->addItem('LOCATION'.$location->getUri().$location->getLanguage().':'.$location->getName());
}
if ($event->getPriority() > 0 && $event->getPriority() <= 9)
$this->stream->addItem('PRIORITY:'.$event->getPriority());
if ($event->getGeo())
$this->stream->addItem('GEO:'.$event->getGeo()->getLatitude().';'.$event->getGeo()->getLongitude());
if ($event->getUrl())
$this->stream->addItem('URL:'.$event->getUrl());
if ($event->getTimestamp())
{
$this->stream->addItem('DTSTAMP:'.$this->formatter->getFormattedUTCDateTime($event->getTimestamp()));
}
else
{
$this->stream->addItem('DTSTAMP:'.$this->formatter->getFormattedUTCDateTime(new \DateTime()));
}
if ($event->getCreated())
$this->stream->addItem('CREATED:'.$this->formatter->getFormattedUTCDateTime($event->getCreated()));
if ($event->getLastModified())
$this->stream->addItem('LAST-MODIFIED:'.$this->formatter->getFormattedUTCDateTime($event->getLastModified()));
foreach ($event->getAttendees() as $attendee)
{
$this->stream->addItem($attendee->__toString());
}
if ($event->getOrganizer())
$this->stream->addItem($event->getOrganizer()->__toString());
/** @var CalendarAlarm $alarm */
foreach ($event->getAlarms() as $alarm)
{
//basic requirements for all types of alarm
$this->stream->addItem('BEGIN:VALARM')
->addItem('TRIGGER;VALUE=DATE-TIME:'.$this->formatter->getFormattedUTCDateTime($alarm->getTrigger()))
->addItem('ACTION:'.$alarm->getAction());
//only handle repeats if both repeat and duration are set
if ($alarm->getRepeat() && $alarm->getDuration()) {
$this->stream->addItem('REPEAT:'.$alarm->getRepeat())
->addItem('DURATION:'.$this->formatter->getFormattedDateInterval($alarm->getDuration()));
}
//action specific logic
switch ($alarm->getAction())
{
case 'AUDIO':
$attachments = $alarm->getAttachments();
$this->stream->addItem('ATTACH;'.$attachments[0]);
break;
case 'DISPLAY':
$this->stream->addItem('DESCRIPTION:'.$alarm->getDescription());
break;
case 'EMAIL':
$this->stream->addItem('SUMMARY:'.$alarm->getSummary())
->addItem('DESCRIPTION:'.$alarm->getDescription());
foreach ($alarm->getAttendees() as $attendee)
{
$this->stream->addItem($attendee->__toString());
}
foreach ($alarm->getAttachments() as $attachment)
{
$this->stream->addItem('ATTACH;'.$attachment);
}
break;
default:
throw new \Exception("Unknown ALARM action: '{$alarm->getAction()}'");
break;
}
$this->stream->addItem('END:VALARM');
}
$this->stream->addItem('END:VEVENT');
}
//end calendar
$this->stream->addItem('END:VCALENDAR');
}
return $this->stream->getStream();
}
/**
* @return array
*/
public function getCalendars()
{
return $this->calendars;
}
/**
* @param array $calendars
* @return CalendarExport
*/
public function setCalendars(array $calendars)
{
$this->calendars = $calendars;
return $this;
}
/**
* @param Calendar $cal
* @return CalendarExport
*/
public function addCalendar(Calendar $cal)
{
$this->calendars[] = $cal;
return $this;
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
namespace Jsvrcek\ICS;
use Jsvrcek\ICS\Constants;
class CalendarStream
{
//length of line in bytes
const LINE_LENGTH = 70;
/**
* echo items as they are set rather than building stream in memory, for use with streaming a large calendar
*
* @var boolean
*/
private $doImmediateOutput = false;
/**
*
* @var string
*/
private $stream = '';
/**
* @param boolean $doImmediateOutput
*/
public function setDoImmediateOutput($doImmediateOutput)
{
$this->doImmediateOutput = $doImmediateOutput;
}
/**
* resets stream to blank string
*/
public function reset()
{
$this->stream = '';
}
/**
* @return string
*/
public function getStream()
{
return $this->stream;
}
/**
* splits item into new lines if necessary
* @param string $item
* @return CalendarStream
*/
public function addItem($item)
{
//get number of bytes
$length = strlen($item);
$block = '';
if ($length > 75)
{
$start = 0;
while ($start < $length)
{
$block .= mb_strcut($item, $start, self::LINE_LENGTH, 'UTF-8');
$start = $start + self::LINE_LENGTH;
//add space if not last line
if ($start < $length) $block .= Constants::CRLF.' ';
}
}
else
{
$block = $item;
}
$this->stream .= $block.Constants::CRLF;
if ($this->doImmediateOutput)
{
echo $this;
$this->reset();
}
return $this;
}
/**
* @return string
*/
public function __toString()
{
return $this->getStream();
}
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace Jsvrcek\ICS;
class Constants
{
const CRLF = "\r\n";
}
@@ -0,0 +1,8 @@
<?php
namespace Jsvrcek\ICS\Exception;
class CalendarEventException extends CalendarException
{
}
@@ -0,0 +1,8 @@
<?php
namespace Jsvrcek\ICS\Exception;
class CalendarException extends \Exception
{
}
@@ -0,0 +1,8 @@
<?php
namespace Jsvrcek\ICS\Exception;
class CalendarRecurrenceException extends CalendarException
{
}
+293
View File
@@ -0,0 +1,293 @@
<?php
namespace Jsvrcek\ICS\Model;
use Jsvrcek\ICS\Utility\Provider;
class Calendar
{
/**
*
* @var string $version
*/
private $version = '2.0';
/**
*
* @var string $prodId
*/
private $prodId = '';
/**
*
* @var string $calendarScale
*/
private $calendarScale = 'GREGORIAN';
/**
*
* @var string $method
*/
private $method = 'PUBLISH';
/**
*
* @var array $customHeaders
*/
private $customHeaders = array();
/**
*
* @var \DateTimeZone $timezone
*/
private $timezone;
/**
*
* @var Provider
*/
private $events;
/**
*
* @var Provider
*/
private $todos;
/**
*
* @var Provider
*/
private $freeBusy;
/**
*
*/
public function __construct()
{
$this->timezone = new \DateTimeZone('America/New_York');
$this->events = new Provider();
$this->todos = new Provider();
$this->freeBusy = new Provider();
}
/**
* For use if you want CalendarExport::getStream to get events in batches from a database during
* the output of the ics feed, instead of adding all events to the Calendar object before outputting
* the ics feed.
* - CalendarExport::getStream iterates through the Calendar::$events internal data array. The $eventsProvider
* closure will be called every time this data array reaches its end during iteration, and the closure should
* return the next batch of events
* - A $startKey argument with the current key of the data array will be passed to the $eventsProvider closure
* - The $eventsProvider must return an array of CalendarEvent objects
*
* Example: Calendar::setEventsProvider(function($startKey){
* //get database rows starting with $startKey
* //return an array of CalendarEvent objects
* })
*
* @param \Closure $eventsProvider
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setEventsProvider(\Closure $eventsProvider)
{
$this->events = new Provider($eventsProvider);
return $this;
}
/**
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* @param string $version
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setVersion($version)
{
$this->version = $version;
return $this;
}
/**
* @return string
*/
public function getProdId()
{
return $this->prodId;
}
/**
* @param string $prodId
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setProdId($prodId)
{
$this->prodId = $prodId;
return $this;
}
/**
* @return string
*/
public function getCalendarScale()
{
return $this->calendarScale;
}
/**
* @param string $calendarScale
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setCalendarScale($calendarScale)
{
$this->calendarScale = $calendarScale;
return $this;
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* @param string $method
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* @return array
*/
public function getCustomHeaders()
{
return $this->customHeaders;
}
/**
* use to add custom headers as array key-value pairs<br>
* <strong>Example:</strong> $customHeaders = array('X-WR-TIMEZONE' => 'America/New_York')
*
* @param array $customHeaders
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setCustomHeaders(array $customHeaders)
{
$this->customHeaders = $customHeaders;
return $this;
}
/**
* @param string $key
* @param string $value
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function addCustomHeader($key, $value)
{
$this->customHeaders[$key] = $value;
return $this;
}
/**
* @return \DateTimeZone
*/
public function getTimezone()
{
return $this->timezone;
}
/**
* @param \DateTimeZone $timezone
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setTimezone(\DateTimeZone $timezone)
{
$this->timezone = $timezone;
return $this;
}
/**
* @return Provider
*/
public function getEvents()
{
return $this->events;
}
/**
* @param CalendarEvent $event
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function addEvent(CalendarEvent $event)
{
$this->events->add($event);
return $this;
}
/**
* @return array $todos returs array of CalendarTodo objects
*/
public function getTodos()
{
return $this->todos;
}
/**
* @param CalendarTodo $todo
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function addTodo(CalendarTodo $todo)
{
$this->todos[] = $todo;
return $this;
}
/**
* @param array $todos
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setTodos(array $todos)
{
$this->todos = $todos;
return $this;
}
/**
* @return array $freeBusy returs array of CalendarFreeBusy objects
*/
public function getFreeBusy()
{
return $this->freeBusy;
}
/**
* @param CalendarFreeBusy $todo
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function addFreeBusy(CalendarFreeBusy $todo)
{
$this->freeBusy[] = $todo;
return $this;
}
/**
* @param array $freeBusy
* @return \Jsvrcek\ICS\Model\Calendar
*/
public function setFreeBusy(array $freeBusy)
{
$this->freeBusy = $freeBusy;
return $this;
}
}
+246
View File
@@ -0,0 +1,246 @@
<?php
namespace Jsvrcek\ICS\Model;
use Jsvrcek\ICS\Model\Relationship\Attendee;
use Jsvrcek\ICS\Utility\Formatter;
/**
* See http://icalendar.org/iCalendar-RFC-5545/3-6-6-alarm-component.html
*
* Class CalendarAlarm
* @package Jsvrcek\ICS\Model
*/
class CalendarAlarm
{
/**
*
* @var string $action
*/
private $action;
/**
* RFC 5545 supports triggers relative to the parent VEVENT or VTODO, but Jsvrcek\ICS does not.
* Only absolute trigger times are supported.
* @todo Support RELATED, DTSTART, and DTEND.
*
* @var \DateTime $trigger
*/
private $trigger;
/**
* For AUDIO and EMAIL actions only.
* For AUDIO there must be exactly one attachment, which must point to a sound resource.
* For EMAIL there can be any number of attachments, including zero.
*
* Should be a string including a path and a file type, like so:
*
* "FMTTYPE=application/msword:http://example.com/agenda.doc"
*
* @var array $attachments
*/
private $attachments = array();
/**
* For DISPLAY and EMAIL actions only. For EMAIL this is the body.
*
* @var string $description
*/
private $description;
/**
* For EMAIL action only. This is the email subject.
*
* @var string $summary
*/
private $summary;
/**
* For EMAIL action only. This is the email recipients.
*
* @var array $attendees
*/
private $attendees = array();
/**
* For all actions. The number of times to repeat the alarm.
* If REPEAT is set then DURATION must also be set.
*
* @var integer $repeat
*/
private $repeat;
/**
* For all actions. The duration of the alarm.
* If DURATION is set then REPEAT must also be set.
*
* @var \DateInterval $duration
*/
private $duration;
/**
* @return string
*
*/
public function getAction()
{
return $this->action;
}
/**
* @param string $action
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setAction($action)
{
$action = strtoupper($action);
$this->action = $action;
return $this;
}
/**
* @return \DateTime
*/
public function getTrigger()
{
return $this->trigger;
}
/**
* @param \DateTime $trigger
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setTrigger($trigger)
{
$this->trigger = $trigger;
return $this;
}
/**
* @return array
*/
public function getAttachments()
{
return $this->attachments;
}
/**
* @param array $attachments
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setAttachments($attachments)
{
$this->attachments = $attachments;
return $this;
}
/**
* @param string $attachment
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function addAttachment($attachment)
{
$this->attachments[] = $attachment;
return $this;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @param string $description
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @return string
*/
public function getSummary()
{
return $this->summary;
}
/**
* @param string $summary
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setSummary($summary)
{
$this->summary = $summary;
return $this;
}
/**
* @return array $attendees array of Attendee objects
*/
public function getAttendees()
{
return $this->attendees;
}
/**
* @param array $attendees array of Attendee objects
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setAttendees(array $attendees)
{
$this->attendees = $attendees;
return $this;
}
/**
* @param Attendee $attendee
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function addAttendee(Attendee $attendee)
{
$this->attendees[] = $attendee;
return $this;
}
/**
* @return int
*/
public function getRepeat()
{
return $this->repeat;
}
/**
* @param int $repeat
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setRepeat($repeat)
{
$this->repeat = $repeat;
return $this;
}
/**
* @return \DateInterval
*/
public function getDuration()
{
return $this->duration;
}
/**
* @param \DateInterval $duration
* @return \Jsvrcek\ICS\Model\CalendarAlarm
*/
public function setDuration($duration)
{
$this->duration = $duration;
return $this;
}
}
+620
View File
@@ -0,0 +1,620 @@
<?php
namespace Jsvrcek\ICS\Model;
use Jsvrcek\ICS\Model\Recurrence\RecurrenceRule;
use Jsvrcek\ICS\Model\Description\Geo;
use Jsvrcek\ICS\Model\Description\Location;
use Jsvrcek\ICS\Model\Relationship\Organizer;
use Jsvrcek\ICS\Model\Relationship\Attendee;
use Jsvrcek\ICS\Model\CalendarAlarm;
use Jsvrcek\ICS\Exception\CalendarEventException;
/**
* @author justinsvrcek
* http://tools.ietf.org/html/rfc5545#page-52
*/
class CalendarEvent
{
/**
*
* @var string $uid
*/
private $uid;
/**
*
* @var \DateTime $start
*/
private $start;
/**
*
* @var \DateTime $end
*/
private $end;
/**
*
* @var RecurrenceRule
*/
private $recurrenceRule;
/**
* array of dates to skip
* https://tools.ietf.org/html/rfc5545#page-120
*
* @var array
*/
private $exceptionDates = array();
/**
* @var string $class
*/
private $class;
/**
* @var \DateTime $created
*/
private $created;
/**
*
* @var string $description
*/
private $description;
/**
* @var Geo $geo
*/
private $geo;
/**
* @var string $lastModified
*/
private $lastModified;
/**
* @var array $locations
*/
private $locations = array();
/**
*
* @var Organizer $organizer
*/
private $organizer;
/**
*
* @var string $priority
*/
private $priority;
/**
*
* @var \DateTime $timestamp
*/
private $timestamp;
/**
*
* @var string $status
*/
private $status;
/**
*
* @var string $summary
*/
private $summary;
/**
* @todo add support in CalendarExport
* @var string $recuringId
*/
private $recuringId;
/**
*
* @var integer
*/
private $sequence;
/**
*
* @var array $attendees
*/
private $attendees = array();
/**
*
* @var array $alarms
*/
private $alarms = array();
/**
* @var string $url
*/
private $url;
/**
* @var boolean
*/
private $allDay = false;
/**
* @return boolean
*/
public function isAllDay()
{
return $this->allDay;
}
/**
* @param boolean $allDay
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setAllDay($allDay)
{
$this->allDay = $allDay;
return $this;
}
/**
* @return string
*/
public function getUid()
{
return $this->uid;
}
/**
* @param string $uid
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setUid($uid)
{
$this->uid = $uid;
return $this;
}
/**
* @return \DateTime
*/
public function getStart()
{
return $this->start;
}
/**
* also sets end time to 30 minutes after start as default<br>
* - end time can be overridden with setEnd()
*
* @param \DateTime $start
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setStart(\DateTime $start)
{
$this->start = $start;
$end = clone $start;
$this->setEnd($end->add(\DateInterval::createFromDateString('30 minutes')));
return $this;
}
/**
* @return \DateTime
*/
public function getEnd()
{
return $this->end;
}
/**
* @param \DateTime $end
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setEnd(\DateTime $end)
{
//check End is greater than Start
if ($this->getStart() instanceof \DateTime)
{
if ($this->getStart() > $end)
throw new CalendarEventException('End DateTime must be greater than Start DateTime');
}
else
{
throw new CalendarEventException('You must set the Start time before setting the End Time of a CalendarEvent');
}
$this->end = $end;
return $this;
}
/**
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function getRecurrenceRule()
{
return $this->recurrenceRule;
}
/**
* @param RecurrenceRule $recurrenceRule
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setRecurrenceRule(RecurrenceRule $recurrenceRule)
{
$this->recurrenceRule = $recurrenceRule;
return $this;
}
/**
* array of DateTime instances
* @param array $dates
*/
public function setExceptionDates(array $dates)
{
$this->exceptionDates = $dates;
return $this;
}
/**
* @return array
*/
public function getExceptionDates()
{
return $this->exceptionDates;
}
/**
* @param \DateTime $date
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function addExceptionDate(\DateTime $date)
{
$this->exceptionDates[] = $date;
return $this;
}
/**
* @return the string
*/
public function getSummary()
{
return $this->summary;
}
/**
* @param string $summary
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setSummary($summary)
{
$this->summary = $summary;
return $this;
}
/**
* @return the string
*/
public function getDescription()
{
return $this->description;
}
/**
* @param string $description
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @return array $alarms returs array of CalendarAlarm objects
*/
public function getAlarms()
{
return $this->alarms;
}
/**
* @param CalendarAlarm $alarm
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function addAlarm(CalendarAlarm $alarm)
{
$this->alarms[] = $alarm;
return $this;
}
/**
* @param array $alarms
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setAlarms(array $alarms)
{
$this->alarms = $alarms;
return $this;
}
/**
*
* @return string
*/
public function getClass()
{
return $this->class;
}
/**
*
* @param string $class
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setClass($class)
{
$this->class = $class;
return $this;
}
/**
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
*
* @param \DateTime $created
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setCreated(\DateTime $created)
{
$this->created = $created;
return $this;
}
/**
*
* @return Geo|null
*/
public function getGeo()
{
return $this->geo;
}
/**
*
* @param Geo $geo
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setGeo(Geo $geo)
{
$this->geo = $geo;
return $this;
}
/**
*
* @return \DateTime
*/
public function getLastModified()
{
return $this->lastModified;
}
/**
*
* @param \DateTime $lastModified
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setLastModified(\DateTime $lastModified)
{
$this->lastModified = $lastModified;
return $this;
}
/**
*
* @return array $locations array of Location objects
*/
public function getLocations()
{
return $this->locations;
}
/**
*
* @param array $locations array of Location objects
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setLocations(array $locations)
{
$this->locations = $locations;
return $this;
}
/**
*
* @param Location $location
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function addLocation(Location $location)
{
$this->locations[] = $location;
return $this;
}
/**
*
* @return Organizer
*/
public function getOrganizer()
{
return $this->organizer;
}
/**
*
* @param Organizer $organizer
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setOrganizer(Organizer $organizer)
{
$this->organizer = $organizer;
return $this;
}
/**
*
* @return string
*/
public function getPriority()
{
return $this->priority;
}
/**
*
* @param string $priority
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setPriority($priority)
{
$this->priority = $priority;
return $this;
}
/**
*
* @return \DateTime
*/
public function getTimestamp()
{
return $this->timestamp;
}
/**
*
* @param \DateTime $timestamp
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setTimestamp(\DateTime $timestamp)
{
$this->timestamp = $timestamp;
return $this;
}
/**
*
* @return string
*/
public function getStatus()
{
return $this->status;
}
/**
*
* @param string $status
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
*
* @return string
*/
public function getRecuringId()
{
return $this->recuringId;
}
/**
*
* @param string $recuringId
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setRecuringId($recuringId)
{
$this->recuringId = $recuringId;
return $this;
}
/**
* @return integer
*/
public function getSequence()
{
return $this->sequence;
}
/**
* @param integer $sequence
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setSequence($sequence)
{
$this->sequence = $sequence;
return $this;
}
/**
*
* @return array $attendees array of Attendee objects
*/
public function getAttendees()
{
return $this->attendees;
}
/**
*
* @param array $attendees array of Attendee objects
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setAttendees(array $attendees)
{
$this->attendees = $attendees;
return $this;
}
/**
* @param Attendee $attendee
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function addAttendee(Attendee $attendee)
{
$this->attendees[] = $attendee;
return $this;
}
/**
* @param string $url
* @return \Jsvrcek\ICS\Model\CalendarEvent
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @return string
*/
public function getUrl()
{
return $this->url;
}
}
@@ -0,0 +1,8 @@
<?php
namespace Jsvrcek\ICS\Model;
class CalendarFreeBusy
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Jsvrcek\ICS\Model;
class CalendarTodo
{
}
@@ -0,0 +1,53 @@
<?php
namespace Jsvrcek\ICS\Model\Description;
class Geo
{
/**
* @var float $latitude
*/
private $latitude;
/**
*
* @var float $longitude
*/
private $longitude;
/**
* @return number
*/
public function getLatitude()
{
return $this->latitude;
}
/**
* @param float $latitude
* @return \Jsvrcek\ICS\Model\Description\Geo
*/
public function setLatitude($latitude)
{
$this->latitude = $latitude;
return $this;
}
/**
* @return number
*/
public function getLongitude()
{
return $this->longitude;
}
/**
* @param float $longitude
* @return \Jsvrcek\ICS\Model\Description\Geo
*/
public function setLongitude($longitude)
{
$this->longitude = $longitude;
return $this;
}
}
@@ -0,0 +1,77 @@
<?php
namespace Jsvrcek\ICS\Model\Description;
class Location
{
/**
*
* @var string $name
*/
private $name;
/**
*
* @var string $uri
*/
private $uri;
/**
*
* @var string $language
*/
private $language;
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return \Jsvrcek\ICS\Model\Description\Location
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
/**
* @param string $uri uri to vCard or other uri
* @return \Jsvrcek\ICS\Model\Description\Location
*/
public function setUri($uri)
{
$this->uri = ';ALTREP="' . $uri . '"';
return $this;
}
/**
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* @param string $language RFC 1766 language identifier
* @return \Jsvrcek\ICS\Model\Description\Location
*/
public function setLanguage($language)
{
$this->language = ';LANGUAGE='.$language;
return $this;
}
}
@@ -0,0 +1,90 @@
<?php
namespace Jsvrcek\ICS\Model\Recurrence\DataType;
use Jsvrcek\ICS\Exception\CalendarRecurrenceException;
/**
* @author justinsvrcek
* http://tools.ietf.org/html/rfc5545#page-38
*/
class Frequency
{
const KEY = 'FREQ';
const SECONDLY = 1;
const MINUTELY = 2;
const HOURLY = 3;
const DAILY = 4;
const WEEKLY = 5;
const MONTHLY = 6;
const YEARLY = 7;
/**
*
* @var integer
*/
private $freq;
public static $values = array(
self::SECONDLY => 'SECONDLY',
self::MINUTELY => 'MINUTELY',
self::HOURLY => 'HOURLY',
self::DAILY => 'DAILY',
self::WEEKLY => 'WEEKLY',
self::MONTHLY => 'MONTHLY',
self::YEARLY => 'YEARLY'
);
/**
* @param integer $frequency Frequency::SECONDLY, Frequency::MINUTELY,
* Frequency::HOURLY, Frequency::DAILY, Frequency::WEEKLY,
* Frequency::MONTHLY, Frequency::YEARLY
*/
public function __construct($frequency)
{
$this->validateFrequency($frequency);
$this->freq = $frequency;
}
/**
* @return number
*/
public function getFreq()
{
return $this->freq;
}
/**
* @param integer $freq
* @return \Jsvrcek\ICS\Model\Recurrence\DataType\Frequency
*/
public function setFreq($freq)
{
$this->validateFrequency($freq);
$this->freq = $freq;
return $this;
}
/**
* @param integer $frequency
* @throws CalendarRecurrenceException
*/
private function validateFrequency($frequency)
{
if (!is_int($frequency) || $frequency < 1 || $frequency > 7)
{
throw new CalendarRecurrenceException('You must pass a Frequency constant to the contructor.');
}
}
/**
* @return string
*/
public function __toString()
{
return self::KEY.'='.self::$values[$this->freq];
}
}
@@ -0,0 +1,92 @@
<?php
namespace Jsvrcek\ICS\Model\Recurrence\DataType;
use Jsvrcek\ICS\Exception\CalendarRecurrenceException;
/**
* @author justinsvrcek
* http://tools.ietf.org/html/rfc5545#page-38
*/
class Weekday
{
const SUNDAY = 1;
const MONDAY = 2;
const TUESDAY = 3;
const WEDNESDAY = 4;
const THURSDAY = 5;
const FRIDAY = 6;
const SATURDAY = 7;
/**
*
* @var integer
*/
private $weekday;
/**
*
* @var array
*/
protected $values = array(
self::SUNDAY => 'SU',
self::MONDAY => 'MO',
self::TUESDAY => 'TU',
self::WEDNESDAY => 'WE',
self::THURSDAY => 'TH',
self::FRIDAY => 'FR',
self::SATURDAY => 'SA'
);
/**
* @param integer $weekday Weekday::SUNDAY, Weekday::MONDAY,
* Weekday::TUESDAY, Weekday::WEDNESDAY, Weekday::THURSDAY,
* Weekday::FRIDAY, Weekday::SATURDAY
*/
public function __construct($weekday)
{
$this->validateWeekday($weekday);
$this->weekday = $weekday;
}
/**
* @return number
*/
public function getWeekday()
{
return $this->weekday;
}
/**
* @param integer $weekday
* @return \Jsvrcek\ICS\Model\Recurrence\DataType\Weekday
*/
public function setWeekday($weekday)
{
$this->validateWeekday($weekday);
$this->weekday = $weekday;
return $this;
}
/**
* @param integer $weekday
* @throws CalendarRecurrenceException
*/
private function validateWeekday($weekday)
{
if (!is_int($weekday) || $weekday < 1 || $weekday > 7)
{
throw new CalendarRecurrenceException('You must pass a Weekday constant to the contructor.');
}
}
/**
* @return string
*/
public function __toString()
{
return $this->values[$this->weekday];
}
}
@@ -0,0 +1,78 @@
<?php
namespace Jsvrcek\ICS\Model\Recurrence\DataType;
use Jsvrcek\ICS\Exception\CalendarRecurrenceException;
/**
* @author justinsvrcek
* http://tools.ietf.org/html/rfc5545#page-38
*/
class WeekdayNum extends Weekday
{
const COUNT_FROM_START = null;
const COUNT_FROM_END = '-';
/**
*
* @var integer 1-53
*/
private $weekdaynum;
/**
*
* @var mixed
*/
private $countFromStartOrEnd;
/**
* WeekdayNum(Weekday::MONDAY, 2, WeekdayNum:COUNT_FROM_END) will convert to the
* string "-2MO", or "second from last Monday"
*
* @param integer $weekday
* @param integer $weekdaynum
* @param string $countFromStartOrEnd
*/
public function __construct($weekday, $weekdaynum = null, $countFromStartOrEnd = null)
{
parent::__construct($weekday);
$this->validateWeekdayNum($weekdaynum);
$this->validateCountFromStartOrEnd($countFromStartOrEnd);
$this->weekdaynum = $weekdaynum;
$this->countFromStartOrEnd = $countFromStartOrEnd;
}
/* (non-PHPdoc)
* @see Jsvrcek\ICS\Model\Recurrence\DataType.Weekday::__toString()
*/
public function __toString()
{
return $this->countFromStartOrEnd.$this->weekdaynum.parent::__toString();
}
/**
* @param mixed $countFromStartOrEnd
* @throws CalendarRecurrenceException
*/
private function validateCountFromStartOrEnd($countFromStartOrEnd)
{
if ($countFromStartOrEnd !== self::COUNT_FROM_START && $countFromStartOrEnd !== self::COUNT_FROM_END)
{
throw new CalendarRecurrenceException('You must use WeekdayNum::COUNT_FROM_START or WeekdayNum::COUNT_FROM_END');
}
}
/**
* @param integer $weekdaynum
* @throws CalendarRecurrenceException
*/
private function validateWeekdayNum($weekdaynum)
{
if (!is_int($weekdaynum) || $weekdaynum < 1 || $weekdaynum > 53)
{
throw new CalendarRecurrenceException('$weekdaynum must be an integer between 1 and 53');
}
}
}
@@ -0,0 +1,536 @@
<?php
namespace Jsvrcek\ICS\Model\Recurrence;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\Model\Recurrence\DataType\Frequency;
use Jsvrcek\ICS\Model\Recurrence\DataType\Weekday;
use Jsvrcek\ICS\Model\Recurrence\DataType\WeekdayNum;
use Jsvrcek\ICS\Exception\CalendarRecurrenceException;
/**
* @todo BYSECOND, BYMINUTE, BYHOUR, BYMONTHDAY, BYYEARDAY, BYWEEKNO, BYMONTH, BYSETPOS, and WKST
* are not implemented in the __toString function yet
*
* @author justinsvrcek
* http://tools.ietf.org/html/rfc5545#page-37
*/
class RecurrenceRule
{
const KEY = 'RRULE:';
/**
*
* @var Frequency
*/
private $frequency;
/**
*
* @var \DateTime
*/
private $until;
/**
*
* @var integer
*/
private $count;
/**
*
* @var integer
*/
private $interval;
/**
*
* @var array
*/
private $bySecondList = array();
/**
*
* @var array
*/
private $byMinuteList = array();
/**
*
* @var array
*/
private $byHourList = array();
/**
*
* @var array
*/
private $byDayList = array();
/**
* @todo need to add to RecurrenceRule::__toString()
*
* @var array
*/
private $byMonthDayList = array();
/**
* @todo need to add to RecurrenceRule::__toString()
*
* @var array
*/
private $byYearDayList = array();
/**
* @todo need to add to RecurrenceRule::__toString()
*
* @var array
*/
private $byWeekNumberList = array();
/**
* @todo need to add to RecurrenceRule::__toString()
*
* @var array
*/
private $byMonthList = array();
/**
* @todo need to add to RecurrenceRule::__toString()
*
* @var array
*/
private $bySetPosYearDayList = array();
/**
* @todo need to add to RecurrenceRule::__toString()
*
* @var Weekday
*/
private $weekStart;
/**
*
* @var Formatter
*/
private $formatter;
/**
* @param Formatter $formatter
*/
public function __construct(Formatter $formatter)
{
$this->formatter = $formatter;
}
/**
* @return \Jsvrcek\ICS\Model\Recurrence\Frequency
*/
public function getFrequency()
{
return $this->frequency;
}
/**
* @param Frequency $frequency
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setFrequency(Frequency $frequency)
{
$this->frequency = $frequency;
return $this;
}
/**
*
* @return \DateTime
*/
public function getUntil()
{
return $this->until;
}
/**
*
* @param \DateTime $until = null
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setUntil(\DateTime $until = null)
{
$this->until = $until;
return $this;
}
/**
*
* @return integer
*/
public function getCount()
{
return $this->count;
}
/**
* @param integer $count
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setCount($count)
{
$this->validateInteger($count);
$this->count = $count;
return $this;
}
/**
*
* @return integer
*/
public function getInterval()
{
return $this->interval;
}
/**
* @param integer $interval
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setInterval($interval)
{
$this->validateInteger($interval);
$this->interval = $interval;
return $this;
}
/**
* @return array
*/
public function getBySecondList()
{
return $this->bySecondList;
}
/**
* @param array $bySecondList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setBySecondList(array $bySecondList)
{
$this->bySecondList = $bySecondList;
return $this;
}
/**
* @param integer $integer 0-60
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function addBySecond($integer)
{
$this->validateInteger($integer);
$this->bySecondList[] = $integer;
return $this;
}
/**
*
* @return array
*/
public function getByMinuteList()
{
return $this->byMinuteList;
}
/**
* @param array $byMinuteList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByMinuteList(array $byMinuteList)
{
$this->byMinuteList = $byMinuteList;
return $this;
}
/**
* @param integer $integer 0-59
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function addByMinute($integer)
{
$this->validateInteger($integer);
$this->byMinuteList[] = $integer;
return $this;
}
/**
*
* @return array
*/
public function getByHourList()
{
return $this->byHourList;
}
/**
* @param array $byHourList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByHourList(array $byHourList)
{
$this->byHourList = $byHourList;
return $this;
}
/**
* @param integer $integer 0-23
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function addByHour($integer)
{
$this->validateInteger($integer);
$this->byHourList[] = $integer;
return $this;
}
/**
*
* @return array
*/
public function getByDayList()
{
return $this->byDayList;
}
/**
* @param array $byDayList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByDayList(array $byDayList)
{
$this->byDayList = $byDayList;
return $this;
}
/**
* @param WeekdayNum $weekdaynum
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function addByDay(WeekdayNum $weekdaynum)
{
$this->byDayList[] = $weekdaynum;
return $this;
}
/**
*
* @return array
*/
public function getByMonthDayList()
{
return $this->byMonthDayList;
}
/**
* @param array $byMonthDayList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByMonthDayList(array $byMonthDayList)
{
$this->byMonthDayList = $byMonthDayList;
return $this;
}
/**
* @param integer $integer
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function addByMonthDay($integer)
{
$this->validateInteger($integer);
$this->byMonthDayList[] = $bySecond;
return $this;
}
/**
*
* @return array
*/
public function getByYearDayList()
{
return $this->byYearDayList;
}
/**
* @param array $byYearDayList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByYearDayList(array $byYearDayList)
{
$this->byYearDayList = $byYearDayList;
return $this;
}
/**
*
* @return array
*/
public function getByWeekNumberList()
{
return $this->byWeekNumberList;
}
/**
* @param array $byWeekNumberList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByWeekNumberList(array $byWeekNumberList)
{
$this->byWeekNumberList = $byWeekNumberList;
return $this;
}
/**
*
* @return array
*/
public function getByMonthList()
{
return $this->byMonthList;
}
/**
* @param array $byMonthList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setByMonthList(array $byMonthList)
{
$this->byMonthList = $byMonthList;
return $this;
}
/**
*
* @return array
*/
public function getBySetPosYearDayList()
{
return $this->bySetPosYearDayList;
}
/**
* @param array $bySetPosYearDayList
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setBySetPosYearDayList(array $bySetPosYearDayList)
{
$this->bySetPosYearDayList = $bySetPosYearDayList;
return $this;
}
/**
*
* @return Weekday
*/
public function getWeekStart()
{
return $this->weekStart;
}
/**
* @param Weekday $weekStart
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function setWeekStart(Weekday $weekStart)
{
$this->weekStart = $weekStart;
return $this;
}
/**
* parses an RRULE string, hydrates self with values
*
* @param string $rRuleString
* @return \Jsvrcek\ICS\Model\Recurrence\RecurrenceRule
*/
public function parse($rRuleString)
{
//remove RRULE:
$string = str_replace(self::KEY, null, $rRuleString);
$attributes = explode(';', $string);
foreach ($attributes as $attribute)
{
list($key, $value) = explode('=', $attribute);
switch ($key)
{
case Frequency::KEY:
if ($valueStringKey = array_search($value, Frequency::$values))
{
$this->setFrequency(new Frequency($valueStringKey));
}
else
{
throw new CalendarRecurrenceException('Unsupported FREQ value in Recurrence Rule (RRULE) string: '.$value);
}
break;
case 'INTERVAL':
$this->setInterval((int)$value);
break;
case 'UNTIL':
$untilDate = new \DateTime(str_replace('Z', '', $value), new \DateTimeZone('UTC'));
$this->setUntil($untilDate);
break;
default:
throw new CalendarRecurrenceException('Unsupported attribute in Recurrence Rule (RRULE) string: '.$key);
}
}
return $this;
}
/**
* @return string
*/
public function __toString()
{
$items = array($this->getFrequency()->__toString());
if ($this->interval)
$items[] = 'INTERVAL='.$this->interval;
if ($this->until)
$items[] = 'UNTIL='.$this->formatter->getFormattedUTCDateTime($this->until);
if ($this->count)
$items[] = 'COUNT='.$this->count;
if ($this->byDayList)
$items[] = 'BYDAY='.implode(',', $this->byDayList);
return self::KEY.implode(';', $items);
}
/**
* @param integer $integer
* @throws CalendarRecurrenceException
*/
private function validateInteger($integer)
{
if (!is_int($integer))
{
throw new CalendarRecurrenceException('Value must be an integer');
}
}
}
@@ -0,0 +1,438 @@
<?php
namespace Jsvrcek\ICS\Model\Relationship;
use Jsvrcek\ICS\Utility\Formatter;
class Attendee
{
/**
*
* @var Formatter
*/
private $formatter;
/**
* RFC 5545 cal-address
* @var string $value
*/
private $value;
/**
* RFC 5545 cutypeparam
* @var string $calendarUserType
*/
private $calendarUserType;
/**
* RFC 5545 memberparam
* @var array $calendarMember array of uri values
*/
private $calendarMembers = array();
/**
* RFC 5545 roleparam
* @var string $role
*/
private $role;
/**
* RFC 5545 partstatparam
* @var string $participationStatus
*/
private $participationStatus;
/**
* RFC 5545 rsvpparam
* @var string $rsvp
*/
private $rsvp;
/**
* RFC 5545 deltoparam
* @var array $delegatedTo array of uri values
*/
private $delegatedTo = array();
/**
* RFC 5545 delfromparam
* @var array $delegatedFrom array of uri values
*/
private $delegatedFrom = array();
/**
* RFC 5545 sentbyparam
* @var string $sentBy
*/
private $sentBy;
/**
* RFC 5545 cnparam
* @var string $name
*/
private $name;
/**
* RFC 5545 dirparam
* @var string $directory
*/
private $directory;
/**
* RFC 5545 languageparam
* @var string $language
*/
private $language;
/**
* @param Formatter $formatter
*/
public function __construct(Formatter $formatter)
{
$this->formatter = $formatter;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* RFC 5545 cal-address http://tools.ietf.org/html/rfc5545#section-3.3.3
* @param string $uri
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setValue($uri)
{
$this->value = $this->formatter->getFormattedUri($uri);
return $this;
}
/**
* @return string
*/
public function getCalendarUserType()
{
return $this->calendarUserType;
}
/**
* RFC 5545 cutypeparam http://tools.ietf.org/html/rfc5545#section-3.2.3
*
* @param string $calendarUserType
* "INDIVIDUAL" ; An individual<br>
* "GROUP" ; A group of individuals<br>
* "RESOURCE" ; A physical resource<br>
* "ROOM" ; A room resource<br>
* "UNKNOWN" ; Otherwise not known<br>
* x-name ; Experimental type<br>
* iana-token) ; Other IANA-registered type
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setCalendarUserType($calendarUserType)
{
$this->calendarUserType = $calendarUserType;
return $this;
}
/**
* @return array
*/
public function getCalendarMembers()
{
return $this->calendarMembers;
}
/**
* RFC 5545 memberparam http://tools.ietf.org/html/rfc5545#section-3.2.11
* @param array $calendarMemberUris array of uri values for calendar users ex. array('sue@example.com', 'joe@example.com')
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setCalendarMembers($calendarMemberUris)
{
foreach ($calendarMemberUris as &$uri)
{
$uri = $this->formatter->getFormattedUri($uri);
}
$this->calendarMembers = $calendarMemberUris;
return $this;
}
/**
* RFC 5545 memberparam http://tools.ietf.org/html/rfc5545#section-3.2.11
* @param string $uri
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function addCalendarMember($uri)
{
$this->calendarMembers[] = $this->formatter->getFormattedUri($uri);
return $this;
}
/**
* @return string
*/
public function getRole()
{
return $this->role;
}
/**
* RFC 5545 roleparam http://tools.ietf.org/html/rfc5545#section-3.2.16
* @param string $role
* "CHAIR" ; Indicates chair of the calendar entity <br>
* "REQ-PARTICIPANT" ; Indicates a participant whose participation is required <br>
* "OPT-PARTICIPANT" ; Indicates a participant whose participation is optional <br>
* "NON-PARTICIPANT" ; Indicates a participant who is copied for information purposes only <br>
* x-name ; Experimental role
* iana-token ; Other IANA role
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* @return string
*/
public function getParticipationStatus()
{
return $this->participationStatus;
}
/**
* RFC 5545 partstatparam http://tools.ietf.org/html/rfc5545#section-3.2.12
* @param string $participationStatus
* Example values for an Event: <br>
* "NEEDS-ACTION" ; Event needs action <br>
* "ACCEPTED" ; Event accepted <br>
* "DECLINED" ; Event declined <br>
* "TENTATIVE" ; Event tentatively accepted <br>
* "DELEGATED" ; Event delegated
*
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setParticipationStatus($participationStatus)
{
$this->participationStatus = $participationStatus;
return $this;
}
/**
* @return string
*/
public function getRsvp()
{
return $this->rsvp;
}
/**
* RFC 5545 rsvpparam http://tools.ietf.org/html/rfc5545#section-3.2.17
* @param string $rsvp "TRUE" or "FALSE"
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setRsvp($rsvp)
{
$this->rsvp = $rsvp;
return $this;
}
/**
* @return array
*/
public function getDelegatedTo()
{
return $this->delegatedTo;
}
/**
* RFC 5545 deltoparam http://tools.ietf.org/html/rfc5545#section-3.2.5
* @param array $delegatedToUris array of uri values for calendar users ex. array('sue@example.com', 'joe@example.com')
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setDelegatedTo(array $delegatedToUris)
{
foreach ($delegatedToUris as &$uri)
{
$uri = $this->formatter->getFormattedUri($uri);
}
$this->delegatedTo = $delegatedToUris;
return $this;
}
/**
* @param string $uri uri value for calendar users ex. 'mary@example.com'
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function addDelegatedTo($uri)
{
$this->delegatedTo[] = $this->formatter->getFormattedUri($uri);
return $this;
}
/**
* @return array
*/
public function getDelegatedFrom()
{
return $this->delegatedFrom;
}
/**
* RFC 5545 delfromparam http://tools.ietf.org/html/rfc5545#section-3.2.4
* @param array $delegatedFromUris array of uri values for calendar users ex. array('sue@example.com', 'joe@example.com')
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setDelegatedFrom(array $delegatedFromUris)
{
foreach ($delegatedFromUris as &$uri)
{
$uri = $this->formatter->getFormattedUri($uri);
}
$this->delegatedFrom = $delegatedFromUris;
return $this;
}
/**
* @param string $uri uri value for calendar users ex. 'mary@example.com'
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function addDelegatedFrom($uri)
{
$this->delegatedFrom[] = $this->formatter->getFormattedUri($uri);
return $this;
}
/**
* @return string
*/
public function getSentBy()
{
return $this->sentBy;
}
/**
* RFC 5545 sentbyparam http://tools.ietf.org/html/rfc5545#section-3.2.18
* @param string $sentBy email address
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setSentBy($sentBy)
{
$this->sentBy = $sentBy;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getDirectory()
{
return $this->directory;
}
/**
* RFC 5545 dirparam http://tools.ietf.org/html/rfc5545#section-3.2.6
* @param string $directory uri directory entry associated with the calendar user
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setDirectory($uri)
{
$this->directory = $uri;
return $this;
}
/**
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* @param string $language RFC 1766 language identifier
* @return \Jsvrcek\ICS\Model\Relationship\Attendee
*/
public function setLanguage($language)
{
$this->language = $language;
return $this;
}
/**
* @return string
*/
public function __toString()
{
$string = 'ATTENDEE';
if ($this->calendarUserType)
$string .= ';CUTYPE='.$this->calendarUserType;
if (count($this->calendarMembers))
{
$string .= ';MEMBER="'.implode('","', $this->calendarMembers).'"';
}
if ($this->role)
$string .= ';ROLE='.$this->role;
if ($this->participationStatus)
$string .= ';PARTSTAT='.$this->participationStatus;
if ($this->rsvp)
$string .= ';RSVP='.$this->rsvp;
if (count($this->delegatedTo))
{
$string .= ';DELEGATED-TO="'.implode('","', $this->delegatedTo).'"';
}
if (count($this->delegatedFrom))
{
$string .= ';DELEGATED-FROM="'.implode('","', $this->delegatedFrom).'"';
}
if ($this->sentBy)
$string .= ';SENT-BY="'.$this->sentBy.'"';
if ($this->name)
$string .= ';CN='.$this->name;
if ($this->directory)
$string .= ';DIR="'.$this->directory.'"';
if ($this->language)
$string .= ';LANGUAGE='.$this->language;
$string .= ':'.$this->value;
return $string;
}
}
@@ -0,0 +1,166 @@
<?php
namespace Jsvrcek\ICS\Model\Relationship;
use Jsvrcek\ICS\Utility\Formatter;
class Organizer
{
/**
*
* @var Formatter
*/
private $formatter;
/**
* RFC 5545 cal-address
* @var string $value
*/
private $value;
/**
* RFC 5545 cnparam
* @var string $name
*/
private $name;
/**
* RFC 5545 dirparam
* @var string $directory
*/
private $directory;
/**
* RFC 5545 sentbyparam
* @var string $sentBy
*/
private $sentBy;
/**
* RFC 5545 languageparam
* @var string $language
*/
private $language;
/**
* @param Formatter $formatter
*/
public function __construct(Formatter $formatter)
{
$this->formatter = $formatter;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* RFC 5545 cal-address http://tools.ietf.org/html/rfc5545#section-3.3.3
* @param string $uri
* @return \Jsvrcek\ICS\Model\Relationship\Organizer
*/
public function setValue($uri)
{
$this->value = $this->formatter->getFormattedUri($uri);
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
* @return \Jsvrcek\ICS\Model\Relationship\Organizer
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getDirectory()
{
return $this->directory;
}
/**
* RFC 5545 dirparam http://tools.ietf.org/html/rfc5545#section-3.2.6
* @param string $directory uri directory entry associated with the calendar user
* @return \Jsvrcek\ICS\Model\Relationship\Organizer
*/
public function setDirectory($uri)
{
$this->directory = $uri;
return $this;
}
/**
* @return string
*/
public function getSentBy()
{
return $this->sentBy;
}
/**
* RFC 5545 sentbyparam http://tools.ietf.org/html/rfc5545#section-3.2.18
* @param string $sentBy email address
* @return \Jsvrcek\ICS\Model\Relationship\Organizer
*/
public function setSentBy($sentBy)
{
$this->sentBy = $sentBy;
return $this;
}
/**
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* @param string $language RFC 1766 language identifier
* @return \Jsvrcek\ICS\Model\Relationship\Organizer
*/
public function setLanguage($language)
{
$this->language = $language;
return $this;
}
public function __toString()
{
$string = 'ORGANIZER';
if ($this->sentBy)
$string .= ';SENT-BY="'.$this->sentBy.'"';
if ($this->name)
$string .= ';CN='.$this->name;
if ($this->directory)
$string .= ';DIR="'.$this->directory.'"';
if ($this->language)
$string .= ';LANGUAGE='.$this->language;
$string .= ':'.$this->value;
return $string;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace Jsvrcek\ICS\Utility;
class Formatter
{
const DATE_TIME = 'Ymd\THis';
const DATE_TIME_UTC = 'Ymd\THis\Z';
const DATE = 'Ymd';
/**
* @param \DateTime $dateTime
* @return string
*/
public function getFormattedDateTime(\DateTime $dateTime)
{
return $dateTime->format(self::DATE_TIME);
}
/**
* @param int $offset
* @return string
*/
public function getFormattedTimeOffset($offset)
{
$prefix = ($offset < 0) ? '-' : '+';
return $prefix.gmdate('Hi', abs($offset));
}
/**
* @param \DateTime $dateTime
* @return string
*/
public function getFormattedUTCDateTime(\DateTime $dateTime)
{
return $dateTime->setTimezone(new \DateTimeZone('UTC'))
->format(self::DATE_TIME_UTC);
}
/**
* @param \DateTime $dateTime
* @return string
*/
public function getFormattedDate(\DateTime $dateTime)
{
return $dateTime->format(self::DATE);
}
/**
* converts email addresses into mailto: uri
* @param string $uri
* @return string
*/
public function getFormattedUri($uri)
{
if (strpos($uri, '@') && stripos($uri, 'mailto:') === false)
$uri = 'mailto:'.$uri;
return $uri;
}
/**
* converts DateInterval object to string that can be used for a VALARM DURATION
* @param \DateInterval $interval
* @return string
*/
public function getFormattedDateInterval(\DateInterval $interval)
{
$format = "P";
if ($interval->y) { $format .= '%yY'; }
if ($interval->m) { $format .= '%mM'; }
if ($interval->d) { $format .= '%dD'; }
if ($interval->h || $interval->i || $interval->s) {
$format .= "T";
}
if ($interval->h) { $format .= '%hH'; }
if ($interval->i) { $format .= '%iM'; }
if ($interval->s) { $format .= '%sS'; }
return $interval->format($format);
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
namespace Jsvrcek\ICS\Utility;
class Provider implements \Iterator
{
/**
*
* @var \Closure
*/
private $provider;
/**
*
* @var array
*/
public $data = array();
/**
*
* @var array
*/
public $manuallyAddedData = array();
/**
*
* @var integer
*/
private $key;
/**
* @param \Closure $provider An optional closure for adding items in batches during iteration. The closure will be
* called each time the end of the internal data array is reached during iteration, and the current data
* array key value will be passed as an argument. The closure should return an array containing the next
* batch of items.
*/
public function __construct(\Closure $provider = null)
{
$this->provider = $provider;
}
/**
* for manually adding items, rather than using a provider closure to add items in batches during iteration
* Cannot be used in conjunction with a provider closure!
*
* @param mixed $item
*/
public function add($item)
{
$this->manuallyAddedData[] = $item;
}
/* (non-PHPdoc)
* @see Iterator::current()
*/
public function current()
{
return current($this->data);
}
/* (non-PHPdoc)
* @see Iterator::key()
*/
public function key()
{
return $this->key;
}
/* (non-PHPdoc)
* @see Iterator::next()
*/
public function next()
{
array_shift($this->data);
$this->key++;
}
/* (non-PHPdoc)
* @see Iterator::rewind()
*/
public function rewind()
{
$this->data = array();
$this->key = 0;
}
/**
* get next batch from provider if data array is at the end
*
* (non-PHPdoc)
* @see Iterator::valid()
*/
public function valid()
{
if (count($this->data) < 1)
{
if ($this->provider instanceof \Closure)
{
$this->data = $this->provider->__invoke($this->key);
}
else
{
$this->data = $this->manuallyAddedData;
$this->manuallyAddedData = array();
}
}
return count($this->data) > 0;
}
}
+154
View File
@@ -0,0 +1,154 @@
<?php
namespace Jsvrcek\ICS\Tests;
use Jsvrcek\ICS\Model\CalendarAlarm;
use Jsvrcek\ICS\Model\Recurrence\DataType\Weekday;
use Jsvrcek\ICS\Model\Recurrence\DataType\WeekdayNum;
use Jsvrcek\ICS\Model\Recurrence\DataType\Frequency;
use Jsvrcek\ICS\Model\Recurrence\RecurrenceRule;
use Jsvrcek\ICS\Model\Relationship\Organizer;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\Model\Relationship\Attendee;
use Jsvrcek\ICS\CalendarExport;
use Jsvrcek\ICS\Model\Calendar;
use Jsvrcek\ICS\Model\CalendarEvent;
class CalendarExportTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Jsvrcek\ICS\CalendarExport::getStream
*/
public function testGetStream()
{
$timezone = new \DateTimeZone('Antarctica/McMurdo');
$organizer = new Organizer(new Formatter());
$organizer->setValue('sue@example.com')
->setName('Sue Jones')
->setSentBy('mary@example.com')
->setLanguage('en');
$attendee = new Attendee(new Formatter());
$attendee->setName('Jane Smith')
->setCalendarUserType('INDIVIDUAL')
->setParticipationStatus('ACCEPTED')
->setRole('REQ-PARTICIPANT')
->setSentBy('joe@example')
->addCalendarMember('list@example.com')
->setValue('jane-smith@example.com');
$event = new CalendarEvent();
$event->setUid('lLKjd89283oja89282lkjd8@example.com')
->setStart(new \DateTime('4 October 2013 12:00:00', $timezone))
->setEnd(new \DateTime('4 October 2013 22:00:00', $timezone))
->setSummary('Poker night at the South Pole')
->addAttendee($attendee)
->setOrganizer($organizer)
->setSequence(3)
->setTimestamp(new \DateTime('1 September 2013', $timezone));
$rrule = new RecurrenceRule(new Formatter());
$rrule->setFrequency(new Frequency(Frequency::MONTHLY))
->setInterval(2)
->setCount(40)
->addByDay(new WeekdayNum(Weekday::SATURDAY, 1))
->addByDay(new WeekdayNum(Weekday::SATURDAY, 2))
->addByDay(new WeekdayNum(Weekday::SATURDAY, 3))
->addByDay(new WeekdayNum(Weekday::SATURDAY, 4))
->addByDay(new WeekdayNum(Weekday::FRIDAY, 1));
$event->setRecurrenceRule($rrule);
//add an alarms to this event
$alarmAudio = new CalendarAlarm();
$alarmAudio->setAction("audio");
$alarmAudio->setTrigger($event->getStart());
$alarmAudio->addAttachment("FMTTYPE=audio/basic:ftp://example.com/pub/sounds/bell-01.aud");
$event->addAlarm($alarmAudio);
$alarmDisplay = new CalendarAlarm();
$alarmDisplay->setAction("display");
$alarmDisplay->setTrigger($event->getStart());
$alarmDisplay->setRepeat(3);
$alarmDisplay->setDuration(new \DateInterval('PT15M'));
$alarmDisplay->setDescription("DESCRIPTION");
$event->addAlarm($alarmDisplay);
$alarmEmail = new CalendarAlarm();
$alarmEmail->setAction('email');
$alarmEmail->setTrigger($event->getStart());
$alarmEmail->addAttendee($attendee);
$alarmEmail->setSummary("EMAIL SUBJECT");
$alarmEmail->setDescription("EMAIL BODY");
$alarmEmail->addAttachment("FMTTYPE=application/msword:http://example.com/agenda.docx");
$alarmEmail->addAttachment("FMTTYPE=application/pdf:http://example.com/agenda.pdf");
$event->addAlarm($alarmEmail);
//test exception dates
$eventTwo = new CalendarEvent();
$eventTwo->setUid('eventtwo@example.com')
->setStart(new \DateTime('2 October 2013', $timezone))
->setSummary('Every Wednesday event')
->setTimestamp(new \DateTime('1 September 2013', $timezone));
$rrule = new RecurrenceRule(new Formatter());
$rrule->setFrequency(new Frequency(Frequency::WEEKLY));
$eventTwo->setRecurrenceRule($rrule);
//add exception dates to the event recurrence
$eventTwo->addExceptionDate(new \DateTime('16 October 2013', $timezone))
->addExceptionDate(new \DateTime('30 October 2013', $timezone));
$cal = new Calendar();
$cal->setProdId('-//Jsvrcek//ICS//EN')
->setTimezone($timezone)
->addEvent($event)
->addEvent($eventTwo);
//create second calendar using batch event provider
$timezone = new \DateTimeZone('Arctic/Longyearbyen');
$calTwo = new Calendar();
$calTwo->setProdId('-//Jsvrcek//ICS//EN2')
->setTimezone($timezone);
$calTwo->setEventsProvider(function($start) use ($timezone){
$eventOne = new CalendarEvent();
$eventOne->setUid('asdfasdf@example.com')
->setStart(new \DateTime('2016-01-01 01:01:01', $timezone))
->setEnd(new \DateTime('2016-01-02 01:01:01', $timezone))
->setSummary('A long day')
->setTimestamp(new \DateTime('1 September 2013', $timezone));
$eventTwo = new CalendarEvent();
$eventTwo->setUid('asdfasdf@example.com')
->setStart(new \DateTime('2016-01-02 01:01:01', $timezone))
->setEnd(new \DateTime('2016-01-03 01:01:01', $timezone))
->setSummary('Another long day')
->setTimestamp(new \DateTime('1 September 2013', $timezone));
return ($start > 0) ? array() : array($eventOne, $eventTwo);
});
$ce = new CalendarExport(new CalendarStream(), new Formatter());
$ce->addCalendar($cal)
->addCalendar($calTwo);
$stream = $ce->getStream();
//file_put_contents(__DIR__.'/test.ics', $stream);
$expected = file_get_contents(__DIR__.'/test.ics');
$this->assertEquals($expected, $stream);
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace Jsvrcek\ICS\Tests;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\Constants;
class CalendarStreamTest extends \PHPUnit_Framework_TestCase
{
public function testGetStream()
{
$s = new CalendarStream();
$this->assertEquals('', $s->getStream());
}
public function testAddItemToStream()
{
//simple test
$s = new CalendarStream();
$item = 'TEST';
$expected = 'TEST'.Constants::CRLF;
$s->addItem($item);
$this->assertEquals($expected, $s->getStream());
//long string test
$s = new CalendarStream();
$item = ' aaaaaaa10 aaaaaaa20 aaaaaaa30 aaaaaaa40 aaaaaaa50 aaaaaaa60 aaaaaaa70 aaaaaaa80 aaaaaaa90';
$expected = ' aaaaaaa10 aaaaaaa20 aaaaaaa30 aaaaaaa40 aaaaaaa50 aaaaaaa60 aaaaaaa70'.Constants::CRLF.' '.' aaaaaaa80 aaaaaaa90'.Constants::CRLF;
$s->addItem($item);
$this->assertEquals($expected, $s->getStream());
$s = new CalendarStream();
$item = ' ëëëë1 ëëëë2 ëëëë3 ëëëë4 ëëëë5 ëëëë6 ëëëëëëëëë8 ëëëë9';
$expected = ' ëëëë1 ëëëë2 ëëëë3 ëëëë4 ëëëë5 ëëëë6 ëëëë'.Constants::CRLF.' '.'ëëëëë8 ëëëë9'.Constants::CRLF;
$s->addItem($item);
$this->assertEquals($expected, $s->getStream());
}
public function testReset()
{
$s = new CalendarStream();
$item = 'TEST';
$s->addItem($item);
$s->reset();
$this->assertEquals('', $s->getStream());
}
public function test__toString()
{
$s = new CalendarStream();
$item = 'TEST';
$s->addItem($item);
$expected = 'TEST'.Constants::CRLF;
$this->assertEquals($expected, $s->__toString());
}
}
@@ -0,0 +1,77 @@
<?php
namespace Jsvrcek\ICS\Tests\Model;
use Jsvrcek\ICS\Model\CalendarEvent;
use Jsvrcek\ICS\Exception\CalendarEventException;
/**
* Generated by PHPUnit_SkeletonGenerator on 2013-10-20 at 12:03:54.
*/
class CalendarEventTest extends \PHPUnit_Framework_TestCase
{
/**
* @var CalendarEvent
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
* @return CalendarEvent
*/
protected function setUp()
{
$this->object = new CalendarEvent;
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* @covers Jsvrcek\ICS\Model\CalendarEvent::setStart
*/
public function testSetStart()
{
$start = new \DateTime();
$this->object->setStart($start);
// test start
$this->assertEquals($start, $this->object->getStart());
//test default end
$this->assertEquals($start->add(\DateInterval::createFromDateString('30 minutes')), $this->object->getEnd());
}
/**
* @covers Jsvrcek\ICS\Model\CalendarEvent::setEnd
* @expectedException Jsvrcek\ICS\Exception\CalendarEventException
* @expectedExceptionMessage End DateTime must be greater than Start DateTime
*/
public function testSetEndDateEarlierThanStart()
{
$start = new \DateTime('now');
$end = new \DateTime('yesterday');
$this->object->setStart($start);
$this->object->setEnd($end);
}
/**
* @covers Jsvrcek\ICS\Model\CalendarEvent::setEnd
* @expectedException Jsvrcek\ICS\Exception\CalendarEventException
* @expectedExceptionMessage You must set the Start time before setting the End Time of a CalendarEvent
*/
public function testSetEndWithoutStart()
{
$end = new \DateTime('yesterday');
$this->object->setEnd($end);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace Jsvrcek\ICS\Tests\Model;
use Jsvrcek\ICS\Model\Calendar;
/**
* Generated by PHPUnit_SkeletonGenerator on 2013-10-20 at 15:24:14.
*/
class CalendarTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Jsvrcek\ICS\Model\Calendar::__construct
*/
public function testConstruct()
{
$c = new Calendar();
//test defaults
$this->assertEquals('America/New_York', $c->getTimezone()->getName());
}
}
@@ -0,0 +1,83 @@
<?php
namespace Jsvrcek\ICS\Tests\Model\Recurrence;
use Jsvrcek\ICS\Model\Recurrence\DataType\Weekday;
use Jsvrcek\ICS\Model\Recurrence\DataType\WeekdayNum;
use Jsvrcek\ICS\Model\Recurrence\DataType\Frequency;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\Model\Recurrence\RecurrenceRule;
class RecurrenceRuleTest extends \PHPUnit_Framework_TestCase
{
/**
* @var RecurrenceRule
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
* @return Rule
*/
protected function setUp()
{
$this->object = new RecurrenceRule(new Formatter());
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* @covers Jsvrcek\ICS\Model\Recurrence\RecurrentRule::__toString()
*/
public function testToString()
{
$this->object
->setFrequency(new Frequency(Frequency::YEARLY))
->setInterval(2)
->setCount(10)
->addByDay(new WeekdayNum(Weekday::MONDAY, 10, WeekdayNum::COUNT_FROM_START))
->addByDay(new WeekdayNum(Weekday::TUESDAY, 10, WeekdayNum::COUNT_FROM_END))
->setUntil(new \DateTime('2050-01-01 00:00:00', new \DateTimeZone('UTC')))
;
$expected = 'RRULE:FREQ=YEARLY;INTERVAL=2;UNTIL=20500101T000000Z;COUNT=10;BYDAY=10MO,-10TU';
$this->assertEquals($expected, $this->object->__toString());
}
/**
* @covers Jsvrcek\ICS\Model\Recurrence\RecurrentRule::parse()
* @depends testToString
*/
public function testParse()
{
//reset instance
$this->setUp();
$rRuleString = 'RRULE:FREQ=YEARLY;INTERVAL=2';
$this->object->parse($rRuleString);
$this->assertEquals($rRuleString, $this->object->__toString());
$rRuleString = 'RRULE:FREQ=WEEKLY;INTERVAL=4';
$this->object->parse($rRuleString);
$this->assertEquals($rRuleString, $this->object->__toString());
$rRuleString = 'RRULE:FREQ=WEEKLY;INTERVAL=4;UNTIL=20500101T000000Z';
$this->object->parse($rRuleString);
$this->assertEquals($rRuleString, $this->object->__toString());
/*
$rRuleString = 'RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYDAY=10MO,-10TU';
$this->object->parse($rRuleString);
$this->assertEquals($rRuleString, $this->object->__toString());
*/
}
}
@@ -0,0 +1,56 @@
<?php
namespace Jsvrcek\ICS\Tests\Model\Relationship;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\Model\Relationship\Attendee;
class AttendeeTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Attendee
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
* @return Attendee
*/
protected function setUp()
{
$this->object = new Attendee(new Formatter());
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* @covers Jsvrcek\ICS\Model\Attendee::__toString()
*/
public function testToString()
{
$this->object
->setValue('joe-smith@example.com')
->setName('Joe Smith')
->setCalendarUserType('INDIVIDUAL')
->addCalendarMember('list@example.com')
->addDelegatedFrom('mary@example.com')
->addDelegatedTo('sue@example.com')
->addDelegatedTo('jane@example.com')
->setParticipationStatus('ACCEPTED')
->setRole('REQ-PARTICIPANT')
->setRsvp('TRUE')
->setSentBy('jack@example.com')
->setLanguage('en');
$expected = 'ATTENDEE;CUTYPE=INDIVIDUAL;MEMBER="mailto:list@example.com";ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;RSVP=TRUE;DELEGATED-TO="mailto:sue@example.com","mailto:jane@example.com";DELEGATED-FROM="mailto:mary@example.com";SENT-BY="jack@example.com";CN=Joe Smith;LANGUAGE=en:mailto:joe-smith@example.com';
$this->assertEquals($expected, $this->object->__toString());
}
}
@@ -0,0 +1,49 @@
<?php
namespace Jsvrcek\ICS\Tests\Model\Relationship;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\Model\Relationship\Organizer;
class OrganizerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Organizer
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
* @return Organizer
*/
protected function setUp()
{
$this->object = new Organizer(new Formatter());
}
/**
* Tears down the fixture, for example, closes a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}
/**
* @covers Jsvrcek\ICS\Model\Organizer::__toString()
*/
public function testToString()
{
$this->object
->setValue('leonardo@example.it')
->setName('Leonardo Da Vinci')
->setLanguage('it')
->setDirectory('http://en.wikipedia.org/wiki/Leonardo_da_Vinci')
->setSentBy('piero@example.com');
$expected = 'ORGANIZER;SENT-BY="piero@example.com";CN=Leonardo Da Vinci;DIR="http://en.wikipedia.org/wiki/Leonardo_da_Vinci";LANGUAGE=it:mailto:leonardo@example.it';
$this->assertEquals($expected, $this->object->__toString());
}
}
@@ -0,0 +1,98 @@
<?php
namespace Jsvrcek\ICS\Tests\Utility;
use Jsvrcek\ICS\Utility\Formatter;
class FormatterTest extends \PHPUnit_Framework_TestCase
{
/**
* @covers Jsvrcek\ICS\Formatter::getFormattedDateTime
*/
public function testGetFormattedDateTime()
{
$ce = new Formatter();
$dateTime = new \DateTime('1998-01-18 23:00:00');
$expected = '19980118T230000';
$actual = $ce->getFormattedDateTime($dateTime);
$this->assertEquals($expected, $actual);
}
/**
* @covers Jsvrcek\ICS\Formatter::getFormattedTimeOffset
*/
public function testGetFormattedTimeOffset()
{
$ce = new Formatter();
$offset = -18000;
$expected = '-0500';
$actual = $ce->getFormattedTimeOffset($offset);
$this->assertEquals($expected, $actual);
$offset = -14400;
$expected = '-0400';
$actual = $ce->getFormattedTimeOffset($offset);
$this->assertEquals($expected, $actual);
$offset = 14400;
$expected = '+0400';
$actual = $ce->getFormattedTimeOffset($offset);
$this->assertEquals($expected, $actual);
}
/**
* @covers Jsvrcek\ICS\Formatter::getFormattedUTCDateTime
*/
public function testGetFormattedUTCDateTime()
{
$ce = new Formatter();
$dateTime = new \DateTime('1998-01-18 23:00:00', new \DateTimeZone('America/New_York'));
$expected = '19980119T040000Z';
$actual = $ce->getFormattedUTCDateTime($dateTime);
$this->assertEquals($expected, $actual);
$ce = new Formatter();
$dateTime = new \DateTime('1998-01-18 11:00:00', new \DateTimeZone('America/New_York'));
$expected = '19980118T160000Z';
$actual = $ce->getFormattedUTCDateTime($dateTime);
$this->assertEquals($expected, $actual);
}
/**
* @covers Jsvrcek\ICS\Formatter::getFormattedUri
*/
public function testGetFormattedUri()
{
$ce = new Formatter();
$expected = 'mailto:test@example.com';
$actual = $ce->getFormattedUri('test@example.com');
$this->assertEquals($expected, $actual);
}
/**
* @covers Jsvrcek\ICS\Formatter::getFormattedDateInterval
*/
public function testGetFormattedDateInterval()
{
$ce = new Formatter();
$tests = array(
"PT15M",
"PT1H",
"P345D",
"P1Y6M29DT4H34M23S"
);
foreach ($tests as $test) {
$this->assertEquals(
$test,
$ce->getFormattedDateInterval(new \DateInterval($test)),
$test
);
}
}
}
+97
View File
@@ -0,0 +1,97 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Jsvrcek//ICS//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:Antarctica/McMurdo
BEGIN:STANDARD
DTSTART:19700101T000000
TZOFFSETTO:+1200
TZOFFSETFROM:+1200
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:lLKjd89283oja89282lkjd8@example.com
DTSTART:20131004T120000
DTEND:20131004T220000
RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=40;BYDAY=1SA,2SA,3SA,4SA,1FR
SEQUENCE:3
STATUS:
SUMMARY:Poker night at the South Pole
DESCRIPTION:
DTSTAMP:20130831T120000Z
ATTENDEE;CUTYPE=INDIVIDUAL;MEMBER="mailto:list@example.com";ROLE=REQ-P
ARTICIPANT;PARTSTAT=ACCEPTED;SENT-BY="joe@example";CN=Jane Smith:mailt
o:jane-smith@example.com
ORGANIZER;SENT-BY="mary@example.com";CN=Sue Jones;LANGUAGE=en:mailto:s
ue@example.com
BEGIN:VALARM
TRIGGER;VALUE=DATE-TIME:20131003T230000Z
ACTION:AUDIO
ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/sounds/bell-01.aud
END:VALARM
BEGIN:VALARM
TRIGGER;VALUE=DATE-TIME:20131003T230000Z
ACTION:DISPLAY
REPEAT:3
DURATION:PT15M
DESCRIPTION:DESCRIPTION
END:VALARM
BEGIN:VALARM
TRIGGER;VALUE=DATE-TIME:20131003T230000Z
ACTION:EMAIL
SUMMARY:EMAIL SUBJECT
DESCRIPTION:EMAIL BODY
ATTENDEE;CUTYPE=INDIVIDUAL;MEMBER="mailto:list@example.com";ROLE=REQ-P
ARTICIPANT;PARTSTAT=ACCEPTED;SENT-BY="joe@example";CN=Jane Smith:mailt
o:jane-smith@example.com
ATTACH;FMTTYPE=application/msword:http://example.com/agenda.docx
ATTACH;FMTTYPE=application/pdf:http://example.com/agenda.pdf
END:VALARM
END:VEVENT
BEGIN:VEVENT
UID:eventtwo@example.com
DTSTART:20131002T000000
DTEND:20131002T003000
RRULE:FREQ=WEEKLY
EXDATE:20131016T000000
EXDATE:20131030T000000
STATUS:
SUMMARY:Every Wednesday event
DESCRIPTION:
DTSTAMP:20130831T120000Z
END:VEVENT
END:VCALENDAR
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Jsvrcek//ICS//EN2
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:Arctic/Longyearbyen
BEGIN:STANDARD
DTSTART:19700101T000000
TZOFFSETTO:+0100
TZOFFSETFROM:+0100
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:asdfasdf@example.com
DTSTART:20160101T010101
DTEND:20160102T010101
STATUS:
SUMMARY:A long day
DESCRIPTION:
DTSTAMP:20130831T220000Z
END:VEVENT
BEGIN:VEVENT
UID:asdfasdf@example.com
DTSTART:20160102T010101
DTEND:20160103T010101
STATUS:
SUMMARY:Another long day
DESCRIPTION:
DTSTAMP:20130831T220000Z
END:VEVENT
END:VCALENDAR