Skip to content

Commit

Permalink
Merge pull request #757 from Security-Onion-Solutions/jertel/wip
Browse files Browse the repository at this point in the history
add confirm delete popup for ui element entries
  • Loading branch information
jertel authored Feb 25, 2025
2 parents 76d152c + aa004bc commit 7394ba8
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 12 deletions.
14 changes: 13 additions & 1 deletion html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5660,7 +5660,7 @@ <h4 v-if="!$root.loading">{{ i18n.settingsCustomized }} {{ settingsCustomized }}
</div>
</v-form>
<p>* {{ i18n.requiredField }}</p>
<v-btn v-if="!isEntryEmpty(selected, entry)" id="'setting-element-' + entryIdx + '-delete-' + entry['field']" icon variant="text" size="small" @click="clearEntry(entry, entryIdx)" :title="i18n.settingDeleteEntryHelp" data-aid="config_item_delete_entry">
<v-btn v-if="!isEntryEmpty(selected, entry)" id="'setting-element-' + entryIdx + '-delete-' + entry['field']" icon variant="text" size="small" @click="showClearEntryDialog(selected, entryIdx)" :title="i18n.settingDeleteEntryHelp" data-aid="config_item_delete_entry">
<v-icon color="error">fa-trash</v-icon>
</v-btn>
</v-expansion-panel-text>
Expand Down Expand Up @@ -5760,6 +5760,18 @@ <h4 v-if="!$root.loading">{{ i18n.settingsCustomized }} {{ settingsCustomized }}
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="confirmRemoveEntryDialog" persistent max-width="390" data-aid="config_confirm_remove_entry_dialog">
<v-card>
<v-card-title class="text-h5" v-text="i18n.settingConfirmRemove" />
<v-card-text v-if="confirmRemoveEntryMessage" v-text="confirmRemoveEntryMessage" />
<v-card-text v-text="i18n.settingConfirmRemoveHelp" />
<v-card-actions>
<v-spacer></v-spacer>
<v-btn variant="text" id="config-remove-entry-proceed-button" @click="confirmClearEntry()" v-text="i18n.yes" data-aid="config_remove_entry_dialog_confirm" />
<v-btn variant="text" id="config-remove-entry-cancel-button" @click="cancelClearEntry()" v-text="i18n.no" data-aid="config_remove_entry_dialog_abort" />
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</script>

Expand Down
3 changes: 3 additions & 0 deletions html/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ const i18n = {
pcapRetention: 'PCAP Retention',
pcapRetentionAbbr: 'PCAP Avail',
pending: 'Pending',
pendingDeletion: '(pending deletion)',
premiumSupport: 'Premium Support',
pro: 'Security Onion Pro',
product: 'Security Onion',
Expand Down Expand Up @@ -801,6 +802,8 @@ const i18n = {
settingCategory_ui: 'User Interface',
settingConfirmCancel: 'Unsaved Changes',
settingConfirmCancelHelp: 'Discard unsaved changes?',
settingConfirmRemove: 'Remove Entry',
settingConfirmRemoveHelp: 'Proceeding will clear out this entry\'s form values. For existing entries the final deletion will occur once the Save button (green checkmark) is pressed. Would you like to proceed?',
settingConfirmReset: 'Reset Value',
settingConfirmResetHelp: 'Delete this custom setting, and reset to default if available?',
settingAdvanced: 'Provide optional, custom configuration in YAML format. Note that improper customizations often are the cause of grid malfunctions.',
Expand Down
29 changes: 22 additions & 7 deletions html/js/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ routes.push({
confirmResetDialog: false,
treeVisible: true,
uiElementsValid: false,
confirmRemoveEntryDialog: false,
confirmRemoveEntryMessage: "",
confirmRemoveEntryIdx: 0,
}
},
mounted() {
Expand Down Expand Up @@ -199,6 +202,7 @@ routes.push({
syntax: setting.syntax,
duplicates: setting.duplicates,
uiElements: setting.uiElements,
uiElementsDeleteMessage: setting.uiElementsDeleteMessage,
forcedType: setting.forcedType,
options: setting.options,
optionSeparator: setting.optionSeparator,
Expand Down Expand Up @@ -507,31 +511,42 @@ routes.push({
}
return value.trim().length == 0;
},
clearEntry(entry, idx) {
cancelClearEntry() {
this.confirmRemoveEntryDialog = false;
this.confirmRemoveEntryIdx = 0;
this.confirmRemoveEntryMessage = "";
},
confirmClearEntry() {
const entry = this.form.entries[this.confirmRemoveEntryIdx];
for (prop in entry) {
if (prop != "_title") {
entry[prop] = "";
}
}
if (entry._title != "+") {
this.markDirtyEntries();
entry._title = "";
this.generateEntryTitle(entry, idx);
entry._title = this.generateIndexTitle(this.confirmRemoveEntryIdx, this.i18n.pendingDeletion);
}
this.cancelClearEntry();
},
showClearEntryDialog(setting, idx) {
this.confirmRemoveEntryDialog = true;
this.confirmRemoveEntryMessage = setting.uiElementsDeleteMessage;
this.confirmRemoveEntryIdx = idx;
},
markDirtyEntries() {
this.form.value = Date.now() + "";
},
generateIndexTitle(idx, title) {
return "" + (idx+1) + ". " + (title ? title : "");
},
generateEntryTitle(entry, idx) {
const setting = this.findActiveSetting();
var title = null;
if (setting && setting.uiElements && setting.uiElements.length > 0) {
title = entry[setting.uiElements[0].field];
}
entry._title = "" + (idx+1) + ". "
if (title) {
entry._title += title;
}
entry._title = this.generateIndexTitle(idx, title);
},
cancel(force) {
var setting = this.findActiveSetting();
Expand Down
35 changes: 31 additions & 4 deletions html/js/routes/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ test('loadData', async () => {
"syntax": undefined,
"title": "Farout",
"uiElements": undefined,
"uiElementsDeleteMessage": undefined,
"value": null,
},
{
Expand Down Expand Up @@ -101,6 +102,7 @@ test('loadData', async () => {
"syntax": undefined,
"title": "CCA",
"uiElements": undefined,
"uiElementsDeleteMessage": undefined,
"value": undefined,
},
{
Expand Down Expand Up @@ -129,6 +131,7 @@ test('loadData', async () => {
"syntax": undefined,
"title": "Barley",
"uiElements": undefined,
"uiElementsDeleteMessage": undefined,
"value": undefined,
}
];
Expand Down Expand Up @@ -164,6 +167,7 @@ test('loadData', async () => {
"syntax": undefined,
"title": "Farout",
"uiElements": undefined,
"uiElementsDeleteMessage": undefined,
"value": null
},
{
Expand Down Expand Up @@ -192,6 +196,7 @@ test('loadData', async () => {
"syntax": undefined,
"title": "Barley",
"uiElements": undefined,
"uiElementsDeleteMessage": undefined,
"value": undefined
}
],
Expand Down Expand Up @@ -228,6 +233,7 @@ test('loadData', async () => {
"syntax": undefined,
"title": "CCA",
"uiElements": undefined,
"uiElementsDeleteMessage": undefined,
"value": undefined
}
];
Expand Down Expand Up @@ -868,17 +874,38 @@ test('markDirtyEntries', () => {
expect(comp.form.value.length).toBe(13);
});

test('clearEntry', () => {
test('showClearEntryDialog', () => {
expect(comp.confirmRemoveEntryDialog).toBe(false);
const setting = {uiElementsDeleteMessage: 'hi'};
comp.showClearEntryDialog(setting, 2);
expect(comp.confirmRemoveEntryDialog).toBe(true);
expect(comp.confirmRemoveEntryMessage).toBe('hi');
expect(comp.confirmRemoveEntryIdx).toBe(2);
});

test('cancelClearEntry', () => {
comp.confirmRemoveEntryDialog = true;
comp.confirmRemoveEntryMessage = 'hi';
comp.confirmRemoveEntryIdx = 2;
comp.cancelClearEntry();
expect(comp.confirmRemoveEntryDialog).toBe(false);
expect(comp.confirmRemoveEntryMessage).toBe('');
expect(comp.confirmRemoveEntryIdx).toBe(0);
});

test('confirmClearEntry', () => {
entry = {foo: 'bar', some: 'value', _title: 'title'};
comp.form.value = 123;
comp.clearEntry(entry, 1);
comp.form.entries = [entry]
comp.confirmRemoveEntryIdx = 0;
comp.confirmClearEntry();
expect(comp.form.value.length).toBe(13);
expect(entry.foo).toBe('');
expect(entry.some).toBe('');
expect(entry._title).toBe('2. ');
expect(entry._title).toBe('1. (pending deletion)');

entry = {foo: 'bar', some: 'value', _title: '+'};
comp.clearEntry(entry, 1);
comp.confirmClearEntry(entry, 1);
expect(entry._title).toBe('+');
});

Expand Down
2 changes: 2 additions & 0 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ type Setting struct {
OptionSeparator string `json:"optionSeparator"`
// (metadata) List of UiElement objects describing how the UI should present the field for input
UiElements []UiElement `json:"uiElements"`
// (metadata) Confirmation message to show when user clicks the delete button on a ui element. If omitted, no message will be shown.
UiElementsDeleteMessage string `json:"uiElementsDeleteMessage"`
}

func NewSetting(id string) *Setting {
Expand Down
2 changes: 2 additions & 0 deletions server/modules/salt/saltstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@ func (store *Saltstore) updateSettingWithAnnotation(setting *model.Setting, anno
log.Error("Invalid annotation; cannot cast to map")
}
}
case "uiElementsDeleteMessage":
setting.UiElementsDeleteMessage = value.(string)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions server/modules/salt/saltstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,7 @@ func TestUpdateSettingWithAnnotation(tester *testing.T) {
annotations["syntax"] = "yaml"
annotations["duplicates"] = true
annotations["jinjaEscaped"] = true
annotations["uiElementsDeleteMessage"] = "hi"

assert.False(tester, setting.Multiline)
salt.updateSettingWithAnnotation(setting, annotations)
Expand All @@ -1139,6 +1140,7 @@ func TestUpdateSettingWithAnnotation(tester *testing.T) {
assert.Equal(tester, "yaml", setting.Syntax)
assert.True(tester, setting.Duplicates)
assert.True(tester, setting.JinjaEscaped)
assert.Equal(tester, "hi", setting.UiElementsDeleteMessage)
}

func TestManageUser_AddUser(tester *testing.T) {
Expand Down

0 comments on commit 7394ba8

Please sign in to comment.