mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-06-04 00:44:41 +08:00
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
use BookStack\Interfaces\Loggable;
|
use BookStack\Interfaces\Loggable;
|
||||||
|
use BookStack\Interfaces\Sluggable;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
use BookStack\Notifications\ResetPassword;
|
use BookStack\Notifications\ResetPassword;
|
||||||
use BookStack\Uploads\Image;
|
use BookStack\Uploads\Image;
|
||||||
@ -22,6 +23,7 @@ use Illuminate\Support\Collection;
|
|||||||
* Class User
|
* Class User
|
||||||
* @property string $id
|
* @property string $id
|
||||||
* @property string $name
|
* @property string $name
|
||||||
|
* @property string $slug
|
||||||
* @property string $email
|
* @property string $email
|
||||||
* @property string $password
|
* @property string $password
|
||||||
* @property Carbon $created_at
|
* @property Carbon $created_at
|
||||||
@ -32,7 +34,7 @@ use Illuminate\Support\Collection;
|
|||||||
* @property string $system_name
|
* @property string $system_name
|
||||||
* @property Collection $roles
|
* @property Collection $roles
|
||||||
*/
|
*/
|
||||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable
|
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, Sluggable
|
||||||
{
|
{
|
||||||
use Authenticatable, CanResetPassword, Notifiable;
|
use Authenticatable, CanResetPassword, Notifiable;
|
||||||
|
|
||||||
@ -73,23 +75,21 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default public user.
|
* Returns the default public user.
|
||||||
* @return User
|
|
||||||
*/
|
*/
|
||||||
public static function getDefault()
|
public static function getDefault(): User
|
||||||
{
|
{
|
||||||
if (!is_null(static::$defaultUser)) {
|
if (!is_null(static::$defaultUser)) {
|
||||||
return 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;
|
return static::$defaultUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the user is the default public user.
|
* Check if the user is the default public user.
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
public function isDefault()
|
public function isDefault(): bool
|
||||||
{
|
{
|
||||||
return $this->system_name === 'public';
|
return $this->system_name === 'public';
|
||||||
}
|
}
|
||||||
@ -116,12 +116,10 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the user has a role.
|
* 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.
|
* Get the social account associated with this user.
|
||||||
* @return HasMany
|
|
||||||
*/
|
*/
|
||||||
public function socialAccounts()
|
public function socialAccounts(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(SocialAccount::class);
|
return $this->hasMany(SocialAccount::class);
|
||||||
}
|
}
|
||||||
@ -208,11 +205,9 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the user's avatar,
|
* Returns a URL to the user's avatar
|
||||||
* @param int $size
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getAvatar($size = 50)
|
public function getAvatar(int $size = 50): string
|
||||||
{
|
{
|
||||||
$default = url('/user_avatar.png');
|
$default = url('/user_avatar.png');
|
||||||
$imageId = $this->image_id;
|
$imageId = $this->image_id;
|
||||||
@ -230,9 +225,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the avatar for the user.
|
* Get the avatar for the user.
|
||||||
* @return BelongsTo
|
|
||||||
*/
|
*/
|
||||||
public function avatar()
|
public function avatar(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Image::class, 'image_id');
|
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.
|
* 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) {
|
if (mb_strlen($this->name) <= $chars) {
|
||||||
return $this->name;
|
return $this->name;
|
||||||
|
@ -9,6 +9,7 @@ use BookStack\Auth\Permissions\JointPermission;
|
|||||||
use BookStack\Entities\Tools\SearchIndex;
|
use BookStack\Entities\Tools\SearchIndex;
|
||||||
use BookStack\Entities\Tools\SlugGenerator;
|
use BookStack\Entities\Tools\SlugGenerator;
|
||||||
use BookStack\Facades\Permissions;
|
use BookStack\Facades\Permissions;
|
||||||
|
use BookStack\Interfaces\Sluggable;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
use BookStack\Traits\HasCreatorAndUpdater;
|
use BookStack\Traits\HasCreatorAndUpdater;
|
||||||
use BookStack\Traits\HasOwner;
|
use BookStack\Traits\HasOwner;
|
||||||
@ -37,7 +38,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||||||
* @method static Builder withLastView()
|
* @method static Builder withLastView()
|
||||||
* @method static Builder withViewCount()
|
* @method static Builder withViewCount()
|
||||||
*/
|
*/
|
||||||
abstract class Entity extends Model
|
abstract class Entity extends Model implements Sluggable
|
||||||
{
|
{
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
use HasCreatorAndUpdater;
|
use HasCreatorAndUpdater;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php namespace BookStack\Entities\Tools;
|
<?php namespace BookStack\Entities\Tools;
|
||||||
|
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\BookChild;
|
||||||
|
use BookStack\Interfaces\Sluggable;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class SlugGenerator
|
class SlugGenerator
|
||||||
@ -10,11 +11,11 @@ class SlugGenerator
|
|||||||
* Generate a fresh slug for the given entity.
|
* Generate a fresh slug for the given entity.
|
||||||
* The slug will generated so it does not conflict within the same parent item.
|
* 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);
|
$slug = $this->formatNameAsSlug($model->name);
|
||||||
while ($this->slugInUse($slug, $entity)) {
|
while ($this->slugInUse($slug, $model)) {
|
||||||
$slug .= '-' . substr(md5(rand(1, 500)), 0, 3);
|
$slug .= '-' . Str::random(3);
|
||||||
}
|
}
|
||||||
return $slug;
|
return $slug;
|
||||||
}
|
}
|
||||||
@ -35,16 +36,16 @@ class SlugGenerator
|
|||||||
* Check if a slug is already in-use for this
|
* Check if a slug is already in-use for this
|
||||||
* type of model within the same parent.
|
* 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) {
|
if ($model instanceof BookChild) {
|
||||||
$query->where('book_id', '=', $entity->book_id);
|
$query->where('book_id', '=', $model->book_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity->id) {
|
if ($model->id) {
|
||||||
$query->where('id', '!=', $entity->id);
|
$query->where('id', '!=', $model->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->count() > 0;
|
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) {
|
$factory->define(\BookStack\Auth\User::class, function ($faker) {
|
||||||
|
$name = $faker->name;
|
||||||
return [
|
return [
|
||||||
'name' => $faker->name,
|
'name' => $name,
|
||||||
'email' => $faker->email,
|
'email' => $faker->email,
|
||||||
|
'slug' => \Illuminate\Support\Str::slug($name . '-' . \Illuminate\Support\Str::random(5)),
|
||||||
'password' => Str::random(10),
|
'password' => Str::random(10),
|
||||||
'remember_token' => Str::random(10),
|
'remember_token' => Str::random(10),
|
||||||
'email_confirmed' => 1
|
'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