ThemeInjectionService.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OCA\Theming\Service;
  7. use OCA\Theming\ITheme;
  8. use OCA\Theming\Themes\DefaultTheme;
  9. use OCA\Theming\Util;
  10. use OCP\IConfig;
  11. use OCP\IURLGenerator;
  12. use OCP\IUserSession;
  13. class ThemeInjectionService {
  14. private ?string $userId;
  15. public function __construct(
  16. private IURLGenerator $urlGenerator,
  17. private ThemesService $themesService,
  18. private DefaultTheme $defaultTheme,
  19. private Util $util,
  20. private IConfig $config,
  21. IUserSession $userSession,
  22. ) {
  23. if ($userSession->getUser() !== null) {
  24. $this->userId = $userSession->getUser()->getUID();
  25. } else {
  26. $this->userId = null;
  27. }
  28. }
  29. public function injectHeaders(): void {
  30. $themes = $this->themesService->getThemes();
  31. $defaultTheme = $themes[$this->defaultTheme->getId()];
  32. $mediaThemes = array_filter($themes, function ($theme) {
  33. // Check if the theme provides a media query
  34. return (bool)$theme->getMediaQuery();
  35. });
  36. // Default theme fallback
  37. $this->addThemeHeaders($defaultTheme);
  38. // Themes applied by media queries
  39. foreach ($mediaThemes as $theme) {
  40. $this->addThemeHeaders($theme, true, $theme->getMediaQuery());
  41. }
  42. // Themes
  43. foreach ($this->themesService->getThemes() as $theme) {
  44. // Ignore default theme as already processed first
  45. if ($theme->getId() === $this->defaultTheme->getId()) {
  46. continue;
  47. }
  48. $this->addThemeHeaders($theme, false);
  49. }
  50. // Meta headers
  51. $this->addThemeMetaHeaders($themes);
  52. }
  53. /**
  54. * Inject theme header into rendered page
  55. *
  56. * @param ITheme $theme the theme
  57. * @param bool $plain request the :root syntax
  58. * @param string $media media query to use in the <link> element
  59. */
  60. private function addThemeHeaders(ITheme $theme, bool $plain = true, ?string $media = null): void {
  61. $linkToCSS = $this->urlGenerator->linkToRoute('theming.Theming.getThemeStylesheet', [
  62. 'themeId' => $theme->getId(),
  63. 'plain' => $plain,
  64. 'v' => $this->util->getCacheBuster(),
  65. ]);
  66. \OCP\Util::addHeader('link', [
  67. 'rel' => 'stylesheet',
  68. 'media' => $media,
  69. 'href' => $linkToCSS,
  70. 'class' => 'theme'
  71. ]);
  72. }
  73. /**
  74. * Inject meta headers into rendered page
  75. *
  76. * @param ITheme[] $themes the theme
  77. */
  78. private function addThemeMetaHeaders(array $themes): void {
  79. $metaHeaders = [];
  80. // Meta headers
  81. foreach ($this->themesService->getThemes() as $theme) {
  82. if (!empty($theme->getMeta())) {
  83. foreach ($theme->getMeta() as $meta) {
  84. if (!isset($meta['name']) || !isset($meta['content'])) {
  85. continue;
  86. }
  87. if (!isset($metaHeaders[$meta['name']])) {
  88. $metaHeaders[$meta['name']] = [];
  89. }
  90. $metaHeaders[$meta['name']][] = $meta['content'];
  91. }
  92. }
  93. }
  94. foreach ($metaHeaders as $name => $content) {
  95. \OCP\Util::addHeader('meta', [
  96. 'name' => $name,
  97. 'content' => join(' ', array_unique($content)),
  98. ]);
  99. }
  100. }
  101. }