Skip to content

Commit

Permalink
fixes ICS generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Elorfin committed May 28, 2024
1 parent e6f4695 commit 1fd48e4
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 103 deletions.
94 changes: 22 additions & 72 deletions src/main/core/Library/ICS/ICS.php
Original file line number Diff line number Diff line change
@@ -1,51 +1,13 @@
<?php

/**
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* 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 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.
*
* For more information, please refer to <http://unlicense.org>
*
* ICS.php
* =============================================================================
* Use this class to create an .ics file.
*
*
* Usage
* -----------------------------------------------------------------------------
* Basic usage - generate ics file contents (see below for available properties):
* $ics = new ICS($props);
* $ics_file_contents = $ics->to_string();
*
* Setting properties after instantiation
* $ics = new ICS();
* $ics->set('summary', 'My awesome event');
*
* You can also set multiple properties at the same time by using an array:
* $ics->set([
* 'dtstart' => 'now + 30 minutes',
* 'dtend' => 'now + 1 hour'
* ]);
* $ics_file_contents = $ics->toString();
*
* Available properties
* -----------------------------------------------------------------------------
Expand All @@ -62,19 +24,19 @@
* summary
* String short summary of the event - usually used as the title.
* url
* A url to attach to the the event. Make sure to add the protocol (http://
* A url to attach to the event. Make sure to add the protocol (http://
* or https://).
*/

namespace Claroline\CoreBundle\Library\ICS;

class ICS
{
const DT_FORMAT = 'Ymd\THis\Z';
public const DT_FORMAT = 'Ymd\THis\Z';

protected $properties = [];
private array $properties = [];

private $available_properties = [
private array $availableProperties = [
'description',
'dtend',
'dtstart',
Expand All @@ -83,35 +45,26 @@ class ICS
'url',
];

public function __construct($props)
public function __construct(array $props)
{
$this->set($props);
}

public function set($key, $val = false)
{
if (is_array($key)) {
foreach ($key as $k => $v) {
$this->set($k, $v);
}
} else {
if (in_array($key, $this->available_properties)) {
$this->properties[$key] = $this->sanitize($val, $key);
foreach ($props as $k => $v) {
if (in_array($k, $this->availableProperties)) {
$this->properties[$k] = $this->sanitize($v, $k);
}
}
}

public function toString()
public function toString(): string
{
$rows = $this->build();

return implode("\r\n", $rows);
}

private function build()
private function build(): array
{
// Build ICS properties - add header
$ics_props = [
$icsProps = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//hacksw/handcal//NONSGML v1.0//EN',
Expand All @@ -123,6 +76,10 @@ private function build()
$props = [];
foreach ($this->properties as $k => $v) {
$props[strtoupper($k.('url' === $k ? ';VALUE=URI' : ''))] = $v;
if ('description' === $k) {
// this is required for Outlook. It doesn't interpret HTML otherwise
$props['X-ALT-DESC;FMTTYPE=text/html'] = $v;
}
}

// Set some default values
Expand All @@ -131,40 +88,33 @@ private function build()

// Append properties
foreach ($props as $k => $v) {
$ics_props[] = "$k:$v";
$icsProps[] = "$k:$v";
}

// Build ICS properties - add footer
$ics_props[] = 'END:VEVENT';
$ics_props[] = 'END:VCALENDAR';
$icsProps[] = 'END:VEVENT';
$icsProps[] = 'END:VCALENDAR';

return $ics_props;
return $icsProps;
}

private function sanitize($val, $key = false)
private function sanitize($val, $key = false): ?string
{
switch ($key) {
case 'dtend':
case 'dtstamp':
case 'dtstart':
$val = $this->formatTimestamp($val);
break;
default:
$val = $this->escape($val);
}

return $val;
}

private function formatTimestamp($timestamp)
private function formatTimestamp($timestamp): string
{
$dt = new \DateTime($timestamp);

return $dt->format(self::DT_FORMAT);
}

private function escape($str)
{
return preg_replace('/([\,;])/', '\\\$1', $str);
}
}
15 changes: 5 additions & 10 deletions src/main/core/Library/ICS/ICSGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,10 @@

class ICSGenerator
{
/** @var string */
private $filesDir;
/** @var Filesystem */
private $filesystem;

public function __construct(string $filesDir, ?Filesystem $filesystem = null)
{
$this->filesDir = $filesDir;
$this->filesystem = $filesystem ?? new Filesystem();
public function __construct(
private readonly string $filesDir,
private readonly Filesystem $filesystem
) {
}

public function create(array $icsProps): string
Expand All @@ -99,7 +94,7 @@ public function createFile(array $icsProps, string $filename = null): string

$path = $this->filesDir.DIRECTORY_SEPARATOR.'ics'.DIRECTORY_SEPARATOR.$filename.'.ics';

$this->filesystem->appendToFile($path, $this->create($icsProps));
$this->filesystem->dumpFile($path, $this->create($icsProps));

return $path;
}
Expand Down
4 changes: 2 additions & 2 deletions src/plugin/agenda/Manager/EventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ public function getICS(Event $event, bool $toFile = false): string
$locationAddress = '';
if ($location) {
$locationAddress = $location->getName();
$locationAddress .= '<br>'.$location->getAddress();
$locationAddress .= ', '.$location->getAddress();
if ($location->getPhone()) {
$locationAddress .= '<br>'.$location->getPhone();
$locationAddress .= ', '.$location->getPhone();
}
}

Expand Down
39 changes: 20 additions & 19 deletions src/plugin/cursus/Manager/EventManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,18 +288,19 @@ public function sendSessionEventInvitation(Event $event, array $users): void

public function getICS(Event $event, bool $toFile = false): string
{
$session = $event->getSession();
$location = $event->getLocation();
$locationAddress = '';
if ($location) {
$locationAddress = $location->getName();
$locationAddress .= '<br>'.$location->getAddress();
$locationAddress .= ', '.$location->getAddress();
if ($location->getPhone()) {
$locationAddress .= '<br>'.$location->getPhone();
$locationAddress .= ', '.$location->getPhone();
}
}

$icsProps = [
'summary' => $event->getName(),
'summary' => $session ? $session->getName().' - '.$event->getName() : $event->getName(),
'description' => $event->getDescription(),
'location' => $locationAddress,
'dtstart' => DateNormalizer::normalize($event->getStartDate()),
Expand Down Expand Up @@ -384,22 +385,22 @@ private function getTemplatePlaceholders(Event $event): array
}

return array_merge([
// course info
'course_name' => $course->getName(),
'course_code' => $course->getCode(),
'course_description' => $course->getDescription(),
// session info
'session_name' => $session->getName(),
'session_description' => $session->getDescription(),
'session_code' => $session->getCode(),
// event info
'event_name' => $event->getName(),
'event_description' => $event->getDescription(),
'event_code' => $event->getCode(),
'event_location_name' => $locationName,
'event_location_address' => $locationAddress,
'event_trainers' => $trainersList,
],
// course info
'course_name' => $course->getName(),
'course_code' => $course->getCode(),
'course_description' => $course->getDescription(),
// session info
'session_name' => $session->getName(),
'session_description' => $session->getDescription(),
'session_code' => $session->getCode(),
// event info
'event_name' => $event->getName(),
'event_description' => $event->getDescription(),
'event_code' => $event->getCode(),
'event_location_name' => $locationName,
'event_location_address' => $locationAddress,
'event_trainers' => $trainersList,
],
$this->templateManager->formatDatePlaceholder('session_start', $session->getStartDate()),
$this->templateManager->formatDatePlaceholder('session_end', $session->getEndDate()),
$this->templateManager->formatDatePlaceholder('event_start', $event->getStartDate()),
Expand Down

0 comments on commit 1fd48e4

Please sign in to comment.