Skip to content

Commit

Permalink
feat: add contact soft delete and prunable (#5826)
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin authored Jan 9, 2022
1 parent 8bdccbb commit 6f887df
Show file tree
Hide file tree
Showing 27 changed files with 315 additions and 81 deletions.
14 changes: 9 additions & 5 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ protected function commands()
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
* @codeCoverageIgnore
*/
protected function schedule(Schedule $schedule)
{
Expand All @@ -50,22 +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();
});
}
}
11 changes: 11 additions & 0 deletions app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
11 changes: 11 additions & 0 deletions app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
25 changes: 25 additions & 0 deletions app/Http/Controllers/DAV/Backend/CardDAV/CardDAVBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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;
Expand Down Expand Up @@ -267,6 +268,19 @@ public function getObjects($collectionId)
->get();
}

/**
* Returns the collection of deleted contacts.
*
* @param string|null $collectionId
* @return \Illuminate\Support\Collection<array-key, Contact>
*/
public function getDeletedObjects($collectionId)
{
return $this->user->account->contacts($collectionId)
->onlyTrashed()
->get();
}

/**
* Returns all cards for a specific addressbook id.
*
Expand Down Expand Up @@ -393,6 +407,17 @@ public function updateCard($addressBookId, $cardUri, $cardData): ?string
*/
public function deleteCard($addressBookId, $cardUri)
{
$contact = $this->getObject($addressBookId, $cardUri);

if ($contact) {
DestroyContact::dispatch([
'account_id' => $contact->account_id,
'contact_id' => $contact->id,
]);

return true;
}

return false;
}

Expand Down
19 changes: 18 additions & 1 deletion app/Http/Controllers/DAV/Backend/SyncDAVBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ public function getChanges($calendarId, $syncToken): ?array
return is_null($timestamp) ||
$obj->created_at >= $timestamp;
});
$deleted = $this->getDeletedObjects($calendarId)
->filter(function ($obj) use ($timestamp) {
$d = $obj->deleted_at;

return is_null($timestamp) ||
$obj->deleted_at >= $timestamp;
});

return [
'syncToken' => $this->refreshSyncToken($calendarId)->id,
Expand All @@ -186,7 +193,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(),
];
}

Expand Down Expand Up @@ -256,6 +265,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();

/**
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);

Expand Down
17 changes: 17 additions & 0 deletions app/Models/Account/Photo.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,21 @@ 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();
}
}
32 changes: 29 additions & 3 deletions app/Models/Contact/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,7 +44,7 @@
*/
class Contact extends Model
{
use Searchable;
use Searchable, SoftDeletes, Prunable;

/** @var array<string> */
protected $dates = [
Expand Down Expand Up @@ -1179,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;
}

Expand Down Expand Up @@ -1564,4 +1568,26 @@ public function throwInactive()
]);
}
}

/**
* Get the prunable model query.
*
* @return \Illuminate\Database\Eloquent\Builder
* @codeCoverageIgnore
*/
public function prunable()
{
return static::where('deleted_at', '<=', now()->subWeek());
}

/**
* Prepare the model for pruning.
*
* @return void
* @codeCoverageIgnore
*/
protected function pruning()
{
$this->deleteAvatars(true);
}
}
19 changes: 19 additions & 0 deletions app/Models/Contact/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -65,4 +67,21 @@ 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();
}
}
8 changes: 0 additions & 8 deletions app/Services/Account/Settings/DestroyAllDocuments.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -35,12 +33,6 @@ public function execute(array $data): bool
->get();

foreach ($documents as $document) {
try {
Storage::delete($document->new_filename);
} catch (FileNotFoundException $e) {
continue;
}

$document->delete();
}

Expand Down
8 changes: 0 additions & 8 deletions app/Services/Account/Settings/DestroyAllPhotos.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -35,12 +33,6 @@ public function execute(array $data): bool
->get();

foreach ($photos as $photo) {
try {
Storage::delete($photo->new_filename);
} catch (FileNotFoundException $e) {
continue;
}

$photo->delete();
}

Expand Down
15 changes: 12 additions & 3 deletions app/Services/Account/Settings/ResetAccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

use App\Services\BaseService;
use App\Models\Account\Account;
use App\Services\QueuableService;
use App\Services\DispatchableService;
use App\Services\Contact\Contact\DestroyContact;

class ResetAccount extends BaseService
class ResetAccount extends BaseService implements QueuableService
{
use DispatchableService;

/**
* Get the validation rules that apply to the service.
*
Expand All @@ -25,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);

Expand Down Expand Up @@ -165,7 +170,11 @@ private function destroyContacts(Account $account)
{
$contacts = $account->contacts;
foreach ($contacts as $contact) {
$contact->delete();
DestroyContact::dispatchSync([
'account_id' => $contact->account_id,
'contact_id' => $contact->id,
'force_delete' => true,
]);
}
}
}
8 changes: 7 additions & 1 deletion app/Services/Contact/Contact/DestroyContact.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function rules()
return [
'account_id' => 'required|integer|exists:accounts,id',
'contact_id' => 'required|integer|exists:contacts,id',
'force_delete' => 'nullable|boolean',
];
}

Expand All @@ -44,7 +45,12 @@ public function handle(array $data): void
$this->destroyRelationships($data, $contact);

$contact->deleteAvatars();
$contact->delete();

if ($this->valueOrFalse($data, 'force_delete') === true) {
$contact->forceDelete();
} else {
$contact->delete();
}
}

/**
Expand Down
Loading

0 comments on commit 6f887df

Please sign in to comment.