Compare commits
1
Commits
v0.3.4
...
78e321f665
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78e321f665 |
@@ -3,6 +3,7 @@
|
||||
namespace App\Actions\Projects;
|
||||
|
||||
use App\Models\Project;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DeleteProject
|
||||
{
|
||||
@@ -10,7 +11,12 @@ class DeleteProject
|
||||
{
|
||||
$projectName = $project->name;
|
||||
|
||||
$project->delete();
|
||||
DB::transaction(function () use ($project): void {
|
||||
$baseTemplate = $project->baseTemplate()->first();
|
||||
|
||||
$project->delete();
|
||||
$baseTemplate?->delete();
|
||||
});
|
||||
|
||||
return $projectName;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class DeleteProjectRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()?->can('access-admin') ?? false;
|
||||
return $this->user()?->can('admin-project', $this->route('project')) ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@ Route::prefix('projects')->name('projects.')->group(function (): void {
|
||||
});
|
||||
|
||||
Route::delete('/', [ProjectController::class, 'destroy'])
|
||||
->middleware('can:access-admin')
|
||||
->middleware('can:admin-project,project')
|
||||
->name('destroy');
|
||||
|
||||
Route::middleware('can:admin-project,project')->group(function (): void {
|
||||
|
||||
@@ -291,7 +291,14 @@ class ProjectApiTest extends TestCase
|
||||
->assertOk()
|
||||
->assertJsonPath('data.key', 'CORE');
|
||||
|
||||
$this->syncGlobalPermissions($admin, ['admin']);
|
||||
$this->withApplicationKey($user)
|
||||
->deleteJson(route('api.projects.destroy', $project), [
|
||||
'confirmation_key' => 'CORE',
|
||||
])
|
||||
->assertForbidden();
|
||||
|
||||
$this->syncProjectPermissions($project, $admin, ['admin']);
|
||||
$this->assertTrue($admin->can('admin-project', $project));
|
||||
|
||||
$this->withApplicationKey($admin)
|
||||
->deleteJson(route('api.projects.destroy', $project), [
|
||||
|
||||
@@ -817,6 +817,75 @@ class ProjectControllerTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_project_admins_can_delete_their_own_projects_and_reuse_the_key(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$this->syncGlobalPermissions($user, ['create']);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('projects.store'), [
|
||||
'creation_mode' => 'scratch',
|
||||
'name' => 'Core Platform',
|
||||
'key' => 'CORE',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$project = Project::query()->where('key', 'CORE')->firstOrFail();
|
||||
$baseTemplateId = $project->project_template_id;
|
||||
$this->assertTrue($user->can('admin-project', $project));
|
||||
|
||||
$this->actingAs($user)
|
||||
->delete(route('projects.destroy', $project), [
|
||||
'confirmation_key' => 'CORE',
|
||||
])
|
||||
->assertRedirect(route('projects.index'));
|
||||
|
||||
$this->assertDatabaseMissing('projects', ['id' => $project->id]);
|
||||
$this->assertDatabaseMissing('project_templates', ['id' => $baseTemplateId]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->post(route('projects.store'), [
|
||||
'creation_mode' => 'scratch',
|
||||
'name' => 'New Core Platform',
|
||||
'key' => 'CORE',
|
||||
])
|
||||
->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('projects', [
|
||||
'key' => 'CORE',
|
||||
'name' => 'New Core Platform',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_deleting_a_project_preserves_its_published_template_and_linked_projects(): void
|
||||
{
|
||||
$admin = User::factory()->admin()->create();
|
||||
$project = Project::factory()->create(['key' => 'CORE']);
|
||||
$otherProject = Project::factory()->create(['key' => 'OTHER']);
|
||||
$template = ProjectTemplate::factory()->create([
|
||||
'source_project_id' => $project->id,
|
||||
'is_published' => true,
|
||||
]);
|
||||
|
||||
$project->update(['project_template_id' => $template->id]);
|
||||
$otherProject->update(['project_template_id' => $template->id]);
|
||||
|
||||
$this->actingAs($admin)
|
||||
->delete(route('projects.destroy', $project), [
|
||||
'confirmation_key' => 'CORE',
|
||||
])
|
||||
->assertRedirect(route('projects.index'));
|
||||
|
||||
$this->assertDatabaseHas('project_templates', [
|
||||
'id' => $template->id,
|
||||
'source_project_id' => null,
|
||||
]);
|
||||
$this->assertDatabaseHas('projects', [
|
||||
'id' => $otherProject->id,
|
||||
'project_template_id' => $template->id,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure project deletion requires the exact project key confirmation.
|
||||
*/
|
||||
@@ -841,16 +910,18 @@ class ProjectControllerTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure non-admin users cannot delete projects.
|
||||
* Ensure users without project administration cannot delete projects.
|
||||
*/
|
||||
public function test_non_admin_users_cannot_delete_projects(): void
|
||||
public function test_project_readers_cannot_delete_projects(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$otherProject = Project::factory()->create();
|
||||
$project = Project::factory()->create([
|
||||
'key' => 'CORE',
|
||||
]);
|
||||
|
||||
$this->syncProjectPermissions($project, $user, ['admin']);
|
||||
$this->syncProjectPermissions($otherProject, $user, ['admin']);
|
||||
$this->syncProjectPermissions($project, $user, ['view']);
|
||||
|
||||
$this->actingAs($user)
|
||||
->delete(route('projects.destroy', $project), [
|
||||
|
||||
Reference in New Issue
Block a user