From f34865eb4eb2cebc6379ebb0479c2b2e997a2301 Mon Sep 17 00:00:00 2001 From: Côme Chilliet Date: Thu, 4 Jan 2024 16:27:18 +0100 Subject: Add RichObject support for SetupChecks descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- core/Command/SetupChecks.php | 28 ++++++++++++++++++++++++++++ lib/public/SetupCheck/SetupResult.php | 34 ++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/core/Command/SetupChecks.php b/core/Command/SetupChecks.php index bd76a9d1e65..0940a139617 100644 --- a/core/Command/SetupChecks.php +++ b/core/Command/SetupChecks.php @@ -45,6 +45,30 @@ class SetupChecks extends Base { ; } + /** + * @throws \InvalidArgumentException if a parameter has no name or no type + */ + private function richToParsed(string $message, array $parameters): string { + $placeholders = []; + $replacements = []; + foreach ($parameters as $placeholder => $parameter) { + $placeholders[] = '{' . $placeholder . '}'; + foreach (['name','type'] as $requiredField) { + if (!isset($parameter[$requiredField]) || !is_string($parameter[$requiredField])) { + throw new \InvalidArgumentException("Invalid rich object, {$requiredField} field is missing"); + } + } + if ($parameter['type'] === 'user') { + $replacements[] = '@' . $parameter['name']; + } elseif ($parameter['type'] === 'file') { + $replacements[] = $parameter['path'] ?? $parameter['name']; + } else { + $replacements[] = $parameter['name']; + } + } + return str_replace($placeholders, $replacements, $message); + } + protected function execute(InputInterface $input, OutputInterface $output): int { $results = $this->setupCheckManager->runAll(); switch ($input->getOption('output')) { @@ -70,6 +94,10 @@ class SetupChecks extends Base { }; $verbosity = ($check->getSeverity() === 'error' ? OutputInterface::VERBOSITY_QUIET : OutputInterface::VERBOSITY_NORMAL); $description = $check->getDescription(); + $descriptionParameters = $check->getDescriptionParameters(); + if ($descriptionParameters !== null) { + $description = $this->richToParsed($description, $descriptionParameters); + } $output->writeln( "\t\t". ($styleTag !== null ? "<{$styleTag}>" : ''). diff --git a/lib/public/SetupCheck/SetupResult.php b/lib/public/SetupCheck/SetupResult.php index e4a7744178a..51428a001e0 100644 --- a/lib/public/SetupCheck/SetupResult.php +++ b/lib/public/SetupCheck/SetupResult.php @@ -46,10 +46,12 @@ class SetupResult implements \JsonSerializable { * @brief Private constructor, use success()/info()/warning()/error() instead * @param self::SUCCESS|self::INFO|self::WARNING|self::ERROR $severity * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters */ private function __construct( private string $severity, private ?string $description = null, + private ?array $descriptionParameters = null, private ?string $linkToDoc = null, ) { } @@ -59,9 +61,10 @@ class SetupResult implements \JsonSerializable { * @param ?string $description Translated detailed description to display to the user * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters */ - public static function success(?string $description = null, ?string $linkToDoc = null): self { - return new self(self::SUCCESS, $description, $linkToDoc); + public static function success(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::SUCCESS, $description, $descriptionParameters, $linkToDoc); } /** @@ -69,9 +72,10 @@ class SetupResult implements \JsonSerializable { * @param ?string $description Translated detailed description to display to the user * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters */ - public static function info(?string $description = null, ?string $linkToDoc = null): self { - return new self(self::INFO, $description, $linkToDoc); + public static function info(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::INFO, $description, $descriptionParameters, $linkToDoc); } /** @@ -79,9 +83,10 @@ class SetupResult implements \JsonSerializable { * @param ?string $description Translated detailed description to display to the user * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters */ - public static function warning(?string $description = null, ?string $linkToDoc = null): self { - return new self(self::WARNING, $description, $linkToDoc); + public static function warning(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::WARNING, $description, $descriptionParameters, $linkToDoc); } /** @@ -89,9 +94,10 @@ class SetupResult implements \JsonSerializable { * @param ?string $description Translated detailed description to display to the user * @param ?string $linkToDoc URI of related relevent documentation, be it from Nextcloud or another project * @since 28.0.0 + * @since 28.0.2 Optional parameter ?array $descriptionParameters */ - public static function error(?string $description = null, ?string $linkToDoc = null): self { - return new self(self::ERROR, $description, $linkToDoc); + public static function error(?string $description = null, ?string $linkToDoc = null, ?array $descriptionParameters = null): self { + return new self(self::ERROR, $description, $descriptionParameters, $linkToDoc); } /** @@ -113,6 +119,17 @@ class SetupResult implements \JsonSerializable { return $this->description; } + /** + * @brief Get the description parameters for the setup check result + * + * If this returns null, description must not be treated as rich text + * + * @since 28.0.2 + */ + public function getDescriptionParameters(): ?array { + return $this->descriptionParameters; + } + /** * @brief Get the name for the setup check * @@ -150,6 +167,7 @@ class SetupResult implements \JsonSerializable { 'name' => $this->name, 'severity' => $this->severity, 'description' => $this->description, + 'descriptionParameters' => $this->descriptionParameters, 'linkToDoc' => $this->linkToDoc, ]; } -- cgit v1.2.3 From a78abd84ce7069b30f299abd83ca5d931496b8e8 Mon Sep 17 00:00:00 2001 From: Côme Chilliet Date: Tue, 9 Jan 2024 16:25:25 +0100 Subject: Validate rich objects passed to SetupResult MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/public/SetupCheck/SetupResult.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/public/SetupCheck/SetupResult.php b/lib/public/SetupCheck/SetupResult.php index 51428a001e0..7d30b8eaff9 100644 --- a/lib/public/SetupCheck/SetupResult.php +++ b/lib/public/SetupCheck/SetupResult.php @@ -26,6 +26,8 @@ declare(strict_types=1); namespace OCP\SetupCheck; +use OCP\RichObjectStrings\IValidator; + /** * @brief This class is used for storing the result of a setup check * @@ -54,6 +56,9 @@ class SetupResult implements \JsonSerializable { private ?array $descriptionParameters = null, private ?string $linkToDoc = null, ) { + if ($description !== null && $descriptionParameters !== null) { + \OCP\Server::get(IValidator::class)->validate($description, $descriptionParameters); + } } /** -- cgit v1.2.3 From ee0175e7d4c0422cbc1698390d94adb8d20da3fc Mon Sep 17 00:00:00 2001 From: Côme Chilliet Date: Tue, 9 Jan 2024 16:27:23 +0100 Subject: Fix psalm issue in SetupChecks command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- core/Command/SetupChecks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Command/SetupChecks.php b/core/Command/SetupChecks.php index 0940a139617..112bbfeb582 100644 --- a/core/Command/SetupChecks.php +++ b/core/Command/SetupChecks.php @@ -95,7 +95,7 @@ class SetupChecks extends Base { $verbosity = ($check->getSeverity() === 'error' ? OutputInterface::VERBOSITY_QUIET : OutputInterface::VERBOSITY_NORMAL); $description = $check->getDescription(); $descriptionParameters = $check->getDescriptionParameters(); - if ($descriptionParameters !== null) { + if ($description !== null && $descriptionParameters !== null) { $description = $this->richToParsed($description, $descriptionParameters); } $output->writeln( -- cgit v1.2.3 From 7274a8d781e46e7862ae0e0634c004abb28592c4 Mon Sep 17 00:00:00 2001 From: Côme Chilliet Date: Tue, 9 Jan 2024 16:43:46 +0100 Subject: Fix UI for rich object in setupcheck results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- core/js/setupchecks.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js index 4331e8e9cc9..bf7c7e43083 100644 --- a/core/js/setupchecks.js +++ b/core/js/setupchecks.js @@ -332,6 +332,28 @@ return deferred.promise(); }, + /** + * @param message The message string containing placeholders. + * @param parameters An object with keys as placeholders and values as their replacements. + * + * @return The message with placeholders replaced by values. + */ + richToParsed: function (message, parameters) { + for (var [placeholder, parameter] of Object.entries(parameters)) { + var replacement; + if (parameter.type === 'user') { + replacement = '@' + parameter.name; + } else if (parameter.type === 'file') { + replacement = parameter.path || parameter.name; + } else { + replacement = parameter.name; + } + message = message.replace('{' + placeholder + '}', replacement); + } + + return message; + }, + addGenericSetupCheck: function(data, check, messages) { var setupCheck = data[check] || { pass: true, description: '', severity: 'info', linkToDoc: null} @@ -343,6 +365,9 @@ } var message = setupCheck.description; + if (setupCheck.descriptionParameters) { + message = this.richToParsed(message, setupCheck.descriptionParameters); + } if (setupCheck.linkToDoc) { message += ' ' + t('core', 'For more details see the {linkstart}documentation ↗{linkend}.') .replace('{linkstart}', '') -- cgit v1.2.3 From 9e4eb34a46117ad1da099c069cddb77cf3787b5f Mon Sep 17 00:00:00 2001 From: Côme Chilliet Date: Tue, 9 Jan 2024 16:44:48 +0100 Subject: Add TODO for richToParsed refactor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- core/Command/SetupChecks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/Command/SetupChecks.php b/core/Command/SetupChecks.php index 112bbfeb582..ca39948de4e 100644 --- a/core/Command/SetupChecks.php +++ b/core/Command/SetupChecks.php @@ -46,6 +46,7 @@ class SetupChecks extends Base { } /** + * @TODO move this method to a common service used by notifications, activity and this command * @throws \InvalidArgumentException if a parameter has no name or no type */ private function richToParsed(string $message, array $parameters): string { -- cgit v1.2.3 From 6fc224dfce04122dc131aa9a900c2592cc2d8b4d Mon Sep 17 00:00:00 2001 From: Côme Chilliet Date: Tue, 9 Jan 2024 16:46:42 +0100 Subject: Document new throw possibility in SetupResult MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/public/SetupCheck/SetupResult.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/public/SetupCheck/SetupResult.php b/lib/public/SetupCheck/SetupResult.php index 7d30b8eaff9..835cbe8b4a2 100644 --- a/lib/public/SetupCheck/SetupResult.php +++ b/lib/public/SetupCheck/SetupResult.php @@ -49,6 +49,7 @@ class SetupResult implements \JsonSerializable { * @param self::SUCCESS|self::INFO|self::WARNING|self::ERROR $severity * @since 28.0.0 * @since 28.0.2 Optional parameter ?array $descriptionParameters + * @since 28.0.2 throws \OCP\RichObjectStrings\InvalidObjectExeption */ private function __construct( private string $severity, -- cgit v1.2.3 From e884ffd4c8046c3fd991ce56eee76b369aabc338 Mon Sep 17 00:00:00 2001 From: Côme Chilliet <91878298+come-nc@users.noreply.github.com> Date: Thu, 11 Jan 2024 11:26:24 +0100 Subject: Use match statement in richToParsed implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benjamin Gaussorgues Signed-off-by: Côme Chilliet --- core/Command/SetupChecks.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/Command/SetupChecks.php b/core/Command/SetupChecks.php index ca39948de4e..e6e54fbcf22 100644 --- a/core/Command/SetupChecks.php +++ b/core/Command/SetupChecks.php @@ -59,13 +59,11 @@ class SetupChecks extends Base { throw new \InvalidArgumentException("Invalid rich object, {$requiredField} field is missing"); } } - if ($parameter['type'] === 'user') { - $replacements[] = '@' . $parameter['name']; - } elseif ($parameter['type'] === 'file') { - $replacements[] = $parameter['path'] ?? $parameter['name']; - } else { - $replacements[] = $parameter['name']; - } + $replacements[] = match($parameter['type']) { + 'user' => '@' . $parameter['name'], + 'file' => $parameter['path'] ?? $parameter['name'], + default => $parameter['name'], + }; } return str_replace($placeholders, $replacements, $message); } -- cgit v1.2.3