Skip to content

Instantly share code, notes, and snippets.

@frontycore
Created May 27, 2023 17:56
Show Gist options
  • Save frontycore/ce4ff94dde5e0616daf8857533a5ba96 to your computer and use it in GitHub Desktop.
Save frontycore/ce4ff94dde5e0616daf8857533a5ba96 to your computer and use it in GitHub Desktop.
Export to calendar (Google + Outlook + ICS)
<?php
namespace App\Utils;
use DateTime;
use App\PostTypes\Event;
use Nette\Utils\Strings;
use Jsvrcek\ICS\CalendarExport;
use Jsvrcek\ICS\CalendarStream;
use Jsvrcek\ICS\Utility\Formatter;
use Jsvrcek\ICS\Model\CalendarEvent;
use Jsvrcek\ICS\Model\Calendar as ICalendar;
/**
* Create Google and Outlook add-to-calendar links, generated iCal file.
*
* @see https://dylanbeattie.net/2021/01/12/adding-events-to-google-calendar-via-a-link.html
* @see https://stackoverflow.com/questions/50518151/how-to-generate-url-to-add-event-to-outlook-web-client-calendar
* @see https://github.com/jasvrcek/ICS
*/
class Calendar
{
/** @var string */
private const ICS_COMPANY = 'Super duper company';
/** @var string */
private const ICS_CALENDAR = 'Calendar of great events';
/** @var string */
private const GOOGLE_URL = 'https://calendar.google.com/calendar/u/0/r/eventedit?';
/** @var string */
private const OUTLOOK_URL = 'https://outlook.office.com/owa/?path=/calendar/action/compose&rruaddevent&';
public function __construct(
private string $id,
private DateTime $start,
private DateTime $end,
private string $name,
private string $description = '',
private string $location = '',
private string $url = ''
) { }
// Factory from WP post type
public static function fromEvent(Event $event): self
{
return new self(
sha1($event->ID),
$event->getStart(),
$event->getEnd(),
$event->post_title,
nl2br($event->getSchedule()),
$event->getAddress(),
get_permalink($event->ID)
);
}
public function googleUrl(): string
{
$googleParams = array_filter([
'text' => $this->name,
'dates' => $this->start->format('Ymd\THisp') . '/' . $this->end->format('Ymd\THisp'),
'location' => $this->location,
'details' => $this->getDetails()
]);
return self::GOOGLE_URL . http_build_query($googleParams);
}
public function outlookUrl(): string
{
$outlookParams = array_filter([
'allDay' => $this->isAllDay() ? 'true' : 'false',
'startdt' => $this->start->format('Y-m-d\TH:i:s'),
'enddt' => $this->end->format('Y-m-d\TH:i:s'),
'subject' => $this->name,
'location' => $this->location,
'body' => $this->getDetails()
]);
return self::OUTLOOK_URL . http_build_query($outlookParams);
}
// WP-relevant method
public function iCalUrl(Event $event): string
{
return get_bloginfo('url') . '/?ical=' . $event->ID;
}
public function getICal(): ICalendar
{
$event = new CalendarEvent();
$event
->setUid($this->id)
->setStart($this->start)
->setEnd($this->end)
->setAllDay($this->isAllDay())
->setSummary($this->name);
if ($this->description) $event->setDescription($this->description);
if ($this->url) $event->setUrl($this->url);
$calendar = new ICalendar();
$calendar->setProdId('-//' . self::ICS_COMPANY . '//' . self::ICS_CALENDAR . '//EN')
->addEvent($event);
return $calendar;
}
private function isAllDay(): bool
{
$day = 24 * 60 * 60;
return $this->end->getTimestamp() - $this->start->getTimestamp() > $day;
}
private function getDetails(): string
{
$details = $this->description;
if ($this->url) $details .= ($details ? PHP_EOL . PHP_EOL : '') . $this->url;
return $details;
}
}
// For Wordpress
add_action('wp', function() {
if (!isset($_GET['ical']) || !is_numeric($_GET['ical'])) return;
$event = new Event($_GET['ical']);
$calendar = Calendar::fromEvent($event)->getICal();
$calendarExport = new CalendarExport(new CalendarStream, new Formatter());
$calendarExport->addCalendar($calendar);
$filename = Strings::webalize($event->post_title);
header('Content-type: text/calendar; charset=utf-8');
header('Content-Disposition: attachment; filename=' . $filename . '.ics');
echo $calendarExport->getStream();
exit();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment