mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-06-05 18:04:33 +08:00
@ -2,6 +2,7 @@
|
||||
|
||||
use BookStack\Api\ApiToken;
|
||||
use BookStack\Interfaces\Loggable;
|
||||
use BookStack\Interfaces\Sluggable;
|
||||
use BookStack\Model;
|
||||
use BookStack\Notifications\ResetPassword;
|
||||
use BookStack\Uploads\Image;
|
||||
@ -22,6 +23,7 @@ use Illuminate\Support\Collection;
|
||||
* Class User
|
||||
* @property string $id
|
||||
* @property string $name
|
||||
* @property string $slug
|
||||
* @property string $email
|
||||
* @property string $password
|
||||
* @property Carbon $created_at
|
||||
@ -32,7 +34,7 @@ use Illuminate\Support\Collection;
|
||||
* @property string $system_name
|
||||
* @property Collection $roles
|
||||
*/
|
||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable
|
||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, Sluggable
|
||||
{
|
||||
use Authenticatable, CanResetPassword, Notifiable;
|
||||
|
||||
@ -73,23 +75,21 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* Returns the default public user.
|
||||
* @return User
|
||||
*/
|
||||
public static function getDefault()
|
||||
public static function getDefault(): User
|
||||
{
|
||||
if (!is_null(static::$defaultUser)) {
|
||||
return static::$defaultUser;
|
||||
}
|
||||
|
||||
static::$defaultUser = static::where('system_name', '=', 'public')->first();
|
||||
static::$defaultUser = static::query()->where('system_name', '=', 'public')->first();
|
||||
return static::$defaultUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is the default public user.
|
||||
* @return bool
|
||||
*/
|
||||
public function isDefault()
|
||||
public function isDefault(): bool
|
||||
{
|
||||
return $this->system_name === 'public';
|
||||
}
|
||||
@ -116,12 +116,10 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* Check if the user has a role.
|
||||
* @param $role
|
||||
* @return mixed
|
||||
*/
|
||||
public function hasSystemRole($role)
|
||||
public function hasSystemRole(string $roleSystemName): bool
|
||||
{
|
||||
return $this->roles->pluck('system_name')->contains($role);
|
||||
return $this->roles->pluck('system_name')->contains($roleSystemName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,9 +183,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* Get the social account associated with this user.
|
||||
* @return HasMany
|
||||
*/
|
||||
public function socialAccounts()
|
||||
public function socialAccounts(): HasMany
|
||||
{
|
||||
return $this->hasMany(SocialAccount::class);
|
||||
}
|
||||
@ -208,11 +205,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user's avatar,
|
||||
* @param int $size
|
||||
* @return string
|
||||
* Returns a URL to the user's avatar
|
||||
*/
|
||||
public function getAvatar($size = 50)
|
||||
public function getAvatar(int $size = 50): string
|
||||
{
|
||||
$default = url('/user_avatar.png');
|
||||
$imageId = $this->image_id;
|
||||
@ -230,9 +225,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* Get the avatar for the user.
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function avatar()
|
||||
public function avatar(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Image::class, 'image_id');
|
||||
}
|
||||
@ -277,10 +271,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
/**
|
||||
* Get a shortened version of the user's name.
|
||||
* @param int $chars
|
||||
* @return string
|
||||
*/
|
||||
public function getShortName($chars = 8)
|
||||
public function getShortName(int $chars = 8): string
|
||||
{
|
||||
if (mb_strlen($this->name) <= $chars) {
|
||||
return $this->name;
|
||||
|
@ -9,6 +9,7 @@ use BookStack\Auth\Permissions\JointPermission;
|
||||
use BookStack\Entities\Tools\SearchIndex;
|
||||
use BookStack\Entities\Tools\SlugGenerator;
|
||||
use BookStack\Facades\Permissions;
|
||||
use BookStack\Interfaces\Sluggable;
|
||||
use BookStack\Model;
|
||||
use BookStack\Traits\HasCreatorAndUpdater;
|
||||
use BookStack\Traits\HasOwner;
|
||||
@ -37,7 +38,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @method static Builder withLastView()
|
||||
* @method static Builder withViewCount()
|
||||
*/
|
||||
abstract class Entity extends Model
|
||||
abstract class Entity extends Model implements Sluggable
|
||||
{
|
||||
use SoftDeletes;
|
||||
use HasCreatorAndUpdater;
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php namespace BookStack\Entities\Tools;
|
||||
|
||||
use BookStack\Entities\Models\Entity;
|
||||
use BookStack\Entities\Models\BookChild;
|
||||
use BookStack\Interfaces\Sluggable;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SlugGenerator
|
||||
@ -10,11 +11,11 @@ class SlugGenerator
|
||||
* Generate a fresh slug for the given entity.
|
||||
* The slug will generated so it does not conflict within the same parent item.
|
||||
*/
|
||||
public function generate(Entity $entity): string
|
||||
public function generate(Sluggable $model): string
|
||||
{
|
||||
$slug = $this->formatNameAsSlug($entity->name);
|
||||
while ($this->slugInUse($slug, $entity)) {
|
||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
||||
$slug = $this->formatNameAsSlug($model->name);
|
||||
while ($this->slugInUse($slug, $model)) {
|
||||
$slug .= '-' . Str::random(3);
|
||||
}
|
||||
return $slug;
|
||||
}
|
||||
@ -35,16 +36,16 @@ class SlugGenerator
|
||||
* Check if a slug is already in-use for this
|
||||
* type of model within the same parent.
|
||||
*/
|
||||
protected function slugInUse(string $slug, Entity $entity): bool
|
||||
protected function slugInUse(string $slug, Sluggable $model): bool
|
||||
{
|
||||
$query = $entity->newQuery()->where('slug', '=', $slug);
|
||||
$query = $model->newQuery()->where('slug', '=', $slug);
|
||||
|
||||
if ($entity instanceof BookChild) {
|
||||
$query->where('book_id', '=', $entity->book_id);
|
||||
if ($model instanceof BookChild) {
|
||||
$query->where('book_id', '=', $model->book_id);
|
||||
}
|
||||
|
||||
if ($entity->id) {
|
||||
$query->where('id', '!=', $entity->id);
|
||||
if ($model->id) {
|
||||
$query->where('id', '!=', $model->id);
|
||||
}
|
||||
|
||||
return $query->count() > 0;
|
||||
|
18
app/Interfaces/Sluggable.php
Normal file
18
app/Interfaces/Sluggable.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php namespace BookStack\Interfaces;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* Interface Sluggable
|
||||
*
|
||||
* Assigned to models that can have slugs.
|
||||
* Must have the below properties.
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $name
|
||||
* @method Builder newQuery
|
||||
*/
|
||||
interface Sluggable
|
||||
{
|
||||
|
||||
}
|
@ -12,9 +12,11 @@
|
||||
*/
|
||||
|
||||
$factory->define(\BookStack\Auth\User::class, function ($faker) {
|
||||
$name = $faker->name;
|
||||
return [
|
||||
'name' => $faker->name,
|
||||
'name' => $name,
|
||||
'email' => $faker->email,
|
||||
'slug' => \Illuminate\Support\Str::slug($name . '-' . \Illuminate\Support\Str::random(5)),
|
||||
'password' => Str::random(10),
|
||||
'remember_token' => Str::random(10),
|
||||
'email_confirmed' => 1
|
||||
|
50
database/migrations/2021_03_08_215138_add_user_slug.php
Normal file
50
database/migrations/2021_03_08_215138_add_user_slug.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class AddUserSlug extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('slug', 250);
|
||||
});
|
||||
|
||||
$slugMap = [];
|
||||
DB::table('users')->cursor()->each(function ($user) use (&$slugMap) {
|
||||
$userSlug = Str::slug($user->name);
|
||||
while (isset($slugMap[$userSlug])) {
|
||||
$userSlug = Str::slug($user->name . Str::random(4));
|
||||
}
|
||||
$slugMap[$userSlug] = true;
|
||||
|
||||
DB::table('users')
|
||||
->where('id', $user->id)
|
||||
->update(['slug' => $userSlug]);
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->unique('slug');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('slug');
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user