Skip to content

Commit 0bc1aa0

Browse files
committed
select and delete multiple records
1 parent 64c820a commit 0bc1aa0

4 files changed

Lines changed: 91 additions & 37 deletions

File tree

includes/record_list.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,12 @@ function GetRecordListTable($parent, $domain)
214214
global $g_editable, $g_show_hidden_types, $g_hidden_record_types, $g_hidden_types_present, $g_total_records_count, $g_hidden_records_count, $g_enable_idn;
215215
$table = new HtmlTable($parent);
216216
$table->Condensed = true;
217-
$table->Headers = [ "Record", "TTL", "Scope", "Type", "Value", "Options" ];
217+
$table->Headers = [ "", "Record", "TTL", "Scope", "Type", "Value", "Options" ];
218218
$table->ClassName = 'table table-bordered table-hover table-sm';
219-
$table->SetColumnWidth(2, '80px'); // Scope
220-
$table->SetColumnWidth(3, '80px'); // Type
221-
$table->SetColumnWidth(5, '80px'); // Options
219+
$table->SetColumnWidth(0, '28px'); // Select
220+
$table->SetColumnWidth(3, '80px'); // Scope
221+
$table->SetColumnWidth(4, '80px'); // Type
222+
$table->SetColumnWidth(6, '80px'); // Options
222223
$records = GetRecordList($domain);
223224
$is_editable = Zones::IsEditable($domain) && IsAuthorizedToWrite($domain);
224225
$has_ptr = Zones::HasPTRZones();
@@ -261,6 +262,7 @@ function GetRecordListTable($parent, $domain)
261262

262263
if (!$is_editable || !in_array($record[3], $g_editable))
263264
{
265+
$select_record = '';
264266
$record[] = '';
265267
} else
266268
{
@@ -283,17 +285,15 @@ function GetRecordListTable($parent, $domain)
283285
}
284286

285287
$delete_value = $ascii_record0 . " " . $record[1] . " " . $record[3] . " " . $ascii_record4;
288+
$select_record = '<input type="checkbox" class="record-select" value="' . htmlspecialchars($delete_value, ENT_QUOTES, 'UTF-8') . '">';
286289
$delete_record = '<a href="#" data-confirm="' . $delete_confirmation . '" data-delete="' . htmlspecialchars($delete_value, ENT_QUOTES, 'UTF-8') .
287290
'" onclick="return submitDeleteRecord(this)"><span class="bi bi-trash" title="Delete"></span></a>';
288291
$delete_record_with_ptr = '';
289292
if ($has_ptr && $record[3] == 'A')
290293
{
291294
// Optional button to delete record together with PTR record, show only if there are PTR zones in cfg
292295
$delete_record_with_ptr = '<a href="#" data-confirm="' . $delete_confirmation . '" data-delete="' . htmlspecialchars($delete_value, ENT_QUOTES, 'UTF-8') .
293-
'" data-ptr="true" data-key="' . htmlspecialchars($ascii_record0, ENT_QUOTES, 'UTF-8') .
294-
'" data-value="' . htmlspecialchars($ascii_record4, ENT_QUOTES, 'UTF-8') .
295-
'" data-type="' . htmlspecialchars($record[3], ENT_QUOTES, 'UTF-8') .
296-
'" onclick="return submitDeleteRecord(this)"><span style="color: #ff0000;" class="bi bi-trash" title="Delete together with associated PTR record (if any exist)"></span></a>';
296+
'" data-ptr="true" onclick="return submitDeleteRecord(this)"><span style="color: #ff0000;" class="bi bi-trash" title="Delete together with associated PTR record (if any exist)"></span></a>';
297297
}
298298
$large_space = '&nbsp;&nbsp;';
299299
$record[] = $delete_record . $large_space . '<a href="index.php?action=edit&domain=' . $display_domain . '&key=' .
@@ -306,11 +306,12 @@ function GetRecordListTable($parent, $domain)
306306
$record[2] = $display_record2;
307307
$record[3] = $display_record3;
308308
$record[4] = '<span class="value">' . $display_record4 . '</span>';
309+
array_unshift($record, $select_record);
309310
$table->AppendRow($record);
310311
}
311312
if ($is_editable) {
312313
$add = '<a href="index.php?action=new&domain=' . $domain . '"><span title="Add New" class="bi bi-plus"></span></a>';
313-
$table->AppendRow(['', '', '', '', '', $add]);
314+
$table->AppendRow(['', '', '', '', '', '', $add]);
314315
}
315316
return $table;
316317
}

includes/tab_manage.php

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,39 @@ public static function ProcessDelete($well)
2828
if (!isset($_POST["delete"]))
2929
return;
3030
CheckCSRFToken();
31-
32-
$record = $_POST["delete"];
33-
34-
if (DNS_DeleteRecord($g_selected_domain, $record))
35-
$well->AppendObject(new BS_Alert("Successfully deleted record " . $record));
31+
32+
$records = $_POST["delete"];
33+
if (!is_array($records))
34+
$records = [ $records ];
35+
36+
$deleted = 0;
37+
foreach ($records as $record)
38+
{
39+
if (DNS_DeleteRecord($g_selected_domain, $record))
40+
$deleted++;
41+
}
42+
43+
if ($deleted == 1)
44+
$well->AppendObject(new BS_Alert("Successfully deleted record " . $records[0]));
45+
else if ($deleted > 1)
46+
$well->AppendObject(new BS_Alert("Successfully deleted $deleted records"));
3647

3748
if (isset($_POST["ptr"]) && $_POST["ptr"] == true)
3849
{
39-
Debug('PTR record deletion was requested for ' . $record);
40-
if (!isset($_POST['key']) || !isset($_POST['value']) || !isset($_POST['type']))
41-
{
42-
Warning('PTR record was not removed because either key, value or type was not specified');
43-
return;
44-
}
45-
$key = $_POST['key'];
46-
$type = $_POST['type'];
47-
$value = $_POST['value'];
48-
if ($type != 'A')
49-
{
50-
Warning('Requested PTR record was not deleted: PTR record can be only deleted when you are changing A record, you deleted ' . $type . ' record instead');
51-
} else
50+
foreach ($records as $record)
5251
{
53-
DNS_DeletePTRForARecord($value, $key, '');
52+
$parts = preg_split('/\s+/', trim($record), 4);
53+
if (count($parts) < 4)
54+
{
55+
Warning('PTR record was not removed because record details were incomplete');
56+
continue;
57+
}
58+
if ($parts[2] != 'A')
59+
{
60+
Debug('Not deleting PTR for non-A record: ' . $record);
61+
continue;
62+
}
63+
DNS_DeletePTRForARecord($parts[3], $parts[0], '');
5464
}
5565
}
5666
}
@@ -101,11 +111,7 @@ public static function GetContents($fc)
101111
$domain = htmlspecialchars($g_selected_domain, ENT_QUOTES, 'UTF-8');
102112
$fc->AppendHtmlLine('<form id="deleteRecordForm" method="post" action="index.php?action=manage&domain=' . $domain . '">' .
103113
'<input type="hidden" name="csrf_token" value="' . $csrf_token . '">' .
104-
'<input type="hidden" name="delete" value="">' .
105114
'<input type="hidden" name="ptr" value="">' .
106-
'<input type="hidden" name="key" value="">' .
107-
'<input type="hidden" name="value" value="">' .
108-
'<input type="hidden" name="type" value="">' .
109115
'</form>');
110116
if ($g_hidden_types_present === true)
111117
{
@@ -116,5 +122,8 @@ public static function GetContents($fc)
116122
$fc->AppendHtml('<div class="hidden_types">This zone contains record types that are hidden by default, click <a href="?action=manage&domain=' . $g_selected_domain . '&hidden_types=hide">here</a> to hide them</div>');
117123
}
118124
$fc->AppendObject($record_list);
125+
if (Zones::IsEditable($g_selected_domain) && IsAuthorizedToWrite($g_selected_domain))
126+
$fc->AppendHtmlLine('<div class="bulk_actions"><button type="button" class="btn btn-sm btn-danger" onclick="return submitSelectedRecords(false)">Delete selected</button> ' .
127+
'<button type="button" class="btn btn-sm btn-danger" onclick="return submitSelectedRecords(true)">Delete selected records and their PTR records</button></div>');
119128
}
120129
}

js/manage.js

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
1+
function clearDeleteForm(form) {
2+
for (const input of form.querySelectorAll("[data-dynamic-delete]")) {
3+
input.remove();
4+
}
5+
form.querySelector("[name=ptr]").value = "";
6+
}
7+
8+
function appendHidden(form, name, value) {
9+
const input = document.createElement("input");
10+
input.type = "hidden";
11+
input.name = name;
12+
input.value = value;
13+
input.dataset.dynamicDelete = "true";
14+
form.appendChild(input);
15+
}
16+
117
function submitDeleteRecord(link) {
218
if (!confirm(link.dataset.confirm)) {
319
return false;
420
}
521

622
const form = document.getElementById("deleteRecordForm");
7-
form.querySelector("[name=delete]").value = link.dataset.delete || "";
8-
form.querySelector("[name=ptr]").value = link.dataset.ptr || "";
9-
form.querySelector("[name=key]").value = link.dataset.key || "";
10-
form.querySelector("[name=value]").value = link.dataset.value || "";
11-
form.querySelector("[name=type]").value = link.dataset.type || "";
23+
clearDeleteForm(form);
24+
appendHidden(form, "delete[]", link.dataset.delete || "");
25+
if (link.dataset.ptr === "true") {
26+
form.querySelector("[name=ptr]").value = "true";
27+
}
28+
form.submit();
29+
return false;
30+
}
31+
32+
function submitSelectedRecords(deletePtr) {
33+
const selectedRecords = document.querySelectorAll(".record-select:checked");
34+
if (selectedRecords.length === 0) {
35+
alert("No records selected");
36+
return false;
37+
}
38+
39+
const action = deletePtr ? "Delete selected records and their PTR records?" : "Delete selected records?";
40+
if (!confirm(action + " (" + selectedRecords.length + ")")) {
41+
return false;
42+
}
43+
44+
const form = document.getElementById("deleteRecordForm");
45+
clearDeleteForm(form);
46+
form.querySelector("[name=ptr]").value = deletePtr ? "true" : "";
47+
48+
for (const record of selectedRecords) {
49+
appendHidden(form, "delete[]", record.value);
50+
}
51+
1252
form.submit();
1353
return false;
1454
}

style.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ code {
109109
right: 20px;
110110
}
111111

112+
.bulk_actions {
113+
margin-top: 0.5rem;
114+
}
115+
112116
.value {
113117
white-space:pre-wrap;
114118
}

0 commit comments

Comments
 (0)