mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-08-26 20:22:18 +00:00
Merge pull request #2360 from BookStackApp/activity_revamp
Tracked activity update
This commit is contained in:
commit
65ddd16532
@ -5,16 +5,17 @@ namespace BookStack\Actions;
|
|||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use BookStack\Entities\Entity;
|
use BookStack\Entities\Entity;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $key
|
* @property string $type
|
||||||
* @property User $user
|
* @property User $user
|
||||||
* @property Entity $entity
|
* @property Entity $entity
|
||||||
* @property string $extra
|
* @property string $detail
|
||||||
* @property string $entity_type
|
* @property string $entity_type
|
||||||
* @property int $entity_id
|
* @property int $entity_id
|
||||||
* @property int $user_id
|
* @property int $user_id
|
||||||
* @property int $book_id
|
|
||||||
*/
|
*/
|
||||||
class Activity extends Model
|
class Activity extends Model
|
||||||
{
|
{
|
||||||
@ -32,20 +33,28 @@ class Activity extends Model
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the user this activity relates to.
|
* Get the user this activity relates to.
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
||||||
*/
|
*/
|
||||||
public function user()
|
public function user(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns text from the language files, Looks up by using the
|
* Returns text from the language files, Looks up by using the activity key.
|
||||||
* activity key.
|
|
||||||
*/
|
*/
|
||||||
public function getText()
|
public function getText(): string
|
||||||
{
|
{
|
||||||
return trans('activities.' . $this->key);
|
return trans('activities.' . $this->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this activity is intended to be for an entity.
|
||||||
|
*/
|
||||||
|
public function isForEntity(): bool
|
||||||
|
{
|
||||||
|
return Str::startsWith($this->type, [
|
||||||
|
'page_', 'chapter_', 'book_', 'bookshelf_'
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,6 +62,6 @@ class Activity extends Model
|
|||||||
*/
|
*/
|
||||||
public function isSimilarTo(Activity $activityB): bool
|
public function isSimilarTo(Activity $activityB): bool
|
||||||
{
|
{
|
||||||
return [$this->key, $this->entity_type, $this->entity_id] === [$activityB->key, $activityB->entity_type, $activityB->entity_id];
|
return [$this->type, $this->entity_type, $this->entity_id] === [$activityB->type, $activityB->entity_type, $activityB->entity_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,58 +2,59 @@
|
|||||||
|
|
||||||
use BookStack\Auth\Permissions\PermissionService;
|
use BookStack\Auth\Permissions\PermissionService;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
|
use BookStack\Entities\Chapter;
|
||||||
use BookStack\Entities\Entity;
|
use BookStack\Entities\Entity;
|
||||||
|
use BookStack\Entities\Page;
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class ActivityService
|
class ActivityService
|
||||||
{
|
{
|
||||||
protected $activity;
|
protected $activity;
|
||||||
protected $user;
|
|
||||||
protected $permissionService;
|
protected $permissionService;
|
||||||
|
|
||||||
/**
|
|
||||||
* ActivityService constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(Activity $activity, PermissionService $permissionService)
|
public function __construct(Activity $activity, PermissionService $permissionService)
|
||||||
{
|
{
|
||||||
$this->activity = $activity;
|
$this->activity = $activity;
|
||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
$this->user = user();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add activity data to database.
|
* Add activity data to database for an entity.
|
||||||
*/
|
*/
|
||||||
public function add(Entity $entity, string $activityKey, ?int $bookId = null)
|
public function addForEntity(Entity $entity, string $type)
|
||||||
{
|
{
|
||||||
$activity = $this->newActivityForUser($activityKey, $bookId);
|
$activity = $this->newActivityForUser($type);
|
||||||
$entity->activity()->save($activity);
|
$entity->activity()->save($activity);
|
||||||
$this->setNotification($activityKey);
|
$this->setNotification($type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a activity history with a message, without binding to a entity.
|
* Add a generic activity event to the database.
|
||||||
|
* @param string|Loggable $detail
|
||||||
*/
|
*/
|
||||||
public function addMessage(string $activityKey, string $message, ?int $bookId = null)
|
public function add(string $type, $detail = '')
|
||||||
{
|
{
|
||||||
$this->newActivityForUser($activityKey, $bookId)->forceFill([
|
if ($detail instanceof Loggable) {
|
||||||
'extra' => $message
|
$detail = $detail->logDescriptor();
|
||||||
])->save();
|
}
|
||||||
|
|
||||||
$this->setNotification($activityKey);
|
$activity = $this->newActivityForUser($type);
|
||||||
|
$activity->detail = $detail;
|
||||||
|
$activity->save();
|
||||||
|
$this->setNotification($type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a new activity instance for the current user.
|
* Get a new activity instance for the current user.
|
||||||
*/
|
*/
|
||||||
protected function newActivityForUser(string $key, ?int $bookId = null): Activity
|
protected function newActivityForUser(string $type): Activity
|
||||||
{
|
{
|
||||||
return $this->activity->newInstance()->forceFill([
|
return $this->activity->newInstance()->forceFill([
|
||||||
'key' => strtolower($key),
|
'type' => strtolower($type),
|
||||||
'user_id' => $this->user->id,
|
'user_id' => user()->id,
|
||||||
'book_id' => $bookId ?? 0,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,15 +63,13 @@ class ActivityService
|
|||||||
* and instead uses the 'extra' field with the entities name.
|
* and instead uses the 'extra' field with the entities name.
|
||||||
* Used when an entity is deleted.
|
* Used when an entity is deleted.
|
||||||
*/
|
*/
|
||||||
public function removeEntity(Entity $entity): Collection
|
public function removeEntity(Entity $entity)
|
||||||
{
|
{
|
||||||
$activities = $entity->activity()->get();
|
|
||||||
$entity->activity()->update([
|
$entity->activity()->update([
|
||||||
'extra' => $entity->name,
|
'detail' => $entity->name,
|
||||||
'entity_id' => 0,
|
'entity_id' => null,
|
||||||
'entity_type' => '',
|
'entity_type' => null,
|
||||||
]);
|
]);
|
||||||
return $activities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,16 +94,27 @@ class ActivityService
|
|||||||
*/
|
*/
|
||||||
public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
|
public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
|
||||||
{
|
{
|
||||||
|
/** @var [string => int[]] $queryIds */
|
||||||
|
$queryIds = [$entity->getMorphClass() => [$entity->id]];
|
||||||
|
|
||||||
if ($entity->isA('book')) {
|
if ($entity->isA('book')) {
|
||||||
$query = $this->activity->newQuery()->where('book_id', '=', $entity->id);
|
$queryIds[(new Chapter)->getMorphClass()] = $entity->chapters()->visible()->pluck('id');
|
||||||
} else {
|
}
|
||||||
$query = $this->activity->newQuery()->where('entity_type', '=', $entity->getMorphClass())
|
if ($entity->isA('book') || $entity->isA('chapter')) {
|
||||||
->where('entity_id', '=', $entity->id);
|
$queryIds[(new Page)->getMorphClass()] = $entity->pages()->visible()->pluck('id');
|
||||||
}
|
}
|
||||||
|
|
||||||
$activity = $this->permissionService
|
$query = $this->activity->newQuery();
|
||||||
->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
|
$query->where(function (Builder $query) use ($queryIds) {
|
||||||
->orderBy('created_at', 'desc')
|
foreach ($queryIds as $morphClass => $idArr) {
|
||||||
|
$query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
|
||||||
|
$innerQuery->where('entity_type', '=', $morphClass)
|
||||||
|
->whereIn('entity_id', $idArr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$activity = $query->orderBy('created_at', 'desc')
|
||||||
->with(['entity' => function (Relation $query) {
|
->with(['entity' => function (Relation $query) {
|
||||||
$query->withTrashed();
|
$query->withTrashed();
|
||||||
}, 'user.avatar'])
|
}, 'user.avatar'])
|
||||||
@ -155,9 +165,9 @@ class ActivityService
|
|||||||
/**
|
/**
|
||||||
* Flashes a notification message to the session if an appropriate message is available.
|
* Flashes a notification message to the session if an appropriate message is available.
|
||||||
*/
|
*/
|
||||||
protected function setNotification(string $activityKey)
|
protected function setNotification(string $type)
|
||||||
{
|
{
|
||||||
$notificationTextKey = 'activities.' . $activityKey . '_notification';
|
$notificationTextKey = 'activities.' . $type . '_notification';
|
||||||
if (trans()->has($notificationTextKey)) {
|
if (trans()->has($notificationTextKey)) {
|
||||||
$message = trans($notificationTextKey);
|
$message = trans($notificationTextKey);
|
||||||
session()->flash('success', $message);
|
session()->flash('success', $message);
|
||||||
|
51
app/Actions/ActivityType.php
Normal file
51
app/Actions/ActivityType.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php namespace BookStack\Actions;
|
||||||
|
|
||||||
|
class ActivityType
|
||||||
|
{
|
||||||
|
const PAGE_CREATE = 'page_create';
|
||||||
|
const PAGE_UPDATE = 'page_update';
|
||||||
|
const PAGE_DELETE = 'page_delete';
|
||||||
|
const PAGE_RESTORE = 'page_restore';
|
||||||
|
const PAGE_MOVE = 'page_move';
|
||||||
|
|
||||||
|
const CHAPTER_CREATE = 'chapter_create';
|
||||||
|
const CHAPTER_UPDATE = 'chapter_update';
|
||||||
|
const CHAPTER_DELETE = 'chapter_delete';
|
||||||
|
const CHAPTER_MOVE = 'chapter_move';
|
||||||
|
|
||||||
|
const BOOK_CREATE = 'book_create';
|
||||||
|
const BOOK_UPDATE = 'book_update';
|
||||||
|
const BOOK_DELETE = 'book_delete';
|
||||||
|
const BOOK_SORT = 'book_sort';
|
||||||
|
|
||||||
|
const BOOKSHELF_CREATE = 'bookshelf_create';
|
||||||
|
const BOOKSHELF_UPDATE = 'bookshelf_update';
|
||||||
|
const BOOKSHELF_DELETE = 'bookshelf_delete';
|
||||||
|
|
||||||
|
const COMMENTED_ON = 'commented_on';
|
||||||
|
const PERMISSIONS_UPDATE = 'permissions_update';
|
||||||
|
|
||||||
|
const SETTINGS_UPDATE = 'settings_update';
|
||||||
|
const MAINTENANCE_ACTION_RUN = 'maintenance_action_run';
|
||||||
|
|
||||||
|
const RECYCLE_BIN_EMPTY = 'recycle_bin_empty';
|
||||||
|
const RECYCLE_BIN_RESTORE = 'recycle_bin_restore';
|
||||||
|
const RECYCLE_BIN_DESTROY = 'recycle_bin_destroy';
|
||||||
|
|
||||||
|
const USER_CREATE = 'user_create';
|
||||||
|
const USER_UPDATE = 'user_update';
|
||||||
|
const USER_DELETE = 'user_delete';
|
||||||
|
|
||||||
|
const API_TOKEN_CREATE = 'api_token_create';
|
||||||
|
const API_TOKEN_UPDATE = 'api_token_update';
|
||||||
|
const API_TOKEN_DELETE = 'api_token_delete';
|
||||||
|
|
||||||
|
const ROLE_CREATE = 'role_create';
|
||||||
|
const ROLE_UPDATE = 'role_update';
|
||||||
|
const ROLE_DELETE = 'role_delete';
|
||||||
|
|
||||||
|
const AUTH_PASSWORD_RESET = 'auth_password_reset_request';
|
||||||
|
const AUTH_PASSWORD_RESET_UPDATE = 'auth_password_reset_update';
|
||||||
|
const AUTH_LOGIN = 'auth_login';
|
||||||
|
const AUTH_REGISTER = 'auth_register';
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use BookStack\Entities\Entity;
|
use BookStack\Entities\Entity;
|
||||||
use League\CommonMark\CommonMarkConverter;
|
use League\CommonMark\CommonMarkConverter;
|
||||||
|
use BookStack\Facades\Activity as ActivityService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CommentRepo
|
* Class CommentRepo
|
||||||
@ -44,6 +45,7 @@ class CommentRepo
|
|||||||
$comment->parent_id = $parent_id;
|
$comment->parent_id = $parent_id;
|
||||||
|
|
||||||
$entity->comments()->save($comment);
|
$entity->comments()->save($comment);
|
||||||
|
ActivityService::addForEntity($entity, ActivityType::COMMENTED_ON);
|
||||||
return $comment;
|
return $comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,22 @@
|
|||||||
<?php namespace BookStack\Api;
|
<?php namespace BookStack\Api;
|
||||||
|
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
class ApiToken extends Model
|
/**
|
||||||
|
* Class ApiToken
|
||||||
|
* @property int $id
|
||||||
|
* @property string $token_id
|
||||||
|
* @property string $secret
|
||||||
|
* @property string $name
|
||||||
|
* @property Carbon $expires_at
|
||||||
|
* @property User $user
|
||||||
|
* @package BookStack\Api
|
||||||
|
*/
|
||||||
|
class ApiToken extends Model implements Loggable
|
||||||
{
|
{
|
||||||
protected $fillable = ['name', 'expires_at'];
|
protected $fillable = ['name', 'expires_at'];
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
@ -28,4 +39,12 @@ class ApiToken extends Model
|
|||||||
{
|
{
|
||||||
return Carbon::now()->addYears(100)->format('Y-m-d');
|
return Carbon::now()->addYears(100)->format('Y-m-d');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function logDescriptor(): string
|
||||||
|
{
|
||||||
|
return "({$this->id}) {$this->name}; User: {$this->user->logDescriptor()}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?php namespace BookStack\Auth\Access;
|
<?php namespace BookStack\Auth\Access;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\SocialAccount;
|
use BookStack\Auth\SocialAccount;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use BookStack\Auth\UserRepo;
|
use BookStack\Auth\UserRepo;
|
||||||
use BookStack\Exceptions\UserRegistrationException;
|
use BookStack\Exceptions\UserRegistrationException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class RegistrationService
|
class RegistrationService
|
||||||
@ -68,6 +70,8 @@ class RegistrationService
|
|||||||
$newUser->socialAccounts()->save($socialAccount);
|
$newUser->socialAccounts()->save($socialAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
|
||||||
|
|
||||||
// Start email confirmation flow if required
|
// Start email confirmation flow if required
|
||||||
if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
|
if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
|
||||||
$newUser->save();
|
$newUser->save();
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?php namespace BookStack\Auth\Access;
|
<?php namespace BookStack\Auth\Access;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use BookStack\Exceptions\JsonDebugException;
|
use BookStack\Exceptions\JsonDebugException;
|
||||||
use BookStack\Exceptions\SamlException;
|
use BookStack\Exceptions\SamlException;
|
||||||
use BookStack\Exceptions\UserRegistrationException;
|
use BookStack\Exceptions\UserRegistrationException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use OneLogin\Saml2\Auth;
|
use OneLogin\Saml2\Auth;
|
||||||
@ -372,6 +374,7 @@ class Saml2Service extends ExternalAuthService
|
|||||||
}
|
}
|
||||||
|
|
||||||
auth()->login($user);
|
auth()->login($user);
|
||||||
|
Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}");
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<?php namespace BookStack\Auth\Access;
|
<?php namespace BookStack\Auth\Access;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\SocialAccount;
|
use BookStack\Auth\SocialAccount;
|
||||||
use BookStack\Auth\UserRepo;
|
use BookStack\Auth\UserRepo;
|
||||||
use BookStack\Exceptions\SocialDriverNotConfigured;
|
use BookStack\Exceptions\SocialDriverNotConfigured;
|
||||||
use BookStack\Exceptions\SocialSignInAccountNotUsed;
|
use BookStack\Exceptions\SocialSignInAccountNotUsed;
|
||||||
use BookStack\Exceptions\UserRegistrationException;
|
use BookStack\Exceptions\UserRegistrationException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Laravel\Socialite\Contracts\Factory as Socialite;
|
use Laravel\Socialite\Contracts\Factory as Socialite;
|
||||||
use Laravel\Socialite\Contracts\Provider;
|
use Laravel\Socialite\Contracts\Provider;
|
||||||
@ -98,6 +100,7 @@ class SocialAuthService
|
|||||||
// Simply log the user into the application.
|
// Simply log the user into the application.
|
||||||
if (!$isLoggedIn && $socialAccount !== null) {
|
if (!$isLoggedIn && $socialAccount !== null) {
|
||||||
auth()->login($socialAccount->user);
|
auth()->login($socialAccount->user);
|
||||||
|
Activity::add(ActivityType::AUTH_LOGIN, $socialAccount);
|
||||||
return redirect()->intended('/');
|
return redirect()->intended('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?php namespace BookStack\Auth\Permissions;
|
<?php namespace BookStack\Auth\Permissions;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\Role;
|
use BookStack\Auth\Role;
|
||||||
use BookStack\Exceptions\PermissionsException;
|
use BookStack\Exceptions\PermissionsException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class PermissionsRepo
|
class PermissionsRepo
|
||||||
{
|
{
|
||||||
@ -60,6 +61,7 @@ class PermissionsRepo
|
|||||||
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
|
$permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
|
||||||
$this->assignRolePermissions($role, $permissions);
|
$this->assignRolePermissions($role, $permissions);
|
||||||
$this->permissionService->buildJointPermissionForRole($role);
|
$this->permissionService->buildJointPermissionForRole($role);
|
||||||
|
Activity::add(ActivityType::ROLE_CREATE, $role);
|
||||||
return $role;
|
return $role;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,12 +90,13 @@ class PermissionsRepo
|
|||||||
$role->fill($roleData);
|
$role->fill($roleData);
|
||||||
$role->save();
|
$role->save();
|
||||||
$this->permissionService->buildJointPermissionForRole($role);
|
$this->permissionService->buildJointPermissionForRole($role);
|
||||||
|
Activity::add(ActivityType::ROLE_UPDATE, $role);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign an list of permission names to an role.
|
* Assign an list of permission names to an role.
|
||||||
*/
|
*/
|
||||||
public function assignRolePermissions(Role $role, array $permissionNameArray = [])
|
protected function assignRolePermissions(Role $role, array $permissionNameArray = [])
|
||||||
{
|
{
|
||||||
$permissions = [];
|
$permissions = [];
|
||||||
$permissionNameArray = array_values($permissionNameArray);
|
$permissionNameArray = array_values($permissionNameArray);
|
||||||
@ -137,6 +140,7 @@ class PermissionsRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->permissionService->deleteJointPermissionsForRole($role);
|
$this->permissionService->deleteJointPermissionsForRole($role);
|
||||||
|
Activity::add(ActivityType::ROLE_DELETE, $role);
|
||||||
$role->delete();
|
$role->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use BookStack\Auth\Permissions\JointPermission;
|
use BookStack\Auth\Permissions\JointPermission;
|
||||||
use BookStack\Auth\Permissions\RolePermission;
|
use BookStack\Auth\Permissions\RolePermission;
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
@ -14,7 +15,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||||||
* @property string $external_auth_id
|
* @property string $external_auth_id
|
||||||
* @property string $system_name
|
* @property string $system_name
|
||||||
*/
|
*/
|
||||||
class Role extends Model
|
class Role extends Model implements Loggable
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $fillable = ['display_name', 'description', 'external_auth_id'];
|
protected $fillable = ['display_name', 'description', 'external_auth_id'];
|
||||||
@ -104,4 +105,12 @@ class Role extends Model
|
|||||||
{
|
{
|
||||||
return static::query()->where('system_name', '!=', 'admin')->get();
|
return static::query()->where('system_name', '!=', 'admin')->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function logDescriptor(): string
|
||||||
|
{
|
||||||
|
return "({$this->id}) {$this->display_name}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
<?php namespace BookStack\Auth;
|
<?php namespace BookStack\Auth;
|
||||||
|
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
|
|
||||||
class SocialAccount extends Model
|
/**
|
||||||
|
* Class SocialAccount
|
||||||
|
* @property string $driver
|
||||||
|
* @property User $user
|
||||||
|
* @package BookStack\Auth
|
||||||
|
*/
|
||||||
|
class SocialAccount extends Model implements Loggable
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $fillable = ['user_id', 'driver', 'driver_id', 'timestamps'];
|
protected $fillable = ['user_id', 'driver', 'driver_id', 'timestamps'];
|
||||||
@ -11,4 +18,12 @@ class SocialAccount extends Model
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsTo(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function logDescriptor(): string
|
||||||
|
{
|
||||||
|
return "{$this->driver}; {$this->user->logDescriptor()}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<?php namespace BookStack\Auth;
|
<?php namespace BookStack\Auth;
|
||||||
|
|
||||||
|
use BookStack\Actions\Activity;
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
use BookStack\Model;
|
use BookStack\Model;
|
||||||
use BookStack\Notifications\ResetPassword;
|
use BookStack\Notifications\ResetPassword;
|
||||||
use BookStack\Uploads\Image;
|
use BookStack\Uploads\Image;
|
||||||
@ -11,6 +13,7 @@ use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
|||||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +30,7 @@ use Illuminate\Notifications\Notifiable;
|
|||||||
* @property string $external_auth_id
|
* @property string $external_auth_id
|
||||||
* @property string $system_name
|
* @property string $system_name
|
||||||
*/
|
*/
|
||||||
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
|
class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable
|
||||||
{
|
{
|
||||||
use Authenticatable, CanResetPassword, Notifiable;
|
use Authenticatable, CanResetPassword, Notifiable;
|
||||||
|
|
||||||
@ -229,6 +232,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
return $this->hasMany(ApiToken::class);
|
return $this->hasMany(ApiToken::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest activity instance for this user.
|
||||||
|
*/
|
||||||
|
public function latestActivity(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(Activity::class)->latest();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the url for editing this user.
|
* Get the url for editing this user.
|
||||||
*/
|
*/
|
||||||
@ -274,4 +285,12 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
{
|
{
|
||||||
$this->notify(new ResetPassword($token));
|
$this->notify(new ResetPassword($token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function logDescriptor(): string
|
||||||
|
{
|
||||||
|
return "({$this->id}) {$this->name}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use BookStack\Exceptions\UserUpdateException;
|
|||||||
use BookStack\Uploads\Image;
|
use BookStack\Uploads\Image;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Images;
|
use Images;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
@ -56,13 +57,19 @@ class UserRepo
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the users with their permissions in a paginated format.
|
* Get all the users with their permissions in a paginated format.
|
||||||
* @param int $count
|
|
||||||
* @param $sortData
|
|
||||||
* @return Builder|static
|
|
||||||
*/
|
*/
|
||||||
public function getAllUsersPaginatedAndSorted($count, $sortData)
|
public function getAllUsersPaginatedAndSorted(int $count, array $sortData): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
$query = $this->user->with('roles', 'avatar')->orderBy($sortData['sort'], $sortData['order']);
|
$sort = $sortData['sort'];
|
||||||
|
if ($sort === 'latest_activity') {
|
||||||
|
$sort = \BookStack\Actions\Activity::query()->select('created_at')
|
||||||
|
->whereColumn('activities.user_id', 'users.id')
|
||||||
|
->latest()
|
||||||
|
->take(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $this->user->with(['roles', 'avatar', 'latestActivity'])
|
||||||
|
->orderBy($sort, $sortData['order']);
|
||||||
|
|
||||||
if ($sortData['search']) {
|
if ($sortData['search']) {
|
||||||
$term = '%' . $sortData['search'] . '%';
|
$term = '%' . $sortData['search'] . '%';
|
||||||
|
@ -45,9 +45,6 @@ class BookChild extends Entity
|
|||||||
$this->save();
|
$this->save();
|
||||||
$this->refresh();
|
$this->refresh();
|
||||||
|
|
||||||
// Update related activity
|
|
||||||
$this->activity()->update(['book_id' => $newBookId]);
|
|
||||||
|
|
||||||
// Update all child pages if a chapter
|
// Update all child pages if a chapter
|
||||||
if ($this instanceof Chapter) {
|
if ($this instanceof Chapter) {
|
||||||
foreach ($this->pages as $page) {
|
foreach ($this->pages as $page) {
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<?php namespace BookStack\Entities;
|
<?php namespace BookStack\Entities;
|
||||||
|
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
|
|
||||||
class Deletion extends Model
|
class Deletion extends Model implements Loggable
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,4 +39,9 @@ class Deletion extends Model
|
|||||||
return $record;
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function logDescriptor(): string
|
||||||
|
{
|
||||||
|
$deletable = $this->deletable()->first();
|
||||||
|
return "Deletion ({$this->id}) for {$deletable->getType()} ({$deletable->id}) {$deletable->name}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
namespace BookStack\Entities\Repos;
|
namespace BookStack\Entities\Repos;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Actions\TagRepo;
|
use BookStack\Actions\TagRepo;
|
||||||
use BookStack\Entities\Book;
|
|
||||||
use BookStack\Entities\Entity;
|
use BookStack\Entities\Entity;
|
||||||
use BookStack\Entities\HasCoverImage;
|
use BookStack\Entities\HasCoverImage;
|
||||||
use BookStack\Exceptions\ImageUploadException;
|
use BookStack\Exceptions\ImageUploadException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use BookStack\Uploads\ImageRepo;
|
use BookStack\Uploads\ImageRepo;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -18,10 +19,6 @@ class BaseRepo
|
|||||||
protected $imageRepo;
|
protected $imageRepo;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BaseRepo constructor.
|
|
||||||
* @param $tagRepo
|
|
||||||
*/
|
|
||||||
public function __construct(TagRepo $tagRepo, ImageRepo $imageRepo)
|
public function __construct(TagRepo $tagRepo, ImageRepo $imageRepo)
|
||||||
{
|
{
|
||||||
$this->tagRepo = $tagRepo;
|
$this->tagRepo = $tagRepo;
|
||||||
@ -115,5 +112,6 @@ class BaseRepo
|
|||||||
|
|
||||||
$entity->save();
|
$entity->save();
|
||||||
$entity->rebuildPermissions();
|
$entity->rebuildPermissions();
|
||||||
|
Activity::addForEntity($entity, ActivityType::PERMISSIONS_UPDATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<?php namespace BookStack\Entities\Repos;
|
<?php namespace BookStack\Entities\Repos;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Actions\TagRepo;
|
use BookStack\Actions\TagRepo;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Managers\TrashCan;
|
use BookStack\Entities\Managers\TrashCan;
|
||||||
use BookStack\Exceptions\ImageUploadException;
|
use BookStack\Exceptions\ImageUploadException;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
use BookStack\Exceptions\NotifyException;
|
use BookStack\Exceptions\NotifyException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use BookStack\Uploads\ImageRepo;
|
use BookStack\Uploads\ImageRepo;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
@ -91,6 +93,7 @@ class BookRepo
|
|||||||
{
|
{
|
||||||
$book = new Book();
|
$book = new Book();
|
||||||
$this->baseRepo->create($book, $input);
|
$this->baseRepo->create($book, $input);
|
||||||
|
Activity::addForEntity($book, ActivityType::BOOK_CREATE);
|
||||||
return $book;
|
return $book;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +103,7 @@ class BookRepo
|
|||||||
public function update(Book $book, array $input): Book
|
public function update(Book $book, array $input): Book
|
||||||
{
|
{
|
||||||
$this->baseRepo->update($book, $input);
|
$this->baseRepo->update($book, $input);
|
||||||
|
Activity::addForEntity($book, ActivityType::BOOK_UPDATE);
|
||||||
return $book;
|
return $book;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,6 +133,8 @@ class BookRepo
|
|||||||
{
|
{
|
||||||
$trashCan = new TrashCan();
|
$trashCan = new TrashCan();
|
||||||
$trashCan->softDestroyBook($book);
|
$trashCan->softDestroyBook($book);
|
||||||
|
Activity::addForEntity($book, ActivityType::BOOK_DELETE);
|
||||||
|
|
||||||
$trashCan->autoClearOld();
|
$trashCan->autoClearOld();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<?php namespace BookStack\Entities\Repos;
|
<?php namespace BookStack\Entities\Repos;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Bookshelf;
|
use BookStack\Entities\Bookshelf;
|
||||||
use BookStack\Entities\Managers\TrashCan;
|
use BookStack\Entities\Managers\TrashCan;
|
||||||
use BookStack\Exceptions\ImageUploadException;
|
use BookStack\Exceptions\ImageUploadException;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
@ -87,11 +89,12 @@ class BookshelfRepo
|
|||||||
$shelf = new Bookshelf();
|
$shelf = new Bookshelf();
|
||||||
$this->baseRepo->create($shelf, $input);
|
$this->baseRepo->create($shelf, $input);
|
||||||
$this->updateBooks($shelf, $bookIds);
|
$this->updateBooks($shelf, $bookIds);
|
||||||
|
Activity::addForEntity($shelf, ActivityType::BOOKSHELF_CREATE);
|
||||||
return $shelf;
|
return $shelf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new shelf in the system.
|
* Update an existing shelf in the system using the given input.
|
||||||
*/
|
*/
|
||||||
public function update(Bookshelf $shelf, array $input, ?array $bookIds): Bookshelf
|
public function update(Bookshelf $shelf, array $input, ?array $bookIds): Bookshelf
|
||||||
{
|
{
|
||||||
@ -101,6 +104,7 @@ class BookshelfRepo
|
|||||||
$this->updateBooks($shelf, $bookIds);
|
$this->updateBooks($shelf, $bookIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Activity::addForEntity($shelf, ActivityType::BOOKSHELF_UPDATE);
|
||||||
return $shelf;
|
return $shelf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +179,7 @@ class BookshelfRepo
|
|||||||
{
|
{
|
||||||
$trashCan = new TrashCan();
|
$trashCan = new TrashCan();
|
||||||
$trashCan->softDestroyShelf($shelf);
|
$trashCan->softDestroyShelf($shelf);
|
||||||
|
Activity::addForEntity($shelf, ActivityType::BOOKSHELF_DELETE);
|
||||||
$trashCan->autoClearOld();
|
$trashCan->autoClearOld();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<?php namespace BookStack\Entities\Repos;
|
<?php namespace BookStack\Entities\Repos;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Chapter;
|
use BookStack\Entities\Chapter;
|
||||||
use BookStack\Entities\Managers\BookContents;
|
use BookStack\Entities\Managers\BookContents;
|
||||||
use BookStack\Entities\Managers\TrashCan;
|
use BookStack\Entities\Managers\TrashCan;
|
||||||
use BookStack\Exceptions\MoveOperationException;
|
use BookStack\Exceptions\MoveOperationException;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
@ -46,6 +48,7 @@ class ChapterRepo
|
|||||||
$chapter->book_id = $parentBook->id;
|
$chapter->book_id = $parentBook->id;
|
||||||
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
|
$chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1;
|
||||||
$this->baseRepo->create($chapter, $input);
|
$this->baseRepo->create($chapter, $input);
|
||||||
|
Activity::addForEntity($chapter, ActivityType::CHAPTER_CREATE);
|
||||||
return $chapter;
|
return $chapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +58,7 @@ class ChapterRepo
|
|||||||
public function update(Chapter $chapter, array $input): Chapter
|
public function update(Chapter $chapter, array $input): Chapter
|
||||||
{
|
{
|
||||||
$this->baseRepo->update($chapter, $input);
|
$this->baseRepo->update($chapter, $input);
|
||||||
|
Activity::addForEntity($chapter, ActivityType::CHAPTER_UPDATE);
|
||||||
return $chapter;
|
return $chapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +78,7 @@ class ChapterRepo
|
|||||||
{
|
{
|
||||||
$trashCan = new TrashCan();
|
$trashCan = new TrashCan();
|
||||||
$trashCan->softDestroyChapter($chapter);
|
$trashCan->softDestroyChapter($chapter);
|
||||||
|
Activity::addForEntity($chapter, ActivityType::CHAPTER_DELETE);
|
||||||
$trashCan->autoClearOld();
|
$trashCan->autoClearOld();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +98,7 @@ class ChapterRepo
|
|||||||
throw new MoveOperationException('Chapters can only be moved into books');
|
throw new MoveOperationException('Chapters can only be moved into books');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Book $parent */
|
||||||
$parent = Book::visible()->where('id', '=', $entityId)->first();
|
$parent = Book::visible()->where('id', '=', $entityId)->first();
|
||||||
if ($parent === null) {
|
if ($parent === null) {
|
||||||
throw new MoveOperationException('Book to move chapter into not found');
|
throw new MoveOperationException('Book to move chapter into not found');
|
||||||
@ -100,6 +106,8 @@ class ChapterRepo
|
|||||||
|
|
||||||
$chapter->changeBook($parent->id);
|
$chapter->changeBook($parent->id);
|
||||||
$chapter->rebuildPermissions();
|
$chapter->rebuildPermissions();
|
||||||
|
Activity::addForEntity($chapter, ActivityType::CHAPTER_MOVE);
|
||||||
|
|
||||||
return $parent;
|
return $parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Entities\Repos;
|
<?php namespace BookStack\Entities\Repos;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Chapter;
|
use BookStack\Entities\Chapter;
|
||||||
use BookStack\Entities\Entity;
|
use BookStack\Entities\Entity;
|
||||||
@ -12,6 +13,7 @@ use BookStack\Exceptions\MoveOperationException;
|
|||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
use BookStack\Exceptions\NotifyException;
|
use BookStack\Exceptions\NotifyException;
|
||||||
use BookStack\Exceptions\PermissionsException;
|
use BookStack\Exceptions\PermissionsException;
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
@ -165,7 +167,10 @@ class PageRepo
|
|||||||
|
|
||||||
$this->savePageRevision($draft, trans('entities.pages_initial_revision'));
|
$this->savePageRevision($draft, trans('entities.pages_initial_revision'));
|
||||||
$draft->indexForSearch();
|
$draft->indexForSearch();
|
||||||
return $draft->refresh();
|
$draft->refresh();
|
||||||
|
|
||||||
|
Activity::addForEntity($draft, ActivityType::PAGE_CREATE);
|
||||||
|
return $draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,6 +208,7 @@ class PageRepo
|
|||||||
$this->savePageRevision($page, $summary);
|
$this->savePageRevision($page, $summary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Activity::addForEntity($page, ActivityType::PAGE_UPDATE);
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +272,7 @@ class PageRepo
|
|||||||
{
|
{
|
||||||
$trashCan = new TrashCan();
|
$trashCan = new TrashCan();
|
||||||
$trashCan->softDestroyPage($page);
|
$trashCan->softDestroyPage($page);
|
||||||
|
Activity::addForEntity($page, ActivityType::PAGE_DELETE);
|
||||||
$trashCan->autoClearOld();
|
$trashCan->autoClearOld();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +293,7 @@ class PageRepo
|
|||||||
$page->save();
|
$page->save();
|
||||||
|
|
||||||
$page->indexForSearch();
|
$page->indexForSearch();
|
||||||
|
Activity::addForEntity($page, ActivityType::PAGE_RESTORE);
|
||||||
return $page;
|
return $page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +304,7 @@ class PageRepo
|
|||||||
* @throws MoveOperationException
|
* @throws MoveOperationException
|
||||||
* @throws PermissionsException
|
* @throws PermissionsException
|
||||||
*/
|
*/
|
||||||
public function move(Page $page, string $parentIdentifier): Book
|
public function move(Page $page, string $parentIdentifier): Entity
|
||||||
{
|
{
|
||||||
$parent = $this->findParentByIdentifier($parentIdentifier);
|
$parent = $this->findParentByIdentifier($parentIdentifier);
|
||||||
if ($parent === null) {
|
if ($parent === null) {
|
||||||
@ -311,7 +319,8 @@ class PageRepo
|
|||||||
$page->changeBook($parent instanceof Book ? $parent->id : $parent->book->id);
|
$page->changeBook($parent instanceof Book ? $parent->id : $parent->book->id);
|
||||||
$page->rebuildPermissions();
|
$page->rebuildPermissions();
|
||||||
|
|
||||||
return ($parent instanceof Book ? $parent : $parent->book);
|
Activity::addForEntity($page, ActivityType::PAGE_MOVE);
|
||||||
|
return $parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Http\Controllers\Api;
|
<?php namespace BookStack\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Repos\BookRepo;
|
use BookStack\Entities\Repos\BookRepo;
|
||||||
use BookStack\Exceptions\NotifyException;
|
use BookStack\Exceptions\NotifyException;
|
||||||
@ -55,8 +56,6 @@ class BookApiController extends ApiController
|
|||||||
$requestData = $this->validate($request, $this->rules['create']);
|
$requestData = $this->validate($request, $this->rules['create']);
|
||||||
|
|
||||||
$book = $this->bookRepo->create($requestData);
|
$book = $this->bookRepo->create($requestData);
|
||||||
Activity::add($book, 'book_create', $book->id);
|
|
||||||
|
|
||||||
return response()->json($book);
|
return response()->json($book);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +79,6 @@ class BookApiController extends ApiController
|
|||||||
|
|
||||||
$requestData = $this->validate($request, $this->rules['update']);
|
$requestData = $this->validate($request, $this->rules['update']);
|
||||||
$book = $this->bookRepo->update($book, $requestData);
|
$book = $this->bookRepo->update($book, $requestData);
|
||||||
Activity::add($book, 'book_update', $book->id);
|
|
||||||
|
|
||||||
return response()->json($book);
|
return response()->json($book);
|
||||||
}
|
}
|
||||||
@ -96,8 +94,6 @@ class BookApiController extends ApiController
|
|||||||
$this->checkOwnablePermission('book-delete', $book);
|
$this->checkOwnablePermission('book-delete', $book);
|
||||||
|
|
||||||
$this->bookRepo->destroy($book);
|
$this->bookRepo->destroy($book);
|
||||||
Activity::addMessage('book_delete', $book->name);
|
|
||||||
|
|
||||||
return response('', 204);
|
return response('', 204);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
<?php namespace BookStack\Http\Controllers\Api;
|
<?php namespace BookStack\Http\Controllers\Api;
|
||||||
|
|
||||||
use BookStack\Facades\Activity;
|
|
||||||
use BookStack\Entities\Repos\BookshelfRepo;
|
use BookStack\Entities\Repos\BookshelfRepo;
|
||||||
use BookStack\Entities\Bookshelf;
|
use BookStack\Entities\Bookshelf;
|
||||||
use Exception;
|
use Exception;
|
||||||
@ -63,7 +62,6 @@ class BookshelfApiController extends ApiController
|
|||||||
$bookIds = $request->get('books', []);
|
$bookIds = $request->get('books', []);
|
||||||
$shelf = $this->bookshelfRepo->create($requestData, $bookIds);
|
$shelf = $this->bookshelfRepo->create($requestData, $bookIds);
|
||||||
|
|
||||||
Activity::add($shelf, 'bookshelf_create', $shelf->id);
|
|
||||||
return response()->json($shelf);
|
return response()->json($shelf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,12 +92,9 @@ class BookshelfApiController extends ApiController
|
|||||||
$this->checkOwnablePermission('bookshelf-update', $shelf);
|
$this->checkOwnablePermission('bookshelf-update', $shelf);
|
||||||
|
|
||||||
$requestData = $this->validate($request, $this->rules['update']);
|
$requestData = $this->validate($request, $this->rules['update']);
|
||||||
|
|
||||||
$bookIds = $request->get('books', null);
|
$bookIds = $request->get('books', null);
|
||||||
|
|
||||||
$shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds);
|
$shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds);
|
||||||
Activity::add($shelf, 'bookshelf_update', $shelf->id);
|
|
||||||
|
|
||||||
return response()->json($shelf);
|
return response()->json($shelf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,8 +110,6 @@ class BookshelfApiController extends ApiController
|
|||||||
$this->checkOwnablePermission('bookshelf-delete', $shelf);
|
$this->checkOwnablePermission('bookshelf-delete', $shelf);
|
||||||
|
|
||||||
$this->bookshelfRepo->destroy($shelf);
|
$this->bookshelfRepo->destroy($shelf);
|
||||||
Activity::addMessage('bookshelf_delete', $shelf->name);
|
|
||||||
|
|
||||||
return response('', 204);
|
return response('', 204);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Http\Controllers\Api;
|
<?php namespace BookStack\Http\Controllers\Api;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Chapter;
|
use BookStack\Entities\Chapter;
|
||||||
use BookStack\Entities\Repos\ChapterRepo;
|
use BookStack\Entities\Repos\ChapterRepo;
|
||||||
@ -58,8 +59,6 @@ class ChapterApiController extends ApiController
|
|||||||
$this->checkOwnablePermission('chapter-create', $book);
|
$this->checkOwnablePermission('chapter-create', $book);
|
||||||
|
|
||||||
$chapter = $this->chapterRepo->create($request->all(), $book);
|
$chapter = $this->chapterRepo->create($request->all(), $book);
|
||||||
Activity::add($chapter, 'chapter_create', $book->id);
|
|
||||||
|
|
||||||
return response()->json($chapter->load(['tags']));
|
return response()->json($chapter->load(['tags']));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,8 +82,6 @@ class ChapterApiController extends ApiController
|
|||||||
$this->checkOwnablePermission('chapter-update', $chapter);
|
$this->checkOwnablePermission('chapter-update', $chapter);
|
||||||
|
|
||||||
$updatedChapter = $this->chapterRepo->update($chapter, $request->all());
|
$updatedChapter = $this->chapterRepo->update($chapter, $request->all());
|
||||||
Activity::add($chapter, 'chapter_update', $chapter->book->id);
|
|
||||||
|
|
||||||
return response()->json($updatedChapter->load(['tags']));
|
return response()->json($updatedChapter->load(['tags']));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,8 +94,6 @@ class ChapterApiController extends ApiController
|
|||||||
$this->checkOwnablePermission('chapter-delete', $chapter);
|
$this->checkOwnablePermission('chapter-delete', $chapter);
|
||||||
|
|
||||||
$this->chapterRepo->destroy($chapter);
|
$this->chapterRepo->destroy($chapter);
|
||||||
Activity::addMessage('chapter_delete', $chapter->name, $chapter->book->id);
|
|
||||||
|
|
||||||
return response('', 204);
|
return response('', 204);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class AuditLogController extends Controller
|
|||||||
->orderBy($listDetails['sort'], $listDetails['order']);
|
->orderBy($listDetails['sort'], $listDetails['order']);
|
||||||
|
|
||||||
if ($listDetails['event']) {
|
if ($listDetails['event']) {
|
||||||
$query->where('key', '=', $listDetails['event']);
|
$query->where('type', '=', $listDetails['event']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($listDetails['date_from']) {
|
if ($listDetails['date_from']) {
|
||||||
@ -45,12 +45,12 @@ class AuditLogController extends Controller
|
|||||||
$activities = $query->paginate(100);
|
$activities = $query->paginate(100);
|
||||||
$activities->appends($listDetails);
|
$activities->appends($listDetails);
|
||||||
|
|
||||||
$keys = DB::table('activities')->select('key')->distinct()->pluck('key');
|
$types = DB::table('activities')->select('type')->distinct()->pluck('type');
|
||||||
$this->setPageTitle(trans('settings.audit'));
|
$this->setPageTitle(trans('settings.audit'));
|
||||||
return view('settings.audit', [
|
return view('settings.audit', [
|
||||||
'activities' => $activities,
|
'activities' => $activities,
|
||||||
'listDetails' => $listDetails,
|
'listDetails' => $listDetails,
|
||||||
'activityKeys' => $keys,
|
'activityTypes' => $types,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Http\Controllers\Auth;
|
namespace BookStack\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Http\Controllers\Controller;
|
use BookStack\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -52,6 +53,10 @@ class ForgotPasswordController extends Controller
|
|||||||
$request->only('email')
|
$request->only('email')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($response === Password::RESET_LINK_SENT) {
|
||||||
|
$this->logActivity(ActivityType::AUTH_PASSWORD_RESET, $request->get('email'));
|
||||||
|
}
|
||||||
|
|
||||||
if ($response === Password::RESET_LINK_SENT || $response === Password::INVALID_USER) {
|
if ($response === Password::RESET_LINK_SENT || $response === Password::INVALID_USER) {
|
||||||
$message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
|
$message = trans('auth.reset_password_sent', ['email' => $request->get('email')]);
|
||||||
$this->showSuccessNotification($message);
|
$this->showSuccessNotification($message);
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
namespace BookStack\Http\Controllers\Auth;
|
namespace BookStack\Http\Controllers\Auth;
|
||||||
|
|
||||||
use Activity;
|
use Activity;
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\Access\SocialAuthService;
|
use BookStack\Auth\Access\SocialAuthService;
|
||||||
use BookStack\Exceptions\LoginAttemptEmailNeededException;
|
use BookStack\Exceptions\LoginAttemptEmailNeededException;
|
||||||
use BookStack\Exceptions\LoginAttemptException;
|
use BookStack\Exceptions\LoginAttemptException;
|
||||||
use BookStack\Exceptions\UserRegistrationException;
|
|
||||||
use BookStack\Http\Controllers\Controller;
|
use BookStack\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -151,6 +151,7 @@ class LoginController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
|
||||||
return redirect()->intended($this->redirectPath());
|
return redirect()->intended($this->redirectPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Http\Controllers\Auth;
|
namespace BookStack\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Http\Controllers\Controller;
|
use BookStack\Http\Controllers\Controller;
|
||||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -47,6 +48,7 @@ class ResetPasswordController extends Controller
|
|||||||
{
|
{
|
||||||
$message = trans('auth.reset_password_success');
|
$message = trans('auth.reset_password_success');
|
||||||
$this->showSuccessNotification($message);
|
$this->showSuccessNotification($message);
|
||||||
|
$this->logActivity(ActivityType::AUTH_PASSWORD_RESET_UPDATE, user());
|
||||||
return redirect($this->redirectPath())
|
return redirect($this->redirectPath())
|
||||||
->with('status', trans($response));
|
->with('status', trans($response));
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
use Activity;
|
use Activity;
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Managers\BookContents;
|
use BookStack\Entities\Managers\BookContents;
|
||||||
use BookStack\Entities\Bookshelf;
|
use BookStack\Entities\Bookshelf;
|
||||||
use BookStack\Entities\Managers\EntityContext;
|
use BookStack\Entities\Managers\EntityContext;
|
||||||
use BookStack\Entities\Repos\BookRepo;
|
use BookStack\Entities\Repos\BookRepo;
|
||||||
use BookStack\Exceptions\ImageUploadException;
|
use BookStack\Exceptions\ImageUploadException;
|
||||||
use BookStack\Exceptions\NotifyException;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -18,9 +18,6 @@ class BookController extends Controller
|
|||||||
protected $bookRepo;
|
protected $bookRepo;
|
||||||
protected $entityContextManager;
|
protected $entityContextManager;
|
||||||
|
|
||||||
/**
|
|
||||||
* BookController constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(EntityContext $entityContextManager, BookRepo $bookRepo)
|
public function __construct(EntityContext $entityContextManager, BookRepo $bookRepo)
|
||||||
{
|
{
|
||||||
$this->bookRepo = $bookRepo;
|
$this->bookRepo = $bookRepo;
|
||||||
@ -97,11 +94,10 @@ class BookController extends Controller
|
|||||||
|
|
||||||
$book = $this->bookRepo->create($request->all());
|
$book = $this->bookRepo->create($request->all());
|
||||||
$this->bookRepo->updateCoverImage($book, $request->file('image', null));
|
$this->bookRepo->updateCoverImage($book, $request->file('image', null));
|
||||||
Activity::add($book, 'book_create', $book->id);
|
|
||||||
|
|
||||||
if ($bookshelf) {
|
if ($bookshelf) {
|
||||||
$bookshelf->appendBook($book);
|
$bookshelf->appendBook($book);
|
||||||
Activity::add($bookshelf, 'bookshelf_update');
|
Activity::addForEntity($bookshelf, ActivityType::BOOKSHELF_UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($book->getUrl());
|
return redirect($book->getUrl());
|
||||||
@ -162,8 +158,6 @@ class BookController extends Controller
|
|||||||
$resetCover = $request->has('image_reset');
|
$resetCover = $request->has('image_reset');
|
||||||
$this->bookRepo->updateCoverImage($book, $request->file('image', null), $resetCover);
|
$this->bookRepo->updateCoverImage($book, $request->file('image', null), $resetCover);
|
||||||
|
|
||||||
Activity::add($book, 'book_update', $book->id);
|
|
||||||
|
|
||||||
return redirect($book->getUrl());
|
return redirect($book->getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +181,6 @@ class BookController extends Controller
|
|||||||
$book = $this->bookRepo->getBySlug($bookSlug);
|
$book = $this->bookRepo->getBySlug($bookSlug);
|
||||||
$this->checkOwnablePermission('book-delete', $book);
|
$this->checkOwnablePermission('book-delete', $book);
|
||||||
|
|
||||||
Activity::add($book, 'book_delete', $book->id);
|
|
||||||
$this->bookRepo->destroy($book);
|
$this->bookRepo->destroy($book);
|
||||||
|
|
||||||
return redirect('/books');
|
return redirect('/books');
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Http\Controllers;
|
namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Managers\BookContents;
|
use BookStack\Entities\Managers\BookContents;
|
||||||
use BookStack\Entities\Repos\BookRepo;
|
use BookStack\Entities\Repos\BookRepo;
|
||||||
@ -14,10 +15,6 @@ class BookSortController extends Controller
|
|||||||
|
|
||||||
protected $bookRepo;
|
protected $bookRepo;
|
||||||
|
|
||||||
/**
|
|
||||||
* BookSortController constructor.
|
|
||||||
* @param $bookRepo
|
|
||||||
*/
|
|
||||||
public function __construct(BookRepo $bookRepo)
|
public function __construct(BookRepo $bookRepo)
|
||||||
{
|
{
|
||||||
$this->bookRepo = $bookRepo;
|
$this->bookRepo = $bookRepo;
|
||||||
@ -74,7 +71,7 @@ class BookSortController extends Controller
|
|||||||
|
|
||||||
// Rebuild permissions and add activity for involved books.
|
// Rebuild permissions and add activity for involved books.
|
||||||
$booksInvolved->each(function (Book $book) {
|
$booksInvolved->each(function (Book $book) {
|
||||||
Activity::add($book, 'book_sort', $book->id);
|
Activity::addForEntity($book, ActivityType::BOOK_SORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
return redirect($book->getUrl());
|
return redirect($book->getUrl());
|
||||||
|
@ -92,7 +92,6 @@ class BookshelfController extends Controller
|
|||||||
$shelf = $this->bookshelfRepo->create($request->all(), $bookIds);
|
$shelf = $this->bookshelfRepo->create($request->all(), $bookIds);
|
||||||
$this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null));
|
$this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null));
|
||||||
|
|
||||||
Activity::add($shelf, 'bookshelf_create');
|
|
||||||
return redirect($shelf->getUrl());
|
return redirect($shelf->getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +155,6 @@ class BookshelfController extends Controller
|
|||||||
$shelf = $this->bookshelfRepo->update($shelf, $request->all(), $bookIds);
|
$shelf = $this->bookshelfRepo->update($shelf, $request->all(), $bookIds);
|
||||||
$resetCover = $request->has('image_reset');
|
$resetCover = $request->has('image_reset');
|
||||||
$this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null), $resetCover);
|
$this->bookshelfRepo->updateCoverImage($shelf, $request->file('image', null), $resetCover);
|
||||||
Activity::add($shelf, 'bookshelf_update');
|
|
||||||
|
|
||||||
return redirect($shelf->getUrl());
|
return redirect($shelf->getUrl());
|
||||||
}
|
}
|
||||||
@ -182,7 +180,6 @@ class BookshelfController extends Controller
|
|||||||
$shelf = $this->bookshelfRepo->getBySlug($slug);
|
$shelf = $this->bookshelfRepo->getBySlug($slug);
|
||||||
$this->checkOwnablePermission('bookshelf-delete', $shelf);
|
$this->checkOwnablePermission('bookshelf-delete', $shelf);
|
||||||
|
|
||||||
Activity::add($shelf, 'bookshelf_delete');
|
|
||||||
$this->bookshelfRepo->destroy($shelf);
|
$this->bookshelfRepo->destroy($shelf);
|
||||||
|
|
||||||
return redirect('/shelves');
|
return redirect('/shelves');
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
use Activity;
|
|
||||||
use BookStack\Entities\Book;
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Managers\BookContents;
|
use BookStack\Entities\Managers\BookContents;
|
||||||
use BookStack\Entities\Repos\ChapterRepo;
|
use BookStack\Entities\Repos\ChapterRepo;
|
||||||
@ -51,7 +50,6 @@ class ChapterController extends Controller
|
|||||||
$this->checkOwnablePermission('chapter-create', $book);
|
$this->checkOwnablePermission('chapter-create', $book);
|
||||||
|
|
||||||
$chapter = $this->chapterRepo->create($request->all(), $book);
|
$chapter = $this->chapterRepo->create($request->all(), $book);
|
||||||
Activity::add($chapter, 'chapter_create', $book->id);
|
|
||||||
|
|
||||||
return redirect($chapter->getUrl());
|
return redirect($chapter->getUrl());
|
||||||
}
|
}
|
||||||
@ -100,7 +98,6 @@ class ChapterController extends Controller
|
|||||||
$this->checkOwnablePermission('chapter-update', $chapter);
|
$this->checkOwnablePermission('chapter-update', $chapter);
|
||||||
|
|
||||||
$this->chapterRepo->update($chapter, $request->all());
|
$this->chapterRepo->update($chapter, $request->all());
|
||||||
Activity::add($chapter, 'chapter_update', $chapter->book->id);
|
|
||||||
|
|
||||||
return redirect($chapter->getUrl());
|
return redirect($chapter->getUrl());
|
||||||
}
|
}
|
||||||
@ -128,7 +125,6 @@ class ChapterController extends Controller
|
|||||||
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
|
$chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
|
||||||
$this->checkOwnablePermission('chapter-delete', $chapter);
|
$this->checkOwnablePermission('chapter-delete', $chapter);
|
||||||
|
|
||||||
Activity::add($chapter, 'chapter_delete', $chapter->book->id);
|
|
||||||
$this->chapterRepo->destroy($chapter);
|
$this->chapterRepo->destroy($chapter);
|
||||||
|
|
||||||
return redirect($chapter->book->getUrl());
|
return redirect($chapter->book->getUrl());
|
||||||
@ -173,8 +169,6 @@ class ChapterController extends Controller
|
|||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Activity::add($chapter, 'chapter_move', $newBook->id);
|
|
||||||
|
|
||||||
$this->showSuccessNotification(trans('entities.chapter_move_success', ['bookName' => $newBook->name]));
|
$this->showSuccessNotification(trans('entities.chapter_move_success', ['bookName' => $newBook->name]));
|
||||||
return redirect($chapter->getUrl());
|
return redirect($chapter->getUrl());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
use Activity;
|
use Activity;
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Actions\CommentRepo;
|
use BookStack\Actions\CommentRepo;
|
||||||
use BookStack\Entities\Page;
|
use BookStack\Entities\Page;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -40,7 +41,6 @@ class CommentController extends Controller
|
|||||||
// Create a new comment.
|
// Create a new comment.
|
||||||
$this->checkPermission('comment-create-all');
|
$this->checkPermission('comment-create-all');
|
||||||
$comment = $this->commentRepo->create($page, $request->get('text'), $request->get('parent_id'));
|
$comment = $this->commentRepo->create($page, $request->get('text'), $request->get('parent_id'));
|
||||||
Activity::add($page, 'commented_on', $page->book->id);
|
|
||||||
return view('comments.comment', ['comment' => $comment]);
|
return view('comments.comment', ['comment' => $comment]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,21 +2,20 @@
|
|||||||
|
|
||||||
namespace BookStack\Http\Controllers;
|
namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Facades\Activity;
|
||||||
|
use BookStack\Interfaces\Loggable;
|
||||||
use BookStack\Ownable;
|
use BookStack\Ownable;
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Http\Exceptions\HttpResponseException;
|
use Illuminate\Http\Exceptions\HttpResponseException;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Response;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
|
|
||||||
abstract class Controller extends BaseController
|
abstract class Controller extends BaseController
|
||||||
{
|
{
|
||||||
use DispatchesJobs, ValidatesRequests;
|
use DispatchesJobs, ValidatesRequests;
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@ -43,9 +42,8 @@ abstract class Controller extends BaseController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the page title into the view.
|
* Adds the page title into the view.
|
||||||
* @param $title
|
|
||||||
*/
|
*/
|
||||||
public function setPageTitle($title)
|
public function setPageTitle(string $title)
|
||||||
{
|
{
|
||||||
view()->share('pageTitle', $title);
|
view()->share('pageTitle', $title);
|
||||||
}
|
}
|
||||||
@ -67,79 +65,59 @@ abstract class Controller extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for a permission.
|
* Checks that the current user has the given permission otherwise throw an exception.
|
||||||
* @param string $permissionName
|
|
||||||
* @return bool|\Illuminate\Http\RedirectResponse
|
|
||||||
*/
|
*/
|
||||||
protected function checkPermission($permissionName)
|
protected function checkPermission(string $permission): void
|
||||||
{
|
{
|
||||||
if (!user() || !user()->can($permissionName)) {
|
if (!user() || !user()->can($permission)) {
|
||||||
$this->showPermissionError();
|
$this->showPermissionError();
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the current user's permissions against an ownable item.
|
* Check the current user's permissions against an ownable item otherwise throw an exception.
|
||||||
* @param $permission
|
|
||||||
* @param Ownable $ownable
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
protected function checkOwnablePermission($permission, Ownable $ownable)
|
protected function checkOwnablePermission(string $permission, Ownable $ownable): void
|
||||||
{
|
{
|
||||||
if (userCan($permission, $ownable)) {
|
if (!userCan($permission, $ownable)) {
|
||||||
return true;
|
$this->showPermissionError();
|
||||||
}
|
}
|
||||||
return $this->showPermissionError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a user has a permission or bypass if the callback is true.
|
* Check if a user has a permission or bypass the permission
|
||||||
* @param $permissionName
|
* check if the given callback resolves true.
|
||||||
* @param $callback
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
protected function checkPermissionOr($permissionName, $callback)
|
protected function checkPermissionOr(string $permission, callable $callback): void
|
||||||
{
|
{
|
||||||
$callbackResult = $callback();
|
if ($callback() !== true) {
|
||||||
if ($callbackResult === false) {
|
$this->checkPermission($permission);
|
||||||
$this->checkPermission($permissionName);
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current user has a permission or bypass if the provided user
|
* Check if the current user has a permission or bypass if the provided user
|
||||||
* id matches the current user.
|
* id matches the current user.
|
||||||
* @param string $permissionName
|
|
||||||
* @param int $userId
|
|
||||||
* @return bool
|
|
||||||
*/
|
*/
|
||||||
protected function checkPermissionOrCurrentUser(string $permissionName, int $userId)
|
protected function checkPermissionOrCurrentUser(string $permission, int $userId): void
|
||||||
{
|
{
|
||||||
return $this->checkPermissionOr($permissionName, function () use ($userId) {
|
$this->checkPermissionOr($permission, function () use ($userId) {
|
||||||
return $userId === user()->id;
|
return $userId === user()->id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send back a json error message.
|
* Send back a json error message.
|
||||||
* @param string $messageText
|
|
||||||
* @param int $statusCode
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
protected function jsonError($messageText = "", $statusCode = 500)
|
protected function jsonError(string $messageText = "", int $statusCode = 500): JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json(['message' => $messageText, 'status' => 'error'], $statusCode);
|
return response()->json(['message' => $messageText, 'status' => 'error'], $statusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a response that forces a download in the browser.
|
* Create a response that forces a download in the browser.
|
||||||
* @param string $content
|
|
||||||
* @param string $fileName
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
*/
|
||||||
protected function downloadResponse(string $content, string $fileName)
|
protected function downloadResponse(string $content, string $fileName): Response
|
||||||
{
|
{
|
||||||
return response()->make($content, 200, [
|
return response()->make($content, 200, [
|
||||||
'Content-Type' => 'application/octet-stream',
|
'Content-Type' => 'application/octet-stream',
|
||||||
@ -149,31 +127,37 @@ abstract class Controller extends BaseController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a positive, successful notification to the user on next view load.
|
* Show a positive, successful notification to the user on next view load.
|
||||||
* @param string $message
|
|
||||||
*/
|
*/
|
||||||
protected function showSuccessNotification(string $message)
|
protected function showSuccessNotification(string $message): void
|
||||||
{
|
{
|
||||||
session()->flash('success', $message);
|
session()->flash('success', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show a warning notification to the user on next view load.
|
* Show a warning notification to the user on next view load.
|
||||||
* @param string $message
|
|
||||||
*/
|
*/
|
||||||
protected function showWarningNotification(string $message)
|
protected function showWarningNotification(string $message): void
|
||||||
{
|
{
|
||||||
session()->flash('warning', $message);
|
session()->flash('warning', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show an error notification to the user on next view load.
|
* Show an error notification to the user on next view load.
|
||||||
* @param string $message
|
|
||||||
*/
|
*/
|
||||||
protected function showErrorNotification(string $message)
|
protected function showErrorNotification(string $message): void
|
||||||
{
|
{
|
||||||
session()->flash('error', $message);
|
session()->flash('error', $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an activity in the system.
|
||||||
|
* @param string|Loggable
|
||||||
|
*/
|
||||||
|
protected function logActivity(string $type, $detail = ''): void
|
||||||
|
{
|
||||||
|
Activity::add($type, $detail);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the validation rules for image files.
|
* Get the validation rules for image files.
|
||||||
*/
|
*/
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Http\Controllers;
|
namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Managers\TrashCan;
|
use BookStack\Entities\Managers\TrashCan;
|
||||||
use BookStack\Notifications\TestEmail;
|
use BookStack\Notifications\TestEmail;
|
||||||
use BookStack\Uploads\ImageService;
|
use BookStack\Uploads\ImageService;
|
||||||
@ -35,6 +36,7 @@ class MaintenanceController extends Controller
|
|||||||
public function cleanupImages(Request $request, ImageService $imageService)
|
public function cleanupImages(Request $request, ImageService $imageService)
|
||||||
{
|
{
|
||||||
$this->checkPermission('settings-manage');
|
$this->checkPermission('settings-manage');
|
||||||
|
$this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'cleanup-images');
|
||||||
|
|
||||||
$checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
|
$checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
|
||||||
$dryRun = !($request->has('confirm'));
|
$dryRun = !($request->has('confirm'));
|
||||||
@ -61,6 +63,7 @@ class MaintenanceController extends Controller
|
|||||||
public function sendTestEmail()
|
public function sendTestEmail()
|
||||||
{
|
{
|
||||||
$this->checkPermission('settings-manage');
|
$this->checkPermission('settings-manage');
|
||||||
|
$this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'send-test-email');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
user()->notify(new TestEmail());
|
user()->notify(new TestEmail());
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
use Activity;
|
use Activity;
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Managers\BookContents;
|
use BookStack\Entities\Managers\BookContents;
|
||||||
use BookStack\Entities\Managers\PageContent;
|
use BookStack\Entities\Managers\PageContent;
|
||||||
use BookStack\Entities\Managers\PageEditActivity;
|
use BookStack\Entities\Managers\PageEditActivity;
|
||||||
@ -107,7 +108,6 @@ class PageController extends Controller
|
|||||||
$this->checkOwnablePermission('page-create', $draftPage->getParent());
|
$this->checkOwnablePermission('page-create', $draftPage->getParent());
|
||||||
|
|
||||||
$page = $this->pageRepo->publishDraft($draftPage, $request->all());
|
$page = $this->pageRepo->publishDraft($draftPage, $request->all());
|
||||||
Activity::add($page, 'page_create', $draftPage->book->id);
|
|
||||||
|
|
||||||
return redirect($page->getUrl());
|
return redirect($page->getUrl());
|
||||||
}
|
}
|
||||||
@ -224,7 +224,6 @@ class PageController extends Controller
|
|||||||
$this->checkOwnablePermission('page-update', $page);
|
$this->checkOwnablePermission('page-update', $page);
|
||||||
|
|
||||||
$this->pageRepo->update($page, $request->all());
|
$this->pageRepo->update($page, $request->all());
|
||||||
Activity::add($page, 'page_update', $page->book->id);
|
|
||||||
|
|
||||||
return redirect($page->getUrl());
|
return redirect($page->getUrl());
|
||||||
}
|
}
|
||||||
@ -304,11 +303,9 @@ class PageController extends Controller
|
|||||||
{
|
{
|
||||||
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
|
$page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
|
||||||
$this->checkOwnablePermission('page-delete', $page);
|
$this->checkOwnablePermission('page-delete', $page);
|
||||||
|
$parent = $page->getParent();
|
||||||
|
|
||||||
$book = $page->book;
|
|
||||||
$parent = $page->chapter ?? $book;
|
|
||||||
$this->pageRepo->destroy($page);
|
$this->pageRepo->destroy($page);
|
||||||
Activity::add($page, 'page_delete', $page->book_id);
|
|
||||||
|
|
||||||
return redirect($parent->getUrl());
|
return redirect($parent->getUrl());
|
||||||
}
|
}
|
||||||
@ -393,7 +390,6 @@ class PageController extends Controller
|
|||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Activity::add($page, 'page_move', $page->book->id);
|
|
||||||
$this->showSuccessNotification(trans('entities.pages_move_success', ['parentName' => $parent->name]));
|
$this->showSuccessNotification(trans('entities.pages_move_success', ['parentName' => $parent->name]));
|
||||||
return redirect($page->getUrl());
|
return redirect($page->getUrl());
|
||||||
}
|
}
|
||||||
@ -438,8 +434,6 @@ class PageController extends Controller
|
|||||||
return redirect()->back();
|
return redirect()->back();
|
||||||
}
|
}
|
||||||
|
|
||||||
Activity::add($pageCopy, 'page_create', $pageCopy->book->id);
|
|
||||||
|
|
||||||
$this->showSuccessNotification(trans('entities.pages_copy_success'));
|
$this->showSuccessNotification(trans('entities.pages_copy_success'));
|
||||||
return redirect($pageCopy->getUrl());
|
return redirect($pageCopy->getUrl());
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Managers\PageContent;
|
use BookStack\Entities\Managers\PageContent;
|
||||||
use BookStack\Entities\Repos\PageRepo;
|
use BookStack\Entities\Repos\PageRepo;
|
||||||
use BookStack\Exceptions\NotFoundException;
|
use BookStack\Exceptions\NotFoundException;
|
||||||
@ -101,7 +102,6 @@ class PageRevisionController extends Controller
|
|||||||
|
|
||||||
$page = $this->pageRepo->restoreRevision($page, $revisionId);
|
$page = $this->pageRepo->restoreRevision($page, $revisionId);
|
||||||
|
|
||||||
Activity::add($page, 'page_restore', $page->book->id);
|
|
||||||
return redirect($page->getUrl());
|
return redirect($page->getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Entities\Deletion;
|
use BookStack\Entities\Deletion;
|
||||||
use BookStack\Entities\Managers\TrashCan;
|
use BookStack\Entities\Managers\TrashCan;
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ class RecycleBinController extends Controller
|
|||||||
{
|
{
|
||||||
/** @var Deletion $deletion */
|
/** @var Deletion $deletion */
|
||||||
$deletion = Deletion::query()->findOrFail($id);
|
$deletion = Deletion::query()->findOrFail($id);
|
||||||
|
$this->logActivity(ActivityType::RECYCLE_BIN_RESTORE, $deletion);
|
||||||
$restoreCount = (new TrashCan())->restoreFromDeletion($deletion);
|
$restoreCount = (new TrashCan())->restoreFromDeletion($deletion);
|
||||||
|
|
||||||
$this->showSuccessNotification(trans('settings.recycle_bin_restore_notification', ['count' => $restoreCount]));
|
$this->showSuccessNotification(trans('settings.recycle_bin_restore_notification', ['count' => $restoreCount]));
|
||||||
@ -83,6 +85,7 @@ class RecycleBinController extends Controller
|
|||||||
{
|
{
|
||||||
/** @var Deletion $deletion */
|
/** @var Deletion $deletion */
|
||||||
$deletion = Deletion::query()->findOrFail($id);
|
$deletion = Deletion::query()->findOrFail($id);
|
||||||
|
$this->logActivity(ActivityType::RECYCLE_BIN_DESTROY, $deletion);
|
||||||
$deleteCount = (new TrashCan())->destroyFromDeletion($deletion);
|
$deleteCount = (new TrashCan())->destroyFromDeletion($deletion);
|
||||||
|
|
||||||
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
|
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
|
||||||
@ -97,6 +100,7 @@ class RecycleBinController extends Controller
|
|||||||
{
|
{
|
||||||
$deleteCount = (new TrashCan())->empty();
|
$deleteCount = (new TrashCan())->empty();
|
||||||
|
|
||||||
|
$this->logActivity(ActivityType::RECYCLE_BIN_EMPTY);
|
||||||
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
|
$this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount]));
|
||||||
return redirect($this->recycleBinBaseUrl);
|
return redirect($this->recycleBinBaseUrl);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use Exception;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
class PermissionController extends Controller
|
class RoleController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $permissionsRepo;
|
protected $permissionsRepo;
|
||||||
@ -23,7 +23,7 @@ class PermissionController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Show a listing of the roles in the system.
|
* Show a listing of the roles in the system.
|
||||||
*/
|
*/
|
||||||
public function listRoles()
|
public function list()
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
$roles = $this->permissionsRepo->getAllRoles();
|
$roles = $this->permissionsRepo->getAllRoles();
|
||||||
@ -33,7 +33,7 @@ class PermissionController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Show the form to create a new role
|
* Show the form to create a new role
|
||||||
*/
|
*/
|
||||||
public function createRole()
|
public function create()
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
return view('settings.roles.create');
|
return view('settings.roles.create');
|
||||||
@ -42,7 +42,7 @@ class PermissionController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Store a new role in the system.
|
* Store a new role in the system.
|
||||||
*/
|
*/
|
||||||
public function storeRole(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
@ -59,7 +59,7 @@ class PermissionController extends Controller
|
|||||||
* Show the form for editing a user role.
|
* Show the form for editing a user role.
|
||||||
* @throws PermissionsException
|
* @throws PermissionsException
|
||||||
*/
|
*/
|
||||||
public function editRole(string $id)
|
public function edit(string $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
$role = $this->permissionsRepo->getRoleById($id);
|
$role = $this->permissionsRepo->getRoleById($id);
|
||||||
@ -73,7 +73,7 @@ class PermissionController extends Controller
|
|||||||
* Updates a user role.
|
* Updates a user role.
|
||||||
* @throws ValidationException
|
* @throws ValidationException
|
||||||
*/
|
*/
|
||||||
public function updateRole(Request $request, string $id)
|
public function update(Request $request, string $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
$this->validate($request, [
|
$this->validate($request, [
|
||||||
@ -90,7 +90,7 @@ class PermissionController extends Controller
|
|||||||
* Show the view to delete a role.
|
* Show the view to delete a role.
|
||||||
* Offers the chance to migrate users.
|
* Offers the chance to migrate users.
|
||||||
*/
|
*/
|
||||||
public function showDeleteRole(string $id)
|
public function showDelete(string $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
$role = $this->permissionsRepo->getRoleById($id);
|
$role = $this->permissionsRepo->getRoleById($id);
|
||||||
@ -105,7 +105,7 @@ class PermissionController extends Controller
|
|||||||
* Migrate from a previous role if set.
|
* Migrate from a previous role if set.
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function deleteRole(Request $request, string $id)
|
public function delete(Request $request, string $id)
|
||||||
{
|
{
|
||||||
$this->checkPermission('user-roles-manage');
|
$this->checkPermission('user-roles-manage');
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use BookStack\Uploads\ImageRepo;
|
use BookStack\Uploads\ImageRepo;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@ -47,10 +48,10 @@ class SettingController extends Controller
|
|||||||
|
|
||||||
// Cycles through posted settings and update them
|
// Cycles through posted settings and update them
|
||||||
foreach ($request->all() as $name => $value) {
|
foreach ($request->all() as $name => $value) {
|
||||||
|
$key = str_replace('setting-', '', trim($name));
|
||||||
if (strpos($name, 'setting-') !== 0) {
|
if (strpos($name, 'setting-') !== 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$key = str_replace('setting-', '', trim($name));
|
|
||||||
setting()->put($key, $value);
|
setting()->put($key, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +69,10 @@ class SettingController extends Controller
|
|||||||
setting()->remove('app-logo');
|
setting()->remove('app-logo');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$section = $request->get('section', '');
|
||||||
|
$this->logActivity(ActivityType::SETTINGS_UPDATE, $section);
|
||||||
$this->showSuccessNotification(trans('settings.settings_save_success'));
|
$this->showSuccessNotification(trans('settings.settings_save_success'));
|
||||||
$redirectLocation = '/settings#' . $request->get('section', '');
|
$redirectLocation = '/settings#' . $section;
|
||||||
return redirect(rtrim($redirectLocation, '#'));
|
return redirect(rtrim($redirectLocation, '#'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
@ -57,6 +57,8 @@ class UserApiTokenController extends Controller
|
|||||||
|
|
||||||
session()->flash('api-token-secret:' . $token->id, $secret);
|
session()->flash('api-token-secret:' . $token->id, $secret);
|
||||||
$this->showSuccessNotification(trans('settings.user_api_token_create_success'));
|
$this->showSuccessNotification(trans('settings.user_api_token_create_success'));
|
||||||
|
$this->logActivity(ActivityType::API_TOKEN_CREATE, $token);
|
||||||
|
|
||||||
return redirect($user->getEditUrl('/api-tokens/' . $token->id));
|
return redirect($user->getEditUrl('/api-tokens/' . $token->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +95,7 @@ class UserApiTokenController extends Controller
|
|||||||
])->save();
|
])->save();
|
||||||
|
|
||||||
$this->showSuccessNotification(trans('settings.user_api_token_update_success'));
|
$this->showSuccessNotification(trans('settings.user_api_token_update_success'));
|
||||||
|
$this->logActivity(ActivityType::API_TOKEN_UPDATE, $token);
|
||||||
return redirect($user->getEditUrl('/api-tokens/' . $token->id));
|
return redirect($user->getEditUrl('/api-tokens/' . $token->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +120,8 @@ class UserApiTokenController extends Controller
|
|||||||
$token->delete();
|
$token->delete();
|
||||||
|
|
||||||
$this->showSuccessNotification(trans('settings.user_api_token_delete_success'));
|
$this->showSuccessNotification(trans('settings.user_api_token_delete_success'));
|
||||||
|
$this->logActivity(ActivityType::API_TOKEN_DELETE, $token);
|
||||||
|
|
||||||
return redirect($user->getEditUrl('#api_tokens'));
|
return redirect($user->getEditUrl('#api_tokens'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack\Http\Controllers;
|
<?php namespace BookStack\Http\Controllers;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\Access\SocialAuthService;
|
use BookStack\Auth\Access\SocialAuthService;
|
||||||
use BookStack\Auth\Access\UserInviteService;
|
use BookStack\Auth\Access\UserInviteService;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
@ -102,6 +103,7 @@ class UserController extends Controller
|
|||||||
|
|
||||||
$this->userRepo->downloadAndAssignUserAvatar($user);
|
$this->userRepo->downloadAndAssignUserAvatar($user);
|
||||||
|
|
||||||
|
$this->logActivity(ActivityType::USER_CREATE, $user);
|
||||||
return redirect('/settings/users');
|
return redirect('/settings/users');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +196,7 @@ class UserController extends Controller
|
|||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
$this->showSuccessNotification(trans('settings.users_edit_success'));
|
$this->showSuccessNotification(trans('settings.users_edit_success'));
|
||||||
|
$this->logActivity(ActivityType::USER_UPDATE, $user);
|
||||||
|
|
||||||
$redirectUrl = userCan('users-manage') ? '/settings/users' : ('/settings/users/' . $user->id);
|
$redirectUrl = userCan('users-manage') ? '/settings/users' : ('/settings/users/' . $user->id);
|
||||||
return redirect($redirectUrl);
|
return redirect($redirectUrl);
|
||||||
@ -234,6 +237,7 @@ class UserController extends Controller
|
|||||||
|
|
||||||
$this->userRepo->destroy($user);
|
$this->userRepo->destroy($user);
|
||||||
$this->showSuccessNotification(trans('settings.users_delete_success'));
|
$this->showSuccessNotification(trans('settings.users_delete_success'));
|
||||||
|
$this->logActivity(ActivityType::USER_DELETE, $user);
|
||||||
|
|
||||||
return redirect('/settings/users');
|
return redirect('/settings/users');
|
||||||
}
|
}
|
||||||
|
11
app/Interfaces/Loggable.php
Normal file
11
app/Interfaces/Loggable.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Interfaces;
|
||||||
|
|
||||||
|
interface Loggable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the string descriptor for this item.
|
||||||
|
*/
|
||||||
|
public function logDescriptor(): string;
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class SimplifyActivitiesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('activities', function (Blueprint $table) {
|
||||||
|
$table->renameColumn('key', 'type');
|
||||||
|
$table->renameColumn('extra', 'detail');
|
||||||
|
$table->dropColumn('book_id');
|
||||||
|
$table->integer('entity_id')->nullable()->change();
|
||||||
|
$table->string('entity_type', 191)->nullable()->change();
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::table('activities')
|
||||||
|
->where('entity_id', '=', 0)
|
||||||
|
->update([
|
||||||
|
'entity_id' => null,
|
||||||
|
'entity_type' => null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
DB::table('activities')
|
||||||
|
->whereNull('entity_id')
|
||||||
|
->update([
|
||||||
|
'entity_id' => 0,
|
||||||
|
'entity_type' => '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Schema::table('activities', function (Blueprint $table) {
|
||||||
|
$table->renameColumn('type', 'key');
|
||||||
|
$table->renameColumn('detail', 'extra');
|
||||||
|
$table->integer('book_id');
|
||||||
|
|
||||||
|
$table->integer('entity_id')->change();
|
||||||
|
$table->string('entity_type', 191)->change();
|
||||||
|
|
||||||
|
$table->index('book_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -45,4 +45,5 @@ return [
|
|||||||
|
|
||||||
// Other
|
// Other
|
||||||
'commented_on' => 'commented on',
|
'commented_on' => 'commented on',
|
||||||
|
'permissions_update' => 'updated permissions',
|
||||||
];
|
];
|
||||||
|
@ -111,7 +111,7 @@ return [
|
|||||||
'audit_deleted_item_name' => 'Name: :name',
|
'audit_deleted_item_name' => 'Name: :name',
|
||||||
'audit_table_user' => 'User',
|
'audit_table_user' => 'User',
|
||||||
'audit_table_event' => 'Event',
|
'audit_table_event' => 'Event',
|
||||||
'audit_table_item' => 'Related Item',
|
'audit_table_related' => 'Related Item or Detail',
|
||||||
'audit_table_date' => 'Activity Date',
|
'audit_table_date' => 'Activity Date',
|
||||||
'audit_date_from' => 'Date Range From',
|
'audit_date_from' => 'Date Range From',
|
||||||
'audit_date_to' => 'Date Range To',
|
'audit_date_to' => 'Date Range To',
|
||||||
@ -157,6 +157,7 @@ return [
|
|||||||
'user_profile' => 'User Profile',
|
'user_profile' => 'User Profile',
|
||||||
'users_add_new' => 'Add New User',
|
'users_add_new' => 'Add New User',
|
||||||
'users_search' => 'Search Users',
|
'users_search' => 'Search Users',
|
||||||
|
'users_latest_activity' => 'Latest Activity',
|
||||||
'users_details' => 'User Details',
|
'users_details' => 'User Details',
|
||||||
'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
|
'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
|
||||||
'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
|
'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
<button refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" class="input-base text-left">{{ $listDetails['event'] ?: trans('settings.audit_event_filter_no_filter') }}</button>
|
<button refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" class="input-base text-left">{{ $listDetails['event'] ?: trans('settings.audit_event_filter_no_filter') }}</button>
|
||||||
<ul refs="dropdown@menu" class="dropdown-menu">
|
<ul refs="dropdown@menu" class="dropdown-menu">
|
||||||
<li @if($listDetails['event'] === '') class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => '']) }}">{{ trans('settings.audit_event_filter_no_filter') }}</a></li>
|
<li @if($listDetails['event'] === '') class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => '']) }}">{{ trans('settings.audit_event_filter_no_filter') }}</a></li>
|
||||||
@foreach($activityKeys as $key)
|
@foreach($activityTypes as $type)
|
||||||
<li @if($key === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $key]) }}">{{ $key }}</a></li>
|
<li @if($type === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $type]) }}">{{ $type }}</a></li>
|
||||||
@endforeach
|
@endforeach
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +53,7 @@
|
|||||||
<th>
|
<th>
|
||||||
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'key']) }}">{{ trans('settings.audit_table_event') }}</a>
|
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'key']) }}">{{ trans('settings.audit_table_event') }}</a>
|
||||||
</th>
|
</th>
|
||||||
<th>{{ trans('settings.audit_table_item') }}</th>
|
<th>{{ trans('settings.audit_table_related') }}</th>
|
||||||
<th>
|
<th>
|
||||||
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'created_at']) }}">{{ trans('settings.audit_table_date') }}</a></th>
|
<a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'created_at']) }}">{{ trans('settings.audit_table_date') }}</a></th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<td>
|
<td>
|
||||||
@include('partials.table-user', ['user' => $activity->user, 'user_id' => $activity->user_id])
|
@include('partials.table-user', ['user' => $activity->user, 'user_id' => $activity->user_id])
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $activity->key }}</td>
|
<td>{{ $activity->type }}</td>
|
||||||
<td>
|
<td>
|
||||||
@if($activity->entity)
|
@if($activity->entity)
|
||||||
<a href="{{ $activity->entity->getUrl() }}" class="table-entity-item">
|
<a href="{{ $activity->entity->getUrl() }}" class="table-entity-item">
|
||||||
@ -71,11 +71,13 @@
|
|||||||
{{ $activity->entity->name }}
|
{{ $activity->entity->name }}
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
@elseif($activity->extra)
|
@elseif($activity->detail && $activity->isForEntity())
|
||||||
<div class="px-m">
|
<div class="px-m">
|
||||||
{{ trans('settings.audit_deleted_item') }} <br>
|
{{ trans('settings.audit_deleted_item') }} <br>
|
||||||
{{ trans('settings.audit_deleted_item_name', ['name' => $activity->extra]) }}
|
{{ trans('settings.audit_deleted_item_name', ['name' => $activity->detail]) }}
|
||||||
</div>
|
</div>
|
||||||
|
@elseif($activity->detail)
|
||||||
|
<div class="px-m">{{ $activity->detail }}</div>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $activity->created_at }}</td>
|
<td>{{ $activity->created_at }}</td>
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{--TODO - Add last login--}}
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
@ -37,6 +36,9 @@
|
|||||||
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a>
|
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'email']) }}">{{ trans('auth.email') }}</a>
|
||||||
</th>
|
</th>
|
||||||
<th>{{ trans('settings.role_user_roles') }}</th>
|
<th>{{ trans('settings.role_user_roles') }}</th>
|
||||||
|
<th class="text-right">
|
||||||
|
<a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'latest_activity']) }}">{{ trans('settings.users_latest_activity') }}</a>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@foreach($users as $user)
|
@foreach($users as $user)
|
||||||
<tr>
|
<tr>
|
||||||
@ -55,6 +57,11 @@
|
|||||||
<small><a href="{{ url("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
|
<small><a href="{{ url("/settings/roles/{$role->id}") }}">{{$role->display_name}}</a>@if($index !== count($user->roles) -1),@endif</small>
|
||||||
@endforeach
|
@endforeach
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-right text-muted">
|
||||||
|
@if($user->latestActivity)
|
||||||
|
<small title="{{ $user->latestActivity->created_at->format('Y-m-d H:i:s') }}">{{ $user->latestActivity->created_at->diffForHumans() }}</small>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</table>
|
</table>
|
||||||
|
@ -201,13 +201,13 @@ Route::group(['middleware' => 'auth'], function () {
|
|||||||
Route::delete('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@destroy');
|
Route::delete('/users/{userId}/api-tokens/{tokenId}', 'UserApiTokenController@destroy');
|
||||||
|
|
||||||
// Roles
|
// Roles
|
||||||
Route::get('/roles', 'PermissionController@listRoles');
|
Route::get('/roles', 'RoleController@list');
|
||||||
Route::get('/roles/new', 'PermissionController@createRole');
|
Route::get('/roles/new', 'RoleController@create');
|
||||||
Route::post('/roles/new', 'PermissionController@storeRole');
|
Route::post('/roles/new', 'RoleController@store');
|
||||||
Route::get('/roles/delete/{id}', 'PermissionController@showDeleteRole');
|
Route::get('/roles/delete/{id}', 'RoleController@showDelete');
|
||||||
Route::delete('/roles/delete/{id}', 'PermissionController@deleteRole');
|
Route::delete('/roles/delete/{id}', 'RoleController@delete');
|
||||||
Route::get('/roles/{id}', 'PermissionController@editRole');
|
Route::get('/roles/{id}', 'RoleController@edit');
|
||||||
Route::put('/roles/{id}', 'PermissionController@updateRole');
|
Route::put('/roles/{id}', 'RoleController@update');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use BookStack\Actions\Activity;
|
use BookStack\Actions\Activity;
|
||||||
use BookStack\Actions\ActivityService;
|
use BookStack\Actions\ActivityService;
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\UserRepo;
|
use BookStack\Auth\UserRepo;
|
||||||
use BookStack\Entities\Managers\TrashCan;
|
use BookStack\Entities\Managers\TrashCan;
|
||||||
use BookStack\Entities\Page;
|
use BookStack\Entities\Page;
|
||||||
@ -10,6 +11,14 @@ use Carbon\Carbon;
|
|||||||
|
|
||||||
class AuditLogTest extends TestCase
|
class AuditLogTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/** @var ActivityService */
|
||||||
|
protected $activityService;
|
||||||
|
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->activityService = app(ActivityService::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_only_accessible_with_right_permissions()
|
public function test_only_accessible_with_right_permissions()
|
||||||
{
|
{
|
||||||
@ -34,7 +43,7 @@ class AuditLogTest extends TestCase
|
|||||||
$admin = $this->getAdmin();
|
$admin = $this->getAdmin();
|
||||||
$this->actingAs($admin);
|
$this->actingAs($admin);
|
||||||
$page = Page::query()->first();
|
$page = Page::query()->first();
|
||||||
app(ActivityService::class)->add($page, 'page_create', $page->book->id);
|
$this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
|
||||||
$activity = Activity::query()->orderBy('id', 'desc')->first();
|
$activity = Activity::query()->orderBy('id', 'desc')->first();
|
||||||
|
|
||||||
$resp = $this->get('settings/audit');
|
$resp = $this->get('settings/audit');
|
||||||
@ -49,7 +58,7 @@ class AuditLogTest extends TestCase
|
|||||||
$this->actingAs( $this->getAdmin());
|
$this->actingAs( $this->getAdmin());
|
||||||
$page = Page::query()->first();
|
$page = Page::query()->first();
|
||||||
$pageName = $page->name;
|
$pageName = $page->name;
|
||||||
app(ActivityService::class)->add($page, 'page_create', $page->book->id);
|
$this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
|
||||||
|
|
||||||
app(PageRepo::class)->destroy($page);
|
app(PageRepo::class)->destroy($page);
|
||||||
app(TrashCan::class)->empty();
|
app(TrashCan::class)->empty();
|
||||||
@ -64,7 +73,7 @@ class AuditLogTest extends TestCase
|
|||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$this->actingAs($viewer);
|
$this->actingAs($viewer);
|
||||||
$page = Page::query()->first();
|
$page = Page::query()->first();
|
||||||
app(ActivityService::class)->add($page, 'page_create', $page->book->id);
|
$this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
|
||||||
|
|
||||||
$this->actingAs($this->getAdmin());
|
$this->actingAs($this->getAdmin());
|
||||||
app(UserRepo::class)->destroy($viewer);
|
app(UserRepo::class)->destroy($viewer);
|
||||||
@ -77,7 +86,7 @@ class AuditLogTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->actingAs($this->getAdmin());
|
$this->actingAs($this->getAdmin());
|
||||||
$page = Page::query()->first();
|
$page = Page::query()->first();
|
||||||
app(ActivityService::class)->add($page, 'page_create', $page->book->id);
|
$this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
|
||||||
|
|
||||||
$resp = $this->get('settings/audit');
|
$resp = $this->get('settings/audit');
|
||||||
$resp->assertSeeText($page->name);
|
$resp->assertSeeText($page->name);
|
||||||
@ -90,7 +99,7 @@ class AuditLogTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->actingAs($this->getAdmin());
|
$this->actingAs($this->getAdmin());
|
||||||
$page = Page::query()->first();
|
$page = Page::query()->first();
|
||||||
app(ActivityService::class)->add($page, 'page_create', $page->book->id);
|
$this->activityService->addForEntity($page, ActivityType::PAGE_CREATE);
|
||||||
|
|
||||||
$yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
|
$yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
|
||||||
$tomorrow = (Carbon::now()->addDay()->format('Y-m-d'));
|
$tomorrow = (Carbon::now()->addDay()->format('Y-m-d'));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace Tests;
|
<?php namespace Tests;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Actions\Comment;
|
use BookStack\Actions\Comment;
|
||||||
use BookStack\Actions\CommentRepo;
|
use BookStack\Actions\CommentRepo;
|
||||||
use BookStack\Auth\Permissions\JointPermission;
|
use BookStack\Auth\Permissions\JointPermission;
|
||||||
@ -37,10 +38,10 @@ class CommandsTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->asEditor();
|
$this->asEditor();
|
||||||
$page = Page::first();
|
$page = Page::first();
|
||||||
\Activity::add($page, 'page_update', $page->book->id);
|
\Activity::addForEntity($page, ActivityType::PAGE_UPDATE);
|
||||||
|
|
||||||
$this->assertDatabaseHas('activities', [
|
$this->assertDatabaseHas('activities', [
|
||||||
'key' => 'page_update',
|
'type' => 'page_update',
|
||||||
'entity_id' => $page->id,
|
'entity_id' => $page->id,
|
||||||
'user_id' => $this->getEditor()->id
|
'user_id' => $this->getEditor()->id
|
||||||
]);
|
]);
|
||||||
@ -50,7 +51,7 @@ class CommandsTest extends TestCase
|
|||||||
|
|
||||||
|
|
||||||
$this->assertDatabaseMissing('activities', [
|
$this->assertDatabaseMissing('activities', [
|
||||||
'key' => 'page_update'
|
'type' => 'page_update'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class SortTest extends TestCase
|
|||||||
$movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
|
$movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
|
||||||
'entity_selection' => 'book:' . $newBook->id
|
'entity_selection' => 'book:' . $newBook->id
|
||||||
]);
|
]);
|
||||||
$page = Page::find($page->id);
|
$page->refresh();
|
||||||
|
|
||||||
$movePageResp->assertRedirect($page->getUrl());
|
$movePageResp->assertRedirect($page->getUrl());
|
||||||
$this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new book');
|
$this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new book');
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
<?php namespace Tests\Permissions;
|
<?php namespace Tests\Permissions;
|
||||||
|
|
||||||
|
use BookStack\Actions\Comment;
|
||||||
|
use BookStack\Auth\User;
|
||||||
|
use BookStack\Entities\Book;
|
||||||
use BookStack\Entities\Bookshelf;
|
use BookStack\Entities\Bookshelf;
|
||||||
|
use BookStack\Entities\Chapter;
|
||||||
use BookStack\Entities\Page;
|
use BookStack\Entities\Page;
|
||||||
use BookStack\Auth\Role;
|
use BookStack\Auth\Role;
|
||||||
|
use BookStack\Uploads\Image;
|
||||||
use Laravel\BrowserKitTesting\HttpException;
|
use Laravel\BrowserKitTesting\HttpException;
|
||||||
use Tests\BrowserKitTest;
|
use Tests\BrowserKitTest;
|
||||||
|
|
||||||
@ -23,7 +28,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_cannot_delete_admin_role()
|
public function test_cannot_delete_admin_role()
|
||||||
{
|
{
|
||||||
$adminRole = \BookStack\Auth\Role::getRole('admin');
|
$adminRole = Role::getRole('admin');
|
||||||
$deletePageUrl = '/settings/roles/delete/' . $adminRole->id;
|
$deletePageUrl = '/settings/roles/delete/' . $adminRole->id;
|
||||||
$this->asAdmin()->visit($deletePageUrl)
|
$this->asAdmin()->visit($deletePageUrl)
|
||||||
->press('Confirm')
|
->press('Confirm')
|
||||||
@ -195,7 +200,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_restrictions_manage_all_permission()
|
public function test_restrictions_manage_all_permission()
|
||||||
{
|
{
|
||||||
$page = \BookStack\Entities\Page::take(1)->get()->first();
|
$page = Page::take(1)->get()->first();
|
||||||
$this->actingAs($this->user)->visit($page->getUrl())
|
$this->actingAs($this->user)->visit($page->getUrl())
|
||||||
->dontSee('Permissions')
|
->dontSee('Permissions')
|
||||||
->visit($page->getUrl() . '/permissions')
|
->visit($page->getUrl() . '/permissions')
|
||||||
@ -209,7 +214,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_restrictions_manage_own_permission()
|
public function test_restrictions_manage_own_permission()
|
||||||
{
|
{
|
||||||
$otherUsersPage = \BookStack\Entities\Page::first();
|
$otherUsersPage = Page::first();
|
||||||
$content = $this->createEntityChainBelongingToUser($this->user);
|
$content = $this->createEntityChainBelongingToUser($this->user);
|
||||||
// Check can't restrict other's content
|
// Check can't restrict other's content
|
||||||
$this->actingAs($this->user)->visit($otherUsersPage->getUrl())
|
$this->actingAs($this->user)->visit($otherUsersPage->getUrl())
|
||||||
@ -301,7 +306,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_bookshelves_edit_all_permission()
|
public function test_bookshelves_edit_all_permission()
|
||||||
{
|
{
|
||||||
$otherShelf = \BookStack\Entities\Bookshelf::first();
|
$otherShelf = Bookshelf::first();
|
||||||
$this->checkAccessPermission('bookshelf-update-all', [
|
$this->checkAccessPermission('bookshelf-update-all', [
|
||||||
$otherShelf->getUrl('/edit')
|
$otherShelf->getUrl('/edit')
|
||||||
], [
|
], [
|
||||||
@ -312,7 +317,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_bookshelves_delete_own_permission()
|
public function test_bookshelves_delete_own_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
|
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
|
||||||
$otherShelf = \BookStack\Entities\Bookshelf::first();
|
$otherShelf = Bookshelf::first();
|
||||||
$ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
|
$ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
|
||||||
$ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
|
$ownShelf->forceFill(['created_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
|
||||||
$this->regenEntityPermissions($ownShelf);
|
$this->regenEntityPermissions($ownShelf);
|
||||||
@ -336,7 +341,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_bookshelves_delete_all_permission()
|
public function test_bookshelves_delete_all_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
|
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
|
||||||
$otherShelf = \BookStack\Entities\Bookshelf::first();
|
$otherShelf = Bookshelf::first();
|
||||||
$this->checkAccessPermission('bookshelf-delete-all', [
|
$this->checkAccessPermission('bookshelf-delete-all', [
|
||||||
$otherShelf->getUrl('/delete')
|
$otherShelf->getUrl('/delete')
|
||||||
], [
|
], [
|
||||||
@ -366,7 +371,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_books_edit_own_permission()
|
public function test_books_edit_own_permission()
|
||||||
{
|
{
|
||||||
$otherBook = \BookStack\Entities\Book::take(1)->get()->first();
|
$otherBook = Book::take(1)->get()->first();
|
||||||
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
||||||
$this->checkAccessPermission('book-update-own', [
|
$this->checkAccessPermission('book-update-own', [
|
||||||
$ownBook->getUrl() . '/edit'
|
$ownBook->getUrl() . '/edit'
|
||||||
@ -382,7 +387,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_books_edit_all_permission()
|
public function test_books_edit_all_permission()
|
||||||
{
|
{
|
||||||
$otherBook = \BookStack\Entities\Book::take(1)->get()->first();
|
$otherBook = Book::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('book-update-all', [
|
$this->checkAccessPermission('book-update-all', [
|
||||||
$otherBook->getUrl() . '/edit'
|
$otherBook->getUrl() . '/edit'
|
||||||
], [
|
], [
|
||||||
@ -393,7 +398,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_books_delete_own_permission()
|
public function test_books_delete_own_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['book-update-all']);
|
$this->giveUserPermissions($this->user, ['book-update-all']);
|
||||||
$otherBook = \BookStack\Entities\Book::take(1)->get()->first();
|
$otherBook = Book::take(1)->get()->first();
|
||||||
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
||||||
$this->checkAccessPermission('book-delete-own', [
|
$this->checkAccessPermission('book-delete-own', [
|
||||||
$ownBook->getUrl() . '/delete'
|
$ownBook->getUrl() . '/delete'
|
||||||
@ -414,7 +419,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_books_delete_all_permission()
|
public function test_books_delete_all_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['book-update-all']);
|
$this->giveUserPermissions($this->user, ['book-update-all']);
|
||||||
$otherBook = \BookStack\Entities\Book::take(1)->get()->first();
|
$otherBook = Book::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('book-delete-all', [
|
$this->checkAccessPermission('book-delete-all', [
|
||||||
$otherBook->getUrl() . '/delete'
|
$otherBook->getUrl() . '/delete'
|
||||||
], [
|
], [
|
||||||
@ -429,7 +434,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_chapter_create_own_permissions()
|
public function test_chapter_create_own_permissions()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Entities\Book::take(1)->get()->first();
|
$book = Book::take(1)->get()->first();
|
||||||
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
$ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
|
||||||
$this->checkAccessPermission('chapter-create-own', [
|
$this->checkAccessPermission('chapter-create-own', [
|
||||||
$ownBook->getUrl('/create-chapter')
|
$ownBook->getUrl('/create-chapter')
|
||||||
@ -451,7 +456,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_chapter_create_all_permissions()
|
public function test_chapter_create_all_permissions()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Entities\Book::take(1)->get()->first();
|
$book = Book::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('chapter-create-all', [
|
$this->checkAccessPermission('chapter-create-all', [
|
||||||
$book->getUrl('/create-chapter')
|
$book->getUrl('/create-chapter')
|
||||||
], [
|
], [
|
||||||
@ -467,7 +472,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_chapter_edit_own_permission()
|
public function test_chapter_edit_own_permission()
|
||||||
{
|
{
|
||||||
$otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
|
$otherChapter = Chapter::take(1)->get()->first();
|
||||||
$ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
|
$ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
|
||||||
$this->checkAccessPermission('chapter-update-own', [
|
$this->checkAccessPermission('chapter-update-own', [
|
||||||
$ownChapter->getUrl() . '/edit'
|
$ownChapter->getUrl() . '/edit'
|
||||||
@ -483,7 +488,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_chapter_edit_all_permission()
|
public function test_chapter_edit_all_permission()
|
||||||
{
|
{
|
||||||
$otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
|
$otherChapter = Chapter::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('chapter-update-all', [
|
$this->checkAccessPermission('chapter-update-all', [
|
||||||
$otherChapter->getUrl() . '/edit'
|
$otherChapter->getUrl() . '/edit'
|
||||||
], [
|
], [
|
||||||
@ -494,7 +499,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_chapter_delete_own_permission()
|
public function test_chapter_delete_own_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['chapter-update-all']);
|
$this->giveUserPermissions($this->user, ['chapter-update-all']);
|
||||||
$otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
|
$otherChapter = Chapter::take(1)->get()->first();
|
||||||
$ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
|
$ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
|
||||||
$this->checkAccessPermission('chapter-delete-own', [
|
$this->checkAccessPermission('chapter-delete-own', [
|
||||||
$ownChapter->getUrl() . '/delete'
|
$ownChapter->getUrl() . '/delete'
|
||||||
@ -516,7 +521,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_chapter_delete_all_permission()
|
public function test_chapter_delete_all_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['chapter-update-all']);
|
$this->giveUserPermissions($this->user, ['chapter-update-all']);
|
||||||
$otherChapter = \BookStack\Entities\Chapter::take(1)->get()->first();
|
$otherChapter = Chapter::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('chapter-delete-all', [
|
$this->checkAccessPermission('chapter-delete-all', [
|
||||||
$otherChapter->getUrl() . '/delete'
|
$otherChapter->getUrl() . '/delete'
|
||||||
], [
|
], [
|
||||||
@ -532,8 +537,8 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_page_create_own_permissions()
|
public function test_page_create_own_permissions()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Entities\Book::first();
|
$book = Book::first();
|
||||||
$chapter = \BookStack\Entities\Chapter::first();
|
$chapter = Chapter::first();
|
||||||
|
|
||||||
$entities = $this->createEntityChainBelongingToUser($this->user);
|
$entities = $this->createEntityChainBelongingToUser($this->user);
|
||||||
$ownBook = $entities['book'];
|
$ownBook = $entities['book'];
|
||||||
@ -557,7 +562,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
foreach ($accessUrls as $index => $url) {
|
foreach ($accessUrls as $index => $url) {
|
||||||
$this->actingAs($this->user)->visit($url);
|
$this->actingAs($this->user)->visit($url);
|
||||||
$expectedUrl = \BookStack\Entities\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
|
$expectedUrl = Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
|
||||||
$this->seePageIs($expectedUrl);
|
$this->seePageIs($expectedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,8 +584,8 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_page_create_all_permissions()
|
public function test_page_create_all_permissions()
|
||||||
{
|
{
|
||||||
$book = \BookStack\Entities\Book::take(1)->get()->first();
|
$book = Book::take(1)->get()->first();
|
||||||
$chapter = \BookStack\Entities\Chapter::take(1)->get()->first();
|
$chapter = Chapter::take(1)->get()->first();
|
||||||
$baseUrl = $book->getUrl() . '/page';
|
$baseUrl = $book->getUrl() . '/page';
|
||||||
$createUrl = $book->getUrl('/create-page');
|
$createUrl = $book->getUrl('/create-page');
|
||||||
|
|
||||||
@ -601,7 +606,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
foreach ($accessUrls as $index => $url) {
|
foreach ($accessUrls as $index => $url) {
|
||||||
$this->actingAs($this->user)->visit($url);
|
$this->actingAs($this->user)->visit($url);
|
||||||
$expectedUrl = \BookStack\Entities\Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
|
$expectedUrl = Page::where('draft', '=', true)->orderBy('id', 'desc')->first()->getUrl();
|
||||||
$this->seePageIs($expectedUrl);
|
$this->seePageIs($expectedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,7 +625,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_page_edit_own_permission()
|
public function test_page_edit_own_permission()
|
||||||
{
|
{
|
||||||
$otherPage = \BookStack\Entities\Page::take(1)->get()->first();
|
$otherPage = Page::take(1)->get()->first();
|
||||||
$ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
|
$ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
|
||||||
$this->checkAccessPermission('page-update-own', [
|
$this->checkAccessPermission('page-update-own', [
|
||||||
$ownPage->getUrl() . '/edit'
|
$ownPage->getUrl() . '/edit'
|
||||||
@ -636,7 +641,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_page_edit_all_permission()
|
public function test_page_edit_all_permission()
|
||||||
{
|
{
|
||||||
$otherPage = \BookStack\Entities\Page::take(1)->get()->first();
|
$otherPage = Page::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('page-update-all', [
|
$this->checkAccessPermission('page-update-all', [
|
||||||
$otherPage->getUrl() . '/edit'
|
$otherPage->getUrl() . '/edit'
|
||||||
], [
|
], [
|
||||||
@ -647,7 +652,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_page_delete_own_permission()
|
public function test_page_delete_own_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['page-update-all']);
|
$this->giveUserPermissions($this->user, ['page-update-all']);
|
||||||
$otherPage = \BookStack\Entities\Page::take(1)->get()->first();
|
$otherPage = Page::take(1)->get()->first();
|
||||||
$ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
|
$ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
|
||||||
$this->checkAccessPermission('page-delete-own', [
|
$this->checkAccessPermission('page-delete-own', [
|
||||||
$ownPage->getUrl() . '/delete'
|
$ownPage->getUrl() . '/delete'
|
||||||
@ -669,7 +674,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_page_delete_all_permission()
|
public function test_page_delete_all_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['page-update-all']);
|
$this->giveUserPermissions($this->user, ['page-update-all']);
|
||||||
$otherPage = \BookStack\Entities\Page::take(1)->get()->first();
|
$otherPage = Page::take(1)->get()->first();
|
||||||
$this->checkAccessPermission('page-delete-all', [
|
$this->checkAccessPermission('page-delete-all', [
|
||||||
$otherPage->getUrl() . '/delete'
|
$otherPage->getUrl() . '/delete'
|
||||||
], [
|
], [
|
||||||
@ -685,7 +690,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
|
|
||||||
public function test_public_role_visible_in_user_edit_screen()
|
public function test_public_role_visible_in_user_edit_screen()
|
||||||
{
|
{
|
||||||
$user = \BookStack\Auth\User::first();
|
$user = User::first();
|
||||||
$adminRole = Role::getSystemRole('admin');
|
$adminRole = Role::getSystemRole('admin');
|
||||||
$publicRole = Role::getSystemRole('public');
|
$publicRole = Role::getSystemRole('public');
|
||||||
$this->asAdmin()->visit('/settings/users/' . $user->id)
|
$this->asAdmin()->visit('/settings/users/' . $user->id)
|
||||||
@ -721,8 +726,8 @@ class RolesTest extends BrowserKitTest
|
|||||||
public function test_image_delete_own_permission()
|
public function test_image_delete_own_permission()
|
||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['image-update-all']);
|
$this->giveUserPermissions($this->user, ['image-update-all']);
|
||||||
$page = \BookStack\Entities\Page::first();
|
$page = Page::first();
|
||||||
$image = factory(\BookStack\Uploads\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);
|
$image = factory(Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $this->user->id, 'updated_by' => $this->user->id]);
|
||||||
|
|
||||||
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)
|
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)
|
||||||
->seeStatusCode(403);
|
->seeStatusCode(403);
|
||||||
@ -738,8 +743,8 @@ class RolesTest extends BrowserKitTest
|
|||||||
{
|
{
|
||||||
$this->giveUserPermissions($this->user, ['image-update-all']);
|
$this->giveUserPermissions($this->user, ['image-update-all']);
|
||||||
$admin = $this->getAdmin();
|
$admin = $this->getAdmin();
|
||||||
$page = \BookStack\Entities\Page::first();
|
$page = Page::first();
|
||||||
$image = factory(\BookStack\Uploads\Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
|
$image = factory(Image::class)->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
|
||||||
|
|
||||||
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)
|
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)
|
||||||
->seeStatusCode(403);
|
->seeStatusCode(403);
|
||||||
@ -760,7 +765,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
{
|
{
|
||||||
// To cover issue fixed in f99c8ff99aee9beb8c692f36d4b84dc6e651e50a.
|
// To cover issue fixed in f99c8ff99aee9beb8c692f36d4b84dc6e651e50a.
|
||||||
$page = Page::first();
|
$page = Page::first();
|
||||||
$viewerRole = \BookStack\Auth\Role::getRole('viewer');
|
$viewerRole = Role::getRole('viewer');
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
$this->actingAs($viewer)->visit($page->getUrl())->assertResponseStatus(200);
|
$this->actingAs($viewer)->visit($page->getUrl())->assertResponseStatus(200);
|
||||||
|
|
||||||
@ -778,14 +783,14 @@ class RolesTest extends BrowserKitTest
|
|||||||
{
|
{
|
||||||
$admin = $this->getAdmin();
|
$admin = $this->getAdmin();
|
||||||
// Book links
|
// Book links
|
||||||
$book = factory(\BookStack\Entities\Book::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
|
$book = factory(Book::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
|
||||||
$this->updateEntityPermissions($book);
|
$this->updateEntityPermissions($book);
|
||||||
$this->actingAs($this->getViewer())->visit($book->getUrl())
|
$this->actingAs($this->getViewer())->visit($book->getUrl())
|
||||||
->dontSee('Create a new page')
|
->dontSee('Create a new page')
|
||||||
->dontSee('Add a chapter');
|
->dontSee('Add a chapter');
|
||||||
|
|
||||||
// Chapter links
|
// Chapter links
|
||||||
$chapter = factory(\BookStack\Entities\Chapter::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
|
$chapter = factory(Chapter::class)->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
|
||||||
$this->updateEntityPermissions($chapter);
|
$this->updateEntityPermissions($chapter);
|
||||||
$this->actingAs($this->getViewer())->visit($chapter->getUrl())
|
$this->actingAs($this->getViewer())->visit($chapter->getUrl())
|
||||||
->dontSee('Create a new page')
|
->dontSee('Create a new page')
|
||||||
@ -869,7 +874,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function addComment($page) {
|
private function addComment($page) {
|
||||||
$comment = factory(\BookStack\Actions\Comment::class)->make();
|
$comment = factory(Comment::class)->make();
|
||||||
$url = "/comment/$page->id";
|
$url = "/comment/$page->id";
|
||||||
$request = [
|
$request = [
|
||||||
'text' => $comment->text,
|
'text' => $comment->text,
|
||||||
@ -882,7 +887,7 @@ class RolesTest extends BrowserKitTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function updateComment($commentId) {
|
private function updateComment($commentId) {
|
||||||
$comment = factory(\BookStack\Actions\Comment::class)->make();
|
$comment = factory(Comment::class)->make();
|
||||||
$url = "/comment/$commentId";
|
$url = "/comment/$commentId";
|
||||||
$request = [
|
$request = [
|
||||||
'text' => $comment->text,
|
'text' => $comment->text,
|
||||||
|
@ -136,7 +136,7 @@ class RecycleBinTest extends TestCase
|
|||||||
$deletion = $page->deletions()->firstOrFail();
|
$deletion = $page->deletions()->firstOrFail();
|
||||||
|
|
||||||
$this->assertDatabaseHas('activities', [
|
$this->assertDatabaseHas('activities', [
|
||||||
'key' => 'page_delete',
|
'type' => 'page_delete',
|
||||||
'entity_id' => $page->id,
|
'entity_id' => $page->id,
|
||||||
'entity_type' => $page->getMorphClass(),
|
'entity_type' => $page->getMorphClass(),
|
||||||
]);
|
]);
|
||||||
@ -144,16 +144,16 @@ class RecycleBinTest extends TestCase
|
|||||||
$this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}");
|
$this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}");
|
||||||
|
|
||||||
$this->assertDatabaseMissing('activities', [
|
$this->assertDatabaseMissing('activities', [
|
||||||
'key' => 'page_delete',
|
'type' => 'page_delete',
|
||||||
'entity_id' => $page->id,
|
'entity_id' => $page->id,
|
||||||
'entity_type' => $page->getMorphClass(),
|
'entity_type' => $page->getMorphClass(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertDatabaseHas('activities', [
|
$this->assertDatabaseHas('activities', [
|
||||||
'key' => 'page_delete',
|
'type' => 'page_delete',
|
||||||
'entity_id' => 0,
|
'entity_id' => null,
|
||||||
'entity_type' => '',
|
'entity_type' => null,
|
||||||
'extra' => $page->name,
|
'detail' => $page->name,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +53,9 @@ abstract class TestCase extends BaseTestCase
|
|||||||
* Assert that an activity entry exists of the given key.
|
* Assert that an activity entry exists of the given key.
|
||||||
* Checks the activity belongs to the given entity if provided.
|
* Checks the activity belongs to the given entity if provided.
|
||||||
*/
|
*/
|
||||||
protected function assertActivityExists(string $key, Entity $entity = null)
|
protected function assertActivityExists(string $type, Entity $entity = null)
|
||||||
{
|
{
|
||||||
$detailsToCheck = ['key' => $key];
|
$detailsToCheck = ['type' => $type];
|
||||||
|
|
||||||
if ($entity) {
|
if ($entity) {
|
||||||
$detailsToCheck['entity_type'] = $entity->getMorphClass();
|
$detailsToCheck['entity_type'] = $entity->getMorphClass();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php namespace Tests\User;
|
<?php namespace Tests\User;
|
||||||
|
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Api\ApiToken;
|
use BookStack\Api\ApiToken;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
@ -67,6 +68,7 @@ class UserApiTokenTest extends TestCase
|
|||||||
$this->assertTrue(strlen($secret) === 32);
|
$this->assertTrue(strlen($secret) === 32);
|
||||||
|
|
||||||
$this->assertSessionHas('success');
|
$this->assertSessionHas('success');
|
||||||
|
$this->assertActivityExists(ActivityType::API_TOKEN_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_create_with_no_expiry_sets_expiry_hundred_years_away()
|
public function test_create_with_no_expiry_sets_expiry_hundred_years_away()
|
||||||
@ -124,6 +126,7 @@ class UserApiTokenTest extends TestCase
|
|||||||
|
|
||||||
$this->assertDatabaseHas('api_tokens', array_merge($updateData, ['id' => $token->id]));
|
$this->assertDatabaseHas('api_tokens', array_merge($updateData, ['id' => $token->id]));
|
||||||
$this->assertSessionHas('success');
|
$this->assertSessionHas('success');
|
||||||
|
$this->assertActivityExists(ActivityType::API_TOKEN_UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_token_update_with_blank_expiry_sets_to_hundred_years_away()
|
public function test_token_update_with_blank_expiry_sets_to_hundred_years_away()
|
||||||
@ -162,6 +165,7 @@ class UserApiTokenTest extends TestCase
|
|||||||
$resp = $this->delete($tokenUrl);
|
$resp = $this->delete($tokenUrl);
|
||||||
$resp->assertRedirect($editor->getEditUrl('#api_tokens'));
|
$resp->assertRedirect($editor->getEditUrl('#api_tokens'));
|
||||||
$this->assertDatabaseMissing('api_tokens', ['id' => $token->id]);
|
$this->assertDatabaseMissing('api_tokens', ['id' => $token->id]);
|
||||||
|
$this->assertActivityExists(ActivityType::API_TOKEN_DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_user_manage_can_delete_token_without_api_permission_themselves()
|
public function test_user_manage_can_delete_token_without_api_permission_themselves()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php namespace Tests\User;
|
<?php namespace Tests\User;
|
||||||
|
|
||||||
use Activity;
|
use Activity;
|
||||||
|
use BookStack\Actions\ActivityType;
|
||||||
use BookStack\Auth\User;
|
use BookStack\Auth\User;
|
||||||
use BookStack\Entities\Bookshelf;
|
use BookStack\Entities\Bookshelf;
|
||||||
use Tests\BrowserKitTest;
|
use Tests\BrowserKitTest;
|
||||||
@ -60,8 +61,8 @@ class UserProfileTest extends BrowserKitTest
|
|||||||
$newUser = $this->getNewBlankUser();
|
$newUser = $this->getNewBlankUser();
|
||||||
$this->actingAs($newUser);
|
$this->actingAs($newUser);
|
||||||
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
|
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
|
||||||
Activity::add($entities['book'], 'book_update', $entities['book']->id);
|
Activity::addForEntity($entities['book'], ActivityType::BOOK_UPDATE);
|
||||||
Activity::add($entities['page'], 'page_create', $entities['book']->id);
|
Activity::addForEntity($entities['page'], ActivityType::PAGE_CREATE);
|
||||||
|
|
||||||
$this->asAdmin()->visit('/user/' . $newUser->id)
|
$this->asAdmin()->visit('/user/' . $newUser->id)
|
||||||
->seeInElement('#recent-user-activity', 'updated book')
|
->seeInElement('#recent-user-activity', 'updated book')
|
||||||
@ -74,8 +75,8 @@ class UserProfileTest extends BrowserKitTest
|
|||||||
$newUser = $this->getNewBlankUser();
|
$newUser = $this->getNewBlankUser();
|
||||||
$this->actingAs($newUser);
|
$this->actingAs($newUser);
|
||||||
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
|
$entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
|
||||||
Activity::add($entities['book'], 'book_update', $entities['book']->id);
|
Activity::addForEntity($entities['book'], ActivityType::BOOK_UPDATE);
|
||||||
Activity::add($entities['page'], 'page_create', $entities['book']->id);
|
Activity::addForEntity($entities['page'], ActivityType::PAGE_CREATE);
|
||||||
|
|
||||||
$this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name)
|
$this->asAdmin()->visit('/')->clickInElement('#recent-activity', $newUser->name)
|
||||||
->seePageIs('/user/' . $newUser->id)
|
->seePageIs('/user/' . $newUser->id)
|
||||||
|
Loading…
Reference in New Issue
Block a user