diff --git a/resources/js/components/fieldsets/FieldsetResetter.vue b/resources/js/components/fieldsets/FieldsetResetter.vue
new file mode 100644
index 0000000000..496982f9eb
--- /dev/null
+++ b/resources/js/components/fieldsets/FieldsetResetter.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
diff --git a/resources/js/components/fieldsets/Listing.vue b/resources/js/components/fieldsets/Listing.vue
index 933fb228a5..b80cf1057c 100644
--- a/resources/js/components/fieldsets/Listing.vue
+++ b/resources/js/components/fieldsets/Listing.vue
@@ -12,6 +12,19 @@
+
+
+
+
import Listing from '../Listing.vue';
import FieldsetDeleter from './FieldsetDeleter.vue';
+import FieldsetResetter from './FieldsetResetter.vue';
export default {
mixins: [Listing],
- components: {FieldsetDeleter},
+ components: {
+ FieldsetDeleter,
+ FieldsetResetter
+ },
props: ['initialRows'],
diff --git a/routes/cp.php b/routes/cp.php
index 5029601f26..5365b02880 100644
--- a/routes/cp.php
+++ b/routes/cp.php
@@ -252,6 +252,7 @@
Route::post('edit', [FieldsController::class, 'edit'])->name('fields.edit');
Route::post('update', [FieldsController::class, 'update'])->name('fields.update');
Route::get('field-meta', [MetaController::class, 'show']);
+ Route::delete('fieldsets/{fieldset}/reset', [FieldsetController::class, 'reset'])->name('fieldsets.reset');
Route::resource('fieldsets', FieldsetController::class)->except(['show']);
Route::get('blueprints', [BlueprintController::class, 'index'])->name('blueprints.index');
Route::get('blueprints/{namespace}/{handle}', [BlueprintController::class, 'edit'])->name('blueprints.edit');
diff --git a/src/Events/Concerns/ListensForContentEvents.php b/src/Events/Concerns/ListensForContentEvents.php
index 5f3c77a258..389b1d84db 100644
--- a/src/Events/Concerns/ListensForContentEvents.php
+++ b/src/Events/Concerns/ListensForContentEvents.php
@@ -27,6 +27,7 @@ trait ListensForContentEvents
\Statamic\Events\EntryDeleted::class,
\Statamic\Events\EntrySaved::class,
\Statamic\Events\FieldsetDeleted::class,
+ \Statamic\Events\FieldsetReset::class,
\Statamic\Events\FieldsetSaved::class,
\Statamic\Events\FormDeleted::class,
\Statamic\Events\FormSaved::class,
diff --git a/src/Events/FieldsetReset.php b/src/Events/FieldsetReset.php
new file mode 100644
index 0000000000..c403389e2c
--- /dev/null
+++ b/src/Events/FieldsetReset.php
@@ -0,0 +1,20 @@
+fieldset = $fieldset;
+ }
+
+ public function commitMessage()
+ {
+ return __('Fieldset reset', [], config('statamic.git.locale'));
+ }
+}
diff --git a/src/Fields/Fieldset.php b/src/Fields/Fieldset.php
index 80cc21a2e9..98bfbb16aa 100644
--- a/src/Fields/Fieldset.php
+++ b/src/Fields/Fieldset.php
@@ -6,6 +6,7 @@
use Statamic\Events\FieldsetCreating;
use Statamic\Events\FieldsetDeleted;
use Statamic\Events\FieldsetDeleting;
+use Statamic\Events\FieldsetReset;
use Statamic\Events\FieldsetSaved;
use Statamic\Events\FieldsetSaving;
use Statamic\Exceptions\FieldsetRecursionException;
@@ -13,6 +14,7 @@
use Statamic\Facades\AssetContainer;
use Statamic\Facades\Collection;
use Statamic\Facades\Fieldset as FieldsetRepository;
+use Statamic\Facades\File;
use Statamic\Facades\GlobalSet;
use Statamic\Facades\Path;
use Statamic\Facades\Taxonomy;
@@ -126,6 +128,11 @@ public function deleteUrl()
return cp_route('fieldsets.destroy', $this->handle());
}
+ public function resetUrl()
+ {
+ return cp_route('fieldsets.reset', $this->handle());
+ }
+
public function importedBy(): array
{
$blueprints = collect([
@@ -193,6 +200,12 @@ public function isDeletable()
return ! $this->isNamespaced();
}
+ public function isResettable()
+ {
+ return $this->isNamespaced()
+ && File::exists(FieldsetRepository::overriddenNamespacedFieldsetPath($this->handle));
+ }
+
public function afterSave($callback)
{
$this->afterSaveCallbacks[] = $callback;
@@ -269,6 +282,15 @@ public function delete()
return true;
}
+ public function reset()
+ {
+ FieldsetRepository::reset($this);
+
+ FieldsetReset::dispatch($this);
+
+ return true;
+ }
+
public static function __callStatic($method, $parameters)
{
return Facades\Fieldset::{$method}(...$parameters);
diff --git a/src/Fields/FieldsetRepository.php b/src/Fields/FieldsetRepository.php
index d85ffa1740..53531bb489 100644
--- a/src/Fields/FieldsetRepository.php
+++ b/src/Fields/FieldsetRepository.php
@@ -116,7 +116,7 @@ private function namespacedFieldsetPath(string $handle)
return "{$this->hints[$namespace]}/{$path}.yaml";
}
- private function overriddenNamespacedFieldsetPath(string $handle)
+ public function overriddenNamespacedFieldsetPath(string $handle)
{
[$namespace, $handle] = explode('::', $handle);
$handle = str_replace('/', '.', $handle);
@@ -183,6 +183,15 @@ public function delete(Fieldset $fieldset)
File::delete("{$this->directory}/{$fieldset->handle()}.yaml");
}
+ public function reset(Fieldset $fieldset)
+ {
+ if (! $fieldset->isNamespaced()) {
+ throw new \Exception('Non-namespaced fieldsets cannot be reset');
+ }
+
+ File::delete($this->overriddenNamespacedFieldsetPath($fieldset->handle()));
+ }
+
public function addNamespace(string $namespace, string $directory): void
{
$this->hints[$namespace] = $directory;
diff --git a/src/Http/Controllers/CP/Fields/FieldsetController.php b/src/Http/Controllers/CP/Fields/FieldsetController.php
index b398077959..91e7544825 100644
--- a/src/Http/Controllers/CP/Fields/FieldsetController.php
+++ b/src/Http/Controllers/CP/Fields/FieldsetController.php
@@ -30,11 +30,13 @@ public function index(Request $request)
'id' => $fieldset->handle(),
'delete_url' => $fieldset->deleteUrl(),
'edit_url' => $fieldset->editUrl(),
+ 'reset_url' => $fieldset->resetUrl(),
'fields' => $fieldset->fields()->all()->count(),
'imported_by' => collect($fieldset->importedBy())->flatten(1)->mapToGroups(function ($item) {
return [$this->group($item) => ['handle' => $item->handle(), 'title' => $item->title()]];
}),
'is_deletable' => $fieldset->isDeletable(),
+ 'is_resettable' => $fieldset->isResettable(),
'title' => $fieldset->title(),
],
];
@@ -158,6 +160,17 @@ public function destroy($fieldset)
return response('');
}
+ public function reset($fieldset)
+ {
+ $fieldset = Facades\Fieldset::find($fieldset);
+
+ $this->authorize('delete', $fieldset);
+
+ $fieldset->reset();
+
+ return response('');
+ }
+
private function groupKey(Fieldset $fieldset): string
{
return $fieldset->isNamespaced() ? Str::of($fieldset->namespace())->replace('_', ' ')->title() : __('My Fieldsets');
diff --git a/tests/Feature/Fieldsets/ViewFieldsetListingTest.php b/tests/Feature/Fieldsets/ViewFieldsetListingTest.php
index 7083869ead..622d87581e 100644
--- a/tests/Feature/Fieldsets/ViewFieldsetListingTest.php
+++ b/tests/Feature/Fieldsets/ViewFieldsetListingTest.php
@@ -25,6 +25,14 @@ public function it_shows_a_list_of_fieldsets()
'baz::baz' => $this->createFieldset('baz::baz'),
]));
+ Facades\Fieldset::shouldReceive('overriddenNamespacedFieldsetPath')
+ ->with('baz::foo')
+ ->andReturn('/fieldsets/vendor/baz/foo.yaml');
+
+ Facades\Fieldset::shouldReceive('overriddenNamespacedFieldsetPath')
+ ->with('baz::bar')
+ ->andReturn('/fieldsets/vendor/baz/bar.yaml');
+
// Custom policy to allow fieldsets to demonstrate how certain fieldset can be restricted
app()->bind(\Statamic\Policies\FieldsetPolicy::class, function () {
return new class extends \Statamic\Policies\FieldsetPolicy
@@ -54,7 +62,9 @@ public function before($user, $ability, $fieldset)
'fields' => 0,
'edit_url' => 'http://localhost/cp/fields/fieldsets/foo/edit',
'delete_url' => 'http://localhost/cp/fields/fieldsets/foo',
+ 'reset_url' => 'http://localhost/cp/fields/fieldsets/foo/reset',
'is_deletable' => true,
+ 'is_resettable' => false,
'imported_by' => collect(),
],
[
@@ -64,7 +74,9 @@ public function before($user, $ability, $fieldset)
'fields' => 0,
'edit_url' => 'http://localhost/cp/fields/fieldsets/bar/edit',
'delete_url' => 'http://localhost/cp/fields/fieldsets/bar',
+ 'reset_url' => 'http://localhost/cp/fields/fieldsets/bar/reset',
'is_deletable' => true,
+ 'is_resettable' => false,
'imported_by' => collect(),
],
]),
@@ -76,7 +88,9 @@ public function before($user, $ability, $fieldset)
'fields' => 0,
'edit_url' => 'http://localhost/cp/fields/fieldsets/baz::foo/edit',
'delete_url' => 'http://localhost/cp/fields/fieldsets/baz::foo',
+ 'reset_url' => 'http://localhost/cp/fields/fieldsets/baz::foo/reset',
'is_deletable' => false,
+ 'is_resettable' => false,
'imported_by' => collect(),
],
[
@@ -86,7 +100,9 @@ public function before($user, $ability, $fieldset)
'fields' => 0,
'edit_url' => 'http://localhost/cp/fields/fieldsets/baz::bar/edit',
'delete_url' => 'http://localhost/cp/fields/fieldsets/baz::bar',
+ 'reset_url' => 'http://localhost/cp/fields/fieldsets/baz::bar/reset',
'is_deletable' => false,
+ 'is_resettable' => false,
'imported_by' => collect(),
],
]),
diff --git a/tests/Fields/FieldsetRepositoryTest.php b/tests/Fields/FieldsetRepositoryTest.php
index 9f64940fc8..ffbe61bd37 100644
--- a/tests/Fields/FieldsetRepositoryTest.php
+++ b/tests/Fields/FieldsetRepositoryTest.php
@@ -252,6 +252,16 @@ public function it_doesnt_delete_namespaced_fieldsets()
$this->repo->delete($fieldset);
}
+ #[Test]
+ public function it_resets_a_namespaced_fieldset()
+ {
+ File::shouldReceive('delete')->once();
+
+ $fieldset = (new Fieldset)->setHandle('foo::test');
+
+ $this->repo->reset($fieldset);
+ }
+
#[Test]
public function it_gets_a_namespaced_fieldset()
{