Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
philipp lang | 8fcbec95b0 |
|
@ -0,0 +1,339 @@
|
|||
<?php
|
||||
|
||||
namespace App\Dav;
|
||||
|
||||
use App\Member\Member;
|
||||
use App\User;
|
||||
use Sabre\CalDAV\Backend\AbstractBackend;
|
||||
use Sabre\DAV\PropPatch;
|
||||
use Sabre\VObject\Component\VCard;
|
||||
|
||||
/**
|
||||
* @template M as array{lastmodified: int, etag: string, uri: string, id: int, size: int}
|
||||
*/
|
||||
class CalendarBackend extends AbstractBackend
|
||||
{
|
||||
/**
|
||||
* Returns a list of calendars for a principal.
|
||||
*
|
||||
* Every project is an array with the following keys:
|
||||
* * id, a unique id that will be used by other functions to modify the
|
||||
* calendar. This can be the same as the uri or a database key.
|
||||
* * uri, which is the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * principaluri. The owner of the calendar. Almost always the same as
|
||||
* principalUri passed to this method.
|
||||
*
|
||||
* Furthermore it can contain webdav properties in clark notation. A very
|
||||
* common one is '{DAV:}displayname'.
|
||||
*
|
||||
* Many clients also require:
|
||||
* {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
|
||||
* For this property, you can just return an instance of
|
||||
* Sabre\CalDAV\Property\SupportedCalendarComponentSet.
|
||||
*
|
||||
* If you return {http://sabredav.org/ns}read-only and set the value to 1,
|
||||
* ACL will automatically be put in read-only mode.
|
||||
*
|
||||
* @param string $principalUri
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri)
|
||||
{
|
||||
if (1 !== preg_match('/^principals\/(.*)$/', $principalUri, $matches)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
User::where('email', $matches[1])->firstOrFail();
|
||||
|
||||
return [
|
||||
[
|
||||
'id' => 'birthdays',
|
||||
'principaluri' => $principalUri,
|
||||
'uri' => 'birthdays',
|
||||
'{DAV:}displayname' => 'Geburtstage',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to
|
||||
* reference this calendar in other methods, such as updateCalendar.
|
||||
*
|
||||
* The id can be any type, including ints, strings, objects or array.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array<string, mixed> $properties
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function createCalendar($principalUri, $calendarUri, array $properties)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties for a calendar.
|
||||
*
|
||||
* The list of mutations is stored in a Sabre\DAV\PropPatch object.
|
||||
* To do the actual updates, you must tell this object which properties
|
||||
* you're going to process with the handle() method.
|
||||
*
|
||||
* Calling the handle method is like telling the PropPatch object "I
|
||||
* promise I can handle updating this property".
|
||||
*
|
||||
* Read the PropPatch documentation for more info and examples.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all its objects.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * calendardata - The iCalendar-compatible calendar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can
|
||||
* be any arbitrary string, but making sure it ends with '.ics' is a
|
||||
* good idea. This is only the basename, or filename, not the full
|
||||
* path.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* '"abcdef"')
|
||||
* * size - The size of the calendar objects, in bytes.
|
||||
* * component - optional, a string containing the type of object, such
|
||||
* as 'vevent' or 'vtodo'. If specified, this will be used to populate
|
||||
* the Content-Type header.
|
||||
*
|
||||
* Note that the etag is optional, but it's highly encouraged to return for
|
||||
* speed reasons.
|
||||
*
|
||||
* The calendardata is also optional. If it's not returned
|
||||
* 'getCalendarObject' will be called later, which *is* expected to return
|
||||
* calendardata.
|
||||
*
|
||||
* If neither etag or size are specified, the calendardata will be
|
||||
* used/fetched to determine these numbers. If both are specified the
|
||||
* amount of times this is needed is reduced by a great degree.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
*
|
||||
* @return array
|
||||
* @return void
|
||||
*/
|
||||
public function getCalendarObjects($calendarId)
|
||||
{
|
||||
return Member::whereNotNull('birthday')->get()->map(fn ($member) => $this->calendarObjectMeta($member))->toArray();
|
||||
}
|
||||
|
||||
private function calendarObjectMeta(Member $member): array
|
||||
{
|
||||
return [
|
||||
'calendardata' => $member->toCalendarObject()->serialize(),
|
||||
'uri' => $member->slug . '.ics',
|
||||
'lastmodified' => $member->updated_at->timestamp,
|
||||
'etag' => '"' . $member->etag . '"',
|
||||
'size' => strlen($member->toCalendarObject()->serialize()),
|
||||
'component' => 'vevent',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* The returned array must have the same keys as getCalendarObjects. The
|
||||
* 'calendardata' object is required here though, while it's not required
|
||||
* for getCalendarObjects.
|
||||
*
|
||||
* This method must return null if the object did not exist.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getCalendarObject($calendarId, $objectUri)
|
||||
{
|
||||
$member = Member::where('slug', str($objectUri)->replace('.ics', ''))->first();
|
||||
|
||||
if (!$member || !$member->toCalendarObject()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
...$this->calendarObjectMeta($member),
|
||||
'calendardata' => $member->toCalendarObject()->serialize(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of calendar objects.
|
||||
*
|
||||
* This method should work identical to getCalendarObject, but instead
|
||||
* return all the calendar objects in the list as an array.
|
||||
*
|
||||
* If the backend supports this, it may allow for some speed-ups.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMultipleCalendarObjects($calendarId, array $uris)
|
||||
{
|
||||
return Member::whereNotNull('birthday')->get()->map(fn ($member) => $this->getCalendarObject($calendarId, $member->slug . '.ics'))->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* It is possible to return an etag from this function, which will be used
|
||||
* in the response to this PUT request. Note that the ETag must be
|
||||
* surrounded by double-quotes.
|
||||
*
|
||||
* However, you should only really return this ETag if you don't mangle the
|
||||
* calendar-data. If the result of a subsequent GET to this object is not
|
||||
* the exact same as this request body, you should omit the ETag.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function createCalendarObject($calendarId, $objectUri, $calendarData)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* It is possible return an etag from this function, which will be used in
|
||||
* the response to this PUT request. Note that the ETag must be surrounded
|
||||
* by double-quotes.
|
||||
*
|
||||
* However, you should only really return this ETag if you don't mangle the
|
||||
* calendar-data. If the result of a subsequent GET to this object is not
|
||||
* the exact same as this request body, you should omit the ETag.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function updateCalendarObject($calendarId, $objectUri, $calendarData)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId, $objectUri)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a calendar-query on the contents of this calendar.
|
||||
*
|
||||
* The calendar-query is defined in RFC4791 : CalDAV. Using the
|
||||
* calendar-query it is possible for a client to request a specific set of
|
||||
* object, based on contents of iCalendar properties, date-ranges and
|
||||
* iCalendar component types (VTODO, VEVENT).
|
||||
*
|
||||
* This method should just return a list of (relative) urls that match this
|
||||
* query.
|
||||
*
|
||||
* The list of filters are specified as an array. The exact array is
|
||||
* documented by Sabre\CalDAV\CalendarQueryParser.
|
||||
*
|
||||
* Note that it is extremely likely that getCalendarObject for every path
|
||||
* returned from this method will be called almost immediately after. You
|
||||
* may want to anticipate this to speed up these requests.
|
||||
*
|
||||
* This method provides a default implementation, which parses *all* the
|
||||
* iCalendar objects in the specified calendar.
|
||||
*
|
||||
* This default may well be good enough for personal use, and calendars
|
||||
* that aren't very large. But if you anticipate high usage, big calendars
|
||||
* or high loads, you are strongly adviced to optimize certain paths.
|
||||
*
|
||||
* The best way to do so is override this method and to optimize
|
||||
* specifically for 'common filters'.
|
||||
*
|
||||
* Requests that are extremely common are:
|
||||
* * requests for just VEVENTS
|
||||
* * requests for just VTODO
|
||||
* * requests with a time-range-filter on either VEVENT or VTODO.
|
||||
*
|
||||
* ..and combinations of these requests. It may not be worth it to try to
|
||||
* handle every possible situation and just rely on the (relatively
|
||||
* easy to use) CalendarQueryValidator to handle the rest.
|
||||
*
|
||||
* Note that especially time-range-filters may be difficult to parse. A
|
||||
* time-range filter specified on a VEVENT must for instance also handle
|
||||
* recurrence rules correctly.
|
||||
* A good example of how to interprete all these filters can also simply
|
||||
* be found in Sabre\CalDAV\CalendarQueryFilter. This class is as correct
|
||||
* as possible, so it gives you a good idea on what type of stuff you need
|
||||
* to think of.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function calendarQuery($calendarId, array $filters)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches through all of a users calendars and calendar objects to find
|
||||
* an object with a specific UID.
|
||||
*
|
||||
* This method should return the path to this object, relative to the
|
||||
* calendar home, so this path usually only contains two parts:
|
||||
*
|
||||
* calendarpath/objectpath.ics
|
||||
*
|
||||
* If the uid is not found, return null.
|
||||
*
|
||||
* This method should only consider * objects that the principal owns, so
|
||||
* any calendars owned by other principals that also appear in this
|
||||
* collection should be ignored.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $uid
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCalendarObjectByUID($principalUri, $uid)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -6,7 +6,9 @@ use Illuminate\Support\ServiceProvider as BaseServiceProvider;
|
|||
use LaravelSabre\Http\Auth\AuthBackend;
|
||||
use LaravelSabre\LaravelSabre;
|
||||
use Sabre\CardDAV\AddressBookRoot;
|
||||
use Sabre\CalDAV\CalendarRoot;
|
||||
use Sabre\CardDAV\Plugin as CardDAVPlugin;
|
||||
use Sabre\CalDAV\Plugin as CalDAVPlugin;
|
||||
use Sabre\DAV\Auth\Plugin as AuthPlugin;
|
||||
use Sabre\DAV\Browser\Plugin as BrowserPlugin;
|
||||
use Sabre\DAVACL\AbstractPrincipalCollection;
|
||||
|
@ -42,11 +44,12 @@ class ServiceProvider extends BaseServiceProvider
|
|||
{
|
||||
$principalBackend = new Principal();
|
||||
$addressBookBackend = new AddressBookBackend();
|
||||
$calendarBackend = new CalendarBackend();
|
||||
|
||||
// Directory tree
|
||||
return [
|
||||
new PrincipalCollection($principalBackend),
|
||||
new AddressBookRoot($principalBackend, $addressBookBackend),
|
||||
new CalendarRoot($principalBackend, $calendarBackend),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -58,6 +61,7 @@ class ServiceProvider extends BaseServiceProvider
|
|||
new BrowserPlugin(),
|
||||
new AuthPlugin($authBackend),
|
||||
new CardDAVPlugin(),
|
||||
new CalDAVPlugin(),
|
||||
new AclPlugin(),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Scout\Attributes\SearchUsingFullText;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
use Sabre\VObject\Component\VCard;
|
||||
use Sabre\VObject\Reader;
|
||||
use Spatie\LaravelData\Lazy;
|
||||
|
@ -437,6 +437,24 @@ class Member extends Model implements Geolocatable
|
|||
return $card;
|
||||
}
|
||||
|
||||
public function toCalendarObject(): ?VCalendar
|
||||
{
|
||||
if (!$this->birthday) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$vcalendar = new VCalendar([
|
||||
'VEVENT' => [
|
||||
'SUMMARY' => 'Geburtstag von ' . $this->fullname,
|
||||
'RRULE' => 'FREQ=YEARLY',
|
||||
]
|
||||
]);
|
||||
|
||||
$vcalendar->VEVENT->add('DTSTART', new \DateTime($this->birthday->format('Y-m-d')), ['VALUE' => 'DATE']);
|
||||
|
||||
return $vcalendar;
|
||||
}
|
||||
|
||||
public function toSender(): Sender
|
||||
{
|
||||
return Sender::from([
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
|
||||
namespace Tests\Feature\Member;
|
||||
|
||||
use App\Group;
|
||||
use App\Member\Member;
|
||||
use App\Nationality;
|
||||
use App\Payment\Subscription;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -14,101 +11,28 @@ class DavTest extends TestCase
|
|||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function testItCanStoreAMemberFromAVcard(): void
|
||||
public function testItDisplaysCalendar(): void
|
||||
{
|
||||
Nationality::factory()->create(['name' => 'englisch']);
|
||||
$subscription = Subscription::factory()->forFee()->create(['name' => 'Voll']);
|
||||
$nationality = Nationality::factory()->create(['name' => 'deutsch']);
|
||||
$group = Group::factory()->create();
|
||||
NamiSettings::fake(['default_group_id' => $group->id]);
|
||||
$cardUri = '97266d2e-36e7-4fb6-8b6c-bbf57a061685.vcf';
|
||||
$cardData = <<<VCARD
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
PRODID:-//Thunderbird.net/NONSGML Thunderbird CardBook V77.0//EN-US
|
||||
UID:97266d2e-36e7-4fb6-8b6c-bbf57a061685
|
||||
CATEGORIES:Scoutrobot
|
||||
FN:given familya Silva
|
||||
N:familya;given;;;
|
||||
BDAY:20221003
|
||||
ORG:Silva
|
||||
EMAIL:mail@maild.ee
|
||||
ITEM1.TEL:+49 176 70342420
|
||||
ITEM1.X-ABLABEL:eltern
|
||||
ADR:;;Itterstr 3;Solingen;NRW;42719;Germany
|
||||
REV:2022-10-07T14:17:06Z
|
||||
END:VCARD
|
||||
$this->createDavUser()->withoutExceptionHandling();
|
||||
|
||||
VCARD;
|
||||
$member = Member::fromVcard($cardUri, $cardData);
|
||||
|
||||
$member->save();
|
||||
|
||||
$this->assertDatabaseHas('members', [
|
||||
'slug' => '97266d2e-36e7-4fb6-8b6c-bbf57a061685',
|
||||
'firstname' => 'given',
|
||||
'lastname' => 'familya',
|
||||
'address' => 'Itterstr 3',
|
||||
'zip' => '42719',
|
||||
'location' => 'Solingen',
|
||||
'group_id' => $group->id,
|
||||
'nationality_id' => $nationality->id,
|
||||
'subscription_id' => $subscription->id,
|
||||
]);
|
||||
$this->getDavCalendars()->assertSee('Geburtstage');
|
||||
}
|
||||
|
||||
public function testTheVcardHasTheMembersSlug(): void
|
||||
public function testItDisplaysBirthdaysOfMembers(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['firstname' => 'max', 'lastname' => 'muster']);
|
||||
$this->createDavUser()->withoutExceptionHandling();
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertEquals('max-muster', $card->UID->getValue());
|
||||
}
|
||||
|
||||
public function testItSetsTheNames(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertEquals(['Muster', 'Max', '', '', ''], $card->N->getParts());
|
||||
$this->assertEquals('Max Muster', $card->FN->getValue());
|
||||
}
|
||||
|
||||
public function testItDoesntNeedBirthday(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['birthday' => null]);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertNull($card->BDAY);
|
||||
}
|
||||
|
||||
public function testItSetsTheBirthday(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['birthday' => '1993-05-06']);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertEquals('19930506', $card->BDAY->getValue());
|
||||
}
|
||||
|
||||
public function testItUnsetsMobilePhoneNumber(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create();
|
||||
|
||||
$member->update(['mobile_phone' => '']);
|
||||
$this->getDavCalendar('birthdays')->assertSee($member->slug . '.ics');
|
||||
}
|
||||
|
||||
if (!is_null($member->toVcard()->TEL)) {
|
||||
foreach ($member->toVcard()->TEL as $t) {
|
||||
if ($t['TYPE'] && 'cell' === $t['TYPE']->getValue()) {
|
||||
$this->assertFalse(true, 'Phone number found');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function testItGetsObjectsWhenBirthdayIsNull(): void
|
||||
{
|
||||
$this->createDavUser()->withoutExceptionHandling();
|
||||
|
||||
$this->assertTrue(true);
|
||||
$member = Member::factory()->defaults()->create(['birthday' => null]);
|
||||
|
||||
$this->getDavCalendar('birthdays')->assertStatus(207);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Member;
|
||||
|
||||
use App\Group;
|
||||
use App\Member\Member;
|
||||
use App\Nationality;
|
||||
use App\Payment\Subscription;
|
||||
use App\Setting\NamiSettings;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
class VcardTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function testItCanStoreAMemberFromAVcard(): void
|
||||
{
|
||||
Nationality::factory()->create(['name' => 'englisch']);
|
||||
$subscription = Subscription::factory()->forFee()->create(['name' => 'Voll']);
|
||||
$nationality = Nationality::factory()->create(['name' => 'deutsch']);
|
||||
$group = Group::factory()->create();
|
||||
NamiSettings::fake(['default_group_id' => $group->id]);
|
||||
$cardUri = '97266d2e-36e7-4fb6-8b6c-bbf57a061685.vcf';
|
||||
$cardData = <<<VCARD
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
PRODID:-//Thunderbird.net/NONSGML Thunderbird CardBook V77.0//EN-US
|
||||
UID:97266d2e-36e7-4fb6-8b6c-bbf57a061685
|
||||
CATEGORIES:Scoutrobot
|
||||
FN:given familya Silva
|
||||
N:familya;given;;;
|
||||
BDAY:20221003
|
||||
ORG:Silva
|
||||
EMAIL:mail@maild.ee
|
||||
ITEM1.TEL:+49 176 70342420
|
||||
ITEM1.X-ABLABEL:eltern
|
||||
ADR:;;Itterstr 3;Solingen;NRW;42719;Germany
|
||||
REV:2022-10-07T14:17:06Z
|
||||
END:VCARD
|
||||
|
||||
VCARD;
|
||||
$member = Member::fromVcard($cardUri, $cardData);
|
||||
|
||||
$member->save();
|
||||
|
||||
$this->assertDatabaseHas('members', [
|
||||
'slug' => '97266d2e-36e7-4fb6-8b6c-bbf57a061685',
|
||||
'firstname' => 'given',
|
||||
'lastname' => 'familya',
|
||||
'address' => 'Itterstr 3',
|
||||
'zip' => '42719',
|
||||
'location' => 'Solingen',
|
||||
'group_id' => $group->id,
|
||||
'nationality_id' => $nationality->id,
|
||||
'subscription_id' => $subscription->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testTheVcardHasTheMembersSlug(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['firstname' => 'max', 'lastname' => 'muster']);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertEquals('max-muster', $card->UID->getValue());
|
||||
}
|
||||
|
||||
public function testItSetsTheNames(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertEquals(['Muster', 'Max', '', '', ''], $card->N->getParts());
|
||||
$this->assertEquals('Max Muster', $card->FN->getValue());
|
||||
}
|
||||
|
||||
public function testItDoesntNeedBirthday(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['birthday' => null]);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertNull($card->BDAY);
|
||||
}
|
||||
|
||||
public function testItSetsTheBirthday(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create(['birthday' => '1993-05-06']);
|
||||
|
||||
$card = $member->toVcard();
|
||||
|
||||
$this->assertEquals('19930506', $card->BDAY->getValue());
|
||||
}
|
||||
|
||||
public function testItUnsetsMobilePhoneNumber(): void
|
||||
{
|
||||
$member = Member::factory()->defaults()->create();
|
||||
|
||||
$member->update(['mobile_phone' => '']);
|
||||
|
||||
if (!is_null($member->toVcard()->TEL)) {
|
||||
foreach ($member->toVcard()->TEL as $t) {
|
||||
if ($t['TYPE'] && 'cell' === $t['TYPE']->getValue()) {
|
||||
$this->assertFalse(true, 'Phone number found');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Lib;
|
||||
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
|
||||
trait TestsDav
|
||||
{
|
||||
|
||||
public $davUserEmail = 'user@example.com';
|
||||
public $davUserPassword = 'secret';
|
||||
|
||||
public function getDavCalendars(): TestResponse
|
||||
{
|
||||
$this->withBasicAuth($this->davUserEmail, $this->davUserPassword);
|
||||
return $this->call('PROPFIND', '/dav/calendars/' . $this->davUserEmail, [], [], [], $this->transformHeadersToServerVars([]));
|
||||
}
|
||||
|
||||
|
||||
public function getDavCalendar(string $calendar): TestResponse
|
||||
{
|
||||
$this->withBasicAuth($this->davUserEmail, $this->davUserPassword);
|
||||
return $this->call('PROPFIND', '/dav/calendars/' . $this->davUserEmail . '/' . $calendar, [], [], [], $this->transformHeadersToServerVars([]));
|
||||
}
|
||||
|
||||
public function createDavUser(): self
|
||||
{
|
||||
$this->me = User::factory()->create(['email' => $this->davUserEmail, 'password' => Hash::make($this->davUserPassword)]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ use Illuminate\Testing\TestResponse;
|
|||
use Phake;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Tests\Lib\MakesHttpCalls;
|
||||
use Tests\Lib\TestsDav;
|
||||
use Tests\Lib\TestsInertia;
|
||||
use Zoomyboy\LaravelNami\Authentication\Auth;
|
||||
|
||||
|
@ -23,6 +24,7 @@ abstract class TestCase extends BaseTestCase
|
|||
use CreatesApplication;
|
||||
use TestsInertia;
|
||||
use MakesHttpCalls;
|
||||
use TestsDav;
|
||||
|
||||
protected User $me;
|
||||
|
||||
|
|
Loading…
Reference in New Issue