vendor and env first commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
/.idea
|
||||
/vendor
|
||||
composer.lock
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "vanguardapp/activity-log",
|
||||
"description": "User activity log plugin for Vanguard.",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"vanguard",
|
||||
"activity log",
|
||||
"user activity"
|
||||
],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Milos Stojanovic",
|
||||
"email": "stojanovic.loshmi@gmail.com",
|
||||
"homepage": "https://mstojanovic.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0.2|^8.1|^8.2",
|
||||
"illuminate/http": "^10.0|^11.0",
|
||||
"vanguardapp/plugins": "^6.0",
|
||||
"spatie/laravel-query-builder": "^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Vanguard\\UserActivity\\": "src/",
|
||||
"Vanguard\\UserActivity\\Database\\Factories\\": "database/factories/",
|
||||
"Vanguard\\UserActivity\\Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Vanguard\\UserActivity\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
|
||||
class ActivityFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Activity::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'user_id' => User::factory(),
|
||||
'description' => substr($this->faker->paragraph, 0, 255),
|
||||
'ip_address' => $this->faker->ipv4,
|
||||
'user_agent' => $this->faker->userAgent,
|
||||
];
|
||||
}
|
||||
}
|
||||
vendor/vanguardapp/activity-log/database/migrations/2015_12_29_224252_create_user_activity_table.php
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class CreateUserActivityTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('user_activity', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->text('description');
|
||||
$table->unsignedInteger('user_id');
|
||||
$table->string('ip_address', 45);
|
||||
$table->text('user_agent');
|
||||
$table->timestamp('created_at');
|
||||
});
|
||||
|
||||
Schema::table('user_activity', function (Blueprint $table) {
|
||||
$table->foreign('user_id')
|
||||
->references('id')
|
||||
->on('users')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
if (DB::getDriverName() != 'sqlite') {
|
||||
Schema::table('user_activity', function (Blueprint $table) {
|
||||
$table->dropForeign('user_activity_user_id_foreign');
|
||||
});
|
||||
}
|
||||
|
||||
Schema::drop('user_activity');
|
||||
|
||||
\DB::table('permissions')->where('name', 'users.activity')->delete();
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Vanguard\Permission;
|
||||
use Vanguard\Role;
|
||||
|
||||
class ActivityPermissionsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$adminRole = Role::where('name', 'Admin')->first();
|
||||
|
||||
$permission = Permission::create([
|
||||
'name' => 'users.activity',
|
||||
'display_name' => 'View System Activity Log',
|
||||
'description' => 'View activity log for all system users.',
|
||||
'removable' => false,
|
||||
]);
|
||||
|
||||
$adminRole->attachPermission($permission);
|
||||
}
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
User Activity Log plugin for [Vanguard - Advanced PHP Login and User Management](https://vanguardapp.io)
|
||||
system.
|
||||
|
||||
This plugin was originally part of the Vanguard itself, but it has been extracted as a separate plugin starting from Vanguard 4.
|
||||
|
||||
## Installation
|
||||
|
||||
This plugin requires Vanguard `5.0.0` or greater.
|
||||
|
||||
### Installation via Composer
|
||||
|
||||
To install the plugin first you will need to pull it via composer
|
||||
by running the following command
|
||||
|
||||
```
|
||||
composer require vanguardapp/activity-log
|
||||
```
|
||||
|
||||
The composer will install the plugin for you as well as it's dependencies.
|
||||
|
||||
The next step is to register the plugin by adding the
|
||||
`\Vanguard\UserActivity\UserActivity::class`
|
||||
to the list of Vanguard plugins inside the `VanguardServiceProvider`:
|
||||
|
||||
```php
|
||||
protected function plugins()
|
||||
{
|
||||
return [
|
||||
//...
|
||||
\Vanguard\UserActivity\UserActivity::class,
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
As soon as your plugin is registered, you should publish the
|
||||
plugins migrations by running the following command:
|
||||
|
||||
```
|
||||
php artisan vendor:publish --provider="Vanguard\UserActivity\UserActivity" --tag="migrations"
|
||||
```
|
||||
|
||||
And, as the last step of the installation, you will need to
|
||||
run the following commands to make all the necessary database modifications:
|
||||
|
||||
```
|
||||
php artisan migrate
|
||||
php artisan db:seed --class="ActivityPermissionsSeeder"
|
||||
```
|
||||
|
||||
At this point the plugin will be fully installed and ready to go.
|
||||
It is configured to listen for most of the events that are coming from
|
||||
Vanguard and to put the into the activity log.
|
||||
|
||||
### Manual Installation
|
||||
|
||||
If you plan to make the modifications to the plugin and customize it to
|
||||
fit your needs, it's much easier if you add it to your project manually.
|
||||
|
||||
To do so, you will need to download the ZIP archive from GitHub
|
||||
by clicking the green "Clone or download" button and then choosing
|
||||
the "Download ZIP" option from the dropdown.
|
||||
|
||||
Once you have the ZIP file on your computer, extract it to the
|
||||
`plugins/ActivityLog` folder (you will need to create this folder
|
||||
since it probably won't be present in your Vanguard installation).
|
||||
|
||||
Next step is to update your main `composer.json` file located in
|
||||
Vanguard's root directory and add the following object to the `repositories`
|
||||
array:
|
||||
|
||||
```
|
||||
{
|
||||
"type": "path",
|
||||
"url": "./plugins/ActivityLog"
|
||||
}
|
||||
```
|
||||
|
||||
This will tell the composer that your plugin is located in `/plugins/ActivityLog`
|
||||
directory and that it should be installed from there.
|
||||
|
||||
Now, add the following to the composer's `require` section
|
||||
|
||||
```
|
||||
"vanguardapp/activity-log": "*"
|
||||
```
|
||||
|
||||
And run `composer update`.
|
||||
|
||||
Composer will now install the plugin from your local directory instead
|
||||
of pulling it from GitHub, which means that you will be able to make
|
||||
the changes to the plugin itself and customize it to fit your needs.
|
||||
|
||||
The rest of the process is the same as when the plugin is installed
|
||||
by directly fetching it via composer from the GitHub repository, so you
|
||||
will need to do all the same steps as above, which in short involves
|
||||
updating the `VanguardServiceProvider` and running the commands to
|
||||
publish plugin's static assets and to update the database.
|
||||
|
||||
## Dashboard Widgets
|
||||
|
||||
A plugin provides user activity dashboard widget that is visible for all users with a role `User`.
|
||||
|
||||
To activate the widget add the `Vanguard\UserActivity\Widgets\ActivityWidget::class` to the widgets array in `VanguardServiceProvider`:
|
||||
|
||||
```php
|
||||
protected function widgets()
|
||||
{
|
||||
return [
|
||||
//...
|
||||
\Vanguard\UserActivity\Widgets\ActivityWidget::class,
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This plugin is an open-source software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"Activity Log": "Aktivitäts-Log",
|
||||
"Search for Action": "Nach einer Aktion suchen...",
|
||||
"User": "Benutzer",
|
||||
"Message": "Nachricht",
|
||||
"Log Time": "Log-Zeitpunkt",
|
||||
"More Info": "Mehr Infos",
|
||||
"View Activity Log": "Aktivitätslog ansehen",
|
||||
"No activity from this user yet.": "Keine Aktivität von diesem Benutzer noch nicht.",
|
||||
"Action": "Aktion",
|
||||
"Date": "Datum",
|
||||
"Latest Activity": "Letzte Aktivitäten",
|
||||
"Complete Activity Log": "Vollständiges Aktivitäts-Log",
|
||||
"View All": "Alle ansehen"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'new_permission' => 'Neue Berechtigung mit dem Namen :name wurde erstellt.',
|
||||
'updated_permission' => 'Berechtigung mit dem Namen :name wurde aktualisiert.',
|
||||
'deleted_permission' => 'Berechtigung mit dem Namen :name wurde gelöscht.',
|
||||
|
||||
'new_role' => 'Neue Benutzerrolle mit dem Namen :name wurde erstellt.',
|
||||
'updated_role' => 'Benutzerrolle mit dem Namen :name wurde aktualisiert.',
|
||||
'deleted_role' => 'Benutzerrolle mit dem Namen :name wurde gelöscht.',
|
||||
'updated_role_permissions' => 'Rollen Benutzerberechtigungen wurden aktualisiert.',
|
||||
|
||||
'logged_in' => 'Angemeldet.',
|
||||
'logged_out' => 'Abgemeldet.',
|
||||
'created_account' => 'Ein Benutzerkonto wurde erstellt.',
|
||||
'updated_avatar' => 'Der Profil-Avatar wurde aktualisiert.',
|
||||
'updated_profile' => 'Die Profil-Details wurden aktualisiert.',
|
||||
'deleted_user' => 'Benutzer :name wurde gelöscht.',
|
||||
'banned_user' => 'Benutzer :name wurde gesperrt.',
|
||||
'updated_profile_details_for' => 'Profildetails für Benutzer :name wurden aktualisiert.',
|
||||
'created_account_for' => 'Ein Benutzerkonto für Benutzer :name wurde erstellt.',
|
||||
'updated_settings' => 'WebSeiten Einstellungen wurden aktualisiert.',
|
||||
'enabled_2fa' => 'Zwei-Faktor Authentifizierung wurde aktiviert.',
|
||||
'disabled_2fa' => 'Zwei-Faktor Authentifizierung wurde deaktiviert.',
|
||||
'enabled_2fa_for' => 'Zwei-Faktor Authentifizierung für Benutzer :name wurde aktiviert.',
|
||||
'disabled_2fa_for' => 'Zwei-Faktor Authentifizierung für Benutzer :name wurde deaktiviert.',
|
||||
'requested_password_reset' => 'Mail zum Zurücksetzen des Passworts wurde angefordert.',
|
||||
'reseted_password' => 'Das Passwort wurde mit Hilfe der Option "Passwort vergessen" zurückgesetzt.',
|
||||
|
||||
'started_impersonating' => 'Gestartet Identitätswechsel Benutzer :name (ID: :id)',
|
||||
'stopped_impersonating' => 'Gestoppt Identitätswechsel Benutzer :name (ID: :id)',
|
||||
];
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"Activity Log": "",
|
||||
"Search for Action": "",
|
||||
"User": "",
|
||||
"Message": "",
|
||||
"Log Time": "",
|
||||
"More Info": "",
|
||||
"View Activity Log": "",
|
||||
"No activity from this user yet.": "",
|
||||
"Action": "",
|
||||
"Date": "",
|
||||
"Latest Activity": "",
|
||||
"Complete Activity Log": "",
|
||||
"View All": ""
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'new_permission' => 'Created new permission called :name.',
|
||||
'updated_permission' => 'Updated the permission named :name.',
|
||||
'deleted_permission' => 'Deleted permission named :name.',
|
||||
|
||||
'new_role' => 'Created new role called :name.',
|
||||
'updated_role' => 'Updated role with name :name.',
|
||||
'deleted_role' => 'Deleted role named :name.',
|
||||
'updated_role_permissions' => 'Updated role permissions.',
|
||||
|
||||
'logged_in' => 'Logged in.',
|
||||
'logged_out' => 'Logged out.',
|
||||
'created_account' => 'Created an account.',
|
||||
'updated_avatar' => 'Updated profile avatar.',
|
||||
'updated_profile' => 'Updated profile details.',
|
||||
'deleted_user' => 'Deleted user :name.',
|
||||
'banned_user' => 'Banned user :name.',
|
||||
'updated_profile_details_for' => 'Updated profile details for :name.',
|
||||
'created_account_for' => 'Created an account for user :name.',
|
||||
'updated_settings' => 'Updated website settings.',
|
||||
'enabled_2fa' => 'Enabled Two-Factor Authentication.',
|
||||
'disabled_2fa' => 'Disabled Two-Factor Authentication.',
|
||||
'enabled_2fa_for' => 'Enabled Two-Factor Authentication for user :name.',
|
||||
'disabled_2fa_for' => 'Disabled Two-Factor Authentication for user :name.',
|
||||
'requested_password_reset' => 'Requested password reset email.',
|
||||
'reseted_password' => 'Reseted password using "Forgot Password" option.',
|
||||
|
||||
'started_impersonating' => 'Started impersonating user :name (ID: :id)',
|
||||
'stopped_impersonating' => 'Stopped impersonating user :name (ID: :id)',
|
||||
];
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"Activity Log": "Aktivnost",
|
||||
"Search for Action": "Pretraži akcije",
|
||||
"User": "Korisnik",
|
||||
"Message": "Poruka",
|
||||
"Log Time": "Vreme kreiranja",
|
||||
"More Info": "Više informacija",
|
||||
"View Activity Log": "Pregled aktivnosti",
|
||||
"No activity from this user yet.": "Još uvek nema aktivnosti od strane ovog korisnika.",
|
||||
"Action": "Akcija",
|
||||
"Date": "Datum",
|
||||
"Latest Activity": "Poslednja aktivnost",
|
||||
"Complete Activity Log": "Kompletna aktivnost",
|
||||
"View All": "Vidi sve"
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'new_permission' => 'Kreirao je novu dozvolu pod nazivom :name.',
|
||||
'updated_permission' => 'Ažurirao je dozvolu pod nazivom :name.',
|
||||
'deleted_permission' => 'Obrisao je dozvolu pod nazivom :name.',
|
||||
|
||||
'new_role' => 'Kreirao je novu ulogu pod nazivom :name.',
|
||||
'updated_role' => 'Ažurirao je ulogu pod nazivom :name.',
|
||||
'deleted_role' => 'Obrisao je ulogu pod nazivom :name.',
|
||||
'updated_role_permissions' => 'Ažurirao je dozvole svih uloga.',
|
||||
|
||||
'logged_in' => 'Prijavio se.',
|
||||
'logged_out' => 'Odjavio se.',
|
||||
'created_account' => 'Napravio je nalog.',
|
||||
'updated_avatar' => 'Ažurirao je profilnu sliku.',
|
||||
'updated_profile' => 'Ažurirao je informacije na profilu.',
|
||||
'deleted_user' => 'Obrisao je korisnika :name.',
|
||||
'banned_user' => 'Blokirao je korisnika :name.',
|
||||
'updated_profile_details_for' => 'Ažurirao je informacije o profilu korisnika :name.',
|
||||
'created_account_for' => 'Obrisao je nalog korisnika :name.',
|
||||
'updated_settings' => 'Ažurirao je podešavanja aplkacije.',
|
||||
'enabled_2fa' => 'Aktivirao je Two-Factor autentifikaciju.',
|
||||
'disabled_2fa' => 'Deaktivirao je Two-Factor autentifikaciju.',
|
||||
'enabled_2fa_for' => 'Aktivirao je Two-Factor autentifikaciju za korisnika :name.',
|
||||
'disabled_2fa_for' => 'Disabled Two-Factor autentifikaciju za korisnika :name.',
|
||||
'requested_password_reset' => 'Zatražio je e-mail za obnavljanje lozinke.',
|
||||
'reseted_password' => 'Obnovio je lozinku korišćenjem opcije "Zaboravljena lozinka".',
|
||||
|
||||
'started_impersonating' => 'Započeo je lažno predstavljanje korisnika :name (ID: :id)',
|
||||
'stopped_impersonating' => 'Završio je sa lažnim predstavljanjem korisnika :name (ID: :id)',
|
||||
];
|
||||
@@ -0,0 +1,100 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('page-title', __('Activity Log'))
|
||||
@section('page-heading', isset($user) ? $user->present()->nameOrEmail : __('Activity Log'))
|
||||
|
||||
@section('breadcrumbs')
|
||||
@if (isset($user) && isset($adminView))
|
||||
<li class="breadcrumb-item">
|
||||
<a href="{{ route('activity.index') }}">@lang('Activity Log')</a>
|
||||
</li>
|
||||
<li class="breadcrumb-item active">
|
||||
{{ $user->present()->nameOrEmail }}
|
||||
</li>
|
||||
@else
|
||||
<li class="breadcrumb-item active">
|
||||
@lang('Activity Log')
|
||||
</li>
|
||||
@endif
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form action="" method="GET" id="users-form" class="border-bottom-light mb-3">
|
||||
<div class="row justify-content-between mt-3 mb-4">
|
||||
<div class="col-lg-5 col-md-6">
|
||||
<div class="input-group custom-search-form">
|
||||
<input type="text"
|
||||
class="form-control input-solid"
|
||||
name="search"
|
||||
value="{{ Request::get('search') }}"
|
||||
placeholder="@lang('Search for Action')">
|
||||
|
||||
<span class="input-group-append">
|
||||
@if (Request::has('search') && Request::get('search') != '')
|
||||
<a href="{{ isset($adminView) ? route('activity.index') : route('profile.activity') }}"
|
||||
class="btn btn-light d-flex align-items-center"
|
||||
role="button">
|
||||
<i class="fas fa-times text-muted"></i>
|
||||
</a>
|
||||
@endif
|
||||
<button class="btn btn-light" type="submit" id="search-activities-btn">
|
||||
<i class="fas fa-search text-muted"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-borderless table-striped">
|
||||
<thead>
|
||||
@if (isset($adminView))
|
||||
<th class="min-width-150">@lang('User')</th>
|
||||
@endif
|
||||
<th>@lang('IP Address')</th>
|
||||
<th class="min-width-200">@lang('Message')</th>
|
||||
<th class="min-width-200">@lang('Log Time')</th>
|
||||
<th class="text-center">@lang('More Info')</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($activities as $activity)
|
||||
<tr>
|
||||
@if (isset($adminView))
|
||||
<td>
|
||||
@if (isset($user))
|
||||
{{ $activity->user->present()->nameOrEmail }}
|
||||
@else
|
||||
<a href="{{ route('activity.user', $activity->user_id) }}"
|
||||
data-toggle="tooltip" title="@lang('View Activity Log')">
|
||||
{{ $activity->user->present()->nameOrEmail }}
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
<td>{{ $activity->ip_address }}</td>
|
||||
<td>{{ $activity->description }}</td>
|
||||
<td>{{ $activity->created_at->format(config('app.date_time_format')) }}</td>
|
||||
<td class="text-center">
|
||||
<a tabindex="0" role="button" class="btn btn-icon"
|
||||
data-trigger="focus"
|
||||
data-placement="left"
|
||||
data-toggle="popover"
|
||||
title="@lang('User Agent')"
|
||||
data-content="{{ $activity->user_agent }}">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!! $activities->render() !!}
|
||||
@stop
|
||||
@@ -0,0 +1,42 @@
|
||||
<div class="card">
|
||||
<h6 class="card-header d-flex align-items-center justify-content-between">
|
||||
@lang('Latest Activity')
|
||||
|
||||
@if (count($activities))
|
||||
<small>
|
||||
<a href="{{ route('activity.user', $user->id) }}"
|
||||
class="edit"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
title="@lang('Complete Activity Log')">
|
||||
@lang('View All')
|
||||
</a>
|
||||
</small>
|
||||
@endif
|
||||
</h6>
|
||||
|
||||
<div class="card-body">
|
||||
@if (count($activities))
|
||||
<table class="table table-borderless table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@lang('Action')</th>
|
||||
<th>@lang('Date')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($activities as $activity)
|
||||
<tr>
|
||||
<td>{{ $activity->description }}</td>
|
||||
<td>{{ $activity->created_at->format(config('app.date_time_format')) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="text-muted font-weight-light">
|
||||
<em>@lang('No activity from this user yet.')</em>
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<script>
|
||||
var labels = @json(array_keys($activities));
|
||||
var activities = @json(array_values($activities));
|
||||
var trans = {
|
||||
chartLabel: "{{ __('Registration History') }}",
|
||||
action: "{{ __('action') }}",
|
||||
actions: "{{ __('actions') }}"
|
||||
};
|
||||
</script>
|
||||
<script src="{{ asset('assets/js/chart.min.js') }}"></script>
|
||||
<script src="{{ asset('assets/js/as/dashboard-default.js') }}"></script>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<div class="card">
|
||||
<h6 class="card-header">
|
||||
@lang('Activity') (@lang('Last Two Weeks'))
|
||||
</h6>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="pt-4 px-3">
|
||||
<canvas id="myChart" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
Route::get('/activity', 'ActivityController@index');
|
||||
Route::get('/stats/activity', 'StatsController@show');
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
Route::group(['middleware' => ['auth', 'verified']], function () {
|
||||
|
||||
Route::get('profile/activity', 'UserActivityController@show')->name('profile.activity');
|
||||
|
||||
Route::get('activity', 'ActivityController@index')->name('activity.index')
|
||||
->middleware('permission:users.activity');
|
||||
|
||||
Route::get('activity/user/{user}/log', 'UserActivityController@index')->name('activity.user')
|
||||
->middleware('permission:users.activity');
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Database\Factories\ActivityFactory;
|
||||
|
||||
class Activity extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
const UPDATED_AT = null;
|
||||
|
||||
protected $table = 'user_activity';
|
||||
|
||||
protected $fillable = ['description', 'user_id', 'ip_address', 'user_agent'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new factory instance for the model.
|
||||
*/
|
||||
protected static function newFactory(): ActivityFactory
|
||||
{
|
||||
return new ActivityFactory;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\Controllers\Api;
|
||||
|
||||
use Spatie\QueryBuilder\AllowedFilter;
|
||||
use Spatie\QueryBuilder\QueryBuilder;
|
||||
use Vanguard\Http\Controllers\Api\ApiController;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
use Vanguard\UserActivity\Http\Requests\GetActivitiesRequest;
|
||||
use Vanguard\UserActivity\Http\Resources\ActivityResource;
|
||||
|
||||
class ActivityController extends ApiController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->middleware('permission:users.activity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginate user activities.
|
||||
*/
|
||||
public function index(GetActivitiesRequest $request): \Illuminate\Http\Resources\Json\AnonymousResourceCollection
|
||||
{
|
||||
$activities = QueryBuilder::for(Activity::class)
|
||||
->allowedIncludes('user')
|
||||
->allowedFilters([
|
||||
AllowedFilter::partial('description'),
|
||||
AllowedFilter::exact('user', 'user_id'),
|
||||
])
|
||||
->allowedSorts('created_at')
|
||||
->defaultSort('-created_at')
|
||||
->paginate($request->per_page ?: 20);
|
||||
|
||||
return ActivityResource::collection($activities);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\Controllers\Api;
|
||||
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Vanguard\Http\Controllers\Api\ApiController;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class StatsController extends ApiController
|
||||
{
|
||||
public function __construct(private readonly ActivityRepository $activities)
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function show(): JsonResponse
|
||||
{
|
||||
$data = $this->activities->userActivityForPeriod(
|
||||
Auth::user()->id,
|
||||
Carbon::now()->subWeeks(2),
|
||||
Carbon::now()
|
||||
);
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\Controllers\Web;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
use Vanguard\Http\Controllers\Controller;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class ActivityController extends Controller
|
||||
{
|
||||
public function __construct(private readonly ActivityRepository $activities)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the page with activities for all system users.
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$activities = $this->activities->paginateActivities(perPage: 20, search: $request->search);
|
||||
|
||||
return view('user-activity::index', [
|
||||
'adminView' => true,
|
||||
'activities' => $activities,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\Controllers\Web;
|
||||
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Vanguard\Http\Controllers\Controller;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class UserActivityController extends Controller
|
||||
{
|
||||
public function __construct(private readonly ActivityRepository $activities)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the activity log page for specific user.
|
||||
*/
|
||||
public function index(User $user, Request $request): View
|
||||
{
|
||||
$activities = $this->activities->paginateActivitiesForUser(
|
||||
userId: $user->id,
|
||||
search: $request->search,
|
||||
);
|
||||
|
||||
return view('user-activity::index', [
|
||||
'user' => $user,
|
||||
'adminView' => true,
|
||||
'activities' => $activities,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display user activity log.
|
||||
*/
|
||||
public function show(Request $request): View
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
$activities = $this->activities->paginateActivitiesForUser(
|
||||
userId: $user->id,
|
||||
search: $request->get('search'),
|
||||
);
|
||||
|
||||
return view('user-activity::index', compact('activities', 'user'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\Requests;
|
||||
|
||||
use Vanguard\Http\Requests\Request;
|
||||
|
||||
class GetActivitiesRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'per_page' => 'integer|max:100',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'per_page.max' => __('Maximum number of records per page is 100.'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ActivityResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
$agent = app('agent');
|
||||
$agent->setUserAgent($this->resource->user_agent);
|
||||
|
||||
return [
|
||||
'id' => (int) $this->id,
|
||||
'user_id' => (int) $this->user_id,
|
||||
'ip_address' => $this->ip_address,
|
||||
'user_agent' => $this->user_agent,
|
||||
'browser' => $agent->browser(),
|
||||
'platform' => $agent->platform(),
|
||||
'device' => $agent->device(),
|
||||
'description' => $this->description,
|
||||
'created_at' => (string) $this->created_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Http\View\Composers;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class ShowUserComposer
|
||||
{
|
||||
public function __construct(private readonly ActivityRepository $activity)
|
||||
{
|
||||
}
|
||||
|
||||
public function compose(View $view): void
|
||||
{
|
||||
$user = $view->getData()['user'];
|
||||
|
||||
$view->with('activities', $this->activity->getLatestActivitiesForUser($user->id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Listeners;
|
||||
|
||||
use Illuminate\Events\Dispatcher;
|
||||
use Vanguard\Events\Permission\Created;
|
||||
use Vanguard\Events\Permission\Deleted;
|
||||
use Vanguard\Events\Permission\Updated;
|
||||
use Vanguard\UserActivity\Logger;
|
||||
|
||||
class PermissionEventsSubscriber
|
||||
{
|
||||
public function __construct(private readonly Logger $logger)
|
||||
{
|
||||
}
|
||||
|
||||
public function onCreate(Created $event): void
|
||||
{
|
||||
$permission = $event->getPermission();
|
||||
|
||||
$name = $permission->display_name ?: $permission->name;
|
||||
$message = trans('user-activity::log.new_permission', ['name' => $name]);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onUpdate(Updated $event): void
|
||||
{
|
||||
$permission = $event->getPermission();
|
||||
|
||||
$name = $permission->display_name ?: $permission->name;
|
||||
$message = trans('user-activity::log.updated_permission', ['name' => $name]);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onDelete(Deleted $event): void
|
||||
{
|
||||
$permission = $event->getPermission();
|
||||
|
||||
$name = $permission->display_name ?: $permission->name;
|
||||
$message = trans('user-activity::log.deleted_permission', ['name' => $name]);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*/
|
||||
public function subscribe(Dispatcher $events): void
|
||||
{
|
||||
$class = self::class;
|
||||
|
||||
$events->listen(Created::class, "{$class}@onCreate");
|
||||
$events->listen(Updated::class, "{$class}@onUpdate");
|
||||
$events->listen(Deleted::class, "{$class}@onDelete");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Listeners;
|
||||
|
||||
use Illuminate\Events\Dispatcher;
|
||||
use Vanguard\Events\Role\Created;
|
||||
use Vanguard\Events\Role\Deleted;
|
||||
use Vanguard\Events\Role\PermissionsUpdated;
|
||||
use Vanguard\Events\Role\Updated;
|
||||
use Vanguard\UserActivity\Logger;
|
||||
|
||||
class RoleEventsSubscriber
|
||||
{
|
||||
public function __construct(private readonly Logger $logger)
|
||||
{
|
||||
}
|
||||
|
||||
public function onCreate(Created $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.new_role',
|
||||
['name' => $event->getRole()->display_name]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onUpdate(Updated $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.updated_role',
|
||||
['name' => $event->getRole()->display_name]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onDelete(Deleted $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.deleted_role',
|
||||
['name' => $event->getRole()->display_name]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onPermissionsUpdate(PermissionsUpdated $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.updated_role_permissions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*/
|
||||
public function subscribe(Dispatcher $events): void
|
||||
{
|
||||
$class = self::class;
|
||||
|
||||
$events->listen(Created::class, "{$class}@onCreate");
|
||||
$events->listen(Updated::class, "{$class}@onUpdate");
|
||||
$events->listen(Deleted::class, "{$class}@onDelete");
|
||||
$events->listen(PermissionsUpdated::class, "{$class}@onPermissionsUpdate");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Listeners;
|
||||
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Events\Dispatcher;
|
||||
use Lab404\Impersonate\Events\LeaveImpersonation;
|
||||
use Lab404\Impersonate\Events\TakeImpersonation;
|
||||
use Vanguard\Events\Settings\Updated as SettingsUpdated;
|
||||
use Vanguard\Events\User\Banned;
|
||||
use Vanguard\Events\User\ChangedAvatar;
|
||||
use Vanguard\Events\User\Created;
|
||||
use Vanguard\Events\User\Deleted;
|
||||
use Vanguard\Events\User\LoggedIn;
|
||||
use Vanguard\Events\User\LoggedOut;
|
||||
use Vanguard\Events\User\RequestedPasswordResetEmail;
|
||||
use Vanguard\Events\User\TwoFactorDisabled;
|
||||
use Vanguard\Events\User\TwoFactorDisabledByAdmin;
|
||||
use Vanguard\Events\User\TwoFactorEnabled;
|
||||
use Vanguard\Events\User\TwoFactorEnabledByAdmin;
|
||||
use Vanguard\Events\User\UpdatedByAdmin;
|
||||
use Vanguard\Events\User\UpdatedProfileDetails;
|
||||
use Vanguard\UserActivity\Logger;
|
||||
|
||||
class UserEventsSubscriber
|
||||
{
|
||||
public function __construct(private readonly Logger $logger)
|
||||
{
|
||||
}
|
||||
|
||||
public function onLogin(LoggedIn $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.logged_in'));
|
||||
}
|
||||
|
||||
public function onLogout(LoggedOut $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.logged_out'));
|
||||
}
|
||||
|
||||
public function onRegister(Registered $event): void
|
||||
{
|
||||
$this->logger->setUser($event->user);
|
||||
$this->logger->log(trans('user-activity::log.created_account'));
|
||||
}
|
||||
|
||||
public function onAvatarChange(ChangedAvatar $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.updated_avatar'));
|
||||
}
|
||||
|
||||
public function onProfileDetailsUpdate(UpdatedProfileDetails $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.updated_profile'));
|
||||
}
|
||||
|
||||
public function onDelete(Deleted $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.deleted_user',
|
||||
['name' => $event->getDeletedUser()->present()->nameOrEmail]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onBan(Banned $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.banned_user',
|
||||
['name' => $event->getBannedUser()->present()->nameOrEmail]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onUpdateByAdmin(UpdatedByAdmin $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.updated_profile_details_for',
|
||||
['name' => $event->getUpdatedUser()->present()->nameOrEmail]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onCreate(Created $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.created_account_for',
|
||||
['name' => $event->getCreatedUser()->present()->nameOrEmail]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onSettingsUpdate(SettingsUpdated $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.updated_settings'));
|
||||
}
|
||||
|
||||
public function onTwoFactorEnable(TwoFactorEnabled $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.enabled_2fa'));
|
||||
}
|
||||
|
||||
public function onTwoFactorDisable(TwoFactorDisabled $event): void
|
||||
{
|
||||
$this->logger->log(trans('user-activity::log.disabled_2fa'));
|
||||
}
|
||||
|
||||
public function onTwoFactorEnableByAdmin(TwoFactorEnabledByAdmin $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.enabled_2fa_for',
|
||||
['name' => $event->getUser()->present()->nameOrEmail]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onTwoFactorDisableByAdmin(TwoFactorDisabledByAdmin $event): void
|
||||
{
|
||||
$message = trans(
|
||||
'user-activity::log.disabled_2fa_for',
|
||||
['name' => $event->getUser()->present()->nameOrEmail]
|
||||
);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onPasswordResetEmailRequest(RequestedPasswordResetEmail $event): void
|
||||
{
|
||||
$this->logger->setUser($event->getUser());
|
||||
$this->logger->log(trans('user-activity::log.requested_password_reset'));
|
||||
}
|
||||
|
||||
public function onPasswordReset(PasswordReset $event): void
|
||||
{
|
||||
$this->logger->setUser($event->user);
|
||||
$this->logger->log(trans('user-activity::log.reseted_password'));
|
||||
}
|
||||
|
||||
public function onStartImpersonating(TakeImpersonation $event): void
|
||||
{
|
||||
$this->logger->setUser($event->impersonator);
|
||||
|
||||
$message = trans('user-activity::log.started_impersonating', [
|
||||
'id' => $event->impersonated->id,
|
||||
'name' => $event->impersonated->present()->name,
|
||||
]);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
public function onStopImpersonating(LeaveImpersonation $event): void
|
||||
{
|
||||
$this->logger->setUser($event->impersonator);
|
||||
|
||||
$message = trans('user-activity::log.stopped_impersonating', [
|
||||
'id' => $event->impersonated->id,
|
||||
'name' => $event->impersonated->present()->name,
|
||||
]);
|
||||
|
||||
$this->logger->log($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the listeners for the subscriber.
|
||||
*/
|
||||
public function subscribe(Dispatcher $events)
|
||||
{
|
||||
$class = self::class;
|
||||
|
||||
$events->listen(LoggedIn::class, "{$class}@onLogin");
|
||||
$events->listen(LoggedOut::class, "{$class}@onLogout");
|
||||
$events->listen(Registered::class, "{$class}@onRegister");
|
||||
$events->listen(Created::class, "{$class}@onCreate");
|
||||
$events->listen(ChangedAvatar::class, "{$class}@onAvatarChange");
|
||||
$events->listen(UpdatedProfileDetails::class, "{$class}@onProfileDetailsUpdate");
|
||||
$events->listen(UpdatedByAdmin::class, "{$class}@onUpdateByAdmin");
|
||||
$events->listen(Deleted::class, "{$class}@onDelete");
|
||||
$events->listen(Banned::class, "{$class}@onBan");
|
||||
$events->listen(SettingsUpdated::class, "{$class}@onSettingsUpdate");
|
||||
$events->listen(TwoFactorEnabled::class, "{$class}@onTwoFactorEnable");
|
||||
$events->listen(TwoFactorDisabled::class, "{$class}@onTwoFactorDisable");
|
||||
$events->listen(TwoFactorEnabledByAdmin::class, "{$class}@onTwoFactorEnableByAdmin");
|
||||
$events->listen(TwoFactorDisabledByAdmin::class, "{$class}@onTwoFactorDisableByAdmin");
|
||||
$events->listen(RequestedPasswordResetEmail::class, "{$class}@onPasswordResetEmailRequest");
|
||||
$events->listen(PasswordReset::class, "{$class}@onPasswordReset");
|
||||
$events->listen(TakeImpersonation::class, "{$class}@onStartImpersonating");
|
||||
$events->listen(LeaveImpersonation::class, "{$class}@onStopImpersonating");
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity;
|
||||
|
||||
use Illuminate\Contracts\Auth\Factory;
|
||||
use Illuminate\Http\Request;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class Logger
|
||||
{
|
||||
protected ?User $user = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly Request $request,
|
||||
private readonly Factory $auth,
|
||||
private readonly ActivityRepository $activities
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Log user action.
|
||||
*/
|
||||
public function log($description): Activity
|
||||
{
|
||||
return $this->activities->log([
|
||||
'description' => $description,
|
||||
'user_id' => $this->getUserId(),
|
||||
'ip_address' => $this->request->ip(),
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id if the user for who we want to log this action.
|
||||
* If user was manually set, then we will just return id of that user.
|
||||
* If not, we will return the id of currently logged user.
|
||||
*/
|
||||
private function getUserId(): ?int
|
||||
{
|
||||
if ($this->user) {
|
||||
return $this->user->id;
|
||||
}
|
||||
|
||||
return $this->auth->guard()->id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user agent from request headers.
|
||||
*/
|
||||
private function getUserAgent(): string
|
||||
{
|
||||
return substr((string) $this->request->header('User-Agent'), 0, 500);
|
||||
}
|
||||
|
||||
public function setUser(?User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Repositories\Activity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Collection as BaseCollection;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
|
||||
interface ActivityRepository
|
||||
{
|
||||
/**
|
||||
* Log user activity.
|
||||
*
|
||||
* @param $data array Array with following fields:
|
||||
* description (string) - Description of user activity.
|
||||
* user_id (int) - User unique identifier.
|
||||
* ip_address (string) - Ip address from which user is accessing the website.
|
||||
* user_agent (string) - User's browser info.
|
||||
* @return mixed
|
||||
*/
|
||||
public function log(array $data): Activity;
|
||||
|
||||
/**
|
||||
* Paginate activities for user.
|
||||
*/
|
||||
public function paginateActivitiesForUser(
|
||||
int $userId,
|
||||
int $perPage = 20,
|
||||
?string $search = null
|
||||
): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Get specified number of latest user activity logs.
|
||||
*
|
||||
* @return Collection<Activity>
|
||||
*/
|
||||
public function getLatestActivitiesForUser(int $userId, int $activitiesCount = 10): Collection;
|
||||
|
||||
/**
|
||||
* Paginate all activity records.
|
||||
*/
|
||||
public function paginateActivities(int $perPage = 20, ?string $search = null): LengthAwarePaginator;
|
||||
|
||||
/**
|
||||
* Get count of user activities per day for given period of time.
|
||||
*/
|
||||
public function userActivityForPeriod(int $userId, Carbon $from, Carbon $to): BaseCollection;
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Repositories\Activity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DB;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection as BaseCollection;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
|
||||
class EloquentActivity implements ActivityRepository
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function log($data): Activity
|
||||
{
|
||||
return Activity::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function paginateActivitiesForUser(
|
||||
int $userId,
|
||||
int $perPage = 20,
|
||||
?string $search = null
|
||||
): LengthAwarePaginator {
|
||||
$query = Activity::where('user_id', $userId);
|
||||
|
||||
return $this->paginateAndFilterResults($perPage, $search, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getLatestActivitiesForUser(int $userId, int $activitiesCount = 10): Collection
|
||||
{
|
||||
return Activity::where('user_id', $userId)
|
||||
->orderBy('created_at', 'DESC')
|
||||
->limit($activitiesCount)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function paginateActivities(int $perPage = 20, ?string $search = null): LengthAwarePaginator
|
||||
{
|
||||
$query = Activity::with('user');
|
||||
|
||||
return $this->paginateAndFilterResults($perPage, $search, $query);
|
||||
}
|
||||
|
||||
private function paginateAndFilterResults($perPage, $search, $query): LengthAwarePaginator
|
||||
{
|
||||
if ($search) {
|
||||
$query->where('description', 'LIKE', "%$search%");
|
||||
}
|
||||
|
||||
$result = $query->orderBy('created_at', 'DESC')
|
||||
->paginate($perPage);
|
||||
|
||||
if ($search) {
|
||||
$result->appends(['search' => $search]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function userActivityForPeriod($userId, Carbon $from, Carbon $to): BaseCollection
|
||||
{
|
||||
$result = Activity::select([
|
||||
DB::raw('DATE(created_at) as day'),
|
||||
DB::raw('count(id) as count'),
|
||||
])
|
||||
->where('user_id', $userId)
|
||||
->whereBetween('created_at', [$from, $to])
|
||||
->groupBy('day')
|
||||
->orderBy('day', 'ASC')
|
||||
->pluck('count', 'day');
|
||||
|
||||
while (! $from->isSameDay($to)) {
|
||||
if (! $result->has($from->toDateString())) {
|
||||
$result->put($from->toDateString(), 0);
|
||||
}
|
||||
$from->addDay();
|
||||
}
|
||||
|
||||
return $result->sortBy(function ($value, $key) {
|
||||
return strtotime($key);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity;
|
||||
|
||||
use Event;
|
||||
use Route;
|
||||
use Vanguard\Plugins\Plugin;
|
||||
use Vanguard\Support\Sidebar\Item;
|
||||
use Vanguard\UserActivity\Http\View\Composers\ShowUserComposer;
|
||||
use Vanguard\UserActivity\Listeners\PermissionEventsSubscriber;
|
||||
use Vanguard\UserActivity\Listeners\RoleEventsSubscriber;
|
||||
use Vanguard\UserActivity\Listeners\UserEventsSubscriber;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
use Vanguard\UserActivity\Repositories\Activity\EloquentActivity;
|
||||
use View;
|
||||
|
||||
class UserActivity extends Plugin
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function sidebar(): Item
|
||||
{
|
||||
return Item::create(__('Activity Log'))
|
||||
->route('activity.index')
|
||||
->icon('fas fa-server')
|
||||
->active('activity*')
|
||||
->permissions('users.activity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(ActivityRepository::class, EloquentActivity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->loadViewsFrom(__DIR__.'/../resources/views', 'user-activity');
|
||||
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'user-activity');
|
||||
$this->loadJsonTranslationsFrom(__DIR__.'/../resources/lang');
|
||||
|
||||
$this->publishes([
|
||||
__DIR__.'/../database/migrations' => database_path('migrations'),
|
||||
], 'migrations');
|
||||
|
||||
$this->app->booted(function () {
|
||||
$this->mapWebRoutes();
|
||||
|
||||
if ($this->app['config']->get('auth.expose_api')) {
|
||||
$this->mapApiRoutes();
|
||||
}
|
||||
});
|
||||
|
||||
$this->attachViewComposers();
|
||||
|
||||
$this->registerEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map web plugin related routes.
|
||||
*/
|
||||
protected function mapWebRoutes(): void
|
||||
{
|
||||
Route::group([
|
||||
'namespace' => 'Vanguard\UserActivity\Http\Controllers\Web',
|
||||
'middleware' => 'web',
|
||||
], function () {
|
||||
$this->loadRoutesFrom(__DIR__.'/../routes/web.php');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map API plugin related routes.
|
||||
*/
|
||||
protected function mapApiRoutes(): void
|
||||
{
|
||||
Route::group([
|
||||
'namespace' => 'Vanguard\UserActivity\Http\Controllers\Api',
|
||||
'middleware' => 'api',
|
||||
'prefix' => 'api',
|
||||
], function () {
|
||||
$this->loadRoutesFrom(__DIR__.'/../routes/api.php');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register event subscribers for the plugin.
|
||||
*/
|
||||
private function registerEventListeners(): void
|
||||
{
|
||||
Event::subscribe(PermissionEventsSubscriber::class);
|
||||
Event::subscribe(RoleEventsSubscriber::class);
|
||||
Event::subscribe(UserEventsSubscriber::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach view composers to add necessary data to the view.
|
||||
*/
|
||||
private function attachViewComposers(): void
|
||||
{
|
||||
View::composer('user.view', ShowUserComposer::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Widgets;
|
||||
|
||||
use Auth;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Vanguard\Plugins\Widget;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class ActivityWidget extends Widget
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public ?string $width = '12';
|
||||
|
||||
private ?array $userActivity = null;
|
||||
|
||||
public function __construct(private readonly ActivityRepository $activities)
|
||||
{
|
||||
$this->permissions(function (User $user) {
|
||||
return $user->hasRole('User');
|
||||
});
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('user-activity::widgets.user-activity', [
|
||||
'activities' => $this->getActivity(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function scripts(): View
|
||||
{
|
||||
return view('user-activity::widgets.user-activity-scripts', [
|
||||
'activities' => $this->getActivity(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getActivity(): array
|
||||
{
|
||||
if ($this->userActivity) {
|
||||
return $this->userActivity;
|
||||
}
|
||||
|
||||
return $this->userActivity = $this->activities->userActivityForPeriod(
|
||||
Auth::user()->id,
|
||||
Carbon::now()->subWeeks(2),
|
||||
Carbon::now()
|
||||
)->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Feature\Api;
|
||||
|
||||
use Facades\Tests\Setup\UserFactory;
|
||||
use Tests\Feature\ApiTestCase;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
use Vanguard\UserActivity\Http\Resources\ActivityResource;
|
||||
|
||||
class ActivityTest extends ApiTestCase
|
||||
{
|
||||
/** @test */
|
||||
public function unauthenticated()
|
||||
{
|
||||
$this->getJson('/api/activity')->assertStatus(401);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function get_activities_without_permission()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($user, self::API_GUARD)
|
||||
->getJson('/api/activity')
|
||||
->assertStatus(403);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function paginate_activities()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$user2 = User::factory()->create();
|
||||
|
||||
$activities = Activity::factory()->times(25)->create(['user_id' => $user->id]);
|
||||
|
||||
Activity::factory()->times(10)->create(['user_id' => $user2->id]);
|
||||
|
||||
$response = $this->actingAs($user, self::API_GUARD)->getJson('/api/activity');
|
||||
|
||||
$transformed = ActivityResource::collection($activities->take(20))->resolve();
|
||||
|
||||
$this->assertEquals($response->json('data'), $transformed);
|
||||
$response->assertJson([
|
||||
'meta' => [
|
||||
'current_page' => 1,
|
||||
'from' => 1,
|
||||
'to' => 20,
|
||||
'last_page' => 2,
|
||||
'path' => url('api/activity'),
|
||||
'total' => 35,
|
||||
'per_page' => 20,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function paginate_activities_with_search_param()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
|
||||
$set1 = Activity::factory()->times(10)->create([
|
||||
'user_id' => $user->id,
|
||||
'description' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
|
||||
]);
|
||||
|
||||
$set2 = Activity::factory()->times(5)->create([
|
||||
'user_id' => $user->id,
|
||||
'description' => 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris...',
|
||||
]);
|
||||
|
||||
$transformed = ActivityResource::collection($set2)->resolve();
|
||||
|
||||
$response = $this->actingAs($user, self::API_GUARD)
|
||||
->getJson('/api/activity?filter[description]=minim&per_page=10&sort=created_at')
|
||||
->assertOk();
|
||||
|
||||
$this->assertEquals($response->json('data'), $transformed);
|
||||
$response->assertJson([
|
||||
'meta' => [
|
||||
'current_page' => 1,
|
||||
'from' => 1,
|
||||
'to' => 5,
|
||||
'last_page' => 1,
|
||||
'total' => 5,
|
||||
'per_page' => 10,
|
||||
'path' => url('api/activity'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function paginate_activities_with_more_records_per_page_than_allowed()
|
||||
{
|
||||
$this->actingAs($this->getUser(), self::API_GUARD)
|
||||
->getJson('/api/activity?per_page=140')
|
||||
->assertStatus(422);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function paginate_activities_for_user()
|
||||
{
|
||||
$user = UserFactory::user()->withPermissions('users.activity')->create();
|
||||
|
||||
$this->be($user, self::API_GUARD);
|
||||
|
||||
$activities = Activity::factory()->times(25)->create(['user_id' => $user->id]);
|
||||
|
||||
$response = $this->getJson("/api/activity?filters[user]={$user->id}");
|
||||
|
||||
$transformed = ActivityResource::collection($activities->take(20))->resolve();
|
||||
|
||||
$this->assertEquals($response->json('data'), $transformed);
|
||||
$response->assertJson([
|
||||
'meta' => [
|
||||
'current_page' => 1,
|
||||
'from' => 1,
|
||||
'to' => 20,
|
||||
'last_page' => 2,
|
||||
'path' => url('api/activity'),
|
||||
'total' => 25,
|
||||
'per_page' => 20,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private function getUser()
|
||||
{
|
||||
return UserFactory::user()->withPermissions('users.activity')->create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Feature\Api;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Tests\Feature\ApiTestCase;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
use Vanguard\UserActivity\Repositories\Activity\ActivityRepository;
|
||||
|
||||
class StatsTest extends ApiTestCase
|
||||
{
|
||||
/** @test */
|
||||
public function non_admin_users_cannot_get_user_stats()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
Carbon::setTestNow(Carbon::now()->subWeek());
|
||||
Activity::factory()->times(5)->create(['user_id' => $user->id]);
|
||||
|
||||
Carbon::setTestNow(null);
|
||||
Activity::factory()->times(5)->create(['user_id' => $user->id]);
|
||||
|
||||
$response = $this->actingAs($user, self::API_GUARD)->getJson('/api/stats/activity');
|
||||
|
||||
$expected = app(ActivityRepository::class)->userActivityForPeriod(
|
||||
$user->id,
|
||||
Carbon::now()->subWeek(2),
|
||||
Carbon::now()
|
||||
)->toArray();
|
||||
|
||||
$response->assertOk()
|
||||
->assertJson($expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Feature\Web;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Facades\Tests\Setup\UserFactory;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
use Vanguard\UserActivity\Logger;
|
||||
|
||||
class ActivityTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public $logger;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->logger = app(Logger::class);
|
||||
$this->artisan('db:seed');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function display_all_activities()
|
||||
{
|
||||
$this->withoutMiddleware();
|
||||
|
||||
$user1 = UserFactory::create();
|
||||
$user2 = UserFactory::create();
|
||||
|
||||
Carbon::setTestNow(Carbon::now());
|
||||
|
||||
$this->be($user1);
|
||||
$this->logger->log('foo');
|
||||
|
||||
$this->be($user2);
|
||||
$this->logger->log('bar');
|
||||
|
||||
$this->get('activity')
|
||||
->assertSee('foo')
|
||||
->assertSee('bar');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function display_activities_for_a_specific_user()
|
||||
{
|
||||
$user = UserFactory::admin()->create();
|
||||
$this->be($user);
|
||||
|
||||
$this->logger->log('foo');
|
||||
|
||||
$this->get("activity/user/{$user->id}/log")
|
||||
->assertSee('foo');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function search_activities()
|
||||
{
|
||||
$this->withoutMiddleware();
|
||||
|
||||
$user = UserFactory::create();
|
||||
$this->be($user);
|
||||
|
||||
$this->logger->log('foo');
|
||||
$this->logger->log('barrr');
|
||||
|
||||
$this->get('activity?search=foo')
|
||||
->assertSee('foo')
|
||||
->assertDontSee('barrr');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Unit\Listeners;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
use Vanguard\User;
|
||||
|
||||
abstract class ListenerTestCase extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected User $user;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->user = User::factory()->create();
|
||||
$this->be($this->user);
|
||||
}
|
||||
|
||||
protected function assertMessageLogged($msg, $user = null): void
|
||||
{
|
||||
$this->assertDatabaseHas('user_activity', [
|
||||
'user_id' => $user ? $user->id : $this->user->id,
|
||||
'ip_address' => \Request::ip(),
|
||||
'user_agent' => \Request::header('User-agent'),
|
||||
'description' => $msg,
|
||||
]);
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Unit\Listeners;
|
||||
|
||||
use Vanguard\Events\Permission\Created;
|
||||
use Vanguard\Events\Permission\Deleted;
|
||||
use Vanguard\Events\Permission\Updated;
|
||||
|
||||
// Manually require the base test case to avoid issues while running automated tests
|
||||
require_once __DIR__.'/ListenerTestCase.php';
|
||||
|
||||
class PermissionEventsSubscriberTest extends \Vanguard\UserActivity\Tests\Unit\Listeners\ListenerTestCase
|
||||
{
|
||||
protected \Vanguard\Permission $perm;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->perm = \Vanguard\Permission::factory()->create();
|
||||
}
|
||||
|
||||
protected function assertMessageLogged($msg, $user = null): void
|
||||
{
|
||||
$this->assertDatabaseHas('user_activity', [
|
||||
'user_id' => $user ? $user->id : $this->user->id,
|
||||
'ip_address' => \Request::ip(),
|
||||
'user_agent' => \Request::header('User-agent'),
|
||||
'description' => $msg,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onCreate()
|
||||
{
|
||||
event(new Created($this->perm));
|
||||
$this->assertMessageLogged("Created new permission called {$this->perm->display_name}.");
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onUpdate()
|
||||
{
|
||||
event(new Updated($this->perm));
|
||||
$this->assertMessageLogged("Updated the permission named {$this->perm->display_name}.");
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onDelete()
|
||||
{
|
||||
event(new Deleted($this->perm));
|
||||
$this->assertMessageLogged("Deleted permission named {$this->perm->display_name}.");
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Unit\Listeners;
|
||||
|
||||
use Vanguard\Events\Role\Created;
|
||||
use Vanguard\Events\Role\Deleted;
|
||||
use Vanguard\Events\Role\PermissionsUpdated;
|
||||
use Vanguard\Events\Role\Updated;
|
||||
|
||||
class RoleEventsSubscriberTest extends ListenerTestCase
|
||||
{
|
||||
protected \Vanguard\Role $role;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->role = \Vanguard\Role::factory()->create();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onCreate()
|
||||
{
|
||||
event(new Created($this->role));
|
||||
$this->assertMessageLogged("Created new role called {$this->role->display_name}.");
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onUpdate()
|
||||
{
|
||||
event(new Updated($this->role));
|
||||
$this->assertMessageLogged("Updated role with name {$this->role->display_name}.");
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onDelete()
|
||||
{
|
||||
event(new Deleted($this->role));
|
||||
$this->assertMessageLogged("Deleted role named {$this->role->display_name}.");
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onPermissionsUpdate()
|
||||
{
|
||||
event(new PermissionsUpdated());
|
||||
$this->assertMessageLogged('Updated role permissions.');
|
||||
}
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Unit\Listeners;
|
||||
|
||||
use Tests\UpdatesSettings;
|
||||
|
||||
class UserEventsSubscriberTest extends ListenerTestCase
|
||||
{
|
||||
use UpdatesSettings;
|
||||
|
||||
protected \Vanguard\User $theUser;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->theUser = \Vanguard\User::factory()->create();
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onLogin()
|
||||
{
|
||||
event(new \Vanguard\Events\User\LoggedIn);
|
||||
$this->assertMessageLogged('Logged in.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onLogout()
|
||||
{
|
||||
event(new \Vanguard\Events\User\LoggedOut());
|
||||
$this->assertMessageLogged('Logged out.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onRegister()
|
||||
{
|
||||
$this->setSettings([
|
||||
'reg_enabled' => true,
|
||||
'reg_email_confirmation' => true,
|
||||
]);
|
||||
|
||||
$user = \Vanguard\User::factory()->create();
|
||||
|
||||
event(new \Illuminate\Auth\Events\Registered($user));
|
||||
|
||||
$this->assertMessageLogged('Created an account.', $user);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onAvatarChange()
|
||||
{
|
||||
event(new \Vanguard\Events\User\ChangedAvatar);
|
||||
$this->assertMessageLogged('Updated profile avatar.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onProfileDetailsUpdate()
|
||||
{
|
||||
event(new \Vanguard\Events\User\UpdatedProfileDetails);
|
||||
$this->assertMessageLogged('Updated profile details.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onDelete()
|
||||
{
|
||||
event(new \Vanguard\Events\User\Deleted($this->theUser));
|
||||
|
||||
$message = sprintf(
|
||||
'Deleted user %s.',
|
||||
$this->theUser->present()->nameOrEmail
|
||||
);
|
||||
|
||||
$this->assertMessageLogged($message);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onBan()
|
||||
{
|
||||
event(new \Vanguard\Events\User\Banned($this->theUser));
|
||||
|
||||
$message = sprintf(
|
||||
'Banned user %s.',
|
||||
$this->theUser->present()->nameOrEmail
|
||||
);
|
||||
|
||||
$this->assertMessageLogged($message);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onUpdateByAdmin()
|
||||
{
|
||||
event(new \Vanguard\Events\User\UpdatedByAdmin($this->theUser));
|
||||
|
||||
$message = sprintf(
|
||||
'Updated profile details for %s.',
|
||||
$this->theUser->present()->nameOrEmail
|
||||
);
|
||||
|
||||
$this->assertMessageLogged($message);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onCreate()
|
||||
{
|
||||
event(new \Vanguard\Events\User\Created($this->theUser));
|
||||
|
||||
$message = sprintf(
|
||||
'Created an account for user %s.',
|
||||
$this->theUser->present()->nameOrEmail
|
||||
);
|
||||
|
||||
$this->assertMessageLogged($message);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onSettingsUpdate()
|
||||
{
|
||||
event(new \Vanguard\Events\Settings\Updated);
|
||||
$this->assertMessageLogged('Updated website settings.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onTwoFactorEnable()
|
||||
{
|
||||
event(new \Vanguard\Events\User\TwoFactorEnabled);
|
||||
$this->assertMessageLogged('Enabled Two-Factor Authentication.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onTwoFactorDisable()
|
||||
{
|
||||
event(new \Vanguard\Events\User\TwoFactorDisabled);
|
||||
$this->assertMessageLogged('Disabled Two-Factor Authentication.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onTwoFactorEnabledByAdmin()
|
||||
{
|
||||
event(new \Vanguard\Events\User\TwoFactorEnabledByAdmin($this->theUser));
|
||||
|
||||
$message = sprintf(
|
||||
'Enabled Two-Factor Authentication for user %s.',
|
||||
$this->theUser->present()->nameOrEmail
|
||||
);
|
||||
|
||||
$this->assertMessageLogged($message);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onTwoFactorDisabledByAdmin()
|
||||
{
|
||||
event(new \Vanguard\Events\User\TwoFactorDisabledByAdmin($this->theUser));
|
||||
|
||||
$message = sprintf(
|
||||
'Disabled Two-Factor Authentication for user %s.',
|
||||
$this->theUser->present()->nameOrEmail
|
||||
);
|
||||
|
||||
$this->assertMessageLogged($message);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onPasswordResetEmailRequest()
|
||||
{
|
||||
event(new \Vanguard\Events\User\RequestedPasswordResetEmail($this->user));
|
||||
$this->assertMessageLogged('Requested password reset email.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onPasswordReset()
|
||||
{
|
||||
event(new \Illuminate\Auth\Events\PasswordReset($this->user));
|
||||
$this->assertMessageLogged('Reseted password using "Forgot Password" option.');
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onStartImpersonating()
|
||||
{
|
||||
$impersonated = \Vanguard\User::factory()->create([
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
]);
|
||||
|
||||
event(new \Lab404\Impersonate\Events\TakeImpersonation($this->user, $impersonated));
|
||||
|
||||
$this->assertMessageLogged("Started impersonating user John Doe (ID: {$impersonated->id})");
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function onStopImpersonating()
|
||||
{
|
||||
$impersonated = \Vanguard\User::factory()->create([
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
]);
|
||||
|
||||
event(new \Lab404\Impersonate\Events\LeaveImpersonation($this->user, $impersonated));
|
||||
|
||||
$this->assertMessageLogged("Stopped impersonating user John Doe (ID: {$impersonated->id})");
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Vanguard\UserActivity\Tests\Unit\Repositories\Activity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Testing\Assert;
|
||||
use Tests\TestCase;
|
||||
use Vanguard\User;
|
||||
use Vanguard\UserActivity\Activity;
|
||||
use Vanguard\UserActivity\Repositories\Activity\EloquentActivity;
|
||||
|
||||
class EloquentActivityTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @var EloquentActivity
|
||||
*/
|
||||
protected $repo;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->repo = app(EloquentActivity::class);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function log()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
Carbon::setTestNow(Carbon::now());
|
||||
|
||||
$data = [
|
||||
'user_id' => $user->id,
|
||||
'ip_address' => '123.456.789.012',
|
||||
'user_agent' => 'foo',
|
||||
'description' => 'descriptionnnn',
|
||||
];
|
||||
|
||||
$this->repo->log($data);
|
||||
|
||||
$this->assertDatabaseHas('user_activity', $data);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function paginate_activities_for_user()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$activities = Activity::factory()->times(10)->create(['user_id' => $user->id]);
|
||||
|
||||
$result = $this->repo->paginateActivitiesForUser($user->id, 6)->toArray();
|
||||
|
||||
$this->assertEquals(6, count($result['data']));
|
||||
$this->assertEquals(10, $result['total']);
|
||||
$this->assertEquals($activities[0]->toArray(), $result['data'][0]);
|
||||
$this->assertEquals($activities[5]->toArray(), $result['data'][5]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function latest_activities_for_user()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
Carbon::setTestNow(Carbon::now()->subDay());
|
||||
$activities1 = Activity::factory()->times(5)->create(['user_id' => $user->id]);
|
||||
|
||||
Carbon::setTestNow(null);
|
||||
$activities2 = Activity::factory()->times(5)->create(['user_id' => $user->id]);
|
||||
|
||||
$result = $this->repo->getLatestActivitiesForUser($user->id, 6)->toArray();
|
||||
|
||||
$this->assertEquals(6, count($result));
|
||||
$this->assertEquals($activities2[0]->toArray(), $result[0]);
|
||||
$this->assertEquals($activities1[0]->toArray(), $result[5]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function paginate_activities()
|
||||
{
|
||||
$activities = Activity::factory()->times(10)->create();
|
||||
|
||||
$result = $this->repo->paginateActivities(6)->toArray();
|
||||
|
||||
$this->assertEquals(6, count($result['data']));
|
||||
$this->assertEquals(10, $result['total']);
|
||||
|
||||
Assert::assertArraySubset($activities[0]->toArray(), $result['data'][0]);
|
||||
Assert::assertArraySubset($activities[5]->toArray(), $result['data'][5]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function userActivityForPeriod()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$now = Carbon::now();
|
||||
|
||||
Carbon::setTestNow($now->copy()->subDays(15));
|
||||
Activity::factory()->times(5)->create(['user_id' => $user->id]);
|
||||
|
||||
Carbon::setTestNow($now->copy()->subDays(11));
|
||||
Activity::factory()->times(2)->create(['user_id' => $user->id]);
|
||||
|
||||
Carbon::setTestNow($now->copy()->subDays(5));
|
||||
Activity::factory()->times(3)->create(['user_id' => $user->id]);
|
||||
|
||||
Carbon::setTestNow($now->copy()->subDays(2));
|
||||
Activity::factory()->times(2)->create(['user_id' => $user->id]);
|
||||
|
||||
Carbon::setTestNow(null);
|
||||
|
||||
$result = $this->repo->userActivityForPeriod(
|
||||
$user->id,
|
||||
Carbon::now()->subWeeks(2),
|
||||
Carbon::now()
|
||||
);
|
||||
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(14)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(13)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(12)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(11)->toDateString()), 2);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(10)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(9)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(8)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(7)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(6)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(5)->toDateString()), 3);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(4)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(3)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(2)->toDateString()), 2);
|
||||
$this->assertEquals($result->get(Carbon::now()->subDays(1)->toDateString()), 0);
|
||||
$this->assertEquals($result->get(Carbon::now()->toDateString()), 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user