PluginManager.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-FileCopyrightText: 2016 ownCloud GmbH.
  6. * SPDX-License-Identifier: AGPL-3.0-only
  7. */
  8. namespace OCA\DAV\AppInfo;
  9. use OC\ServerContainer;
  10. use OCA\DAV\CalDAV\AppCalendar\AppCalendarPlugin;
  11. use OCA\DAV\CalDAV\Integration\ICalendarProvider;
  12. use OCA\DAV\CardDAV\Integration\IAddressBookProvider;
  13. use OCP\App\IAppManager;
  14. use OCP\AppFramework\QueryException;
  15. use Sabre\DAV\Collection;
  16. use Sabre\DAV\ServerPlugin;
  17. use function array_map;
  18. use function class_exists;
  19. use function is_array;
  20. /**
  21. * Manager for DAV plugins from apps, used to register them
  22. * to the Sabre server.
  23. */
  24. class PluginManager {
  25. /**
  26. * @var ServerContainer
  27. */
  28. private $container;
  29. /**
  30. * @var IAppManager
  31. */
  32. private $appManager;
  33. /**
  34. * App plugins
  35. *
  36. * @var ServerPlugin[]
  37. */
  38. private $plugins = [];
  39. /**
  40. * App collections
  41. *
  42. * @var Collection[]
  43. */
  44. private $collections = [];
  45. /**
  46. * Address book plugins
  47. *
  48. * @var IAddressBookProvider[]
  49. */
  50. private $addressBookPlugins = [];
  51. /**
  52. * Calendar plugins
  53. *
  54. * @var ICalendarProvider[]
  55. */
  56. private $calendarPlugins = [];
  57. /** @var bool */
  58. private $populated = false;
  59. /**
  60. * Contstruct a PluginManager
  61. *
  62. * @param ServerContainer $container server container for resolving plugin classes
  63. * @param IAppManager $appManager app manager to loading apps and their info
  64. */
  65. public function __construct(ServerContainer $container, IAppManager $appManager) {
  66. $this->container = $container;
  67. $this->appManager = $appManager;
  68. }
  69. /**
  70. * Returns an array of app-registered plugins
  71. *
  72. * @return ServerPlugin[]
  73. */
  74. public function getAppPlugins() {
  75. $this->populate();
  76. return $this->plugins;
  77. }
  78. /**
  79. * Returns an array of app-registered collections
  80. *
  81. * @return array
  82. */
  83. public function getAppCollections() {
  84. $this->populate();
  85. return $this->collections;
  86. }
  87. /**
  88. * @return IAddressBookProvider[]
  89. */
  90. public function getAddressBookPlugins(): array {
  91. $this->populate();
  92. return $this->addressBookPlugins;
  93. }
  94. /**
  95. * Returns an array of app-registered calendar plugins
  96. *
  97. * @return ICalendarProvider[]
  98. */
  99. public function getCalendarPlugins():array {
  100. $this->populate();
  101. return $this->calendarPlugins;
  102. }
  103. /**
  104. * Retrieve plugin and collection list and populate attributes
  105. */
  106. private function populate(): void {
  107. if ($this->populated) {
  108. return;
  109. }
  110. $this->populated = true;
  111. $this->calendarPlugins[] = $this->container->get(AppCalendarPlugin::class);
  112. foreach ($this->appManager->getInstalledApps() as $app) {
  113. // load plugins and collections from info.xml
  114. $info = $this->appManager->getAppInfo($app);
  115. if (!isset($info['types']) || !in_array('dav', $info['types'], true)) {
  116. continue;
  117. }
  118. $plugins = $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info));
  119. foreach ($plugins as $plugin) {
  120. $this->plugins[] = $plugin;
  121. }
  122. $collections = $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info));
  123. foreach ($collections as $collection) {
  124. $this->collections[] = $collection;
  125. }
  126. $addresbookPlugins = $this->loadSabreAddressBookPluginsFromInfoXml($this->extractAddressBookPluginList($info));
  127. foreach ($addresbookPlugins as $addresbookPlugin) {
  128. $this->addressBookPlugins[] = $addresbookPlugin;
  129. }
  130. $calendarPlugins = $this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info));
  131. foreach ($calendarPlugins as $calendarPlugin) {
  132. $this->calendarPlugins[] = $calendarPlugin;
  133. }
  134. }
  135. }
  136. /**
  137. * @param array $array
  138. * @return string[]
  139. */
  140. private function extractPluginList(array $array): array {
  141. if (isset($array['sabre']) && is_array($array['sabre'])) {
  142. if (isset($array['sabre']['plugins']) && is_array($array['sabre']['plugins'])) {
  143. if (isset($array['sabre']['plugins']['plugin'])) {
  144. $items = $array['sabre']['plugins']['plugin'];
  145. if (!is_array($items)) {
  146. $items = [$items];
  147. }
  148. return $items;
  149. }
  150. }
  151. }
  152. return [];
  153. }
  154. /**
  155. * @param array $array
  156. * @return string[]
  157. */
  158. private function extractCollectionList(array $array): array {
  159. if (isset($array['sabre']) && is_array($array['sabre'])) {
  160. if (isset($array['sabre']['collections']) && is_array($array['sabre']['collections'])) {
  161. if (isset($array['sabre']['collections']['collection'])) {
  162. $items = $array['sabre']['collections']['collection'];
  163. if (!is_array($items)) {
  164. $items = [$items];
  165. }
  166. return $items;
  167. }
  168. }
  169. }
  170. return [];
  171. }
  172. /**
  173. * @param array $array
  174. * @return string[]
  175. */
  176. private function extractAddressBookPluginList(array $array): array {
  177. if (!isset($array['sabre']) || !is_array($array['sabre'])) {
  178. return [];
  179. }
  180. if (!isset($array['sabre']['address-book-plugins']) || !is_array($array['sabre']['address-book-plugins'])) {
  181. return [];
  182. }
  183. if (!isset($array['sabre']['address-book-plugins']['plugin'])) {
  184. return [];
  185. }
  186. $items = $array['sabre']['address-book-plugins']['plugin'];
  187. if (!is_array($items)) {
  188. $items = [$items];
  189. }
  190. return $items;
  191. }
  192. /**
  193. * @param array $array
  194. * @return string[]
  195. */
  196. private function extractCalendarPluginList(array $array): array {
  197. if (isset($array['sabre']) && is_array($array['sabre'])) {
  198. if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) {
  199. if (isset($array['sabre']['calendar-plugins']['plugin'])) {
  200. $items = $array['sabre']['calendar-plugins']['plugin'];
  201. if (!is_array($items)) {
  202. $items = [$items];
  203. }
  204. return $items;
  205. }
  206. }
  207. }
  208. return [];
  209. }
  210. private function createClass(string $className): object {
  211. try {
  212. return $this->container->get($className);
  213. } catch (QueryException $e) {
  214. if (class_exists($className)) {
  215. return new $className();
  216. }
  217. }
  218. throw new \Exception('Could not load ' . $className, 0, $e);
  219. }
  220. /**
  221. * @param string[] $classes
  222. * @return ServerPlugin[]
  223. * @throws \Exception
  224. */
  225. private function loadSabrePluginsFromInfoXml(array $classes): array {
  226. return array_map(function (string $className): ServerPlugin {
  227. $instance = $this->createClass($className);
  228. if (!($instance instanceof ServerPlugin)) {
  229. throw new \Exception('Sabre server plugin ' . $className . ' does not implement the ' . ServerPlugin::class . ' interface');
  230. }
  231. return $instance;
  232. }, $classes);
  233. }
  234. /**
  235. * @param string[] $classes
  236. * @return Collection[]
  237. */
  238. private function loadSabreCollectionsFromInfoXml(array $classes): array {
  239. return array_map(function (string $className): Collection {
  240. $instance = $this->createClass($className);
  241. if (!($instance instanceof Collection)) {
  242. throw new \Exception('Sabre collection plugin ' . $className . ' does not implement the ' . Collection::class . ' interface');
  243. }
  244. return $instance;
  245. }, $classes);
  246. }
  247. /**
  248. * @param string[] $classes
  249. * @return IAddressBookProvider[]
  250. */
  251. private function loadSabreAddressBookPluginsFromInfoXml(array $classes): array {
  252. return array_map(function (string $className): IAddressBookProvider {
  253. $instance = $this->createClass($className);
  254. if (!($instance instanceof IAddressBookProvider)) {
  255. throw new \Exception('Sabre address book plugin class ' . $className . ' does not implement the ' . IAddressBookProvider::class . ' interface');
  256. }
  257. return $instance;
  258. }, $classes);
  259. }
  260. /**
  261. * @param string[] $classes
  262. * @return ICalendarProvider[]
  263. */
  264. private function loadSabreCalendarPluginsFromInfoXml(array $classes): array {
  265. return array_map(function (string $className): ICalendarProvider {
  266. $instance = $this->createClass($className);
  267. if (!($instance instanceof ICalendarProvider)) {
  268. throw new \Exception('Sabre calendar plugin class ' . $className . ' does not implement the ' . ICalendarProvider::class . ' interface');
  269. }
  270. return $instance;
  271. }, $classes);
  272. }
  273. }