From 07d43a539477c8c767090fbc10e9f94220b4b87a Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 2 Dec 2016 22:27:15 +0100 Subject: [PATCH] Let apps register Sabre plugins or collections --- apps/comments/appinfo/info.xml | 1 + apps/dav/appinfo/v2/remote.php | 1 + apps/dav/lib/AppInfo/PluginManager.php | 169 ++++++++++++++++++ apps/dav/lib/Server.php | 15 +- .../tests/unit/AppInfo/PluginManagerTest.php | 104 +++++++++++ apps/dav/tests/unit/ServerTest.php | 4 +- 6 files changed, 291 insertions(+), 3 deletions(-) create mode 100644 apps/dav/lib/AppInfo/PluginManager.php create mode 100644 apps/dav/tests/unit/AppInfo/PluginManagerTest.php diff --git a/apps/comments/appinfo/info.xml b/apps/comments/appinfo/info.xml index 8cd9c0a4af16..022b90fddbb1 100644 --- a/apps/comments/appinfo/info.xml +++ b/apps/comments/appinfo/info.xml @@ -12,5 +12,6 @@ + diff --git a/apps/dav/appinfo/v2/remote.php b/apps/dav/appinfo/v2/remote.php index dd5929e88bbc..5f1f0d03e4f7 100644 --- a/apps/dav/appinfo/v2/remote.php +++ b/apps/dav/appinfo/v2/remote.php @@ -1,6 +1,7 @@ + * @author Vincent Petry * * @copyright Copyright (c) 2016, ownCloud GmbH. * @license AGPL-3.0 diff --git a/apps/dav/lib/AppInfo/PluginManager.php b/apps/dav/lib/AppInfo/PluginManager.php new file mode 100644 index 000000000000..71c2e88ae11f --- /dev/null +++ b/apps/dav/lib/AppInfo/PluginManager.php @@ -0,0 +1,169 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ +namespace OCA\DAV\AppInfo; + +use OCP\App\IAppManager; +use OC\ServerContainer; + +/** + * Manager for DAV plugins from apps, used to register them + * to the Sabre server. + */ +class PluginManager { + + /** + * @var ServerContainer + */ + private $container; + + /** + * @var IAppManager + */ + private $appManager; + + /** + * App plugins + * + * @var array + */ + private $plugins = null; + + /** + * App collections + * + * @var array + */ + private $collections = null; + + /** + * Contstruct a PluginManager + * + * @param ServerContainer $container server container for resolving plugin classes + * @param IAppManager $appManager app manager to loading apps and their info + */ + public function __construct(ServerContainer $container, IAppManager $appManager) { + $this->container = $container; + $this->appManager = $appManager; + } + + /** + * Returns an array of app-registered plugins + * + * @return array + */ + public function getAppPlugins() { + if (is_null($this->plugins)) { + $this->populate(); + } + return $this->plugins; + } + + /** + * Returns an array of app-registered collections + * + * @return array + */ + public function getAppCollections() { + if (is_null($this->collections)) { + $this->populate(); + } + return $this->collections; + } + + /** + * Retrieve plugin and collection list and populate attributes + */ + private function populate() { + $this->plugins = []; + $this->collections = []; + foreach ($this->appManager->getInstalledApps() as $app) { + // load plugins and collections from info.xml + $info = $this->appManager->getAppInfo($app); + if (!isset($info['types']) || !in_array('dav', $info['types'])) { + continue; + } + // FIXME: switch to public API once available + // load app to make sure its classes are available + \OC_App::loadApp($app); + $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info)); + $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info)); + } + } + + private function extractPluginList($array) { + if (isset($array['sabre']) && is_array($array['sabre'])) { + if (isset($array['sabre']['plugins']) && is_array($array['sabre']['plugins'])) { + if (isset($array['sabre']['plugins']['plugin'])) { + $items = $array['sabre']['plugins']['plugin']; + if (!is_array($items)) { + $items = [$items]; + } + return $items; + } + } + } + return []; + } + + private function extractCollectionList($array) { + if (isset($array['sabre']) && is_array($array['sabre'])) { + if (isset($array['sabre']['collections']) && is_array($array['sabre']['collections'])) { + if (isset($array['sabre']['collections']['collection'])) { + $items = $array['sabre']['collections']['collection']; + if (!is_array($items)) { + $items = [$items]; + } + return $items; + } + } + } + return []; + } + + private function loadSabrePluginsFromInfoXml($plugins) { + foreach ($plugins as $plugin) { + try { + $this->plugins[] = $this->container->query($plugin); + } catch (QueryException $e) { + if (class_exists($plugin)) { + $this->plugins[] = new $plugin(); + } else { + throw new \Exception("Sabre plugin class '$plugin' is unknown and could not be loaded"); + } + } + } + } + + private function loadSabreCollectionsFromInfoXml($collections) { + foreach ($collections as $collection) { + try { + $this->collections[] = $this->container->query($collection); + } catch (QueryException $e) { + if (class_exists($collection)) { + $this->collections[] = new $collection(); + } else { + throw new \Exception("Sabre collection class '$collection' is unknown and could not be loaded"); + } + } + }; + } + +} diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 95f9dc889307..1497957770f7 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -49,6 +49,7 @@ use Sabre\CardDAV\VCFExportPlugin; use Sabre\DAV\Auth\Plugin; use OCA\DAV\Connector\Sabre\TagsPlugin; +use OCA\DAV\AppInfo\PluginManager; class Server { @@ -155,7 +156,7 @@ public function __construct(IRequest $request, $baseUri) { } // wait with registering these until auth is handled and the filesystem is setup - $this->server->on('beforeMethod', function () { + $this->server->on('beforeMethod', function () use ($root) { // custom properties plugin must be the last one $userSession = \OC::$server->getUserSession(); $user = $userSession->getUser(); @@ -214,6 +215,18 @@ public function __construct(IRequest $request, $baseUri) { )); } } + + // register plugins from apps + $pluginManager = new PluginManager( + \OC::$server, + \OC::$server->getAppManager() + ); + foreach ($pluginManager->getAppPlugins() as $appPlugin) { + $this->server->addPlugin($appPlugin); + } + foreach ($pluginManager->getAppCollections() as $appCollection) { + $root->addChild($appCollection); + } }); } diff --git a/apps/dav/tests/unit/AppInfo/PluginManagerTest.php b/apps/dav/tests/unit/AppInfo/PluginManagerTest.php new file mode 100644 index 000000000000..5776b9391237 --- /dev/null +++ b/apps/dav/tests/unit/AppInfo/PluginManagerTest.php @@ -0,0 +1,104 @@ + + * + * @copyright Copyright (c) 2016, ownCloud GmbH. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\DAV\Tests\unit\AppInfo; + +use Test\TestCase; +use OCP\App\IAppManager; +use OC\ServerContainer; +use OCA\DAV\AppInfo\PluginManager; + +/** + * Class PluginManagerTest + * + * @package OCA\DAV\Tests\Unit\AppInfo + */ +class PluginManagerTest extends TestCase { + public function test() { + $server = $this->createMock(ServerContainer::class); + + + $appManager = $this->createMock(IAppManager::class); + $appManager->method('getInstalledApps') + ->willReturn(['adavapp', 'adavapp2']); + + $appInfo1 = [ + 'types' => ['dav'], + 'sabre' => [ + 'plugins' => [ + 'plugin' => [ + '\OCA\DAV\ADavApp\PluginOne', + '\OCA\DAV\ADavApp\PluginTwo', + ], + ], + 'collections' => [ + 'collection' => [ + '\OCA\DAV\ADavApp\CollectionOne', + '\OCA\DAV\ADavApp\CollectionTwo', + ] + ], + ], + ]; + $appInfo2 = [ + 'types' => ['logging', 'dav'], + 'sabre' => [ + 'plugins' => [ + 'plugin' => '\OCA\DAV\ADavApp2\PluginOne', + ], + 'collections' => [ + 'collection' => '\OCA\DAV\ADavApp2\CollectionOne', + ], + ], + ]; + + $appManager->method('getAppInfo') + ->will($this->returnValueMap([ + ['adavapp', $appInfo1], + ['adavapp2', $appInfo2], + ])); + + $pluginManager = new PluginManager($server, $appManager); + + $server->method('query') + ->will($this->returnValueMap([ + ['\OCA\DAV\ADavApp\PluginOne', 'dummyplugin1'], + ['\OCA\DAV\ADavApp\PluginTwo', 'dummyplugin2'], + ['\OCA\DAV\ADavApp\CollectionOne', 'dummycollection1'], + ['\OCA\DAV\ADavApp\CollectionTwo', 'dummycollection2'], + ['\OCA\DAV\ADavApp2\PluginOne', 'dummy2plugin1'], + ['\OCA\DAV\ADavApp2\CollectionOne', 'dummy2collection1'], + ])); + + $expectedPlugins = [ + 'dummyplugin1', + 'dummyplugin2', + 'dummy2plugin1', + ]; + $expectedCollections = [ + 'dummycollection1', + 'dummycollection2', + 'dummy2collection1', + ]; + + $this->assertEquals($expectedPlugins, $pluginManager->getAppPlugins()); + $this->assertEquals($expectedCollections, $pluginManager->getAppCollections()); + } +} diff --git a/apps/dav/tests/unit/ServerTest.php b/apps/dav/tests/unit/ServerTest.php index aac075275acc..248281198d86 100644 --- a/apps/dav/tests/unit/ServerTest.php +++ b/apps/dav/tests/unit/ServerTest.php @@ -24,6 +24,7 @@ use OCA\DAV\Server; use OCP\IRequest; +use OCA\DAV\AppInfo\PluginManager; /** * Class ServerTest @@ -36,8 +37,7 @@ class ServerTest extends \Test\TestCase { public function test() { /** @var IRequest $r */ - $r = $this->getMockBuilder('\OCP\IRequest') - ->disableOriginalConstructor()->getMock(); + $r = $this->createMock(IRequest::class); $s = new Server($r, '/'); $this->assertNotNull($s->server); }