diff --git a/apps/dav/lib/CalDAV/BirthdayService.php b/apps/dav/lib/CalDAV/BirthdayService.php index 0e8926e775a39..278373733a41d 100644 --- a/apps/dav/lib/CalDAV/BirthdayService.php +++ b/apps/dav/lib/CalDAV/BirthdayService.php @@ -1,7 +1,8 @@ * @author Georg Ehrke @@ -33,6 +34,7 @@ use OCA\DAV\DAV\GroupPrincipalBackend; use OCP\IConfig; use OCP\IDBConnection; +use OCP\IL10N; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VCard; use Sabre\VObject\DateTimeParser; @@ -41,6 +43,11 @@ use Sabre\VObject\Property\VCard\DateAndOrTime; use Sabre\VObject\Reader; +/** + * Class BirthdayService + * + * @package OCA\DAV\CalDAV + */ class BirthdayService { const BIRTHDAY_CALENDAR_URI = 'contact_birthdays'; @@ -60,20 +67,31 @@ class BirthdayService { /** @var IDBConnection */ private $dbConnection; + /** @var IL10N */ + private $l10n; + /** * BirthdayService constructor. * * @param CalDavBackend $calDavBackEnd * @param CardDavBackend $cardDavBackEnd * @param GroupPrincipalBackend $principalBackend - * @param IConfig $config; + * @param IConfig $config + * @param IDBConnection $dbConnection + * @param IL10N $l10n */ - public function __construct(CalDavBackend $calDavBackEnd, CardDavBackend $cardDavBackEnd, GroupPrincipalBackend $principalBackend, IConfig $config, IDBConnection $dbConnection) { + public function __construct(CalDavBackend $calDavBackEnd, + CardDavBackend $cardDavBackEnd, + GroupPrincipalBackend $principalBackend, + IConfig $config, + IDBConnection $dbConnection, + IL10N $l10n) { $this->calDavBackEnd = $calDavBackEnd; $this->cardDavBackEnd = $cardDavBackEnd; $this->principalBackend = $principalBackend; $this->config = $config; $this->dbConnection = $dbConnection; + $this->l10n = $l10n; } /** @@ -81,7 +99,9 @@ public function __construct(CalDavBackend $calDavBackEnd, CardDavBackend $cardDa * @param string $cardUri * @param string $cardData */ - public function onCardChanged($addressBookId, $cardUri, $cardData) { + public function onCardChanged(int $addressBookId, + string $cardUri, + string $cardData) { if (!$this->isGloballyEnabled()) { return; } @@ -90,10 +110,11 @@ public function onCardChanged($addressBookId, $cardUri, $cardData) { $book = $this->cardDavBackEnd->getAddressBookById($addressBookId); $targetPrincipals[] = $book['principaluri']; $datesToSync = [ - ['postfix' => '', 'field' => 'BDAY', 'symbol' => '*', 'utfSymbol' => 'πŸŽ‚'], - ['postfix' => '-death', 'field' => 'DEATHDATE', 'symbol' => "†", 'utfSymbol' => '⚰️'], - ['postfix' => '-anniversary', 'field' => 'ANNIVERSARY', 'symbol' => "⚭", 'utfSymbol' => 'πŸ’'], + ['postfix' => '', 'field' => 'BDAY'], + ['postfix' => '-death', 'field' => 'DEATHDATE'], + ['postfix' => '-anniversary', 'field' => 'ANNIVERSARY'], ]; + foreach ($targetPrincipals as $principalUri) { if (!$this->isUserEnabled($principalUri)) { continue; @@ -101,7 +122,7 @@ public function onCardChanged($addressBookId, $cardUri, $cardData) { $calendar = $this->ensureCalendarExists($principalUri); foreach ($datesToSync as $type) { - $this->updateCalendar($cardUri, $cardData, $book, $calendar['id'], $type); + $this->updateCalendar($cardUri, $cardData, $book, (int) $calendar['id'], $type); } } } @@ -110,7 +131,8 @@ public function onCardChanged($addressBookId, $cardUri, $cardData) { * @param int $addressBookId * @param string $cardUri */ - public function onCardDeleted($addressBookId, $cardUri) { + public function onCardDeleted(int $addressBookId, + string $cardUri) { if (!$this->isGloballyEnabled()) { return; } @@ -136,7 +158,7 @@ public function onCardDeleted($addressBookId, $cardUri) { * @return array|null * @throws \Sabre\DAV\Exception\BadRequest */ - public function ensureCalendarExists($principal) { + public function ensureCalendarExists(string $principal):?array { $calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); if (!is_null($calendar)) { return $calendar; @@ -151,14 +173,15 @@ public function ensureCalendarExists($principal) { } /** - * @param string $cardData - * @param string $dateField - * @param string $postfix - * @param string $summarySymbol - * @param string $utfSummarySymbol - * @return null|VCalendar + * @param $cardData + * @param $dateField + * @param $postfix + * @return VCalendar|null + * @throws InvalidDataException */ - public function buildDateFromContact($cardData, $dateField, $postfix, $summarySymbol, $utfSummarySymbol) { + public function buildDateFromContact(string $cardData, + string $dateField, + string $postfix):?VCalendar { if (empty($cardData)) { return null; } @@ -224,19 +247,8 @@ public function buildDateFromContact($cardData, $dateField, $postfix, $summarySy } catch (Exception $e) { return null; } - if ($this->dbConnection->supports4ByteText()) { - if ($unknownYear) { - $summary = $utfSummarySymbol . ' ' . $doc->FN->getValue(); - } else { - $summary = $utfSummarySymbol . ' ' . $doc->FN->getValue() . " ($originalYear)"; - } - } else { - if ($unknownYear) { - $summary = $doc->FN->getValue() . ' ' . $summarySymbol; - } else { - $summary = $doc->FN->getValue() . " ($summarySymbol$originalYear)"; - } - } + + $summary = $this->formatTitle($dateField, $doc->FN->getValue(), $originalYear, $this->dbConnection->supports4ByteText()); $vCal = new VCalendar(); $vCal->VERSION = '2.0'; @@ -273,7 +285,7 @@ public function buildDateFromContact($cardData, $dateField, $postfix, $summarySy /** * @param string $user */ - public function resetForUser($user) { + public function resetForUser(string $user):void { $principal = 'principals/users/'.$user; $calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); $calendarObjects = $this->calDavBackEnd->getCalendarObjects($calendar['id'], CalDavBackend::CALENDAR_TYPE_CALENDAR); @@ -285,8 +297,9 @@ public function resetForUser($user) { /** * @param string $user + * @throws \Sabre\DAV\Exception\BadRequest */ - public function syncUser($user) { + public function syncUser(string $user):void { $principal = 'principals/users/'.$user; $this->ensureCalendarExists($principal); $books = $this->cardDavBackEnd->getAddressBooksForUser($principal); @@ -303,25 +316,25 @@ public function syncUser($user) { * @param VCalendar $newCalendarData * @return bool */ - public function birthdayEvenChanged($existingCalendarData, $newCalendarData) { + public function birthdayEvenChanged(string $existingCalendarData, + VCalendar $newCalendarData):bool { try { $existingBirthday = Reader::read($existingCalendarData); } catch (Exception $ex) { return true; } - if ($newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() || + + return ( + $newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() || $newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue() - ) { - return true; - } - return false; + ); } /** * @param integer $addressBookId * @return mixed */ - protected function getAllAffectedPrincipals($addressBookId) { + protected function getAllAffectedPrincipals(int $addressBookId) { $targetPrincipals = []; $shares = $this->cardDavBackEnd->getShares($addressBookId); foreach ($shares as $share) { @@ -339,14 +352,20 @@ protected function getAllAffectedPrincipals($addressBookId) { /** * @param string $cardUri - * @param string $cardData + * @param string $cardData * @param array $book * @param int $calendarId - * @param string[] $type + * @param array $type + * @throws InvalidDataException + * @throws \Sabre\DAV\Exception\BadRequest */ - private function updateCalendar($cardUri, $cardData, $book, $calendarId, $type) { + private function updateCalendar(string $cardUri, + string $cardData, + array $book, + int $calendarId, + array $type):void { $objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics'; - $calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix'], $type['symbol'], $type['utfSymbol']); + $calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix']); $existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri); if (is_null($calendarData)) { if (!is_null($existing)) { @@ -368,18 +387,17 @@ private function updateCalendar($cardUri, $cardData, $book, $calendarId, $type) * * @return bool */ - private function isGloballyEnabled() { - $isGloballyEnabled = $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes'); - return $isGloballyEnabled === 'yes'; + private function isGloballyEnabled():bool { + return $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes') === 'yes'; } /** - * checks if the user opted-out of birthday calendars + * Checks if the user opted-out of birthday calendars * - * @param $userPrincipal + * @param string $userPrincipal The user principal to check for * @return bool */ - private function isUserEnabled($userPrincipal) { + private function isUserEnabled(string $userPrincipal):bool { if (strpos($userPrincipal, 'principals/users/') === 0) { $userId = substr($userPrincipal, 17); $isEnabled = $this->config->getUserValue($userId, 'dav', 'generateBirthdayCalendar', 'yes'); @@ -390,4 +408,69 @@ private function isUserEnabled($userPrincipal) { return true; } + /** + * Formats title of Birthday event + * + * @param string $field Field name like BDAY, ANNIVERSARY, ... + * @param string $name Name of contact + * @param int|null $year Year of birth, anniversary, ... + * @param bool $supports4Byte Whether or not the database supports 4 byte chars + * @return string The formatted title + */ + private function formatTitle(string $field, + string $name, + int $year=null, + bool $supports4Byte=true):string { + if ($supports4Byte) { + switch ($field) { + case 'BDAY': + return implode('', [ + 'πŸŽ‚ ', + $name, + $year ? (' (' . $year . ')') : '', + ]); + + case 'DEATHDATE': + return implode('', [ + $this->l10n->t('Death of %s', [$name]), + $year ? (' (' . $year . ')') : '', + ]); + + case 'ANNIVERSARY': + return implode('', [ + 'πŸ’ ', + $name, + $year ? (' (' . $year . ')') : '', + ]); + + default: + return ''; + } + } else { + switch($field) { + case 'BDAY': + return implode('', [ + $name, + ' ', + $year ? ('(*' . $year . ')') : '*', + ]); + + case 'DEATHDATE': + return implode('', [ + $this->l10n->t('Death of %s', [$name]), + $year ? (' (' . $year . ')') : '', + ]); + + case 'ANNIVERSARY': + return implode('', [ + $name, + ' ', + $year ? ('(⚭' . $year . ')') : '⚭', + ]); + + default: + return ''; + } + } + } } diff --git a/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php b/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php index 46aa06e853791..06ea1329ec00f 100644 --- a/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php +++ b/apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php @@ -30,6 +30,7 @@ use OCA\DAV\DAV\GroupPrincipalBackend; use OCP\IConfig; use OCP\IDBConnection; +use OCP\IL10N; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Reader; use Test\TestCase; @@ -48,6 +49,8 @@ class BirthdayServiceTest extends TestCase { private $config; /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject */ private $dbConnection; + /** @var IL10N | \PHPUnit_Framework_MockObject_MockObject */ + private $l10n; public function setUp() { parent::setUp(); @@ -57,9 +60,17 @@ public function setUp() { $this->groupPrincipalBackend = $this->createMock(GroupPrincipalBackend::class); $this->config = $this->createMock(IConfig::class); $this->dbConnection = $this->createMock(IDBConnection::class); + $this->l10n = $this->createMock(IL10N::class); - $this->service = new BirthdayService($this->calDav, $this->cardDav, - $this->groupPrincipalBackend, $this->config, $this->dbConnection); + $this->l10n->expects($this->any()) + ->method('t') + ->willReturnCallback(function($string, $args) { + return vsprintf($string, $args); + }); + + $this->service = new BirthdayService($this->calDav,$this->cardDav, + $this->groupPrincipalBackend, $this->config, + $this->dbConnection, $this->l10n); } /** @@ -71,9 +82,9 @@ public function setUp() { * @param string $expectedOriginalYear * @param string | null $data */ - public function testBuildBirthdayFromContact($expectedSummary, $expectedDTStart, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $data, $supports4Bytes) { + public function testBuildBirthdayFromContact($expectedSummary, $expectedDTStart, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $data, $fieldType, $prefix, $supports4Bytes) { $this->dbConnection->method('supports4ByteText')->willReturn($supports4Bytes); - $cal = $this->service->buildDateFromContact($data, 'BDAY', '', '*', 'πŸŽ‚'); + $cal = $this->service->buildDateFromContact($data, $fieldType, $prefix); if ($expectedSummary === null) { $this->assertNull($cal); @@ -169,7 +180,7 @@ public function testOnCardChangedGloballyDisabled() { $service = $this->getMockBuilder(BirthdayService::class) ->setMethods(['buildDateFromContact', 'birthdayEvenChanged']) - ->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection]) + ->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection, $this->l10n]) ->getMock(); $service->onCardChanged(666, 'gump.vcf', ''); @@ -198,7 +209,7 @@ public function testOnCardChangedUserDisabled() { /** @var BirthdayService | \PHPUnit_Framework_MockObject_MockObject $service */ $service = $this->getMockBuilder(BirthdayService::class) ->setMethods(['buildDateFromContact', 'birthdayEvenChanged']) - ->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection]) + ->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection, $this->l10n]) ->getMock(); $service->onCardChanged(666, 'gump.vcf', ''); @@ -234,7 +245,7 @@ public function testOnCardChanged($expectedOp) { /** @var BirthdayService | \PHPUnit_Framework_MockObject_MockObject $service */ $service = $this->getMockBuilder(BirthdayService::class) ->setMethods(['buildDateFromContact', 'birthdayEvenChanged']) - ->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection]) + ->setConstructorArgs([$this->calDav, $this->cardDav, $this->groupPrincipalBackend, $this->config, $this->dbConnection, $this->l10n]) ->getMock(); if ($expectedOp === 'delete') { @@ -382,27 +393,29 @@ public function providesCardChanges(){ public function providesVCards() { return [ - // $expectedSummary, $expectedDTStart, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $data, $supports4Byte - [null, null, null, null, null, null, true], - [null, null, null, null, null, '', true], - [null, null, null, null, null, 'yasfewf', true], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", true], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", true], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", true], - ['πŸŽ‚ 12345 (1900)', '19700101', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", true], - ['πŸŽ‚ 12345 (1900)', '19701231', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", true], - ['πŸŽ‚ 12345', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", true], - ['πŸŽ‚ 12345', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", true], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", true], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", true], - ['πŸŽ‚ 12345 (900)', '19701231', 'BDAY', '0', '900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", true], - ['12345 (*1900)', '19700101', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", false], - ['12345 (*1900)', '19701231', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", false], - ['12345 *', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", false], - ['12345 *', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", false], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", false], - [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", false], - ['12345 (*900)', '19701231', 'BDAY', '0', '900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", false], + // $expectedSummary, $expectedDTStart, $expectedFieldType, $expectedUnknownYear, $expectedOriginalYear, $data, $fieldType, $prefix, $supports4Byte + [null, null, null, null, null, 'yasfewf', '', '', true], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", 'BDAY', '', true], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:\r\nEND:VCARD\r\n", 'BDAY', '', true], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:someday\r\nEND:VCARD\r\n", 'BDAY', '', true], + ['πŸŽ‚ 12345 (1900)', '19700101', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', true], + ['πŸŽ‚ 12345 (1900)', '19701231', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', true], + ['Death of 12345 (1900)', '19701231', 'DEATHDATE', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', true], + ['Death of 12345 (1900)', '19701231', 'DEATHDATE', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nDEATHDATE:19001231\r\nEND:VCARD\r\n", 'DEATHDATE', '-death', false], + ['πŸ’ 12345 (1900)', '19701231', 'ANNIVERSARY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', true], + ['12345 (⚭1900)', '19701231', 'ANNIVERSARY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nANNIVERSARY:19001231\r\nEND:VCARD\r\n", 'ANNIVERSARY', '-anniversary', false], + ['πŸŽ‚ 12345', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', true], + ['πŸŽ‚ 12345', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', true], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', true], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', true], + ['πŸŽ‚ 12345 (900)', '19701231', 'BDAY', '0', '900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', true], + ['12345 (*1900)', '19700101', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19000101\r\nEND:VCARD\r\n", 'BDAY', '', false], + ['12345 (*1900)', '19701231', 'BDAY', '0', '1900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:19001231\r\nEND:VCARD\r\n", 'BDAY', '', false], + ['12345 *', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:--1231\r\nEND:VCARD\r\n", 'BDAY', '', false], + ['12345 *', '19701231', 'BDAY', '1', null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY;X-APPLE-OMIT-YEAR=1604:16041231\r\nEND:VCARD\r\n", 'BDAY', '', false], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:;VALUE=text:circa 1800\r\nEND:VCARD\r\n", 'BDAY', '', false], + [null, null, null, null, null, "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nN:12345;;;;\r\nBDAY:20031231\r\nEND:VCARD\r\n", 'BDAY', '', false], + ['12345 (*900)', '19701231', 'BDAY', '0', '900', "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 4.1.1//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nBDAY:09001231\r\nEND:VCARD\r\n", 'BDAY', '', false], ]; } }