Add View Audit Logs to view/edit user. #330
This commit is contained in:
parent
5b167ef680
commit
afeb06006d
6 changed files with 204 additions and 64 deletions
|
|
@ -21,6 +21,8 @@ access_keys:
|
|||
label: Super Admin Role
|
||||
- id: user.profile
|
||||
label: User Profile
|
||||
- id: user.logs
|
||||
label: User Logs
|
||||
- id: role
|
||||
label: Role Access
|
||||
acls:
|
||||
|
|
|
|||
|
|
@ -41,3 +41,13 @@ user_profile_submit:
|
|||
path: /profile
|
||||
controller: App\Controller\UserController::profileSubmit
|
||||
methods: [POST]
|
||||
|
||||
user_view_logs_form:
|
||||
path: /users/{id}/logs
|
||||
controller: App\Controller\UserController::viewLogsForm
|
||||
methods: [GET]
|
||||
|
||||
user_view_logs:
|
||||
path: /user/{id}/logs
|
||||
controller: App\Controller\UserController::getLogs
|
||||
methods: [POST]
|
||||
|
|
|
|||
|
|
@ -23,12 +23,10 @@ services:
|
|||
# The best practice is to be explicit about your dependencies anyway.
|
||||
|
||||
# influxdb
|
||||
influxdb_client:
|
||||
class: InfluxDB\Client
|
||||
InfluxDB\Client:
|
||||
arguments: ['%env(INFLUXDB_HOST)%', '%env(INFLUXDB_PORT)%']
|
||||
influxdb_database:
|
||||
class: InfluxDB\Database
|
||||
arguments: ['%env(INFLUXDB_DB)%', '@influxdb_client']
|
||||
InfluxDB\Database:
|
||||
arguments: ['%env(INFLUXDB_DB)%', "@InfluxDB\\Client"]
|
||||
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
|
|
@ -270,7 +268,7 @@ services:
|
|||
App\EventListener\EntityListener:
|
||||
arguments:
|
||||
$token_storage: "@security.token_storage"
|
||||
$log_db: '@influxdb_database'
|
||||
$log_db: "@InfluxDB\\Database"
|
||||
$entities: ['App\Entity\User', 'App\Entity\Role', 'App\Entity\Partner']
|
||||
tags:
|
||||
- name: 'doctrine.event_listener'
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
|||
|
||||
use Catalyst\MenuBundle\Annotation\Menu;
|
||||
|
||||
use InfluxDB\Client;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
|
|
@ -484,4 +486,49 @@ class UserController extends Controller
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Menu(selected="user_list")
|
||||
*/
|
||||
public function viewLogsForm($id)
|
||||
{
|
||||
$this->denyAccessUnlessGranted('user.logs', null, 'No access.');
|
||||
|
||||
$params['id'] = $id;
|
||||
|
||||
// response
|
||||
return $this->render('user/log.html.twig', $params);
|
||||
}
|
||||
|
||||
public function getLogs(Client $client, $id)
|
||||
{
|
||||
error_log('in getLogs');
|
||||
|
||||
// fetch database
|
||||
// TODO: find way to replace hardcoded db name
|
||||
$database = $client->selectDB('logging_db');
|
||||
|
||||
// query will return a resultset object
|
||||
$result = $database->query('SELECT * FROM entity_log');
|
||||
|
||||
// get the points from the resultset, which is an array
|
||||
$points = $result->getPoints();
|
||||
|
||||
error_log('getLogs count points ' . count($points));
|
||||
|
||||
// array has format
|
||||
/*
|
||||
$rows = [];
|
||||
foreach ($points as $point)
|
||||
{
|
||||
$row['time'] = $point['time'];
|
||||
error_log($point['entity_type']);
|
||||
|
||||
error_log($point['action']);
|
||||
} */
|
||||
|
||||
return $this->json([
|
||||
'data' => $points,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@
|
|||
<div class="form-control-feedback hide" data-field="username"></div>
|
||||
<span class="m-form__help">Unique alias for this user</span>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<a href="{{ url('user_view_logs_form', {'id':obj.getID()}) }}" class="btn btn-success">View Audit Logs</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group m-form__group row">
|
||||
<div class="col-lg-6">
|
||||
|
|
@ -182,72 +185,73 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$(function() {
|
||||
$("#row-form").submit(function(e) {
|
||||
var form = $(this);
|
||||
<script>
|
||||
$(function() {
|
||||
$("#row-form").submit(function(e) {
|
||||
var form = $(this);
|
||||
|
||||
e.preventDefault();
|
||||
e.preventDefault();
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: form.prop('action'),
|
||||
data: form.serialize()
|
||||
}).done(function(response) {
|
||||
// remove all error classes
|
||||
removeErrors();
|
||||
swal({
|
||||
title: 'Done!',
|
||||
text: 'Your changes have been saved!',
|
||||
type: 'success',
|
||||
onClose: function() {
|
||||
window.location.href = "{{ url(mode == 'profile' ? 'home' : 'user_list') }}";
|
||||
}
|
||||
});
|
||||
}).fail(function(response) {
|
||||
if (response.status == 422) {
|
||||
var errors = response.responseJSON.errors;
|
||||
var firstfield = false;
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: form.prop('action'),
|
||||
data: form.serialize()
|
||||
}).done(function(response) {
|
||||
// remove all error classes
|
||||
removeErrors();
|
||||
swal({
|
||||
title: 'Done!',
|
||||
text: 'Your changes have been saved!',
|
||||
type: 'success',
|
||||
onClose: function() {
|
||||
window.location.href = "{{ url(mode == 'profile' ? 'home' : 'user_list') }}";
|
||||
}
|
||||
});
|
||||
}).fail(function(response) {
|
||||
if (response.status == 422) {
|
||||
var errors = response.responseJSON.errors;
|
||||
var firstfield = false;
|
||||
|
||||
// remove all error classes first
|
||||
removeErrors();
|
||||
// remove all error classes first
|
||||
removeErrors();
|
||||
|
||||
// display errors contextually
|
||||
$.each(errors, function(field, msg) {
|
||||
var formfield = $("[name='" + field + "']");
|
||||
var label = $("label[data-field='" + field + "']");
|
||||
var msgbox = $(".form-control-feedback[data-field='" + field + "']");
|
||||
// display errors contextually
|
||||
$.each(errors, function(field, msg) {
|
||||
var formfield = $("[name='" + field + "']");
|
||||
var label = $("label[data-field='" + field + "']");
|
||||
var msgbox = $(".form-control-feedback[data-field='" + field + "']");
|
||||
|
||||
// add error classes to bad fields
|
||||
formfield.addClass('form-control-danger');
|
||||
label.addClass('has-danger');
|
||||
msgbox.html(msg).addClass('has-danger').removeClass('hide');
|
||||
// add error classes to bad fields
|
||||
formfield.addClass('form-control-danger');
|
||||
label.addClass('has-danger');
|
||||
msgbox.html(msg).addClass('has-danger').removeClass('hide');
|
||||
|
||||
// check if this field comes first in DOM
|
||||
var domfield = formfield.get(0);
|
||||
// check if this field comes first in DOM
|
||||
var domfield = formfield.get(0);
|
||||
|
||||
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
|
||||
firstfield = domfield;
|
||||
}
|
||||
});
|
||||
if (!firstfield || (firstfield && firstfield.compareDocumentPosition(domfield) === 2)) {
|
||||
firstfield = domfield;
|
||||
}
|
||||
});
|
||||
|
||||
// focus on first bad field
|
||||
firstfield.focus();
|
||||
// focus on first bad field
|
||||
firstfield.focus();
|
||||
|
||||
// scroll to above that field to make it visible
|
||||
$('html, body').animate({
|
||||
scrollTop: $(firstfield).offset().top - 200
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
// scroll to above that field to make it visible
|
||||
$('html, body').animate({
|
||||
scrollTop: $(firstfield).offset().top - 200
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// remove all error classes
|
||||
function removeErrors() {
|
||||
$(".form-control-danger").removeClass('form-control-danger');
|
||||
$("[data-field]").removeClass('has-danger');
|
||||
$(".form-control-feedback[data-field]").addClass('hide');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
// remove all error classes
|
||||
function removeErrors() {
|
||||
$(".form-control-danger").removeClass('form-control-danger');
|
||||
$("[data-field]").removeClass('has-danger');
|
||||
$(".form-control-feedback[data-field]").addClass('hide');
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
79
templates/user/log.html.twig
Normal file
79
templates/user/log.html.twig
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<!-- BEGIN: Subheader -->
|
||||
<div class="m-subheader">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="mr-auto">
|
||||
<h3 class="m-subheader__title">Audit Logs</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END: Subheader -->
|
||||
<div class="m-content">
|
||||
<!--Begin::Section-->
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<div class="m-portlet m-portlet--mobile">
|
||||
<div class="m-portlet__body">
|
||||
<!--begin: Datatable -->
|
||||
<div id="data-rows"></div>
|
||||
<!--end: Datatable -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$(function() {
|
||||
console.log( {{ id }} );
|
||||
var options = {
|
||||
data: {
|
||||
type: 'remote',
|
||||
source: {
|
||||
read: {
|
||||
url: '{{ url('user_view_logs', {'id': id}) }}',
|
||||
method: 'POST'
|
||||
}
|
||||
},
|
||||
saveState: {
|
||||
cookie: false,
|
||||
webstorage: false
|
||||
},
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'time',
|
||||
title: 'Time'
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
title: 'Action'
|
||||
},
|
||||
{
|
||||
field: 'content',
|
||||
title: 'Content'
|
||||
},
|
||||
{
|
||||
field: 'entity_id',
|
||||
title: 'Entity ID'
|
||||
},
|
||||
{
|
||||
field: 'entity_type',
|
||||
title: 'Entity Type'
|
||||
},
|
||||
{
|
||||
field: 'user',
|
||||
title: 'User'
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
var table = $("#data-rows").mDatatable(options);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
Reference in a new issue