Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add contact soft delete and prunable #5826

Merged
merged 22 commits into from
Jan 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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