mirror of
https://github.com/Kovah/LinkAce.git
synced 2025-01-17 13:18:21 +01:00
Add audit logs for settings (#467)
This commit is contained in:
parent
18089253fe
commit
d67a04ebee
15
app/Audits/Modifiers/DarkmodeSettingModifier.php
Normal file
15
app/Audits/Modifiers/DarkmodeSettingModifier.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Audits\Modifiers;
|
||||||
|
|
||||||
|
class DarkmodeSettingModifier implements ModifierInterface
|
||||||
|
{
|
||||||
|
public function modify($value): string
|
||||||
|
{
|
||||||
|
return match ((int)$value) {
|
||||||
|
0 => trans('settings.darkmode_disabled'),
|
||||||
|
1 => trans('settings.darkmode_permanent'),
|
||||||
|
2 => trans('settings.darkmode_auto'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
16
app/Audits/Modifiers/DisplayModeSettingModifier.php
Normal file
16
app/Audits/Modifiers/DisplayModeSettingModifier.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Audits\Modifiers;
|
||||||
|
|
||||||
|
class DisplayModeSettingModifier implements ModifierInterface
|
||||||
|
{
|
||||||
|
public function modify($value): string
|
||||||
|
{
|
||||||
|
return match ((int)$value) {
|
||||||
|
0 => trans('settings.display_mode_list_detailed'),
|
||||||
|
1 => trans('settings.display_mode_cards'),
|
||||||
|
2 => trans('settings.display_mode_list_simple'),
|
||||||
|
3 => trans('settings.display_mode_cards_detailed'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
11
app/Audits/Modifiers/LocaleSettingModifier.php
Normal file
11
app/Audits/Modifiers/LocaleSettingModifier.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Audits\Modifiers;
|
||||||
|
|
||||||
|
class LocaleSettingModifier implements ModifierInterface
|
||||||
|
{
|
||||||
|
public function modify($value): string
|
||||||
|
{
|
||||||
|
return config('app.available_locales.' . $value);
|
||||||
|
}
|
||||||
|
}
|
20
app/Http/Controllers/App/AuditController.php
Normal file
20
app/Http/Controllers/App/AuditController.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\App;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Setting;
|
||||||
|
use OwenIt\Auditing\Models\Audit;
|
||||||
|
|
||||||
|
class AuditController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
$settingsHistory = Audit::where('auditable_type', Setting::class)->with('auditable')
|
||||||
|
->latest()->paginate(pageName: 'settings');
|
||||||
|
|
||||||
|
return view('app.audit-logs', [
|
||||||
|
'settings_history' => $settingsHistory,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -52,20 +52,21 @@ class SystemSettingsController extends Controller
|
|||||||
|
|
||||||
if ($guestSharingSettings) {
|
if ($guestSharingSettings) {
|
||||||
foreach (config('sharing.services') as $service => $details) {
|
foreach (config('sharing.services') as $service => $details) {
|
||||||
$toggle = array_key_exists($service, $guestSharingSettings);
|
$toggle = (int)array_key_exists($service, $guestSharingSettings);
|
||||||
|
|
||||||
Setting::updateOrCreate([
|
Setting::updateOrCreate([
|
||||||
'user_id' => null,
|
'user_id' => null,
|
||||||
'key' => 'guest_share_' . $service,
|
'key' => 'guest_share_' . $service,
|
||||||
], [
|
], [
|
||||||
'key' => 'guest_share_' . $service,
|
'key' => 'guest_share_' . $service,
|
||||||
'value' => $toggle,
|
'value' => (string)$toggle,
|
||||||
'user_id' => null,
|
'user_id' => null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Cache::forget('systemsettings');
|
Cache::forget('systemsettings');
|
||||||
|
Cache::forget('settings_keys');
|
||||||
|
|
||||||
flash(trans('settings.settings_saved'));
|
flash(trans('settings.settings_saved'));
|
||||||
return redirect()->route('get-systemsettings');
|
return redirect()->route('get-systemsettings');
|
||||||
|
@ -12,6 +12,7 @@ use Illuminate\Contracts\View\View;
|
|||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
@ -73,16 +74,18 @@ class UserSettingsController extends Controller
|
|||||||
$userServices = $userServices['share'] ?? [];
|
$userServices = $userServices['share'] ?? [];
|
||||||
|
|
||||||
foreach (config('sharing.services') as $service => $details) {
|
foreach (config('sharing.services') as $service => $details) {
|
||||||
$toggle = array_key_exists($service, $userServices);
|
$toggle = (int)array_key_exists($service, $userServices);
|
||||||
|
|
||||||
Setting::updateOrCreate([
|
Setting::updateOrCreate([
|
||||||
'user_id' => $userId,
|
'user_id' => $userId,
|
||||||
'key' => 'share_' . $service,
|
'key' => 'share_' . $service,
|
||||||
], [
|
], [
|
||||||
'value' => $toggle,
|
'value' => (string)$toggle,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cache::forget('settings_keys');
|
||||||
|
|
||||||
flash(trans('settings.settings_saved'), 'success');
|
flash(trans('settings.settings_saved'), 'success');
|
||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Audits\Modifiers\BooleanModifier;
|
||||||
|
use App\Audits\Modifiers\DarkmodeSettingModifier;
|
||||||
|
use App\Audits\Modifiers\DisplayModeSettingModifier;
|
||||||
|
use App\Audits\Modifiers\LocaleSettingModifier;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use OwenIt\Auditing\Auditable as AuditableTrait;
|
||||||
|
use OwenIt\Auditing\Contracts\Auditable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Setting
|
* Class Setting
|
||||||
@ -16,8 +24,10 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
* @method static Builder|Setting byUser($user_id)
|
* @method static Builder|Setting byUser($user_id)
|
||||||
* @method static Builder|Setting systemOnly()
|
* @method static Builder|Setting systemOnly()
|
||||||
*/
|
*/
|
||||||
class Setting extends Model
|
class Setting extends Model implements Auditable
|
||||||
{
|
{
|
||||||
|
use AuditableTrait;
|
||||||
|
|
||||||
public $timestamps = false;
|
public $timestamps = false;
|
||||||
|
|
||||||
public $fillable = [
|
public $fillable = [
|
||||||
@ -31,8 +41,53 @@ class Setting extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
| ========================================================================
|
* ========================================================================
|
||||||
| SCOPES
|
* AUDIT SETTINGS
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected $auditEvents = [
|
||||||
|
'updated',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static array $auditModifiers = [
|
||||||
|
'archive_backups_enabled' => BooleanModifier::class,
|
||||||
|
'archive_private_backups_enabled' => BooleanModifier::class,
|
||||||
|
'darkmode_setting' => DarkmodeSettingModifier::class,
|
||||||
|
'link_display_mode' => DisplayModeSettingModifier::class,
|
||||||
|
'links_new_tab' => BooleanModifier::class,
|
||||||
|
'links_private_default' => BooleanModifier::class,
|
||||||
|
'lists_private_default' => BooleanModifier::class,
|
||||||
|
'locale' => LocaleSettingModifier::class,
|
||||||
|
'markdown_for_text' => BooleanModifier::class,
|
||||||
|
'notes_private_default' => BooleanModifier::class,
|
||||||
|
'private_default' => BooleanModifier::class,
|
||||||
|
'share_service' => BooleanModifier::class,
|
||||||
|
'system_guest_access' => BooleanModifier::class,
|
||||||
|
'tags_private_default' => BooleanModifier::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instead of having 'value' as the changed field, use the actual settings
|
||||||
|
* key as the changed field.
|
||||||
|
*
|
||||||
|
* @param array $data
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function transformAudit(array $data): array
|
||||||
|
{
|
||||||
|
$keys = self::getSettingKeys();
|
||||||
|
$key = $keys[$data['auditable_id']];
|
||||||
|
|
||||||
|
$data['old_values'][$key] = $data['old_values']['value'];
|
||||||
|
$data['new_values'][$key] = $data['new_values']['value'];
|
||||||
|
unset($data['old_values']['value'], $data['new_values']['value']);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ========================================================================
|
||||||
|
* SCOPES
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,4 +112,24 @@ class Setting extends Model
|
|||||||
{
|
{
|
||||||
return $query->whereNull('user_id');
|
return $query->whereNull('user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ========================================================================
|
||||||
|
* RELATIONSHIPS
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ========================================================================
|
||||||
|
* METHODS
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static function getSettingKeys()
|
||||||
|
{
|
||||||
|
return Cache::rememberForever('settings_keys', fn() => Setting::get()->pluck('key', 'id'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\View\Components\History;
|
namespace App\View\Components\History;
|
||||||
|
|
||||||
use App\Models\Link;
|
use App\Models\LinkList;
|
||||||
use Illuminate\View\Component;
|
use Illuminate\View\Component;
|
||||||
use OwenIt\Auditing\Models\Audit;
|
use OwenIt\Auditing\Models\Audit;
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ class ListEntry extends Component
|
|||||||
$oldValue = $changeData['old'] ?? null;
|
$oldValue = $changeData['old'] ?? null;
|
||||||
$newValue = $changeData['new'] ?? null;
|
$newValue = $changeData['new'] ?? null;
|
||||||
|
|
||||||
/** @var Link $model */
|
/** @var LinkList $model */
|
||||||
$model = app($this->entry->auditable_type);
|
$model = app($this->entry->auditable_type);
|
||||||
|
|
||||||
if (isset($model->auditModifiers[$field])) {
|
if (isset($model->auditModifiers[$field])) {
|
||||||
|
98
app/View/Components/History/SettingsEntry.php
Normal file
98
app/View/Components/History/SettingsEntry.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\View\Components\History;
|
||||||
|
|
||||||
|
use App\Models\Setting;
|
||||||
|
use Illuminate\View\Component;
|
||||||
|
use OwenIt\Auditing\Models\Audit;
|
||||||
|
|
||||||
|
class SettingsEntry extends Component
|
||||||
|
{
|
||||||
|
public function __construct(private Audit $entry, private array $changes = [])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
$timestamp = formatDateTime($this->entry->created_at);
|
||||||
|
|
||||||
|
foreach ($this->entry->getModified() as $field => $change) {
|
||||||
|
$this->processChange($field, $change);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('components.history-entry', [
|
||||||
|
'timestamp' => $timestamp,
|
||||||
|
'changes' => $this->changes,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function processChange(string $field, array $changeData): void
|
||||||
|
{
|
||||||
|
$fieldName = $this->processFieldName($field);
|
||||||
|
[$oldValue, $newValue] = $this->processValues($field, $changeData);
|
||||||
|
|
||||||
|
$change = trans('linkace.history_changed', [
|
||||||
|
'fieldname' => $fieldName,
|
||||||
|
'oldvalue' => htmlspecialchars($oldValue),
|
||||||
|
'newvalue' => htmlspecialchars($newValue),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->changes[] = $change;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the field name appearance to make sure it is properly displayed
|
||||||
|
* in the audit log.
|
||||||
|
* All guest settings will get the 'guest_' prefix removed and prepend
|
||||||
|
* 'Guest Setting:' to the field name.
|
||||||
|
* If the setting of a user was changed, append this info to the field.
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function processFieldName(string $field)
|
||||||
|
{
|
||||||
|
if (str_starts_with($field, 'guest_')) {
|
||||||
|
$field = str_replace('guest_', '', $field);
|
||||||
|
$prepend = trans('settings.guest_settings') . ': ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($field, 'share_')) {
|
||||||
|
$service = str_replace('share_', '', $field);
|
||||||
|
return trans('settings.sharing') . ': ' . trans('sharing.service.' . $service);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->entry->auditable->user_id !== null) {
|
||||||
|
$append = sprintf(' %s %s', trans('user.for_user'), $this->entry->auditable->user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($prepend ?? '') . trans('settings.' . $field) . ($append ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply specialized methods for different fields to handle particular
|
||||||
|
* formatting needs of these fields.
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
* @param array $changeData
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function processValues(string $field, array $changeData): array
|
||||||
|
{
|
||||||
|
$oldValue = $changeData['old'] ?? null;
|
||||||
|
$newValue = $changeData['new'] ?? null;
|
||||||
|
|
||||||
|
if (str_contains($field, 'guest_share_') || str_contains($field, 'share_')) {
|
||||||
|
$field = 'share_service';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset(Setting::$auditModifiers[$field])) {
|
||||||
|
$modifier = app(Setting::$auditModifiers[$field]);
|
||||||
|
$oldValue = $modifier->modify($oldValue);
|
||||||
|
$newValue = $modifier->modify($newValue);
|
||||||
|
return [$oldValue, $newValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$oldValue, $newValue];
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\View\Components\History;
|
namespace App\View\Components\History;
|
||||||
|
|
||||||
use App\Models\Link;
|
use App\Models\Tag;
|
||||||
use Illuminate\View\Component;
|
use Illuminate\View\Component;
|
||||||
use OwenIt\Auditing\Models\Audit;
|
use OwenIt\Auditing\Models\Audit;
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ class TagEntry extends Component
|
|||||||
$oldValue = $changeData['old'] ?? null;
|
$oldValue = $changeData['old'] ?? null;
|
||||||
$newValue = $changeData['new'] ?? null;
|
$newValue = $changeData['new'] ?? null;
|
||||||
|
|
||||||
/** @var Link $model */
|
/** @var Tag $model */
|
||||||
$model = app($this->entry->auditable_type);
|
$model = app($this->entry->auditable_type);
|
||||||
|
|
||||||
if (isset($model->auditModifiers[$field])) {
|
if (isset($model->auditModifiers[$field])) {
|
||||||
|
8
lang/en_US/audit.php
Normal file
8
lang/en_US/audit.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'log' => 'Audit Log',
|
||||||
|
'settings_history' => 'Settings History',
|
||||||
|
|
||||||
|
];
|
@ -7,7 +7,7 @@ return [
|
|||||||
'system_settings' => 'System Settings',
|
'system_settings' => 'System Settings',
|
||||||
'guest_settings' => 'Guest Settings',
|
'guest_settings' => 'Guest Settings',
|
||||||
|
|
||||||
'language' => 'Language',
|
'locale' => 'Language',
|
||||||
'timezone' => 'Timezone',
|
'timezone' => 'Timezone',
|
||||||
'date_format' => 'Date Format',
|
'date_format' => 'Date Format',
|
||||||
'time_format' => 'Time Format',
|
'time_format' => 'Time Format',
|
||||||
@ -34,6 +34,7 @@ return [
|
|||||||
'archive_private_backups_enabled' => 'Enable backups for private links',
|
'archive_private_backups_enabled' => 'Enable backups for private links',
|
||||||
'archive_private_backups_enabled_help' => 'If enabled, private links will also be saved. Backups must be enabled.',
|
'archive_private_backups_enabled_help' => 'If enabled, private links will also be saved. Backups must be enabled.',
|
||||||
|
|
||||||
|
'link_display_mode' => 'Link Display Mode',
|
||||||
'display_mode' => 'Display links as',
|
'display_mode' => 'Display links as',
|
||||||
'display_mode_list_detailed' => 'list with many details',
|
'display_mode_list_detailed' => 'list with many details',
|
||||||
'display_mode_list_simple' => 'list with less details',
|
'display_mode_list_simple' => 'list with less details',
|
||||||
@ -41,10 +42,11 @@ return [
|
|||||||
'display_mode_cards_detailed' => 'cards with many details',
|
'display_mode_cards_detailed' => 'cards with many details',
|
||||||
|
|
||||||
'sharing' => 'Link Sharing',
|
'sharing' => 'Link Sharing',
|
||||||
|
'guest_sharing' => 'Guest Link Sharing',
|
||||||
'sharing_help' => 'Enable all services you want to display for links, to be able to share them easily with one click.',
|
'sharing_help' => 'Enable all services you want to display for links, to be able to share them easily with one click.',
|
||||||
'sharing_toggle' => 'Toggle all on/off',
|
'sharing_toggle' => 'Toggle all on/off',
|
||||||
|
|
||||||
'darkmode' => 'Darkmode',
|
'darkmode_setting' => 'Darkmode',
|
||||||
'darkmode_help' => 'You can either choose to turn on permanently or automatically based on your device settings. (<small>Check <a href="https://caniuse.com/#search=prefers-color-scheme">here</a> if your browser supports automatic detection</small>)',
|
'darkmode_help' => 'You can either choose to turn on permanently or automatically based on your device settings. (<small>Check <a href="https://caniuse.com/#search=prefers-color-scheme">here</a> if your browser supports automatic detection</small>)',
|
||||||
'darkmode_disabled' => 'Disabled',
|
'darkmode_disabled' => 'Disabled',
|
||||||
'darkmode_auto' => 'Automatically',
|
'darkmode_auto' => 'Automatically',
|
||||||
|
@ -7,4 +7,5 @@ return [
|
|||||||
'email' => 'Email',
|
'email' => 'Email',
|
||||||
|
|
||||||
'hello' => 'Hello :user!',
|
'hello' => 'Hello :user!',
|
||||||
|
'for_user' => 'for User',
|
||||||
];
|
];
|
||||||
|
22
resources/views/app/audit-logs.blade.php
Normal file
22
resources/views/app/audit-logs.blade.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
@lang('audit.settings_history')
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="history mb-6">
|
||||||
|
@foreach($settings_history as $entry)
|
||||||
|
<x-history.settings-entry :entry="$entry"/>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!! $settings_history->onEachSide(1)->links() !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
@ -1,7 +1,7 @@
|
|||||||
<div class="mb-3 my-5">
|
<div class="mb-3 my-5">
|
||||||
|
|
||||||
<h5>
|
<h5>
|
||||||
@lang('settings.darkmode')
|
@lang('settings.darkmode_setting')
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<p class="my-3 small">@lang('settings.darkmode_help')</p>
|
<p class="my-3 small">@lang('settings.darkmode_help')</p>
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label" for="locale">
|
<label class="form-label" for="locale">
|
||||||
@lang('settings.language')
|
@lang('settings.locale')
|
||||||
</label>
|
</label>
|
||||||
<select id="locale" name="locale"
|
<select id="locale" name="locale"
|
||||||
class="simple-select {{ $errors->has('locale') ? ' is-invalid' : '' }}">
|
class="simple-select {{ $errors->has('locale') ? ' is-invalid' : '' }}">
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</option>
|
</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
@if ($errors->has('locale'))
|
@if($errors->has('locale'))
|
||||||
<p class="invalid-feedback" role="alert">
|
<p class="invalid-feedback" role="alert">
|
||||||
{{ $errors->first('locale') }}
|
{{ $errors->first('locale') }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<div class="mb-3 my-5">
|
<div class="mb-3 my-5">
|
||||||
|
|
||||||
<h5>
|
<h5>
|
||||||
@lang('settings.darkmode')
|
@lang('settings.darkmode_setting')
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<p class="my-3 small">@lang('settings.darkmode_help')</p>
|
<p class="my-3 small">@lang('settings.darkmode_help')</p>
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
<a href="{{ route('get-systemsettings') }}" class="dropdown-item">
|
<a href="{{ route('get-systemsettings') }}" class="dropdown-item">
|
||||||
@lang('settings.system_settings')
|
@lang('settings.system_settings')
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ route('system-audit') }}" class="dropdown-item">
|
||||||
|
@lang('audit.log')
|
||||||
|
</a>
|
||||||
<a href="{{ route('system-logs') }}" class="dropdown-item">
|
<a href="{{ route('system-logs') }}" class="dropdown-item">
|
||||||
@lang('linkace.system_logs')
|
@lang('linkace.system_logs')
|
||||||
</a>
|
</a>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\App\AuditController;
|
||||||
use App\Http\Controllers\App\BookmarkletController;
|
use App\Http\Controllers\App\BookmarkletController;
|
||||||
use App\Http\Controllers\App\DashboardController;
|
use App\Http\Controllers\App\DashboardController;
|
||||||
use App\Http\Controllers\App\ExportController;
|
use App\Http\Controllers\App\ExportController;
|
||||||
@ -136,6 +137,8 @@ Route::group(['middleware' => ['auth']], function () {
|
|||||||
|
|
||||||
Route::get('system/logs', [LogViewerController::class, 'index'])
|
Route::get('system/logs', [LogViewerController::class, 'index'])
|
||||||
->name('system-logs');
|
->name('system-logs');
|
||||||
|
|
||||||
|
Route::get('system/audit', AuditController::class)->name('system-audit');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Guest access routes
|
// Guest access routes
|
||||||
|
140
tests/Components/History/SettingsEntryTest.php
Normal file
140
tests/Components/History/SettingsEntryTest.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Components\History;
|
||||||
|
|
||||||
|
use App\Models\Setting;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\View\Components\History\SettingsEntry;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use OwenIt\Auditing\Models\Audit;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class SettingsEntryTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$user = User::factory()->create(['name' => 'TestUser']);
|
||||||
|
$this->actingAs($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStringSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'timezone', 'value' => 'Europe/Berlin']);
|
||||||
|
$setting->update(['value' => 'UTC']);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Timezone from <code>Europe/Berlin</code> to <code>UTC</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBooleanSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'archive_backups_enabled', 'value' => true]);
|
||||||
|
$setting->update(['value' => false]);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString('Changed Enable backups from <code>Yes</code> to <code>No</code>', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDarkmodeSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'darkmode_setting', 'value' => 1]);
|
||||||
|
$setting->update(['value' => 2]);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Darkmode from <code>Permanent</code> to <code>Automatically</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDisplayModeSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'link_display_mode', 'value' => 1]);
|
||||||
|
$setting->update(['value' => 2]);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Link Display Mode from <code>cards with less details</code> to <code>list with less details</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testLocaleSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'locale', 'value' => 'en_US']);
|
||||||
|
$setting->update(['value' => 'de_DE']);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Language from <code>English</code> to <code>Deutsch</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSharingSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'share_email', 'value' => true]);
|
||||||
|
$setting->update(['value' => false]);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Link Sharing: Email from <code>Yes</code> to <code>No</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGuestSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'guest_listitem_count', 'value' => 24]);
|
||||||
|
$setting->update(['value' => 60]);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Guest Settings: Number of Items in Lists from <code>24</code> to <code>60</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUserSettingsChange(): void
|
||||||
|
{
|
||||||
|
$setting = Setting::create(['key' => 'locale', 'value' => 'en_US', 'user_id' => 1]);
|
||||||
|
$setting->update(['value' => 'de_DE']);
|
||||||
|
|
||||||
|
$historyEntry = Audit::where('auditable_type', Setting::class)->with('auditable')->latest()->first();
|
||||||
|
|
||||||
|
$output = (new SettingsEntry($historyEntry))->render();
|
||||||
|
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
'Changed Language for User 1 from <code>English</code> to <code>Deutsch</code>',
|
||||||
|
$output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user