From 72e596c7b1dee272b11785780123d7ea7914006c Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 00:05:08 +0100 Subject: [PATCH 01/19] feat: add contact soft delete and prunable --- app/Console/Kernel.php | 1 + .../DAV/Backend/CalDAV/CalDAVBirthdays.php | 11 +++++++ .../DAV/Backend/CalDAV/CalDAVTasks.php | 11 +++++++ .../DAV/Backend/CardDAV/CardDAVBackend.php | 21 ++++++++++++ .../DAV/Backend/SyncDAVBackend.php | 17 +++++++++- app/Models/Account/Photo.php | 16 ++++++++++ app/Models/Contact/Contact.php | 29 ++++++++++++++++- app/Models/Contact/Document.php | 18 +++++++++++ .../2022_01_02_222042_contact_soft_delete.php | 32 +++++++++++++++++++ 9 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 database/migrations/2022_01_02_222042_contact_soft_delete.php diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 54dd1a88d1b..c81f4584ba7 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -52,6 +52,7 @@ protected function schedule(Schedule $schedule) if (config('trustedproxy.cloudflare')) { $this->scheduleCommand($schedule, 'cloudflare:reload', 'daily'); // @codeCoverageIgnore } + $this->scheduleCommand($schedule, 'model:prune', 'daily'); } /** diff --git a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php index 8522a239831..1d30c132a8b 100644 --- a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php +++ b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php @@ -142,6 +142,17 @@ public function getObjects($collectionId) }); } + /** + * Returns the collection of deleted birthdays. + * + * @param string|null $collectionId + * @return \Illuminate\Support\Collection + */ + public function getDeletedObjects($collectionId) + { + return collect(); + } + /** * @return string|null */ diff --git a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php index e1248680600..c67384bb364 100644 --- a/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php +++ b/app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php @@ -49,6 +49,17 @@ public function getObjects($collectionId) ->get(); } + /** + * Returns the collection of deleted tasks. + * + * @param string|null $collectionId + * @return \Illuminate\Support\Collection + */ + public function getDeletedObjects($collectionId) + { + return collect(); + } + /** * Returns the contact for the specific uuid. * diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index ffcbbc9c32e..a6b8a0e759b 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -267,6 +267,19 @@ public function getObjects($collectionId) ->get(); } + /** + * Returns the collection of deleted contacts. + * + * @param string|null $collectionId + * @return \Illuminate\Support\Collection + */ + public function getDeletedObjects($collectionId) + { + return $this->user->account->contacts($collectionId) + ->onlyTrashed() + ->get(); + } + /** * Returns all cards for a specific addressbook id. * @@ -393,6 +406,14 @@ public function updateCard($addressBookId, $cardUri, $cardData): ?string */ public function deleteCard($addressBookId, $cardUri) { + $contact = $this->getObject($addressBookId, $cardUri); + + if ($contact) { + $contact->delete(); + + return true; + } + return false; } diff --git a/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php b/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php index ac0faae6123..4cf1993e437 100644 --- a/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php @@ -175,6 +175,11 @@ public function getChanges($calendarId, $syncToken): ?array return is_null($timestamp) || $obj->created_at >= $timestamp; }); + $deleted = $this->getDeletedObjects($calendarId) + ->filter(function ($obj) use ($timestamp) { + return is_null($timestamp) || + $obj->deleted_at >= $timestamp; + }); return [ 'syncToken' => $this->refreshSyncToken($calendarId)->id, @@ -186,7 +191,9 @@ public function getChanges($calendarId, $syncToken): ?array return $this->encodeUri($obj); })->values()->toArray(), - 'deleted' => [], + 'deleted' => $deleted->map(function ($obj) { + return $this->encodeUri($obj); + })->values()->toArray(), ]; } @@ -256,6 +263,14 @@ abstract public function getObjectUuid($collectionId, $uuid); */ abstract public function getObjects($collectionId); + /** + * Returns the collection of objects. + * + * @param string|null $collectionId + * @return \Illuminate\Support\Collection + */ + abstract public function getDeletedObjects($collectionId); + abstract public function getExtension(); /** diff --git a/app/Models/Account/Photo.php b/app/Models/Account/Photo.php index add8c53d54c..543fff64918 100644 --- a/app/Models/Account/Photo.php +++ b/app/Models/Account/Photo.php @@ -87,4 +87,20 @@ public function dataUrl(): ?string return null; } } + + /** + * Delete the model from the database. + * + * @return bool|null + */ + public function delete() + { + try { + Storage::disk(config('filesystems.default')) + ->delete($this->new_filename); + } catch (FileNotFoundException $e) { + // continue + } + return parent::delete(); + } } diff --git a/app/Models/Contact/Contact.php b/app/Models/Contact/Contact.php index 48aeae8065d..0e056bebce5 100644 --- a/app/Models/Contact/Contact.php +++ b/app/Models/Contact/Contact.php @@ -25,7 +25,9 @@ use App\Models\Account\ActivityStatistic; use App\Models\Relationship\Relationship; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Prunable; use App\Models\ModelBindingHasher as Model; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Validation\ValidationException; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Database\Eloquent\Relations\HasOne; @@ -42,7 +44,7 @@ */ class Contact extends Model { - use Searchable; + use Searchable, SoftDeletes, Prunable; /** @var array */ protected $dates = [ @@ -1564,4 +1566,29 @@ public function throwInactive() ]); } } + + /** + * Get the prunable model query. + * + * @return \Illuminate\Database\Eloquent\Builder + */ + public function prunable() + { + return static::where('deleted_at', '<=', now()->subWeek()); + } + + /** + * Prepare the model for pruning. + * + * @return void + */ + protected function pruning() + { + try { + Storage::disk(config('filesystems.default')) + ->delete($this->avatar_default_url); + } catch (FileNotFoundException $e) { + // continue + } + } } diff --git a/app/Models/Contact/Document.php b/app/Models/Contact/Document.php index d53ceb83f19..f69e6198c05 100644 --- a/app/Models/Contact/Document.php +++ b/app/Models/Contact/Document.php @@ -5,7 +5,9 @@ use App\Helpers\StorageHelper; use App\Models\Account\Account; use App\Models\ModelBinding as Model; +use Illuminate\Support\Facades\Storage; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Contracts\Filesystem\FileNotFoundException; class Document extends Model { @@ -65,4 +67,20 @@ public function getDownloadLink(): string return route('storage', ['file' => $this->new_filename]); } + + /** + * Delete the model from the database. + * + * @return bool|null + */ + public function delete() + { + try { + Storage::disk(config('filesystems.default')) + ->delete($this->new_filename); + } catch (FileNotFoundException $e) { + // continue + } + return parent::delete(); + } } diff --git a/database/migrations/2022_01_02_222042_contact_soft_delete.php b/database/migrations/2022_01_02_222042_contact_soft_delete.php new file mode 100644 index 00000000000..d4c8a438e04 --- /dev/null +++ b/database/migrations/2022_01_02_222042_contact_soft_delete.php @@ -0,0 +1,32 @@ +softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('contacts', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + } +} From bb25e1cec64898ccca4add6664124115eb43ca18 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 2 Jan 2022 23:05:23 +0000 Subject: [PATCH 02/19] Apply fixes from StyleCI --- app/Models/Account/Photo.php | 1 + app/Models/Contact/Document.php | 1 + database/migrations/2022_01_02_222042_contact_soft_delete.php | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Models/Account/Photo.php b/app/Models/Account/Photo.php index 543fff64918..070ad1ac74f 100644 --- a/app/Models/Account/Photo.php +++ b/app/Models/Account/Photo.php @@ -101,6 +101,7 @@ public function delete() } catch (FileNotFoundException $e) { // continue } + return parent::delete(); } } diff --git a/app/Models/Contact/Document.php b/app/Models/Contact/Document.php index f69e6198c05..6493e328780 100644 --- a/app/Models/Contact/Document.php +++ b/app/Models/Contact/Document.php @@ -81,6 +81,7 @@ public function delete() } catch (FileNotFoundException $e) { // continue } + return parent::delete(); } } diff --git a/database/migrations/2022_01_02_222042_contact_soft_delete.php b/database/migrations/2022_01_02_222042_contact_soft_delete.php index d4c8a438e04..d975f458985 100644 --- a/database/migrations/2022_01_02_222042_contact_soft_delete.php +++ b/database/migrations/2022_01_02_222042_contact_soft_delete.php @@ -1,8 +1,8 @@ Date: Mon, 3 Jan 2022 11:54:47 +0100 Subject: [PATCH 03/19] update --- ..._215705_remove_deleted_at_from_contact.php | 34 ------------------- .../2022_01_02_222042_contact_soft_delete.php | 8 +++-- 2 files changed, 5 insertions(+), 37 deletions(-) delete mode 100644 database/migrations/2017_02_10_215705_remove_deleted_at_from_contact.php diff --git a/database/migrations/2017_02_10_215705_remove_deleted_at_from_contact.php b/database/migrations/2017_02_10_215705_remove_deleted_at_from_contact.php deleted file mode 100644 index d244989b8a2..00000000000 --- a/database/migrations/2017_02_10_215705_remove_deleted_at_from_contact.php +++ /dev/null @@ -1,34 +0,0 @@ -dropColumn( - 'deleted_at' - ); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('contacts', function (Blueprint $table) { - $table->softDeletes(); - }); - } -} diff --git a/database/migrations/2022_01_02_222042_contact_soft_delete.php b/database/migrations/2022_01_02_222042_contact_soft_delete.php index d4c8a438e04..27d59dbc452 100644 --- a/database/migrations/2022_01_02_222042_contact_soft_delete.php +++ b/database/migrations/2022_01_02_222042_contact_soft_delete.php @@ -13,9 +13,11 @@ class ContactSoftDelete extends Migration */ public function up() { - Schema::table('contacts', function (Blueprint $table) { - $table->softDeletes(); - }); + if (! Schema::hasColumn('contacts', 'deleted_at')) { + Schema::table('contacts', function (Blueprint $table) { + $table->softDeletes(); + }); + } } /** From 136aff955c573fdaa5cf064231ddb7ec6d00fab6 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 12:10:23 +0100 Subject: [PATCH 04/19] fixes --- .../Controllers/DAV/Backend/CardDAV/CardDAVBackend.php | 5 ++++- app/Models/Contact/Contact.php | 7 +------ app/Services/Account/Settings/DestroyAllDocuments.php | 6 ------ app/Services/Account/Settings/DestroyAllPhotos.php | 6 ------ app/Services/Account/Settings/ResetAccount.php | 7 ++++++- app/Services/Contact/Contact/DestroyContact.php | 8 +++++++- tests/Api/Contact/ApiContactControllerTest.php | 1 + .../Unit/Services/Account/Settings/DestroyAccountTest.php | 1 + tests/Unit/Services/Account/Settings/ResetAccountTest.php | 1 + .../Unit/Services/Contact/Contact/DestroyContactTest.php | 1 + .../Contact/Relationship/DestroyRelationshipTest.php | 1 + 11 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index a6b8a0e759b..c662fbe9805 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -409,7 +409,10 @@ public function deleteCard($addressBookId, $cardUri) $contact = $this->getObject($addressBookId, $cardUri); if ($contact) { - $contact->delete(); + app(DestroyContact::class)->execute([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, + ]); return true; } diff --git a/app/Models/Contact/Contact.php b/app/Models/Contact/Contact.php index 0e056bebce5..962d1257909 100644 --- a/app/Models/Contact/Contact.php +++ b/app/Models/Contact/Contact.php @@ -1584,11 +1584,6 @@ public function prunable() */ protected function pruning() { - try { - Storage::disk(config('filesystems.default')) - ->delete($this->avatar_default_url); - } catch (FileNotFoundException $e) { - // continue - } + $this->deleteAvatars(); } } diff --git a/app/Services/Account/Settings/DestroyAllDocuments.php b/app/Services/Account/Settings/DestroyAllDocuments.php index 4ec92e6bd5b..241308c8f67 100644 --- a/app/Services/Account/Settings/DestroyAllDocuments.php +++ b/app/Services/Account/Settings/DestroyAllDocuments.php @@ -35,12 +35,6 @@ public function execute(array $data): bool ->get(); foreach ($documents as $document) { - try { - Storage::delete($document->new_filename); - } catch (FileNotFoundException $e) { - continue; - } - $document->delete(); } diff --git a/app/Services/Account/Settings/DestroyAllPhotos.php b/app/Services/Account/Settings/DestroyAllPhotos.php index 3937d795045..39b36fb391e 100644 --- a/app/Services/Account/Settings/DestroyAllPhotos.php +++ b/app/Services/Account/Settings/DestroyAllPhotos.php @@ -35,12 +35,6 @@ public function execute(array $data): bool ->get(); foreach ($photos as $photo) { - try { - Storage::delete($photo->new_filename); - } catch (FileNotFoundException $e) { - continue; - } - $photo->delete(); } diff --git a/app/Services/Account/Settings/ResetAccount.php b/app/Services/Account/Settings/ResetAccount.php index 25e14bf9302..51d6aef0112 100644 --- a/app/Services/Account/Settings/ResetAccount.php +++ b/app/Services/Account/Settings/ResetAccount.php @@ -4,6 +4,7 @@ use App\Services\BaseService; use App\Models\Account\Account; +use App\Services\Contact\Contact\DestroyContact; class ResetAccount extends BaseService { @@ -165,7 +166,11 @@ private function destroyContacts(Account $account) { $contacts = $account->contacts; foreach ($contacts as $contact) { - $contact->delete(); + app(DestroyContact::class)->execute([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, + 'force_delete' => 'true', + ]); } } } diff --git a/app/Services/Contact/Contact/DestroyContact.php b/app/Services/Contact/Contact/DestroyContact.php index 5e3a497de26..1ee200e5d3b 100644 --- a/app/Services/Contact/Contact/DestroyContact.php +++ b/app/Services/Contact/Contact/DestroyContact.php @@ -19,6 +19,7 @@ public function rules() return [ 'account_id' => 'required|integer|exists:accounts,id', 'contact_id' => 'required|integer|exists:contacts,id', + 'force_delete' => 'nullable|bool' ]; } @@ -40,7 +41,12 @@ public function execute(array $data): bool $this->destroyRelationships($data, $contact); $contact->deleteAvatars(); - $contact->delete(); + + if ($data['force_delete'] === 'true') { + $contact->forceDelete(); + } else { + $contact->delete(); + } return true; } diff --git a/tests/Api/Contact/ApiContactControllerTest.php b/tests/Api/Contact/ApiContactControllerTest.php index 77cbdad7166..86f4a7f220f 100644 --- a/tests/Api/Contact/ApiContactControllerTest.php +++ b/tests/Api/Contact/ApiContactControllerTest.php @@ -1381,6 +1381,7 @@ public function it_deletes_a_contact() $this->assertDatabaseMissing('contacts', [ 'account_id' => $user->account_id, 'id' => $contact->id, + 'deleted_at' => null, ]); } diff --git a/tests/Unit/Services/Account/Settings/DestroyAccountTest.php b/tests/Unit/Services/Account/Settings/DestroyAccountTest.php index b820fae60e1..b56ef0fca24 100644 --- a/tests/Unit/Services/Account/Settings/DestroyAccountTest.php +++ b/tests/Unit/Services/Account/Settings/DestroyAccountTest.php @@ -29,6 +29,7 @@ public function it_destroys_an_account() $this->assertDatabaseMissing('contacts', [ 'account_id' => $user->account_id, + 'deleted_at' => null, ]); $this->assertDatabaseMissing('accounts', [ 'id' => $user->account_id, diff --git a/tests/Unit/Services/Account/Settings/ResetAccountTest.php b/tests/Unit/Services/Account/Settings/ResetAccountTest.php index ba2d40917bb..28d0d28e702 100644 --- a/tests/Unit/Services/Account/Settings/ResetAccountTest.php +++ b/tests/Unit/Services/Account/Settings/ResetAccountTest.php @@ -49,6 +49,7 @@ public function it_resets_an_account() $this->assertDatabaseMissing('contacts', [ 'account_id' => $user->account_id, + 'deleted_at' => null, ]); $this->assertDatabaseMissing('activities', [ 'account_id' => $user->account_id, diff --git a/tests/Unit/Services/Contact/Contact/DestroyContactTest.php b/tests/Unit/Services/Contact/Contact/DestroyContactTest.php index a8c8e8362cf..7db2f87055a 100644 --- a/tests/Unit/Services/Contact/Contact/DestroyContactTest.php +++ b/tests/Unit/Services/Contact/Contact/DestroyContactTest.php @@ -28,6 +28,7 @@ public function it_destroys_a_contact() $this->assertDatabaseMissing('contacts', [ 'id' => $contact->id, + 'deleted_at' => null, ]); } diff --git a/tests/Unit/Services/Contact/Relationship/DestroyRelationshipTest.php b/tests/Unit/Services/Contact/Relationship/DestroyRelationshipTest.php index dcf095edd32..55ca0a4e7e1 100644 --- a/tests/Unit/Services/Contact/Relationship/DestroyRelationshipTest.php +++ b/tests/Unit/Services/Contact/Relationship/DestroyRelationshipTest.php @@ -151,6 +151,7 @@ public function it_destroys_a_relationship_and_reverse_and_partial_contact() ]); $this->assertDatabaseMissing('contacts', [ 'id' => $contactB->id, + 'deleted_at' => null, ]); } From b5cabda121fa9bff35714f2c5de89ee16183bd67 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 3 Jan 2022 11:10:40 +0000 Subject: [PATCH 05/19] Apply fixes from StyleCI --- app/Services/Account/Settings/DestroyAllDocuments.php | 2 -- app/Services/Account/Settings/DestroyAllPhotos.php | 2 -- app/Services/Contact/Contact/DestroyContact.php | 10 +++++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/Services/Account/Settings/DestroyAllDocuments.php b/app/Services/Account/Settings/DestroyAllDocuments.php index 241308c8f67..6610fe9d207 100644 --- a/app/Services/Account/Settings/DestroyAllDocuments.php +++ b/app/Services/Account/Settings/DestroyAllDocuments.php @@ -4,8 +4,6 @@ use App\Services\BaseService; use App\Models\Contact\Document; -use Illuminate\Support\Facades\Storage; -use Illuminate\Contracts\Filesystem\FileNotFoundException; class DestroyAllDocuments extends BaseService { diff --git a/app/Services/Account/Settings/DestroyAllPhotos.php b/app/Services/Account/Settings/DestroyAllPhotos.php index 39b36fb391e..a1a21db9e30 100644 --- a/app/Services/Account/Settings/DestroyAllPhotos.php +++ b/app/Services/Account/Settings/DestroyAllPhotos.php @@ -4,8 +4,6 @@ use App\Models\Account\Photo; use App\Services\BaseService; -use Illuminate\Support\Facades\Storage; -use Illuminate\Contracts\Filesystem\FileNotFoundException; class DestroyAllPhotos extends BaseService { diff --git a/app/Services/Contact/Contact/DestroyContact.php b/app/Services/Contact/Contact/DestroyContact.php index 1ee200e5d3b..8123703984e 100644 --- a/app/Services/Contact/Contact/DestroyContact.php +++ b/app/Services/Contact/Contact/DestroyContact.php @@ -19,7 +19,7 @@ public function rules() return [ 'account_id' => 'required|integer|exists:accounts,id', 'contact_id' => 'required|integer|exists:contacts,id', - 'force_delete' => 'nullable|bool' + 'force_delete' => 'nullable|bool', ]; } @@ -43,10 +43,10 @@ public function execute(array $data): bool $contact->deleteAvatars(); if ($data['force_delete'] === 'true') { - $contact->forceDelete(); - } else { - $contact->delete(); - } + $contact->forceDelete(); + } else { + $contact->delete(); + } return true; } From dee707fbdb177f5af4bac83d92c09f25e238c170 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 12:25:11 +0100 Subject: [PATCH 06/19] fix case --- app/Services/Account/Settings/ResetAccount.php | 2 +- app/Services/Contact/Contact/DestroyContact.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Services/Account/Settings/ResetAccount.php b/app/Services/Account/Settings/ResetAccount.php index 51d6aef0112..43d3ba33087 100644 --- a/app/Services/Account/Settings/ResetAccount.php +++ b/app/Services/Account/Settings/ResetAccount.php @@ -169,7 +169,7 @@ private function destroyContacts(Account $account) app(DestroyContact::class)->execute([ 'account_id' => $contact->account_id, 'contact_id' => $contact->id, - 'force_delete' => 'true', + 'force_delete' => true, ]); } } diff --git a/app/Services/Contact/Contact/DestroyContact.php b/app/Services/Contact/Contact/DestroyContact.php index 8123703984e..205d2be0aa2 100644 --- a/app/Services/Contact/Contact/DestroyContact.php +++ b/app/Services/Contact/Contact/DestroyContact.php @@ -19,7 +19,7 @@ public function rules() return [ 'account_id' => 'required|integer|exists:accounts,id', 'contact_id' => 'required|integer|exists:contacts,id', - 'force_delete' => 'nullable|bool', + 'force_delete' => 'nullable|boolean', ]; } @@ -42,7 +42,7 @@ public function execute(array $data): bool $contact->deleteAvatars(); - if ($data['force_delete'] === 'true') { + if ($this->valueOrFalse($data, 'force_delete') === true) { $contact->forceDelete(); } else { $contact->delete(); From 1aed1f77556f64e0a2ec78fe550a5b0677591bf3 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 12:28:44 +0100 Subject: [PATCH 07/19] fix class use --- app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index c662fbe9805..a9c8853265c 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -20,6 +20,7 @@ use App\Http\Controllers\DAV\Backend\IDAVBackend; use App\Http\Controllers\DAV\Backend\SyncDAVBackend; use App\Http\Controllers\DAV\DAVACL\PrincipalBackend; +use App\Services\Contact\Contact\DestroyContact; use App\Services\DavClient\Utils\Model\ContactUpdateDto; class CardDAVBackend extends AbstractBackend implements SyncSupport, IDAVBackend From 9e4de21ba6b1b71707e36a90c762221f2fe79bc9 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 3 Jan 2022 11:28:57 +0000 Subject: [PATCH 08/19] Apply fixes from StyleCI --- app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index a9c8853265c..8ece52bd074 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -17,10 +17,10 @@ use Sabre\CardDAV\Plugin as CardDAVPlugin; use Sabre\DAV\Sync\Plugin as DAVSyncPlugin; use App\Services\Contact\Contact\SetMeContact; +use App\Services\Contact\Contact\DestroyContact; use App\Http\Controllers\DAV\Backend\IDAVBackend; use App\Http\Controllers\DAV\Backend\SyncDAVBackend; use App\Http\Controllers\DAV\DAVACL\PrincipalBackend; -use App\Services\Contact\Contact\DestroyContact; use App\Services\DavClient\Utils\Model\ContactUpdateDto; class CardDAVBackend extends AbstractBackend implements SyncSupport, IDAVBackend From 7743e4be3dfb3e60e2a62f9fd094c6887a5ade68 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 13:54:06 +0100 Subject: [PATCH 09/19] fix --- .../Controllers/Api/ApiContactController.php | 8 ++-- app/Http/Controllers/ContactsController.php | 7 ++-- .../DAV/Backend/CardDAV/CardDAVBackend.php | 3 +- app/Jobs/ServiceQueueJob.php | 37 +++++++++++++++++++ tests/Api/DAV/VCardContactTest.php | 24 ++++++++++++ 5 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 app/Jobs/ServiceQueueJob.php diff --git a/app/Http/Controllers/Api/ApiContactController.php b/app/Http/Controllers/Api/ApiContactController.php index 4367b5124b0..8b8bc9bad1d 100644 --- a/app/Http/Controllers/Api/ApiContactController.php +++ b/app/Http/Controllers/Api/ApiContactController.php @@ -18,6 +18,7 @@ use App\Services\Contact\Contact\UpdateWorkInformation; use Illuminate\Database\Eloquent\ModelNotFoundException; use App\Http\Resources\Contact\Contact as ContactResource; +use App\Jobs\ServiceQueueJob; use App\Services\Contact\Contact\UpdateContactIntroduction; use App\Services\Contact\Contact\UpdateContactFoodPreferences; @@ -166,11 +167,10 @@ public function update(Request $request, $contactId) */ public function destroy(Request $request, $contactId) { - $data = [ - 'contact_id' => $contactId, + ServiceQueueJob::dispatch(DestroyContact::class, [ 'account_id' => auth()->user()->account_id, - ]; - app(DestroyContact::class)->execute($data); + 'contact_id' => $contactId, + ]); return $this->respondObjectDeleted($contactId); } diff --git a/app/Http/Controllers/ContactsController.php b/app/Http/Controllers/ContactsController.php index d6127e2b512..c43a94d0b3b 100644 --- a/app/Http/Controllers/ContactsController.php +++ b/app/Http/Controllers/ContactsController.php @@ -28,6 +28,7 @@ use App\Services\Contact\Contact\UpdateWorkInformation; use App\Services\Contact\Contact\UpdateContactFoodPreferences; use App\Http\Resources\Contact\ContactSearch as ContactResource; +use App\Jobs\ServiceQueueJob; class ContactsController extends Controller { @@ -473,12 +474,10 @@ public function destroy(Request $request, Contact $contact) return redirect()->route('people.index'); } - $data = [ + ServiceQueueJob::dispatch(DestroyContact::class, [ 'account_id' => auth()->user()->account_id, 'contact_id' => $contact->id, - ]; - - app(DestroyContact::class)->execute($data); + ]); return redirect()->route('people.index') ->with('success', trans('people.people_delete_success')); diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index 8ece52bd074..b562bf1a6e2 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -21,6 +21,7 @@ use App\Http\Controllers\DAV\Backend\IDAVBackend; use App\Http\Controllers\DAV\Backend\SyncDAVBackend; use App\Http\Controllers\DAV\DAVACL\PrincipalBackend; +use App\Jobs\ServiceQueueJob; use App\Services\DavClient\Utils\Model\ContactUpdateDto; class CardDAVBackend extends AbstractBackend implements SyncSupport, IDAVBackend @@ -410,7 +411,7 @@ public function deleteCard($addressBookId, $cardUri) $contact = $this->getObject($addressBookId, $cardUri); if ($contact) { - app(DestroyContact::class)->execute([ + ServiceQueueJob::dispatch(DestroyContact::class, [ 'account_id' => $contact->account_id, 'contact_id' => $contact->id, ]); diff --git a/app/Jobs/ServiceQueueJob.php b/app/Jobs/ServiceQueueJob.php new file mode 100644 index 00000000000..325f1b9e09e --- /dev/null +++ b/app/Jobs/ServiceQueueJob.php @@ -0,0 +1,37 @@ +service = $service; + $this->parameters = $parameters; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle() + { + app($this->service)->execute($this->parameters); + } +} diff --git a/tests/Api/DAV/VCardContactTest.php b/tests/Api/DAV/VCardContactTest.php index 39796da5c39..01f3f9892d4 100644 --- a/tests/Api/DAV/VCardContactTest.php +++ b/tests/Api/DAV/VCardContactTest.php @@ -467,4 +467,28 @@ public function test_carddav_contacts_report_multiget() ''. '', false); } + + /** + * @group dav + * @test + */ + public function carddav_delete_one_contact() + { + $user = $this->signin(); + $contact = factory(Contact::class)->create([ + 'account_id' => $user->account_id, + ]); + + $response = $this->call('DELETE', "/dav/addressbooks/{$user->email}/contacts/{$contact->uuid}.vcf"); + + $response->assertStatus(204); + $response->assertHeader('X-Sabre-Version'); + $response->assertHeaderMissing('ETag'); + + $this->assertDatabaseMissing('contacts', [ + 'account_id' => $user->account_id, + 'id' => $contact->id, + 'deleted_at' => null, + ]); + } } From 11ad62d5b05848c2f2f29bdf6214a6659c00ae79 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 3 Jan 2022 12:54:21 +0000 Subject: [PATCH 10/19] Apply fixes from StyleCI --- app/Http/Controllers/Api/ApiContactController.php | 2 +- app/Http/Controllers/ContactsController.php | 2 +- app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/Api/ApiContactController.php b/app/Http/Controllers/Api/ApiContactController.php index 8b8bc9bad1d..d1a1f0ba04c 100644 --- a/app/Http/Controllers/Api/ApiContactController.php +++ b/app/Http/Controllers/Api/ApiContactController.php @@ -4,6 +4,7 @@ use Illuminate\Http\Request; use App\Helpers\SearchHelper; +use App\Jobs\ServiceQueueJob; use App\Models\Contact\Contact; use Illuminate\Http\JsonResponse; use App\Jobs\UpdateLastConsultedDate; @@ -18,7 +19,6 @@ use App\Services\Contact\Contact\UpdateWorkInformation; use Illuminate\Database\Eloquent\ModelNotFoundException; use App\Http\Resources\Contact\Contact as ContactResource; -use App\Jobs\ServiceQueueJob; use App\Services\Contact\Contact\UpdateContactIntroduction; use App\Services\Contact\Contact\UpdateContactFoodPreferences; diff --git a/app/Http/Controllers/ContactsController.php b/app/Http/Controllers/ContactsController.php index c43a94d0b3b..d125626121a 100644 --- a/app/Http/Controllers/ContactsController.php +++ b/app/Http/Controllers/ContactsController.php @@ -11,6 +11,7 @@ use App\Helpers\GenderHelper; use App\Helpers\LocaleHelper; use App\Helpers\SearchHelper; +use App\Jobs\ServiceQueueJob; use App\Helpers\AccountHelper; use App\Helpers\StorageHelper; use App\Models\Contact\Contact; @@ -28,7 +29,6 @@ use App\Services\Contact\Contact\UpdateWorkInformation; use App\Services\Contact\Contact\UpdateContactFoodPreferences; use App\Http\Resources\Contact\ContactSearch as ContactResource; -use App\Jobs\ServiceQueueJob; class ContactsController extends Controller { diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index b562bf1a6e2..b8c2b0d10d0 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -4,6 +4,7 @@ use Sabre\DAV; use App\Jobs\Dav\UpdateVCard; +use App\Jobs\ServiceQueueJob; use App\Models\Contact\Contact; use App\Services\VCard\GetEtag; use App\Models\Account\AddressBook; @@ -21,7 +22,6 @@ use App\Http\Controllers\DAV\Backend\IDAVBackend; use App\Http\Controllers\DAV\Backend\SyncDAVBackend; use App\Http\Controllers\DAV\DAVACL\PrincipalBackend; -use App\Jobs\ServiceQueueJob; use App\Services\DavClient\Utils\Model\ContactUpdateDto; class CardDAVBackend extends AbstractBackend implements SyncSupport, IDAVBackend From 4de6aae0aaedf965a11f67d6dad368efbb20b1e9 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 3 Jan 2022 16:24:55 +0000 Subject: [PATCH 11/19] Apply fixes from StyleCI --- app/Http/Controllers/Api/ApiContactController.php | 1 - app/Http/Controllers/ContactsController.php | 1 - 2 files changed, 2 deletions(-) diff --git a/app/Http/Controllers/Api/ApiContactController.php b/app/Http/Controllers/Api/ApiContactController.php index d4a384da96d..83325e13abb 100644 --- a/app/Http/Controllers/Api/ApiContactController.php +++ b/app/Http/Controllers/Api/ApiContactController.php @@ -4,7 +4,6 @@ use Illuminate\Http\Request; use App\Helpers\SearchHelper; -use App\Jobs\ServiceQueueJob; use App\Models\Contact\Contact; use Illuminate\Http\JsonResponse; use App\Jobs\UpdateLastConsultedDate; diff --git a/app/Http/Controllers/ContactsController.php b/app/Http/Controllers/ContactsController.php index 732b1b71576..941584fc0f4 100644 --- a/app/Http/Controllers/ContactsController.php +++ b/app/Http/Controllers/ContactsController.php @@ -11,7 +11,6 @@ use App\Helpers\GenderHelper; use App\Helpers\LocaleHelper; use App\Helpers\SearchHelper; -use App\Jobs\ServiceQueueJob; use App\Helpers\AccountHelper; use App\Helpers\StorageHelper; use App\Models\Contact\Contact; From 2e66f6251a1cd52ec0efa698179ae0b1bcbc46f1 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 17:30:26 +0100 Subject: [PATCH 12/19] fix ResetAccount --- app/Http/Controllers/SettingsController.php | 2 +- app/Services/Account/Settings/ResetAccount.php | 10 +++++++--- .../Services/Account/Settings/ResetAccountTest.php | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 6216f56dd1f..977d4e7d33b 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -157,7 +157,7 @@ public function reset(Request $request) $user = $request->user(); $account = $user->account; - app(ResetAccount::class)->execute([ + ResetAccount::dispatch([ 'account_id' => $account->id, ]); diff --git a/app/Services/Account/Settings/ResetAccount.php b/app/Services/Account/Settings/ResetAccount.php index 43d3ba33087..be1a682902c 100644 --- a/app/Services/Account/Settings/ResetAccount.php +++ b/app/Services/Account/Settings/ResetAccount.php @@ -5,9 +5,13 @@ use App\Services\BaseService; use App\Models\Account\Account; use App\Services\Contact\Contact\DestroyContact; +use App\Services\DispatchableService; +use App\Services\QueuableService; -class ResetAccount extends BaseService +class ResetAccount extends BaseService implements QueuableService { + use DispatchableService; + /** * Get the validation rules that apply to the service. * @@ -26,7 +30,7 @@ public function rules() * @param array $data * @return void */ - public function execute(array $data): void + public function handle(array $data): void { $this->validate($data); @@ -166,7 +170,7 @@ private function destroyContacts(Account $account) { $contacts = $account->contacts; foreach ($contacts as $contact) { - app(DestroyContact::class)->execute([ + DestroyContact::dispatchSync([ 'account_id' => $contact->account_id, 'contact_id' => $contact->id, 'force_delete' => true, diff --git a/tests/Unit/Services/Account/Settings/ResetAccountTest.php b/tests/Unit/Services/Account/Settings/ResetAccountTest.php index 28d0d28e702..6da12a0bb9a 100644 --- a/tests/Unit/Services/Account/Settings/ResetAccountTest.php +++ b/tests/Unit/Services/Account/Settings/ResetAccountTest.php @@ -45,7 +45,7 @@ public function it_resets_an_account() 'account_id' => $user->account_id, ]; - app(ResetAccount::class)->execute($request); + app(ResetAccount::class)->handle($request); $this->assertDatabaseMissing('contacts', [ 'account_id' => $user->account_id, @@ -62,6 +62,6 @@ public function it_fails_if_wrong_parameters_are_given() $request = []; $this->expectException(ValidationException::class); - app(ResetAccount::class)->execute($request); + app(ResetAccount::class)->handle($request); } } From d0920e22d44a5d0144996cb95f98495169e6bc8e Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 3 Jan 2022 16:30:41 +0000 Subject: [PATCH 13/19] Apply fixes from StyleCI --- app/Services/Account/Settings/ResetAccount.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Services/Account/Settings/ResetAccount.php b/app/Services/Account/Settings/ResetAccount.php index be1a682902c..b0dc333dd54 100644 --- a/app/Services/Account/Settings/ResetAccount.php +++ b/app/Services/Account/Settings/ResetAccount.php @@ -4,9 +4,9 @@ use App\Services\BaseService; use App\Models\Account\Account; -use App\Services\Contact\Contact\DestroyContact; -use App\Services\DispatchableService; use App\Services\QueuableService; +use App\Services\DispatchableService; +use App\Services\Contact\Contact\DestroyContact; class ResetAccount extends BaseService implements QueuableService { From e05e33a2d4cb4b61d07f01383c2ac6a8a5041f20 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 23:21:41 +0100 Subject: [PATCH 14/19] remove ServiceQueueJob --- .../DAV/Backend/CardDAV/CardDAVBackend.php | 3 +- app/Jobs/ServiceQueueJob.php | 37 ------------------- tests/Api/DAV/VCardContactTest.php | 1 + 3 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 app/Jobs/ServiceQueueJob.php diff --git a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php index b8c2b0d10d0..0a611cb4eed 100644 --- a/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php @@ -4,7 +4,6 @@ use Sabre\DAV; use App\Jobs\Dav\UpdateVCard; -use App\Jobs\ServiceQueueJob; use App\Models\Contact\Contact; use App\Services\VCard\GetEtag; use App\Models\Account\AddressBook; @@ -411,7 +410,7 @@ public function deleteCard($addressBookId, $cardUri) $contact = $this->getObject($addressBookId, $cardUri); if ($contact) { - ServiceQueueJob::dispatch(DestroyContact::class, [ + DestroyContact::dispatch([ 'account_id' => $contact->account_id, 'contact_id' => $contact->id, ]); diff --git a/app/Jobs/ServiceQueueJob.php b/app/Jobs/ServiceQueueJob.php deleted file mode 100644 index 325f1b9e09e..00000000000 --- a/app/Jobs/ServiceQueueJob.php +++ /dev/null @@ -1,37 +0,0 @@ -service = $service; - $this->parameters = $parameters; - } - - /** - * Execute the job. - * - * @return void - */ - public function handle() - { - app($this->service)->execute($this->parameters); - } -} diff --git a/tests/Api/DAV/VCardContactTest.php b/tests/Api/DAV/VCardContactTest.php index 01f3f9892d4..7ddfd291432 100644 --- a/tests/Api/DAV/VCardContactTest.php +++ b/tests/Api/DAV/VCardContactTest.php @@ -480,6 +480,7 @@ public function carddav_delete_one_contact() ]); $response = $this->call('DELETE', "/dav/addressbooks/{$user->email}/contacts/{$contact->uuid}.vcf"); + $response->dump(); $response->assertStatus(204); $response->assertHeader('X-Sabre-Version'); From 3f178dc5a22ed78c5b2b64682af1a01f3d721e18 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Mon, 3 Jan 2022 23:50:14 +0100 Subject: [PATCH 15/19] make sure to delete avatars --- app/Models/Contact/Contact.php | 8 +++++--- app/Services/Contact/Relationship/DestroyRelationship.php | 6 +++++- .../migrations/2019_12_17_024553_add_foreign_keys.php | 2 +- tests/Api/DAV/VCardContactTest.php | 1 - 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/Models/Contact/Contact.php b/app/Models/Contact/Contact.php index 962d1257909..1d897a92476 100644 --- a/app/Models/Contact/Contact.php +++ b/app/Models/Contact/Contact.php @@ -1181,10 +1181,12 @@ public function getAvatarURL() /** * Delete avatars files. * This does not touch avatar_location or avatar_file_name properties of the contact. + * + * @param bool $force */ - public function deleteAvatars() + public function deleteAvatars(bool $force = false) { - if (! $this->has_avatar || $this->avatar_location == 'external') { + if (! $force && (! $this->has_avatar || $this->avatar_location == 'external')) { return; } @@ -1584,6 +1586,6 @@ public function prunable() */ protected function pruning() { - $this->deleteAvatars(); + $this->deleteAvatars(true); } } diff --git a/app/Services/Contact/Relationship/DestroyRelationship.php b/app/Services/Contact/Relationship/DestroyRelationship.php index 7ab347d299c..f113fd2bf88 100644 --- a/app/Services/Contact/Relationship/DestroyRelationship.php +++ b/app/Services/Contact/Relationship/DestroyRelationship.php @@ -6,6 +6,7 @@ use App\Models\Contact\Contact; use App\Models\Relationship\Relationship; use Illuminate\Database\Eloquent\Builder; +use App\Services\Contact\Contact\DestroyContact; class DestroyRelationship extends BaseService { @@ -79,7 +80,10 @@ private function deletePartialContact(Contact $contact) ->count(); if ($otherRelations == 0) { - $contact->delete(); + DestroyContact::dispatch([ + 'account_id' => $contact->account_id, + 'contact_id' => $contact->id, + ]); } } } diff --git a/database/migrations/2019_12_17_024553_add_foreign_keys.php b/database/migrations/2019_12_17_024553_add_foreign_keys.php index 991023a4f06..77565bec1ba 100644 --- a/database/migrations/2019_12_17_024553_add_foreign_keys.php +++ b/database/migrations/2019_12_17_024553_add_foreign_keys.php @@ -634,7 +634,7 @@ private function cleanContactTable() try { $this->accountExistOrFail($contact->account_id); } catch (ModelNotFoundException $e) { - $contact->delete(); + $contact->forceDelete(); continue; } } diff --git a/tests/Api/DAV/VCardContactTest.php b/tests/Api/DAV/VCardContactTest.php index 7ddfd291432..01f3f9892d4 100644 --- a/tests/Api/DAV/VCardContactTest.php +++ b/tests/Api/DAV/VCardContactTest.php @@ -480,7 +480,6 @@ public function carddav_delete_one_contact() ]); $response = $this->call('DELETE', "/dav/addressbooks/{$user->email}/contacts/{$contact->uuid}.vcf"); - $response->dump(); $response->assertStatus(204); $response->assertHeader('X-Sabre-Version'); From 7cfa325f5d7914097e573ec4b32a538de44686de Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Fri, 7 Jan 2022 19:48:37 +0100 Subject: [PATCH 16/19] up --- app/Console/Kernel.php | 13 ++++++++----- app/Models/Contact/Contact.php | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index c81f4584ba7..ebe38f15d3a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -38,6 +38,7 @@ protected function commands() * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void + * @codeCoverageIgnore */ protected function schedule(Schedule $schedule) { @@ -50,23 +51,25 @@ protected function schedule(Schedule $schedule) $this->scheduleCommand($schedule, 'monica:clean', 'daily'); $this->scheduleCommand($schedule, 'monica:updategravatars', 'weekly'); if (config('trustedproxy.cloudflare')) { - $this->scheduleCommand($schedule, 'cloudflare:reload', 'daily'); // @codeCoverageIgnore + $this->scheduleCommand($schedule, 'cloudflare:reload', 'daily'); } $this->scheduleCommand($schedule, 'model:prune', 'daily'); } /** * Define a new schedule command with a frequency. + * + * @codeCoverageIgnore */ private function scheduleCommand(Schedule $schedule, string $command, $frequency) { $schedule->command($command)->when(function () use ($command, $frequency) { - $event = CronEvent::command($command); // @codeCoverageIgnore - if ($frequency) { // @codeCoverageIgnore - $event = $event->$frequency(); // @codeCoverageIgnore + $event = CronEvent::command($command); + if ($frequency) { + $event = $event->$frequency(); } - return $event->isDue(); // @codeCoverageIgnore + return $event->isDue(); }); } } diff --git a/app/Models/Contact/Contact.php b/app/Models/Contact/Contact.php index 1d897a92476..cbfdc77d733 100644 --- a/app/Models/Contact/Contact.php +++ b/app/Models/Contact/Contact.php @@ -1573,6 +1573,7 @@ public function throwInactive() * Get the prunable model query. * * @return \Illuminate\Database\Eloquent\Builder + * @codeCoverageIgnore */ public function prunable() { @@ -1583,6 +1584,7 @@ public function prunable() * Prepare the model for pruning. * * @return void + * @codeCoverageIgnore */ protected function pruning() { From 9feedaf987f34d56970f1859b3a563209be16c16 Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Fri, 7 Jan 2022 20:41:47 +0100 Subject: [PATCH 17/19] add tests and fixes --- .../DAV/Backend/SyncDAVBackend.php | 1 + .../Utils/AddressBookSynchronizer.php | 14 ++--- .../DavClient/Utils/Dav/DavClient.php | 22 ++++--- tests/Api/DAV/CardDAVTest.php | 57 +++++++++++++++++++ .../Utils/AddressBookSynchronizerTest.php | 17 +++--- 5 files changed, 86 insertions(+), 25 deletions(-) diff --git a/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php b/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php index 4cf1993e437..8bc894fa3dc 100644 --- a/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php @@ -177,6 +177,7 @@ public function getChanges($calendarId, $syncToken): ?array }); $deleted = $this->getDeletedObjects($calendarId) ->filter(function ($obj) use ($timestamp) { + $d = $obj->deleted_at; return is_null($timestamp) || $obj->deleted_at >= $timestamp; }); diff --git a/app/Services/DavClient/Utils/AddressBookSynchronizer.php b/app/Services/DavClient/Utils/AddressBookSynchronizer.php index 01616490134..9dbb86ee291 100644 --- a/app/Services/DavClient/Utils/AddressBookSynchronizer.php +++ b/app/Services/DavClient/Utils/AddressBookSynchronizer.php @@ -119,11 +119,11 @@ private function getDistantChanges(): Collection return $this->filterDistantContacts($contact, $href); }) ->map(function ($contact, $href): ContactDto { - return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag')); + return new ContactDto($href, Arr::get($contact, 'properties.200.{DAV:}getetag')); }); $deleted = $etags->filter(function ($contact): bool { - return isset($contact['404']); + return is_array($contact) && $contact['status'] === '404'; }) ->map(function ($contact, $href): ContactDto { return new ContactDeleteDto($href); @@ -142,14 +142,14 @@ private function getDistantChanges(): Collection private function filterDistantContacts($contact, $href): bool { // only return vcards - if (! is_array($contact) || ! Str::contains(Arr::get($contact, '200.{DAV:}getcontenttype'), 'text/vcard')) { + if (! is_array($contact) || ! Str::contains(Arr::get($contact, 'properties.200.{DAV:}getcontenttype'), 'text/vcard')) { return false; } // only new contact or contact with etag that match $card = $this->backend()->getCard($this->sync->addressBookName(), $href); - return $card === false || $card['etag'] !== Arr::get($contact, '200.{DAV:}getetag'); + return $card === false || $card['etag'] !== Arr::get($contact, 'properties.200.{DAV:}getetag'); } /** @@ -228,13 +228,13 @@ private function getAllContactsEtag(): Collection $data = collect($data); $updated = $data->filter(function ($contact): bool { - return isset($contact[200]); + return is_array($contact) && $contact['status'] === '200'; }) ->map(function ($contact, $href): ContactDto { - return new ContactDto($href, Arr::get($contact, '200.{DAV:}getetag')); + return new ContactDto($href, Arr::get($contact, 'properties.200.{DAV:}getetag')); }); $deleted = $data->filter(function ($contact): bool { - return isset($contact[404]); + return is_array($contact) && $contact['status'] === '404'; }) ->map(function ($contact, $href): ContactDto { return new ContactDeleteDto($href); diff --git a/app/Services/DavClient/Utils/Dav/DavClient.php b/app/Services/DavClient/Utils/Dav/DavClient.php index 45fbaf6d7c0..3546319e5d0 100644 --- a/app/Services/DavClient/Utils/Dav/DavClient.php +++ b/app/Services/DavClient/Utils/Dav/DavClient.php @@ -527,14 +527,17 @@ public function request(string $method, string $url = '', $body = null, array $h * * [ * 'url/to/resource' => [ - * '200' => [ - * '{DAV:}property1' => 'value1', - * '{DAV:}property2' => 'value2', - * ], - * '404' => [ - * '{DAV:}property1' => null, - * '{DAV:}property2' => null, + * 'properties' => [ + * '200' => [ + * '{DAV:}property1' => 'value1', + * '{DAV:}property2' => 'value2', + * ], + * '404' => [ + * '{DAV:}property1' => null, + * '{DAV:}property2' => null, + * ], * ], + * 'status' => 200, * ], * 'url/to/resource2' => [ * .. etc .. @@ -554,7 +557,10 @@ private static function parseMultiStatus(string $body): array $result = []; foreach ($multistatus->getResponses() as $response) { - $result[$response->getHref()] = $response->getResponseProperties(); + $result[$response->getHref()] = [ + 'properties' => $response->getResponseProperties(), + 'status' => $response->getHttpStatus() ?? '200', + ]; } $synctoken = $multistatus->getSyncToken(); diff --git a/tests/Api/DAV/CardDAVTest.php b/tests/Api/DAV/CardDAVTest.php index d4c8c072601..01362b8fc42 100644 --- a/tests/Api/DAV/CardDAVTest.php +++ b/tests/Api/DAV/CardDAVTest.php @@ -393,6 +393,63 @@ public function test_carddav_sync_collection_init() http://sabre.io/ns/sync/{$token->id} +", false); + } + + public function test_carddav_sync_collection_deleted_contact() + { + Carbon::setTestNow(Carbon::create(2019, 1, 1, 9, 0, 0)); + + $user = $this->signin(); + $contact = factory(Contact::class)->create([ + 'account_id' => $user->account_id, + 'deleted_at' => Carbon::create(2019, 3, 1, 9, 0, 0), + ]); + + Carbon::setTestNow(Carbon::create(2019, 2, 1, 9, 0, 0)); + $token = factory(SyncToken::class)->create([ + 'account_id' => $user->account_id, + 'user_id' => $user->id, + 'name' => 'contacts', + 'timestamp' => now(), + ]); + + Carbon::setTestNow(Carbon::create(2019, 4, 1, 9, 0, 0)); + + $response = $this->call('REPORT', "/dav/addressbooks/{$user->email}/contacts/", [], [], [], + [ + 'content-type' => 'application/xml; charset=utf-8', + ], + " + http://sabre.io/ns/sync/{$token->id} + 1 + + + + " + ); + + $response->assertStatus(207); + + $token = SyncToken::where([ + 'account_id' => $user->account_id, + 'user_id' => $user->id, + 'name' => 'contacts', + ]) + ->orderBy('created_at') + ->get() + ->last(); + + $response->assertSee(" + + HTTP/1.1 404 Not Found + /dav/addressbooks/{$user->email}/contacts/{$contact->uuid}.vcf + + + HTTP/1.1 418 I'm a teapot + + + http://sabre.io/ns/sync/{$token->id} ", false); } } diff --git a/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php b/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php index 38f7a73b693..ca0c468ee32 100644 --- a/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php +++ b/tests/Unit/Services/DavClient/Utils/AddressBookSynchronizerTest.php @@ -164,13 +164,11 @@ public function it_sync_changes_deleted_contact_batched() $tester->getSynctoken('"token"') ->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response(DavTester::multistatusHeader(). ''. + 'HTTP/1.1 404 Not Found'. 'https://test/dav/addressbooks/user@test.com/contacts/uuid'. ''. - ''. - ''. - 'text/vcard'. - ''. - 'HTTP/1.1 404 Not Found'. + ''. + 'HTTP/1.1 418 I\'m a teapot'. ''. ''. 'token'. @@ -310,7 +308,7 @@ public function it_forcesync_changes_added_local_contact_batched() } /** @test */ - public function it_forcesync_changes_removed_contact_batched() + public function it_forcesync_changes_deleted_contact_batched() { Bus::fake(); @@ -320,12 +318,11 @@ public function it_forcesync_changes_removed_contact_batched() ->fake(); $tester->addResponse('https://test/dav/addressbooks/user@test.com/contacts/', Http::response(DavTester::multistatusHeader(). ''. + 'HTTP/1.1 404 Not Found'. 'https://test/dav/uuid1'. ''. - ''. - ''. - ''. - 'HTTP/1.1 404 Not Found'. + ''. + 'HTTP/1.1 418 I\'m a teapot'. ''. ''. ''), ''."\n". From a925e1a419adb0af84095864cf55a7d72157ecfa Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 7 Jan 2022 19:42:07 +0000 Subject: [PATCH 18/19] Apply fixes from StyleCI --- app/Http/Controllers/DAV/Backend/SyncDAVBackend.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php b/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php index 8bc894fa3dc..2916d0ae81d 100644 --- a/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php +++ b/app/Http/Controllers/DAV/Backend/SyncDAVBackend.php @@ -178,6 +178,7 @@ public function getChanges($calendarId, $syncToken): ?array $deleted = $this->getDeletedObjects($calendarId) ->filter(function ($obj) use ($timestamp) { $d = $obj->deleted_at; + return is_null($timestamp) || $obj->deleted_at >= $timestamp; }); From f2b467d63fc565c706bf5cb2ec86b98d62ff357f Mon Sep 17 00:00:00 2001 From: Alexis Saettler Date: Fri, 7 Jan 2022 21:55:13 +0100 Subject: [PATCH 19/19] fix --- .../DavClient/Utils/Dav/DavClient.php | 6 ++-- .../DavClient/Utils/Dav/DavClientTest.php | 36 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/Services/DavClient/Utils/Dav/DavClient.php b/app/Services/DavClient/Utils/Dav/DavClient.php index 3546319e5d0..14061b588b8 100644 --- a/app/Services/DavClient/Utils/Dav/DavClient.php +++ b/app/Services/DavClient/Utils/Dav/DavClient.php @@ -192,11 +192,11 @@ public function propFind($properties, int $depth = 0, array $options = [], strin reset($result); $result = current($result); - return Arr::get($result, 200, []); + return Arr::get($result, 'properties.200', []); } return array_map(function ($statusList) { - return Arr::get($statusList, 200, []); + return Arr::get($statusList, 'properties.200', []); }, $result); } @@ -454,7 +454,7 @@ public function propPatch(array $properties, string $url = ''): bool $errorProperties = []; foreach ($result as $statusList) { - foreach ($statusList as $status => $properties) { + foreach ($statusList['properties'] as $status => $properties) { if ($status >= 400) { foreach ($properties as $propName => $propValue) { $errorProperties[] = $propName.' ('.$status.')'; diff --git a/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php b/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php index 76235630509..590acee3254 100644 --- a/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php +++ b/tests/Unit/Services/DavClient/Utils/Dav/DavClientTest.php @@ -254,10 +254,13 @@ public function it_sync_collection() $tester->assert(); $this->assertEquals([ 'href' => [ - 200 => [ - '{DAV:}getetag' => '"00001-abcd1"', - '{DAV:}test' => 'value', + 'properties' => [ + 200 => [ + '{DAV:}getetag' => '"00001-abcd1"', + '{DAV:}test' => 'value', + ], ], + 'status' => '200', ], 'synctoken' => '"00001-abcd1"', ], $result); @@ -296,10 +299,13 @@ public function it_sync_collection_with_synctoken() $tester->assert(); $this->assertEquals([ 'href' => [ - 200 => [ - '{DAV:}getetag' => '"00001-abcd1"', - '{DAV:}test' => 'value', + 'properties' => [ + 200 => [ + '{DAV:}getetag' => '"00001-abcd1"', + '{DAV:}test' => 'value', + ], ], + 'status' => '200', ], 'synctoken' => '"00001-abcd1"', ], $result); @@ -335,10 +341,13 @@ public function it_run_addressbook_multiget_report() $this->assertEquals([ 'href' => [ - 200 => [ - '{DAV:}getetag' => '"00001-abcd1"', - '{DAV:}test' => 'value', + 'properties' => [ + 200 => [ + '{DAV:}getetag' => '"00001-abcd1"', + '{DAV:}test' => 'value', + ], ], + 'status' => '200', ], ], $result); @@ -375,10 +384,13 @@ public function it_run_addressbook_query_report() $tester->assert(); $this->assertEquals([ 'href' => [ - 200 => [ - '{DAV:}getetag' => '"00001-abcd1"', - '{DAV:}test' => 'value', + 'properties' => [ + 200 => [ + '{DAV:}getetag' => '"00001-abcd1"', + '{DAV:}test' => 'value', + ], ], + 'status' => '200', ], ], $result); }