From 38749860132e93219d6e7996671ebb69a303637f Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Sat, 30 Jul 2022 21:41:12 +0100 Subject: [PATCH 01/17] Improvements on console UI for install command --- src/BackpackServiceProvider.php | 4 +- .../Commands/Addons/RequireDevTools.php | 100 ++++++++++ .../Addons/RequireEditableColumns.php | 98 ++++++++++ .../Console/Commands/Addons/RequirePro.php | 98 ++++++++++ src/app/Console/Commands/Install.php | 185 ++++++++++++++++-- src/app/Console/Commands/RequireDevTools.php | 161 --------------- .../Console/Commands/Traits/AddonsHelper.php | 179 +++++++++++++++++ .../Commands/Traits/PrettyCommandOutput.php | 155 +++++++++++++-- 8 files changed, 776 insertions(+), 204 deletions(-) create mode 100644 src/app/Console/Commands/Addons/RequireDevTools.php create mode 100644 src/app/Console/Commands/Addons/RequireEditableColumns.php create mode 100644 src/app/Console/Commands/Addons/RequirePro.php delete mode 100644 src/app/Console/Commands/RequireDevTools.php create mode 100644 src/app/Console/Commands/Traits/AddonsHelper.php diff --git a/src/BackpackServiceProvider.php b/src/BackpackServiceProvider.php index bba0d914f2..024e34f8c5 100644 --- a/src/BackpackServiceProvider.php +++ b/src/BackpackServiceProvider.php @@ -20,7 +20,9 @@ class BackpackServiceProvider extends ServiceProvider \Backpack\CRUD\app\Console\Commands\CreateUser::class, \Backpack\CRUD\app\Console\Commands\PublishBackpackMiddleware::class, \Backpack\CRUD\app\Console\Commands\PublishView::class, - \Backpack\CRUD\app\Console\Commands\RequireDevTools::class, + \Backpack\CRUD\app\Console\Commands\Addons\RequireDevTools::class, + \Backpack\CRUD\app\Console\Commands\Addons\RequireEditableColumns::class, + \Backpack\CRUD\app\Console\Commands\Addons\RequirePro::class, \Backpack\CRUD\app\Console\Commands\Fix::class, ]; diff --git a/src/app/Console/Commands/Addons/RequireDevTools.php b/src/app/Console/Commands/Addons/RequireDevTools.php new file mode 100644 index 0000000000..f964030fb7 --- /dev/null +++ b/src/app/Console/Commands/Addons/RequireDevTools.php @@ -0,0 +1,100 @@ + 'DevTools', + 'description' => [ + 'Helps generate models, migrations, operations and CRUDs', + ], + 'path' => 'vendor/backpack/devtools', + 'command' => 'backpack:require:devtools', + ]; + + /** + * Execute the console command. + * + * @return mixed Command-line output + */ + public function handle() + { + $this->newLine(); + $this->infoBlock($this->description); + + // Check if repositories exists + $this->composerRepositories(); + + // Check for authentication + $this->checkForAuthentication(); + + $this->progressBlock($this->description); + + // Check if it is installed + if ($this->isInstalled()) { + $this->closeProgressBlock(); + $this->line(' It was already installed', 'fg=gray'); + $this->newLine(); + + return; + } + + // Require package + try { + $this->composerRequire('backpack/composerRequire', ['--dev', '--with-all-dependencies']); + } catch (\Throwable $e) { + $this->errorProgressBlock(); + $this->line(' '.$e->getMessage(), 'fg=red'); + $this->newLine(); + + return; + } + + // Display general error in case it failed + if (!$this->isInstalled()) { + $this->errorProgressBlock(); + $this->note('For further information please check the log file.'); + $this->note('You can also follow the manual installation process documented in https://backpackforlaravel.com/addons/'); + $this->newLine(); + + return; + } + + // Finish + $this->closeProgressBlock(); + $this->newLine(); + + // manually include the command in the run-time + if (!class_exists(\Backpack\DevTools\Console\Commands\InstallDevTools::class)) { + include base_path('vendor/backpack/devtools/src/Console/Commands/InstallDevTools.php'); + } + + $this->call(\Backpack\DevTools\Console\Commands\InstallDevTools::class); + } +} diff --git a/src/app/Console/Commands/Addons/RequireEditableColumns.php b/src/app/Console/Commands/Addons/RequireEditableColumns.php new file mode 100644 index 0000000000..f849f582c7 --- /dev/null +++ b/src/app/Console/Commands/Addons/RequireEditableColumns.php @@ -0,0 +1,98 @@ + 'Editable Columns', + 'description' => [ + 'Allow your admins to make small changes from the table view', + ], + 'path' => 'vendor/backpack/editable-columns', + 'command' => 'backpack:require:editablecolumns', + ]; + + /** + * Execute the console command. + * + * @return mixed Command-line output + */ + public function handle() + { + $this->newLine(); + $this->infoBlock($this->description); + + // Check if repositories exists + $this->composerRepositories(); + + // Check for authentication + $this->checkForAuthentication(); + + $this->progressBlock($this->description); + + // Check if it is installed + if ($this->isInstalled()) { + $this->closeProgressBlock(); + $this->line(' It was already installed', 'fg=gray'); + $this->newLine(); + + return; + } + + // Require package + try { + $this->composerRequire('backpack/editable-columns'); + } catch (\Throwable $e) { + $this->errorProgressBlock(); + $this->line(' '.$e->getMessage(), 'fg=red'); + $this->newLine(); + + return; + } + + // Display general error in case it failed + if (!$this->isInstalled()) { + $this->errorProgressBlock(); + $this->note('For further information please check the log file.'); + $this->note('You can also follow the manual installation process documented in https://backpackforlaravel.com/products/editable-columns'); + $this->newLine(); + + return; + } + + // Finish + $this->closeProgressBlock(); + $this->newLine(); + } + + public function isInstalled() + { + return file_exists('vendor/backpack/editable-columns/composer.json'); + } +} diff --git a/src/app/Console/Commands/Addons/RequirePro.php b/src/app/Console/Commands/Addons/RequirePro.php new file mode 100644 index 0000000000..b1b3785222 --- /dev/null +++ b/src/app/Console/Commands/Addons/RequirePro.php @@ -0,0 +1,98 @@ + 'Backpack Pro', + 'description' => [ + 'Adds 50+ features', + ], + 'path' => 'vendor/backpack/pro--', + 'command' => 'backpack:require:pro', + ]; + + /** + * Execute the console command. + * + * @return mixed Command-line output + */ + public function handle() + { + $this->newLine(); + $this->infoBlock($this->description); + + // Check if repositories exists + $this->composerRepositories(); + + // Check for authentication + $this->checkForAuthentication(); + + $this->progressBlock($this->description); + + // Check if it is installed + if ($this->isInstalled()) { + $this->closeProgressBlock(); + $this->line(' It was already installed', 'fg=gray'); + $this->newLine(); + + return; + } + + // Require package + try { + $this->composerRequire('backpack/pro'); + } catch (\Throwable $e) { + $this->errorProgressBlock(); + $this->line(' '.$e->getMessage(), 'fg=red'); + $this->newLine(); + + return; + } + + // Display general error in case it failed + if (!$this->isInstalled()) { + $this->errorProgressBlock(); + $this->note('For further information please check the log file.'); + $this->note('You can also follow the manual installation process documented in https://backpackforlaravel.com/addons/'); + $this->newLine(); + + return; + } + + // Finish + $this->closeProgressBlock(); + $this->newLine(); + } + + public function isInstalled() + { + return file_exists('vendor/backpack/pro/composer.json'); + } +} diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index c31fa70813..330b6aa39c 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -2,7 +2,11 @@ namespace Backpack\CRUD\app\Console\Commands; +use Backpack\CRUD\BackpackServiceProvider; +use Carbon\Carbon; use Illuminate\Console\Command; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; class Install extends Command { @@ -26,6 +30,17 @@ class Install extends Command */ protected $description = 'Install Backpack requirements on dev, publish files and create uploads directory.'; + /** + * Addons variable. + * + * @var array + */ + protected $addons = [ + Addons\RequirePro::class, + Addons\RequireDevTools::class, + Addons\RequireEditableColumns::class, + ]; + /** * Execute the console command. * @@ -33,38 +48,166 @@ class Install extends Command */ public function handle() { - $this->progressBar = $this->output->createProgressBar(5); - $this->progressBar->minSecondsBetweenRedraws(0); - $this->progressBar->maxSecondsBetweenRedraws(120); - $this->progressBar->setRedrawFrequency(1); - - $this->progressBar->start(); - - $this->info(' Backpack installation started. Please wait...'); - $this->progressBar->advance(); + $this->infoBlock('Backpack installation started.'); - $this->line(' Publishing configs, views, js and css files'); + // Publish files + $this->progressBlock('Publishing configs, views, js and css files'); $this->executeArtisanProcess('vendor:publish', [ - '--provider' => 'Backpack\CRUD\BackpackServiceProvider', + '--provider' => BackpackServiceProvider::class, '--tag' => 'minimum', ]); + $this->closeProgressBlock(); - $this->line(" Creating users table (using Laravel's default migration)"); + // Create users table + $this->progressBlock('Creating users table'); $this->executeArtisanProcess('migrate', $this->option('no-interaction') ? ['--no-interaction' => true] : []); + $this->closeProgressBlock(); - $this->line(" Creating App\Http\Middleware\CheckIfAdmin.php"); + // Create CheckIfAdmin middleware + $this->progressBlock('Creating CheckIfAdmin middleware'); $this->executeArtisanProcess('backpack:publish-middleware'); + $this->closeProgressBlock(); + + // Install Backpack Generators + $this->progressBlock('Installing Backpack Generators'); + // $process = new Process(['composer', 'require', '--dev', 'backpack/generators']); + // $process->setTimeout(300); + // $process->run(); + $this->closeProgressBlock(); + + // Create admins + $this->createAdminUsers(); + + // Addons + $this->installAddons(); + + // Done + $url = Str::of(config('app.url'))->finish('/')->append('admin/'); + $this->infoBlock('Backpack installation complete.', 'done'); + $this->note("Head to $url to view your new admin panel"); + $this->note('You may need to run `php artisan serve` to serve your project.'); + $this->newLine(); + } + + private function createAdminUsers() + { + $userModel = config('backpack.base.user_model_fqn'); + $permissionTable = config('permission.table_names.model_has_roles'); + + // Count current admins + $currentAdmins = DB::table($permissionTable)->where([ + 'role_id' => 1, + 'model_type' => $userModel, + ])->count(); + + $this->newLine(); + $this->infoBlock('Create an admin to access your admin panel'); + $this->note('By adding an admin you\'ll be able to quickly jump in your admin panel.'); + $this->note('Currently there '.trans_choice("{0} are no admins|{1} is 1 admin|[2,*] are $currentAdmins admins", $currentAdmins).' in the database'); + + $total = 0; + while ($this->confirm(' Add '.($total ? 'another' : 'an').' admin user?')) { + $name = $this->ask(' Name'); + $mail = $this->ask(" {$name}'s email"); + $pass = $this->secret(" {$name}'s password"); + + try { + (new $userModel())->insert([ + 'name' => $name, + 'email' => $mail, + 'password' => bcrypt($pass), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]); + + // Admin Role + DB::table($permissionTable)->insert([ + 'role_id' => 1, + 'model_type' => $userModel, + 'model_id' => DB::getPdo()->lastInsertId(), + ]); + + $this->deleteLines(12); + $this->progressBlock('Adding admin user'); + $this->closeProgressBlock(); + $this->note($name); + $this->note($mail); + + ++$total; + } catch (\Throwable$e) { + $this->errorBlock($e->getMessage()); + } + } + + $this->deleteLines(1); + } + + private function isEveryAddonInstalled() + { + return collect($this->addons)->every(function ($addon) { return file_exists($addon->path); }); + } + + private function updateAddonsStatus() + { + $this->addons = $this->addons->each(function (&$addon) { + $isInstalled = file_exists($addon->path); + $addon->status = $isInstalled ? 'installed' : 'not installed'; + $addon->statusColor = $isInstalled ? 'green' : 'yellow'; + }); + } + + private function installAddons() + { + // map the addons status + + $this->addons = collect($this->addons) + ->map(function ($class) { return (object) $class::$addon; }); + + $this->updateAddonsStatus(); + + // if all addons are installed do nothing + if ($this->isEveryAddonInstalled()) { + return; + } + + $this->newLine(); + $this->infoBlock('Backpack addons'); + $this->note('We believe these addons are everything you need to build admin panels of any complexity.'); + $this->note('However, addons are paid, for more info, payment and access please visit https://backpackforlaravel.com/addons'); + $this->newLine(); + + // Calculate the printed line count + $printedLines = $this->addons + ->map(function ($e) { return count($e->description); }) + ->reduce(function ($sum, $item) { return $sum + $item + 2; }, 0); + + $total = 0; + while (!$this->isEveryAddonInstalled()) { + $input = (int) $this->listChoice('Would you like to install any Backpack Addon? (enter option number)', $this->addons->toArray()); + + if ($input < 1 || $input > $this->addons->count()) { + break; + } + + // Clear list + $this->deleteLines($printedLines + 4 + ($total ? 2 : 0)); + + try { + $addon = $this->addons[$input - 1]; + + // Install addon + $this->call($addon->command); - $this->progressBar->finish(); - $this->info(' Backpack installation finished.'); + // refresh list + $this->updateAddonsStatus(); - // DevTools - $this->box('Did you know about Backpack DevTools?'); - $this->note('DevTools adds a dead-simple web interface to easily generate Models, Migrations, Seeders, Factories, CRUDs, etc.'); - $this->note('But it\'s a paid tool. For more info, payment and access, please visit https://backpackforlaravel.com/products/devtools'); + ++$total; + } catch (\Throwable $e) { + $this->errorBlock($e->getMessage()); + } - if ($this->confirm('Would you like to install Backpack DevTools?', false)) { - $this->call('backpack:require:devtools'); + $this->line(' ──────────', 'fg=gray'); + $this->newLine(); } } } diff --git a/src/app/Console/Commands/RequireDevTools.php b/src/app/Console/Commands/RequireDevTools.php deleted file mode 100644 index 90eb1c57f0..0000000000 --- a/src/app/Console/Commands/RequireDevTools.php +++ /dev/null @@ -1,161 +0,0 @@ -progressBar = $this->output->createProgressBar(29); - $this->progressBar->minSecondsBetweenRedraws(0); - $this->progressBar->maxSecondsBetweenRedraws(120); - $this->progressBar->setRedrawFrequency(1); - - $this->progressBar->start(); - - $this->info(' Requiring DevTools. Please wait...'); - $this->progressBar->advance(); - - // Check if auth exists - $details = null; - $process = new Process(['composer', 'config', 'http-basic.backpackforlaravel.com']); - $process->run(function ($type, $buffer) use (&$details) { - if ($type !== Process::ERR && $buffer !== '') { - $details = json_decode($buffer); - } elseif (File::exists('auth.json')) { - $details = json_decode(File::get('auth.json'), true)['http-basic']['backpackforlaravel.com'] ?? false; - } - $this->progressBar->advance(); - }); - - // Create an auth.json file - if (! $details) { - $this->info(' Creating auth.json file with your authentication token'); - - $this->line(' (Find your access token details on https://backpackforlaravel.com/user/tokens)'); - $username = $this->ask('Access token username'); - $password = $this->ask('Access token password'); - - $process = new Process(['composer', 'config', 'http-basic.backpackforlaravel.com', $username, $password]); - $process->run(function ($type, $buffer) use ($username, $password) { - if ($type === Process::ERR) { - // Fallback - $authFile = [ - 'http-basic' => [ - 'backpackforlaravel.com' => [ - 'username' => $username, - 'password' => $password, - ], - ], - ]; - - if (File::exists('auth.json')) { - $currentFile = json_decode(File::get('auth.json'), true); - if (! ($currentFile['http-basic']['backpackforlaravel.com'] ?? false)) { - $authFile = array_merge_recursive($authFile, $currentFile); - } - } - - File::put('auth.json', json_encode($authFile, JSON_PRETTY_PRINT)); - } - $this->progressBar->advance(); - }); - } - - // Check if repositories exists - $details = null; - $process = new Process(['composer', 'config', 'repositories.backpack/devtools']); - $process->run(function ($type, $buffer) use (&$details) { - if ($type !== Process::ERR && $buffer !== '') { - $details = json_decode($buffer); - } else { - $details = json_decode(File::get('composer.json'), true)['repositories']['backpack/devtools'] ?? false; - } - $this->progressBar->advance(); - }); - - // Create repositories - if (! $details) { - $this->info(' Creating repositories entry in composer.json'); - - $process = new Process(['composer', 'config', 'repositories.backpack/devtools', 'composer', 'https://repo.backpackforlaravel.com']); - $process->run(function ($type, $buffer) { - if ($type === Process::ERR) { - // Fallback - $composerJson = Str::of(File::get('composer.json')); - - $currentRepositories = json_decode($composerJson, true)['repositories'] ?? false; - - $repositories = Str::of(json_encode([ - 'repositories' => [ - 'backpack/devtools' => [ - 'type' => 'composer', - 'url' => 'https://repo.backpackforlaravel.com', - ], - ], - ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - - $composerJson = $currentRepositories - // Replace current repositories - ? preg_replace('/"repositories":\s{\r?\n/', $repositories->match("/\{\n\s+([\s\S]+)\n\s{4}\}/m")->append(",\n"), $composerJson) - // Append to the end of the file - : preg_replace("/\r?\n}/", $repositories->replaceFirst('{', ','), $composerJson); - - File::put('composer.json', $composerJson); - } - $this->progressBar->advance(); - }); - } - - // Require package - $process = new Process(['composer', 'require', '--dev', '--with-all-dependencies', 'backpack/devtools']); - $process->setTimeout(300); - $process->run(function ($type, $buffer) { - $this->progressBar->advance(); - }); - - // Finish - $this->progressBar->finish(); - $this->info(' DevTools is now required.'); - - // DevTools inside installer - $this->info(''); - $this->info(' Now running the DevTools installation command.'); - - // manually include the command in the run-time - if (! class_exists(\Backpack\DevTools\Console\Commands\InstallDevTools::class)) { - include base_path('vendor/backpack/devtools/src/Console/Commands/InstallDevTools.php'); - } - - $this->call(\Backpack\DevTools\Console\Commands\InstallDevTools::class); - } -} diff --git a/src/app/Console/Commands/Traits/AddonsHelper.php b/src/app/Console/Commands/Traits/AddonsHelper.php new file mode 100644 index 0000000000..ae025713a4 --- /dev/null +++ b/src/app/Console/Commands/Traits/AddonsHelper.php @@ -0,0 +1,179 @@ +run(function ($type, $buffer) use (&$details) { + if ($type !== Process::ERR && $buffer !== '') { + $details = json_decode($buffer); + } else { + $details = json_decode(File::get('composer.json'))->repositories ?? false; + } + }); + + // validate if backpack repo exists + if (collect($details)->pluck('url')->contains($backpackRepo)) { + return; + } + + // Create repositories + $this->progressBlock('Creating repositories entry in composer.json'); + + $process = new Process(['composera', 'config', 'repositories.backpack', 'composer', $backpackRepo]); + $process->run(function ($type, $buffer) use ($backpackRepo) { + if ($type === Process::ERR) { + // Fallback + $composerJson = Str::of(File::get('composer.json')); + + $currentRepositories = json_decode($composerJson)->repositories ?? false; + + $repositories = Str::of(json_encode([ + 'repositories' => [ + 'backpack' => [ + 'type' => 'composer', + 'url' => $backpackRepo, + ], + ], + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + + $composerJson = $currentRepositories + // Replace current repositories + ? preg_replace('/"repositories":\s{\r?\n/', $repositories->match("/\{\n\s+([\s\S]+)\n\s{4}\}/m")->append(",\n"), $composerJson) + // Append to the end of the file + : preg_replace("/\r?\n}/", $repositories->replaceFirst('{', ','), $composerJson); + + File::put('composer.json', $composerJson); + } + }); + + $this->closeProgressBlock(); + } + + /** + * Checks for backpack authentication. + * + * @return void + */ + public function checkForAuthentication() + { + // Check if auth exists + $details = null; + $process = new Process(['composer', 'config', 'http-basic.backpackforlaravel.com']); + $process->run(function ($type, $buffer) use (&$details) { + if ($type !== Process::ERR && $buffer !== '') { + $details = json_decode($buffer); + } elseif (File::exists('auth.json')) { + $details = json_decode(File::get('auth.json'), true)['http-basic']['backpackforlaravel.com'] ?? false; + } + }); + + // Create an auth.json file + if (!$details) { + $this->progressBlock('Creating auth.json file with your authentication token'); + $this->newLine(); + + $this->note('(Find your access token details on https://backpackforlaravel.com/user/tokens)'); + $username = $this->ask('Access token username'); + $password = $this->ask('Access token password'); + + $this->deleteLines(9); + $this->progressBlock('Creating auth.json file with your authentication token'); + + $process = new Process(['composer', 'config', 'http-basic.backpackforlaravel.com', $username, $password]); + $process->run(function ($type, $buffer) use ($username, $password) { + if ($type === Process::ERR) { + // Fallback + $authFile = [ + 'http-basic' => [ + 'backpackforlaravel.com' => [ + 'username' => $username, + 'password' => $password, + ], + ], + ]; + + if (File::exists('auth.json')) { + $currentFile = json_decode(File::get('auth.json'), true); + if (!($currentFile['http-basic']['backpackforlaravel.com'] ?? false)) { + $authFile = array_merge_recursive($authFile, $currentFile); + } + } + + File::put('auth.json', json_encode($authFile, JSON_PRETTY_PRINT)); + } + }); + + $this->closeProgressBlock(); + } + } + + /** + * Clears authentication from auth.json file. + * + * @return void + */ + public function clearAuthentication() + { + if (File::exists('auth.json')) { + $auth = json_decode(File::get('auth.json')); + if ($auth->{'http-basic'}->{'backpackforlaravel.com'} ?? false) { + unset($auth->{'http-basic'}->{'backpackforlaravel.com'}); + + File::put('auth.json', json_encode($auth, JSON_PRETTY_PRINT)); + } + } + } + + /** + * Composer require helper. + * + * @return void + */ + public function composerRequire(string $package, array $options = []) + { + // Require package + $process = new Process(array_merge(['composer', 'require'], $options, [$package])); + $process->setTimeout(300); + $process->run(function ($type, $buffer) use ($process) { + if ($type === Process::ERR) { + \Log::error($buffer); + } + + if (strpos($buffer, 'Permission denied') !== false) { + // Clear authentication + $this->clearAuthentication(); + + throw new Exception('Permission denied. Could not authenticate the credentials.'); + } + + if (strpos($buffer, 'curl error') !== false) { + $process->stop(0); + + throw new Exception('Connection refused. Failed to connect due to network issues.'); + } + + if (strpos($buffer, 'Your requirements could not be resolved') !== false) { + $process->stop(0); + + throw new Exception($buffer); + } + }); + } +} diff --git a/src/app/Console/Commands/Traits/PrettyCommandOutput.php b/src/app/Console/Commands/Traits/PrettyCommandOutput.php index f02250620e..306ad3f84f 100644 --- a/src/app/Console/Commands/Traits/PrettyCommandOutput.php +++ b/src/app/Console/Commands/Traits/PrettyCommandOutput.php @@ -12,9 +12,10 @@ trait PrettyCommandOutput /** * Run a SSH command. * - * @param string $command The SSH command that needs to be run - * @param bool $beforeNotice Information for the user before the command is run - * @param bool $afterNotice Information for the user after the command is run + * @param string $command The SSH command that needs to be run + * @param bool $beforeNotice Information for the user before the command is run + * @param bool $afterNotice Information for the user after the command is run + * * @return mixed Command-line output */ public function executeProcess($command, $beforeNotice = false, $afterNotice = false) @@ -34,7 +35,7 @@ public function executeProcess($command, $beforeNotice = false, $afterNotice = f }); // executes after the command finishes - if (! $process->isSuccessful()) { + if (!$process->isSuccessful()) { throw new ProcessFailedException($process); } @@ -50,10 +51,11 @@ public function executeProcess($command, $beforeNotice = false, $afterNotice = f /** * Run an artisan command. * - * @param string $command The artisan command to be run. - * @param array $arguments Key-value array of arguments to the artisan command. - * @param bool $beforeNotice Information for the user before the command is run - * @param bool $afterNotice Information for the user after the command is run + * @param string $command the artisan command to be run + * @param array $arguments key-value array of arguments to the artisan command + * @param bool $beforeNotice Information for the user before the command is run + * @param bool $afterNotice Information for the user after the command is run + * * @return mixed Command-line output */ public function executeArtisanProcess($command, $arguments = [], $beforeNotice = false, $afterNotice = false) @@ -80,8 +82,8 @@ public function executeArtisanProcess($command, $arguments = [], $beforeNotice = /** * Write text to the screen for the user to see. * - * @param string $type line, info, comment, question, error - * @param string $content + * @param string $type line, info, comment, question, error + * @param string $content */ public function echo($type, $content) { @@ -98,25 +100,136 @@ public function echo($type, $content) /** * Write a title inside a box. * - * @param string $content + * @param string $header */ - public function box($content) + public function box($header, $color = 'green') { - for ($i = 0, $line = ''; $i < strlen($content); ++$i, $line .= '─'); + $line = str_repeat('─', strlen($header)); - $this->line(''); - $this->info("┌───{$line}───┐"); - $this->info("│ $content │"); - $this->info("└───{$line}───┘"); + $this->newLine(); + $this->line("┌───{$line}───┐"); + $this->line("│ $header │"); + $this->line("└───{$line}───┘"); } /** - * Write a title inside a box. + * List choice element. + * + * @return void + */ + public function listChoice(string $question, array $options, string $default = 'no', string $hint = null) + { + foreach ($options as $key => $option) { + $value = $key + 1; + $this->progressBlock("$value {$option->name}"); + $this->closeProgressBlock($option->status, $option->statusColor ?? ''); + foreach ($option->description ?? [] as $line) { + $this->line(" {$line}"); + } + $this->newLine(); + } + + return $this->ask(" $question", $default); + } + + /** + * Default info block element. + * + * @return void + */ + public function infoBlock(string $text, string $title = 'info', string $background = 'blue', string $foreground = 'white') + { + $this->newLine(); + $this->line(sprintf(" %s $text", strtoupper($title))); + $this->newLine(); + } + + /** + * Default error block element + * Shortcute to info block with error message. + * + * @return void + */ + public function errorBlock(string $text) + { + $this->infoBlock($text, 'ERROR', 'red'); + } + + /** + * Note element, usually used after an info block + * Prints an indented text with a lighter color. + * + * @return void + */ + public function note(string $text, string $color = 'gray') + { + $this->line(" │ $text", "fg=$color"); + } + + /** + * Progress element generates a pending in progress line block. + * + * @return void + */ + public function progressBlock(string $text, string $progress = 'running', string $color = 'blue') + { + $defaultSize = 128; + + exec('mode con', $output); + $output = preg_match("/Columns:\s+(\d+)/", join('', $output), $result); + $lineWidth = min($result[10] ?? $defaultSize, $defaultSize); + + $this->output->write(sprintf( + " $text %s %s", + str_repeat('.', $lineWidth - 5 - strlen(strip_tags($text.$progress))), + strtoupper($progress) + )); + } + + /** + * Closes a progress block after it has been started. + * + * @return void + */ + public function closeProgressBlock(string $text = 'done', string $color = 'green') + { + $this->deleteChars(20); + + $this->output->write(sprintf( + "%s %s", + str_repeat('.', 19 - strlen($text)), + strtoupper($text), + )); + $this->newLine(); + } + + /** + * Closes a progress block with an error. + * + * @return void + */ + public function errorProgressBlock(string $text = 'error') + { + $this->closeProgressBlock($text, 'red'); + } + + /** + * Deletes one or multiple lines. + * + * @return void + */ + public function deleteLines(int $amount = 1) + { + $this->output->write(str_repeat("\033[A\33[2K\r", $amount)); + } + + /** + * Deletes one or multiple chars. * - * @param string $content + * @return void */ - public function note($content) + public function deleteChars(int $amount = 1) { - $this->line("│ $content"); + $this->output->write(str_repeat(chr(8), $amount)); } } From c6d5e06be38280c33d215a8658f9c009e97348a6 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sat, 30 Jul 2022 20:41:41 +0000 Subject: [PATCH 02/17] Apply fixes from StyleCI [ci skip] [skip ci] --- .../Commands/Addons/RequireDevTools.php | 4 ++-- .../Addons/RequireEditableColumns.php | 2 +- .../Console/Commands/Addons/RequirePro.php | 2 +- src/app/Console/Commands/Install.php | 22 +++++++++++------ .../Console/Commands/Traits/AddonsHelper.php | 4 ++-- .../Commands/Traits/PrettyCommandOutput.php | 24 +++++++++---------- 6 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/app/Console/Commands/Addons/RequireDevTools.php b/src/app/Console/Commands/Addons/RequireDevTools.php index f964030fb7..e7a319446d 100644 --- a/src/app/Console/Commands/Addons/RequireDevTools.php +++ b/src/app/Console/Commands/Addons/RequireDevTools.php @@ -77,7 +77,7 @@ public function handle() } // Display general error in case it failed - if (!$this->isInstalled()) { + if (! $this->isInstalled()) { $this->errorProgressBlock(); $this->note('For further information please check the log file.'); $this->note('You can also follow the manual installation process documented in https://backpackforlaravel.com/addons/'); @@ -91,7 +91,7 @@ public function handle() $this->newLine(); // manually include the command in the run-time - if (!class_exists(\Backpack\DevTools\Console\Commands\InstallDevTools::class)) { + if (! class_exists(\Backpack\DevTools\Console\Commands\InstallDevTools::class)) { include base_path('vendor/backpack/devtools/src/Console/Commands/InstallDevTools.php'); } diff --git a/src/app/Console/Commands/Addons/RequireEditableColumns.php b/src/app/Console/Commands/Addons/RequireEditableColumns.php index f849f582c7..4f8ea54701 100644 --- a/src/app/Console/Commands/Addons/RequireEditableColumns.php +++ b/src/app/Console/Commands/Addons/RequireEditableColumns.php @@ -77,7 +77,7 @@ public function handle() } // Display general error in case it failed - if (!$this->isInstalled()) { + if (! $this->isInstalled()) { $this->errorProgressBlock(); $this->note('For further information please check the log file.'); $this->note('You can also follow the manual installation process documented in https://backpackforlaravel.com/products/editable-columns'); diff --git a/src/app/Console/Commands/Addons/RequirePro.php b/src/app/Console/Commands/Addons/RequirePro.php index b1b3785222..78afc15273 100644 --- a/src/app/Console/Commands/Addons/RequirePro.php +++ b/src/app/Console/Commands/Addons/RequirePro.php @@ -77,7 +77,7 @@ public function handle() } // Display general error in case it failed - if (!$this->isInstalled()) { + if (! $this->isInstalled()) { $this->errorProgressBlock(); $this->note('For further information please check the log file.'); $this->note('You can also follow the manual installation process documented in https://backpackforlaravel.com/addons/'); diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 330b6aa39c..c83fcb20ce 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -133,7 +133,7 @@ private function createAdminUsers() $this->note($name); $this->note($mail); - ++$total; + $total++; } catch (\Throwable$e) { $this->errorBlock($e->getMessage()); } @@ -144,7 +144,9 @@ private function createAdminUsers() private function isEveryAddonInstalled() { - return collect($this->addons)->every(function ($addon) { return file_exists($addon->path); }); + return collect($this->addons)->every(function ($addon) { + return file_exists($addon->path); + }); } private function updateAddonsStatus() @@ -161,7 +163,9 @@ private function installAddons() // map the addons status $this->addons = collect($this->addons) - ->map(function ($class) { return (object) $class::$addon; }); + ->map(function ($class) { + return (object) $class::$addon; + }); $this->updateAddonsStatus(); @@ -178,11 +182,15 @@ private function installAddons() // Calculate the printed line count $printedLines = $this->addons - ->map(function ($e) { return count($e->description); }) - ->reduce(function ($sum, $item) { return $sum + $item + 2; }, 0); + ->map(function ($e) { + return count($e->description); + }) + ->reduce(function ($sum, $item) { + return $sum + $item + 2; + }, 0); $total = 0; - while (!$this->isEveryAddonInstalled()) { + while (! $this->isEveryAddonInstalled()) { $input = (int) $this->listChoice('Would you like to install any Backpack Addon? (enter option number)', $this->addons->toArray()); if ($input < 1 || $input > $this->addons->count()) { @@ -201,7 +209,7 @@ private function installAddons() // refresh list $this->updateAddonsStatus(); - ++$total; + $total++; } catch (\Throwable $e) { $this->errorBlock($e->getMessage()); } diff --git a/src/app/Console/Commands/Traits/AddonsHelper.php b/src/app/Console/Commands/Traits/AddonsHelper.php index ae025713a4..a5d75ad89f 100644 --- a/src/app/Console/Commands/Traits/AddonsHelper.php +++ b/src/app/Console/Commands/Traits/AddonsHelper.php @@ -85,7 +85,7 @@ public function checkForAuthentication() }); // Create an auth.json file - if (!$details) { + if (! $details) { $this->progressBlock('Creating auth.json file with your authentication token'); $this->newLine(); @@ -111,7 +111,7 @@ public function checkForAuthentication() if (File::exists('auth.json')) { $currentFile = json_decode(File::get('auth.json'), true); - if (!($currentFile['http-basic']['backpackforlaravel.com'] ?? false)) { + if (! ($currentFile['http-basic']['backpackforlaravel.com'] ?? false)) { $authFile = array_merge_recursive($authFile, $currentFile); } } diff --git a/src/app/Console/Commands/Traits/PrettyCommandOutput.php b/src/app/Console/Commands/Traits/PrettyCommandOutput.php index 306ad3f84f..784e3f85c4 100644 --- a/src/app/Console/Commands/Traits/PrettyCommandOutput.php +++ b/src/app/Console/Commands/Traits/PrettyCommandOutput.php @@ -12,10 +12,9 @@ trait PrettyCommandOutput /** * Run a SSH command. * - * @param string $command The SSH command that needs to be run - * @param bool $beforeNotice Information for the user before the command is run - * @param bool $afterNotice Information for the user after the command is run - * + * @param string $command The SSH command that needs to be run + * @param bool $beforeNotice Information for the user before the command is run + * @param bool $afterNotice Information for the user after the command is run * @return mixed Command-line output */ public function executeProcess($command, $beforeNotice = false, $afterNotice = false) @@ -35,7 +34,7 @@ public function executeProcess($command, $beforeNotice = false, $afterNotice = f }); // executes after the command finishes - if (!$process->isSuccessful()) { + if (! $process->isSuccessful()) { throw new ProcessFailedException($process); } @@ -51,11 +50,10 @@ public function executeProcess($command, $beforeNotice = false, $afterNotice = f /** * Run an artisan command. * - * @param string $command the artisan command to be run - * @param array $arguments key-value array of arguments to the artisan command - * @param bool $beforeNotice Information for the user before the command is run - * @param bool $afterNotice Information for the user after the command is run - * + * @param string $command the artisan command to be run + * @param array $arguments key-value array of arguments to the artisan command + * @param bool $beforeNotice Information for the user before the command is run + * @param bool $afterNotice Information for the user after the command is run * @return mixed Command-line output */ public function executeArtisanProcess($command, $arguments = [], $beforeNotice = false, $afterNotice = false) @@ -82,8 +80,8 @@ public function executeArtisanProcess($command, $arguments = [], $beforeNotice = /** * Write text to the screen for the user to see. * - * @param string $type line, info, comment, question, error - * @param string $content + * @param string $type line, info, comment, question, error + * @param string $content */ public function echo($type, $content) { @@ -100,7 +98,7 @@ public function echo($type, $content) /** * Write a title inside a box. * - * @param string $header + * @param string $header */ public function box($header, $color = 'green') { From 2fc5279ab74b16adfd8e31d9c24e9dd52263eeaf Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Sat, 30 Jul 2022 23:54:49 +0100 Subject: [PATCH 03/17] Added missing function to RequireDevTools class --- .../Commands/Addons/RequireDevTools.php | 7 +++- .../Addons/RequireEditableColumns.php | 2 +- .../Console/Commands/Addons/RequirePro.php | 4 +-- src/app/Console/Commands/Install.php | 32 ++++++------------- .../Commands/Traits/PrettyCommandOutput.php | 2 +- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/app/Console/Commands/Addons/RequireDevTools.php b/src/app/Console/Commands/Addons/RequireDevTools.php index f964030fb7..9e2b769a8e 100644 --- a/src/app/Console/Commands/Addons/RequireDevTools.php +++ b/src/app/Console/Commands/Addons/RequireDevTools.php @@ -67,7 +67,7 @@ public function handle() // Require package try { - $this->composerRequire('backpack/composerRequire', ['--dev', '--with-all-dependencies']); + $this->composerRequire('backpack/devtools', ['--dev', '--with-all-dependencies']); } catch (\Throwable $e) { $this->errorProgressBlock(); $this->line(' '.$e->getMessage(), 'fg=red'); @@ -97,4 +97,9 @@ public function handle() $this->call(\Backpack\DevTools\Console\Commands\InstallDevTools::class); } + + public function isInstalled() + { + return file_exists(self::$addon['path'].'/composer.json'); + } } diff --git a/src/app/Console/Commands/Addons/RequireEditableColumns.php b/src/app/Console/Commands/Addons/RequireEditableColumns.php index f849f582c7..bb305d9441 100644 --- a/src/app/Console/Commands/Addons/RequireEditableColumns.php +++ b/src/app/Console/Commands/Addons/RequireEditableColumns.php @@ -93,6 +93,6 @@ public function handle() public function isInstalled() { - return file_exists('vendor/backpack/editable-columns/composer.json'); + return file_exists(self::$addon['path'].'/composer.json'); } } diff --git a/src/app/Console/Commands/Addons/RequirePro.php b/src/app/Console/Commands/Addons/RequirePro.php index b1b3785222..b496531779 100644 --- a/src/app/Console/Commands/Addons/RequirePro.php +++ b/src/app/Console/Commands/Addons/RequirePro.php @@ -34,7 +34,7 @@ class RequirePro extends Command 'description' => [ 'Adds 50+ features', ], - 'path' => 'vendor/backpack/pro--', + 'path' => 'vendor/backpack/pro', 'command' => 'backpack:require:pro', ]; @@ -93,6 +93,6 @@ public function handle() public function isInstalled() { - return file_exists('vendor/backpack/pro/composer.json'); + return file_exists(self::$addon['path'].'/composer.json'); } } diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 330b6aa39c..46442b4ed3 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -5,7 +5,6 @@ use Backpack\CRUD\BackpackServiceProvider; use Carbon\Carbon; use Illuminate\Console\Command; -use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; class Install extends Command @@ -75,8 +74,8 @@ public function handle() // $process->run(); $this->closeProgressBlock(); - // Create admins - $this->createAdminUsers(); + // Create users + $this->createUsers(); // Addons $this->installAddons(); @@ -89,21 +88,17 @@ public function handle() $this->newLine(); } - private function createAdminUsers() + private function createUsers() { - $userModel = config('backpack.base.user_model_fqn'); - $permissionTable = config('permission.table_names.model_has_roles'); + $userModel = new (config('backpack.base.user_model_fqn', 'App\Models\User'))(); - // Count current admins - $currentAdmins = DB::table($permissionTable)->where([ - 'role_id' => 1, - 'model_type' => $userModel, - ])->count(); + // Count current users + $currentUsers = $userModel->count(); $this->newLine(); - $this->infoBlock('Create an admin to access your admin panel'); - $this->note('By adding an admin you\'ll be able to quickly jump in your admin panel.'); - $this->note('Currently there '.trans_choice("{0} are no admins|{1} is 1 admin|[2,*] are $currentAdmins admins", $currentAdmins).' in the database'); + $this->infoBlock('Create a user to access your admin panel'); + $this->note('By adding an user you\'ll be able to quickly jump in your admin panel.'); + $this->note('Currently there '.trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers).' in the database'); $total = 0; while ($this->confirm(' Add '.($total ? 'another' : 'an').' admin user?')) { @@ -112,7 +107,7 @@ private function createAdminUsers() $pass = $this->secret(" {$name}'s password"); try { - (new $userModel())->insert([ + $userModel->insert([ 'name' => $name, 'email' => $mail, 'password' => bcrypt($pass), @@ -120,13 +115,6 @@ private function createAdminUsers() 'updated_at' => Carbon::now(), ]); - // Admin Role - DB::table($permissionTable)->insert([ - 'role_id' => 1, - 'model_type' => $userModel, - 'model_id' => DB::getPdo()->lastInsertId(), - ]); - $this->deleteLines(12); $this->progressBlock('Adding admin user'); $this->closeProgressBlock(); diff --git a/src/app/Console/Commands/Traits/PrettyCommandOutput.php b/src/app/Console/Commands/Traits/PrettyCommandOutput.php index 306ad3f84f..53cd3b738f 100644 --- a/src/app/Console/Commands/Traits/PrettyCommandOutput.php +++ b/src/app/Console/Commands/Traits/PrettyCommandOutput.php @@ -177,7 +177,7 @@ public function progressBlock(string $text, string $progress = 'running', string exec('mode con', $output); $output = preg_match("/Columns:\s+(\d+)/", join('', $output), $result); - $lineWidth = min($result[10] ?? $defaultSize, $defaultSize); + $lineWidth = min($result[1] ?? $defaultSize, $defaultSize); $this->output->write(sprintf( " $text %s %s", From ded4d72f7b137954a301280cb705feb5d4484445 Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Sun, 31 Jul 2022 00:18:04 +0100 Subject: [PATCH 04/17] Fix for PHP7 --- src/app/Console/Commands/Install.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 7bd5bbb223..4378bc21e2 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -90,7 +90,8 @@ public function handle() private function createUsers() { - $userModel = new (config('backpack.base.user_model_fqn', 'App\Models\User'))(); + $userClass = config('backpack.base.user_model_fqn', 'App\Models\User'); + $userModel = new $userClass(); // Count current users $currentUsers = $userModel->count(); From 38a82cd42699b5582693b13709d33e06bbbf8c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Almeida?= Date: Sun, 31 Jul 2022 11:48:27 +0100 Subject: [PATCH 05/17] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cristian Tăbăcitu --- src/app/Console/Commands/Addons/RequirePro.php | 2 +- src/app/Console/Commands/Install.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/Console/Commands/Addons/RequirePro.php b/src/app/Console/Commands/Addons/RequirePro.php index 049d13f6c4..94d118e05b 100644 --- a/src/app/Console/Commands/Addons/RequirePro.php +++ b/src/app/Console/Commands/Addons/RequirePro.php @@ -32,7 +32,7 @@ class RequirePro extends Command public static $addon = [ 'name' => 'Backpack Pro', 'description' => [ - 'Adds 50+ features', + 'Adds 5 operations, 10 filters, 28 fields, 6 columns, charts', ], 'path' => 'vendor/backpack/pro', 'command' => 'backpack:require:pro', diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 4378bc21e2..9fcc5057e4 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -83,8 +83,8 @@ public function handle() // Done $url = Str::of(config('app.url'))->finish('/')->append('admin/'); $this->infoBlock('Backpack installation complete.', 'done'); - $this->note("Head to $url to view your new admin panel"); - $this->note('You may need to run `php artisan serve` to serve your project.'); + $this->note("Head to $url to access your new admin panel."); + $this->note('You may need to run `php artisan serve` to serve your Laravel project.'); $this->newLine(); } @@ -97,9 +97,9 @@ private function createUsers() $currentUsers = $userModel->count(); $this->newLine(); - $this->infoBlock('Create a user to access your admin panel'); - $this->note('By adding an user you\'ll be able to quickly jump in your admin panel.'); - $this->note('Currently there '.trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers).' in the database'); + $this->infoBlock('Create a user'); + $this->note('Quickly jump in your admin panel, using the email & password you choose here.'); + $this->note('Currently there '.trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers).' in the database.'); $total = 0; while ($this->confirm(' Add '.($total ? 'another' : 'an').' admin user?')) { From 83368c4b020a494116be92aad3b738944368c020 Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Sun, 31 Jul 2022 22:22:16 +0100 Subject: [PATCH 06/17] Fix console width win/unix --- .../Console/Commands/Traits/PrettyCommandOutput.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/Console/Commands/Traits/PrettyCommandOutput.php b/src/app/Console/Commands/Traits/PrettyCommandOutput.php index c2d5efee7c..4c779e5117 100644 --- a/src/app/Console/Commands/Traits/PrettyCommandOutput.php +++ b/src/app/Console/Commands/Traits/PrettyCommandOutput.php @@ -4,6 +4,7 @@ use Artisan; use Illuminate\Console\Command; +use Symfony\Component\Console\Terminal; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; @@ -171,15 +172,13 @@ public function note(string $text, string $color = 'gray') */ public function progressBlock(string $text, string $progress = 'running', string $color = 'blue') { - $defaultSize = 128; - - exec('mode con', $output); - $output = preg_match("/Columns:\s+(\d+)/", join('', $output), $result); - $lineWidth = min($result[1] ?? $defaultSize, $defaultSize); + $this->maxWidth = $this->maxWidth ?? 128; + $this->terminal = $this->terminal ?? new Terminal(); + $width = min($this->terminal->getWidth(), $this->maxWidth); $this->output->write(sprintf( " $text %s %s", - str_repeat('.', $lineWidth - 5 - strlen(strip_tags($text.$progress))), + str_repeat('.', $width - 5 - strlen(strip_tags($text.$progress))), strtoupper($progress) )); } From 02284b602c48794b999132222e7447927b16981b Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Mon, 1 Aug 2022 03:01:29 +0100 Subject: [PATCH 07/17] Default bar color to gray --- src/app/Console/Commands/Traits/PrettyCommandOutput.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/Console/Commands/Traits/PrettyCommandOutput.php b/src/app/Console/Commands/Traits/PrettyCommandOutput.php index 4c779e5117..78b4be2f02 100644 --- a/src/app/Console/Commands/Traits/PrettyCommandOutput.php +++ b/src/app/Console/Commands/Traits/PrettyCommandOutput.php @@ -160,9 +160,9 @@ public function errorBlock(string $text) * * @return void */ - public function note(string $text, string $color = 'gray') + public function note(string $text, string $color = 'gray', string $barColor = 'gray') { - $this->line(" │ $text", "fg=$color"); + $this->line(" │ $text", "fg=$color"); } /** From 8c037c113bc822e31733a30c70a68be7bf94e5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristian=20T=C4=83b=C4=83citu?= Date: Mon, 1 Aug 2022 08:30:39 +0300 Subject: [PATCH 08/17] Apply suggestions from code review --- src/app/Console/Commands/Install.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 9fcc5057e4..f2ca18978e 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -47,7 +47,7 @@ class Install extends Command */ public function handle() { - $this->infoBlock('Backpack installation started.'); + $this->infoBlock('Installing Backpack CRUD:'); // Publish files $this->progressBlock('Publishing configs, views, js and css files'); @@ -83,7 +83,7 @@ public function handle() // Done $url = Str::of(config('app.url'))->finish('/')->append('admin/'); $this->infoBlock('Backpack installation complete.', 'done'); - $this->note("Head to $url to access your new admin panel."); + $this->note("Go to $url to access your new admin panel."); $this->note('You may need to run `php artisan serve` to serve your Laravel project.'); $this->newLine(); } @@ -97,7 +97,7 @@ private function createUsers() $currentUsers = $userModel->count(); $this->newLine(); - $this->infoBlock('Create a user'); + $this->infoBlock('Creating an admin:'); $this->note('Quickly jump in your admin panel, using the email & password you choose here.'); $this->note('Currently there '.trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers).' in the database.'); @@ -164,9 +164,9 @@ private function installAddons() } $this->newLine(); - $this->infoBlock('Backpack addons'); - $this->note('We believe these addons are everything you need to build admin panels of any complexity.'); - $this->note('However, addons are paid, for more info, payment and access please visit https://backpackforlaravel.com/addons'); + $this->infoBlock('Installing premium Backpack add-ons:'); + $this->note('Add tons of features and functionality to your admin panel, using our paid add-ons.'); + $this->note('For more information, payment and access please visit https://backpackforlaravel.com/pricing'); $this->newLine(); // Calculate the printed line count @@ -180,7 +180,7 @@ private function installAddons() $total = 0; while (! $this->isEveryAddonInstalled()) { - $input = (int) $this->listChoice('Would you like to install any Backpack Addon? (enter option number)', $this->addons->toArray()); + $input = (int) $this->listChoice('Would you like to install a premium Backpack add-on? (enter option number)', $this->addons->toArray()); if ($input < 1 || $input > $this->addons->count()) { break; From fa5d42826a9cbca3cba39061a60cf6266ba74aee Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Mon, 1 Aug 2022 15:20:15 +0100 Subject: [PATCH 09/17] Setup installation on a low verbose level --- .../Commands/Addons/RequireDevTools.php | 21 ++++--- .../Addons/RequireEditableColumns.php | 21 ++++--- .../Console/Commands/Addons/RequirePro.php | 21 ++++--- src/app/Console/Commands/Install.php | 19 +++--- .../Console/Commands/Traits/AddonsHelper.php | 61 +++++++++++-------- .../Commands/Traits/PrettyCommandOutput.php | 14 ++++- 6 files changed, 91 insertions(+), 66 deletions(-) diff --git a/src/app/Console/Commands/Addons/RequireDevTools.php b/src/app/Console/Commands/Addons/RequireDevTools.php index 4f8d416d44..392f724f10 100644 --- a/src/app/Console/Commands/Addons/RequireDevTools.php +++ b/src/app/Console/Commands/Addons/RequireDevTools.php @@ -45,8 +45,17 @@ class RequireDevTools extends Command */ public function handle() { + // Check if it is installed + if ($this->isInstalled()) { + $this->newLine(); + $this->line(sprintf(' %s was already installed', self::$addon['name']), 'fg=red'); + $this->newLine(); + + return; + } + $this->newLine(); - $this->infoBlock($this->description); + $this->infoBlock('Connecting to the Backpack add-on repository'); // Check if repositories exists $this->composerRepositories(); @@ -54,17 +63,9 @@ public function handle() // Check for authentication $this->checkForAuthentication(); + $this->newLine(); $this->progressBlock($this->description); - // Check if it is installed - if ($this->isInstalled()) { - $this->closeProgressBlock(); - $this->line(' It was already installed', 'fg=gray'); - $this->newLine(); - - return; - } - // Require package try { $this->composerRequire('backpack/devtools', ['--dev', '--with-all-dependencies']); diff --git a/src/app/Console/Commands/Addons/RequireEditableColumns.php b/src/app/Console/Commands/Addons/RequireEditableColumns.php index 37575dbe70..4ce0320d21 100644 --- a/src/app/Console/Commands/Addons/RequireEditableColumns.php +++ b/src/app/Console/Commands/Addons/RequireEditableColumns.php @@ -45,8 +45,17 @@ class RequireEditableColumns extends Command */ public function handle() { + // Check if it is installed + if ($this->isInstalled()) { + $this->newLine(); + $this->line(sprintf(' %s was already installed', self::$addon['name']), 'fg=red'); + $this->newLine(); + + return; + } + $this->newLine(); - $this->infoBlock($this->description); + $this->infoBlock('Connecting to the Backpack add-on repository'); // Check if repositories exists $this->composerRepositories(); @@ -54,17 +63,9 @@ public function handle() // Check for authentication $this->checkForAuthentication(); + $this->newLine(); $this->progressBlock($this->description); - // Check if it is installed - if ($this->isInstalled()) { - $this->closeProgressBlock(); - $this->line(' It was already installed', 'fg=gray'); - $this->newLine(); - - return; - } - // Require package try { $this->composerRequire('backpack/editable-columns'); diff --git a/src/app/Console/Commands/Addons/RequirePro.php b/src/app/Console/Commands/Addons/RequirePro.php index 94d118e05b..b0665478ac 100644 --- a/src/app/Console/Commands/Addons/RequirePro.php +++ b/src/app/Console/Commands/Addons/RequirePro.php @@ -45,8 +45,17 @@ class RequirePro extends Command */ public function handle() { + // Check if it is installed + if ($this->isInstalled()) { + $this->closeProgressBlock(); + $this->line(' It was already installed.', 'fg=gray'); + $this->newLine(); + + return; + } + $this->newLine(); - $this->infoBlock($this->description); + $this->infoBlock('Connecting to the Backpack add-on repository'); // Check if repositories exists $this->composerRepositories(); @@ -54,17 +63,9 @@ public function handle() // Check for authentication $this->checkForAuthentication(); + $this->newLine(); $this->progressBlock($this->description); - // Check if it is installed - if ($this->isInstalled()) { - $this->closeProgressBlock(); - $this->line(' It was already installed', 'fg=gray'); - $this->newLine(); - - return; - } - // Require package try { $this->composerRequire('backpack/pro'); diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 9fcc5057e4..94620b3a34 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -6,6 +6,8 @@ use Carbon\Carbon; use Illuminate\Console\Command; use Illuminate\Support\Str; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; class Install extends Command { @@ -69,9 +71,9 @@ public function handle() // Install Backpack Generators $this->progressBlock('Installing Backpack Generators'); - // $process = new Process(['composer', 'require', '--dev', 'backpack/generators']); - // $process->setTimeout(300); - // $process->run(); + $process = new Process(['composer', 'require', '--dev', 'backpack/generators']); + $process->setTimeout(300); + $process->run(); $this->closeProgressBlock(); // Create users @@ -149,13 +151,13 @@ private function updateAddonsStatus() private function installAddons() { - // map the addons status - + // map the addons $this->addons = collect($this->addons) ->map(function ($class) { return (object) $class::$addon; }); + // set addons current status (installed / not installed) $this->updateAddonsStatus(); // if all addons are installed do nothing @@ -166,7 +168,7 @@ private function installAddons() $this->newLine(); $this->infoBlock('Backpack addons'); $this->note('We believe these addons are everything you need to build admin panels of any complexity.'); - $this->note('However, addons are paid, for more info, payment and access please visit https://backpackforlaravel.com/addons'); + $this->note('However, addons are paid, for more info, payment and access please visit https://backpackforlaravel.com/addons.'); $this->newLine(); // Calculate the printed line count @@ -192,8 +194,11 @@ private function installAddons() try { $addon = $this->addons[$input - 1]; - // Install addon + // Install addon (low verbose level) + $currentVerbosity = $this->output->getVerbosity(); + $this->output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); $this->call($addon->command); + $this->output->setVerbosity($currentVerbosity); // refresh list $this->updateAddonsStatus(); diff --git a/src/app/Console/Commands/Traits/AddonsHelper.php b/src/app/Console/Commands/Traits/AddonsHelper.php index a5d75ad89f..80f57738db 100644 --- a/src/app/Console/Commands/Traits/AddonsHelper.php +++ b/src/app/Console/Commands/Traits/AddonsHelper.php @@ -30,13 +30,15 @@ public function composerRepositories() // validate if backpack repo exists if (collect($details)->pluck('url')->contains($backpackRepo)) { + $this->note('Backpack repository entry is present in composer.json.'); + return; } // Create repositories $this->progressBlock('Creating repositories entry in composer.json'); - $process = new Process(['composera', 'config', 'repositories.backpack', 'composer', $backpackRepo]); + $process = new Process(['composer', 'config', 'repositories.backpack', 'composer', $backpackRepo]); $process->run(function ($type, $buffer) use ($backpackRepo) { if ($type === Process::ERR) { // Fallback @@ -84,23 +86,29 @@ public function checkForAuthentication() } }); + // Token exists + if ($details) { + $this->note('Backpack authentication token is present.'); + + return; + } + // Create an auth.json file - if (! $details) { - $this->progressBlock('Creating auth.json file with your authentication token'); - $this->newLine(); - - $this->note('(Find your access token details on https://backpackforlaravel.com/user/tokens)'); - $username = $this->ask('Access token username'); - $password = $this->ask('Access token password'); - - $this->deleteLines(9); - $this->progressBlock('Creating auth.json file with your authentication token'); - - $process = new Process(['composer', 'config', 'http-basic.backpackforlaravel.com', $username, $password]); - $process->run(function ($type, $buffer) use ($username, $password) { - if ($type === Process::ERR) { - // Fallback - $authFile = [ + $this->progressBlock('Creating auth.json file with your authentication token'); + $this->newLine(); + + $this->note('(Find your access token details on https://backpackforlaravel.com/user/tokens)'); + $username = $this->ask('Access token username'); + $password = $this->ask('Access token password'); + + $this->deleteLines(9); + $this->progressBlock('Creating auth.json file with your authentication token'); + + $process = new Process(['composer', 'config', 'http-basic.backpackforlaravel.com', $username, $password]); + $process->run(function ($type, $buffer) use ($username, $password) { + if ($type === Process::ERR) { + // Fallback + $authFile = [ 'http-basic' => [ 'backpackforlaravel.com' => [ 'username' => $username, @@ -109,19 +117,18 @@ public function checkForAuthentication() ], ]; - if (File::exists('auth.json')) { - $currentFile = json_decode(File::get('auth.json'), true); - if (! ($currentFile['http-basic']['backpackforlaravel.com'] ?? false)) { - $authFile = array_merge_recursive($authFile, $currentFile); - } + if (File::exists('auth.json')) { + $currentFile = json_decode(File::get('auth.json'), true); + if (! ($currentFile['http-basic']['backpackforlaravel.com'] ?? false)) { + $authFile = array_merge_recursive($authFile, $currentFile); } - - File::put('auth.json', json_encode($authFile, JSON_PRETTY_PRINT)); } - }); - $this->closeProgressBlock(); - } + File::put('auth.json', json_encode($authFile, JSON_PRETTY_PRINT)); + } + }); + + $this->closeProgressBlock(); } /** diff --git a/src/app/Console/Commands/Traits/PrettyCommandOutput.php b/src/app/Console/Commands/Traits/PrettyCommandOutput.php index 78b4be2f02..ac2e82afc8 100644 --- a/src/app/Console/Commands/Traits/PrettyCommandOutput.php +++ b/src/app/Console/Commands/Traits/PrettyCommandOutput.php @@ -39,7 +39,7 @@ public function executeProcess($command, $beforeNotice = false, $afterNotice = f throw new ProcessFailedException($process); } - if ($this->progressBar) { + if ($this->progressBar ?? null) { $this->progressBar->advance(); } @@ -69,7 +69,7 @@ public function executeArtisanProcess($command, $arguments = [], $beforeNotice = throw new ProcessFailedException($e); } - if ($this->progressBar) { + if ($this->progressBar ?? null) { $this->progressBar->advance(); } @@ -139,6 +139,16 @@ public function listChoice(string $question, array $options, string $default = ' public function infoBlock(string $text, string $title = 'info', string $background = 'blue', string $foreground = 'white') { $this->newLine(); + + // low verbose level (-v) will display a note instead of info block + if ($this->output->isVerbose()) { + if ($title !== 'info') { + $text = "$text [$title]"; + } + + return $this->line(" $text"); + } + $this->line(sprintf(" %s $text", strtoupper($title))); $this->newLine(); } From 2ab770b80e0d65e5a4410de08358444d3acdc028 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 1 Aug 2022 14:24:30 +0000 Subject: [PATCH 10/17] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Console/Commands/Traits/AddonsHelper.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/Console/Commands/Traits/AddonsHelper.php b/src/app/Console/Commands/Traits/AddonsHelper.php index 80f57738db..f6a54bc8b5 100644 --- a/src/app/Console/Commands/Traits/AddonsHelper.php +++ b/src/app/Console/Commands/Traits/AddonsHelper.php @@ -109,13 +109,13 @@ public function checkForAuthentication() if ($type === Process::ERR) { // Fallback $authFile = [ - 'http-basic' => [ - 'backpackforlaravel.com' => [ - 'username' => $username, - 'password' => $password, - ], + 'http-basic' => [ + 'backpackforlaravel.com' => [ + 'username' => $username, + 'password' => $password, ], - ]; + ], + ]; if (File::exists('auth.json')) { $currentFile = json_decode(File::get('auth.json'), true); From e1e0f2a4172ef9d777afd5d3463bb134055adfca Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Tue, 2 Aug 2022 11:36:22 +0100 Subject: [PATCH 11/17] Added steps as info block titles Removed questions after being answered --- src/app/Console/Commands/Install.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index c49f595e42..24cd2261eb 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -49,7 +49,7 @@ class Install extends Command */ public function handle() { - $this->infoBlock('Installing Backpack CRUD:'); + $this->infoBlock('Installing Backpack CRUD:', 'Step 1'); // Publish files $this->progressBlock('Publishing configs, views, js and css files'); @@ -99,7 +99,7 @@ private function createUsers() $currentUsers = $userModel->count(); $this->newLine(); - $this->infoBlock('Creating an admin:'); + $this->infoBlock('Creating an admin:', 'Step 2'); $this->note('Quickly jump in your admin panel, using the email & password you choose here.'); $this->note('Currently there '.trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers).' in the database.'); @@ -130,7 +130,13 @@ private function createUsers() } } - $this->deleteLines(1); + $this->deleteLines(3); + + if (!$total) { + $this->deleteLines(); + $this->note('Skipping creating an admin user.'); + $this->newLine(); + } } private function isEveryAddonInstalled() @@ -165,8 +171,7 @@ private function installAddons() return; } - $this->newLine(); - $this->infoBlock('Installing premium Backpack add-ons:'); + $this->infoBlock('Installing premium Backpack add-ons:', 'Step 3'); $this->note('Add tons of features and functionality to your admin panel, using our paid add-ons.'); $this->note('For more information, payment and access please visit https://backpackforlaravel.com/pricing'); $this->newLine(); @@ -185,6 +190,7 @@ private function installAddons() $input = (int) $this->listChoice('Would you like to install a premium Backpack add-on? (enter option number)', $this->addons->toArray()); if ($input < 1 || $input > $this->addons->count()) { + $this->deleteLines(3); break; } From 4a910358cb83fccd93fd19891056d72b04a0686f Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Tue, 2 Aug 2022 10:36:41 +0000 Subject: [PATCH 12/17] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Console/Commands/Install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 24cd2261eb..cfd1c486f8 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -132,7 +132,7 @@ private function createUsers() $this->deleteLines(3); - if (!$total) { + if (! $total) { $this->deleteLines(); $this->note('Skipping creating an admin user.'); $this->newLine(); From e06af5faaa9b4961a67567716ff12a8d17940ddf Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Tue, 2 Aug 2022 11:59:36 +0100 Subject: [PATCH 13/17] Using color instead of back ticks in artisan serve code snippet --- src/app/Console/Commands/Install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 24cd2261eb..36289a9de5 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -86,7 +86,7 @@ public function handle() $url = Str::of(config('app.url'))->finish('/')->append('admin/'); $this->infoBlock('Backpack installation complete.', 'done'); $this->note("Go to $url to access your new admin panel."); - $this->note('You may need to run `php artisan serve` to serve your Laravel project.'); + $this->note('You may need to run php artisan serve to serve your Laravel project.'); $this->newLine(); } From a4bf6b1fdfaa866803b5dfebd063dfb780b9d9d7 Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Tue, 2 Aug 2022 12:15:35 +0100 Subject: [PATCH 14/17] Validate if user model has timestamps --- src/app/Console/Commands/Install.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 9291efbcf9..11079ef2ab 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -110,14 +110,22 @@ private function createUsers() $pass = $this->secret(" {$name}'s password"); try { - $userModel->insert([ + $user = collect([ 'name' => $name, 'email' => $mail, 'password' => bcrypt($pass), - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now(), ]); + // Merge timestamps + if ($userModel->timestamps) { + $user = $user->merge([ + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]); + } + + $userModel->insert($user->toArray()); + $this->deleteLines(12); $this->progressBlock('Adding admin user'); $this->closeProgressBlock(); From b72ccd75734b2d71c44411abed2b44bd8f80db9c Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Tue, 2 Aug 2022 12:18:43 +0100 Subject: [PATCH 15/17] Replace bcrypt with Hash::make --- src/app/Console/Commands/Install.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index 11079ef2ab..f7153b4711 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -5,6 +5,7 @@ use Backpack\CRUD\BackpackServiceProvider; use Carbon\Carbon; use Illuminate\Console\Command; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; @@ -113,7 +114,7 @@ private function createUsers() $user = collect([ 'name' => $name, 'email' => $mail, - 'password' => bcrypt($pass), + 'password' => Hash::make($pass), ]); // Merge timestamps From ff694747f9c7a50f06e143cf084d99be5e4820a8 Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Tue, 2 Aug 2022 12:55:26 +0100 Subject: [PATCH 16/17] Removed user count --- src/app/Console/Commands/Install.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index f7153b4711..aa28991cf6 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -96,13 +96,10 @@ private function createUsers() $userClass = config('backpack.base.user_model_fqn', 'App\Models\User'); $userModel = new $userClass(); - // Count current users - $currentUsers = $userModel->count(); - $this->newLine(); $this->infoBlock('Creating an admin:', 'Step 2'); $this->note('Quickly jump in your admin panel, using the email & password you choose here.'); - $this->note('Currently there '.trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers).' in the database.'); + $this->note(sprintf('Using %s table, defined in the %s model.', $userModel->getTable(), $userClass)); $total = 0; while ($this->confirm(' Add '.($total ? 'another' : 'an').' admin user?')) { From 9a629d6a22e9dd5d1e233f9c00cb9db26cb3592f Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Tue, 2 Aug 2022 15:27:41 +0100 Subject: [PATCH 17/17] Get back user count --- src/app/Console/Commands/Install.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/Console/Commands/Install.php b/src/app/Console/Commands/Install.php index aa28991cf6..84d9a4bfd4 100644 --- a/src/app/Console/Commands/Install.php +++ b/src/app/Console/Commands/Install.php @@ -96,10 +96,13 @@ private function createUsers() $userClass = config('backpack.base.user_model_fqn', 'App\Models\User'); $userModel = new $userClass(); + // Count current users + $currentUsers = $userModel->count(); + $this->newLine(); $this->infoBlock('Creating an admin:', 'Step 2'); $this->note('Quickly jump in your admin panel, using the email & password you choose here.'); - $this->note(sprintf('Using %s table, defined in the %s model.', $userModel->getTable(), $userClass)); + $this->note(sprintf('Currently there %s in the %s table.', trans_choice("{0} are no users|{1} is 1 user|[2,*] are $currentUsers users", $currentUsers), $userModel->getTable())); $total = 0; while ($this->confirm(' Add '.($total ? 'another' : 'an').' admin user?')) { @@ -179,7 +182,7 @@ private function installAddons() $this->infoBlock('Installing premium Backpack add-ons:', 'Step 3'); $this->note('Add tons of features and functionality to your admin panel, using our paid add-ons.'); - $this->note('For more information, payment and access please visit https://backpackforlaravel.com/pricing'); + $this->note('For more information, payment and access please visit https://backpackforlaravel.com/pricing'); $this->newLine(); // Calculate the printed line count