diff --git a/app/Dav/AddressBookBackend.php b/app/Dav/AddressBookBackend.php index 97732d5a..179ee305 100644 --- a/app/Dav/AddressBookBackend.php +++ b/app/Dav/AddressBookBackend.php @@ -137,7 +137,11 @@ class AddressBookBackend extends AbstractBackend */ public function getCard($addressBookId, $cardUri) { - $member = Member::where('slug', $cardUri)->firstOrFail(); + $member = Member::where('slug', $cardUri)->first(); + + if (!$member) { + return false; + } return [ ...$this->cardMeta($member), @@ -193,7 +197,10 @@ class AddressBookBackend extends AbstractBackend */ public function createCard($addressBookId, $cardUri, $cardData) { - return null; + $member = Member::fromVcard($cardUri, $cardData); + $member->save(); + + return $member->fresh()->etag; } /** diff --git a/app/Member/Member.php b/app/Member/Member.php index f55919d8..76d8ee49 100644 --- a/app/Member/Member.php +++ b/app/Member/Member.php @@ -14,6 +14,7 @@ use App\Payment\Subscription; use App\Region; use App\Setting\NamiSettings; use App\Subactivity; +use Carbon\Carbon; use Cviebrock\EloquentSluggable\Sluggable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -22,6 +23,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Notifications\Notifiable; use Sabre\VObject\Component\VCard; +use Sabre\VObject\Reader; use Zoomyboy\LaravelNami\Api; use Zoomyboy\LaravelNami\Data\MembershipEntry; @@ -378,7 +380,30 @@ class Member extends Model ]); } - public function toVcard(): VCard + public static function fromVcard(string $url, string $data): static + { + $settings = app(NamiSettings::class); + $card = Reader::read($data); + [$lastname, $firstname] = $card->N->getParts(); + [$deprecated1, $deprecated2 , $address, $location, $region, $zip, $country] = $card->ADR->getParts(); + + return new static([ + 'joined_at' => now(), + 'send_newspaper' => false, + 'firstname' => $firstname, + 'lastname' => $lastname, + 'birthday' => Carbon::createFromFormat('Ymd', $card->BDAY->getValue()), + 'slug' => pathinfo($url, PATHINFO_FILENAME), + 'address' => $address, + 'zip' => $zip, + 'location' => $location, + 'group_id' => $settings->default_group_id, + 'nationality_id' => Nationality::firstWhere('name', 'deutsch')->id, + 'subscription_id' => Subscription::firstWhere('name', 'Voll')->id, + ]); + } + + public function toVcard(): Vcard { $card = new VCard([ 'VERSION' => '3.0', @@ -387,6 +412,7 @@ class Member extends Model 'N' => [$this->lastname, $this->firstname, '', '', ''], 'BDAY' => $this->birthday->format('Ymd'), 'CATEGORIES' => 'Scoutrobot', + 'UID' => $this->slug, ]); $card->add('child.X-ABLABEL', 'Kind'); diff --git a/tests/Feature/Member/DavTest.php b/tests/Feature/Member/DavTest.php new file mode 100644 index 00000000..35e47829 --- /dev/null +++ b/tests/Feature/Member/DavTest.php @@ -0,0 +1,125 @@ +<?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 DavTest extends TestCase +{ + use DatabaseTransactions; + + public function testItCanStoreAMemberFromAVcard(): void + { + Nationality::factory()->create(['name' => 'englisch']); + $subscription = Subscription::factory()->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 testItSetsTheBirthday(): void + { + $member = Member::factory()->defaults()->create(['birthday' => '1993-05-06']); + + $card = $member->toVcard(); + + $this->assertEquals('19930506', $card->BDAY->getValue()); + } + + public function testItCanSetAndUnsetMobilePhone(): void + { + $member = Member::factory()->defaults()->create(); + + $member->update(['mobile_phone' => '+49 176 555555']); + + $this->assertTrue(count($member->toVcard()->TEL) > 0); + foreach ($member->toVcard()->TEL as $t) { + if (!$t['TYPE'] || 'cell' !== $t['TYPE']->getValue()) { + continue; + } + + $this->assertEquals('+49 176 555555', $t->getValue()); + + return; + } + + $this->assertFalse(true, 'No Phone number found in card'); + } + + 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); + } +}