Manager.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OC\Settings;
  7. use Closure;
  8. use OCP\AppFramework\QueryException;
  9. use OCP\Group\ISubAdmin;
  10. use OCP\IGroupManager;
  11. use OCP\IL10N;
  12. use OCP\IServerContainer;
  13. use OCP\IURLGenerator;
  14. use OCP\IUser;
  15. use OCP\L10N\IFactory;
  16. use OCP\Settings\IIconSection;
  17. use OCP\Settings\IManager;
  18. use OCP\Settings\ISettings;
  19. use OCP\Settings\ISubAdminSettings;
  20. use Psr\Log\LoggerInterface;
  21. class Manager implements IManager {
  22. /** @var LoggerInterface */
  23. private $log;
  24. /** @var IL10N */
  25. private $l;
  26. /** @var IFactory */
  27. private $l10nFactory;
  28. /** @var IURLGenerator */
  29. private $url;
  30. /** @var IServerContainer */
  31. private $container;
  32. /** @var AuthorizedGroupMapper $mapper */
  33. private $mapper;
  34. /** @var IGroupManager $groupManager */
  35. private $groupManager;
  36. /** @var ISubAdmin $subAdmin */
  37. private $subAdmin;
  38. public function __construct(
  39. LoggerInterface $log,
  40. IFactory $l10nFactory,
  41. IURLGenerator $url,
  42. IServerContainer $container,
  43. AuthorizedGroupMapper $mapper,
  44. IGroupManager $groupManager,
  45. ISubAdmin $subAdmin
  46. ) {
  47. $this->log = $log;
  48. $this->l10nFactory = $l10nFactory;
  49. $this->url = $url;
  50. $this->container = $container;
  51. $this->mapper = $mapper;
  52. $this->groupManager = $groupManager;
  53. $this->subAdmin = $subAdmin;
  54. }
  55. /** @var array<self::SETTINGS_*, list<class-string<IIconSection>>> */
  56. protected $sectionClasses = [];
  57. /** @var array<self::SETTINGS_*, array<string, IIconSection>> */
  58. protected $sections = [];
  59. /**
  60. * @inheritdoc
  61. */
  62. public function registerSection(string $type, string $section) {
  63. if (!isset($this->sectionClasses[$type])) {
  64. $this->sectionClasses[$type] = [];
  65. }
  66. $this->sectionClasses[$type][] = $section;
  67. }
  68. /**
  69. * @psalm-param self::SETTINGS_* $type
  70. *
  71. * @return IIconSection[]
  72. */
  73. protected function getSections(string $type): array {
  74. if (!isset($this->sections[$type])) {
  75. $this->sections[$type] = [];
  76. }
  77. if (!isset($this->sectionClasses[$type])) {
  78. return $this->sections[$type];
  79. }
  80. foreach (array_unique($this->sectionClasses[$type]) as $index => $class) {
  81. try {
  82. /** @var IIconSection $section */
  83. $section = $this->container->get($class);
  84. } catch (QueryException $e) {
  85. $this->log->info($e->getMessage(), ['exception' => $e]);
  86. continue;
  87. }
  88. $sectionID = $section->getID();
  89. if (!$this->isKnownDuplicateSectionId($sectionID) && isset($this->sections[$type][$sectionID])) {
  90. $e = new \InvalidArgumentException('Section with the same ID already registered: ' . $sectionID . ', class: ' . $class);
  91. $this->log->info($e->getMessage(), ['exception' => $e]);
  92. continue;
  93. }
  94. $this->sections[$type][$sectionID] = $section;
  95. unset($this->sectionClasses[$type][$index]);
  96. }
  97. return $this->sections[$type];
  98. }
  99. /**
  100. * @inheritdoc
  101. */
  102. public function getSection(string $type, string $sectionId): ?IIconSection {
  103. if (isset($this->sections[$type]) && isset($this->sections[$type][$sectionId])) {
  104. return $this->sections[$type][$sectionId];
  105. }
  106. return null;
  107. }
  108. protected function isKnownDuplicateSectionId(string $sectionID): bool {
  109. return in_array($sectionID, [
  110. 'connected-accounts',
  111. 'notifications',
  112. ], true);
  113. }
  114. /** @var array<class-string<ISettings>, self::SETTINGS_*> */
  115. protected $settingClasses = [];
  116. /** @var array<self::SETTINGS_*, array<string, list<ISettings>>> */
  117. protected $settings = [];
  118. /**
  119. * @inheritdoc
  120. */
  121. public function registerSetting(string $type, string $setting) {
  122. $this->settingClasses[$setting] = $type;
  123. }
  124. /**
  125. * @psalm-param self::SETTINGS_* $type The type of the setting.
  126. * @param string $section
  127. * @param ?Closure $filter optional filter to apply on all loaded ISettings
  128. *
  129. * @return ISettings[]
  130. */
  131. protected function getSettings(string $type, string $section, ?Closure $filter = null): array {
  132. if (!isset($this->settings[$type])) {
  133. $this->settings[$type] = [];
  134. }
  135. if (!isset($this->settings[$type][$section])) {
  136. $this->settings[$type][$section] = [];
  137. }
  138. foreach ($this->settingClasses as $class => $settingsType) {
  139. if ($type !== $settingsType) {
  140. continue;
  141. }
  142. try {
  143. /** @var ISettings $setting */
  144. $setting = $this->container->get($class);
  145. } catch (QueryException $e) {
  146. $this->log->info($e->getMessage(), ['exception' => $e]);
  147. continue;
  148. }
  149. if (!$setting instanceof ISettings) {
  150. $e = new \InvalidArgumentException('Invalid settings setting registered (' . $class . ')');
  151. $this->log->info($e->getMessage(), ['exception' => $e]);
  152. continue;
  153. }
  154. if ($filter !== null && !$filter($setting)) {
  155. continue;
  156. }
  157. if ($setting->getSection() === null) {
  158. continue;
  159. }
  160. if (!isset($this->settings[$settingsType][$setting->getSection()])) {
  161. $this->settings[$settingsType][$setting->getSection()] = [];
  162. }
  163. $this->settings[$settingsType][$setting->getSection()][] = $setting;
  164. unset($this->settingClasses[$class]);
  165. }
  166. return $this->settings[$type][$section];
  167. }
  168. /**
  169. * @inheritdoc
  170. */
  171. public function getAdminSections(): array {
  172. // built-in sections
  173. $sections = [];
  174. $appSections = $this->getSections('admin');
  175. foreach ($appSections as $section) {
  176. /** @var IIconSection $section */
  177. if (!isset($sections[$section->getPriority()])) {
  178. $sections[$section->getPriority()] = [];
  179. }
  180. $sections[$section->getPriority()][] = $section;
  181. }
  182. ksort($sections);
  183. return $sections;
  184. }
  185. /**
  186. * @inheritdoc
  187. */
  188. public function getAdminSettings(string $section, bool $subAdminOnly = false): array {
  189. if ($subAdminOnly) {
  190. $subAdminSettingsFilter = function (ISettings $settings) {
  191. return $settings instanceof ISubAdminSettings;
  192. };
  193. $appSettings = $this->getSettings('admin', $section, $subAdminSettingsFilter);
  194. } else {
  195. $appSettings = $this->getSettings('admin', $section);
  196. }
  197. $settings = [];
  198. foreach ($appSettings as $setting) {
  199. if (!isset($settings[$setting->getPriority()])) {
  200. $settings[$setting->getPriority()] = [];
  201. }
  202. $settings[$setting->getPriority()][] = $setting;
  203. }
  204. ksort($settings);
  205. return $settings;
  206. }
  207. /**
  208. * @inheritdoc
  209. */
  210. public function getPersonalSections(): array {
  211. if ($this->l === null) {
  212. $this->l = $this->l10nFactory->get('lib');
  213. }
  214. $sections = [];
  215. $legacyForms = \OC_App::getForms('personal');
  216. if ((!empty($legacyForms) && $this->hasLegacyPersonalSettingsToRender($legacyForms))
  217. || count($this->getPersonalSettings('additional')) > 1) {
  218. $sections[98] = [new Section('additional', $this->l->t('Additional settings'), 0, $this->url->imagePath('core', 'actions/settings-dark.svg'))];
  219. }
  220. $appSections = $this->getSections('personal');
  221. foreach ($appSections as $section) {
  222. /** @var IIconSection $section */
  223. if (!isset($sections[$section->getPriority()])) {
  224. $sections[$section->getPriority()] = [];
  225. }
  226. $sections[$section->getPriority()][] = $section;
  227. }
  228. ksort($sections);
  229. return $sections;
  230. }
  231. /**
  232. * @param string[] $forms
  233. *
  234. * @return bool
  235. */
  236. private function hasLegacyPersonalSettingsToRender(array $forms): bool {
  237. foreach ($forms as $form) {
  238. if (trim($form) !== '') {
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. /**
  245. * @inheritdoc
  246. */
  247. public function getPersonalSettings(string $section): array {
  248. $settings = [];
  249. $appSettings = $this->getSettings('personal', $section);
  250. foreach ($appSettings as $setting) {
  251. if (!isset($settings[$setting->getPriority()])) {
  252. $settings[$setting->getPriority()] = [];
  253. }
  254. $settings[$setting->getPriority()][] = $setting;
  255. }
  256. ksort($settings);
  257. return $settings;
  258. }
  259. /**
  260. * @inheritdoc
  261. */
  262. public function getAllowedAdminSettings(string $section, IUser $user): array {
  263. $isAdmin = $this->groupManager->isAdmin($user->getUID());
  264. if ($isAdmin) {
  265. $appSettings = $this->getSettings('admin', $section);
  266. } else {
  267. $authorizedSettingsClasses = $this->mapper->findAllClassesForUser($user);
  268. if ($this->subAdmin->isSubAdmin($user)) {
  269. $authorizedGroupFilter = function (ISettings $settings) use ($authorizedSettingsClasses) {
  270. return $settings instanceof ISubAdminSettings
  271. || in_array(get_class($settings), $authorizedSettingsClasses) === true;
  272. };
  273. } else {
  274. $authorizedGroupFilter = function (ISettings $settings) use ($authorizedSettingsClasses) {
  275. return in_array(get_class($settings), $authorizedSettingsClasses) === true;
  276. };
  277. }
  278. $appSettings = $this->getSettings('admin', $section, $authorizedGroupFilter);
  279. }
  280. $settings = [];
  281. foreach ($appSettings as $setting) {
  282. if (!isset($settings[$setting->getPriority()])) {
  283. $settings[$setting->getPriority()] = [];
  284. }
  285. $settings[$setting->getPriority()][] = $setting;
  286. }
  287. ksort($settings);
  288. return $settings;
  289. }
  290. /**
  291. * @inheritdoc
  292. */
  293. public function getAllAllowedAdminSettings(IUser $user): array {
  294. $this->getSettings('admin', ''); // Make sure all the settings are loaded
  295. $settings = [];
  296. $authorizedSettingsClasses = $this->mapper->findAllClassesForUser($user);
  297. foreach ($this->settings['admin'] as $section) {
  298. foreach ($section as $setting) {
  299. if (in_array(get_class($setting), $authorizedSettingsClasses) === true) {
  300. $settings[] = $setting;
  301. }
  302. }
  303. }
  304. return $settings;
  305. }
  306. }