You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PluginManager.php 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2016, ownCloud GmbH.
  5. *
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Georg Ehrke <oc.list@georgehrke.com>
  8. * @author Julius Härtl <jus@bitgrid.net>
  9. * @author Roeland Jago Douma <roeland@famdouma.nl>
  10. * @author Vincent Petry <vincent@nextcloud.com>
  11. *
  12. * @license AGPL-3.0
  13. *
  14. * This code is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License, version 3,
  16. * as published by the Free Software Foundation.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License, version 3,
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>
  25. *
  26. */
  27. namespace OCA\DAV\AppInfo;
  28. use OC\ServerContainer;
  29. use OCA\DAV\CalDAV\Integration\ICalendarProvider;
  30. use OCA\DAV\CardDAV\Integration\IAddressBookProvider;
  31. use OCP\App\IAppManager;
  32. use OCP\AppFramework\QueryException;
  33. use Sabre\DAV\Collection;
  34. use Sabre\DAV\ServerPlugin;
  35. use function array_map;
  36. use function class_exists;
  37. use function is_array;
  38. /**
  39. * Manager for DAV plugins from apps, used to register them
  40. * to the Sabre server.
  41. */
  42. class PluginManager {
  43. /**
  44. * @var ServerContainer
  45. */
  46. private $container;
  47. /**
  48. * @var IAppManager
  49. */
  50. private $appManager;
  51. /**
  52. * App plugins
  53. *
  54. * @var ServerPlugin[]
  55. */
  56. private $plugins = [];
  57. /**
  58. * App collections
  59. *
  60. * @var Collection[]
  61. */
  62. private $collections = [];
  63. /**
  64. * Address book plugins
  65. *
  66. * @var IAddressBookProvider[]
  67. */
  68. private $addressBookPlugins = [];
  69. /**
  70. * Calendar plugins
  71. *
  72. * @var ICalendarProvider[]
  73. */
  74. private $calendarPlugins = [];
  75. /** @var bool */
  76. private $populated = false;
  77. /**
  78. * Contstruct a PluginManager
  79. *
  80. * @param ServerContainer $container server container for resolving plugin classes
  81. * @param IAppManager $appManager app manager to loading apps and their info
  82. */
  83. public function __construct(ServerContainer $container, IAppManager $appManager) {
  84. $this->container = $container;
  85. $this->appManager = $appManager;
  86. }
  87. /**
  88. * Returns an array of app-registered plugins
  89. *
  90. * @return ServerPlugin[]
  91. */
  92. public function getAppPlugins() {
  93. $this->populate();
  94. return $this->plugins;
  95. }
  96. /**
  97. * Returns an array of app-registered collections
  98. *
  99. * @return array
  100. */
  101. public function getAppCollections() {
  102. $this->populate();
  103. return $this->collections;
  104. }
  105. /**
  106. * @return IAddressBookProvider[]
  107. */
  108. public function getAddressBookPlugins(): array {
  109. $this->populate();
  110. return $this->addressBookPlugins;
  111. }
  112. /**
  113. * Returns an array of app-registered calendar plugins
  114. *
  115. * @return ICalendarProvider[]
  116. */
  117. public function getCalendarPlugins():array {
  118. $this->populate();
  119. return $this->calendarPlugins;
  120. }
  121. /**
  122. * Retrieve plugin and collection list and populate attributes
  123. */
  124. private function populate(): void {
  125. if ($this->populated) {
  126. return;
  127. }
  128. $this->populated = true;
  129. foreach ($this->appManager->getInstalledApps() as $app) {
  130. // load plugins and collections from info.xml
  131. $info = $this->appManager->getAppInfo($app);
  132. if (!isset($info['types']) || !in_array('dav', $info['types'], true)) {
  133. continue;
  134. }
  135. $plugins = $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info));
  136. foreach ($plugins as $plugin) {
  137. $this->plugins[] = $plugin;
  138. }
  139. $collections = $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info));
  140. foreach ($collections as $collection) {
  141. $this->collections[] = $collection;
  142. }
  143. $addresbookPlugins = $this->loadSabreAddressBookPluginsFromInfoXml($this->extractAddressBookPluginList($info));
  144. foreach ($addresbookPlugins as $addresbookPlugin) {
  145. $this->addressBookPlugins[] = $addresbookPlugin;
  146. }
  147. $calendarPlugins = $this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info));
  148. foreach ($calendarPlugins as $calendarPlugin) {
  149. $this->calendarPlugins[] = $calendarPlugin;
  150. }
  151. }
  152. }
  153. /**
  154. * @param array $array
  155. * @return string[]
  156. */
  157. private function extractPluginList(array $array): array {
  158. if (isset($array['sabre']) && is_array($array['sabre'])) {
  159. if (isset($array['sabre']['plugins']) && is_array($array['sabre']['plugins'])) {
  160. if (isset($array['sabre']['plugins']['plugin'])) {
  161. $items = $array['sabre']['plugins']['plugin'];
  162. if (!is_array($items)) {
  163. $items = [$items];
  164. }
  165. return $items;
  166. }
  167. }
  168. }
  169. return [];
  170. }
  171. /**
  172. * @param array $array
  173. * @return string[]
  174. */
  175. private function extractCollectionList(array $array): array {
  176. if (isset($array['sabre']) && is_array($array['sabre'])) {
  177. if (isset($array['sabre']['collections']) && is_array($array['sabre']['collections'])) {
  178. if (isset($array['sabre']['collections']['collection'])) {
  179. $items = $array['sabre']['collections']['collection'];
  180. if (!is_array($items)) {
  181. $items = [$items];
  182. }
  183. return $items;
  184. }
  185. }
  186. }
  187. return [];
  188. }
  189. /**
  190. * @param array $array
  191. * @return string[]
  192. */
  193. private function extractAddressBookPluginList(array $array): array {
  194. if (!isset($array['sabre']) || !is_array($array['sabre'])) {
  195. return [];
  196. }
  197. if (!isset($array['sabre']['address-book-plugins']) || !is_array($array['sabre']['address-book-plugins'])) {
  198. return [];
  199. }
  200. if (!isset($array['sabre']['address-book-plugins']['plugin'])) {
  201. return [];
  202. }
  203. $items = $array['sabre']['address-book-plugins']['plugin'];
  204. if (!is_array($items)) {
  205. $items = [$items];
  206. }
  207. return $items;
  208. }
  209. /**
  210. * @param array $array
  211. * @return string[]
  212. */
  213. private function extractCalendarPluginList(array $array): array {
  214. if (isset($array['sabre']) && is_array($array['sabre'])) {
  215. if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) {
  216. if (isset($array['sabre']['calendar-plugins']['plugin'])) {
  217. $items = $array['sabre']['calendar-plugins']['plugin'];
  218. if (!is_array($items)) {
  219. $items = [$items];
  220. }
  221. return $items;
  222. }
  223. }
  224. }
  225. return [];
  226. }
  227. private function createClass(string $className): object {
  228. try {
  229. return $this->container->query($className);
  230. } catch (QueryException $e) {
  231. if (class_exists($className)) {
  232. return new $className();
  233. }
  234. }
  235. throw new \Exception('Could not load ' . $className, 0, $e);
  236. }
  237. /**
  238. * @param string[] $classes
  239. * @return ServerPlugin[]
  240. * @throws \Exception
  241. */
  242. private function loadSabrePluginsFromInfoXml(array $classes): array {
  243. return array_map(function (string $className): ServerPlugin {
  244. $instance = $this->createClass($className);
  245. if (!($instance instanceof ServerPlugin)) {
  246. throw new \Exception('Sabre server plugin ' . $className . ' does not implement the ' . ServerPlugin::class . ' interface');
  247. }
  248. return $instance;
  249. }, $classes);
  250. }
  251. /**
  252. * @param string[] $classes
  253. * @return Collection[]
  254. */
  255. private function loadSabreCollectionsFromInfoXml(array $classes): array {
  256. return array_map(function (string $className): Collection {
  257. $instance = $this->createClass($className);
  258. if (!($instance instanceof Collection)) {
  259. throw new \Exception('Sabre collection plugin ' . $className . ' does not implement the ' . Collection::class . ' interface');
  260. }
  261. return $instance;
  262. }, $classes);
  263. }
  264. /**
  265. * @param string[] $classes
  266. * @return IAddressBookProvider[]
  267. */
  268. private function loadSabreAddressBookPluginsFromInfoXml(array $classes): array {
  269. return array_map(function (string $className): IAddressBookProvider {
  270. $instance = $this->createClass($className);
  271. if (!($instance instanceof IAddressBookProvider)) {
  272. throw new \Exception('Sabre address book plugin class ' . $className . ' does not implement the ' . IAddressBookProvider::class . ' interface');
  273. }
  274. return $instance;
  275. }, $classes);
  276. }
  277. /**
  278. * @param string[] $classes
  279. * @return ICalendarProvider[]
  280. */
  281. private function loadSabreCalendarPluginsFromInfoXml(array $classes): array {
  282. return array_map(function (string $className): ICalendarProvider {
  283. $instance = $this->createClass($className);
  284. if (!($instance instanceof ICalendarProvider)) {
  285. throw new \Exception('Sabre calendar plugin class ' . $className . ' does not implement the ' . ICalendarProvider::class . ' interface');
  286. }
  287. return $instance;
  288. }, $classes);
  289. }
  290. }