From 84d6a3f80fa24577ceae0abf201227511cee3f30 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Sat, 4 Nov 2023 23:02:46 +0100 Subject: [PATCH] feat: add more vcard exports (#6997) --- .../Contact/Dav/ImportVCardResource.php | 5 + app/Domains/Contact/Dav/Importer.php | 22 ++ .../Contact/Dav/Services/ImportVCard.php | 24 ++- .../Dav/{ExportAdr.php => ExportAddress.php} | 2 +- .../ManageContact/Dav/ExportLabels.php | 34 +++ .../ManageContact/Dav/ImportAddress.php | 155 ++++++++++++++ .../ManageContact/Dav/ImportContact.php | 19 +- .../ManageContact/Dav/ImportLabels.php | 102 +++++++++ .../Contact/ManageGroups/Dav/ImportGroup.php | 11 +- .../ManageGroups/Dav/ImportMembers.php | 11 +- .../Services/CreateAddressType.php | 4 +- .../Services/CreateLabel.php | 8 +- database/factories/LabelFactory.php | 4 +- ...xportAdrTest.php => ExportAddressTest.php} | 6 +- .../ManageContact/Dav/ExportLabelsTest.php | 68 ++++++ .../ManageContact/Dav/ImportAddressTest.php | 196 ++++++++++++++++++ .../ManageContact/Dav/ImportLabelsTest.php | 142 +++++++++++++ 17 files changed, 767 insertions(+), 46 deletions(-) rename app/Domains/Contact/ManageContact/Dav/{ExportAdr.php => ExportAddress.php} (95%) create mode 100644 app/Domains/Contact/ManageContact/Dav/ExportLabels.php create mode 100644 app/Domains/Contact/ManageContact/Dav/ImportAddress.php create mode 100644 app/Domains/Contact/ManageContact/Dav/ImportLabels.php rename tests/Unit/Domains/Contact/ManageContact/Dav/{ExportAdrTest.php => ExportAddressTest.php} (90%) create mode 100644 tests/Unit/Domains/Contact/ManageContact/Dav/ExportLabelsTest.php create mode 100644 tests/Unit/Domains/Contact/ManageContact/Dav/ImportAddressTest.php create mode 100644 tests/Unit/Domains/Contact/ManageContact/Dav/ImportLabelsTest.php diff --git a/app/Domains/Contact/Dav/ImportVCardResource.php b/app/Domains/Contact/Dav/ImportVCardResource.php index 0a414d474d7..0bddd4c29c5 100644 --- a/app/Domains/Contact/Dav/ImportVCardResource.php +++ b/app/Domains/Contact/Dav/ImportVCardResource.php @@ -12,6 +12,11 @@ interface ImportVCardResource */ public function setContext(ImportVCard $context): self; + /** + * Test if the Card is handled by this importer. + */ + public function handle(VCard $vcard): bool; + /** * Can import Card. */ diff --git a/app/Domains/Contact/Dav/Importer.php b/app/Domains/Contact/Dav/Importer.php index baed3e8ccde..44aef0d6014 100644 --- a/app/Domains/Contact/Dav/Importer.php +++ b/app/Domains/Contact/Dav/Importer.php @@ -13,6 +13,14 @@ abstract class Importer implements ImportVCardResource { public ImportVCard $context; + /** + * Can import Card. + */ + public function can(VCard $vcard): bool + { + return true; + } + /** * Set context. */ @@ -78,4 +86,18 @@ protected function importUid(array $data, VCard $entry): array return $data; } + + protected function kind(VCard $entry): string + { + $kind = $entry->KIND; + + if ($kind === null) { + $kinds = $entry->select('X-ADDRESSBOOKSERVER-KIND'); + if (! empty($kinds)) { + $kind = $kinds[0]; + } + } + + return optional($kind)->getValue() ?? 'individual'; + } } diff --git a/app/Domains/Contact/Dav/Services/ImportVCard.php b/app/Domains/Contact/Dav/Services/ImportVCard.php index e18eefea38b..249fdea7109 100644 --- a/app/Domains/Contact/Dav/Services/ImportVCard.php +++ b/app/Domains/Contact/Dav/Services/ImportVCard.php @@ -216,13 +216,20 @@ private function processEntryCard(VCard $entry, string $vcard): array */ private function canImportCurrentEntry(VCard $entry): bool { - foreach ($this->importers() as $importer) { - if ($importer->can($entry)) { - return true; + $importers = $this->importers() + ->filter(fn (ImportVCardResource $importer): bool => $importer->handle($entry)); + + if ($importers->isEmpty()) { + return false; + } + + foreach ($importers as $importer) { + if (! $importer->can($entry)) { + return false; } } - return false; + return true; } /** @@ -232,10 +239,11 @@ private function importEntry(VCard $entry): ?VCardResource { $result = null; - foreach ($this->importers() as $importer) { - if ($importer->can($entry)) { - $result = $importer->import($entry, $result); - } + $importers = $this->importers() + ->filter(fn (ImportVCardResource $importer): bool => $importer->handle($entry)); + + foreach ($importers as $importer) { + $result = $importer->import($entry, $result); } return $result; 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/ExportLabels.php b/app/Domains/Contact/ManageContact/Dav/ExportLabels.php new file mode 100644 index 00000000000..ca6a236a7e2 --- /dev/null +++ b/app/Domains/Contact/ManageContact/Dav/ExportLabels.php @@ -0,0 +1,34 @@ + + */ +#[Order(50)] +class ExportLabels extends Exporter implements ExportVCardResource +{ + public function getType(): string + { + return Contact::class; + } + + /** + * @param Contact $resource + */ + public function export(mixed $resource, VCard $vcard): void + { + // https://datatracker.ietf.org/doc/html/rfc6350#section-6.7.1 + $vcard->remove('CATEGORIES'); + + if ($resource->labels->count() > 0) { + $vcard->add('CATEGORIES', $resource->labels->pluck('name')->toArray()); + } + } +} diff --git a/app/Domains/Contact/ManageContact/Dav/ImportAddress.php b/app/Domains/Contact/ManageContact/Dav/ImportAddress.php new file mode 100644 index 00000000000..821bc1fd6b8 --- /dev/null +++ b/app/Domains/Contact/ManageContact/Dav/ImportAddress.php @@ -0,0 +1,155 @@ +kind($vcard) === 'individual'; + } + + /** + * 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/app/Domains/Contact/ManageContact/Dav/ImportContact.php b/app/Domains/Contact/ManageContact/Dav/ImportContact.php index 9d18b53a7bb..f197f18e6e3 100644 --- a/app/Domains/Contact/ManageContact/Dav/ImportContact.php +++ b/app/Domains/Contact/ManageContact/Dav/ImportContact.php @@ -22,21 +22,20 @@ #[Order(1)] class ImportContact extends Importer implements ImportVCardResource { + /** + * Test if the Card is handled by this importer. + */ + public function handle(VCard $vcard): bool + { + return $this->kind($vcard) === 'individual'; + } + /** * Can import Contact. */ public function can(VCard $vcard): bool { - if (! ($this->hasFN($vcard) || $this->hasNICKNAME($vcard) || $this->hasFirstnameInN($vcard))) { - return false; - } - - $kind = (string) ($vcard->KIND || $vcard->select('X-ADDRESSBOOKSERVER-KIND')); - if (! empty($kind) && $kind !== 'individual') { - return false; - } - - return true; + return $this->hasFN($vcard) || $this->hasNICKNAME($vcard) || $this->hasFirstnameInN($vcard); } /** diff --git a/app/Domains/Contact/ManageContact/Dav/ImportLabels.php b/app/Domains/Contact/ManageContact/Dav/ImportLabels.php new file mode 100644 index 00000000000..fe80be42082 --- /dev/null +++ b/app/Domains/Contact/ManageContact/Dav/ImportLabels.php @@ -0,0 +1,102 @@ +kind($vcard) === 'individual'; + } + + /** + * Import Contact labels. + */ + public function import(VCard $vcard, ?VCardResource $result): ?VCardResource + { + /** @var Contact $contact */ + $contact = $result; + + $labels = $contact->labels->mapWithKeys(fn (Label $label): array => [$label->name => $label]); + $categories = $this->getCategories($vcard); + + $toAdd = $categories->diffKeys($labels); + $toRemove = $labels->diffKeys($categories); + + $refresh = false; + foreach ($toRemove as $label) { + $this->removeLabel($contact, $label); + $refresh = true; + } + foreach ($toAdd as $name) { + $this->addLabel($contact, $name); + $refresh = true; + } + + return $refresh ? $contact->refresh() : $contact; + } + + private function getCategories(VCard $vcard): Collection + { + $categories = $vcard->CATEGORIES; + + return $categories === null + ? collect() + : collect($categories->getParts()); + } + + private function addLabel(Contact $contact, string $name): void + { + $label = $this->getLabel($name) ?? $this->createLabel($name); + + (new AssignLabel)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'contact_id' => $contact->id, + 'label_id' => $label->id, + ]); + } + + private function removeLabel(Contact $contact, Label $label): void + { + (new RemoveLabel)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'contact_id' => $contact->id, + 'label_id' => $label->id, + ]); + } + + private function getLabel(string $name): ?Label + { + return $this->vault()->labels()->where('name', $name)->first(); + } + + private function createLabel(string $name): Label + { + return (new CreateLabel)->execute([ + 'account_id' => $this->account()->id, + 'vault_id' => $this->vault()->id, + 'author_id' => $this->author()->id, + 'name' => $name, + ]); + } +} diff --git a/app/Domains/Contact/ManageGroups/Dav/ImportGroup.php b/app/Domains/Contact/ManageGroups/Dav/ImportGroup.php index 27a47b491f1..e63e7019fda 100644 --- a/app/Domains/Contact/ManageGroups/Dav/ImportGroup.php +++ b/app/Domains/Contact/ManageGroups/Dav/ImportGroup.php @@ -17,16 +17,11 @@ class ImportGroup extends Importer implements ImportVCardResource { /** - * Can import Group. + * Test if the Card is handled by this importer. */ - public function can(VCard $vcard): bool + public function handle(VCard $vcard): bool { - $kind = (string) $vcard->KIND; - if ($kind == null) { - $kind = (string) collect($vcard->select('X-ADDRESSBOOKSERVER-KIND'))->first(); - } - - return $kind === 'group'; + return $this->kind($vcard) === 'group'; } /** diff --git a/app/Domains/Contact/ManageGroups/Dav/ImportMembers.php b/app/Domains/Contact/ManageGroups/Dav/ImportMembers.php index 603167f6957..f6a2565af9f 100644 --- a/app/Domains/Contact/ManageGroups/Dav/ImportMembers.php +++ b/app/Domains/Contact/ManageGroups/Dav/ImportMembers.php @@ -19,16 +19,11 @@ class ImportMembers extends Importer implements ImportVCardResource { /** - * Can import Group. + * Test if the Card is handled by this importer. */ - public function can(VCard $vcard): bool + public function handle(VCard $vcard): bool { - $kind = (string) $vcard->KIND; - if ($kind == null) { - $kind = (string) collect($vcard->select('X-ADDRESSBOOKSERVER-KIND'))->first(); - } - - return $kind === 'group'; + return $this->kind($vcard) === 'group'; } /** diff --git a/app/Domains/Settings/ManageAddressTypes/Services/CreateAddressType.php b/app/Domains/Settings/ManageAddressTypes/Services/CreateAddressType.php index 288958f6628..1a9d490733c 100644 --- a/app/Domains/Settings/ManageAddressTypes/Services/CreateAddressType.php +++ b/app/Domains/Settings/ManageAddressTypes/Services/CreateAddressType.php @@ -41,8 +41,8 @@ public function execute(array $data): AddressType $type = AddressType::create([ 'account_id' => $data['account_id'], - 'name' => $data['name'] ?? null, - 'name_translation_key' => $data['name_translation_key'] ?? null, + 'name' => $this->valueOrNull($data, 'name'), + 'name_translation_key' => $this->valueOrNull($data, 'name_translation_key'), ]); return $type; diff --git a/app/Domains/Vault/ManageVaultSettings/Services/CreateLabel.php b/app/Domains/Vault/ManageVaultSettings/Services/CreateLabel.php index 2c8d5ef62b4..3ea6f22f976 100644 --- a/app/Domains/Vault/ManageVaultSettings/Services/CreateLabel.php +++ b/app/Domains/Vault/ManageVaultSettings/Services/CreateLabel.php @@ -22,8 +22,8 @@ public function rules(): array 'vault_id' => 'required|uuid|exists:vaults,id', 'name' => 'required|string|max:255', 'description' => 'nullable|string|max:65535', - 'bg_color' => 'string|max:255', - 'text_color' => 'string|max:255', + 'bg_color' => 'nullable|string|max:255', + 'text_color' => 'nullable|string|max:255', ]; } @@ -51,8 +51,8 @@ public function execute(array $data): Label 'name' => $data['name'], 'description' => $this->valueOrNull($data, 'description'), 'slug' => Str::slug($data['name'], '-', language: currentLang()), - 'bg_color' => $data['bg_color'], - 'text_color' => $data['text_color'], + 'bg_color' => $this->valueOrNull($data, 'bg_color') ?? 'bg-neutral-200', + 'text_color' => $this->valueOrNull($data, 'text_color') ?? 'text-neutral-800', ]); return $this->label; diff --git a/database/factories/LabelFactory.php b/database/factories/LabelFactory.php index cf526cb48c6..8bfcbc0d8b2 100644 --- a/database/factories/LabelFactory.php +++ b/database/factories/LabelFactory.php @@ -22,8 +22,8 @@ public function definition() { return [ 'vault_id' => Vault::factory(), - 'name' => $this->faker->name(), - 'slug' => $this->faker->name(), + 'name' => $this->faker->word(), + 'slug' => $this->faker->word(), 'description' => $this->faker->sentence(), ]; } diff --git a/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAdrTest.php b/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php similarity index 90% rename from tests/Unit/Domains/Contact/ManageContact/Dav/ExportAdrTest.php rename to tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php index 4ac09f6be22..d9def3d9e2c 100644 --- a/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAdrTest.php +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ExportAddressTest.php @@ -2,7 +2,7 @@ namespace Tests\Unit\Domains\Contact\ManageContact\Dav; -use App\Domains\Contact\ManageContact\Dav\ExportAdr; +use App\Domains\Contact\ManageContact\Dav\ExportAddress; use App\Models\Address; use App\Models\Contact; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -14,7 +14,7 @@ use Tests\TestCase; use Tests\Unit\Domains\Contact\DAV\CardEtag; -class ExportAdrTest extends TestCase +class ExportAddressTest extends TestCase { use CardEtag, DatabaseTransactions, @@ -34,7 +34,7 @@ public function it_adds_address_to_vcard() $address->contacts()->attach($contact->id); $vCard = new VCard(); - (new ExportAdr)->export($contact, $vCard); + (new ExportAddress)->export($contact, $vCard); $this->assertCount( self::defaultPropsCount + 1, diff --git a/tests/Unit/Domains/Contact/ManageContact/Dav/ExportLabelsTest.php b/tests/Unit/Domains/Contact/ManageContact/Dav/ExportLabelsTest.php new file mode 100644 index 00000000000..01248cc0be3 --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ExportLabelsTest.php @@ -0,0 +1,68 @@ +createUser(); + $vault = $this->createVaultUser($user); + $contact = Contact::factory()->random()->create(['vault_id' => $vault->id]); + $label = Label::factory()->create(['vault_id' => $vault->id]); + $label->contacts()->attach($contact->id); + + $vCard = new VCard(); + (new ExportLabels)->export($contact, $vCard); + + $this->assertCount( + self::defaultPropsCount + 1, + $vCard->children() + ); + + $this->assertStringContainsString("CATEGORIES:{$label->name}", $vCard->serialize()); + } + + #[Group('dav')] + #[Test] + public function it_adds_multiple_categories_to_vcard() + { + $user = $this->createUser(); + $vault = $this->createVaultUser($user); + $contact = Contact::factory()->random()->create(['vault_id' => $vault->id]); + $labels = Label::factory(3)->create(['vault_id' => $vault->id]); + foreach ($labels as $label) { + $label->contacts()->attach($contact->id); + } + + $vCard = new VCard(); + (new ExportLabels)->export($contact, $vCard); + + $this->assertCount( + self::defaultPropsCount + 1, + $vCard->children() + ); + + $this->assertStringContainsString("CATEGORIES:{$labels->pluck('name')->implode(',')}", $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..f7c61f5a6ea --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportAddressTest.php @@ -0,0 +1,196 @@ +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_type() + { + $user = $this->createAdministrator(); + $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(); + $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->assertDatabaseHas('address_types', [ + 'account_id' => $user->account_id, + 'name' => 'home', + ]); + + $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); + } +} diff --git a/tests/Unit/Domains/Contact/ManageContact/Dav/ImportLabelsTest.php b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportLabelsTest.php new file mode 100644 index 00000000000..be782be7fd7 --- /dev/null +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportLabelsTest.php @@ -0,0 +1,142 @@ +createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportLabels(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + + $vcard = new VCard([ + 'CATEGORIES' => ['tag'], + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(1, $contact->labels); + $label = $contact->labels->first(); + $this->assertEquals('tag', $label->name); + } + + #[Group('dav')] + #[Test] + public function it_imports_multiple_labels() + { + $user = $this->createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportLabels(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + + $vcard = new VCard([ + 'CATEGORIES' => ['tag1', 'tag2'], + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(2, $contact->labels); + $labels = $contact->labels->pluck('name')->toArray(); + $this->assertContains('tag1', $labels); + $this->assertContains('tag2', $labels); + } + + #[Group('dav')] + #[Test] + public function it_imports_labels_and_remove_old_labels() + { + $user = $this->createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportLabels(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + + $label = Label::factory()->create([ + 'vault_id' => $vault->id, + ]); + $contact->labels()->syncWithoutDetaching($label); + + $vcard = new VCard([ + 'CATEGORIES' => ['tag1', 'tag2'], + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(2, $contact->labels); + $labels = $contact->labels->pluck('name')->toArray(); + $this->assertNotContains($label->name, $labels); + $this->assertContains('tag1', $labels); + $this->assertContains('tag2', $labels); + } + + #[Group('dav')] + #[Test] + public function it_imports_labels_no_change() + { + $user = $this->createUser(); + $vault = $this->createVaultUser($user, Vault::PERMISSION_MANAGE); + $importVCard = new ImportVCard(); + $importVCard->author = $user; + $importVCard->vault = $vault; + $importer = new ImportLabels(); + $importer->setContext($importVCard); + + $contact = Contact::factory()->create([ + 'vault_id' => $vault->id, + ]); + + $label = Label::factory()->create([ + 'vault_id' => $vault->id, + ]); + $contact->labels()->syncWithoutDetaching($label); + + $vcard = new VCard([ + 'CATEGORIES' => [$label->name], + ]); + + $contact = $importer->import($vcard, $contact); + + $this->assertCount(1, $contact->labels); + $labels = $contact->labels->pluck('name')->toArray(); + $this->assertContains($label->name, $labels); + } +}