Skip to content

Commit bb67804

Browse files
authored
Merge pull request #18153 from craftcms/feature/users-service
[6.x] Users service
2 parents 38e0a28 + 6ccc2c2 commit bb67804

File tree

297 files changed

+8070
-6305
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

297 files changed

+8070
-6305
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"commerceguys/addressing": "^2.1.1",
3939
"composer/semver": "^3.3.2",
4040
"craftcms/laravel-aliases": "^2.0",
41-
"craftcms/laravel-dependency-aware-cache": "^1.0",
41+
"craftcms/laravel-dependency-aware-cache": "^1.1",
4242
"craftcms/plugin-installer": "~1.6.0",
4343
"craftcms/server-check": "~5.0.1",
4444
"craftcms/yii2-adapter": "self.version",

database/Factories/UserFactory.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,49 @@
77
use CraftCms\Cms\Element\Models\Element;
88
use CraftCms\Cms\User\Models\User;
99
use Illuminate\Database\Eloquent\Factories\Factory;
10+
use Override;
1011

1112
final class UserFactory extends Factory
1213
{
1314
protected $model = User::class;
1415

15-
#[\Override]
16-
public function definition()
16+
#[Override]
17+
public function definition(): array
1718
{
1819
return [
1920
'id' => Element::factory(),
2021
'username' => $this->faker->userName(),
22+
'email' => $this->faker->email(),
2123
];
2224
}
25+
26+
public function pending(): self
27+
{
28+
return $this->state(fn () => [
29+
'pending' => true,
30+
]);
31+
}
32+
33+
public function locked(): self
34+
{
35+
return $this->state(fn () => [
36+
'locked' => true,
37+
'invalidLoginCount' => 2,
38+
'lockoutDate' => now(),
39+
]);
40+
}
41+
42+
public function active(): self
43+
{
44+
return $this->state(fn () => [
45+
'active' => true,
46+
]);
47+
}
48+
49+
public function suspended(): self
50+
{
51+
return $this->state(fn () => [
52+
'suspended' => true,
53+
]);
54+
}
2355
}

routes/actions.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,15 @@
3333
use CraftCms\Cms\Http\Controllers\StructuresController;
3434
use CraftCms\Cms\Http\Controllers\Updates\UpdaterController;
3535
use CraftCms\Cms\Http\Controllers\Updates\UpdatesController;
36+
use CraftCms\Cms\Http\Controllers\Users\ActivateController;
37+
use CraftCms\Cms\Http\Controllers\Users\EnableController;
3638
use CraftCms\Cms\Http\Controllers\Users\ImpersonationController;
3739
use CraftCms\Cms\Http\Controllers\Users\PermissionsController;
40+
use CraftCms\Cms\Http\Controllers\Users\PhotoController;
41+
use CraftCms\Cms\Http\Controllers\Users\PreferencesController;
42+
use CraftCms\Cms\Http\Controllers\Users\SuspendController;
43+
use CraftCms\Cms\Http\Controllers\Users\UnlockController;
44+
use CraftCms\Cms\Http\Controllers\Users\UsersController;
3845
use CraftCms\Cms\Http\Controllers\Utilities\ClearCachesController;
3946
use CraftCms\Cms\Http\Controllers\Utilities\DbBackupController;
4047
use CraftCms\Cms\Http\Controllers\Utilities\DeprecationErrorsController;
@@ -72,6 +79,8 @@
7279

7380
Route::middleware(['auth:craft'])->group(function () {
7481
Route::post('entries/save-entry', StoreEntryController::class);
82+
Route::post('users/save-address', [\CraftCms\Cms\Http\Controllers\Users\AddressesController::class, 'store']);
83+
Route::post('users/delete-address', [\CraftCms\Cms\Http\Controllers\Users\AddressesController::class, 'destroy']);
7584
});
7685

7786
Route::middleware([RequireToken::class])->group(function () {
@@ -270,6 +279,17 @@
270279
});
271280

272281
Route::post('users/save-permissions', [PermissionsController::class, 'store']);
282+
Route::post('users/save-preferences', [PreferencesController::class, 'store']);
283+
Route::post('users/activate-user', [ActivateController::class, 'activate']);
284+
Route::post('users/deactivate-user', [ActivateController::class, 'deactivate']);
285+
Route::post('users/suspend-user', [SuspendController::class, 'suspend']);
286+
Route::post('users/unsuspend-user', [SuspendController::class, 'unsuspend']);
287+
Route::post('users/enable-user', EnableController::class);
288+
Route::post('users/unlock-user', UnlockController::class);
289+
Route::post('users/delete-user', [UsersController::class, 'destroy']);
290+
Route::post('users/render-photo-input', [PhotoController::class, 'renderInput']);
291+
Route::post('users/upload-user-photo', [PhotoController::class, 'upload']);
292+
Route::post('users/delete-user-photo', [PhotoController::class, 'destroy']);
273293

274294
// User groups
275295
Route::middleware([

routes/cp.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919
use CraftCms\Cms\Http\Controllers\Settings\UserGroupsController;
2020
use CraftCms\Cms\Http\Controllers\Settings\UserSettingsController;
2121
use CraftCms\Cms\Http\Controllers\Updates\UpdaterController;
22+
use CraftCms\Cms\Http\Controllers\Users\AddressesController;
23+
use CraftCms\Cms\Http\Controllers\Users\PasskeysController;
24+
use CraftCms\Cms\Http\Controllers\Users\PasswordController;
2225
use CraftCms\Cms\Http\Controllers\Users\PermissionsController;
26+
use CraftCms\Cms\Http\Controllers\Users\PreferencesController;
27+
use CraftCms\Cms\Http\Controllers\Users\UsersController;
2328
use CraftCms\Cms\Http\Controllers\Utilities\UtilitiesController;
2429
use CraftCms\Cms\Http\Middleware\HandleInertiaRequests;
2530
use CraftCms\Cms\Http\Middleware\RequireAdmin;
@@ -62,8 +67,23 @@
6267
/**
6368
* Users
6469
*/
65-
Route::get('users/{user}/permissions', [PermissionsController::class, 'index']);
70+
Route::get('myaccount', [UsersController::class, 'edit']);
71+
Route::get('myaccount/addresses', [AddressesController::class, 'index']);
6672
Route::get('myaccount/permissions', [PermissionsController::class, 'index']);
73+
Route::get('myaccount/passkeys', [PasskeysController::class, 'index']);
74+
Route::get('myaccount/password', [PasswordController::class, 'index']);
75+
Route::get('myaccount/preferences', [PreferencesController::class, 'index']);
76+
77+
Route::middleware([
78+
RequireEdition::class.':'.Edition::Team->value,
79+
])->group(function () {
80+
Route::get('users/new', [UsersController::class, 'create']);
81+
Route::get('users/{userId}', [UsersController::class, 'edit'])->whereNumber('userId');
82+
Route::get('users/{userId}/addresses', [AddressesController::class, 'index'])->whereNumber('userId');
83+
Route::get('users/{userId}/permissions', [PermissionsController::class, 'index']);
84+
});
85+
86+
Route::get('users/{slug?}', [UsersController::class, 'index']);
6787

6888
/**
6989
* Routes that require admin, but do not require admin changes
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CraftCms\Cms\Auth\Events;
6+
7+
use CraftCms\Cms\User\Elements\User;
8+
9+
final class LoginUserRetrieved
10+
{
11+
public function __construct(
12+
public string $loginName,
13+
public User $user,
14+
) {}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CraftCms\Cms\Auth\Events;
6+
7+
use CraftCms\Cms\User\Elements\User;
8+
9+
final class RetrievingLoginUser
10+
{
11+
public function __construct(
12+
public string $loginName,
13+
public ?User $user = null,
14+
) {}
15+
}
Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
declare(strict_types=1);
44

5-
namespace CraftCms\Cms\User;
5+
namespace CraftCms\Cms\Auth;
66

7-
use Closure;
8-
use craft\elements\User;
7+
use CraftCms\Cms\Auth\Events\LoginUserRetrieved;
8+
use CraftCms\Cms\Auth\Events\RetrievingLoginUser;
99
use CraftCms\Cms\Database\Table;
10+
use CraftCms\Cms\Support\Facades\Users;
11+
use CraftCms\Cms\User\Elements\User;
1012
use Illuminate\Contracts\Auth\Authenticatable;
1113
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
12-
use Illuminate\Contracts\Support\Arrayable;
1314
use Illuminate\Support\Facades\DB;
15+
use Illuminate\Support\Facades\Event;
1416
use SensitiveParameter;
1517

1618
final readonly class UserProvider implements \Illuminate\Contracts\Auth\UserProvider
@@ -55,7 +57,7 @@ public function updateRememberToken(Authenticatable $user, #[SensitiveParameter]
5557
{
5658
DB::table(Table::USERS)
5759
->where('id', $user->getAuthIdentifier())
58-
->update([$user->getRememberTokenName() => $token]);
60+
->update(['rememberToken' => $token]);
5961
}
6062

6163
/**
@@ -73,19 +75,23 @@ public function retrieveByCredentials(#[SensitiveParameter] array $credentials):
7375
return null;
7476
}
7577

76-
$query = User::find();
78+
$loginName = $credentials['loginName'];
7779

78-
foreach ($credentials as $key => $value) {
79-
if (is_array($value) || $value instanceof Arrayable) {
80-
$query->whereIn($key, $value);
81-
} elseif ($value instanceof Closure) {
82-
$value($query);
83-
} else {
84-
$query->where($key, $value);
85-
}
80+
$user = null;
81+
if (Event::hasListeners(RetrievingLoginUser::class)) {
82+
Event::dispatch($event = new RetrievingLoginUser($loginName));
83+
$user = $event->user;
8684
}
8785

88-
return $query->first();
86+
$user ??= Users::getUserByUsernameOrEmail($loginName);
87+
88+
if (Event::hasListeners(LoginUserRetrieved::class)) {
89+
Event::dispatch($event = new LoginUserRetrieved($loginName, $user));
90+
91+
return $event->user;
92+
}
93+
94+
return $user;
8995
}
9096

9197
/**

src/Console/Commands/Utils/AsciiFilenamesCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
namespace CraftCms\Cms\Console\Commands\Utils;
66

77
use Craft;
8-
use craft\errors\InvalidElementException;
98
use craft\helpers\FileHelper;
109
use CraftCms\Cms\Asset\Elements\Asset;
1110
use CraftCms\Cms\Config\GeneralConfig;
1211
use CraftCms\Cms\Console\CraftCommand;
12+
use CraftCms\Cms\Element\Exceptions\InvalidElementException;
1313
use Exception;
1414
use Illuminate\Console\Command;
1515
use Illuminate\Support\Facades\DB;

src/Dashboard/Dashboard.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ public function changeWidgetColspan(int $widgetId, int $colspan): bool
296296
*/
297297
private function addDefaultUserWidgets(): void
298298
{
299-
/** @var ?\craft\elements\User $user */
300299
$user = Auth::user();
301300

302301
// Recent Entries widget

src/Dashboard/Widgets/CraftSupport.php

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use CraftCms\Cms\Plugin\Plugins;
1313
use CraftCms\Cms\Support\PHP;
1414
use Illuminate\Support\Facades\Auth;
15+
use Override;
1516

1617
use function CraftCms\Cms\normalizeVersion;
1718
use function CraftCms\Cms\t;
@@ -29,7 +30,7 @@ public function __construct(
2930
/**
3031
* {@inheritdoc}
3132
*/
32-
#[\Override]
33+
#[Override]
3334
public static function displayName(): string
3435
{
3536
return t('Craft Support');
@@ -38,17 +39,17 @@ public static function displayName(): string
3839
/**
3940
* {@inheritdoc}
4041
*/
41-
#[\Override]
42+
#[Override]
4243
public static function isSelectable(): bool
4344
{
4445
// Only admins get the Craft Support widget.
45-
return parent::isSelectable() && Auth::user()->isAdmin();
46+
return parent::isSelectable() && Auth::user()?->isAdmin();
4647
}
4748

4849
/**
4950
* {@inheritdoc}
5051
*/
51-
#[\Override]
52+
#[Override]
5253
protected static function allowMultipleInstances(): bool
5354
{
5455
return false;
@@ -57,7 +58,7 @@ protected static function allowMultipleInstances(): bool
5758
/**
5859
* {@inheritdoc}
5960
*/
60-
#[\Override]
61+
#[Override]
6162
public static function icon(): string
6263
{
6364
return 'life-ring';
@@ -66,7 +67,7 @@ public static function icon(): string
6667
/**
6768
* {@inheritdoc}
6869
*/
69-
#[\Override]
70+
#[Override]
7071
public function getTitle(): ?string
7172
{
7273
return null;
@@ -75,11 +76,11 @@ public function getTitle(): ?string
7576
/**
7677
* {@inheritdoc}
7778
*/
78-
#[\Override]
79+
#[Override]
7980
public function getBodyHtml(): ?string
8081
{
8182
// Only admins get the Craft Support widget.
82-
if (! Auth::user()->isAdmin()) {
83+
if (! Auth::user()?->isAdmin()) {
8384
return null;
8485
}
8586

@@ -126,7 +127,7 @@ public function getBodyHtml(): ?string
126127
EOD;
127128

128129
$view->registerJsWithVars(fn ($id, $settings) => <<<JS
129-
new Craft.CraftSupportWidget($id, $settings);
130+
new Craft.CraftSupportWidget($id, $settings)
130131
JS, [
131132
$this->id,
132133
[

0 commit comments

Comments
 (0)