Skip to content

Commit

Permalink
feat: add more vcard exports (#6997)
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin committed Nov 4, 2023
1 parent 5709fbd commit 84d6a3f
Show file tree
Hide file tree
Showing 17 changed files with 767 additions and 46 deletions.
5 changes: 5 additions & 0 deletions app/Domains/Contact/Dav/ImportVCardResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
22 changes: 22 additions & 0 deletions app/Domains/Contact/Dav/Importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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';
}
}
24 changes: 16 additions & 8 deletions app/Domains/Contact/Dav/Services/ImportVCard.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @implements ExportVCardResource<Contact>
*/
#[Order(20)]
class ExportAdr implements ExportVCardResource
class ExportAddress implements ExportVCardResource
{
public function getType(): string
{
Expand Down
34 changes: 34 additions & 0 deletions app/Domains/Contact/ManageContact/Dav/ExportLabels.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace App\Domains\Contact\ManageContact\Dav;

use App\Domains\Contact\Dav\Exporter;
use App\Domains\Contact\Dav\ExportVCardResource;
use App\Domains\Contact\Dav\Order;
use App\Models\Contact;
use Sabre\VObject\Component\VCard;

/**
* @implements ExportVCardResource<Contact>
*/
#[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());
}
}
}
155 changes: 155 additions & 0 deletions app/Domains/Contact/ManageContact/Dav/ImportAddress.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php

namespace App\Domains\Contact\ManageContact\Dav;

use App\Domains\Contact\Dav\Importer;
use App\Domains\Contact\Dav\ImportVCardResource;
use App\Domains\Contact\Dav\Order;
use App\Domains\Contact\Dav\VCardResource;
use App\Domains\Contact\ManageContactAddresses\Services\AssociateAddressToContact;
use App\Domains\Contact\ManageContactAddresses\Services\RemoveAddressFromContact;
use App\Domains\Settings\ManageAddressTypes\Services\CreateAddressType;
use App\Domains\Vault\ManageAddresses\Services\CreateAddress;
use App\Domains\Vault\ManageAddresses\Services\UpdateAddress;
use App\Exceptions\NotEnoughPermissionException;
use App\Models\Address;
use App\Models\AddressType;
use App\Models\Contact;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Arr;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property;

#[Order(20)]
class ImportAddress extends Importer implements ImportVCardResource
{
/**
* Test if the Card is handled by this importer.
*/
public function handle(VCard $vcard): bool
{
return $this->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,
]);
}
}
19 changes: 9 additions & 10 deletions app/Domains/Contact/ManageContact/Dav/ImportContact.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down

0 comments on commit 84d6a3f

Please sign in to comment.