794 lines
14 KiB
PHP
794 lines
14 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace davigil\helpers\ics;
|
||
|
|
|
||
|
|
require_once('vendor/autoload.php');
|
||
|
|
require_once('helpers/string.php');
|
||
|
|
require_once('helpers/pit.php');
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_rrule
|
||
|
|
{
|
||
|
|
public ?string $freq;
|
||
|
|
public ?string $byday;
|
||
|
|
public ?string $bymonth;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
// type type_offset = string;
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
// type type_timestamp = string;
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
enum enum_class
|
||
|
|
{
|
||
|
|
case public_/* = 'public'*/;
|
||
|
|
case private_/* = 'private'*/;
|
||
|
|
case confidential/* = 'confidential'*/;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
enum enum_event_status
|
||
|
|
{
|
||
|
|
case tentative/* = 'tentative'*/;
|
||
|
|
case confirmed/* = 'confirmed'*/;
|
||
|
|
case cancelled/* = 'cancelled'*/;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
enum enum_transp
|
||
|
|
{
|
||
|
|
case opaque/* = 'opaque'*/;
|
||
|
|
case transparent/* = 'transparent'*/;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
// type type_tzid = string;
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_date
|
||
|
|
{
|
||
|
|
public int $year;
|
||
|
|
public int $month;
|
||
|
|
public int $day;
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
int $year,
|
||
|
|
int $month,
|
||
|
|
int $day,
|
||
|
|
)
|
||
|
|
{
|
||
|
|
$this->year = $year;
|
||
|
|
$this->month = $month;
|
||
|
|
$this->day = $day;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_time
|
||
|
|
{
|
||
|
|
public int $hour;
|
||
|
|
public int $minute;
|
||
|
|
public int $second;
|
||
|
|
public bool $utc;
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
int $hour,
|
||
|
|
int $minute,
|
||
|
|
int $second,
|
||
|
|
bool $utc
|
||
|
|
)
|
||
|
|
{
|
||
|
|
$this->hour = $hour;
|
||
|
|
$this->minute = $minute;
|
||
|
|
$this->second = $second;
|
||
|
|
$this->utc = $utc;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_datetime
|
||
|
|
{
|
||
|
|
public struct_date $date;
|
||
|
|
public ?struct_time $time;
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
struct_date $date,
|
||
|
|
?struct_time $time
|
||
|
|
)
|
||
|
|
{
|
||
|
|
$this->date = $date;
|
||
|
|
$this->time = $time;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_dt
|
||
|
|
{
|
||
|
|
public /*type_tzid*/string $tzid;
|
||
|
|
public struct_datetime $value;
|
||
|
|
|
||
|
|
public function __construct(
|
||
|
|
string $tzid,
|
||
|
|
struct_datetime $value
|
||
|
|
)
|
||
|
|
{
|
||
|
|
$this->tzid = $tzid;
|
||
|
|
$this->value = $value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_duration
|
||
|
|
{
|
||
|
|
public bool $negative;
|
||
|
|
public ?int $weeks;
|
||
|
|
public ?int $days;
|
||
|
|
public ?int $hours;
|
||
|
|
public ?int $minutes;
|
||
|
|
public ?int $seconds;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_vtimezone_entry
|
||
|
|
{
|
||
|
|
public struct_datetime $dtstart;
|
||
|
|
public struct_rrule $rrule;
|
||
|
|
public ?/*type_offset*/string $tzoffsetfrom;
|
||
|
|
public ?/*type_offset*/string $tzoffsetto;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_vtimezone
|
||
|
|
{
|
||
|
|
public ?string/*type_tzid*/ $tzid;
|
||
|
|
public ?struct_vtimezone_entry $standard;
|
||
|
|
public ?struct_vtimezone_entry $daylight;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_geo
|
||
|
|
{
|
||
|
|
public float $latitude;
|
||
|
|
public float $longitude;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
class struct_organizer
|
||
|
|
{
|
||
|
|
public ?string $value;
|
||
|
|
public ?string $cn;
|
||
|
|
public ?string $dir;
|
||
|
|
public ?string $sent_by;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @see https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1
|
||
|
|
*/
|
||
|
|
class struct_vevent
|
||
|
|
{
|
||
|
|
// required
|
||
|
|
public string $uid;
|
||
|
|
public struct_datetime $dtstamp;
|
||
|
|
|
||
|
|
// required if "method" is not specified in the parent
|
||
|
|
public ?struct_dt $dtstart;
|
||
|
|
|
||
|
|
// optional
|
||
|
|
public ?enum_class $class;
|
||
|
|
public ?struct_datetime $created;
|
||
|
|
public ?string $description;
|
||
|
|
public ?struct_geo $geo;
|
||
|
|
public ?struct_datetime $last_modified;
|
||
|
|
public ?string $location;
|
||
|
|
/**
|
||
|
|
* @see https://www.rfc-editor.org/rfc/rfc5545#section-3.8.4.3
|
||
|
|
*/
|
||
|
|
public ?struct_organizer $organizer;
|
||
|
|
public ?int $priority;
|
||
|
|
public ?int $sequence;
|
||
|
|
public ?enum_event_status $status;
|
||
|
|
public ?string $summary;
|
||
|
|
public ?enum_transp $transp;
|
||
|
|
public ?string $url;
|
||
|
|
public $recurid;
|
||
|
|
public ?type_rrule $rrule;
|
||
|
|
|
||
|
|
// either or
|
||
|
|
public ?struct_dt $dtend;
|
||
|
|
public ?type_duration $duration;
|
||
|
|
|
||
|
|
// optional
|
||
|
|
public $attach;
|
||
|
|
public ?string $attendee;
|
||
|
|
public /*list<string>*/?array $categories;
|
||
|
|
public $comment;
|
||
|
|
public $contact;
|
||
|
|
public $exdate;
|
||
|
|
public $rstatus;
|
||
|
|
public $related;
|
||
|
|
public $resources;
|
||
|
|
public $rdate;
|
||
|
|
|
||
|
|
// extra
|
||
|
|
public /*map<string,string>*/?array $x_props;
|
||
|
|
public /*map<string,string>*/?array $iana_props;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @see https://www.rfc-editor.org/rfc/rfc5545#section-3.4
|
||
|
|
*/
|
||
|
|
class struct_vcalendar
|
||
|
|
{
|
||
|
|
// required
|
||
|
|
public string $version;
|
||
|
|
public string $prodid;
|
||
|
|
public /*list<struct_event>*/array $vevents;
|
||
|
|
|
||
|
|
// optional
|
||
|
|
public ?string $calscale;
|
||
|
|
public ?string $method;
|
||
|
|
public ?struct_vtimezone $vtimezone;
|
||
|
|
|
||
|
|
// extra
|
||
|
|
public /*map<string,string>*/?array $x_props;
|
||
|
|
public /*map<string,string>*/?array $iana_props;
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function date_decode(
|
||
|
|
string $date_encoded
|
||
|
|
) : struct_date
|
||
|
|
{
|
||
|
|
return (new struct_date(
|
||
|
|
\intval(\substr($date_encoded, 0, 4)),
|
||
|
|
\intval(\substr($date_encoded, 4, 2)),
|
||
|
|
\intval(\substr($date_encoded, 6, 2))
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function time_decode(
|
||
|
|
string $time_encoded
|
||
|
|
) : struct_time
|
||
|
|
{
|
||
|
|
return (new struct_time(
|
||
|
|
\intval(\substr($time_encoded, 0, 2)),
|
||
|
|
\intval(\substr($time_encoded, 2, 2)),
|
||
|
|
\intval(\substr($time_encoded, 4, 2)),
|
||
|
|
((\strlen($time_encoded >= 7) && ($time_encoded[6] === 'Z')))
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function datetime_decode(
|
||
|
|
string $datetime_encoded
|
||
|
|
) : struct_datetime
|
||
|
|
{
|
||
|
|
$parts = \explode('T', $datetime_encoded, 2);
|
||
|
|
return (new struct_datetime(
|
||
|
|
date_decode($parts[0]),
|
||
|
|
((\count($parts) >= 2) ? time_decode($parts[1]) : null)
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
enum enum_decode_state_label {
|
||
|
|
case expect_vcalendar_begin/* = "expect_vcalendar_begin"*/;
|
||
|
|
case expect_vcalendar_property/* = "expect_vcalendar_property"*/;
|
||
|
|
case expect_vevent_property/* = "expect_vevent_property"*/;
|
||
|
|
case done/* = "done"*/;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function class_encode(
|
||
|
|
enum_class $class
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
switch ($class)
|
||
|
|
{
|
||
|
|
case enum_class::private_: {return 'PRIVATE'; break;}
|
||
|
|
case enum_class::public_: {return 'PUBLIC'; break;}
|
||
|
|
case enum_class::confidential: {return 'CONFIDENTIAL'; break;}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function class_decode(
|
||
|
|
string $class_encoded
|
||
|
|
) : enum_class
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'PRIVATE' => enum_class::private_,
|
||
|
|
'PUBLIC' => enum_class::public_,
|
||
|
|
'CONFIDENTIAL' => enum_class::confidential,
|
||
|
|
][$class_encoded];
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function event_status_encode(
|
||
|
|
enum_event_status $event_status
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
switch ($event_status)
|
||
|
|
{
|
||
|
|
case enum_event_status::tentative: {return 'TENTATIVE'; break;}
|
||
|
|
case enum_event_status::confirmed: {return 'CONFIRMED'; break;}
|
||
|
|
case enum_event_status::cancelled: {return 'CANCELLED'; break;}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function event_status_decode(
|
||
|
|
string $event_status_encoded
|
||
|
|
) : enum_event_status
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'TENTATIVE' => enum_event_status::tentative,
|
||
|
|
'CONFIRMED' => enum_event_status::confirmed,
|
||
|
|
'CANCELLED' => enum_event_status::cancelled,
|
||
|
|
][$event_status_encoded];
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function transp_encode(
|
||
|
|
enum_transp $transp
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
switch (transp)
|
||
|
|
{
|
||
|
|
case enum_transp::opaque: {return 'OPAQUE'; break;}
|
||
|
|
case enum_transp::transparent: {return 'TRANSPARENT'; break;}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function transp_decode(
|
||
|
|
string $transp_encoded
|
||
|
|
) : enum_transp
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'OPAQUE' => enum_transp::opaque,
|
||
|
|
'TRANSPARENT' => enum_transp::transparent,
|
||
|
|
][$transp_encoded];
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
/*
|
||
|
|
function datetime_to_unixtimestamp(
|
||
|
|
struct_datetime $datetime
|
||
|
|
) : int
|
||
|
|
{
|
||
|
|
if (($datetime->time !== null) && (! $datetime->time->utc))
|
||
|
|
{
|
||
|
|
throw (new \Exception('can not convert not utc time values'));
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return lib_plankton.pit.from_datetime(
|
||
|
|
{
|
||
|
|
"timezone_shift": 0,
|
||
|
|
"date": {
|
||
|
|
"year": datetime.date.year,
|
||
|
|
"month": datetime.date.month,
|
||
|
|
"day": datetime.date.day,
|
||
|
|
},
|
||
|
|
"time": {
|
||
|
|
"hour": ((datetime.time === null) ? 0 : datetime.time.hour),
|
||
|
|
"minute": ((datetime.time === null) ? 0 : datetime.time.minute),
|
||
|
|
"second": ((datetime.time === null) ? 0 : datetime.time.second),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function vcalendar_decode(
|
||
|
|
string $ics
|
||
|
|
) : struct_vcalendar
|
||
|
|
{
|
||
|
|
$path = \sprintf('/tmp/foo.ics');
|
||
|
|
\file_put_contents($path, $ics);
|
||
|
|
$ical = new \ICal\ICal(
|
||
|
|
$path,
|
||
|
|
[
|
||
|
|
'defaultSpan' => 2, // Default value
|
||
|
|
'defaultTimeZone' => 'UTC',
|
||
|
|
'defaultWeekStart' => 'MO', // Default value
|
||
|
|
'disableCharacterReplacement' => false, // Default value
|
||
|
|
'filterDaysAfter' => null, // Default value
|
||
|
|
'filterDaysBefore' => null, // Default value
|
||
|
|
'httpUserAgent' => null, // Default value
|
||
|
|
'skipRecurrence' => false, // Default value
|
||
|
|
]
|
||
|
|
);
|
||
|
|
// $ical->initFile($path);
|
||
|
|
/**
|
||
|
|
* @todo transform correctly
|
||
|
|
*/
|
||
|
|
$result = new struct_vcalendar();
|
||
|
|
$result->events = \array_map(
|
||
|
|
function ($event_raw) {
|
||
|
|
$vevent = new struct_vevent();
|
||
|
|
$vevent->uid = $event_raw->uid;
|
||
|
|
$vevent->summary = $event_raw->summary;
|
||
|
|
$vevent->dtstart = (new struct_dt(
|
||
|
|
'',
|
||
|
|
datetime_decode($event_raw->dtstart)
|
||
|
|
));
|
||
|
|
$vevent->dtend = (
|
||
|
|
($event_raw->dtend === null)
|
||
|
|
?
|
||
|
|
null
|
||
|
|
:
|
||
|
|
(new struct_dt(
|
||
|
|
'',
|
||
|
|
datetime_decode($event_raw->dtend)
|
||
|
|
))
|
||
|
|
);
|
||
|
|
$vevent->location = $event_raw->location;
|
||
|
|
$vevent->description = $event_raw->description;
|
||
|
|
$vevent->categories = \array_reduce(
|
||
|
|
\array_map(
|
||
|
|
fn($x) => \explode(',', $x),
|
||
|
|
\array_values(
|
||
|
|
\array_filter(
|
||
|
|
$event_raw->additionalProperties['categories_array'],
|
||
|
|
fn($x) => \is_string($x)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
),
|
||
|
|
fn($x, $y) => \array_merge($x, $y),
|
||
|
|
[]
|
||
|
|
);
|
||
|
|
return $vevent;
|
||
|
|
},
|
||
|
|
$ical->events()
|
||
|
|
);
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @see https://www.rfc-editor.org/rfc/rfc5545
|
||
|
|
* @see https://icalendar.org/iCalendar-RFC-5545/
|
||
|
|
*/
|
||
|
|
function date_encode(
|
||
|
|
struct_date $date
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
return \davigil\helpers\string_\coin(
|
||
|
|
'{{year}}{{month}}{{day}}',
|
||
|
|
[
|
||
|
|
'year' => \str_pad(\sprintf('%u', $date->year), 4, '0', \STR_PAD_LEFT),
|
||
|
|
'month' => \str_pad(\sprintf('%u', $date->month), 2, '0', \STR_PAD_LEFT),
|
||
|
|
'day' => \str_pad(\sprintf('%u', $date->day), 2, '0', \STR_PAD_LEFT),
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function time_encode(
|
||
|
|
struct_time $time
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
return \davigil\helpers\string_\coin(
|
||
|
|
'{{hour}}{{minute}}{{second}}{{utc}}',
|
||
|
|
[
|
||
|
|
'hour' => \str_pad(\sprintf('%u', $time->hour), 2, '0', \STR_PAD_LEFT),
|
||
|
|
'minute' => \str_pad(\sprintf('%u', $time->minute), 2, '0', \STR_PAD_LEFT),
|
||
|
|
'second' => \str_pad(\sprintf('%u', $time->second), 2, '0', \STR_PAD_LEFT),
|
||
|
|
'utc' => ($time->utc ? 'Z' : ''),
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function datetime_encode(
|
||
|
|
struct_datetime $datetime
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
return \davigil\helpers\string_\coin(
|
||
|
|
'{{date}}T{{time}}',
|
||
|
|
[
|
||
|
|
'date' => date_encode($datetime->date),
|
||
|
|
'time' => time_encode($datetime->time),
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @todo complete
|
||
|
|
*/
|
||
|
|
function vcalendar_encode(
|
||
|
|
struct_vcalendar $vcalendar
|
||
|
|
) : string
|
||
|
|
{
|
||
|
|
$content_lines = [];
|
||
|
|
\array_push($content_lines, 'BEGIN:VCALENDAR');
|
||
|
|
\array_push($content_lines, \davigil\helpers\string_\coin('VERSION:{{version}}', ['version' => $vcalendar->version]));
|
||
|
|
\array_push($content_lines, \davigil\helpers\string_\coin('PRODID:{{prodid}}', ['prodid' => $vcalendar->prodid]));
|
||
|
|
\array_push($content_lines, \davigil\helpers\string_\coin('METHOD:{{method}}', ['method' => $vcalendar->method]));
|
||
|
|
foreach ($vcalendar->events as $vevent)
|
||
|
|
{
|
||
|
|
\array_push($content_lines, 'BEGIN:VEVENT');
|
||
|
|
{
|
||
|
|
// uid
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'UID:{{uid}}',
|
||
|
|
[
|
||
|
|
'uid' => $vevent->uid,
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// dtstart
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'DTSTART:{{dtstart}}',
|
||
|
|
[
|
||
|
|
'dtstart' => datetime_encode($vevent->dtstart->value),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// dtend
|
||
|
|
if ($vevent->dtend !== null)
|
||
|
|
{
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'DTEND:{{dtend}}',
|
||
|
|
[
|
||
|
|
'dtend' => datetime_encode($vevent->dtend->value),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// dtstamp
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'DTSTAMP:{{dtstamp}}',
|
||
|
|
[
|
||
|
|
'dtstamp' => datetime_encode($vevent->dtstamp),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// class
|
||
|
|
if ($vevent->class !== null)
|
||
|
|
{
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'CLASS:{{class}}',
|
||
|
|
[
|
||
|
|
'class' => class_encode($vevent->class),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// summary
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'SUMMARY:{{summary}}',
|
||
|
|
[
|
||
|
|
'summary' => $vevent->summary,
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
// description
|
||
|
|
if ($vevent->description !== null)
|
||
|
|
{
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'DESCRIPTION:{{description}}',
|
||
|
|
[
|
||
|
|
'description' => $vevent->description,
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// location
|
||
|
|
if ($vevent->location !== null)
|
||
|
|
{
|
||
|
|
\array_push(
|
||
|
|
$content_lines,
|
||
|
|
\davigil\helpers\string_\coin(
|
||
|
|
'LOCATION:{{location}}',
|
||
|
|
[
|
||
|
|
'location' => $vevent->location,
|
||
|
|
]
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
// geo
|
||
|
|
if (vevent.geo !== undefined) {
|
||
|
|
content_lines.push(
|
||
|
|
lib_plankton.string.coin(
|
||
|
|
"GEO:{{geo_latitude}};{{geo_longitude}}",
|
||
|
|
{
|
||
|
|
"geo_latitude": vevent.geo.latitude.toFixed(4),
|
||
|
|
"geo_longitude": vevent.geo.longitude.toFixed(4),
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
|
||
|
|
/*
|
||
|
|
// url
|
||
|
|
if (vevent.url !== undefined) {
|
||
|
|
content_lines.push(
|
||
|
|
lib_plankton.string.coin(
|
||
|
|
"URL:{{url}}",
|
||
|
|
{
|
||
|
|
"url": vevent.url,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
*/
|
||
|
|
}
|
||
|
|
\array_push($content_lines, 'END:VEVENT');
|
||
|
|
}
|
||
|
|
\array_push($content_lines, 'END:VCALENDAR');
|
||
|
|
|
||
|
|
$lines = [];
|
||
|
|
foreach ($content_lines as $content_line)
|
||
|
|
{
|
||
|
|
$slices = \davigil\helpers\string_\slice($content_line, 75 - 1);
|
||
|
|
\array_push($lines, $slices[0]);
|
||
|
|
foreach (\array_slice($slices, 1) as $slice)
|
||
|
|
{
|
||
|
|
\array_push($lines, ' ' . $slice);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return \implode("\r\n", $lines);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function datetime_to_unix_timestamp(
|
||
|
|
struct_datetime $datetime
|
||
|
|
) : int
|
||
|
|
{
|
||
|
|
return \davigil\helpers\pit\pit_to_unix_timestamp(
|
||
|
|
\davigil\helpers\pit\pit_from_datetime(
|
||
|
|
new \davigil\helpers\pit\struct_datetime(
|
||
|
|
0,
|
||
|
|
new \davigil\helpers\pit\struct_date(
|
||
|
|
$datetime->date->year,
|
||
|
|
$datetime->date->month,
|
||
|
|
$datetime->date->day
|
||
|
|
),
|
||
|
|
(
|
||
|
|
($datetime->time === null)
|
||
|
|
?
|
||
|
|
null
|
||
|
|
:
|
||
|
|
new \davigil\helpers\pit\struct_time(
|
||
|
|
$datetime->time->hour,
|
||
|
|
$datetime->time->minute,
|
||
|
|
$datetime->time->second
|
||
|
|
)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
*/
|
||
|
|
function datetime_from_unix_timestamp(
|
||
|
|
int $unix_timestamp
|
||
|
|
) : struct_datetime
|
||
|
|
{
|
||
|
|
$pit = \davigil\helpers\pit\pit_from_unix_timestamp($unix_timestamp);
|
||
|
|
$datetime = \davigil\helpers\pit\pit_to_datetime($pit);
|
||
|
|
return (new struct_datetime(
|
||
|
|
new struct_date(
|
||
|
|
$datetime->date->year,
|
||
|
|
$datetime->date->month,
|
||
|
|
$datetime->date->day
|
||
|
|
),
|
||
|
|
(
|
||
|
|
($datetime->time === null)
|
||
|
|
?
|
||
|
|
null
|
||
|
|
:
|
||
|
|
new struct_time(
|
||
|
|
$datetime->time->hour,
|
||
|
|
$datetime->time->minute,
|
||
|
|
$datetime->time->second,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
)
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
?>
|