Skip to content

Commit 6355b90

Browse files
committed
#12 Add custom field create/update dialog to admin section
1 parent 7bec5f2 commit 6355b90

File tree

11 files changed

+186
-82
lines changed

11 files changed

+186
-82
lines changed

thehive-backend/conf/routes

+2
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,11 @@ POST /api/maintenance/migrate org.elastic4play.controllers.M
7676

7777
GET /api/list org.elastic4play.controllers.DBListCtrl.list()
7878
DELETE /api/list/:itemId org.elastic4play.controllers.DBListCtrl.deleteItem(itemId)
79+
PATCH /api/list/:itemId org.elastic4play.controllers.DBListCtrl.updateItem(itemId)
7980
POST /api/list/:listName org.elastic4play.controllers.DBListCtrl.addItem(listName)
8081
GET /api/list/:listName org.elastic4play.controllers.DBListCtrl.listItems(listName)
8182

83+
8284
GET /api/user/current controllers.UserCtrl.currentUser()
8385
POST /api/user/_search controllers.UserCtrl.find()
8486
POST /api/user controllers.UserCtrl.create()

ui/app/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
<script src="scripts/controllers/SettingsCtrl.js"></script>
135135
<script src="scripts/controllers/StatisticsCtrl.js"></script>
136136
<script src="scripts/controllers/admin/AdminCaseTemplatesCtrl.js"></script>
137+
<script src="scripts/controllers/admin/AdminCustomFieldDialogCtrl.js"></script>
137138
<script src="scripts/controllers/admin/AdminCustomFieldsCtrl.js"></script>
138139
<script src="scripts/controllers/admin/AdminMetricsCtrl.js"></script>
139140
<script src="scripts/controllers/admin/AdminObservablesCtrl.js"></script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
(function() {
2+
'use strict';
3+
4+
angular.module('theHiveControllers').controller('AdminCustomFieldDialogCtrl',
5+
function($scope, $uibModalInstance, ListSrv, NotificationSrv, customField) {
6+
var self = this;
7+
self.reference = {
8+
types: ['string', 'number', 'boolean', 'date']
9+
};
10+
11+
self.customField = customField;
12+
self.customField.options = (customField.options || []).join('\n');
13+
14+
var onSuccess = function(data) {
15+
$uibModalInstance.close(data);
16+
};
17+
18+
var onFailure = function(response) {
19+
NotificationSrv.error('AdminCustomFieldDialogCtrl', response.data, response.status);
20+
};
21+
22+
self.saveField = function() {
23+
var postData = _.pick(self.customField, 'name', 'title', 'label', 'description', 'type');
24+
postData.options = _.isArray(self.customField.options) ? self.customField.options : self.customField.options.split('\n');
25+
26+
if(self.customField.id) {
27+
ListSrv.update(
28+
{'itemId': self.customField.id},
29+
{'value': JSON.stringify(postData)},
30+
onSuccess,
31+
onFailure);
32+
} else {
33+
ListSrv.save(
34+
{'listId': 'custom_fields'},
35+
{'value': JSON.stringify(postData)},
36+
onSuccess,
37+
onFailure);
38+
}
39+
};
40+
41+
self.cancel = function() {
42+
$uibModalInstance.dismiss();
43+
}
44+
45+
});
46+
})();

ui/app/scripts/controllers/admin/AdminCustomFieldsCtrl.js

+26-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
'use strict';
33

44
angular.module('theHiveControllers').controller('AdminCustomFieldsCtrl',
5-
function($scope, ListSrv, CustomFieldsCacheSrv, NotificationSrv) {
5+
function($scope, $uibModal, ListSrv, CustomFieldsCacheSrv, NotificationSrv) {
66
var self = this;
77

88
self.reference = {
@@ -24,31 +24,39 @@
2424
'listId': 'custom_fields'
2525
}, {}, function(response) {
2626

27-
self.customFields = _.values(response).filter(_.isString).map(function(item) {
28-
return JSON.parse(item);
27+
// self.customFields = _.values(response).filter(_.isString).map(function(item) {
28+
// return JSON.parse(item);
29+
// });
30+
31+
self.customFields = _.map(response.toJSON(), function(value, key) {
32+
var obj = JSON.parse(value);
33+
obj.id = key;
34+
35+
return obj;
2936
});
3037

3138
}, function(response) {
3239
NotificationSrv.error('AdminCustomfieldsCtrl', response.data, response.status);
3340
});
3441
};
3542

36-
self.addCustomField = function() {
37-
ListSrv.save({
38-
'listId': 'custom_fields'
39-
}, {
40-
'value': JSON.stringify(self.formData)
41-
}, function() {
42-
self.initCustomfields();
43-
44-
CustomFieldsCacheSrv.clearCache();
43+
self.showFieldDialog = function(customField) {
44+
var modalInstance = $uibModal.open({
45+
templateUrl: 'views/partials/admin/custom-field-dialog.html',
46+
controller: 'AdminCustomFieldDialogCtrl',
47+
controllerAs: '$vm',
48+
size: 'lg',
49+
resolve: {
50+
customField: angular.copy(customField) || {}
51+
}
52+
});
4553

46-
$scope.$emit('custom-fields:refresh');
47-
},
48-
function(response) {
49-
NotificationSrv.error('AdminCustomfieldsCtrl', response.data, response.status);
50-
});
51-
};
54+
modalInstance.result.then(function(data) {
55+
self.initCustomfields();
56+
CustomFieldsCacheSrv.clearCache();
57+
$scope.$emit('custom-fields:refresh');
58+
});
59+
}
5260

5361
self.initCustomfields();
5462
});

ui/app/scripts/services/ListSrv.js

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
},
99
add: {
1010
method: 'PUT'
11+
},
12+
update: {
13+
url: './api/list/:itemId',
14+
method: 'PATCH',
1115
}
1216
});
1317
});

ui/app/styles/main.css

+4-3
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,9 @@ ul.observable-reports-summary li {
260260
padding: 20px 10px 10px 10px;
261261
}
262262

263-
.case-metrics dt {
264-
width: 300px !important;
263+
.case-metrics dt,
264+
.case-custom-fields dt {
265+
width: 200px !important;
265266
}
266267

267268
.scrollable {
@@ -495,7 +496,7 @@ tags-input.input-sm .tags.focused {
495496
}
496497

497498
footer.main-footer {
498-
padding: 2px;
499+
padding: 2px;
499500
line-height: 40px;
500501
}
501502

+18-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<span ng-hide="updatable.updating" ng-init="active = false" ng-mouseenter="active = true" ng-mouseleave="active = false">
2-
<span class="updatable-value" ng-bind="value | showDate" style="vertical-align:top"></span>
2+
<span ng-if="value!==null && value !==''" style="vertical-align:top" class="updatable-value" >{{value | showDate}}</span>
3+
<span ng-if="value===null || value ===''" style="vertical-align: top; white-space: pre-wrap" class="updatable-value text-warning"><em>Not Specified</em></span>
34
<small ng-show="active">
45
<a style="cursor: pointer;" target="_self" tooltip-popup-delay='500' uib-tooltip="edit">
56
<i class="glyphicon glyphicon-pencil" ng-click="edit()"></i>
@@ -8,20 +9,21 @@
89
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
910
</span>
1011
<span ng-init="updatable.dropdownOpen=false" ng-show="updatable.updating">
11-
<div class="form-inline">
12-
<div class="form-group">
13-
<div class="input-group">
14-
<input class="input-datetime form-control input-sm" ng-click="dateNow=false" ng-model="humanDate" type="datetime"/>
15-
<span uib-btn-checkbox="" class="btn btn-primary input-group-addon" ng-model="dateNow">now</span>
16-
</div>
17-
</div>
18-
<div class="form-group">
19-
<span class="btn" ng-click='update()' target="_self" tooltip-placement="top" tooltip-popup-delay='500' uib-tooltip="save">
20-
<i class="text-success glyphicon glyphicon-ok"></i>
12+
13+
<form ng-submit="update()">
14+
<div class="input-group input-group-sm">
15+
<input class="input-datetime form-control input-sm" ng-click="dateNow=false" ng-model="humanDate" type="datetime"/>
16+
<span class="input-group-btn">
17+
<span uib-btn-checkbox="" class="btn btn-sm btn-primary" ng-model="dateNow">
18+
Now
19+
</span>
20+
<button class="btn btn-sm btn-default" type="submit">
21+
<i class="text-success glyphicon glyphicon-ok"></i>
22+
</button>
23+
<button class="btn btn-sm btn-default" type="button" ng-click="cancel()">
24+
<i class="text-danger glyphicon glyphicon-remove"></i>
25+
</button>
2126
</span>
22-
<span class="btn" ng-click='cancel()' target="_self" tooltip-placement="top" tooltip-popup-delay='500' uib-tooltip="cancel">
23-
<i class="text-danger glyphicon glyphicon-remove"></i>
24-
</span>
25-
</div>
26-
</div>
27+
</div>
28+
</form>
2729
</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<form class="form-horizontal" name="fieldsForm" ng-submit="$vm.saveField()" novalidate>
2+
<div class="modal-header bg-primary">
3+
<h3 class="modal-title">{{$vm.customField.id ? 'Update' : 'Add'}} custom field</h3>
4+
</div>
5+
<div class="modal-body">
6+
7+
<div class="form-group" ng-class="{ 'has-error' : fieldsForm.name.$invalid && !fieldsForm.name.$pristine }">
8+
<label class="col-sm-2 control-label">
9+
Name
10+
<i class="fa fa-asterisk text-danger"></i>
11+
</label>
12+
<div class="col-sm-10">
13+
<input class="form-control" name="name" ng-model="$vm.customField.name" placeholder="Ex: cvss, threatActor, businessRisk" required type="text">
14+
<p class="help-block" ng-show="fieldsForm.name.$invalid && !fieldsForm.name.$pristine">This field is required.</p>
15+
</div>
16+
</div>
17+
<div class="form-group" ng-class="{ 'has-error' : fieldsForm.label.$invalid && !fieldsForm.label.$pristine }">
18+
<label class="col-sm-2 control-label">
19+
Label
20+
<i class="fa fa-asterisk text-danger"></i>
21+
</label>
22+
<div class="col-sm-10">
23+
<input class="form-control" name="label" ng-model="$vm.customField.label" placeholder="Ex: CVSS, Threat actor, Business risk" required type="text">
24+
<p class="help-block" ng-show="fieldsForm.label.$invalid && !fieldsForm.label.$pristine">This field is required.</p>
25+
</div>
26+
</div>
27+
28+
<div class="form-group" ng-class="{ 'has-error' : fieldsForm.description.$invalid && !fieldsForm.description.$pristine }">
29+
<label class="col-sm-2 control-label">
30+
Description
31+
<i class="fa fa-asterisk text-danger"></i>
32+
</label>
33+
<div class="col-sm-10">
34+
<input class="form-control" name="description" ng-model="$vm.customField.description" placeholder="A brief description of the custom field" required type="text">
35+
<p class="help-block" ng-show="fieldsForm.description.$invalid && !fieldsForm.description.$pristine">This field is required.</p>
36+
</div>
37+
</div>
38+
<div class="form-group" ng-class="{ 'has-error' : fieldsForm.type.$invalid && !fieldsForm.type.$pristine }">
39+
<label class="col-sm-2 control-label">
40+
Type
41+
<i class="fa fa-asterisk text-danger"></i>
42+
</label>
43+
<div class="col-sm-10">
44+
<select class="form-control" name="type" ng-model="$vm.customField.type"
45+
ng-options="fieldType for fieldType in $vm.reference.types"
46+
placeholder="Field's type" required></select>
47+
<p class="help-block" ng-show="fieldsForm.type.$invalid && !fieldsForm.type.$pristine">This field is required.</p>
48+
</div>
49+
</div>
50+
51+
<div class="form-group">
52+
<label class="col-sm-2 control-label">
53+
Possible values
54+
</label>
55+
<div class="col-sm-10">
56+
<textarea class="form-control" name="options" ng-model="$vm.customField.options" rows="5" placeholder="Possible values, one per line" type="text"></textarea>
57+
</div>
58+
</div>
59+
</div>
60+
<div class="modal-footer text-left">
61+
<button class="btn btn-default" ng-click="$vm.cancel()" type="button">Cancel</button>
62+
<button class="btn btn-primary pull-right" ng-disabled="fieldsForm.$invalid" type="submit">Save field</button>
63+
</div>
64+
</form>

ui/app/views/partials/admin/custom-fields.html

+19-43
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,8 @@
33
<h3 class="box-title">Case custom fields ({{$vm.customFields.length}})</h3>
44
</div>
55
<div class="box-body">
6-
<form name="fieldsForm" ng-submit="$vm.addCustomField()">
7-
<div class="row">
8-
<div class="col-sm-4">
9-
<div class="form-group">
10-
<label>Name</label>
11-
<input class="form-control" ng-model="$vm.formData.name" placeholder="Field's name" required type="text">
12-
</div>
13-
<div class="form-group">
14-
<label>Label</label>
15-
<input class="form-control" ng-model="$vm.formData.label" placeholder="Field's label" required type="text">
16-
</div>
17-
</div>
186

19-
<div class="col-sm-4">
20-
<div class="form-group">
21-
<label>Description</label>
22-
<input class="form-control" ng-model="$vm.formData.description" placeholder="A brief description of the custom field" type="text">
23-
</div>
24-
<div class="form-group">
25-
<label>Type</label>
26-
<!-- <input class="form-control" ng-model="$vm.formData.type" placeholder="Field's type" required type="text"> -->
27-
<select class="form-control" ng-model="$vm.formData.type"
28-
ng-options="fieldType for fieldType in $vm.reference.types"
29-
placeholder="Field's type"></select>
30-
</div>
31-
</div>
32-
33-
34-
<div class="form-group col-sm-4">
35-
<label>Options</label>
36-
<textarea class="form-control" ng-model="$vm.formData.options" rows="5" placeholder="Possible values, one per line" type="text"></textarea>
37-
</div>
38-
</div>
39-
<div class="text-right">
40-
<button class="btn btn-primary" type="submit" ng-disabled="fieldsForm.$invalid">Add custom field</button>
41-
</div>
42-
</form>
43-
<hr>
7+
<button class="btn btn-sm btn-primary" type="button" ng-click="$vm.showFieldDialog({})">Add custom field</button>
448

459
<div class="row mt-xs">
4610
<div class="col-md-12" ng-if="$vm.customFields.length === 0">
@@ -50,18 +14,30 @@ <h3 class="box-title">Case custom fields ({{$vm.customFields.length}})</h3>
5014
<table class="table table-striped">
5115
<thead>
5216
<tr>
53-
<th class="col-md-3">Name</th>
54-
<th class="col-md-3">Label</th>
55-
<th class="col-md-3">Type</th>
17+
<th width="80">Type</th>
18+
<th width="250">Name</th>
19+
<th width="250">Label</th>
5620
<th>Description</th>
21+
<th width="200">Options</th>
22+
<th width="200">Actions</th>
5723
</tr>
5824
</thead>
5925
<tbody>
60-
<tr ng-repeat="field in $vm.customFields">
61-
<td>{{field.name}}</td>
26+
<tr ng-repeat="field in $vm.customFields | orderBy:'name'">
27+
<td align="center">
28+
<span class="label label-default">{{field.type | uppercase}}</span>
29+
</td>
30+
<td><a href ng-click="$vm.showFieldDialog(field)">{{field.name}}</a></td>
6231
<td>{{field.label}}</td>
63-
<td><span class="label label-default">{{field.type | uppercase}}</span></td>
6432
<td>{{field.description || 'N/A'}}</td>
33+
<td ng-if="field.options.length === 0">
34+
<em>None</em>
35+
</td>
36+
<td ng-if="field.options.length > 0">
37+
<ul class="list list-unstyled" ng-repeat="option in field.options track by $index">
38+
<li>{{option}}</li>
39+
</ul>
40+
</td>
6541
</tr>
6642
</tbody>
6743
</table>

ui/app/views/partials/admin/report-template-dialog.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ <h3 class="modal-title">{{vm.reportTemplate.id ? 'Update' : 'Add'}} report templ
3434
</div>
3535
<div class="modal-footer text-left">
3636
<button class="btn btn-default" ng-click="vm.cancel()" type="button">Cancel</button>
37-
<button class="btn btn-primary pull-right" ng-disabled="taskForm.$invalid" type="submit">Save template</button>
37+
<button class="btn btn-primary pull-right" ng-disabled="tplForm.$invalid" type="submit">Save template</button>
3838
</div>
3939
</form>

ui/app/views/partials/case/case.details.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ <h4 class="vpad10 text-primary">Basic information</h4>
7878
</dd>
7979
</dl>
8080
</div>
81-
<div class="col-md-5 case-metrics">
81+
<div class="col-md-5">
8282

8383
<ng-include src="'views/partials/case/details/custom.fields.html'"></ng-include>
8484
<ng-include src="'views/partials/case/details/metrics.html'"></ng-include>

0 commit comments

Comments
 (0)