diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/private/appframework/app.php | 44 | ||||
-rw-r--r-- | lib/private/appframework/dependencyinjection/dicontainer.php | 177 | ||||
-rw-r--r-- | lib/private/appframework/utility/simplecontainer.php | 85 | ||||
-rw-r--r-- | lib/private/route/router.php | 38 | ||||
-rw-r--r-- | lib/public/appframework/app.php | 16 | ||||
-rw-r--r-- | lib/public/appframework/queryexception.php | 28 |
6 files changed, 374 insertions, 14 deletions
diff --git a/lib/private/appframework/app.php b/lib/private/appframework/app.php index f56ba4af870..b94c7bd9957 100644 --- a/lib/private/appframework/app.php +++ b/lib/private/appframework/app.php @@ -24,8 +24,9 @@ namespace OC\AppFramework; -use OC\AppFramework\DependencyInjection\DIContainer; - +use \OC_App; +use \OC\AppFramework\DependencyInjection\DIContainer; +use \OCP\AppFramework\QueryException; /** * Entry point for every request in your app. You can consider this as your @@ -37,6 +38,34 @@ class App { /** + * Turns an app id into a namespace by either reading the appinfo.xml's + * namespace tag or uppercasing the appid's first letter + * @param string $appId the app id + * @param string $topNamespace the namespace which should be prepended to + * the transformed app id, defaults to OCA\ + * @return string the starting namespace for the app + */ + public static function buildAppNamespace($appId, $topNamespace='OCA\\') { + // first try to parse the app's appinfo/info.xml <namespace> tag + $filePath = OC_App::getAppPath($appId) . '/appinfo/info.xml'; + $loadEntities = libxml_disable_entity_loader(false); + $xml = @simplexml_load_file($filePath); + libxml_disable_entity_loader($loadEntities); + + if ($xml) { + $result = $xml->xpath('/info/namespace'); + if ($result && count($result) > 0) { + // take first namespace result + return $topNamespace . trim((string) $result[0]); + } + } + + // if the tag is not found, fall back to uppercasing the first letter + return $topNamespace . ucfirst($appId); + } + + + /** * Shortcut for calling a controller method and printing the result * @param string $controllerName the name of the controller under which it is * stored in the DI container @@ -48,7 +77,16 @@ class App { if (!is_null($urlParams)) { $container['urlParams'] = $urlParams; } - $controller = $container[$controllerName]; + $appName = $container['AppName']; + + // first try $controllerName then go for \OCA\AppName\Controller\$controllerName + try { + $controller = $container->query($controllerName); + } catch(QueryException $e) { + $appNameSpace = self::buildAppNamespace($appName); + $controllerName = $appNameSpace . '\\Controller\\' . $controllerName; + $controller = $container->query($controllerName); + } // initialize the dispatcher and run all the middleware before the controller $dispatcher = $container['Dispatcher']; diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php index dc57ef4c167..2c5089865a3 100644 --- a/lib/private/appframework/dependencyinjection/dicontainer.php +++ b/lib/private/appframework/dependencyinjection/dicontainer.php @@ -36,12 +36,13 @@ use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; use OC\AppFramework\Utility\ControllerMethodReflector; use OCP\AppFramework\IApi; +use OCP\AppFramework\QueryException; use OCP\AppFramework\IAppContainer; use OCP\AppFramework\Middleware; use OCP\IServerContainer; -class DIContainer extends SimpleContainer implements IAppContainer{ +class DIContainer extends SimpleContainer implements IAppContainer { /** * @var array @@ -53,19 +54,181 @@ class DIContainer extends SimpleContainer implements IAppContainer{ * @param string $appName the name of the app */ public function __construct($appName, $urlParams = array()){ - $this['AppName'] = $appName; $this['urlParams'] = $urlParams; - $this->registerParameter('ServerContainer', \OC::$server); + /** + * Core services + */ + $this->registerService('OCP\\IAppConfig', function($c) { + return \OC::$server->getAppConfig(); + }); - $this->registerService('API', function($c){ - return new API($c['AppName']); + $this->registerService('OCP\\IAppManager', function($c) { + return \OC::$server->getAppManager(); + }); + + $this->registerService('OCP\\IAvatarManager', function($c) { + return \OC::$server->getAvatarManager(); + }); + + $this->registerService('OCP\\Activity\\IManager', function($c) { + return \OC::$server->getActivityManager(); + }); + + $this->registerService('OCP\\ICache', function($c) { + return \OC::$server->getCache(); + }); + + $this->registerService('OCP\\ICacheFactory', function($c) { + return \OC::$server->getMemCacheFactory(); + }); + + $this->registerService('OCP\\IConfig', function($c) { + return \OC::$server->getConfig(); + }); + + $this->registerService('OCP\\Contacts\\IManager', function($c) { + return \OC::$server->getContactsManager(); + }); + + $this->registerService('OCP\\IDateTimeZone', function($c) { + return \OC::$server->getDateTimeZone(); + }); + + $this->registerService('OCP\\IDb', function($c) { + return \OC::$server->getDb(); + }); + + $this->registerService('OCP\\IDBConnection', function($c) { + return \OC::$server->getDatabaseConnection(); + }); + + $this->registerService('OCP\\Diagnostics\\IEventLogger', function($c) { + return \OC::$server->getEventLogger(); + }); + + $this->registerService('OCP\\Diagnostics\\IQueryLogger', function($c) { + return \OC::$server->getQueryLogger(); + }); + + $this->registerService('OCP\\Files\\Config\\IMountProviderCollection', function($c) { + return \OC::$server->getMountProviderCollection(); + }); + + $this->registerService('OCP\\Files\\IRootFolder', function($c) { + return \OC::$server->getRootFolder(); + }); + + $this->registerService('OCP\\IGroupManager', function($c) { + return \OC::$server->getGroupManager(); + }); + + $this->registerService('OCP\\IL10N', function($c) { + return \OC::$server->getL10N($c->query('AppName')); + }); + + $this->registerService('OCP\\ILogger', function($c) { + return \OC::$server->getLogger(); + }); + + $this->registerService('OCP\\BackgroundJob\\IJobList', function($c) { + return \OC::$server->getJobList(); + }); + + $this->registerService('OCP\\AppFramework\\Utility\\IControllerMethodReflector', function($c) { + return $c->query('ControllerMethodReflector'); + }); + + $this->registerService('OCP\\INavigationManager', function($c) { + return \OC::$server->getNavigationManager(); }); + $this->registerService('OCP\\IPreview', function($c) { + return \OC::$server->getPreviewManager(); + }); + + $this->registerService('OCP\\IRequest', function($c) { + return $c->query('Request'); + }); + + $this->registerService('OCP\\ITagManager', function($c) { + return \OC::$server->getTagManager(); + }); + + $this->registerService('OCP\\ITempManager', function($c) { + return \OC::$server->getTempManager(); + }); + + $this->registerService('OCP\\AppFramework\\Utility\\ITimeFactory', function($c) { + return $c->query('TimeFactory'); + }); + + $this->registerService('OCP\\Route\\IRouter', function($c) { + return \OC::$server->getRouter(); + }); + + $this->registerService('OCP\\ISearch', function($c) { + return \OC::$server->getSearch(); + }); + + $this->registerService('OCP\\ISearch', function($c) { + return \OC::$server->getSearch(); + }); + + $this->registerService('OCP\\Security\\ICrypto', function($c) { + return \OC::$server->getCrypto(); + }); + + $this->registerService('OCP\\Security\\IHasher', function($c) { + return \OC::$server->getHasher(); + }); + + $this->registerService('OCP\\Security\\ISecureRandom', function($c) { + return \OC::$server->getSecureRandom(); + }); + + $this->registerService('OCP\\IURLGenerator', function($c) { + return \OC::$server->getURLGenerator(); + }); + + $this->registerService('OCP\\IUserManager', function($c) { + return \OC::$server->getUserManager(); + }); + + $this->registerService('OCP\\IUserSession', function($c) { + return \OC::$server->getUserSession(); + }); + + $this->registerService('ServerContainer', function ($c) { + $c->query('OCP\\ILogger')->warning( + 'Accessing the server container is deprecated. Use type ' . + 'annotations to inject core services instead!' + ); + return \OC::$server; + }); + + // commonly used attributes + $this->registerService('UserId', function ($c) { + return $c->query('OCP\\IUserSession')->getSession()->get('user_id'); + }); + + $this->registerService('WebRoot', function ($c) { + return $c->query('ServerContainer')->getWebRoot(); + }); + + /** - * Http + * App Framework APIs */ + $this->registerService('API', function($c){ + $c->query('OCP\\ILogger')->warning( + 'Accessing the API class is deprecated! Use the appropriate ' . + 'services instead!' + ); + return new API($c['AppName']); + }); + $this->registerService('Request', function($c) { /** @var $c SimpleContainer */ /** @var $server SimpleContainer */ @@ -234,4 +397,6 @@ class DIContainer extends SimpleContainer implements IAppContainer{ } \OCP\Util::writeLog($this->getAppName(), $message, $level); } + + } diff --git a/lib/private/appframework/utility/simplecontainer.php b/lib/private/appframework/utility/simplecontainer.php index 55b9cf7a977..68d52d759e0 100644 --- a/lib/private/appframework/utility/simplecontainer.php +++ b/lib/private/appframework/utility/simplecontainer.php @@ -1,7 +1,29 @@ <?php +/** + * ownCloud - App Framework + * + * @author Bernhard Posselt + * @copyright 2014 Bernhard Posselt <dev@bernhard-posselt.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ namespace OC\AppFramework\Utility; +use \OCP\AppFramework\QueryException; + /** * Class SimpleContainer * @@ -9,12 +31,71 @@ namespace OC\AppFramework\Utility; */ class SimpleContainer extends \Pimple\Container implements \OCP\IContainer { + + /** + * @param ReflectionClass $class the class to instantiate + * @return stdClass the created class + */ + private function buildClass(\ReflectionClass $class) { + $constructor = $class->getConstructor(); + if ($constructor === null) { + return $class->newInstance(); + } else { + $parameters = []; + foreach ($constructor->getParameters() as $parameter) { + $parameterClass = $parameter->getClass(); + + // try to find out if it is a class or a simple parameter + if ($parameterClass === null) { + $resolveName = $parameter->getName(); + } else { + $resolveName = $parameterClass->name; + } + + $parameters[] = $this->query($resolveName); + } + return $class->newInstanceArgs($parameters); + } + } + + + /** + * If a parameter is not registered in the container try to instantiate it + * by using reflection to find out how to build the class + * @param string $name the class name to resolve + * @throws QueryException if the class could not be found or instantiated + */ + private function resolve($name) { + $baseMsg = 'Could not resolve ' . $name . '!'; + try { + $class = new \ReflectionClass($name); + if ($class->isInstantiable()) { + return $this->buildClass($class); + } else { + throw new QueryException($baseMsg . + ' Class can not be instantiated'); + } + } catch(\ReflectionException $e) { + throw new QueryException($baseMsg . ' ' . $e->getMessage()); + } + } + + /** * @param string $name name of the service to query for * @return mixed registered service for the given $name + * @throws QueryExcpetion if the query could not be resolved */ public function query($name) { - return $this->offsetGet($name); + if ($this->offsetExists($name)) { + return $this->offsetGet($name); + } else { + $object = $this->resolve($name); + $this->registerService($name, function () use ($object) { + return $object; + }); + return $object; + } } /** @@ -44,4 +125,6 @@ class SimpleContainer extends \Pimple\Container implements \OCP\IContainer { $this[$name] = parent::factory($closure); } } + + } diff --git a/lib/private/route/router.php b/lib/private/route/router.php index 5d6f621dc38..3559b841926 100644 --- a/lib/private/route/router.php +++ b/lib/private/route/router.php @@ -9,6 +9,7 @@ namespace OC\Route; use OCP\Route\IRouter; +use OCP\AppFramework\App; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\RequestContext; @@ -129,7 +130,7 @@ class Router implements IRouter { if (!isset($this->loadedApps[$app])) { $this->loadedApps[$app] = true; $this->useCollection($app); - $this->requireRouteFile($file); + $this->requireRouteFile($file, $app); $collection = $this->getCollection($app); $collection->addPrefix('/apps/' . $app); $this->root->addCollection($collection); @@ -283,10 +284,39 @@ class Router implements IRouter { /** * To isolate the variable scope used inside the $file it is required in it's own method - * @param string $file + * @param string $file the route file location to include + * @param string $appName */ - private function requireRouteFile($file) { - require_once $file; + private function requireRouteFile($file, $appName) { + $this->setupRoutes(include_once $file, $appName); } + + /** + * If a routes.php file returns an array, try to set up the application and + * register the routes for the app. The application class will be chosen by + * camelcasing the appname, e.g.: my_app will be turned into + * \OCA\MyApp\AppInfo\Application. If that class does not exist, a default + * App will be intialized. This makes it optional to ship an + * appinfo/application.php by using the built in query resolver + * @param array $routes the application routes + * @param string $appName the name of the app. + */ + private function setupRoutes($routes, $appName) { + if (is_array($routes)) { + $appNameSpace = App::buildAppNamespace($appName); + + $applicationClassName = $appNameSpace . '\\AppInfo\\Application'; + + if (class_exists($applicationClassName)) { + $application = new $applicationClassName(); + } else { + $application = new App($appName); + } + + $application->registerRoutes($this, $routes); + } + } + + } diff --git a/lib/public/appframework/app.php b/lib/public/appframework/app.php index 21612327879..da405262aef 100644 --- a/lib/public/appframework/app.php +++ b/lib/public/appframework/app.php @@ -37,6 +37,22 @@ use OC\AppFramework\routing\RouteConfig; * to be registered using IContainer::registerService */ class App { + + + /** + * Turns an app id into a namespace by convetion. The id is split at the + * underscores, all parts are camelcased and reassembled. e.g.: + * some_app_id -> OCA\SomeAppId + * @param string $appId the app id + * @param string $topNamespace the namespace which should be prepended to + * the transformed app id, defaults to OCA\ + * @return string the starting namespace for the app + */ + public static function buildAppNamespace($appId, $topNamespace='OCA\\') { + return \OC\AppFramework\App::buildAppNamespace($appId, $topNamespace); + } + + /** * @param array $urlParams an array with variables extracted from the routes */ diff --git a/lib/public/appframework/queryexception.php b/lib/public/appframework/queryexception.php new file mode 100644 index 00000000000..f08d5b9a12f --- /dev/null +++ b/lib/public/appframework/queryexception.php @@ -0,0 +1,28 @@ +<?php +/** + * ownCloud - App Framework + * + * @author Bernhard Posselt + * @copyright 2014 Bernhard Posselt <dev@bernhard-posselt.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\AppFramework; + +use Exception; + + +class QueryException extends Exception {} |