Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/Entries/Entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ public function delete()

Facades\Entry::delete($this);

$withEvents ? $this->deleteRevisions() : $this->deleteRevisionsQuietly();

if ($withEvents) {
EntryDeleted::dispatch($this);
}
Expand Down
14 changes: 14 additions & 0 deletions src/Revisions/Revisable.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ public function deleteWorkingCopy()
return optional($this->workingCopy())->delete();
}

public function deleteRevisions()
{
$this->revisions()->each->delete();

$this->deleteWorkingCopy();
}

public function deleteRevisionsQuietly()
{
$this->revisions()->each->deleteQuietly();

optional($this->workingCopy())->deleteQuietly();
}

public function publishWorkingCopy($options = [])
{
$item = $this->fromWorkingCopy();
Expand Down
15 changes: 14 additions & 1 deletion src/Revisions/Revision.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Revision implements Arrayable, ContainsQueryableValues, Contract
protected $message;
protected $action = 'revision';
protected $attributes = [];
protected $withEvents = true;

public function id()
{
Expand Down Expand Up @@ -147,11 +148,23 @@ public function save()
return true;
}

public function deleteQuietly()
{
$this->withEvents = false;

return $this->delete();
}

public function delete()
{
$withEvents = $this->withEvents;
$this->withEvents = true;

Revisions::delete($this);

RevisionDeleted::dispatch($this);
if ($withEvents) {
RevisionDeleted::dispatch($this);
}
}

public function isWorkingCopy(): bool
Expand Down
6 changes: 6 additions & 0 deletions src/Revisions/RevisionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ public function save(RevisionContract $revision)
public function delete(RevisionContract $revision)
{
$this->store->delete($revision);

$directory = $this->directory().'/'.$revision->key();

if (File::exists($directory) && File::isEmpty($directory)) {
File::delete($directory);
}
}

public function query()
Expand Down
138 changes: 138 additions & 0 deletions tests/Feature/Entries/EntryRevisionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@
use Facades\Statamic\Fields\BlueprintRepository;
use Facades\Tests\Factories\EntryFactory;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Event;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Events\EntryDeleted;
use Statamic\Events\EntryDeleting;
use Statamic\Events\RevisionDeleted;
use Statamic\Facades\Collection;
use Statamic\Facades\Entry;
use Statamic\Facades\Folder;
use Statamic\Facades\Path;
use Statamic\Facades\Revision as Revisions;
use Statamic\Facades\Stache;
use Statamic\Facades\User;
use Statamic\Fields\Blueprint;
use Statamic\Revisions\Revision;
Expand Down Expand Up @@ -868,6 +875,137 @@ public function revision_localizations_only_includes_authorized_sites()
$this->assertEquals(['en', 'fr'], array_column($localizations, 'handle'));
}

#[Test]
public function it_deletes_revisions_and_the_working_copy_when_the_entry_is_deleted()
{
Event::fake([RevisionDeleted::class]);

[$entry, $revisions, $workingCopy] = $this->entryWithRevisions('1');

$this->assertRevisionFilesExist($entry, $revisions, $workingCopy);

$entry->delete();

$this->assertRevisionFilesAreGone($entry, $revisions, $workingCopy);
$this->assertCount(0, $entry->revisions());
$this->assertNull($entry->workingCopy());
Event::assertDispatchedTimes(RevisionDeleted::class, 3);
}

#[Test]
public function it_leaves_revisions_belonging_to_other_entries()
{
[$entry, $revisions, $workingCopy] = $this->entryWithRevisions('1');
[$other, $otherRevisions, $otherWorkingCopy] = $this->entryWithRevisions('2');

$entry->delete();

$this->assertRevisionFilesAreGone($entry, $revisions, $workingCopy);
$this->assertRevisionFilesExist($other, $otherRevisions, $otherWorkingCopy);
$this->assertCount(2, $other->revisions());
$this->assertNotNull($other->workingCopy());
}

#[Test]
public function it_keeps_revisions_when_entry_deletion_is_cancelled()
{
Event::fake([RevisionDeleted::class]);

Event::listen(EntryDeleting::class, function () {
return false;
});

[$entry, $revisions, $workingCopy] = $this->entryWithRevisions('1');

$this->assertFalse($entry->delete());

$this->assertRevisionFilesExist($entry, $revisions, $workingCopy);
$this->assertCount(2, $entry->revisions());
$this->assertNotNull($entry->workingCopy());
Event::assertNotDispatched(RevisionDeleted::class);
}

#[Test]
public function it_deletes_revisions_when_the_entry_is_deleted_quietly()
{
Event::fake([EntryDeleted::class, RevisionDeleted::class]);

[$entry, $revisions, $workingCopy] = $this->entryWithRevisions('1');

$entry->deleteQuietly();

$this->assertRevisionFilesAreGone($entry, $revisions, $workingCopy);
Event::assertNotDispatched(EntryDeleted::class);
Event::assertNotDispatched(RevisionDeleted::class);
}

#[Test]
public function it_deletes_revisions_from_a_custom_revisions_path()
{
$custom = Path::tidy($this->dir.'/revisions');

config(['statamic.revisions.path' => $custom]);

Stache::store('revisions')->directory(config('statamic.revisions.path'));

[$entry, $revisions, $workingCopy] = $this->entryWithRevisions('1');

$this->assertStringStartsWith($custom, $revisions[0]->path());
$this->assertRevisionFilesExist($entry, $revisions, $workingCopy);

$entry->delete();

$this->assertRevisionFilesAreGone($entry, $revisions, $workingCopy);
$this->assertFileDoesNotExist($this->fakeStacheDirectory.'/revisions/collections/blog/en/1');
}

private function entryWithRevisions(string $id): array
{
$entry = EntryFactory::id($id)
->slug('entry-'.$id)
->collection('blog')
->date('2010-12-25')
->data(['title' => 'Title '.$id])
->create();

$revisions = collect([
$entry->makeRevision()->message('Revision one')->date(Carbon::parse('2017-02-01')),
$entry->makeRevision()->message('Revision two')->date(Carbon::parse('2017-02-03')),
]);

$revisions->each->save();

$workingCopy = $entry->makeWorkingCopy();
$workingCopy->save();

return [$entry, $revisions->all(), $workingCopy];
}

private function assertRevisionFilesExist($entry, array $revisions, Revision $workingCopy): void
{
foreach ($revisions as $revision) {
$this->assertFileExists($revision->path());
}

$this->assertFileExists($workingCopy->path());
$this->assertDirectoryExists($this->revisionDirectory($entry));
}

private function assertRevisionFilesAreGone($entry, array $revisions, Revision $workingCopy): void
{
foreach ($revisions as $revision) {
$this->assertFileDoesNotExist($revision->path());
}

$this->assertFileDoesNotExist($workingCopy->path());
$this->assertDirectoryDoesNotExist($this->revisionDirectory($entry));
}

private function revisionDirectory($entry): string
{
return Revisions::directory().'/collections/blog/en/'.$entry->id();
}

private function setTestBlueprint($handle, $fields)
{
$blueprint = Blueprint::makeFromFields($fields)->setHandle($handle);
Expand Down
Loading