Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 app/Auth/Access/SocialAuthService.php
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public function fillSocialAccount(string $socialDriver, SocialUser $socialUser):
* Detach a social account from a user.
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function detachSocialAccount(string $socialDriver)
public function detachSocialAccount(string $socialDriver): void
{
user()->socialAccounts()->where('driver', '=', $socialDriver)->delete();
}
Expand Down
46 changes: 24 additions & 22 deletions app/Auth/User.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<?php namespace BookStack\Auth;

use BookStack\Api\ApiToken;
use BookStack\Entities\Tools\SlugGenerator;
use BookStack\Interfaces\Loggable;
use BookStack\Interfaces\Sluggable;
use BookStack\Model;
use BookStack\Notifications\ResetPassword;
use BookStack\Uploads\Image;
Expand All @@ -22,6 +24,7 @@
* Class User
* @property string $id
* @property string $name
* @property string $slug
* @property string $email
* @property string $password
* @property Carbon $created_at
Expand All @@ -32,7 +35,7 @@
* @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;

Expand Down Expand Up @@ -73,23 +76,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';
}
Expand All @@ -116,12 +117,10 @@ public function hasRole($roleId): bool

/**
* 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);
}

/**
Expand Down Expand Up @@ -185,9 +184,8 @@ public function attachRole(Role $role)

/**
* Get the social account associated with this user.
* @return HasMany
*/
public function socialAccounts()
public function socialAccounts(): HasMany
{
return $this->hasMany(SocialAccount::class);
}
Expand All @@ -208,11 +206,9 @@ public function hasSocialAccount($socialDriver = false)
}

/**
* 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;
Expand All @@ -230,9 +226,8 @@ public function getAvatar($size = 50)

/**
* Get the avatar for the user.
* @return BelongsTo
*/
public function avatar()
public function avatar(): BelongsTo
{
return $this->belongsTo(Image::class, 'image_id');
}
Expand Down Expand Up @@ -272,15 +267,13 @@ public function getEditUrl(string $path = ''): string
*/
public function getProfileUrl(): string
{
return url('/user/' . $this->id);
return url('/user/' . $this->slug);
}

/**
* 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;
Expand Down Expand Up @@ -311,4 +304,13 @@ public function logDescriptor(): string
{
return "({$this->id}) {$this->name}";
}

/**
* @inheritDoc
*/
public function refreshSlug(): string
{
$this->slug = app(SlugGenerator::class)->generate($this);
return $this->slug;
}
}
16 changes: 15 additions & 1 deletion app/Auth/UserRepo.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ public function getById(int $id): User
return User::query()->findOrFail($id);
}

/**
* Get a user by their slug.
*/
public function getBySlug(string $slug): User
{
return User::query()->where('slug', '=', $slug)->firstOrFail();
}

/**
* Get all the users with their permissions.
*/
Expand Down Expand Up @@ -159,7 +167,13 @@ public function create(array $data, bool $emailConfirmed = false): User
'email_confirmed' => $emailConfirmed,
'external_auth_id' => $data['external_auth_id'] ?? '',
];
return User::query()->forceCreate($details);

$user = new User();
$user->forceFill($details);
$user->refreshSlug();
$user->save();

return $user;
}

/**
Expand Down
7 changes: 4 additions & 3 deletions app/Entities/Models/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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;
Expand Down Expand Up @@ -37,7 +38,7 @@
* @method static Builder withLastView()
* @method static Builder withViewCount()
*/
abstract class Entity extends Model
abstract class Entity extends Model implements Sluggable
{
use SoftDeletes;
use HasCreatorAndUpdater;
Expand Down Expand Up @@ -289,11 +290,11 @@ public function indexForSearch()
}

/**
* Generate and set a new URL slug for this model.
* @inheritdoc
*/
public function refreshSlug(): string
{
$this->slug = (new SlugGenerator)->generate($this);
$this->slug = app(SlugGenerator::class)->generate($this);
return $this->slug;
}
}
21 changes: 9 additions & 12 deletions app/Entities/Tools/SearchRunner.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php namespace BookStack\Entities\Tools;

use BookStack\Auth\Permissions\PermissionService;
use BookStack\Auth\User;
use BookStack\Entities\EntityProvider;
use BookStack\Entities\Models\Entity;
use Illuminate\Database\Connection;
Expand Down Expand Up @@ -270,24 +271,20 @@ protected function filterCreatedBefore(EloquentBuilder $query, Entity $model, $i

protected function filterCreatedBy(EloquentBuilder $query, Entity $model, $input)
{
if (!is_numeric($input) && $input !== 'me') {
return;
}
if ($input === 'me') {
$input = user()->id;
$userSlug = $input === 'me' ? user()->slug : trim($input);
$user = User::query()->where('slug', '=', $userSlug)->first(['id']);
if ($user) {
$query->where('created_by', '=', $user->id);
}
$query->where('created_by', '=', $input);
}

protected function filterUpdatedBy(EloquentBuilder $query, Entity $model, $input)
{
if (!is_numeric($input) && $input !== 'me') {
return;
}
if ($input === 'me') {
$input = user()->id;
$userSlug = $input === 'me' ? user()->slug : trim($input);
$user = User::query()->where('slug', '=', $userSlug)->first(['id']);
if ($user) {
$query->where('updated_by', '=', $user->id);
}
$query->where('updated_by', '=', $input);
}

protected function filterInName(EloquentBuilder $query, Entity $model, $input)
Expand Down
23 changes: 12 additions & 11 deletions app/Entities/Tools/SlugGenerator.php
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
}
Expand All @@ -35,16 +36,16 @@ protected function formatNameAsSlug(string $name): string
* 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;
Expand Down
13 changes: 5 additions & 8 deletions app/Http/Controllers/Auth/SocialController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
use BookStack\Exceptions\SocialSignInException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\User as SocialUser;

Expand All @@ -31,12 +29,11 @@ public function __construct(SocialAuthService $socialAuthService, RegistrationSe
$this->registrationService = $registrationService;
}


/**
* Redirect to the relevant social site.
* @throws \BookStack\Exceptions\SocialDriverNotConfigured
* @throws SocialDriverNotConfigured
*/
public function getSocialLogin(string $socialDriver)
public function login(string $socialDriver)
{
session()->put('social-callback', 'login');
return $this->socialAuthService->startLogIn($socialDriver);
Expand All @@ -47,7 +44,7 @@ public function getSocialLogin(string $socialDriver)
* @throws SocialDriverNotConfigured
* @throws UserRegistrationException
*/
public function socialRegister(string $socialDriver)
public function register(string $socialDriver)
{
$this->registrationService->ensureRegistrationAllowed();
session()->put('social-callback', 'register');
Expand All @@ -60,7 +57,7 @@ public function socialRegister(string $socialDriver)
* @throws SocialDriverNotConfigured
* @throws UserRegistrationException
*/
public function socialCallback(Request $request, string $socialDriver)
public function callback(Request $request, string $socialDriver)
{
if (!session()->has('social-callback')) {
throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
Expand Down Expand Up @@ -99,7 +96,7 @@ public function socialCallback(Request $request, string $socialDriver)
/**
* Detach a social account from a user.
*/
public function detachSocialAccount(string $socialDriver)
public function detach(string $socialDriver)
{
$this->socialAuthService->detachSocialAccount($socialDriver);
session()->flash('success', trans('settings.users_social_disconnected', ['socialAccount' => Str::title($socialDriver)]));
Expand Down
3 changes: 0 additions & 3 deletions app/Http/Controllers/SearchController.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<?php namespace BookStack\Http\Controllers;

use BookStack\Actions\ViewService;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Tools\SearchRunner;
use BookStack\Entities\Tools\ShelfContext;
use BookStack\Entities\Tools\SearchOptions;
Expand Down
Loading