1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-07-30 04:23:11 +03:00

Added audit log interface

- Displays the currently tracked activities in the system.

Related to #2173 and #1167
This commit is contained in:
Dan Brown
2020-09-19 12:06:45 +01:00
parent e5f0b4dd85
commit 78bf044a7a
13 changed files with 351 additions and 8 deletions

View File

@ -42,6 +42,7 @@ import settingColorPicker from "./setting-color-picker.js"
import shelfSort from "./shelf-sort.js"
import sidebar from "./sidebar.js"
import sortableList from "./sortable-list.js"
import submitOnChange from "./submit-on-change.js"
import tabs from "./tabs.js"
import tagManager from "./tag-manager.js"
import templateManager from "./template-manager.js"
@ -94,6 +95,7 @@ const componentMapping = {
"shelf-sort": shelfSort,
"sidebar": sidebar,
"sortable-list": sortableList,
"submit-on-change": submitOnChange,
"tabs": tabs,
"tag-manager": tagManager,
"template-manager": templateManager,

View File

@ -0,0 +1,19 @@
/**
* Submit on change
* Simply submits a parent form when this input is changed.
* @extends {Component}
*/
class SubmitOnChange {
setup() {
this.$el.addEventListener('change', () => {
const form = this.$el.closest('form');
if (form) {
form.submit();
}
});
}
}
export default SubmitOnChange;

View File

@ -81,6 +81,20 @@ return [
'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
// Audit Log
'audit' => 'Audit Log',
'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
'audit_event_filter' => 'Event Filter',
'audit_event_filter_no_filter' => 'No Filter',
'audit_deleted_item' => 'Deleted Item',
'audit_deleted_item_name' => 'Name: :name',
'audit_table_user' => 'User',
'audit_table_event' => 'Event',
'audit_table_item' => 'Related Item',
'audit_table_date' => 'Activity Date',
'audit_date_from' => 'Date Range From',
'audit_date_to' => 'Date Range To',
// Role Settings
'roles' => 'Roles',
'role_user_roles' => 'User Roles',

View File

@ -121,6 +121,11 @@ body.flexbox {
position: relative;
}
.flex-container-row {
display: flex;
flex-direction: row;
}
.flex-container-column {
display: flex;
flex-direction: column;

View File

@ -288,4 +288,15 @@ $btt-size: 40px;
transform: rotate(180deg);
}
}
}
table a.audit-log-user {
display: grid;
grid-template-columns: 42px 1fr;
align-items: center;
}
table a.icon-list-item {
display: grid;
grid-template-columns: 36px 1fr;
align-items: center;
}

View File

@ -0,0 +1,98 @@
@extends('simple-layout')
@section('body')
<div class="container">
<div class="grid left-focus v-center no-row-gap">
<div class="py-m">
@include('settings.navbar', ['selected' => 'audit'])
</div>
</div>
<div class="card content-wrap auto-height">
<h2 class="list-heading">{{ trans('settings.audit') }}</h2>
<p class="text-muted">{{ trans('settings.audit_desc') }}</p>
<div class="flex-container-row">
<div component="dropdown" class="list-sort-type dropdown-container mr-m">
<label for="">{{ trans('settings.audit_event_filter') }}</label>
<button refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" class="input-base text-left">{{ $listDetails['event'] ?: trans('settings.audit_event_filter_no_filter') }}</button>
<ul refs="dropdown@menu" class="dropdown-menu">
<li @if($listDetails['event'] === '') class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => '']) }}">{{ trans('settings.audit_event_filter_no_filter') }}</a></li>
@foreach($activityKeys as $key)
<li @if($key === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $key]) }}">{{ $key }}</a></li>
@endforeach
</ul>
</div>
@foreach(['date_from', 'date_to'] as $filterKey)
<form action="{{ url('/settings/audit') }}" method="get" class="block mr-m">
@foreach($listDetails as $param => $val)
@if(!empty($val) && $param !== $filterKey)
<input type="hidden" name="{{ $param }}" value="{{ $val }}">
@endif
@endforeach
<label for="audit_filter_{{ $filterKey }}">{{ trans('settings.audit_' . $filterKey) }}</label>
<input id="audit_filter_{{ $filterKey }}"
component="submit-on-change"
type="date"
name="{{ $filterKey }}"
value="{{ $listDetails[$filterKey] ?? '' }}">
</form>
@endforeach
</div>
<hr class="mt-l mb-s">
{{ $activities->links() }}
<table class="table">
<tbody>
<tr>
<th>{{ trans('settings.audit_table_user') }}</th>
<th>
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'key']) }}">{{ trans('settings.audit_table_event') }}</a>
</th>
<th>{{ trans('settings.audit_table_item') }}</th>
<th>
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'created_at']) }}">{{ trans('settings.audit_table_date') }}</a></th>
</tr>
@foreach($activities as $activity)
<tr>
<td>
@if($activity->user)
<a href="{{ $activity->user->getEditUrl() }}" class="audit-log-user">
<div><img class="avatar block" src="{{ $activity->user->getAvatar(40)}}" alt="{{ $activity->user->name }}"></div>
<div>{{ $activity->user->name }}</div>
</a>
@else
[ID: {{ $activity->user_id }}] {{ trans('common.deleted_user') }}
@endif
</td>
<td>{{ $activity->key }}</td>
<td>
@if($activity->entity)
<a href="{{ $activity->entity->getUrl() }}" class="icon-list-item">
<span role="presentation" class="icon text-{{$activity->entity->getType()}}">@icon($activity->entity->getType())</span>
<div class="text-{{ $activity->entity->getType() }}">
{{ $activity->entity->name }}
</div>
</a>
@elseif($activity->extra)
<div class="px-m">
{{ trans('settings.audit_deleted_item') }} <br>
{{ trans('settings.audit_deleted_item_name', ['name' => $activity->extra]) }}
</div>
@endif
</td>
<td>{{ $activity->created_at }}</td>
</tr>
@endforeach
</tbody>
</table>
{{ $activities->links() }}
</div>
</div>
@stop

View File

@ -4,6 +4,9 @@
<a href="{{ url('/settings') }}" @if($selected == 'settings') class="active" @endif>@icon('settings'){{ trans('settings.settings') }}</a>
<a href="{{ url('/settings/maintenance') }}" @if($selected == 'maintenance') class="active" @endif>@icon('spanner'){{ trans('settings.maint') }}</a>
@endif
@if($currentUser->can('settings-manage') && $currentUser->can('users-manage'))
<a href="{{ url('/settings/audit') }}" @if($selected == 'audit') class="active" @endif>@icon('open-book'){{ trans('settings.audit') }}</a>
@endif
@if($currentUser->can('users-manage'))
<a href="{{ url('/settings/users') }}" @if($selected == 'users') class="active" @endif>@icon('users'){{ trans('settings.users') }}</a>
@endif