Skip to content

Commit

Permalink
CLI commands to manage sites
Browse files Browse the repository at this point in the history
Implement `sites:list`

Reference: gh-47
  • Loading branch information
nikosdion committed Oct 18, 2023
1 parent 95b13b5 commit 73cf8fb
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 57 deletions.
1 change: 1 addition & 0 deletions cli/panopticon.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Symfony\Component\Console\Application;

const AKEEBA = 1;
const AKEEBA_CLI = 1;

// Make sure we're running under the PHP CLI SAPI
if (php_sapi_name() !== 'cli')
Expand Down
79 changes: 37 additions & 42 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,14 @@ public static function getUserMenuTitle(): string

$avatar = $user->getAvatar(64);

return "<img src=\"$avatar\" alt=\"\" class=\"me-1\" style=\"width: 1.25em; border-radius: 0.625em \" >" .
$user->getUsername();
return "<img src=\"$avatar\" alt=\"\" class=\"me-1\" style=\"width: 1.25em; border-radius: 0.625em \" >"
. $user->getUsername();
}

public static function getUserNameTitle(): string
{
return sprintf(
'<span class="small text-muted">%s</span>',
Factory::getContainer()->userManager->getUser()->getName()
'<span class="small text-muted">%s</span>', Factory::getContainer()->userManager->getUser()->getName()
);
}

Expand Down Expand Up @@ -278,6 +277,35 @@ public function createOrUpdateSessionPath(string $path, bool $silent = true): vo
}
}

public function loadLanguages(): void
{
try
{
$defaultLanguage = $this->container->appConfig->get('language', 'en-GB');
}
catch (Exception $e)
{
$defaultLanguage = 'en-GB';
}

$detectedLanguage = Text::detectLanguage($this->container, '.ini', $this->container->languagePath);

// Always load the English (Great Britain) language. It contains all the strings.
Text::loadLanguage('en-GB', $this->container, '.ini', true, $this->container->languagePath);

// Load the site's default language, if it's different from en-GB.
if ($defaultLanguage != 'en-GB')
{
Text::loadLanguage($defaultLanguage, $this->container, '.ini', true, $this->container->languagePath);
}

// Load the auto-detected preferred language (per browser settings), as long as it's not one we already loaded.
if (!in_array($detectedLanguage, [$defaultLanguage, 'en-GB']))
{
Text::loadLanguage($detectedLanguage, $this->container, '.ini', true, $this->container->languagePath);
}
}

private function initialiseMenu(array $items = self::MAIN_MENU, ?Item $parent = null): void
{
$menu = $this->getDocument()->getMenu();
Expand All @@ -288,8 +316,7 @@ private function initialiseMenu(array $items = self::MAIN_MENU, ?Item $parent =
{
$allowed = array_reduce(
$params['permissions'] ?? [],
fn(bool $carry, string $permission) => $carry && $user->getPrivilege($permission),
true
fn(bool $carry, string $permission) => $carry && $user->getPrivilege($permission), true
);

if (!$allowed)
Expand Down Expand Up @@ -463,35 +490,6 @@ private function discoverSessionSavePath(): void
}
}

private function loadLanguages(): void
{
try
{
$defaultLanguage = $this->container->appConfig->get('language', 'en-GB');
}
catch (Exception $e)
{
$defaultLanguage = 'en-GB';
}

$detectedLanguage = Text::detectLanguage($this->container, '.ini', $this->container->languagePath);

// Always load the English (Great Britain) language. It contains all the strings.
Text::loadLanguage('en-GB', $this->container, '.ini', true, $this->container->languagePath);

// Load the site's default language, if it's different from en-GB.
if ($defaultLanguage != 'en-GB')
{
Text::loadLanguage($defaultLanguage, $this->container, '.ini', true, $this->container->languagePath);
}

// Load the auto-detected preferred language (per browser settings), as long as it's not one we already loaded.
if (!in_array($detectedLanguage, [$defaultLanguage, 'en-GB']))
{
Text::loadLanguage($detectedLanguage, $this->container, '.ini', true, $this->container->languagePath);
}
}

private function applyTimezonePreference(): void
{
if (!function_exists('date_default_timezone_get') || !function_exists('date_default_timezone_set'))
Expand Down Expand Up @@ -615,13 +613,10 @@ private function redirectToSetup(): bool
{
$configPath = $this->container->appConfig->getDefaultPath();

if (
@file_exists($configPath)
|| in_array(
$this->getContainer()->input->getCmd('view', ''),
self::NO_LOGIN_VIEWS
)
)
if (@file_exists($configPath)
|| in_array(
$this->getContainer()->input->getCmd('view', ''), self::NO_LOGIN_VIEWS
))
{
return false;
}
Expand Down
52 changes: 37 additions & 15 deletions src/CliCommand/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Akeeba\Panopticon\CliCommand\Attribute\AppHeader;
use Akeeba\Panopticon\CliCommand\Attribute\ConfigAssertion;
use Akeeba\Panopticon\Factory;
use Awf\Text\Text;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -37,11 +38,28 @@ protected function initialize(InputInterface $input, OutputInterface $output)

parent::initialize($input, $output);

$this->header();
// Load the application language
Factory::getApplication()->loadLanguages();

// Conditionally emit header
$this->header($input);
}

protected function header()
protected function header(InputInterface $input)
{
// No header in quiet mode
if ($this->ioStyle->isQuiet())
{
return;
}

// No header when using a special output format
if ($input->hasOption('format') && !in_array($input->getOption('format'), [null, 'table', 'txt', 'text', 'human']))
{
return;
}

// Check the command class' attributes
$showHeader = true;
$cliApp = $this->getApplication();

Expand All @@ -53,21 +71,25 @@ protected function header()
$showHeader = $attributes[0]->getArguments()[0];
}

if ($showHeader && !$this->ioStyle->isQuiet())
// Forced to never emit a header? Go away.
if (!$showHeader)
{
$this->ioStyle->writeln($cliApp->getName() . ' <info>' . $cliApp->getVersion() . '</info>');

$year = gmdate('Y');
$this->ioStyle->writeln([
"Copyright (c) 2023-$year Akeeba Ltd",
"",
"<debug>Distributed under the terms of the GNU General Public License as published",
"by the Free Software Foundation, either version 3 of the License, or (at your",
"option) any later version. See LICENSE.txt.</debug>",
]);

$this->ioStyle->title($this->getDescription());
return;
}

// If I am still here I need to emit the header.
$this->ioStyle->writeln($cliApp->getName() . ' <info>' . $cliApp->getVersion() . '</info>');

$year = gmdate('Y');
$this->ioStyle->writeln([
"Copyright (c) 2023-$year Akeeba Ltd",
"",
"<debug>Distributed under the terms of the GNU General Public License as published",
"by the Free Software Foundation, either version 3 of the License, or (at your",
"option) any later version. See LICENSE.txt.</debug>",
]);

$this->ioStyle->title($this->getDescription());
}

protected function configureSymfonyIO(InputInterface $input, OutputInterface $output)
Expand Down
132 changes: 132 additions & 0 deletions src/CliCommand/SitesList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php
/**
* @package panopticon
* @copyright Copyright (c)2023-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license https://www.gnu.org/licenses/agpl-3.0.txt GNU Affero General Public License, version 3 or later
*/

namespace Akeeba\Panopticon\CliCommand;

defined('AKEEBA') || die;

use Akeeba\Panopticon\CliCommand\Attribute\ConfigAssertion;
use Akeeba\Panopticon\CliCommand\Trait\PrintFormattedArrayTrait;
use Akeeba\Panopticon\Factory;
use Akeeba\Panopticon\Model\Sites;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'sites:list',
description: 'List configured sites',
hidden: false,
)]
#[ConfigAssertion(true)]
class SitesList extends AbstractCommand
{
use PrintFormattedArrayTrait;

protected function execute(InputInterface $input, OutputInterface $output): int
{
$container = Factory::getContainer();
/** @var Sites $model */
$model = $container->mvcFactory->makeTempModel('Sites');

// Apply filters
$enabled = $input->getOption('enabled');

if ($enabled !== null)
{
$model->setState('enabled', $enabled ? '1' : '0');
}

$search = $input->getOption('search');

if ($search !== null)
{
$model->setState('search', $search);
}

$coreUpdate = $input->getOption('core-update');

if ($coreUpdate !== null)
{
$model->setState('coreUpdates', $coreUpdate ? '1' : '0');
}

$extensionUpdate = $input->getOption('extension-update');

if ($extensionUpdate !== null)
{
$model->setState('extUpdates', $extensionUpdate ? '1' : '0');
}

$cmsFamily = $input->getOption('cms-family');

if ($cmsFamily !== null)
{
$model->setState('cmsFamily', $cmsFamily);
}

$phpFamily = $input->getOption('php-family');

if ($phpFamily !== null)
{
$model->setState('phpFamily', $phpFamily);
}

// Get the items, removing the configuration parameters
$items = $model
->get(true)
->map(
fn(Sites $x) => [
'id' => $x->id,
'name' => $x->name,
'url' => $x->getBaseUrl(),
'enabled' => $x->enabled,
'created_by' => $x->created_by,
'created_on' => $x->created_on,
'modified_by' => $x->modified_by,
'modified_on' => $x->modified_on,
]
);

$this->printFormattedAndReturn(
$items->toArray(),
$input->getOption('format') ?: 'table'
);

return Command::SUCCESS;
}

protected function configure(): void
{
$this
->addOption(
'format', 'f', InputOption::VALUE_OPTIONAL, 'Output format (table, json, yaml, csv, count)', 'mysqli'
)
->addOption(
'enabled', 'e', InputOption::VALUE_NEGATABLE, 'Only show enabled sites'
)
->addOption(
'search', 's', InputOption::VALUE_OPTIONAL, 'Search among titles and URLs'
)
->addOption(
'cms-family', null, InputOption::VALUE_OPTIONAL, 'Only show sites with this CMS family (e.g. 1.2)'
)
->addOption(
'php-family', null, InputOption::VALUE_OPTIONAL, 'Only show sites with this PHP family (e.g. 1.2)'
)
->addOption(
'core-update', null, InputOption::VALUE_NEGATABLE, 'Only show sites with available core updates'
)
->addOption(
'extension-update', null, InputOption::VALUE_NEGATABLE, 'Only show sites with available extension updates'
)
;
}

}
Loading

0 comments on commit 73cf8fb

Please sign in to comment.