diff --git a/src/DataTypes/Calendar.php b/src/DataTypes/Calendar.php new file mode 100644 index 0000000..4682d11 --- /dev/null +++ b/src/DataTypes/Calendar.php @@ -0,0 +1,413 @@ +setProperties($arguments); + } + + /** + * Returns the correct QrCode format. + * + * @return string + */ + public function __toString() + { + return $this->buildCalendarString(); + } + + /** + * Builds the WiFi string. + * + * @return string + */ + protected function buildCalendarString() + { + $calendar = $this->prefix.$this->separator; + + if (isset($this->summary)) { + $calendar .= 'SUMMARY:'.$this->summary.$this->separator; + } + if (isset($this->startDateTime)) { + $calendar .= 'DTSTART'; + if (isset($this->timezone)) { + $calendar .= $this->timezone; + } + $calendar .= ':'.$this->startDateTime.$this->separator; + } + if (isset($this->endDateTime)) { + $calendar .= 'DTEND'; + if (isset($this->timezone)) { + $calendar .= $this->timezone; + } + $calendar .= ':'.$this->endDateTime.$this->separator; + } + if (isset($this->location)) { + $calendar .= 'LOCATION:'.$this->location.$this->separator; + } + if (isset($this->url)) { + $calendar .= 'URL:'.$this->url.$this->separator; + } + if (isset($this->description)) { + $calendar .= 'DESCRIPTION:'.$this->description.$this->separator; + } + if (isset($this->categories)) { + $calendar .= 'CATEGORIES:'.$this->categories.$this->separator; + } + if (isset($this->alarm)) { + $calendar .= $this->alarm; + } + + $calendar .= $this->suffix; + + return $calendar; + } + + /** + * Sets the Calendar properties. + * + * @param $arguments + */ + protected function setProperties(array $arguments) + { + $arguments = $arguments[0]; + if (! isset($arguments['summary'])) { + throw new InvalidArgumentException('Please provide an event summary.'); + } elseif (! isset($arguments['startDateTime'])) { + throw new InvalidArgumentException('Please provide a start date for the event.'); + } else { + if (isset($arguments['dateTimeFormat'])) { + $this->dateTimeFormat = $arguments['dateTimeFormat']; + } + if (isset($arguments['timezone'])) { + $this->setEventTimeZone($arguments['timezone']); + } + + $this->summary = $arguments['summary']; + $this->setEventDateTime($arguments['startDateTime'], 'start'); + + if (isset($arguments['endDateTime'])) { + $this->setEventDateTime($arguments['endDateTime'], 'end'); + } + if (isset($arguments['location'])) { + $this->location = $arguments['location']; + } + if (isset($arguments['url'])) { + $this->setUrl($arguments['url']); + } + if (isset($arguments['description'])) { + $this->description = $arguments['description']; + } + if (isset($arguments['categories'])) { + $this->setCategories($arguments['categories']); + } + if (isset($arguments['alarm'])) { + $this->setAlarm($arguments['alarm']); + } + } + } + + /** + * Sets the url property. + * + * @param $url + */ + protected function setUrl($url) + { + if ($this->isValidUrl($url)) { + $this->url = $url; + } + } + + /** + * Sets the datetime property. + * + * @param string $eventDateTime + * @param string $type + */ + protected function setEventDateTime($eventDateTime, $type = '') + { + if ($this->isValidDateTime($eventDateTime, $type)) { + if ($type == 'start') { + $this->startDateTime = $this->convertEventDateTimeToString($eventDateTime); + } + if ($type == 'end') { + $this->endDateTime = $this->convertEventDateTimeToString($eventDateTime); + } + } + } + + /** + * Ensures url is valid. + * + * @param string $url + * + * @return bool + */ + protected function isValidUrl($url) + { + if (! filter_var($url, FILTER_VALIDATE_URL)) { + throw new InvalidArgumentException('Invalid url provided'); + } + + return true; + } + + /** + * Ensures datetime is valid. + * + * @param string $dateTime + * @param string $type + * + * @return bool + */ + protected function isValidDateTime($dateTime, $type = '') + { + $newDate = DateTime::createFromFormat($this->dateTimeFormat, $dateTime); + if (! ($newDate && $newDate->format($this->dateTimeFormat) == $dateTime)) { + if ($type == 'start') { + throw new InvalidArgumentException('Invalid start date provided. Date must be of format '. + $this->dateTimeFormat); + } + if ($type == 'end') { + throw new InvalidArgumentException('Invalid end date provided. Date must be of format '. + $this->dateTimeFormat); + } + throw new InvalidArgumentException('Invalid date provided. Date must be of format '. + $this->dateTimeFormat); + } + + return true; + } + + /** + * Returns correct date time to string. + * + * @param string $dateTime + * @return string + */ + protected function convertEventDateTimeToString($dateTime) + { + try { + $date = new DateTime($dateTime); + } catch (Exception $e) { + throw new InvalidArgumentException('Invalid date provided'); + } + + return $date->format('yymd\THms'); + } + + /** + * Ensures timezone string is valid. ('Africa/Douala'). + * + * @param string $timezone + * + * @return bool + */ + protected function isValidTimeZone($timezone) + { + if (! in_array($timezone, DateTimeZone::listIdentifiers())) { + throw new InvalidArgumentException('Invalid timezone provided.'); + } + + return true; + } + + /** + * Sets the timezone property. + * + * @param $timezone + */ + protected function setEventTimeZone($timezone) + { + if ($this->isValidTimeZone($timezone)) { + $this->timezone = ';TZID='.$timezone; + } + } + + /** + * Sets the alarm property. + * + * @param array $alarm + */ + protected function setAlarm($alarm) + { + if (is_array($alarm)) { + $alarmString = 'BEGIN:VALARM'.$this->separator; + $alarmString .= 'ACTION:DISPLAY'.$this->separator; + $alarmString .= $this->keyExists('repeat', $alarm) ? + 'REPEAT:'.$alarm['repeat'].$this->separator : ''; + $alarmString .= $this->keyExists('summary', $alarm) ? + 'SUMMARY:'.$alarm['summary'].$this->separator : ''; + $alarmString .= $this->keyExists('description', $alarm) ? + 'DESCRIPTION:'.$alarm['description'].$this->separator : ''; + if ($this->keyExists('trigger', $alarm) && is_array($alarm['trigger'])) { + $alarmString .= $this->setAlarmTrigger($alarm['trigger']).$this->separator; + } + $alarmString .= 'END:VALARM'.$this->separator; + $this->alarm = $alarmString; + } else { + throw new InvalidArgumentException('Invalid alarm provided.'); + } + } + + /** + * Sets the alarm trigger property. + * + * @param array $alarm + * @return string + */ + protected function setAlarmTrigger($alarm) + { + if (is_array($alarm)) { + $alarmString = 'TRIGGER:'; + $alarmString .= $this->keyExists('before', $alarm) ? $alarm['before'] ? '-' : '' : ''; + $alarmString .= 'P'; + $alarmString .= $this->keyExists('weeks', $alarm) ? $alarm['weeks'].'W' : ''; + $alarmString .= $this->keyExists('days', $alarm) ? $alarm['days'].'D' : ''; + $alarmString .= 'T'; + $alarmString .= $this->keyExists('hours', $alarm) ? $alarm['hours'].'H' : ''; + $alarmString .= $this->keyExists('minutes', $alarm) ? $alarm['minutes'].'M' : ''; + $alarmString .= $this->keyExists('seconds', $alarm) ? $alarm['seconds'].'S' : ''; + + return $alarmString; + } else { + throw new InvalidArgumentException('Invalid alarm duration provided.'); + } + } + + /** + * Sets the event categories property. + * + * @param array $categories + */ + protected function setCategories($categories) + { + if (is_array($categories)) { + $this->categories = implode(',', $categories); + } else { + throw new InvalidArgumentException('Invalid categories provided.'); + } + } + + /** + * Checks if key exists in array. + * + * @param string $key + * @param array $array + * + * @return bool + */ + protected function keyExists($key, $array) + { + return array_key_exists($key, $array); + } +} diff --git a/tests/DataTypes/CalendarTest.php b/tests/DataTypes/CalendarTest.php new file mode 100644 index 0000000..8f7890d --- /dev/null +++ b/tests/DataTypes/CalendarTest.php @@ -0,0 +1,213 @@ +separator = "\r\n"; + $this->calendar = new Calendar(); + } + + public function test_summary_is_mandatory() + { + $this->expectException(InvalidArgumentException::class); + $this->calendar->create([ + 0 => [ + 'startDateTime' => '2020-04-15 14:00', + ], + ]); + } + + public function test_start_date_time_is_mandatory() + { + $exceptionOccurred = false; + try { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + ], + ]); + } catch (InvalidArgumentException $e) { + $exceptionOccurred = true; + $this->assertEquals($e->getMessage(), 'Please provide a start date for the event.'); + } + + $this->assertTrue($exceptionOccurred); + } + + public function test_it_generates_a_proper_format_with_just_the_summary_and_start_date_time() + { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + ], + ]); + + $properFormat = 'BEGIN:VEVENT'.$this->separator. + 'SUMMARY:My FooBar Event'.$this->separator. + 'DTSTART:20201008T161000'.$this->separator. + 'END:VEVENT'; + + $this->assertEquals($properFormat, strval($this->calendar)); + } + + public function test_it_generates_a_proper_format_with_summary_start_date_time_and_end_date_time() + { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + 'endDateTime' => '2020-10-08 18:00', + ], + ]); + + $properFormat = 'BEGIN:VEVENT'.$this->separator. + 'SUMMARY:My FooBar Event'.$this->separator. + 'DTSTART:20201008T161000'.$this->separator. + 'DTEND:20201008T181000'.$this->separator. + 'END:VEVENT'; + + $this->assertEquals($properFormat, strval($this->calendar)); + } + + public function test_it_generates_a_proper_format_with_specified_date_time_format() + { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + 'endDateTime' => '2020-10-08 18:00', + 'dateTimeFormat' => 'Y-m-d H:i', + ], + ]); + + $properFormat = 'BEGIN:VEVENT'.$this->separator. + 'SUMMARY:My FooBar Event'.$this->separator. + 'DTSTART:20201008T161000'.$this->separator. + 'DTEND:20201008T181000'.$this->separator. + 'END:VEVENT'; + + $this->assertEquals($properFormat, strval($this->calendar)); + } + + public function test_date_time_format_validation() + { + $this->expectException(InvalidArgumentException::class); + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + 'endDateTime' => '2020-10-08 18:00', + 'dateTimeFormat' => 'Y/m/d H:i', + ], + ]); + } + + public function test_it_generates_a_proper_format_with_summary_start_date_time_and_just_location() + { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + 'location' => 'Fooon', + ], + ]); + + $properFormat = 'BEGIN:VEVENT'.$this->separator. + 'SUMMARY:My FooBar Event'.$this->separator. + 'DTSTART:20201008T161000'.$this->separator. + 'LOCATION:Fooon'.$this->separator. + 'END:VEVENT'; + + $this->assertEquals($properFormat, strval($this->calendar)); + } + + public function test_it_generates_a_proper_format_with_summary_start_date_time_and_just_url() + { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + 'url' => 'https://www.google.com', + ], + ]); + + $properFormat = 'BEGIN:VEVENT'.$this->separator. + 'SUMMARY:My FooBar Event'.$this->separator. + 'DTSTART:20201008T161000'.$this->separator. + 'URL:https://www.google.com'.$this->separator. + 'END:VEVENT'; + + $this->assertEquals($properFormat, strval($this->calendar)); + } + + public function test_it_generates_a_proper_format_with_calendar_parameters() + { + $this->calendar->create([ + 0 => [ + 'summary' => 'My FooBar Event', + 'startDateTime' => '2020-10-08 16:00', + 'endDateTime' => '2020-10-08 18:00', + 'location' => 'Foo Location', + 'timezone' => 'Africa/Douala', + 'url' => 'https://www.google.com', + 'categories' => ['Business', 'Anniversary', 'Special'], + 'dateTimeFormat' => 'Y-m-d H:i', + 'description' => 'FooBar\'s Event description can be longer than this', + 'alarm' => [ + 'trigger' => [ + 'weeks' => 1, + 'days' => 2, + 'hours' => 3, + 'minutes' => 20, + 'seconds' => 0, + 'before' => true, + ], + 'repeat' => 2, + 'summary' => 'Alarm Summary', + 'description' => 'Alarm Description can be longer', + ], + ], + ]); + + $properFormat = 'BEGIN:VEVENT'.$this->separator. + 'SUMMARY:My FooBar Event'.$this->separator. + 'DTSTART;TZID=Africa/Douala:20201008T161000'.$this->separator. + 'DTEND;TZID=Africa/Douala:20201008T181000'.$this->separator. + 'LOCATION:Foo Location'.$this->separator. + 'URL:https://www.google.com'.$this->separator. + 'DESCRIPTION:FooBar\'s Event description can be longer than this'.$this->separator. + 'CATEGORIES:Business,Anniversary,Special'.$this->separator. + 'BEGIN:VALARM'.$this->separator. + 'ACTION:DISPLAY'.$this->separator. + 'REPEAT:2'.$this->separator. + 'SUMMARY:Alarm Summary'.$this->separator. + 'DESCRIPTION:Alarm Description can be longer'.$this->separator. + 'TRIGGER:-P1W2DT3H20M0S'.$this->separator. + 'END:VALARM'.$this->separator. + 'END:VEVENT'; + + $this->assertEquals($properFormat, strval($this->calendar)); + } +}