@DisableDelete WIP

This commit is contained in:
Francesco 2023-10-06 12:46:11 +02:00
parent 5fdf7e18e7
commit 7e7089ea72
9 changed files with 153 additions and 5 deletions

View File

@ -0,0 +1,33 @@
/*
* Spring Boot Database Admin - An automatically generated CRUD admin UI for Spring Boot apps
* Copyright (C) 2023 Ailef (http://ailef.tech)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.ailef.dbadmin.external.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Disables create actions on the Entity class.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DisableCreate {
}

View File

@ -0,0 +1,33 @@
/*
* Spring Boot Database Admin - An automatically generated CRUD admin UI for Spring Boot apps
* Copyright (C) 2023 Ailef (http://ailef.tech)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.ailef.dbadmin.external.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Disables delete actions on the Entity class.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DisableDelete {
}

View File

@ -0,0 +1,33 @@
/*
* Spring Boot Database Admin - An automatically generated CRUD admin UI for Spring Boot apps
* Copyright (C) 2023 Ailef (http://ailef.tech)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.ailef.dbadmin.external.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Disables edit actions on the Entity class.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface DisableEdit {
}

View File

@ -309,6 +309,12 @@ public class DefaultDbAdminController {
public String delete(@PathVariable String className, @PathVariable String id, RedirectAttributes attr) { public String delete(@PathVariable String className, @PathVariable String id, RedirectAttributes attr) {
DbObjectSchema schema = dbAdmin.findSchemaByClassName(className); DbObjectSchema schema = dbAdmin.findSchemaByClassName(className);
if (!schema.isDeleteEnabled()) {
attr.addFlashAttribute("errorTitle", "Unable to DELETE row");
attr.addFlashAttribute("error", "DELETE operations have been disabled on this table.");
return "redirect:/" + properties.getBaseUrl() + "/model/" + className;
}
try { try {
repository.delete(schema, id); repository.delete(schema, id);
} catch (DataIntegrityViolationException e) { } catch (DataIntegrityViolationException e) {
@ -332,6 +338,12 @@ public class DefaultDbAdminController {
public String delete(@PathVariable String className, @RequestParam String[] ids, RedirectAttributes attr) { public String delete(@PathVariable String className, @RequestParam String[] ids, RedirectAttributes attr) {
DbObjectSchema schema = dbAdmin.findSchemaByClassName(className); DbObjectSchema schema = dbAdmin.findSchemaByClassName(className);
if (!schema.isDeleteEnabled()) {
attr.addFlashAttribute("errorTitle", "Unable to DELETE rows");
attr.addFlashAttribute("error", "DELETE operations have been disabled on this table.");
return "redirect:/" + properties.getBaseUrl() + "/model/" + className;
}
int countDeleted = 0; int countDeleted = 0;
for (String id : ids) { for (String id : ids) {
try { try {

View File

@ -38,6 +38,9 @@ import jakarta.persistence.OneToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import tech.ailef.dbadmin.external.DbAdmin; import tech.ailef.dbadmin.external.DbAdmin;
import tech.ailef.dbadmin.external.annotations.ComputedColumn; import tech.ailef.dbadmin.external.annotations.ComputedColumn;
import tech.ailef.dbadmin.external.annotations.DisableCreate;
import tech.ailef.dbadmin.external.annotations.DisableDelete;
import tech.ailef.dbadmin.external.annotations.DisableEdit;
import tech.ailef.dbadmin.external.annotations.HiddenColumn; import tech.ailef.dbadmin.external.annotations.HiddenColumn;
import tech.ailef.dbadmin.external.dto.MappingError; import tech.ailef.dbadmin.external.dto.MappingError;
import tech.ailef.dbadmin.external.exceptions.DbAdminException; import tech.ailef.dbadmin.external.exceptions.DbAdminException;
@ -335,6 +338,18 @@ public class DbObjectSchema {
}).toList(); }).toList();
} }
public boolean isDeleteEnabled() {
return entityClass.getAnnotation(DisableDelete.class) == null;
}
public boolean isEditEnabled() {
return entityClass.getAnnotation(DisableEdit.class) == null;
}
public boolean isCreateEnabled() {
return entityClass.getAnnotation(DisableCreate.class) == null;
}
/** /**
* Returns all the data in this schema, as `DbObject`s * Returns all the data in this schema, as `DbObject`s
* @return * @return

View File

@ -30,6 +30,15 @@ a {
background: #EDECEF; background: #EDECEF;
} }
.btn-secondary.disable {
background-color: #A8ADB3;
border: 1px solid #A8ADB3;
}
form.delete-form button.disable .bi {
color: #9298A0 !important;
}
.null-label { .null-label {
background-color: #EEE; background-color: #EEE;
border-radius: 5px; border-radius: 5px;

View File

@ -1,7 +1,16 @@
function updateBulkActions(table, selected) { function updateBulkActions(table, selected) {
let deleteEnabled = table.dataset.deleteenabled;
let divs = document.querySelectorAll(".bulk-actions"); let divs = document.querySelectorAll(".bulk-actions");
divs.forEach(div => { divs.forEach(div => {
div.innerHTML = `${selected} items selected <input type="submit" form="multi-delete-form" class="ui-btn btn btn-secondary" value="Delete">`; if (deleteEnabled === "true") {
div.innerHTML = `${selected} items selected
<input type="submit" form="multi-delete-form"
class="ui-btn btn btn-secondary ${deleteEnabled === "false" ? 'disable' : ''} " value="Delete">`;
} else {
div.innerHTML = `<p class="text-muted m-0 mt-1">DELETE not allowed on this table</p>`;
}
}); });
} }

View File

@ -5,14 +5,15 @@
<tr th:fragment="data_row(row, selectable)" class="table-data-row"> <tr th:fragment="data_row(row, selectable)" class="table-data-row">
<td th:if=${selectable} class="table-checkbox"> <td th:if=${selectable} class="table-checkbox">
<input type="checkbox" class="form-check-input" name="ids" <input type="checkbox" class="form-check-input" name="ids"
th:value="${row.getPrimaryKeyValue()}" form="multi-delete-form"> th:value="${row.getPrimaryKeyValue()}" form="multi-delete-form"
th:classAppend="|${!schema.isDeleteEnabled() ? 'disable' : ''}|">
</td> </td>
<td class="text-center row-icons"> <td class="text-center row-icons">
<a class="ps-1" th:href="|/${baseUrl}/model/${schema.getJavaClass().getName()}/edit/${row.getPrimaryKeyValue()}|"> <a class="ps-1" th:href="|/${baseUrl}/model/${schema.getJavaClass().getName()}/edit/${row.getPrimaryKeyValue()}|">
<i class="bi bi-pencil-square"></i></a> <i class="bi bi-pencil-square"></i></a>
<form class="delete-form" method="POST" <form class="delete-form" method="POST"
th:action="|/${baseUrl}/model/${schema.getJavaClass().getName()}/delete/${row.getPrimaryKeyValue()}|"> th:action="|/${baseUrl}/model/${schema.getJavaClass().getName()}/delete/${row.getPrimaryKeyValue()}|">
<button><i class="bi bi-trash"></i></button> <button th:class="|${!schema.isDeleteEnabled() ? 'disable' : ''}|"><i class="bi bi-trash"></i></button>
</form> </form>
</td> </td>
<td th:each="field : ${schema.getSortedFields()}" <td th:each="field : ${schema.getSortedFields()}"

View File

@ -19,7 +19,9 @@
</a> </a>
</th:block> </th:block>
</th:block> </th:block>
<div class="table-selectable table-responsive" th:fragment="table(results, schema)"> <div class="table-selectable table-responsive" th:fragment="table(results, schema)"
th:data-deleteenabled="${schema.isDeleteEnabled()}"
th:data-editenabled="${schema.isEditEnabled()}">
<div th:if="${results.isEmpty()}"> <div th:if="${results.isEmpty()}">
<p>This table contains no data.</p> <p>This table contains no data.</p>
</div> </div>
@ -30,7 +32,8 @@
</nav> </nav>
<table class="table table-striped align-middle mt-3"> <table class="table table-striped align-middle mt-3">
<tr class="table-data-row"> <tr class="table-data-row">
<th class="table-checkbox"><input type="checkbox" class="form-check-input check-all"></th> <th class="table-checkbox"><input type="checkbox" class="form-check-input check-all"
th:classAppend="|${!schema.isDeleteEnabled() ? 'disable' : ''}|"></th>
<th></th> <th></th>
<th class="table-data-row" th:each="field : ${schema.getSortedFields()}"> <th class="table-data-row" th:each="field : ${schema.getSortedFields()}">
<div class="m-0 p-0 d-flex justify-content-between"> <div class="m-0 p-0 d-flex justify-content-between">