diff --git a/resources/views/form-components/permission.blade.php b/resources/views/form-components/permission.blade.php
index e7802ce40..49f5846c4 100644
--- a/resources/views/form-components/permission.blade.php
+++ b/resources/views/form-components/permission.blade.php
@@ -1,4 +1,4 @@
-@if($item->exists && $item instanceof \MoonShine\Models\MoonshineUser)
+@if($item->exists)
{{ $element->label() }}
@@ -25,10 +25,7 @@ class="form-group-inline"
type="checkbox"
name="permissions[{{ get_class($resource) }}][{{ $ability }}]"
value="1"
- :checked="($item->moonshineUserPermission
- && $item->moonshineUserPermission->permissions->has(get_class($resource))
- && isset($item->moonshineUserPermission->permissions[get_class($resource)][$ability])
- )"
+ :checked="$resource->checkUserPermissions($item, $ability)"
/>
diff --git a/resources/views/profile.blade.php b/resources/views/profile.blade.php
index 084c15282..aecde4ae0 100644
--- a/resources/views/profile.blade.php
+++ b/resources/views/profile.blade.php
@@ -15,24 +15,26 @@
name="name"
@class(['form-invalid' => $errors->has('name')])
placeholder="{{ trans('moonshine::ui.resource.name') }}"
- value="{{ old('name', auth(config('moonshine.auth.guard'))->user()->name) }}"
+ value="{{ old('name', auth(config('moonshine.auth.guard'))->user()
+ ->{config('moonshine.auth.fields.name', 'name')}) }}"
/>
$errors->has('email')])
- placeholder="{{ trans('moonshine::ui.resource.email') }}"
- autocomplete="email"
+ id="username"
+ type="text"
+ name="username"
+ @class(['form-invalid' => $errors->has('username')])
+ placeholder="{{ trans('moonshine::ui.login.username') }}"
+ autocomplete="username"
required
- value="{{ old('email', auth(config('moonshine.auth.guard'))->user()->email) }}"
+ value="{{ old('username', auth(config('moonshine.auth.guard'))->user()
+ ->{config('moonshine.auth.fields.username', 'email')}) }}"
/>
@@ -73,7 +75,8 @@
id="avatar"
@class(['form-invalid' => $errors->has('avatar')])
placeholder="{{ trans('moonshine::ui.resource.avatar') }}"
- :files="[auth(config('moonshine.auth.guard'))->user()->avatar ?? null]"
+ :files="[auth(config('moonshine.auth.guard'))->user()
+ ->{config('moonshine.auth.fields.avatar', 'avatar')} ?? null]"
dir="moonshine_users"
:path="Storage::disk('public')->url('/')"
:removable="true"
diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php
index 50360d83a..5c19b4f9d 100644
--- a/src/Commands/InstallCommand.php
+++ b/src/Commands/InstallCommand.php
@@ -22,12 +22,28 @@ public function handle(): void
{
$this->components->info('MoonShine installation ...');
+ $this->initVendorPublish();
+ $this->initStorage();
$this->initServiceProvider();
$this->initDirectories();
$this->initDashboard();
+ $this->initMigrations();
$this->components->info('Installation completed');
+ if (! app()->runningUnitTests()) {
+ $this->choice('Can you quickly star our GitHub repository? 🙏🏻', [
+ 'yes', 'no',
+ ], 'yes');
+
+ $this->components->bulletList([
+ 'Star or contribute to MoonShine: https://github.com/moonshine-software/moonshine',
+ 'MoonShine Documentation: https://moonshine.cutcode.dev',
+ 'CutCode: https://cutcode.dev',
+ ]);
+ }
+
+ $this->components->task('');
$this->components->info("Now run 'php artisan moonshine:user'");
}
@@ -40,7 +56,10 @@ protected function initDirectories(): void
$this->makeDir($this->getDirectory() . '/Resources');
$this->components->task('Resources directory created');
+ }
+ protected function initVendorPublish(): void
+ {
Artisan::call('vendor:publish', [
'--provider' => MoonShineServiceProvider::class,
'--force' => true,
@@ -48,10 +67,18 @@ protected function initDirectories(): void
$this->components->task('Vendor published');
+
+ }
+
+ protected function initStorage(): void
+ {
Artisan::call('storage:link');
$this->components->task('Storage link created');
+ }
+ protected function initMigrations(): void
+ {
if (config('moonshine.use_migrations', true)) {
Artisan::call('migrate');
@@ -59,18 +86,6 @@ protected function initDirectories(): void
} else {
$this->components->task('Installed without default migrations');
}
-
- if (! app()->runningUnitTests()) {
- $this->choice('Can you quickly star our GitHub repository? 🙏🏻', [
- 'yes', 'no',
- ], 'yes');
-
- $this->components->bulletList([
- 'Star or contribute to MoonShine: https://github.com/moonshine-software/moonshine',
- 'MoonShine Documentation: https://moonshine.cutcode.dev',
- 'CutCode: https://cutcode.dev',
- ]);
- }
}
/**
diff --git a/src/Commands/UserCommand.php b/src/Commands/UserCommand.php
index d57fdfcbb..76f0fe87e 100644
--- a/src/Commands/UserCommand.php
+++ b/src/Commands/UserCommand.php
@@ -15,15 +15,15 @@ class UserCommand extends MoonShineCommand
public function handle(): void
{
- $email = $this->ask('Email');
+ $username = $this->ask('Username('.config('moonshine.auth.fields.username', 'email').')');
$name = $this->ask('Name');
$password = $this->secret('Password');
- if ($email && $name && $password) {
+ if ($username && $name && $password) {
MoonShineAuth::model()->query()->create([
- 'email' => $email,
- 'name' => $name,
- 'password' => Hash::make($password),
+ config('moonshine.auth.fields.username', 'email') => $username,
+ config('moonshine.auth.fields.name', 'name') => $name,
+ config('moonshine.auth.fields.password', 'password') => Hash::make($password),
]);
$this->components->info('User is created');
diff --git a/src/Http/Controllers/ProfileController.php b/src/Http/Controllers/ProfileController.php
index 6d8b42b6b..1774bd19b 100644
--- a/src/Http/Controllers/ProfileController.php
+++ b/src/Http/Controllers/ProfileController.php
@@ -17,21 +17,25 @@ class ProfileController extends BaseController
public function store(ProfileFormRequest $request): RedirectResponse
{
$data = $request->validated();
+ $resultData = [
+ config('moonshine.auth.fields.username', 'email') => $data['username'],
+ config('moonshine.auth.fields.name', 'name') => $data['name'],
+ ];
if (isset($data['password']) && $data['password'] !== '') {
- $data['password'] = Hash::make($data['password']);
+ $resultData[config('moonshine.auth.fields.password', 'password')] = Hash::make($data['password']);
} else {
unset($data['password']);
}
if ($request->hasFile('avatar')) {
- $data['avatar'] = $request->file('avatar')
+ $resultData[config('moonshine.auth.fields.avatar', 'avatar')] = $request->file('avatar')
->store('moonshine_users', 'public');
} else {
- $data['avatar'] = $request->get('hidden_avatar');
+ $resultData[config('moonshine.auth.fields.avatar', 'avatar')] = $request->get('hidden_avatar');
}
- $request->user()->update($data);
+ $request->user()->update($resultData);
return back();
}
diff --git a/src/Http/Middleware/Authenticate.php b/src/Http/Middleware/Authenticate.php
index 06999315e..31ed522a4 100644
--- a/src/Http/Middleware/Authenticate.php
+++ b/src/Http/Middleware/Authenticate.php
@@ -6,15 +6,10 @@
use Closure;
-use function config;
-
use Illuminate\Http\Request;
use MoonShine\MoonShineAuth;
-use function redirect;
-use function route;
-
class Authenticate
{
public function handle($request, Closure $next)
diff --git a/src/Http/Requests/LoginFormRequest.php b/src/Http/Requests/LoginFormRequest.php
index 86086b3fd..5b49ba059 100644
--- a/src/Http/Requests/LoginFormRequest.php
+++ b/src/Http/Requests/LoginFormRequest.php
@@ -7,6 +7,7 @@
use Illuminate\Auth\Events\Lockout;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str;
+use Illuminate\Support\Stringable;
use Illuminate\Validation\ValidationException;
use MoonShine\MoonShineAuth;
use MoonShine\MoonShineRequest;
@@ -31,7 +32,7 @@ public function authorize(): bool
public function rules(): array
{
return [
- 'email' => ['required', 'email'],
+ 'username' => ['required'],
'password' => ['required'],
];
}
@@ -39,9 +40,12 @@ public function rules(): array
protected function prepareForValidation(): void
{
$this->merge([
- 'email' => (string)str(request('email'))
- ->lower()
- ->trim(),
+ 'username' => (string)str(request('username'))
+ ->when(
+ config('moonshine.auth.fields.username', 'email') === 'email',
+ fn (Stringable $str) => $str->lower()
+ )
+ ->squish(),
]);
}
@@ -56,14 +60,19 @@ public function authenticate(): void
{
$this->ensureIsNotRateLimited();
+ $credentials = [
+ config('moonshine.auth.fields.username', 'email') => $this->get('username'),
+ config('moonshine.auth.fields.password', 'password') => $this->get('password'),
+ ];
+
if (! MoonShineAuth::guard()->attempt(
- $this->only('email', 'password'),
+ $credentials,
$this->boolean('remember')
)) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
- 'email' => __('moonshine::auth.failed'),
+ 'username' => __('moonshine::auth.failed'),
]);
}
@@ -88,7 +97,7 @@ public function ensureIsNotRateLimited(): void
$seconds = RateLimiter::availableIn($this->throttleKey());
throw ValidationException::withMessages([
- 'email' => trans('auth.throttle', [
+ 'username' => trans('auth.throttle', [
'seconds' => $seconds,
'minutes' => ceil($seconds / 60),
]),
@@ -103,7 +112,7 @@ public function ensureIsNotRateLimited(): void
public function throttleKey(): string
{
return Str::transliterate(
- str($this->input('email').'|'.$this->ip())
+ str($this->input('username').'|'.$this->ip())
->lower()
);
}
diff --git a/src/Http/Requests/ProfileFormRequest.php b/src/Http/Requests/ProfileFormRequest.php
index dc48b5118..181d1f342 100644
--- a/src/Http/Requests/ProfileFormRequest.php
+++ b/src/Http/Requests/ProfileFormRequest.php
@@ -29,10 +29,12 @@ public function rules(): array
{
return [
'name' => ['required'],
- 'email' => [
+ 'username' => [
'required',
- Rule::unique(MoonShineAuth::model()?->getTable(), 'email')
- ->ignore(MoonShineAuth::guard()->id()),
+ Rule::unique(
+ MoonShineAuth::model()?->getTable(),
+ config('moonshine.auth.fields.username', 'email')
+ )->ignore(MoonShineAuth::guard()->id()),
],
'avatar' => ['image'],
'password' => 'sometimes|nullable|min:6|required_with:password_repeat|same:password_repeat',
diff --git a/src/Models/MoonshineUser.php b/src/Models/MoonshineUser.php
index c887762ce..9670a3f03 100644
--- a/src/Models/MoonshineUser.php
+++ b/src/Models/MoonshineUser.php
@@ -6,16 +6,17 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
-use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use MoonShine\Traits\Models\HasMoonShineChangeLog;
use MoonShine\Traits\Models\HasMoonShinePermissions;
+use MoonShine\Traits\Models\HasMoonShineSocialite;
class MoonshineUser extends Authenticatable
{
use HasMoonShineChangeLog;
use HasMoonShinePermissions;
+ use HasMoonShineSocialite;
use HasFactory;
use Notifiable;
@@ -33,10 +34,4 @@ public function moonshineUserRole(): BelongsTo
{
return $this->belongsTo(MoonshineUserRole::class);
}
-
-
- public function moonshineSocialites(): HasMany
- {
- return $this->hasMany(MoonshineSocialite::class);
- }
}
diff --git a/src/Traits/Models/HasMoonShineChangeLog.php b/src/Traits/Models/HasMoonShineChangeLog.php
index 13ecae555..456d48e37 100644
--- a/src/Traits/Models/HasMoonShineChangeLog.php
+++ b/src/Traits/Models/HasMoonShineChangeLog.php
@@ -5,10 +5,8 @@
namespace MoonShine\Traits\Models;
use Illuminate\Database\Eloquent\Model;
-
use Illuminate\Database\Eloquent\Relations\MorphMany;
use MoonShine\Models\MoonshineChangeLog;
-
use MoonShine\MoonShineAuth;
trait HasMoonShineChangeLog
@@ -37,8 +35,9 @@ public function createLog(): void
public function changeLogs(): MorphMany
{
- return $this->morphMany(MoonshineChangeLog::class, 'changelogable')
- ->where(['moonshine_user_id' => MoonShineAuth::guard()->id()])
- ->orderByDesc('created_at');
+ return $this->morphMany(
+ MoonshineChangeLog::class,
+ 'changelogable'
+ )->orderByDesc('created_at');
}
}
diff --git a/src/Traits/Models/HasMoonShinePermissions.php b/src/Traits/Models/HasMoonShinePermissions.php
index 7b47ad78d..6fcac1f8b 100644
--- a/src/Traits/Models/HasMoonShinePermissions.php
+++ b/src/Traits/Models/HasMoonShinePermissions.php
@@ -6,11 +6,16 @@
use Illuminate\Database\Eloquent\Relations\HasOne;
use MoonShine\Models\MoonshineUserPermission;
+use MoonShine\MoonShineAuth;
trait HasMoonShinePermissions
{
public function moonshineUserPermission(): HasOne
{
- return $this->hasOne(MoonshineUserPermission::class);
+ return $this->hasOne(
+ MoonshineUserPermission::class,
+ 'moonshine_user_id',
+ MoonShineAuth::model()?->getKeyName() ?? 'id'
+ );
}
}
diff --git a/src/Traits/Models/HasMoonShineSocialite.php b/src/Traits/Models/HasMoonShineSocialite.php
new file mode 100644
index 000000000..51cf90008
--- /dev/null
+++ b/src/Traits/Models/HasMoonShineSocialite.php
@@ -0,0 +1,21 @@
+hasMany(
+ MoonshineSocialite::class,
+ 'moonshine_user_id',
+ MoonShineAuth::model()?->getKeyName() ?? 'id'
+ );
+ }
+}
diff --git a/src/Traits/Resource/ResourceModelPolicy.php b/src/Traits/Resource/ResourceModelPolicy.php
index 12d76e4a3..25d0246f1 100644
--- a/src/Traits/Resource/ResourceModelPolicy.php
+++ b/src/Traits/Resource/ResourceModelPolicy.php
@@ -61,7 +61,7 @@ class_uses_recursive(MoonShineAuth::model()::class),
);
}
- private function checkUserPermissions(Model $user, string $ability): bool
+ public function checkUserPermissions(Model $user, string $ability): bool
{
if (! $this->hasUserPermissions()) {
return true;
diff --git a/tests/Feature/Commands/UserCommandTest.php b/tests/Feature/Commands/UserCommandTest.php
index 344e5429b..c8d42015f 100644
--- a/tests/Feature/Commands/UserCommandTest.php
+++ b/tests/Feature/Commands/UserCommandTest.php
@@ -10,7 +10,10 @@
it('reports progress', function () {
artisan(UserCommand::class)
- ->expectsQuestion('Email', 'example@example.com')
+ ->expectsQuestion(
+ 'Username('.config('moonshine.auth.fields.username', 'email').')',
+ 'example@example.com'
+ )
->expectsQuestion('Name', 'Admin')
->expectsQuestion('Password', 'example')
->expectsOutputToContain('User is created')
diff --git a/tests/Feature/Http/Controllers/AuthenticateControllerTest.php b/tests/Feature/Http/Controllers/AuthenticateControllerTest.php
index 894eda88f..f455fb87d 100644
--- a/tests/Feature/Http/Controllers/AuthenticateControllerTest.php
+++ b/tests/Feature/Http/Controllers/AuthenticateControllerTest.php
@@ -19,15 +19,15 @@
it('successful authenticated', function () {
$this->post(
route('moonshine.authenticate'),
- ['email' => $this->adminUser()->email, 'password' => 'test']
+ ['username' => $this->adminUser()->email, 'password' => 'test']
)->assertValid();
});
it('invalid credentials', function () {
$this->post(
route('moonshine.authenticate'),
- ['email' => $this->adminUser()->email, 'password' => 'invalid']
- )->assertInvalid(['email']);
+ ['username' => $this->adminUser()->email, 'password' => 'invalid']
+ )->assertInvalid(['username']);
});
it('logout', function () {
diff --git a/tests/Feature/Http/Controllers/ProfileControllerTest.php b/tests/Feature/Http/Controllers/ProfileControllerTest.php
index 3ec0cf4df..a33ff15d4 100644
--- a/tests/Feature/Http/Controllers/ProfileControllerTest.php
+++ b/tests/Feature/Http/Controllers/ProfileControllerTest.php
@@ -22,7 +22,7 @@
asAdmin()
->post(route('moonshine.profile.store'), [
'name' => $updatedName,
- 'email' => fake()->email(),
+ 'username' => fake()->email(),
])
->assertValid();
@@ -36,7 +36,7 @@
->post(route('moonshine.profile.store'), [
'name' => fake()->name(),
])
- ->assertInvalid(['email']);
+ ->assertInvalid(['username']);
});
it('avatar uploaded', function () {
@@ -45,7 +45,7 @@
asAdmin()
->post(route('moonshine.profile.store'), [
'name' => fake()->name(),
- 'email' => fake()->email(),
+ 'username' => fake()->email(),
'avatar' => $avatar,
])
->assertValid();
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 729b2fa73..302565b4f 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -4,7 +4,6 @@
namespace MoonShine\Tests;
-use Arr;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
@@ -27,23 +26,6 @@ class TestCase extends Orchestra
protected Resource $moonShineUserResource;
- protected function getEnvironmentSetUp($app)
- {
- $app['config']->set('auth.guards', [
- 'moonshine' => [
- 'driver' => 'session',
- 'provider' => 'moonshine',
- ],
- ]);
-
- $app['config']->set('auth.providers', [
- 'moonshine' => [
- 'driver' => 'eloquent',
- 'model' => MoonshineUser::class,
- ],
- ]);
- }
-
protected function setUp(): void
{
parent::setUp();
@@ -57,10 +39,6 @@ protected function setUp(): void
protected function performApplication(): static
{
- config(
- Arr::dot(config('moonshine.auth', []), 'auth.')
- );
-
$this->artisan('moonshine:install');
$this->artisan('config:clear');