Browse Source

Fix generation of birthday, deathdate and anniversary in case where no year is set - which is allowed as per https://tools.ietf.org/html/rfc6350#section-6.2.5 (#26756)

Signed-off-by: Lukas Reschke <lukas@statuscode.ch>
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
tags/v12.0.0beta1
Thomas Müller 7 years ago
parent
commit
d5d726fc24
No account linked to committer's email address

+ 39
- 6
apps/dav/lib/CalDAV/BirthdayService.php View File

@@ -30,6 +30,11 @@ use Exception;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\DAV\GroupPrincipalBackend;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\Document;
use Sabre\VObject\InvalidDataException;
use Sabre\VObject\Property\VCard\DateAndOrTime;
use Sabre\VObject\Reader;

class BirthdayService {
@@ -129,6 +134,12 @@ class BirthdayService {
}
try {
$doc = Reader::read($cardData);
// We're always converting to vCard 4.0 so we can rely on the
// VCardConverter handling the X-APPLE-OMIT-YEAR property for us.
if (!$doc instanceof VCard) {
return null;
}
$doc = $doc->convert(Document::VCARD40);
} catch (Exception $e) {
return null;
}
@@ -136,21 +147,43 @@ class BirthdayService {
if (!isset($doc->{$dateField})) {
return null;
}
if (!isset($doc->FN)) {
return null;
}
$birthday = $doc->{$dateField};
if (!(string)$birthday) {
return null;
}
$title = str_replace('{name}',
strtr((string)$doc->FN, array('\,' => ',', '\;' => ';')),
'{name}'
);
// Skip if the BDAY property is not of the right type.
if (!$birthday instanceof DateAndOrTime) {
return null;
}

// Skip if we can't parse the BDAY value.
try {
$dateParts = DateTimeParser::parseVCardDateTime($birthday->getValue());
} catch (InvalidDataException $e) {
return null;
}

$unknownYear = false;
if (!$dateParts['year']) {
$birthday = '1900-' . $dateParts['month'] . '-' . $dateParts['date'];

$unknownYear = true;
}

try {
$date = new \DateTime($birthday);
} catch (Exception $e) {
return null;
}

$summary = $title . ' (' . $summarySymbol . $date->format('Y') . ')';
if ($unknownYear) {
$summary = $doc->FN->getValue() . ' ' . $summarySymbol;
} else {
$year = (int)$date->format('Y');
$summary = $doc->FN->getValue() . " ($summarySymbol$year)";
}
$vCal = new VCalendar();
$vCal->VERSION = '2.0';
$vEvent = $vCal->createComponent('VEVENT');

+ 17
- 12
apps/dav/tests/unit/CardDAV/BirthdayServiceTest.php View File

@@ -55,18 +55,18 @@ class BirthdayServiceTest extends TestCase {

/**
* @dataProvider providesVCards
* @param boolean $nullExpected
* @param boolean $expectedSummary
* @param string | null $data
*/
public function testBuildBirthdayFromContact($nullExpected, $data) {
public function testBuildBirthdayFromContact($expectedSummary, $data) {
$cal = $this->service->buildDateFromContact($data, 'BDAY', '*');
if ($nullExpected) {
if ($expectedSummary === null) {
$this->assertNull($cal);
} else {
$this->assertInstanceOf('Sabre\VObject\Component\VCalendar', $cal);
$this->assertTrue(isset($cal->VEVENT));
$this->assertEquals('FREQ=YEARLY', $cal->VEVENT->RRULE->getValue());
$this->assertEquals('12345 (*1900)', $cal->VEVENT->SUMMARY->getValue());
$this->assertEquals($expectedSummary, $cal->VEVENT->SUMMARY->getValue());
$this->assertEquals('TRANSPARENT', $cal->VEVENT->TRANSP->getValue());
}
}
@@ -233,14 +233,19 @@ class BirthdayServiceTest extends TestCase {

public function providesVCards() {
return [
[true, null],
[true, ''],
[true, 'yasfewf'],
[true, "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", "Dr. Foo Bar"],
[true, "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", "Dr. Foo Bar"],
[true, "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", "Dr. Foo Bar"],
[false, "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:1900-01-01\r\nEND:VCARD\r\n", "Dr. Foo Bar"],
[false, "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:1900-12-31\r\nEND:VCARD\r\n", "Dr. Foo Bar"],
[null, null],
[null, ''],
[null, 'yasfewf'],
[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", "Dr. Foo Bar"],
[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", "Dr. Foo Bar"],
[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", "Dr. Foo Bar"],
['12345 (*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", "Dr. Foo Bar"],
['12345 (*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", "Dr. Foo Bar"],
['12345 *', "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", "Dr. Foo Bar"],
['12345 *', "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", "Dr. Foo Bar"],
[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", "Dr. Foo Bar"],
[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", "Dr. Foo Bar"],
['12345 (*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", "Dr. Foo Bar"],
];
}
}

Loading…
Cancel
Save