Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2811cee
feat(activity): Enhance activity logging with context and subject rel…
lcharette Aug 7, 2026
bab5465
Making StyleCI happy
lcharette Aug 7, 2026
c866901
Added: Get activity description from i18n dictionary based on the act…
lcharette Aug 11, 2026
357a26d
Fix sort order
lcharette Aug 11, 2026
f702bee
feat(activity): Add localized labels to sort and filter activities wi…
lcharette Aug 11, 2026
3beba8e
feat(activity): Enhance activity logging with email and password upda…
lcharette Aug 16, 2026
9276d04
Added activity for cache cleared
lcharette Aug 24, 2026
9c32a7d
- Updated Activity model to include 'properties' for tracking old and…
lcharette Aug 25, 2026
2228fcb
feat(activity): Add 'withProperties' parameter to ActivityRecorder fo…
lcharette Aug 25, 2026
b63da0c
feat: Refactor user management modals and forms
lcharette Sep 11, 2026
bbf5108
Fix style
lcharette Sep 11, 2026
967b09c
Update dependencies
lcharette Sep 11, 2026
05e79f9
feat(role): Implement dedicated API for role permissions management a…
lcharette Sep 12, 2026
68f200f
Fix style
lcharette Sep 12, 2026
732f2ec
Merge remote-tracking branch 'origin/6.1' into 6.1-feature-activity2.0
Copilot Sep 12, 2026
1f6e5f6
Fix failing test since merge
lcharette Sep 13, 2026
d9c5ad9
Merge branch '6.1' into 6.1-feature-activity2.0
lcharette Sep 13, 2026
6af94cb
Merge branch '6.1' into 6.1-feature-activity2.0
lcharette Sep 13, 2026
5a6710b
Fix some tests related to non-SQLite errors
lcharette Sep 13, 2026
6d36418
Merge branch '6.1' into 6.1-feature-activity2.0
lcharette Sep 13, 2026
67e35e5
Merge branch '6.1' into 6.1-feature-activity2.0
lcharette Sep 13, 2026
9f2abae
Fix type tolerance on PGSQL
lcharette Sep 13, 2026
4eed0ea
Up test coverage
lcharette Sep 13, 2026
f4ecb19
Fix admin user group assignment to use null instead of undefined
lcharette Sep 13, 2026
ee6eb18
Add withProperties parameter to account activity logging
lcharette Sep 13, 2026
e2f0a55
Add Activity model cleanup in tearDown method of CreateAdminUserTest
lcharette Sep 13, 2026
0bf0a35
Remove unnecessary Omit
lcharette Sep 13, 2026
9ede209
Use withProperties in GroupEditAction & RoleEditAction
lcharette Sep 14, 2026
ad039d0
Refactor user action handlers to streamline response handling and imp…
lcharette Sep 14, 2026
15aadf5
Fix style
lcharette Sep 14, 2026
564bc0d
Renamed ActivitySubjectInterfaceTest
lcharette Sep 14, 2026
f90a9c3
Refactor role and permission handling in user and role actions; updat…
lcharette Sep 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ These **must** be run before committing code, from the root of the monorepo:

## Changelog Guidelines

**Every change must include a changelog update** — do this automatically, without waiting to be asked:
Before committing, **every change must include a changelog update**. Once a change is completed, ask the user if he wants to update the changelog. If yes, follow these steps:

1. Update the relevant `packages/*/CHANGELOG.md` file(s) for affected packages
2. Add entries under the `## [Unreleased]` section
Expand Down
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,12 @@
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .oxlint*, oxlint*, .prettier*, prettier*, .editorconfig",
"composer.json": "composer.lock, phpunit.xml*, phpstan.neon*, phpstan-baseline.neon*, .php-cs*, .php_cs*, .styleci.yml*, .phpdoc.*"
},
"cSpell.words": ["axios", "pinia", "Sprunjer", "tdate", "TOTP"]
"cSpell.words": [
"axios",
"Morphable",
"pinia",
"Sprunjer",
"tdate",
"TOTP"
]
}
3 changes: 3 additions & 0 deletions packages/sprinkle-account/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Changed
- Added activity logging for profile changes.

### New Feature
- Added MorphMapProvider for Eloquent polymorphic type aliases.

Expand Down
20 changes: 20 additions & 0 deletions packages/sprinkle-account/app/src/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use UserFrosting\Sprinkle\Account\Database\Migrations\v600\DropVerificationsTable;
use UserFrosting\Sprinkle\Account\Database\Migrations\v600\UpdateUsersTable as V600UpdateUsersTable;
use UserFrosting\Sprinkle\Account\Database\Migrations\v600\UserVerificationTable;
use UserFrosting\Sprinkle\Account\Database\Migrations\v610\ActivitiesV2Table;
use UserFrosting\Sprinkle\Account\Database\Seeds\DefaultGroups;
use UserFrosting\Sprinkle\Account\Database\Seeds\DefaultPermissions;
use UserFrosting\Sprinkle\Account\Database\Seeds\DefaultRoles;
Expand All @@ -48,15 +49,19 @@
use UserFrosting\Sprinkle\Account\Listener\UpgradePassword;
use UserFrosting\Sprinkle\Account\Listener\UserLogoutActivity;
use UserFrosting\Sprinkle\Account\Listener\UserSignInActivity;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Account\Routes\AuthRoutes;
use UserFrosting\Sprinkle\Account\ServicesProvider\AccessConditionsService;
use UserFrosting\Sprinkle\Account\ServicesProvider\ActivityService;
use UserFrosting\Sprinkle\Account\ServicesProvider\AuthorizationService;
use UserFrosting\Sprinkle\Account\ServicesProvider\AuthService;
use UserFrosting\Sprinkle\Account\ServicesProvider\I18nService;
use UserFrosting\Sprinkle\Account\ServicesProvider\LoggersService;
use UserFrosting\Sprinkle\Account\ServicesProvider\MFAServices;
use UserFrosting\Sprinkle\Account\ServicesProvider\ModelsService;
use UserFrosting\Sprinkle\Account\ServicesProvider\MorphMapProvider;
use UserFrosting\Sprinkle\Account\Sprinkle\Recipe\ActivityRecipe;
use UserFrosting\Sprinkle\Account\Twig\AccountExtension;
use UserFrosting\Sprinkle\BakeryRecipe;
use UserFrosting\Sprinkle\Core\Bakery\Event\BakeCommandEvent;
Expand All @@ -68,6 +73,7 @@

class Account implements
SprinkleRecipe,
ActivityRecipe,
MigrationRecipe,
SeedRecipe,
EventListenerRecipe,
Expand Down Expand Up @@ -113,6 +119,17 @@ public function getSprinkles(): array
];
}

/**
* {@inheritDoc}
*/
public function getActivityTypes(): array
{
return [
UserActivityTypes::class,
AccountActivityTypes::class,
];
}

/**
* {@inheritDoc}
*/
Expand All @@ -130,6 +147,7 @@ public function getServices(): array
{
return [
AccessConditionsService::class,
ActivityService::class,
AuthorizationService::class,
AuthService::class,
ModelsService::class,
Expand Down Expand Up @@ -171,6 +189,8 @@ public function getMigrations(): array
V600UpdateUsersTable::class,
DropPasswordResetsTable::class,
DropVerificationsTable::class,
// v610
ActivitiesV2Table::class,
];
}

Expand Down
15 changes: 8 additions & 7 deletions packages/sprinkle-account/app/src/Bakery/CreateUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
use UserFrosting\Sprinkle\Account\Database\Models\User;
use UserFrosting\Sprinkle\Account\Event\UserCreatedEvent;
use UserFrosting\Sprinkle\Account\Exceptions\AccountException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Account\Validators\UserValidation;
use UserFrosting\Sprinkle\Core\Bakery\Helper\DatabaseTest;
use UserFrosting\Sprinkle\Core\Database\Migrator\MigrationRepositoryInterface;
Expand Down Expand Up @@ -70,7 +70,7 @@ class CreateUser extends Command
protected EventDispatcherInterface $eventDispatcher;

#[Inject]
protected UserActivityLoggerInterface $logger;
protected ActivityRecorderInterface $logger;

#[Inject]
protected Capsule $capsule;
Expand Down Expand Up @@ -190,10 +190,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
$user = $this->eventDispatcher->dispatch($event)->user;

// Create activity record
$this->logger->info("User {$user->user_name} account was created.", [
'type' => UserActivityTypes::REGISTER,
'user_id' => $user->id,
]);
$this->logger->record(
user: null,
type: AccountActivityTypes::CREATE,
subject: $user
);

return $user;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
use UserFrosting\Sprinkle\Account\Exceptions\FailedVerificationException;
use UserFrosting\Sprinkle\Account\Exceptions\VerificationDisabledException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\Throttle\Throttler;
Expand Down Expand Up @@ -73,7 +73,7 @@ class EmailVerificationValidationAction
* @param RequestDataTransformer $transformer
* @param ServerSideValidator $validator
* @param UserInterface $userModel
* @param UserActivityLoggerInterface $logger
* @param ActivityRecorderInterface $logger
* @param Connection $db
* @param Throttler $throttler
*/
Expand All @@ -84,7 +84,7 @@ public function __construct(
protected RequestDataTransformer $transformer,
protected ServerSideValidator $validator,
protected UserInterface $userModel,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
protected Connection $db,
protected Throttler $throttler,
protected Config $config,
Expand Down Expand Up @@ -163,10 +163,11 @@ protected function handle(Request $request): void
$user->save();

// Create activity record
$this->logger->info("User {$user->user_name} verified it's account.", [
'type' => UserActivityTypes::VERIFIED,
'user_id' => $user->id,
]);
$this->logger->record(
user: $user,
type: UserActivityTypes::VERIFIED,
subject: $user
);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use UserFrosting\Sprinkle\Account\Authenticate\Interfaces\EmailVerificationProvider;
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
use UserFrosting\Sprinkle\Account\Exceptions\PasswordResetInvalidException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\Util\ApiResponse;
Expand Down Expand Up @@ -56,15 +56,15 @@ class ForgetPasswordSetPasswordAction
/**
* Inject dependencies.
*
* @param Translator $translator
* @param Config $config
* @param RouteParserInterface $routeParser
* @param EmailVerificationProvider $emailVerification
* @param RequestDataTransformer $transformer
* @param ServerSideValidator $validator
* @param Connection $db
* @param UserInterface $userModel
* @param UserActivityLoggerInterface $logger
* @param Translator $translator
* @param Config $config
* @param RouteParserInterface $routeParser
* @param EmailVerificationProvider $emailVerification
* @param RequestDataTransformer $transformer
* @param ServerSideValidator $validator
* @param Connection $db
* @param UserInterface $userModel
* @param ActivityRecorderInterface $logger
*/
public function __construct(
protected Translator $translator,
Expand All @@ -75,7 +75,7 @@ public function __construct(
protected ServerSideValidator $validator,
protected Connection $db,
protected UserInterface $userModel,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
) {
}

Expand Down Expand Up @@ -134,10 +134,11 @@ protected function handle(Request $request): void
$user->save();

// Create activity record
$this->logger->info("User {$user->user_name} reset it's password.", [
'type' => UserActivityTypes::PASSWORD_RESET,
'user_id' => $user->id,
]);
$this->logger->record(
user: $user,
type: UserActivityTypes::PASSWORD_RESET,
subject: $user
);
});
}

Expand Down
30 changes: 19 additions & 11 deletions packages/sprinkle-account/app/src/Controller/ProfileEditAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace UserFrosting\Sprinkle\Account\Controller;

use Illuminate\Database\Connection;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use UserFrosting\Fortress\RequestSchema;
Expand All @@ -23,7 +24,8 @@
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
use UserFrosting\Sprinkle\Account\Exceptions\ForbiddenException;
use UserFrosting\Sprinkle\Account\Exceptions\LocaleNotFoundException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\I18n\SiteLocale;
use UserFrosting\Sprinkle\Core\Util\ApiResponse;
Expand Down Expand Up @@ -53,9 +55,10 @@ public function __construct(
protected Translator $translator,
protected Authenticator $authenticator,
protected SiteLocale $locale,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
protected RequestDataTransformer $transformer,
protected ServerSideValidator $validator
protected ServerSideValidator $validator,
protected Connection $db
) {
}

Expand Down Expand Up @@ -124,14 +127,19 @@ protected function handle(Request $request): void
// Looks good, let's update with new values!
// Note that only fields listed in `profile-settings.yaml` will be
// permitted in $data, so this prevents the user from updating all columns in the DB
$currentUser->fill($data);
$currentUser->save();

// Create activity record
$this->logger->info("User {$currentUser->user_name} updated their profile settings.", [
'type' => 'update_profile_settings',
'user_id' => $currentUser->id,
]);
$this->db->transaction(function () use ($currentUser, $data): void {
$currentUser->fill($data);

// Record while the subject still contains its dirty attributes.
$this->logger->record(
user: $currentUser,
type: AccountActivityTypes::UPDATE_PROFILE_SETTINGS,
subject: $currentUser,
withProperties: true,
);

$currentUser->save();
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace UserFrosting\Sprinkle\Account\Controller;

use Illuminate\Database\Connection;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use UserFrosting\Config\Config;
Expand All @@ -25,7 +26,8 @@
use UserFrosting\Sprinkle\Account\Exceptions\EmailNotUniqueException;
use UserFrosting\Sprinkle\Account\Exceptions\ForbiddenException;
use UserFrosting\Sprinkle\Account\Exceptions\PasswordInvalidException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\Util\ApiResponse;

Expand Down Expand Up @@ -55,10 +57,11 @@ public function __construct(
protected Translator $translator,
protected Authenticator $authenticator,
protected Config $config,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
protected UserInterface $userModel,
protected RequestDataTransformer $transformer,
protected ServerSideValidator $validator
protected ServerSideValidator $validator,
protected Connection $db
) {
}

Expand Down Expand Up @@ -123,17 +126,28 @@ protected function handle(Request $request): void
throw new EmailNotUniqueException();
}

// Keep the old email for activity log
$oldEmail = $currentUser->email;

// Looks good, let's update with new values!
// Note that only fields listed in `account-email.yaml` will be
// permitted in $data, so this prevents the user from updating all columns in the DB
$currentUser->fill($data);
$currentUser->save();

// Create activity record
$this->logger->info("User {$currentUser->user_name} updated their account settings.", [
'type' => 'update_account_settings',
'user_id' => $currentUser->id,
]);
$this->db->transaction(function () use ($currentUser, $data, $oldEmail): void {
$currentUser->fill($data);

// Record while the subject still contains its dirty attributes.
$this->logger->record(
user: $currentUser,
type: AccountActivityTypes::UPDATE_EMAIL,
subject: $currentUser,
metadata: [
'old' => $oldEmail,
'new' => $data['email'],
]
);

$currentUser->save();
});
}

/**
Expand Down
Loading
Loading