mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-06-12 23:03:34 +08:00
Merge pull request #4262 from BookStackApp/command_cleanup
Command cleanup & alignment
This commit is contained in:
@ -54,12 +54,4 @@ class View extends Model
|
|||||||
|
|
||||||
return $view->views;
|
return $view->views;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all views from the system.
|
|
||||||
*/
|
|
||||||
public static function clearAll()
|
|
||||||
{
|
|
||||||
static::query()->truncate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use BookStack\Uploads\ImageService;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
class CleanupImages extends Command
|
class CleanupImagesCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -25,60 +25,49 @@ class CleanupImages extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Cleanup images and drawings';
|
protected $description = 'Cleanup images and drawings';
|
||||||
|
|
||||||
protected $imageService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @param \BookStack\Uploads\ImageService $imageService
|
|
||||||
*/
|
|
||||||
public function __construct(ImageService $imageService)
|
|
||||||
{
|
|
||||||
$this->imageService = $imageService;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(ImageService $imageService): int
|
||||||
{
|
{
|
||||||
$checkRevisions = $this->option('all') ? false : true;
|
$checkRevisions = !$this->option('all');
|
||||||
$dryRun = $this->option('force') ? false : true;
|
$dryRun = !$this->option('force');
|
||||||
|
|
||||||
if (!$dryRun) {
|
if (!$dryRun) {
|
||||||
$proceed = $this->confirm("This operation is destructive and is not guaranteed to be fully accurate.\nEnsure you have a backup of your images.\nAre you sure you want to proceed?");
|
$this->warn("This operation is destructive and is not guaranteed to be fully accurate.\nEnsure you have a backup of your images.\n");
|
||||||
|
$proceed = $this->confirm("Are you sure you want to proceed?");
|
||||||
if (!$proceed) {
|
if (!$proceed) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$deleted = $this->imageService->deleteUnusedImages($checkRevisions, $dryRun);
|
$deleted = $imageService->deleteUnusedImages($checkRevisions, $dryRun);
|
||||||
$deleteCount = count($deleted);
|
$deleteCount = count($deleted);
|
||||||
|
|
||||||
if ($dryRun) {
|
if ($dryRun) {
|
||||||
$this->comment('Dry run, No images have been deleted');
|
$this->comment('Dry run, no images have been deleted');
|
||||||
$this->comment($deleteCount . ' images found that would have been deleted');
|
$this->comment($deleteCount . ' images found that would have been deleted');
|
||||||
$this->showDeletedImages($deleted);
|
$this->showDeletedImages($deleted);
|
||||||
$this->comment('Run with -f or --force to perform deletions');
|
$this->comment('Run with -f or --force to perform deletions');
|
||||||
|
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->showDeletedImages($deleted);
|
$this->showDeletedImages($deleted);
|
||||||
$this->comment($deleteCount . ' images deleted');
|
$this->comment($deleteCount . ' images deleted');
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function showDeletedImages($paths)
|
protected function showDeletedImages($paths): void
|
||||||
{
|
{
|
||||||
if ($this->getOutput()->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL) {
|
if ($this->getOutput()->getVerbosity() <= OutputInterface::VERBOSITY_NORMAL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($paths) > 0) {
|
if (count($paths) > 0) {
|
||||||
$this->line('Images to delete:');
|
$this->line('Images to delete:');
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($paths as $path) {
|
foreach ($paths as $path) {
|
||||||
$this->line($path);
|
$this->line($path);
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ namespace BookStack\Console\Commands;
|
|||||||
use BookStack\Activity\Models\Activity;
|
use BookStack\Activity\Models\Activity;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class ClearActivity extends Command
|
class ClearActivityCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -21,27 +21,13 @@ class ClearActivity extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Clear user activity from the system';
|
protected $description = 'Clear user activity from the system';
|
||||||
|
|
||||||
protected $activity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @param Activity $activity
|
|
||||||
*/
|
|
||||||
public function __construct(Activity $activity)
|
|
||||||
{
|
|
||||||
$this->activity = $activity;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$this->activity->newQuery()->truncate();
|
Activity::query()->truncate();
|
||||||
$this->comment('System activity cleared');
|
$this->comment('System activity cleared');
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ namespace BookStack\Console\Commands;
|
|||||||
use BookStack\Entities\Models\PageRevision;
|
use BookStack\Entities\Models\PageRevision;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class ClearRevisions extends Command
|
class ClearRevisionsCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -23,28 +23,14 @@ class ClearRevisions extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Clear page revisions';
|
protected $description = 'Clear page revisions';
|
||||||
|
|
||||||
protected $pageRevision;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @param PageRevision $pageRevision
|
|
||||||
*/
|
|
||||||
public function __construct(PageRevision $pageRevision)
|
|
||||||
{
|
|
||||||
$this->pageRevision = $pageRevision;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$deleteTypes = $this->option('all') ? ['version', 'update_draft'] : ['version'];
|
$deleteTypes = $this->option('all') ? ['version', 'update_draft'] : ['version'];
|
||||||
$this->pageRevision->newQuery()->whereIn('type', $deleteTypes)->delete();
|
PageRevision::query()->whereIn('type', $deleteTypes)->delete();
|
||||||
$this->comment('Revisions deleted');
|
$this->comment('Revisions deleted');
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ namespace BookStack\Console\Commands;
|
|||||||
use BookStack\Activity\Models\View;
|
use BookStack\Activity\Models\View;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class ClearViews extends Command
|
class ClearViewsCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -21,22 +21,13 @@ class ClearViews extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Clear all view-counts for all entities';
|
protected $description = 'Clear all view-counts for all entities';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): int
|
||||||
{
|
{
|
||||||
View::clearAll();
|
View::query()->truncate();
|
||||||
$this->comment('Views cleared');
|
$this->comment('Views cleared');
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ use BookStack\Entities\Models\Bookshelf;
|
|||||||
use BookStack\Entities\Tools\PermissionsUpdater;
|
use BookStack\Entities\Tools\PermissionsUpdater;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class CopyShelfPermissions extends Command
|
class CopyShelfPermissionsCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -25,25 +25,10 @@ class CopyShelfPermissions extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Copy shelf permissions to all child books';
|
protected $description = 'Copy shelf permissions to all child books';
|
||||||
|
|
||||||
protected PermissionsUpdater $permissionsUpdater;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(PermissionsUpdater $permissionsUpdater)
|
|
||||||
{
|
|
||||||
$this->permissionsUpdater = $permissionsUpdater;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(PermissionsUpdater $permissionsUpdater): int
|
||||||
{
|
{
|
||||||
$shelfSlug = $this->option('slug');
|
$shelfSlug = $this->option('slug');
|
||||||
$cascadeAll = $this->option('all');
|
$cascadeAll = $this->option('all');
|
||||||
@ -52,7 +37,7 @@ class CopyShelfPermissions extends Command
|
|||||||
if (!$cascadeAll && !$shelfSlug) {
|
if (!$cascadeAll && !$shelfSlug) {
|
||||||
$this->error('Either a --slug or --all option must be provided.');
|
$this->error('Either a --slug or --all option must be provided.');
|
||||||
|
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($cascadeAll) {
|
if ($cascadeAll) {
|
||||||
@ -63,7 +48,7 @@ class CopyShelfPermissions extends Command
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!$continue && !$this->hasOption('no-interaction')) {
|
if (!$continue && !$this->hasOption('no-interaction')) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$shelves = Bookshelf::query()->get(['id']);
|
$shelves = Bookshelf::query()->get(['id']);
|
||||||
@ -77,10 +62,11 @@ class CopyShelfPermissions extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($shelves as $shelf) {
|
foreach ($shelves as $shelf) {
|
||||||
$this->permissionsUpdater->updateBookPermissionsFromShelf($shelf, false);
|
$permissionsUpdater->updateBookPermissionsFromShelf($shelf, false);
|
||||||
$this->info('Copied permissions for shelf [' . $shelf->id . ']');
|
$this->info('Copied permissions for shelf [' . $shelf->id . ']');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->info('Permissions copied for ' . $shelves->count() . ' shelves.');
|
$this->info('Permissions copied for ' . $shelves->count() . ' shelves.');
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace BookStack\Console\Commands;
|
namespace BookStack\Console\Commands;
|
||||||
|
|
||||||
use BookStack\Exceptions\NotFoundException;
|
|
||||||
use BookStack\Users\Models\Role;
|
use BookStack\Users\Models\Role;
|
||||||
use BookStack\Users\UserRepo;
|
use BookStack\Users\UserRepo;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
@ -10,9 +9,8 @@ use Illuminate\Support\Facades\Validator;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\Rules\Password;
|
use Illuminate\Validation\Rules\Password;
|
||||||
use Illuminate\Validation\Rules\Unique;
|
use Illuminate\Validation\Rules\Unique;
|
||||||
use Symfony\Component\Console\Command\Command as SymfonyCommand;
|
|
||||||
|
|
||||||
class CreateAdmin extends Command
|
class CreateAdminCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -32,25 +30,10 @@ class CreateAdmin extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Add a new admin user to the system';
|
protected $description = 'Add a new admin user to the system';
|
||||||
|
|
||||||
protected $userRepo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct(UserRepo $userRepo)
|
|
||||||
{
|
|
||||||
$this->userRepo = $userRepo;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @throws NotFoundException
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(UserRepo $userRepo): int
|
||||||
{
|
{
|
||||||
$details = $this->snakeCaseOptions();
|
$details = $this->snakeCaseOptions();
|
||||||
|
|
||||||
@ -82,17 +65,17 @@ class CreateAdmin extends Command
|
|||||||
$this->error($error);
|
$this->error($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return SymfonyCommand::FAILURE;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->userRepo->createWithoutActivity($validator->validated());
|
$user = $userRepo->createWithoutActivity($validator->validated());
|
||||||
$user->attachRole(Role::getSystemRole('admin'));
|
$user->attachRole(Role::getSystemRole('admin'));
|
||||||
$user->email_confirmed = true;
|
$user->email_confirmed = true;
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
$this->info("Admin account with email \"{$user->email}\" successfully created!");
|
$this->info("Admin account with email \"{$user->email}\" successfully created!");
|
||||||
|
|
||||||
return SymfonyCommand::SUCCESS;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function snakeCaseOptions(): array
|
protected function snakeCaseOptions(): array
|
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace BookStack\Console\Commands;
|
|
||||||
|
|
||||||
use BookStack\Users\Models\User;
|
|
||||||
use BookStack\Users\UserRepo;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class DeleteUsers extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'bookstack:delete-users';
|
|
||||||
|
|
||||||
protected $userRepo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Delete users that are not "admin" or system users';
|
|
||||||
|
|
||||||
public function __construct(UserRepo $userRepo)
|
|
||||||
{
|
|
||||||
$this->userRepo = $userRepo;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$confirm = $this->ask('This will delete all users from the system that are not "admin" or system users. Are you sure you want to continue? (Type "yes" to continue)');
|
|
||||||
$numDeleted = 0;
|
|
||||||
if (strtolower(trim($confirm)) === 'yes') {
|
|
||||||
$totalUsers = User::query()->count();
|
|
||||||
$users = User::query()->whereNull('system_name')->with('roles')->get();
|
|
||||||
foreach ($users as $user) {
|
|
||||||
if ($user->hasSystemRole('admin')) {
|
|
||||||
// don't delete users with "admin" role
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->userRepo->destroy($user);
|
|
||||||
$numDeleted++;
|
|
||||||
}
|
|
||||||
$this->info("Deleted $numDeleted of $totalUsers total users.");
|
|
||||||
} else {
|
|
||||||
$this->info('Exiting...');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
53
app/Console/Commands/DeleteUsersCommand.php
Normal file
53
app/Console/Commands/DeleteUsersCommand.php
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Console\Commands;
|
||||||
|
|
||||||
|
use BookStack\Users\Models\User;
|
||||||
|
use BookStack\Users\UserRepo;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class DeleteUsersCommand extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'bookstack:delete-users';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Delete users that are not "admin" or system users';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle(UserRepo $userRepo): int
|
||||||
|
{
|
||||||
|
$this->warn('This will delete all users from the system that are not "admin" or system users.');
|
||||||
|
$confirm = $this->confirm('Are you sure you want to continue?');
|
||||||
|
|
||||||
|
if (!$confirm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalUsers = User::query()->count();
|
||||||
|
$numDeleted = 0;
|
||||||
|
$users = User::query()->whereNull('system_name')->with('roles')->get();
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
if ($user->hasSystemRole('admin')) {
|
||||||
|
// don't delete users with "admin" role
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$userRepo->destroy($user);
|
||||||
|
$numDeleted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info("Deleted $numDeleted of $totalUsers total users.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -7,14 +7,15 @@ use BookStack\Activity\Models\Comment;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class RegenerateCommentContent extends Command
|
class RegenerateCommentContentCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'bookstack:regenerate-comment-content {--database= : The database connection to use.}';
|
protected $signature = 'bookstack:regenerate-comment-content
|
||||||
|
{--database= : The database connection to use}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -23,35 +24,19 @@ class RegenerateCommentContent extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Regenerate the stored HTML of all comments';
|
protected $description = 'Regenerate the stored HTML of all comments';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var CommentRepo
|
|
||||||
*/
|
|
||||||
protected $commentRepo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct(CommentRepo $commentRepo)
|
|
||||||
{
|
|
||||||
$this->commentRepo = $commentRepo;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(CommentRepo $commentRepo): int
|
||||||
{
|
{
|
||||||
$connection = DB::getDefaultConnection();
|
$connection = DB::getDefaultConnection();
|
||||||
if ($this->option('database') !== null) {
|
if ($this->option('database') !== null) {
|
||||||
DB::setDefaultConnection($this->option('database'));
|
DB::setDefaultConnection($this->option('database'));
|
||||||
}
|
}
|
||||||
|
|
||||||
Comment::query()->chunk(100, function ($comments) {
|
Comment::query()->chunk(100, function ($comments) use ($commentRepo) {
|
||||||
foreach ($comments as $comment) {
|
foreach ($comments as $comment) {
|
||||||
$comment->html = $this->commentRepo->commentToHtml($comment->text);
|
$comment->html = $commentRepo->commentToHtml($comment->text);
|
||||||
$comment->save();
|
$comment->save();
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -6,14 +6,15 @@ use BookStack\Permissions\JointPermissionBuilder;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class RegeneratePermissions extends Command
|
class RegeneratePermissionsCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'bookstack:regenerate-permissions {--database= : The database connection to use.}';
|
protected $signature = 'bookstack:regenerate-permissions
|
||||||
|
{--database= : The database connection to use}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -22,23 +23,10 @@ class RegeneratePermissions extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Regenerate all system permissions';
|
protected $description = 'Regenerate all system permissions';
|
||||||
|
|
||||||
protected JointPermissionBuilder $permissionBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct(JointPermissionBuilder $permissionBuilder)
|
|
||||||
{
|
|
||||||
$this->permissionBuilder = $permissionBuilder;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(JointPermissionBuilder $permissionBuilder): int
|
||||||
{
|
{
|
||||||
$connection = DB::getDefaultConnection();
|
$connection = DB::getDefaultConnection();
|
||||||
|
|
||||||
@ -46,7 +34,7 @@ class RegeneratePermissions extends Command
|
|||||||
DB::setDefaultConnection($this->option('database'));
|
DB::setDefaultConnection($this->option('database'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->permissionBuilder->rebuildForAll();
|
$permissionBuilder->rebuildForAll();
|
||||||
|
|
||||||
DB::setDefaultConnection($connection);
|
DB::setDefaultConnection($connection);
|
||||||
$this->comment('Permissions regenerated');
|
$this->comment('Permissions regenerated');
|
@ -6,14 +6,15 @@ use BookStack\References\ReferenceStore;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class RegenerateReferences extends Command
|
class RegenerateReferencesCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'bookstack:regenerate-references {--database= : The database connection to use.}';
|
protected $signature = 'bookstack:regenerate-references
|
||||||
|
{--database= : The database connection to use}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -22,25 +23,10 @@ class RegenerateReferences extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Regenerate all the cross-item model reference index';
|
protected $description = 'Regenerate all the cross-item model reference index';
|
||||||
|
|
||||||
protected ReferenceStore $references;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(ReferenceStore $references)
|
|
||||||
{
|
|
||||||
$this->references = $references;
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(ReferenceStore $references): int
|
||||||
{
|
{
|
||||||
$connection = DB::getDefaultConnection();
|
$connection = DB::getDefaultConnection();
|
||||||
|
|
||||||
@ -48,7 +34,7 @@ class RegenerateReferences extends Command
|
|||||||
DB::setDefaultConnection($this->option('database'));
|
DB::setDefaultConnection($this->option('database'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->references->updateForAllPages();
|
$references->updateForAllPages();
|
||||||
|
|
||||||
DB::setDefaultConnection($connection);
|
DB::setDefaultConnection($connection);
|
||||||
|
|
@ -7,14 +7,15 @@ use BookStack\Search\SearchIndex;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class RegenerateSearch extends Command
|
class RegenerateSearchCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'bookstack:regenerate-search {--database= : The database connection to use.}';
|
protected $signature = 'bookstack:regenerate-search
|
||||||
|
{--database= : The database connection to use}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -23,33 +24,17 @@ class RegenerateSearch extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Re-index all content for searching';
|
protected $description = 'Re-index all content for searching';
|
||||||
|
|
||||||
/**
|
|
||||||
* @var SearchIndex
|
|
||||||
*/
|
|
||||||
protected $searchIndex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct(SearchIndex $searchIndex)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
$this->searchIndex = $searchIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(SearchIndex $searchIndex): int
|
||||||
{
|
{
|
||||||
$connection = DB::getDefaultConnection();
|
$connection = DB::getDefaultConnection();
|
||||||
if ($this->option('database') !== null) {
|
if ($this->option('database') !== null) {
|
||||||
DB::setDefaultConnection($this->option('database'));
|
DB::setDefaultConnection($this->option('database'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->searchIndex->indexAllEntities(function (Entity $model, int $processed, int $total): void {
|
$searchIndex->indexAllEntities(function (Entity $model, int $processed, int $total): void {
|
||||||
$this->info('Indexed ' . class_basename($model) . ' entries (' . $processed . '/' . $total . ')');
|
$this->info('Indexed ' . class_basename($model) . ' entries (' . $processed . '/' . $total . ')');
|
||||||
});
|
});
|
||||||
|
|
@ -5,7 +5,7 @@ namespace BookStack\Console\Commands;
|
|||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
class ResetMfa extends Command
|
class ResetMfaCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -24,22 +24,10 @@ class ResetMfa extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Reset & Clear any configured MFA methods for the given user';
|
protected $description = 'Reset & Clear any configured MFA methods for the given user';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$id = $this->option('id');
|
$id = $this->option('id');
|
||||||
$email = $this->option('email');
|
$email = $this->option('email');
|
||||||
@ -66,13 +54,13 @@ class ResetMfa extends Command
|
|||||||
$this->info("This will delete any configure multi-factor authentication methods for user: \n- ID: {$user->id}\n- Name: {$user->name}\n- Email: {$user->email}\n");
|
$this->info("This will delete any configure multi-factor authentication methods for user: \n- ID: {$user->id}\n- Name: {$user->name}\n- Email: {$user->email}\n");
|
||||||
$this->info('If multi-factor authentication is required for this user they will be asked to reconfigure their methods on next login.');
|
$this->info('If multi-factor authentication is required for this user they will be asked to reconfigure their methods on next login.');
|
||||||
$confirm = $this->confirm('Are you sure you want to proceed?');
|
$confirm = $this->confirm('Are you sure you want to proceed?');
|
||||||
if ($confirm) {
|
if (!$confirm) {
|
||||||
$user->mfaValues()->delete();
|
return 1;
|
||||||
$this->info('User MFA methods have been reset.');
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
$user->mfaValues()->delete();
|
||||||
|
$this->info('User MFA methods have been reset.');
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ namespace BookStack\Console\Commands;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Database\Connection;
|
use Illuminate\Database\Connection;
|
||||||
|
|
||||||
class UpdateUrl extends Command
|
class UpdateUrlCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
@ -26,10 +26,8 @@ class UpdateUrl extends Command
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle(Connection $db)
|
public function handle(Connection $db): int
|
||||||
{
|
{
|
||||||
$oldUrl = str_replace("'", '', $this->argument('oldUrl'));
|
$oldUrl = str_replace("'", '', $this->argument('oldUrl'));
|
||||||
$newUrl = str_replace("'", '', $this->argument('newUrl'));
|
$newUrl = str_replace("'", '', $this->argument('newUrl'));
|
@ -5,14 +5,15 @@ namespace BookStack\Console\Commands;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class UpgradeDatabaseEncoding extends Command
|
class UpgradeDatabaseEncodingCommand extends Command
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $signature = 'bookstack:db-utf8mb4 {--database= : The database connection to use.}';
|
protected $signature = 'bookstack:db-utf8mb4
|
||||||
|
{--database= : The database connection to use}';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The console command description.
|
* The console command description.
|
||||||
@ -21,20 +22,11 @@ class UpgradeDatabaseEncoding extends Command
|
|||||||
*/
|
*/
|
||||||
protected $description = 'Generate SQL commands to upgrade the database to UTF8mb4';
|
protected $description = 'Generate SQL commands to upgrade the database to UTF8mb4';
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new command instance.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$connection = DB::getDefaultConnection();
|
$connection = DB::getDefaultConnection();
|
||||||
if ($this->option('database') !== null) {
|
if ($this->option('database') !== null) {
|
||||||
@ -48,9 +40,11 @@ class UpgradeDatabaseEncoding extends Command
|
|||||||
$key = 'Tables_in_' . $database;
|
$key = 'Tables_in_' . $database;
|
||||||
foreach ($tables as $table) {
|
foreach ($tables as $table) {
|
||||||
$tableName = $table->$key;
|
$tableName = $table->$key;
|
||||||
$this->line('ALTER TABLE `' . $tableName . '` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
$this->line("ALTER TABLE `{$tableName}` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;");
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::setDefaultConnection($connection);
|
DB::setDefaultConnection($connection);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -462,6 +462,7 @@ class ImageService
|
|||||||
|
|
||||||
Image::query()->whereIn('type', $types)
|
Image::query()->whereIn('type', $types)
|
||||||
->chunk(1000, function ($images) use ($checkRevisions, &$deletedPaths, $dryRun) {
|
->chunk(1000, function ($images) use ($checkRevisions, &$deletedPaths, $dryRun) {
|
||||||
|
/** @var Image $image */
|
||||||
foreach ($images as $image) {
|
foreach ($images as $image) {
|
||||||
$searchQuery = '%' . basename($image->path) . '%';
|
$searchQuery = '%' . basename($image->path) . '%';
|
||||||
$inPage = DB::table('pages')
|
$inPage = DB::table('pages')
|
||||||
|
49
tests/Commands/CleanupImagesCommandTest.php
Normal file
49
tests/Commands/CleanupImagesCommandTest.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Commands;
|
||||||
|
|
||||||
|
use BookStack\Uploads\Image;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class CleanupImagesCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_command_defaults_to_dry_run()
|
||||||
|
{
|
||||||
|
$page = $this->entities->page();
|
||||||
|
$image = Image::factory()->create(['uploaded_to' => $page->id]);
|
||||||
|
|
||||||
|
$this->artisan('bookstack:cleanup-images -v')
|
||||||
|
->expectsOutput('Dry run, no images have been deleted')
|
||||||
|
->expectsOutput('1 images found that would have been deleted')
|
||||||
|
->expectsOutputToContain($image->path)
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('images', ['id' => $image->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_command_force_run()
|
||||||
|
{
|
||||||
|
$page = $this->entities->page();
|
||||||
|
$image = Image::factory()->create(['uploaded_to' => $page->id]);
|
||||||
|
|
||||||
|
$this->artisan('bookstack:cleanup-images --force')
|
||||||
|
->expectsOutputToContain('This operation is destructive and is not guaranteed to be fully accurate')
|
||||||
|
->expectsConfirmation('Are you sure you want to proceed?', 'yes')
|
||||||
|
->expectsOutput('1 images deleted')
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
$this->assertDatabaseMissing('images', ['id' => $image->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_command_force_run_negative_confirmation()
|
||||||
|
{
|
||||||
|
$page = $this->entities->page();
|
||||||
|
$image = Image::factory()->create(['uploaded_to' => $page->id]);
|
||||||
|
|
||||||
|
$this->artisan('bookstack:cleanup-images --force')
|
||||||
|
->expectsConfirmation('Are you sure you want to proceed?', 'no')
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('images', ['id' => $image->id]);
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ class CopyShelfPermissionsCommandTest extends TestCase
|
|||||||
{
|
{
|
||||||
$this->artisan('bookstack:copy-shelf-permissions')
|
$this->artisan('bookstack:copy-shelf-permissions')
|
||||||
->expectsOutput('Either a --slug or --all option must be provided.')
|
->expectsOutput('Either a --slug or --all option must be provided.')
|
||||||
->assertExitCode(0);
|
->assertExitCode(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_copy_shelf_permissions_command_using_slug()
|
public function test_copy_shelf_permissions_command_using_slug()
|
||||||
|
44
tests/Commands/DeleteUsersCommandTest.php
Normal file
44
tests/Commands/DeleteUsersCommandTest.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Commands;
|
||||||
|
|
||||||
|
use BookStack\Users\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class DeleteUsersCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_command_deletes_users()
|
||||||
|
{
|
||||||
|
$userCount = User::query()->count();
|
||||||
|
$normalUsers = $this->getNormalUsers();
|
||||||
|
|
||||||
|
$normalUserCount = $userCount - count($normalUsers);
|
||||||
|
$this->artisan('bookstack:delete-users')
|
||||||
|
->expectsConfirmation('Are you sure you want to continue?', 'yes')
|
||||||
|
->expectsOutputToContain("Deleted $normalUserCount of $userCount total users.")
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
$this->assertDatabaseMissing('users', ['id' => $normalUsers->first()->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_command_requires_confirmation()
|
||||||
|
{
|
||||||
|
$normalUsers = $this->getNormalUsers();
|
||||||
|
|
||||||
|
$this->artisan('bookstack:delete-users')
|
||||||
|
->expectsConfirmation('Are you sure you want to continue?', 'no')
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('users', ['id' => $normalUsers->first()->id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getNormalUsers(): Collection
|
||||||
|
{
|
||||||
|
return User::query()->whereNull('system_name')
|
||||||
|
->get()
|
||||||
|
->filter(function (User $user) {
|
||||||
|
return !$user->hasSystemRole('admin');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
29
tests/Commands/RegenerateSearchCommandTest.php
Normal file
29
tests/Commands/RegenerateSearchCommandTest.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Commands;
|
||||||
|
|
||||||
|
use BookStack\Search\SearchTerm;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class RegenerateSearchCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_command_regenerates_index()
|
||||||
|
{
|
||||||
|
DB::rollBack();
|
||||||
|
$page = $this->entities->page();
|
||||||
|
SearchTerm::truncate();
|
||||||
|
|
||||||
|
$this->assertDatabaseMissing('search_terms', ['entity_id' => $page->id]);
|
||||||
|
|
||||||
|
$this->artisan('bookstack:regenerate-search')
|
||||||
|
->expectsOutput('Search index regenerated!')
|
||||||
|
->assertExitCode(0);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('search_terms', [
|
||||||
|
'entity_type' => 'page',
|
||||||
|
'entity_id' => $page->id
|
||||||
|
]);
|
||||||
|
DB::beginTransaction();
|
||||||
|
}
|
||||||
|
}
|
15
tests/Commands/UpgradeDatabaseEncodingCommandTest.php
Normal file
15
tests/Commands/UpgradeDatabaseEncodingCommandTest.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Commands;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class UpgradeDatabaseEncodingCommandTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_command_outputs_sql()
|
||||||
|
{
|
||||||
|
$this->artisan('bookstack:db-utf8mb4')
|
||||||
|
->expectsOutputToContain('ALTER DATABASE')
|
||||||
|
->expectsOutputToContain('ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user