diff --git a/app/Domains/Contact/ManageContact/Dav/ExportAdr.php b/app/Domains/Contact/ManageContact/Dav/ExportAddress.php similarity index 95% rename from app/Domains/Contact/ManageContact/Dav/ExportAdr.php rename to app/Domains/Contact/ManageContact/Dav/ExportAddress.php index d7a3a0793a4..ad02a70ccef 100644 --- a/app/Domains/Contact/ManageContact/Dav/ExportAdr.php +++ b/app/Domains/Contact/ManageContact/Dav/ExportAddress.php @@ -11,7 +11,7 @@ * @implements ExportVCardResource */ #[Order(20)] -class ExportAdr implements ExportVCardResource +class ExportAddress implements ExportVCardResource { public function getType(): string { diff --git a/app/Domains/Contact/ManageContact/Dav/ImportAddress.php b/app/Domains/Contact/ManageContact/Dav/ImportAddress.php new file mode 100644 index 00000000000..3efa2e82b21 --- /dev/null +++ b/app/Domains/Contact/ManageContact/Dav/ImportAddress.php @@ -0,0 +1,160 @@ +KIND || $vcard->select('X-ADDRESSBOOKSERVER-KIND')); + if (! empty($kind) && $kind !== 'individual') { + return false; + } + + return true; + } + + /** + * Import Contact addresses. + */ + public function import(VCard $vcard, ?VCardResource $result): ?VCardResource + { + /** @var Contact $contact */ + $contact = $result; + + $addresses = $contact->addresses() + ->wherePivot('is_past_address', false) + ->get(); + $adr = $vcard->select('ADR'); + + for ($i = 0; $i < count($adr) || $i < $addresses->count(); $i++) { + if ($i < count($adr)) { + $addressType = $this->getAddressType($adr[$i]); + + if ($i < $addresses->count()) { + $this->updateAddress($adr[$i], $addresses[$i], $addressType); + } else { + $this->createAddress($contact, $adr[$i], $addressType); + } + } elseif ($i < $addresses->count()) { + $this->removeAddress($contact, $addresses[$i]); + } + } + + return $contact->refresh(); + } + + private function getAddressType(Property $adr): ?AddressType + { + $type = Arr::get($adr->parameters(), 'TYPE'); + + if ($type) { + try { + return AddressType::where([ + 'account_id' => $this->account()->id, + 'name' => $type->getValue(), + ])->firstOrFail(); + } catch (ModelNotFoundException) { + try { + return (new CreateAddressType)->execute([ + 'account_id' => $this->account()->id, + 'author_id' => $this->author()->id, + 'name' => $type->getValue(), + ]); + } catch (NotEnoughPermissionException) { + // catch + } + } + } + + return null; + } + + private function updateAddress(Property $adr, Address $address, ?AddressType $addressType) + { + (new UpdateAddress)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'address_id' => $address->id, + 'address_type_id' => optional($addressType)->id, + 'line_1' => $adr->getParts()[1], + 'line_2' => $adr->getParts()[2], + 'city' => $adr->getParts()[3], + 'province' => $adr->getParts()[4], + 'postal_code' => $adr->getParts()[5], + 'country' => $adr->getParts()[6], + ]); + } + + private function createAddress(Contact $contact, Property $adr, ?AddressType $addressType) + { + $address = Address::where([ + 'vault_id' => $this->vault()->id, + 'address_type_id' => optional($addressType)->id, + 'line_1' => $adr->getParts()[1], + 'line_2' => $adr->getParts()[2], + 'city' => $adr->getParts()[3], + 'province' => $adr->getParts()[4], + 'postal_code' => $adr->getParts()[5], + 'country' => $adr->getParts()[6], + ])->first(); + + if ($address === null) { + $address = (new CreateAddress)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'address_type_id' => optional($addressType)->id, + 'line_1' => $adr->getParts()[1], + 'line_2' => $adr->getParts()[2], + 'city' => $adr->getParts()[3], + 'province' => $adr->getParts()[4], + 'postal_code' => $adr->getParts()[5], + 'country' => $adr->getParts()[6], + ]); + } + + (new AssociateAddressToContact)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'contact_id' => $contact->id, + 'address_id' => $address->id, + 'is_past_address' => false, + ]); + } + + private function removeAddress(Contact $contact, Address $address) + { + (new RemoveAddressFromContact)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'contact_id' => $contact->id, + 'address_id' => $address->id, + ]); + } +} diff --git a/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php b/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php new file mode 100644 index 00000000000..f8d7725e835 --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php @@ -0,0 +1,48 @@ +createUser(); + $vault = $this->createVaultUser($user); + $contact = Contact::factory()->random()->create(['vault_id' => $vault->id]); + $address = Address::factory()->create(['vault_id' => $vault->id]); + $address->contacts()->attach($contact->id); + + $vCard = new VCard(); + (new ExportAddress)->export($contact, $vCard); + + $this->assertCount( + self::defaultPropsCount + 1, + $vCard->children() + ); + + $text = "ADR;TYPE={$address->addressType->name}:;{$address->line_1};{$address->line_2};{$address->city};{$address->province};{$address->postal_code};{$address->country}"; + $this->assertStringContainsString(Str::substr($text, 0, 75), $vCard->serialize()); + $this->assertStringContainsString(' '.Str::substr($text, 76), $vCard->serialize()); + } +} diff --git a/tests/Unit/Domains/Contact/ManageContact/Dav/ImportAddressTest.php b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportAddressTest.php new file mode 100644 index 00000000000..6c8c9af396e --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportAddressTest.php @@ -0,0 +1,153 @@ +createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportAddress(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + + $vcard = new VCard([ + 'ADR' => [ + '', + 'line 1', + 'line 2', + 'city', + 'province', + 'postal code', + 'country', + ], + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(1, $contact->addresses); + $address = $contact->addresses->first(); + $this->assertEquals('line 1', $address->line_1); + $this->assertEquals('line 2', $address->line_2); + $this->assertEquals('city', $address->city); + $this->assertEquals('province', $address->province); + $this->assertEquals('postal code', $address->postal_code); + $this->assertEquals('country', $address->country); + } + + #[Group('dav')] + #[Test] + public function it_imports_address_type() + { + $user = $this->createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportAddress(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + + AddressType::factory()->create([ + 'account_id' => $user->account_id, + 'name' => 'home', + ]); + + $vcard = new VCard(); + $vcard->add('ADR', [ + '', + 'line 1', + 'line 2', + 'city', + 'province', + 'postal code', + 'country', + ], [ + 'TYPE' => 'home', + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(1, $contact->addresses); + $address = $contact->addresses->first(); + + $this->assertNotNull($address->addressType); + $this->assertEquals('home', $address->addressType->name); + } + + #[Group('dav')] + #[Test] + public function it_imports_new_address_and_remove_old() + { + $user = $this->createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportAddress(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + $addresses = Address::factory(2)->create([ + 'vault_id' => $vault->id, + ]); + foreach ($addresses as $address) { + $contact->addresses()->attach($address, + ['is_past_address' => false] + ); + } + + $vcard = new VCard([ + 'ADR' => [ + '', + 'line 1', + 'line 2', + 'city', + 'province', + 'postal code', + 'country', + ], + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(1, $contact->addresses); + $address = $contact->addresses->first(); + $this->assertEquals('line 1', $address->line_1); + $this->assertEquals('line 2', $address->line_2); + $this->assertEquals('city', $address->city); + $this->assertEquals('province', $address->province); + $this->assertEquals('postal code', $address->postal_code); + $this->assertEquals('country', $address->country); + } +}