diff --git a/app/Actions/AttemptToAuthenticateSocialite.php b/app/Actions/AttemptToAuthenticateSocialite.php index e4ca870a079..1482339c0e0 100644 --- a/app/Actions/AttemptToAuthenticateSocialite.php +++ b/app/Actions/AttemptToAuthenticateSocialite.php @@ -78,10 +78,10 @@ private function getSocialiteProvider(string $driver): Provider */ private function authenticateUser(Request $request, string $driver, SocialiteUser $socialite): User { - if ($userToken = UserToken::where([ + if ($userToken = UserToken::firstWhere([ 'driver_id' => $socialite->getId(), 'driver' => $driver, - ])->first()) { + ])) { // Association already exist $user = $userToken->user; diff --git a/app/Console/Commands/UpdateAddressBookSubscription.php b/app/Console/Commands/Local/UpdateAddressBookSubscription.php similarity index 72% rename from app/Console/Commands/UpdateAddressBookSubscription.php rename to app/Console/Commands/Local/UpdateAddressBookSubscription.php index 13f4a88ff6b..edb8a80d4b6 100644 --- a/app/Console/Commands/UpdateAddressBookSubscription.php +++ b/app/Console/Commands/Local/UpdateAddressBookSubscription.php @@ -1,6 +1,6 @@ option('subscriptionId')); - SynchronizeAddressBooks::dispatch($subscription, true); + SynchronizeAddressBooks::dispatch($subscription, $this->option('force')); } } diff --git a/app/Console/Commands/NewAddressBookSubscription.php b/app/Console/Commands/NewAddressBookSubscription.php index f2e595c087c..b7159a45b06 100644 --- a/app/Console/Commands/NewAddressBookSubscription.php +++ b/app/Console/Commands/NewAddressBookSubscription.php @@ -7,6 +7,7 @@ use App\Models\User; use App\Models\Vault; use Illuminate\Console\Command; +use Illuminate\Database\Eloquent\ModelNotFoundException; class NewAddressBookSubscription extends Command { @@ -31,23 +32,25 @@ class NewAddressBookSubscription extends Command /** * Execute the console command. - * - * @return void */ - public function handle() + public function handle(): int { - $user = User::where('email', $this->option('email'))->firstOrFail(); - $vault = Vault::findOrFail($this->option('vaultId')); + if (($user = $this->user()) === null) { + return 1; + } + if (($vault = $this->vault()) === null) { + return 2; + } if ($user->account_id !== $vault->account_id) { $this->error('Vault does not belong to this account'); - return; + return 3; } - $url = $this->option('url') ?? $this->ask('url', 'CardDAV url of the address book'); - $login = $this->option('login') ?? $this->ask('login', 'Login name'); - $password = $this->option('password') ?? $this->ask('password', 'User password'); + $url = $this->option('url') ?? $this->ask('CardDAV url of the address book'); + $login = $this->option('login') ?? $this->ask('Login name'); + $password = $this->option('password') ?? $this->secret('User password'); try { $addressBookSubscription = app(CreateAddressBookSubscription::class)->execute([ @@ -60,13 +63,53 @@ public function handle() ]); } catch (\Exception $e) { $this->error($e->getMessage()); + + return -1; } if (! isset($addressBookSubscription)) { $this->error('Could not add subscription'); + + return -2; } else { $this->info('Subscription added'); SynchronizeAddressBooks::dispatch($addressBookSubscription, true); } + + return 0; + } + + private function user(): ?User + { + if (($email = $this->option('email')) === null) { + $this->error('Please provide an email address'); + + return null; + } + + try { + return User::where('email', $email)->firstOrFail(); + } catch (ModelNotFoundException) { + $this->error('Could not find user'); + + return null; + } + } + + private function vault(): ?Vault + { + if (($vaultId = $this->option('vaultId')) === null) { + $this->error('Please provide an vaultId'); + + return null; + } + + try { + return Vault::findOrFail($vaultId); + } catch (ModelNotFoundException) { + $this->error('Could not find vault'); + + return null; + } } } diff --git a/app/Domains/Contact/Dav/Jobs/UpdateVCard.php b/app/Domains/Contact/Dav/Jobs/UpdateVCard.php index 749e769efa9..c7d3ab565f2 100644 --- a/app/Domains/Contact/Dav/Jobs/UpdateVCard.php +++ b/app/Domains/Contact/Dav/Jobs/UpdateVCard.php @@ -6,6 +6,7 @@ use App\Domains\Contact\Dav\Services\ImportVCard; use App\Domains\Contact\Dav\Web\Backend\CardDAV\CardDAVBackend; use App\Interfaces\ServiceInterface; +use App\Models\Contact; use App\Services\QueuableService; use Closure; use Illuminate\Bus\Batchable; @@ -28,6 +29,7 @@ public function rules(): array 'vault_id' => 'required|uuid|exists:vaults,id', 'uri' => 'required|string', 'etag' => 'nullable|string', + 'external' => 'nullable|boolean', 'card' => [ 'required', function (string $attribute, mixed $value, Closure $fail) { @@ -76,16 +78,24 @@ public function execute(array $data): void /** * Update the contact with the carddata. */ - private function updateCard(string $cardUri, mixed $cardData): ?string + private function updateCard(string $uri, mixed $card): ?string { $backend = app(CardDAVBackend::class)->withUser($this->author); $contactId = null; - if ($cardUri) { - $contactObject = $backend->getObject($this->vault->id, $cardUri); - + if ($uri) { + $contactObject = $backend->getObject($this->vault->id, $uri); if ($contactObject) { $contactId = $contactObject->id; + } else { + $contactObject = Contact::firstWhere([ + 'vault_id' => $this->vault->id, + 'distant_uri' => $uri, + ]); + + if ($contactObject) { + $contactId = $contactObject->id; + } } } @@ -95,8 +105,10 @@ private function updateCard(string $cardUri, mixed $cardData): ?string 'author_id' => $this->author->id, 'vault_id' => $this->vault->id, 'contact_id' => $contactId, - 'entry' => $cardData, + 'entry' => $card, 'etag' => Arr::get($this->data, 'etag'), + 'uri' => $uri, + 'external' => Arr::get($this->data, 'external', false), 'behaviour' => ImportVCard::BEHAVIOUR_REPLACE, ]); @@ -110,9 +122,9 @@ private function updateCard(string $cardUri, mixed $cardData): ?string } } catch (\Exception $e) { Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [ - 'contacturl' => $cardUri, + 'contacturl' => $uri, 'contact_id' => $contactId, - 'carddata' => $cardData, + 'carddata' => $card, $e, ]); throw $e; diff --git a/app/Domains/Contact/Dav/Services/ExportVCard.php b/app/Domains/Contact/Dav/Services/ExportVCard.php index 93099b3bc7f..32588ab138e 100644 --- a/app/Domains/Contact/Dav/Services/ExportVCard.php +++ b/app/Domains/Contact/Dav/Services/ExportVCard.php @@ -76,7 +76,7 @@ private function export(Contact $contact): VCard /** @var VCard */ $vcard = Reader::read($contact->vcard, Reader::OPTION_FORGIVING + Reader::OPTION_IGNORE_INVALID_LINES); if (! $vcard->UID) { - $vcard->UID = $contact->id; + $vcard->UID = $contact->distant_uuid ?? $contact->id; } } catch (ParseException $e) { // Ignore error diff --git a/app/Domains/Contact/Dav/Services/ImportVCard.php b/app/Domains/Contact/Dav/Services/ImportVCard.php index 76debc54b5b..688d46b9626 100644 --- a/app/Domains/Contact/Dav/Services/ImportVCard.php +++ b/app/Domains/Contact/Dav/Services/ImportVCard.php @@ -60,6 +60,8 @@ class ImportVCard extends BaseService implements ServiceInterface self::BEHAVIOUR_REPLACE, ]; + public bool $external = false; + /** * Get the validation rules that apply to the service. */ @@ -83,6 +85,8 @@ function (string $attribute, mixed $value, Closure $fail) { Rule::in(self::$behaviourTypes), ], 'etag' => 'nullable|string', + 'uri' => 'nullable|string', + 'external' => 'nullable|boolean', ]; } @@ -111,6 +115,8 @@ public function execute(array $data): array { $this->validateRules($data); + $this->external = Arr::get($data, 'external', false); + if (Arr::get($data, 'contact_id') !== null) { $this->validateContactBelongsToVault($data); @@ -205,16 +211,22 @@ private function processEntryContact(array $data, ?Contact $contact, VCard $entr ]; } - if ($contact !== null) { - $timestamps = $contact->timestamps; - $contact->timestamps = false; - } + $contact = $this->importEntry($contact, $entry); - $contact = $this->importEntry($contact, $entry, $vcard, Arr::get($data, 'etag')); + // Save vcard content + $contact->vcard = $vcard; - if (isset($timestamps)) { - $contact->timestamps = $timestamps; - } + $contact = Contact::withoutTimestamps(function () use ($contact, $data): Contact { + $uri = Arr::get($data, 'uri'); + if (Arr::get($data, 'external', false)) { + $contact->distant_etag = Arr::get($data, 'etag'); + $contact->distant_uri = $uri; + } + + $contact->save(); + + return $contact; + }); return [ 'contact_id' => $contact->id, @@ -293,17 +305,22 @@ private function getExistingContact(VCard $entry): ?Contact private function existingUuid(VCard $entry): ?Contact { return ! empty($uuid = (string) $entry->UID) && Uuid::isValid($uuid) - ? Contact::where([ + ? + Contact::firstWhere([ + 'vault_id' => $this->vault->id, + 'distant_uuid' => $uuid, + ]) ?? + Contact::firstWhere([ 'vault_id' => $this->vault->id, 'id' => $uuid, - ])->first() + ]) : null; } /** * Create the Contact object matching the current entry. */ - private function importEntry(?Contact $contact, VCard $entry, string $vcard, ?string $etag): Contact + private function importEntry(?Contact $contact, VCard $entry): Contact { /** @var \Illuminate\Support\Collection */ $importers = collect($this->importers()) @@ -314,12 +331,6 @@ private function importEntry(?Contact $contact, VCard $entry, string $vcard, ?st $contact = $importer->import($contact, $entry); } - // Save vcard content - $contact->vcard = $vcard; - $contact->distant_etag = $etag; - - $contact->save(); - return $contact; } diff --git a/app/Domains/Contact/Dav/Web/Backend/CardDAV/CardDAVBackend.php b/app/Domains/Contact/Dav/Web/Backend/CardDAV/CardDAVBackend.php index 63139d51ce3..725f54f18fc 100644 --- a/app/Domains/Contact/Dav/Web/Backend/CardDAV/CardDAVBackend.php +++ b/app/Domains/Contact/Dav/Web/Backend/CardDAV/CardDAVBackend.php @@ -233,10 +233,13 @@ public function getObjectUuid(?string $collectionId, string $uuid): ?Contact throw new NotEnoughPermissionException(); } - return Contact::where([ + return Contact::firstWhere([ + 'distant_uuid' => $uuid, + 'vault_id' => $vault->id, + ]) ?? Contact::firstWhere([ 'id' => $uuid, 'vault_id' => $vault->id, - ])->first(); + ]); } /** diff --git a/app/Domains/Contact/Dav/Web/Backend/SyncDAVBackend.php b/app/Domains/Contact/Dav/Web/Backend/SyncDAVBackend.php index 229c42e5a2a..80128a5ee7b 100644 --- a/app/Domains/Contact/Dav/Web/Backend/SyncDAVBackend.php +++ b/app/Domains/Contact/Dav/Web/Backend/SyncDAVBackend.php @@ -198,7 +198,7 @@ private function getDeleted(string $collectionId, ?Carbon $timestamp): array protected function encodeUri($obj): string { - return urlencode($obj->id.$this->getExtension()); + return urlencode(($obj->distant_uuid ?? $obj->id).$this->getExtension()); } private function decodeUri(string $uri): string diff --git a/app/Domains/Contact/DavClient/Jobs/GetMultipleVCard.php b/app/Domains/Contact/DavClient/Jobs/GetMultipleVCard.php index 02abc86e956..4aba1ed135b 100644 --- a/app/Domains/Contact/DavClient/Jobs/GetMultipleVCard.php +++ b/app/Domains/Contact/DavClient/Jobs/GetMultipleVCard.php @@ -64,6 +64,7 @@ private function updateVCard(array $contact, string $href): ?UpdateVCard 'uri' => $href, 'etag' => Arr::get($contact, 'properties.200.{DAV:}getetag'), 'card' => $card, + 'external' => true, ]); } diff --git a/app/Domains/Contact/DavClient/Jobs/GetVCard.php b/app/Domains/Contact/DavClient/Jobs/GetVCard.php index c54024d3c86..35d69b34a17 100644 --- a/app/Domains/Contact/DavClient/Jobs/GetVCard.php +++ b/app/Domains/Contact/DavClient/Jobs/GetVCard.php @@ -54,6 +54,7 @@ private function updateVCard(string $card): UpdateVCard 'uri' => $this->contact->uri, 'etag' => $this->contact->etag, 'card' => $card, + 'external' => true, ]); } } diff --git a/app/Domains/Contact/DavClient/Jobs/PushVCard.php b/app/Domains/Contact/DavClient/Jobs/PushVCard.php index eae42ae8ee3..8eb48a7988a 100644 --- a/app/Domains/Contact/DavClient/Jobs/PushVCard.php +++ b/app/Domains/Contact/DavClient/Jobs/PushVCard.php @@ -50,11 +50,17 @@ public function handle(): void $etag = $this->pushDistant(); - $contact->distant_etag = $etag === '' ? null : $etag; - $contact->save(); + if ($contact->distant_uri !== null) { + Contact::withoutTimestamps(function () use ($contact, $etag): void { + + $contact->distant_etag = empty($etag) ? null : $etag; + + $contact->save(); + }); + } } - private function pushDistant(): string + private function pushDistant(int $depth = 1): string { try { $response = $this->subscription->getClient() @@ -62,12 +68,19 @@ private function pushDistant(): string return $response->header('Etag'); } catch (RequestException $e) { - Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [ - 'body' => $e->response->body(), - $e, - ]); - $this->fail($e); - throw $e; + if ($depth > 0 && $e->response->status() === 412) { + // If the status is 412 (Precondition Failed), then we retry once with a mode match NONE + $this->mode = self::MODE_MATCH_NONE; + + return $this->pushDistant(--$depth); + } else { + Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [ + 'body' => $e->response->body(), + $e, + ]); + $this->fail($e); + throw $e; + } } } diff --git a/app/Domains/Contact/DavClient/Services/Utils/AddressBookGetter.php b/app/Domains/Contact/DavClient/Services/Utils/AddressBookGetter.php index f7d58dd23fb..a30fcd981e1 100644 --- a/app/Domains/Contact/DavClient/Services/Utils/AddressBookGetter.php +++ b/app/Domains/Contact/DavClient/Services/Utils/AddressBookGetter.php @@ -162,7 +162,7 @@ private function getAddressBookHome(string $principal): string } /** - * Get Url fro address book. + * Get Url for address book. */ private function getAddressBookUrl(string $principal): ?string { @@ -171,7 +171,7 @@ private function getAddressBookUrl(string $principal): ?string $books = $this->client->propfind('{DAV:}resourcetype', 1, [], $home); foreach ($books as $book => $properties) { - if ($book == $home) { + if ($book === $home) { continue; } diff --git a/app/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizer.php b/app/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizer.php index cd5f12a9d48..261bd35d334 100644 --- a/app/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizer.php +++ b/app/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizer.php @@ -8,9 +8,11 @@ use App\Domains\Contact\DavClient\Services\Utils\Model\ContactDto; use App\Domains\Contact\DavClient\Services\Utils\Traits\HasCapability; use App\Domains\Contact\DavClient\Services\Utils\Traits\HasSubscription; +use Illuminate\Http\Client\RequestException; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; class AddressBookSynchronizer @@ -85,13 +87,13 @@ private function forcesync(?Collection $localChanges): Collection { // Get current list of contacts $localContacts = $this->backend()->getObjects($this->subscription->vault_id); - $uuids = $localContacts->pluck('id'); + $localUuids = $localContacts->pluck('id'); // Get distant changes to sync $distContacts = $this->getAllContactsEtag(); // Get missed contacts - $missed = $distContacts->reject(fn (ContactDto $contact): bool => $uuids->contains($this->backend()->getUuid($contact->uri))); + $missed = $distContacts->reject(fn (ContactDto $contact): bool => $localUuids->contains($this->backend()->getUuid($contact->uri))); $jobs = app(PrepareJobsContactUpdater::class) ->withSubscription($this->subscription) ->execute($missed); @@ -202,14 +204,21 @@ private function callSyncCollection(): array $syncToken = $this->subscription->syncToken ?? ''; // get sync - $collection = $this->client->syncCollection([ - '{DAV:}getcontenttype', - '{DAV:}getetag', - ], $syncToken); - - // save the new syncToken as current one - if ($newSyncToken = Arr::get($collection, 'synctoken')) { - $this->subscription->syncToken = $newSyncToken; + try { + $collection = $this->client->syncCollection([ + '{DAV:}getcontenttype', + '{DAV:}getetag', + ], $syncToken); + + // save the new syncToken as current one + if ($newSyncToken = Arr::get($collection, 'synctoken')) { + $this->subscription->syncToken = $newSyncToken; + $this->subscription->save(); + } + } catch (RequestException $e) { + Log::error(__CLASS__.' '.__FUNCTION__.':'.$e->getMessage(), [$e]); + $collection = []; + $this->subscription->syncToken = null; $this->subscription->save(); } diff --git a/app/Domains/Contact/DavClient/Services/Utils/Dav/DavClient.php b/app/Domains/Contact/DavClient/Services/Utils/Dav/DavClient.php index 9934e16b0dc..d9212c5327f 100644 --- a/app/Domains/Contact/DavClient/Services/Utils/Dav/DavClient.php +++ b/app/Domains/Contact/DavClient/Services/Utils/Dav/DavClient.php @@ -6,6 +6,7 @@ use Illuminate\Http\Client\PendingRequest; use Illuminate\Http\Client\Response; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Illuminate\Support\Str; @@ -67,8 +68,11 @@ public function path(?string $path = null): string */ public function getRequest(): PendingRequest { - $request = Http::withUserAgent('Monica DavClient '.config('monica.app_version').'/Guzzle') - ->withoutVerifying(); + $request = Http::withUserAgent('Monica DavClient '.config('monica.app_version').'/Guzzle'); + + if (App::environment('local')) { + $request = $request->withoutVerifying(); + } if ($this->username !== null && $this->password !== null) { $request = $request->withBasicAuth($this->username, $this->password); @@ -471,7 +475,7 @@ public function request(string $method, string $url = '', mixed $body = null, ar $url = Str::startsWith($url, 'http') ? $url : $this->path($url); - Log::debug(__CLASS__.' '.__FUNCTION__.': '.$method.' '.$url, [ + Log::debug(__CLASS__.' '.__FUNCTION__.'[request]: '.$method.' '.$url, [ 'body' => $body, 'headers' => $headers, 'options' => $options, @@ -479,7 +483,12 @@ public function request(string $method, string $url = '', mixed $body = null, ar $response = $request ->send($method, $url, $options) - ->throw(); + ->throw(function (Response $response) use ($method, $url) { + Log::debug(__CLASS__.' '.__FUNCTION__.'[error]: '.$method.' '.$url.' '.$response->status(), [ + 'body' => $response->body(), + 'headers' => $response->headers(), + ]); + }); Log::debug(__CLASS__.' '.__FUNCTION__.'[response]: '.$method.' '.$url.' '.$response->status(), [ 'body' => $response->body(), diff --git a/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPush.php b/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPush.php index dc4b7fa2d5d..257491e599a 100644 --- a/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPush.php +++ b/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPush.php @@ -32,14 +32,13 @@ public function execute(Collection $localChanges, ?Collection $changes = null): /** * Get list of requests to push new contacts. - * * @param Collection $contacts */ private function preparePushAddedContacts(Collection $contacts): Collection { // All added contact must be pushed - return $contacts + return $this->filterContacts($contacts) ->map(function (string $uri): ?PushVCard { $card = $this->backend()->getCard($this->subscription->vault_id, $uri); @@ -62,7 +61,7 @@ private function preparePushAddedContacts(Collection $contacts): Collection private function prepareDeletedContacts(Collection $contacts): Collection { // All removed contact must be deleted - return $contacts + return $this->filterContacts($contacts) ->map(fn (string $uri): DeleteVCard => new DeleteVCard($this->subscription, $uri)); } @@ -77,7 +76,7 @@ private function preparePushChangedContacts(Collection $contacts, Collection $ch $refreshIds = $changes->map(fn (ContactDto $contact): string => $this->backend()->getUuid($contact->uri)); // We don't push contact that have just been pulled - return $contacts + return $this->filterContacts($contacts) ->reject(fn (string $uri): bool => $refreshIds->contains($this->backend()->getUuid($uri))) ->map(function (string $uri): ?PushVCard { $card = $this->backend()->getCard($this->subscription->vault_id, $uri); @@ -95,4 +94,26 @@ private function preparePushChangedContacts(Collection $contacts, Collection $ch ); }); } + + /** + * Filter list of contacts. + * + * @param Collection $contacts + */ + private function filterContacts(Collection $contacts): Collection + { + return $contacts->reject(fn (ContactDto $contact): bool => $this->getContactInVault() === $this->backend()->getUuid($contact->uri) + ); + } + + private function getContactInVault(): ?string + { + $entry = $this->subscription->user->vaults() + ->wherePivot('vault_id', $this->subscription->vault_id) + ->first(); + + $pivot = optional($entry)->pivot; + + return optional($pivot)->contact_id; + } } diff --git a/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissed.php b/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissed.php index c273ae3447f..339ec04692d 100644 --- a/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissed.php +++ b/app/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissed.php @@ -44,8 +44,7 @@ private function preparePushMissedContacts(Collection $added, Collection $distCo $addedUuids = $added->map(fn (string $uri): string => $this->backend()->getUuid($uri)); return $localContacts - ->filter(fn (Contact $contact): bool => ! $distUuids->contains($contact->id) - && ! $addedUuids->contains($contact->id) + ->reject(fn (Contact $contact): bool => $distUuids->contains($contact->id) || $addedUuids->contains($contact->id) ) ->map(function (Contact $contact): PushVCard { $card = $this->backend()->prepareCard($contact); diff --git a/app/Domains/Contact/ManageContact/Dav/ImportContact.php b/app/Domains/Contact/ManageContact/Dav/ImportContact.php index de0ab0c2bde..8bc5b4dba8b 100644 --- a/app/Domains/Contact/ManageContact/Dav/ImportContact.php +++ b/app/Domains/Contact/ManageContact/Dav/ImportContact.php @@ -43,6 +43,11 @@ public function import(?Contact $contact, VCard $vcard): Contact $contact = app(CreateContact::class)->execute($contactData); } + if ($this->context->external && $contact->distant_uuid === null) { + $contact->distant_uuid = $this->getUid($vcard); + $contact->save(); + } + return $contact; } @@ -156,13 +161,25 @@ private function importNameFromFN(array $contactData, VCard $entry): array */ private function importUid(array $contactData, VCard $entry): array { - if (! empty($uuid = (string) $entry->UID) && Uuid::isValid($uuid)) { + if (($uuid = $this->getUid($entry)) !== null && ! $this->context->external) { $contactData['id'] = $uuid; } return $contactData; } + /** + * Import uid of the contact. + */ + private function getUid(VCard $entry): ?string + { + if (! empty($uuid = (string) $entry->UID) && Uuid::isValid($uuid)) { + return $uuid; + } + + return null; + } + /** * Import gender of the contact. */ @@ -217,8 +234,7 @@ private function getGender(string $genderCode): Gender private function getGenderByName(string $name): ?Gender { return $this->account()->genders - ->where('name', $name) - ->first(); + ->firstWhere('name', $name); } /** @@ -227,7 +243,6 @@ private function getGenderByName(string $name): ?Gender private function getGenderByType(string $type): ?Gender { return $this->account()->genders - ->where('type', $type) - ->first(); + ->firstWhere('type', $type); } } diff --git a/app/Domains/Contact/ManageContact/Services/CreateContact.php b/app/Domains/Contact/ManageContact/Services/CreateContact.php index 82bdd3af9dc..bd950bdadad 100644 --- a/app/Domains/Contact/ManageContact/Services/CreateContact.php +++ b/app/Domains/Contact/ManageContact/Services/CreateContact.php @@ -7,6 +7,7 @@ use App\Models\ContactFeedItem; use App\Services\BaseService; use Carbon\Carbon; +use Illuminate\Support\Facades\Log; class CreateContact extends BaseService implements ServiceInterface { @@ -53,6 +54,7 @@ public function permissions(): array */ public function execute(array $data): Contact { + Log::debug(__CLASS__, $data); $this->data = $data; $this->validate(); diff --git a/app/Domains/Contact/ManageContact/Services/UpdateContact.php b/app/Domains/Contact/ManageContact/Services/UpdateContact.php index 76ee7a40d44..9acf7426731 100644 --- a/app/Domains/Contact/ManageContact/Services/UpdateContact.php +++ b/app/Domains/Contact/ManageContact/Services/UpdateContact.php @@ -9,6 +9,7 @@ use App\Models\Pronoun; use App\Services\BaseService; use Carbon\Carbon; +use Illuminate\Support\Facades\Log; class UpdateContact extends BaseService implements ServiceInterface { @@ -58,6 +59,7 @@ public function permissions(): array */ public function execute(array $data): Contact { + Log::debug(__CLASS__, $data); $this->data = $data; $this->validate(); diff --git a/app/Domains/Contact/ManageContact/Web/Controllers/ContactLabelController.php b/app/Domains/Contact/ManageContact/Web/Controllers/ContactLabelController.php index d3a49cf8764..5d1a915bbe5 100644 --- a/app/Domains/Contact/ManageContact/Web/Controllers/ContactLabelController.php +++ b/app/Domains/Contact/ManageContact/Web/Controllers/ContactLabelController.php @@ -18,7 +18,7 @@ public function index(Request $request, string $vaultId, int $labelId) { $vault = Vault::findOrFail($vaultId); - $contacts = Label::where('id', $labelId)->first() + $contacts = Label::find($labelId) ->contacts() ->where('vault_id', $request->route()->parameter('vault')) ->where('listed', true) diff --git a/app/Domains/Contact/ManageContactAddresses/Web/Controllers/ContactModuleAddressController.php b/app/Domains/Contact/ManageContactAddresses/Web/Controllers/ContactModuleAddressController.php index d2b91055a20..9fd8c08b5d9 100644 --- a/app/Domains/Contact/ManageContactAddresses/Web/Controllers/ContactModuleAddressController.php +++ b/app/Domains/Contact/ManageContactAddresses/Web/Controllers/ContactModuleAddressController.php @@ -82,7 +82,7 @@ public function update(Request $request, string $vaultId, string $contactId, int ]); // get the address with pivot information - $address = $contact->addresses()->where('address_id', $addressId)->first(); + $address = $contact->addresses()->firstWhere('address_id', $addressId); return response()->json([ 'data' => ModuleContactAddressesViewHelper::dto($contact, $address, Auth::user()), diff --git a/app/Domains/Contact/ManageRelationships/Web/ViewHelpers/ModuleFamilySummaryViewHelper.php b/app/Domains/Contact/ManageRelationships/Web/ViewHelpers/ModuleFamilySummaryViewHelper.php index b917456041a..f280f7384ec 100644 --- a/app/Domains/Contact/ManageRelationships/Web/ViewHelpers/ModuleFamilySummaryViewHelper.php +++ b/app/Domains/Contact/ManageRelationships/Web/ViewHelpers/ModuleFamilySummaryViewHelper.php @@ -20,8 +20,7 @@ class ModuleFamilySummaryViewHelper public static function data(Contact $contact, User $user): array { $loveRelationshipType = $contact->vault->account->relationshipGroupTypes() - ->where('type', RelationshipGroupType::TYPE_LOVE) - ->first(); + ->firstWhere('type', RelationshipGroupType::TYPE_LOVE); $loveRelationships = $loveRelationshipType->types() ->where('type', RelationshipType::TYPE_LOVE) @@ -30,8 +29,7 @@ public static function data(Contact $contact, User $user): array $loveRelationshipsCollection = self::getRelations($loveRelationships, $contact); $familyRelationshipType = $contact->vault->account->relationshipGroupTypes() - ->where('type', RelationshipGroupType::TYPE_FAMILY) - ->first(); + ->firstWhere('type', RelationshipGroupType::TYPE_FAMILY); $familyRelationships = $familyRelationshipType->types() ->where('type', RelationshipType::TYPE_CHILD) diff --git a/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php b/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php index 0a77a53c737..4ae1a9af1e9 100644 --- a/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php +++ b/app/Domains/Settings/CreateAccount/Jobs/SetupAccount.php @@ -132,8 +132,7 @@ private function addTemplatePageContactInformation(): void // the contact information page is automatically created when we // create the template $templatePageContact = $this->template->pages() - ->where('type', TemplatePage::TYPE_CONTACT) - ->first(); + ->firstWhere('type', TemplatePage::TYPE_CONTACT); // avatar $module = (new CreateModule())->execute([ diff --git a/app/Domains/Settings/ManageCurrencies/Web/ViewHelpers/PersonalizeCurrencyIndexViewHelper.php b/app/Domains/Settings/ManageCurrencies/Web/ViewHelpers/PersonalizeCurrencyIndexViewHelper.php index ecb63bb63c6..7543bfb734c 100644 --- a/app/Domains/Settings/ManageCurrencies/Web/ViewHelpers/PersonalizeCurrencyIndexViewHelper.php +++ b/app/Domains/Settings/ManageCurrencies/Web/ViewHelpers/PersonalizeCurrencyIndexViewHelper.php @@ -33,8 +33,10 @@ public static function data(Account $account): array public static function dtoCurrency(Currency $currency, Account $account): array { $record = DB::table('account_currency') - ->where('account_id', $account->id) - ->where('currency_id', $currency->id) + ->where([ + 'account_id' => $account->id, + 'currency_id' => $currency->id, + ]) ->first(); return [ diff --git a/app/Domains/Settings/ManageNotificationChannels/Web/ViewHelpers/NotificationsIndexViewHelper.php b/app/Domains/Settings/ManageNotificationChannels/Web/ViewHelpers/NotificationsIndexViewHelper.php index b3a1d88067a..cccd57ef5d4 100644 --- a/app/Domains/Settings/ManageNotificationChannels/Web/ViewHelpers/NotificationsIndexViewHelper.php +++ b/app/Domains/Settings/ManageNotificationChannels/Web/ViewHelpers/NotificationsIndexViewHelper.php @@ -12,17 +12,11 @@ public static function data(User $user): array $channels = $user->notificationChannels; // emails - $emails = $channels->filter(function ($channel) { - return $channel->type === 'email'; - }); - $emailsCollection = $emails->map(function ($channel) { - return self::dtoEmail($channel); - }); + $emails = $channels->filter(fn ($channel) => $channel->type === 'email'); + $emailsCollection = $emails->map(fn ($channel) => self::dtoEmail($channel)); // telegram - $telegram = $channels->filter(function ($channel) { - return $channel->type === 'telegram'; - })->first(); + $telegram = $channels->filter(fn ($channel) => $channel->type === 'telegram')->first(); return [ 'emails' => $emailsCollection, diff --git a/app/Domains/Settings/ManageTemplates/Web/ViewHelpers/PersonalizeTemplateShowViewHelper.php b/app/Domains/Settings/ManageTemplates/Web/ViewHelpers/PersonalizeTemplateShowViewHelper.php index ef4f7b09f91..2d0d5874a57 100644 --- a/app/Domains/Settings/ManageTemplates/Web/ViewHelpers/PersonalizeTemplateShowViewHelper.php +++ b/app/Domains/Settings/ManageTemplates/Web/ViewHelpers/PersonalizeTemplateShowViewHelper.php @@ -23,8 +23,7 @@ public static function data(Template $template): array } $contactInformationTemplatePage = $template->pages() - ->where('type', 'contact_information') - ->first(); + ->firstWhere('type', 'contact_information'); return [ 'template' => [ diff --git a/app/Domains/Vault/ManageVaultSettings/Services/ChangeVaultAccess.php b/app/Domains/Vault/ManageVaultSettings/Services/ChangeVaultAccess.php index e68a28ee8eb..14762fa4cf6 100644 --- a/app/Domains/Vault/ManageVaultSettings/Services/ChangeVaultAccess.php +++ b/app/Domains/Vault/ManageVaultSettings/Services/ChangeVaultAccess.php @@ -68,8 +68,7 @@ private function change(): void { // We need to get the vault with pivot, $this->vault does not contains it. $this->user->vaults() - ->where('vault_id', $this->vault->id) - ->first() + ->firstWhere('vault_id', $this->vault->id) ->pivot ->update([ 'permission' => $this->data['permission'], diff --git a/app/Helpers/ContactImportantDateHelper.php b/app/Helpers/ContactImportantDateHelper.php index 1bb3dc7d9d1..1aabdf28c11 100644 --- a/app/Helpers/ContactImportantDateHelper.php +++ b/app/Helpers/ContactImportantDateHelper.php @@ -13,11 +13,10 @@ class ContactImportantDateHelper public static function getImportantDateType(string $type, string $vaultId): ?ContactImportantDateType { return Cache::store('array')->remember("ImportantDateType:{$vaultId}:{$type}", 5, - fn () => ContactImportantDateType::where([ + fn () => ContactImportantDateType::firstWhere([ 'vault_id' => $vaultId, 'internal_type' => $type, ]) - ->first() ); } } diff --git a/app/Models/Contact.php b/app/Models/Contact.php index 7603579d1f9..8a3725aee7a 100644 --- a/app/Models/Contact.php +++ b/app/Models/Contact.php @@ -60,7 +60,9 @@ class Contact extends Model 'file_id', 'religion_id', 'vcard', + 'distant_uuid', 'distant_etag', + 'distant_uri', 'prefix', 'suffix', ]; @@ -414,8 +416,7 @@ protected function age(): Attribute } $birthdate = $this->importantDates - ->where('contact_important_date_type_id', $type->id) - ->first(); + ->firstWhere('contact_important_date_type_id', $type->id); if (! $birthdate) { return null; diff --git a/app/Services/BaseService.php b/app/Services/BaseService.php index 424ffe2eec0..5f066429d81 100644 --- a/app/Services/BaseService.php +++ b/app/Services/BaseService.php @@ -7,6 +7,7 @@ use App\Models\Contact; use App\Models\User; use App\Models\Vault; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Facades\Validator; abstract class BaseService @@ -192,6 +193,10 @@ public function validateContactBelongsToVault(array $data): void { $this->contact = $this->vault->contacts() ->findOrFail($data['contact_id']); + + if ($this->contact->vault_id !== $this->vault->id) { + throw new ModelNotFoundException(); + } } /** diff --git a/config/debugbar.php b/config/debugbar.php index 5a75df02670..34c853ebb53 100644 --- a/config/debugbar.php +++ b/config/debugbar.php @@ -19,6 +19,7 @@ 'telescope*', 'horizon*', 'docs*', + 'dav*', ], /* diff --git a/database/migrations/2020_04_25_133132_create_contacts_table.php b/database/migrations/2020_04_25_133132_create_contacts_table.php index 4d46d130ccc..89f25c724c2 100644 --- a/database/migrations/2020_04_25_133132_create_contacts_table.php +++ b/database/migrations/2020_04_25_133132_create_contacts_table.php @@ -39,7 +39,9 @@ public function up() $table->boolean('listed')->default(true); $table->mediumText('vcard')->nullable(); + $table->uuid('distant_uuid')->nullable(); $table->string('distant_etag', 256)->nullable(); + $table->string('distant_uri', 2096)->nullable(); $table->datetime('last_updated_at')->nullable(); $table->softDeletes(); diff --git a/database/migrations/2023_07_29_200200_add_contact_distant_uri.php b/database/migrations/2023_07_29_200200_add_contact_distant_uri.php new file mode 100644 index 00000000000..303bb960a65 --- /dev/null +++ b/database/migrations/2023_07_29_200200_add_contact_distant_uri.php @@ -0,0 +1,42 @@ +uuid('distant_uuid')->nullable()->after('vcard'); + }); + } + if (! Schema::hasColumn('contacts', 'distant_uri')) { + Schema::table('contacts', function (Blueprint $table) { + $table->string('distant_uri', 2096)->nullable()->after('distant_etag'); + }); + } + } + + /** + * Reverse the migrations. + */ + public function down() + { + if (Schema::hasColumn('contacts', 'distant_uri')) { + Schema::table('contacts', function (Blueprint $table) { + $table->dropColumn('distant_uri'); + }); + } + if (Schema::hasColumn('contacts', 'distant_uuid')) { + Schema::table('contacts', function (Blueprint $table) { + $table->dropColumn('distant_uuid'); + }); + } + } +}; diff --git a/tests/Feature/Controllers/Auth/SocialiteCallbackControllerTest.php b/tests/Feature/Controllers/Auth/SocialiteCallbackControllerTest.php index 7332f7bfd44..6b89a200096 100644 --- a/tests/Feature/Controllers/Auth/SocialiteCallbackControllerTest.php +++ b/tests/Feature/Controllers/Auth/SocialiteCallbackControllerTest.php @@ -99,7 +99,7 @@ public function it_get_user_created(): void $this->assertDatabaseHas('users', [ 'email' => 'customer@legit.com', ]); - $user = User::where('email', 'customer@legit.com')->first(); + $user = User::firstWhere('email', 'customer@legit.com'); $this->assertDatabaseHas('user_tokens', [ 'driver_id' => 12345, 'driver' => 'test', diff --git a/tests/Unit/Domains/Contact/DAV/Jobs/UpdateVCardTest.php b/tests/Unit/Domains/Contact/DAV/Jobs/UpdateVCardTest.php index aba4386da8f..5efbe357825 100644 --- a/tests/Unit/Domains/Contact/DAV/Jobs/UpdateVCardTest.php +++ b/tests/Unit/Domains/Contact/DAV/Jobs/UpdateVCardTest.php @@ -38,13 +38,13 @@ public function it_creates_a_contact() 'uri' => 'https://test/dav/affacde9-b2fe-4371-9acb-6612aaee6971', 'etag' => $etag, 'card' => $card, + 'external' => true, ]; (new UpdateVCard($data))->handle(); $this->assertDatabaseHas('contacts', [ 'first_name' => 'Test', - 'id' => 'affacde9-b2fe-4371-9acb-6612aaee6971', 'vcard' => $card, 'distant_etag' => $etag, ]); diff --git a/tests/Unit/Domains/Contact/DavClient/Jobs/GetMultipleVCardTest.php b/tests/Unit/Domains/Contact/DavClient/Jobs/GetMultipleVCardTest.php index aa8fc163a0c..e8e1578ac9f 100644 --- a/tests/Unit/Domains/Contact/DavClient/Jobs/GetMultipleVCardTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Jobs/GetMultipleVCardTest.php @@ -40,8 +40,8 @@ public function it_get_cards() $etag = $this->getEtag($contact, true); $this->mock(DavClient::class, function (MockInterface $mock) use ($card, $etag) { - $mock->shouldReceive('setBaseUri')->once()->andReturn($mock); - $mock->shouldReceive('setCredentials')->once()->andReturn($mock); + $mock->shouldReceive('setBaseUri')->once()->andReturnSelf(); + $mock->shouldReceive('setCredentials')->once()->andReturnSelf(); $mock->shouldReceive('addressbookMultiget') ->once() ->withArgs(function ($properties, $contacts) { @@ -96,6 +96,7 @@ public function it_get_cards() 'uri' => 'https://test/dav/uri', 'etag' => $etag, 'card' => $card, + 'external' => true, ], $updateVCard->data); return true; @@ -120,8 +121,8 @@ public function it_get_cards_mock_http() $etag = $this->getEtag($contact, true); $this->mock(DavClient::class, function (MockInterface $mock) use ($card, $etag) { - $mock->shouldReceive('setBaseUri')->once()->andReturn($mock); - $mock->shouldReceive('setCredentials')->once()->andReturn($mock); + $mock->shouldReceive('setBaseUri')->once()->andReturnSelf(); + $mock->shouldReceive('setCredentials')->once()->andReturnSelf(); $mock->shouldReceive('addressbookMultiget') ->once() ->withArgs(function ($properties, $contacts) { @@ -176,6 +177,7 @@ public function it_get_cards_mock_http() 'uri' => 'https://test/dav/uri', 'etag' => $etag, 'card' => $card, + 'external' => true, ], $updateVCard->data); return true; diff --git a/tests/Unit/Domains/Contact/DavClient/Jobs/GetVCardTest.php b/tests/Unit/Domains/Contact/DavClient/Jobs/GetVCardTest.php index a6335aae3fc..8dddc044e60 100644 --- a/tests/Unit/Domains/Contact/DavClient/Jobs/GetVCardTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Jobs/GetVCardTest.php @@ -65,6 +65,7 @@ public function it_get_card() 'uri' => 'https://test/dav/uri', 'etag' => $etag, 'card' => $card, + 'external' => true, ], $updateVCard->data); return true; diff --git a/tests/Unit/Domains/Contact/DavClient/Services/AddAddressBookTest.php b/tests/Unit/Domains/Contact/DavClient/Services/AddAddressBookTest.php index a92cb66a800..683b071d89b 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/AddAddressBookTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/AddAddressBookTest.php @@ -27,7 +27,7 @@ public function it_creates_an_addressbook() ]); $this->mock(AddressBookGetter::class, function (MockInterface $mock) { - $mock->shouldReceive('withClient')->andReturn($mock); + $mock->shouldReceive('withClient')->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->andReturn($this->mockReturn()); diff --git a/tests/Unit/Domains/Contact/DavClient/Services/SynchronizeAddressBookTest.php b/tests/Unit/Domains/Contact/DavClient/Services/SynchronizeAddressBookTest.php index bb1564ad826..a6255b0976c 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/SynchronizeAddressBookTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/SynchronizeAddressBookTest.php @@ -17,7 +17,7 @@ class SynchronizeAddressBookTest extends TestCase public function it_runs_sync() { $this->mock(AddressBookSynchronizer::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->withArgs(function ($force) { @@ -41,7 +41,7 @@ public function it_runs_sync() public function it_runs_sync_force() { $this->mock(AddressBookSynchronizer::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->withArgs(function ($force) { diff --git a/tests/Unit/Domains/Contact/DavClient/Services/UpdateSubscriptionLocalSyncTokenTest.php b/tests/Unit/Domains/Contact/DavClient/Services/UpdateSubscriptionLocalSyncTokenTest.php index cf9c88e79e0..7d010efa53a 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/UpdateSubscriptionLocalSyncTokenTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/UpdateSubscriptionLocalSyncTokenTest.php @@ -26,7 +26,7 @@ public function it_update_token() ]); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($token, $subscription) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('getCurrentSyncToken') ->withArgs(function ($id) use ($subscription) { $this->assertEquals($id, $subscription->vault_id); @@ -52,7 +52,7 @@ public function it_wont_update_null_token() $subscription = AddressBookSubscription::factory()->create(); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($subscription) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('getCurrentSyncToken') ->withArgs(function ($id) use ($subscription) { $this->assertEquals($id, $subscription->vault_id); diff --git a/tests/Unit/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizerTest.php b/tests/Unit/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizerTest.php index 29ee50dbecb..35019329738 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizerTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/Utils/AddressBookSynchronizerTest.php @@ -34,13 +34,13 @@ public function it_sync_empty_changes() Bus::fake(); $this->partialMock(PrepareJobsContactUpdater::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->andReturn(collect()); }); $this->partialMock(PrepareJobsContactPush::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->andReturn(collect()); @@ -65,13 +65,13 @@ public function it_sync_no_changes() Bus::fake(); $this->mock(PrepareJobsContactUpdater::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->andReturn(collect()); }); $this->partialMock(PrepareJobsContactPush::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->andReturn(collect()); @@ -109,7 +109,7 @@ public function it_sync_changes_added_local_contact() ->fake(); $this->mock(PrepareJobsContactUpdater::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->withArgs(function ($contacts) { @@ -121,7 +121,7 @@ public function it_sync_changes_added_local_contact() ->andReturn(collect()); }); $this->partialMock(PrepareJobsContactPush::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->withArgs(function ($localChanges, $changes) { @@ -243,14 +243,14 @@ public function it_forcesync_changes_added_local_contact() "\n", 'REPORT'); $this->mock(PrepareJobsContactUpdater::class, function (MockInterface $mock) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->andReturn(collect()); }); $this->mock(PrepareJobsContactPushMissed::class, function (MockInterface $mock) use ($contact, $etag) { - $mock->shouldReceive('withSubscription')->once()->andReturn($mock); + $mock->shouldReceive('withSubscription')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->withArgs(function ($localChanges, $distContacts, $localContacts) use ($contact, $etag) { diff --git a/tests/Unit/Domains/Contact/DavClient/Services/Utils/Dav/DavClientTest.php b/tests/Unit/Domains/Contact/DavClient/Services/Utils/Dav/DavClientTest.php index 8923fec9377..764cbc94e53 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/Utils/Dav/DavClientTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/Utils/Dav/DavClientTest.php @@ -70,7 +70,7 @@ public function it_get_non_standard_serviceurl() public function it_get_serviceurl_name() { $this->mock(ServiceUrlQuery::class, function ($mock) { - $mock->shouldReceive('withClient')->once()->andReturn($mock); + $mock->shouldReceive('withClient')->once()->andReturnSelf(); $mock->shouldReceive('execute') ->once() ->withArgs(function (string $name, bool $https, string $baseUri) { diff --git a/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissedTest.php b/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissedTest.php index 436b1e13953..1d14ae7e6a8 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissedTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushMissedTest.php @@ -43,7 +43,7 @@ public function it_push_contacts_missed() $etag = $this->getEtag($contact, true); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag, $contact) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('getUuid') ->once() ->withArgs(function ($uri) { diff --git a/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushTest.php b/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushTest.php index bb4ef427656..b38e5c0bcd1 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactPushTest.php @@ -43,7 +43,7 @@ public function it_push_contacts_added() $etag = $this->getEtag($contact, true); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($contact, $card, $etag) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('getCard') ->withArgs(function ($name, $uri) { $this->assertEquals($uri, 'uricontact2'); @@ -58,11 +58,17 @@ public function it_push_contacts_added() ]); $mock->shouldReceive('getUuid') ->withArgs(function ($uri) { - $this->assertEquals($uri, 'https://test/dav/uricontact1'); + if ($uri !== 'uricontact2' && $uri !== 'https://test/dav/uricontact1') { + $this->fail("Invalid uri: $uri"); + + return false; + } return true; }) - ->andReturn('uricontact1'); + ->andReturnUsing(function ($uri) { + return Str::contains($uri, 'uricontact1') ? 'uricontact1' : 'uricontact2'; + }); }); $batchs = (new PrepareJobsContactPush) @@ -102,7 +108,7 @@ public function it_push_contacts_modified() $etag = $this->getEtag($contact, true); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($contact, $card, $etag) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('getUuid') ->withArgs(function ($uri) { $this->assertStringContainsString('uricontact', $uri); diff --git a/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactUpdaterTest.php b/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactUpdaterTest.php index 0a9bfd16378..89747b2dc25 100644 --- a/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactUpdaterTest.php +++ b/tests/Unit/Domains/Contact/DavClient/Services/Utils/PrepareJobsContactUpdaterTest.php @@ -46,7 +46,7 @@ public function it_sync_changes_multiget() $etag = $this->getEtag($contact, true); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('updateCard') ->withArgs(function ($addressBookId, $cardUri, $cardData) use ($card) { $this->assertEquals($card, $cardData); @@ -128,7 +128,7 @@ public function it_sync_changes_simple() $etag = $this->getEtag($contact, true); $this->mock(CardDAVBackend::class, function (MockInterface $mock) use ($card, $etag) { - $mock->shouldReceive('withUser')->andReturn($mock); + $mock->shouldReceive('withUser')->andReturnSelf(); $mock->shouldReceive('updateCard') ->withArgs(function ($addressBookId, $cardUri, $cardData) use ($card) { $this->assertTrue(is_resource($cardData)); diff --git a/tests/Unit/Domains/Contact/ManageContact/Dav/ImportContactTest.php b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportContactTest.php index b44dbced69e..a8a55f9961c 100644 --- a/tests/Unit/Domains/Contact/ManageContact/Dav/ImportContactTest.php +++ b/tests/Unit/Domains/Contact/ManageContact/Dav/ImportContactTest.php @@ -163,6 +163,7 @@ public function it_imports_names_FN_multiple() public function it_imports_uuid_default() { $importContact = new ImportContact(); + $importContact->setContext(new ImportVCard($this->app)); $vcard = new VCard([ 'UID' => '31fdc242-c974-436e-98de-6b21624d6e34', diff --git a/tests/Unit/Domains/Vault/ManageAddresses/Web/ViewHelpers/ModuleContactAddressesViewHelperTest.php b/tests/Unit/Domains/Vault/ManageAddresses/Web/ViewHelpers/ModuleContactAddressesViewHelperTest.php index a6c728d71ee..3f258343725 100644 --- a/tests/Unit/Domains/Vault/ManageAddresses/Web/ViewHelpers/ModuleContactAddressesViewHelperTest.php +++ b/tests/Unit/Domains/Vault/ManageAddresses/Web/ViewHelpers/ModuleContactAddressesViewHelperTest.php @@ -114,7 +114,7 @@ public function it_gets_the_data_transfer_object(): void ]); $activeAddress->contacts()->attach($contact, ['is_past_address' => false]); - $address = $contact->addresses()->where('address_id', $activeAddress->id)->first(); + $address = $contact->addresses()->firstWhere('address_id', $activeAddress->id); $collection = ModuleContactAddressesViewHelper::dto($contact, $address, $user); diff --git a/tests/Unit/Domains/Vault/ManageVaultSettings/Services/GrantVaultAccessToUserTest.php b/tests/Unit/Domains/Vault/ManageVaultSettings/Services/GrantVaultAccessToUserTest.php index 71b05c2fe7b..6edafc1e830 100644 --- a/tests/Unit/Domains/Vault/ManageVaultSettings/Services/GrantVaultAccessToUserTest.php +++ b/tests/Unit/Domains/Vault/ManageVaultSettings/Services/GrantVaultAccessToUserTest.php @@ -130,7 +130,7 @@ private function executeService(Account $account, User $user, User $anotherUser, $this->assertDatabaseCount('contacts', 3); - $contact = Contact::where('first_name', $anotherUser->first_name)->first(); + $contact = Contact::firstWhere('first_name', $anotherUser->first_name); $this->assertFalse( $contact->can_be_deleted