Manager.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Dashboard;
  8. use InvalidArgumentException;
  9. use OCP\App\IAppManager;
  10. use OCP\Dashboard\IConditionalWidget;
  11. use OCP\Dashboard\IManager;
  12. use OCP\Dashboard\IWidget;
  13. use Psr\Container\ContainerExceptionInterface;
  14. use Psr\Container\ContainerInterface;
  15. use Psr\Log\LoggerInterface;
  16. use Throwable;
  17. class Manager implements IManager {
  18. /** @var array */
  19. private $lazyWidgets = [];
  20. /** @var array<string, IWidget> */
  21. private array $widgets = [];
  22. private ?IAppManager $appManager = null;
  23. public function __construct(
  24. private ContainerInterface $serverContainer,
  25. private LoggerInterface $logger,
  26. ) {
  27. }
  28. private function registerWidget(IWidget $widget): void {
  29. if (array_key_exists($widget->getId(), $this->widgets)) {
  30. throw new InvalidArgumentException('Dashboard widget with this id has already been registered');
  31. }
  32. if (!preg_match('/^[a-z][a-z0-9\-_]*$/', $widget->getId())) {
  33. $this->logger->debug('Deprecated dashboard widget ID provided: "' . $widget->getId() . '" [ ' . get_class($widget) . ' ]. Please use a-z, 0-9, - and _ only, starting with a-z');
  34. }
  35. $this->widgets[$widget->getId()] = $widget;
  36. }
  37. public function lazyRegisterWidget(string $widgetClass, string $appId): void {
  38. $this->lazyWidgets[] = ['class' => $widgetClass, 'appId' => $appId];
  39. }
  40. public function loadLazyPanels(): void {
  41. if ($this->appManager === null) {
  42. $this->appManager = $this->serverContainer->get(IAppManager::class);
  43. }
  44. $services = $this->lazyWidgets;
  45. foreach ($services as $service) {
  46. /** @psalm-suppress InvalidCatch */
  47. try {
  48. if (!$this->appManager->isEnabledForUser($service['appId'])) {
  49. // all apps are registered, but some may not be enabled for the user
  50. continue;
  51. }
  52. /** @var IWidget $widget */
  53. $widget = $this->serverContainer->get($service['class']);
  54. } catch (ContainerExceptionInterface $e) {
  55. /*
  56. * There is a circular dependency between the logger and the registry, so
  57. * we can not inject it. Thus the static call.
  58. */
  59. \OC::$server->get(LoggerInterface::class)->critical(
  60. 'Could not load lazy dashboard widget: ' . $service['class'],
  61. ['exception' => $e]
  62. );
  63. continue;
  64. }
  65. /**
  66. * Try to register the loaded reporter. Theoretically it could be of a wrong
  67. * type, so we might get a TypeError here that we should catch.
  68. */
  69. try {
  70. if ($widget instanceof IConditionalWidget && !$widget->isEnabled()) {
  71. continue;
  72. }
  73. $this->registerWidget($widget);
  74. } catch (Throwable $e) {
  75. /*
  76. * There is a circular dependency between the logger and the registry, so
  77. * we can not inject it. Thus the static call.
  78. */
  79. \OC::$server->get(LoggerInterface::class)->critical(
  80. 'Could not register lazy dashboard widget: ' . $service['class'],
  81. ['exception' => $e]
  82. );
  83. continue;
  84. }
  85. try {
  86. $startTime = microtime(true);
  87. $widget->load();
  88. $endTime = microtime(true);
  89. $duration = $endTime - $startTime;
  90. if ($duration > 1) {
  91. \OC::$server->get(LoggerInterface::class)->info(
  92. 'Dashboard widget {widget} took {duration} seconds to load.',
  93. [
  94. 'widget' => $widget->getId(),
  95. 'duration' => round($duration, 2),
  96. ]
  97. );
  98. }
  99. } catch (Throwable $e) {
  100. \OC::$server->get(LoggerInterface::class)->critical(
  101. 'Error during dashboard widget loading: ' . $service['class'],
  102. ['exception' => $e]
  103. );
  104. continue;
  105. }
  106. }
  107. $this->lazyWidgets = [];
  108. }
  109. /**
  110. * @return array<string, IWidget>
  111. */
  112. public function getWidgets(): array {
  113. $this->loadLazyPanels();
  114. return $this->widgets;
  115. }
  116. }