Обновление клиента
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Support\Sections;
|
||||
|
||||
use OCA\AppAPI\Db\ExAppMapper;
|
||||
use OCA\AppAPI\Service\DaemonConfigService;
|
||||
use OCA\Support\IDetail;
|
||||
use OCA\Support\Section;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
use OCP\Server;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
|
||||
class AppApiSection extends Section {
|
||||
public function __construct(
|
||||
protected readonly IConfig $config,
|
||||
protected readonly IAppManager $appManager,
|
||||
protected readonly IClientService $clientService,
|
||||
) {
|
||||
parent::__construct('app_api', 'AppAPI');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getDetails(): array {
|
||||
$this->createDetail('AppAPI configuration', $this->getAppApiInfo(), IDetail::TYPE_COLLAPSIBLE);
|
||||
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
public function isAppApiEnabled(): bool {
|
||||
return $this->appManager->isInstalled('app_api');
|
||||
}
|
||||
|
||||
private function getAppApiInfo(): string {
|
||||
$output = PHP_EOL;
|
||||
|
||||
try {
|
||||
$exAppMapper = Server::get(ExAppMapper::class);
|
||||
$exApps = $exAppMapper->findAll();
|
||||
$output .= PHP_EOL;
|
||||
$output .= '## ExApps' . PHP_EOL;
|
||||
if (!empty($exApps)) {
|
||||
foreach ($exApps as $exApp) {
|
||||
$enabled = $exApp->getEnabled() ? 'enabled' : 'disabled';
|
||||
$output .= ' * ' . $exApp->getAppid() . ' (' . $exApp->getName() . '): ' . $exApp->getVersion() . ' [' . $enabled . ']' . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
$output .= ' * no ExApps installed' . PHP_EOL;
|
||||
}
|
||||
|
||||
$daemonConfigService = Server::get(DaemonConfigService::class);
|
||||
$daemonConfigs = $daemonConfigService->getRegisteredDaemonConfigs();
|
||||
$output .= PHP_EOL;
|
||||
$output .= '## Deploy daemons' . PHP_EOL;
|
||||
foreach ($daemonConfigs as $daemon) {
|
||||
$deployConfig = $daemon->getDeployConfig();
|
||||
$deployConfig['haproxy_password'] = '***';
|
||||
$output .= ' * ' . $daemon->getName() . ' (' . $daemon->getDisplayName() . ')' . PHP_EOL;
|
||||
$output .= ' - Is HaRP: ' . (isset($deployConfig['harp']) ? 'yes' : 'no') . PHP_EOL;
|
||||
$output .= ' - Deployment method: ' . $daemon->getAcceptsDeployId() . PHP_EOL;
|
||||
$output .= ' - Protocol: ' . $daemon->getProtocol() . PHP_EOL;
|
||||
$output .= ' - Host: ' . $daemon->getHost() . PHP_EOL;
|
||||
$output .= ' - Deploy config: ' . json_encode($deployConfig, JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
}
|
||||
|
||||
$config = [
|
||||
'default_daemon_config' => $this->config->getAppValue('app_api', 'default_daemon_config'),
|
||||
'init_timeout' => $this->config->getAppValue('app_api', 'init_timeout', '40'),
|
||||
'container_restart_policy' => $this->config->getAppValue('app_api', 'container_restart_policy', 'unless-stopped'),
|
||||
];
|
||||
$output .= PHP_EOL;
|
||||
$output .= '## Config' . PHP_EOL;
|
||||
$output .= ' * Default daemon config (default_daemon_config): ' . $config['default_daemon_config'] . PHP_EOL;
|
||||
$output .= ' * Init timeout (init_timeout): ' . $config['init_timeout'] . PHP_EOL;
|
||||
$output .= ' * Container restart policy (container_restart_policy): ' . $config['container_restart_policy'] . PHP_EOL;
|
||||
|
||||
} catch (NotFoundExceptionInterface|ContainerExceptionInterface) {
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Support\Sections;
|
||||
|
||||
use OCA\Support\IDetail;
|
||||
use OCA\Support\Section;
|
||||
use OCA\User_LDAP\Configuration;
|
||||
use OCA\User_LDAP\Helper;
|
||||
use OCA\User_LDAP\User_Proxy;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Server;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
|
||||
class LdapSection extends Section {
|
||||
public function __construct(
|
||||
protected readonly IUserManager $userManager,
|
||||
) {
|
||||
parent::__construct('ldap', 'LDAP');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getDetails(): array {
|
||||
$this->createDetail('LDAP configuration', $this->getLDAPInfo(), IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
public function isLDAPEnabled(): bool {
|
||||
$backends = $this->userManager->getBackends();
|
||||
|
||||
foreach ($backends as $backend) {
|
||||
if ($backend instanceof User_Proxy) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getLDAPInfo(): string {
|
||||
$helper = Server::get(Helper::class);
|
||||
|
||||
$output = new BufferedOutput();
|
||||
|
||||
// copy of OCA\User_LDAP\Command\ShowConfig::renderConfigs
|
||||
$configIDs = $helper->getServerConfigurationPrefixes();
|
||||
foreach ($configIDs as $id) {
|
||||
$configHolder = new Configuration($id);
|
||||
$configuration = $configHolder->getConfiguration();
|
||||
ksort($configuration);
|
||||
|
||||
$table = new Table($output);
|
||||
$table->setHeaders(['Configuration', $id]);
|
||||
$rows = [];
|
||||
foreach ($configuration as $key => $value) {
|
||||
if ($key === 'ldapAgentPassword') {
|
||||
$value = '***';
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$value = implode(';', $value);
|
||||
}
|
||||
$rows[] = [$key, $value];
|
||||
}
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
}
|
||||
|
||||
return $output->fetch();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Support\Sections;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMXPath;
|
||||
use OCA\Support\IDetail;
|
||||
use OCA\Support\Section;
|
||||
|
||||
class PhpInfoSection extends Section {
|
||||
public function __construct(
|
||||
) {
|
||||
parent::__construct('phpinfo', 'Phpinfo');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getDetails(): array {
|
||||
ob_start();
|
||||
phpinfo(INFO_CONFIGURATION | INFO_MODULES);
|
||||
$phpinfo = ob_get_clean();
|
||||
|
||||
if ($phpinfo === false) {
|
||||
$this->createDetail('error', 'Failed to retrieve phpinfo output.', IDetail::TYPE_SINGLE_LINE);
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
if (strpos($phpinfo, '<!DOCTYPE html>') === false) {
|
||||
// If phpinfo output is not HTML, we dump the raw output
|
||||
$this->createDetail('phpifo', $phpinfo, IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
$parsedInfo = array_values($this->parsePhpInfoFromHtml($phpinfo))[0] ?? [];
|
||||
foreach ($parsedInfo as $sectionName => $sectionData) {
|
||||
$this->createDetail($sectionName, json_encode($sectionData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
}
|
||||
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
private function parsePhpInfoFromHtml(string $phpInfoHtmlStr): array {
|
||||
$dom = new DOMDocument();
|
||||
libxml_use_internal_errors(true);
|
||||
$dom->loadHTML($phpInfoHtmlStr, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
libxml_clear_errors();
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
|
||||
$result = [];
|
||||
$currentSection = '';
|
||||
$currentSubsection = '';
|
||||
|
||||
$elements = $xpath->query('//h1 | //h2 | //table');
|
||||
|
||||
foreach ($elements as $el) {
|
||||
if ($el->nodeName === 'h1') {
|
||||
$currentSection = trim($el->textContent);
|
||||
$result[$currentSection] = [];
|
||||
} elseif ($el->nodeName === 'h2') {
|
||||
$currentSubsection = trim($el->textContent);
|
||||
if (!isset($result[$currentSection][$currentSubsection])) {
|
||||
$result[$currentSection][$currentSubsection] = [];
|
||||
}
|
||||
} elseif ($el->nodeName === 'table') {
|
||||
$rows = $el->getElementsByTagName('tr');
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$cols = $row->getElementsByTagName('td');
|
||||
$ths = $row->getElementsByTagName('th');
|
||||
|
||||
if ($cols->length === 2) {
|
||||
// Single key => value
|
||||
$key = trim($cols->item(0)->textContent);
|
||||
$val = trim($cols->item(1)->textContent);
|
||||
$result[$currentSection][$currentSubsection][$key] = $val;
|
||||
} elseif ($cols->length === 3) {
|
||||
// Directive with local/master value
|
||||
$key = trim($cols->item(0)->textContent);
|
||||
$local = trim($cols->item(1)->textContent);
|
||||
$master = trim($cols->item(2)->textContent);
|
||||
$result[$currentSection][$currentSubsection][$key] = [
|
||||
'local' => $local,
|
||||
'master' => $master,
|
||||
];
|
||||
} elseif ($ths->length > 0) {
|
||||
// This is a header row; skip or save metadata
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Support\Sections;
|
||||
|
||||
use OC\IntegrityCheck\Checker;
|
||||
use OC\SystemConfig;
|
||||
use OCA\Files_External\Lib\StorageConfig;
|
||||
use OCA\Files_External\Service\GlobalStoragesService;
|
||||
use OCA\Support\IDetail;
|
||||
use OCA\Support\Section;
|
||||
use OCA\Support\Service\SubscriptionService;
|
||||
use OCA\Support\Subscription\SubscriptionAdapter;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\DB\Exception;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Server;
|
||||
use OCP\ServerVersion;
|
||||
use OCP\Support\Subscription\IRegistry;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
|
||||
class ServerSection extends Section {
|
||||
public function __construct(
|
||||
protected readonly IConfig $config,
|
||||
protected readonly Checker $checker,
|
||||
protected readonly IAppManager $appManager,
|
||||
protected readonly IDBConnection $connection,
|
||||
protected readonly IUserManager $userManager,
|
||||
protected readonly LoggerInterface $logger,
|
||||
protected readonly SystemConfig $systemConfig,
|
||||
protected readonly IAppConfig $appConfig,
|
||||
protected readonly ServerVersion $serverVersion,
|
||||
protected readonly SubscriptionAdapter $adapter,
|
||||
protected readonly ITimeFactory $timeFactory,
|
||||
) {
|
||||
parent::__construct('server-detail', 'Server configuration detail');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getDetails(): array {
|
||||
$this->createDetail('Operating system', $this->getOsVersion());
|
||||
$this->createDetail('Webserver', $this->getWebserver());
|
||||
$this->createDetail('Database', $this->getDatabaseInfo());
|
||||
$this->createDetail('PHP version', $this->getPhpVersion());
|
||||
$this->createDetail('Nextcloud version', $this->getNextcloudVersion());
|
||||
$this->createDetail('Updated from an older Nextcloud/ownCloud or fresh install', '');
|
||||
$this->createDetail('Where did you install Nextcloud from', $this->getInstallMethod());
|
||||
$this->createDetail('Signing status', $this->getIntegrityResults(), IDetail::TYPE_COLLAPSIBLE);
|
||||
$this->createDetail('List of activated apps', $this->renderAppList(), IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
|
||||
$this->createDetail('Configuration (config/config.php)', print_r(json_encode($this->getConfig(), JSON_PRETTY_PRINT), true), IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
$this->createDetail('Cron Configuration', $this->getCronConfig());
|
||||
|
||||
$externalStorageEnabled = $this->appManager->isEnabledForUser('files_external');
|
||||
$this->createDetail('External storages', $externalStorageEnabled ? 'yes' : 'files_external is disabled');
|
||||
if ($externalStorageEnabled) {
|
||||
$this->createDetail('External storage configuration', $this->getExternalStorageInfo(), IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
}
|
||||
|
||||
$this->createDetail('Encryption', $this->getEncryptionInfo());
|
||||
$this->createDetail('User-backends', $this->getUserBackendInfo());
|
||||
$this->createDetail('Subscription', $this->getSubscriptionInfo());
|
||||
|
||||
$this->createDetail('Browser', $this->getBrowser());
|
||||
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
private function getWebserver(): string {
|
||||
return ($_SERVER['SERVER_SOFTWARE'] ?? 'Unknown') . ' (' . PHP_SAPI . ')';
|
||||
}
|
||||
|
||||
private function getNextcloudVersion(): string {
|
||||
return $this->serverVersion->getHumanVersion() . ' - ' . $this->config->getSystemValueString('version');
|
||||
}
|
||||
private function getOsVersion(): string {
|
||||
return function_exists('php_uname') ? php_uname('s') . ' ' . php_uname('r') . ' ' . php_uname('v') . ' ' . php_uname('m') : PHP_OS;
|
||||
}
|
||||
private function getPhpVersion(): string {
|
||||
return PHP_VERSION . "\n\nModules loaded: " . implode(', ', get_loaded_extensions());
|
||||
}
|
||||
|
||||
protected function getDatabaseInfo(): string {
|
||||
return $this->config->getSystemValueString('dbtype') . ' ' . $this->getDatabaseVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* original source from nextcloud/survey_client
|
||||
* @link https://github.com/nextcloud/survey_client/blob/master/lib/Categories/Database.php#L80-L107
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @license AGPL-3.0
|
||||
*/
|
||||
private function getDatabaseVersion(): string {
|
||||
switch ($this->config->getSystemValueString('dbtype')) {
|
||||
case 'sqlite':
|
||||
case 'sqlite3':
|
||||
$sql = 'SELECT sqlite_version() AS version';
|
||||
break;
|
||||
case 'oci':
|
||||
$sql = 'SELECT VERSION FROM PRODUCT_COMPONENT_VERSION';
|
||||
break;
|
||||
case 'mysql':
|
||||
case 'pgsql':
|
||||
default:
|
||||
$sql = 'SELECT VERSION() AS version';
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->connection->executeQuery($sql);
|
||||
$version = $result->fetchOne();
|
||||
$result->closeCursor();
|
||||
if ($version) {
|
||||
return $this->cleanVersion($version);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->logger->debug('Unable to determine database version', [
|
||||
'exception' => $e
|
||||
]);
|
||||
}
|
||||
|
||||
return 'N/A';
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to strip away additional information
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* @param string $version E.g. `5.6.27-0ubuntu0.14.04.1`
|
||||
* @return string `5.6.27`
|
||||
*/
|
||||
protected function cleanVersion(string $version): string {
|
||||
$matches = [];
|
||||
preg_match('/^(\d+)(\.\d+)(\.\d+)/', $version, $matches);
|
||||
if (isset($matches[0])) {
|
||||
return $matches[0];
|
||||
}
|
||||
return $version;
|
||||
}
|
||||
|
||||
private function getCronConfig(): string {
|
||||
$mode = $this->appConfig->getValueString('core', 'backgroundjobs_mode', 'ajax');
|
||||
$last = $this->appConfig->getValueInt('core', 'lastcron', 0);
|
||||
|
||||
if ($last === 0) {
|
||||
$formattedLast = 'never';
|
||||
} else {
|
||||
$formattedLast = date('c', $last) . ' (' . (time() - $last) . ' seconds ago)';
|
||||
}
|
||||
|
||||
return PHP_EOL . PHP_EOL
|
||||
. 'Mode: ' . $mode . PHP_EOL
|
||||
. 'Last: ' . $formattedLast . PHP_EOL;
|
||||
}
|
||||
|
||||
private function getIntegrityResults(): string {
|
||||
if (!$this->checker->isCodeCheckEnforced()) {
|
||||
return 'Integrity checker has been disabled. Integrity cannot be verified.';
|
||||
}
|
||||
return print_r(json_encode($this->checker->getResults(), JSON_PRETTY_PRINT), true);
|
||||
}
|
||||
|
||||
private function getInstallMethod(): string {
|
||||
$base = \OC::$SERVERROOT;
|
||||
if (file_exists($base . '/.git')) {
|
||||
return 'git';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
private function renderAppList(): string {
|
||||
$apps = $this->getAppList();
|
||||
|
||||
$result = '';
|
||||
if ($apps['supported'] !== []) {
|
||||
$result .= "Supported:\n";
|
||||
foreach ($apps['supported'] as $name => $version) {
|
||||
$result .= ' - ' . $name . ': ' . $version . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
$result .= "Enabled:\n";
|
||||
foreach ($apps['enabled'] as $name => $version) {
|
||||
$result .= ' - ' . $name . ': ' . $version . "\n";
|
||||
}
|
||||
|
||||
$result .= "Disabled:\n";
|
||||
foreach ($apps['disabled'] as $name => $version) {
|
||||
if ($version) {
|
||||
$result .= ' - ' . $name . ': ' . $version . "\n";
|
||||
} else {
|
||||
$result .= ' - ' . $name . "\n";
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array<string, string|bool>>
|
||||
*/
|
||||
private function getAppList(): array {
|
||||
$apps = $this->appManager->getAllAppsInAppsFolders();
|
||||
$alwaysEnabled = $this->appManager->getAlwaysEnabledApps();
|
||||
|
||||
$subscriptionRegistry = Server::get(IRegistry::class);
|
||||
$supportedAppsIDs = $subscriptionRegistry->delegateGetSupportedApps();
|
||||
|
||||
$supportedApps = $enabledApps = $disabledApps = [];
|
||||
$versions = $this->appManager->getAppInstalledVersions();
|
||||
|
||||
// sort enabled apps above disabled apps
|
||||
foreach ($apps as $app) {
|
||||
if (in_array($app, $alwaysEnabled)) {
|
||||
continue;
|
||||
}
|
||||
if ($this->appManager->isEnabledForAnyone($app)) {
|
||||
if (in_array($app, $supportedAppsIDs)) {
|
||||
$supportedApps[] = $app;
|
||||
continue;
|
||||
}
|
||||
|
||||
// enabled but not ours
|
||||
$enabledApps[] = $app;
|
||||
continue;
|
||||
}
|
||||
|
||||
$disabledApps[] = $app;
|
||||
}
|
||||
|
||||
$apps = [
|
||||
'supported' => [],
|
||||
'enabled' => [],
|
||||
'disabled' => []
|
||||
];
|
||||
|
||||
sort($supportedApps);
|
||||
foreach ($supportedApps as $app) {
|
||||
$apps['supported'][$app] = $versions[$app] ?? true;
|
||||
}
|
||||
|
||||
sort($enabledApps);
|
||||
foreach ($enabledApps as $app) {
|
||||
$apps['enabled'][$app] = $versions[$app] ?? true;
|
||||
}
|
||||
|
||||
sort($disabledApps);
|
||||
foreach ($disabledApps as $app) {
|
||||
$apps['disabled'][$app] = $versions[$app] ?? false;
|
||||
}
|
||||
|
||||
return $apps;
|
||||
}
|
||||
|
||||
protected function getEncryptionInfo(): string {
|
||||
return $this->appConfig->getValueString('core', 'encryption_enabled', 'no');
|
||||
}
|
||||
|
||||
protected function getExternalStorageInfo(): string {
|
||||
$globalService = Server::get(GlobalStoragesService::class);
|
||||
$mounts = $globalService->getStorageForAllUsers();
|
||||
|
||||
// copy of OCA\Files_External\Command\ListCommand::listMounts
|
||||
if ($mounts === null || count($mounts) === 0) {
|
||||
return 'No mounts configured';
|
||||
}
|
||||
$headers = ['Mount ID', 'Mount Point', 'Storage', 'Authentication Type', 'Configuration', 'Options'];
|
||||
$headers[] = 'Applicable Users';
|
||||
$headers[] = 'Applicable Groups';
|
||||
$headers[] = 'Type';
|
||||
|
||||
$hideKeys = ['password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key', 'key', 'secret'];
|
||||
/** @var StorageConfig $mount */
|
||||
foreach ($mounts as $mount) {
|
||||
$config = $mount->getBackendOptions();
|
||||
foreach ($config as $key => $value) {
|
||||
if (in_array($key, $hideKeys)) {
|
||||
$mount->setBackendOption($key, '***');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$defaultMountOptions = [
|
||||
'encrypt' => true,
|
||||
'previews' => true,
|
||||
'filesystem_check_changes' => 1,
|
||||
'enable_sharing' => false,
|
||||
'encoding_compatibility' => false,
|
||||
'readonly' => false,
|
||||
];
|
||||
$rows = array_map(function (StorageConfig $config) use ($defaultMountOptions) {
|
||||
$storageConfig = $config->getBackendOptions();
|
||||
$keys = array_keys($storageConfig);
|
||||
$values = array_values($storageConfig);
|
||||
$configStrings = array_map(function ($key, $value) {
|
||||
return $key . ': ' . json_encode($value);
|
||||
}, $keys, $values);
|
||||
$configString = implode(', ', $configStrings);
|
||||
$mountOptions = $config->getMountOptions();
|
||||
// hide defaults
|
||||
foreach ($mountOptions as $key => $value) {
|
||||
if (isset($defaultMountOptions[$key]) && ($value === $defaultMountOptions[$key])) {
|
||||
unset($mountOptions[$key]);
|
||||
}
|
||||
}
|
||||
$keys = array_keys($mountOptions);
|
||||
$values = array_values($mountOptions);
|
||||
$optionsStrings = array_map(function ($key, $value) {
|
||||
return $key . ': ' . json_encode($value);
|
||||
}, $keys, $values);
|
||||
$optionsString = implode(', ', $optionsStrings);
|
||||
$values = [
|
||||
$config->getId(),
|
||||
$config->getMountPoint(),
|
||||
$config->getBackend()->getText(),
|
||||
$config->getAuthMechanism()->getText(),
|
||||
$configString,
|
||||
$optionsString
|
||||
];
|
||||
$applicableUsers = implode(', ', $config->getApplicableUsers());
|
||||
$applicableGroups = implode(', ', $config->getApplicableGroups());
|
||||
if ($applicableUsers === '' && $applicableGroups === '') {
|
||||
$applicableUsers = 'All';
|
||||
}
|
||||
$values[] = $applicableUsers;
|
||||
$values[] = $applicableGroups;
|
||||
$values[] = $config->getType() === StorageConfig::MOUNT_TYPE_ADMIN ? 'Admin' : 'Personal';
|
||||
|
||||
return $values;
|
||||
}, $mounts);
|
||||
|
||||
$output = new BufferedOutput();
|
||||
$table = new Table($output);
|
||||
$table->setHeaders($headers);
|
||||
$table->setRows($rows);
|
||||
$table->render();
|
||||
|
||||
return $output->fetch();
|
||||
}
|
||||
|
||||
private function getConfig(): array {
|
||||
$keys = $this->systemConfig->getKeys();
|
||||
$configs = [];
|
||||
foreach ($keys as $key) {
|
||||
$value = $this->config->getFilteredSystemValue($key, serialize(null));
|
||||
if ($value !== 'N;') {
|
||||
$configs[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $configs;
|
||||
}
|
||||
|
||||
private function getBrowser(): string {
|
||||
return $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
|
||||
}
|
||||
|
||||
private function getUserBackendInfo(): string {
|
||||
$backends = $this->userManager->getBackends();
|
||||
|
||||
$output = PHP_EOL;
|
||||
foreach ($backends as $backend) {
|
||||
$output .= ' * ' . get_class($backend) . PHP_EOL;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function getSubscriptionInfo(): string {
|
||||
$output = PHP_EOL;
|
||||
|
||||
if ($this->adapter->hasValidSubscription()) {
|
||||
$output .= ' * Instance has valid subscription key set' . PHP_EOL;
|
||||
} else {
|
||||
$output .= ' * No valid subscription key set' . PHP_EOL;
|
||||
}
|
||||
|
||||
$lastError = $this->appConfig->getValueInt('support', 'last_error');
|
||||
|
||||
if ($lastError > 0) {
|
||||
switch ($lastError) {
|
||||
case SubscriptionService::ERROR_FAILED_RETRY:
|
||||
$output .= ' * The subscription info could not properly fetched and will be retried' . PHP_EOL;
|
||||
break;
|
||||
case SubscriptionService::ERROR_FAILED_INVALID:
|
||||
$output .= ' * The subscription key was invalid' . PHP_EOL;
|
||||
break;
|
||||
case SubscriptionService::ERROR_NO_INTERNET_CONNECTION:
|
||||
$output .= ' * The subscription key could not be verified, because this server has no internet connection' . PHP_EOL;
|
||||
break;
|
||||
case SubscriptionService::ERROR_INVALID_SUBSCRIPTION_KEY:
|
||||
$output .= ' * The subscription key had an invalid format' . PHP_EOL;
|
||||
break;
|
||||
default:
|
||||
$output .= ' * An error occurred while fetching the subscription information' . PHP_EOL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->adapter->isHardUserLimitReached()) {
|
||||
$output .= ' * Reached user limit of subscription' . PHP_EOL;
|
||||
}
|
||||
|
||||
$rateLimitReached = $this->appConfig->getValueInt('notifications', 'rate_limit_reached');
|
||||
if ($rateLimitReached >= ($this->timeFactory->now()->getTimestamp() - 7 * 24 * 3600)) {
|
||||
$output .= ' * Fair-use push notification limit reached' . PHP_EOL;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Support\Sections;
|
||||
|
||||
use OCA\Support\IDetail;
|
||||
use OCA\Support\Section;
|
||||
use OCP\RichObjectStrings\IRichTextFormatter;
|
||||
use OCP\SetupCheck\ISetupCheckManager;
|
||||
use OCP\SetupCheck\SetupResult;
|
||||
|
||||
class SetupChecksSection extends Section {
|
||||
public function __construct(
|
||||
private ISetupCheckManager $setupCheckManager,
|
||||
private IRichTextFormatter $richTextFormatter,
|
||||
) {
|
||||
parent::__construct('setupchecks', 'Setup checks');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getDetails(): array {
|
||||
// FIXME Make sure to use the cached version once we have it
|
||||
$results = $this->setupCheckManager->runAll();
|
||||
foreach ($results as $category => $content) {
|
||||
if ($category === 'accounts') {
|
||||
/* Do not include accounts section in the report */
|
||||
continue;
|
||||
}
|
||||
$problems = '';
|
||||
foreach ($content as $class => $result) {
|
||||
if ($result->getSeverity() != SetupResult::SUCCESS) {
|
||||
$description = $result->getDescription();
|
||||
$descriptionParameters = $result->getDescriptionParameters();
|
||||
if ($description !== null && $descriptionParameters !== null) {
|
||||
$description = $this->richTextFormatter->richToParsed($description, $descriptionParameters);
|
||||
}
|
||||
$descriptionLines = explode("\n", $description);
|
||||
$problems .= ' * ' . $result->getName() . ': ' . implode("\n ", $descriptionLines) . "\n";
|
||||
}
|
||||
}
|
||||
if ($problems !== '') {
|
||||
$this->createDetail($category, $problems, IDetail::TYPE_COLLAPSIBLE);
|
||||
}
|
||||
}
|
||||
return parent::getDetails();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Support\Sections;
|
||||
|
||||
use OCA\Support\IDetail;
|
||||
use OCA\Support\Section;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IConfig;
|
||||
|
||||
class TalkSection extends Section {
|
||||
public function __construct(
|
||||
protected readonly IConfig $config,
|
||||
protected readonly IAppManager $appManager,
|
||||
protected readonly IClientService $clientService,
|
||||
protected readonly IAppConfig $appConfig,
|
||||
) {
|
||||
parent::__construct('talk', 'Talk');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getDetails(): array {
|
||||
$this->createDetail('Talk configuration', $this->getTalkInfo());
|
||||
$this->createDetail('Talk app configuration', $this->getTalkAppConfiguration(), IDetail::TYPE_COLLAPSIBLE_PREFORMAT);
|
||||
|
||||
return parent::getDetails();
|
||||
}
|
||||
|
||||
public function isTalkEnabled(): bool {
|
||||
return $this->appManager->isEnabledForUser('spreed');
|
||||
}
|
||||
|
||||
private function getTalkInfo(): string {
|
||||
$output = PHP_EOL;
|
||||
|
||||
$config = $this->config->getAppValue('spreed', 'stun_servers');
|
||||
$servers = json_decode($config, true);
|
||||
|
||||
$output .= PHP_EOL;
|
||||
$output .= 'STUN servers' . PHP_EOL;
|
||||
if (empty($servers)) {
|
||||
$output .= ' * no custom server configured' . PHP_EOL;
|
||||
} else {
|
||||
foreach ($servers as $server) {
|
||||
$output .= ' * ' . $server . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$config = $this->config->getAppValue('spreed', 'turn_servers');
|
||||
$servers = json_decode($config, true);
|
||||
|
||||
$output .= PHP_EOL;
|
||||
$output .= 'TURN servers' . PHP_EOL;
|
||||
if (empty($servers)) {
|
||||
$output .= ' * no custom server configured' . PHP_EOL;
|
||||
} else {
|
||||
foreach ($servers as $server) {
|
||||
$output .= ' * ' . ($server['schemes'] ?? 'turn') . ':' . $server['server'] . ' - ' . $server['protocols'] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$config = $this->config->getAppValue('spreed', 'signaling_mode', 'default');
|
||||
$output .= PHP_EOL;
|
||||
$output .= 'Signaling servers (mode: ' . $config . '):' . PHP_EOL;
|
||||
|
||||
if ($this->config->getAppValue('spreed', 'sip_bridge_shared_secret') !== '') {
|
||||
$output .= ' * SIP dialin is enabled' . PHP_EOL;
|
||||
} else {
|
||||
$output .= ' * SIP dialin is disabled' . PHP_EOL;
|
||||
}
|
||||
if ($this->config->getAppValue('spreed', 'sip_dialout', 'no') !== 'no') {
|
||||
$output .= ' * SIP dialout is enabled' . PHP_EOL;
|
||||
} else {
|
||||
$output .= ' * SIP dialout is disabled' . PHP_EOL;
|
||||
}
|
||||
|
||||
$config = $this->config->getAppValue('spreed', 'signaling_servers');
|
||||
$servers = json_decode($config, true);
|
||||
|
||||
if (empty($servers['servers'])) {
|
||||
$output .= ' * no custom server configured' . PHP_EOL;
|
||||
} else {
|
||||
foreach ($servers['servers'] as $server) {
|
||||
$output .= ' * ' . $server['server'] . ' - ' . $this->getTalkComponentVersion($server['server']) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$output .= PHP_EOL;
|
||||
$output .= 'Recording servers:' . PHP_EOL;
|
||||
if ($this->config->getAppValue('spreed', 'call_recording', 'yes') !== 'yes') {
|
||||
$output .= ' * Recording is disabled' . PHP_EOL;
|
||||
} else {
|
||||
$output .= ' * Recording is enabled' . PHP_EOL;
|
||||
}
|
||||
$output .= ' * Recording consent is set to "' . $this->config->getAppValue('spreed', 'recording_consent', 'default') . '"' . PHP_EOL;
|
||||
|
||||
$config = $this->config->getAppValue('spreed', 'recording_servers');
|
||||
$servers = json_decode($config, true);
|
||||
|
||||
if (empty($servers['servers'])) {
|
||||
$output .= ' * no recording server configured' . PHP_EOL;
|
||||
} else {
|
||||
foreach ($servers['servers'] as $server) {
|
||||
$output .= ' * ' . $server['server'] . ' - ' . $this->getTalkComponentVersion($server['server']) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function getTalkAppConfiguration(): string {
|
||||
$spreedConfig = $this->appConfig->getAllValues('spreed', filtered: true);
|
||||
return json_encode($spreedConfig, JSON_PRETTY_PRINT) . PHP_EOL;
|
||||
}
|
||||
|
||||
private function getTalkComponentVersion(string $url): string {
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
if (strpos($url, 'wss://') === 0) {
|
||||
$url = 'https://' . substr($url, 6);
|
||||
}
|
||||
|
||||
if (strpos($url, 'ws://') === 0) {
|
||||
$url = 'http://' . substr($url, 5);
|
||||
}
|
||||
|
||||
$client = $this->clientService->newClient();
|
||||
try {
|
||||
$response = $client->get($url . '/api/v1/welcome', [
|
||||
'verify' => false,
|
||||
'nextcloud' => [
|
||||
'allow_local_address' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$body = $response->getBody();
|
||||
|
||||
$data = json_decode($body, true);
|
||||
if (!is_array($data) || !isset($data['version'])) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
return (string)$data['version'];
|
||||
} catch (\Exception $e) {
|
||||
return 'error: ' . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user