From 33bb2238aeb0fddd3ddd3fe18307c5e7548e00bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 16 Aug 2013 08:06:25 +0200 Subject: [PATCH 001/180] updating 3rdparty repo commit --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 2f3ae9f56a..8d68fa1eab 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 2f3ae9f56a9838b45254393e13c14f8a8c380d6b +Subproject commit 8d68fa1eabe8c1d033cb89676b31f0eaaf99335b From fde9cabe9774b67e88ee8aa8fa39fe044fe2da2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 17 Aug 2013 11:16:48 +0200 Subject: [PATCH 002/180] initial import of appframework --- lib/appframework/app.php | 97 ++++ lib/appframework/controller/controller.php | 154 +++++ lib/appframework/core/api.php | 524 ++++++++++++++++++ .../dependencyinjection/dicontainer.php | 125 +++++ lib/appframework/http/dispatcher.php | 98 ++++ lib/appframework/http/downloadresponse.php | 51 ++ lib/appframework/http/http.php | 208 +++++++ lib/appframework/http/jsonresponse.php | 74 +++ lib/appframework/http/redirectresponse.php | 54 ++ lib/appframework/http/request.php | 217 ++++++++ lib/appframework/http/response.php | 169 ++++++ lib/appframework/http/templateresponse.php | 126 +++++ lib/appframework/middleware/middleware.php | 100 ++++ .../middleware/middlewaredispatcher.php | 159 ++++++ .../middleware/security/securityexception.php | 41 ++ .../security/securitymiddleware.php | 141 +++++ .../routing/routeactionhandler.php | 42 ++ lib/appframework/routing/routeconfig.php | 186 +++++++ .../utility/methodannotationreader.php | 61 ++ lib/appframework/utility/timefactory.php | 42 ++ tests/lib/appframework/AppTest.php | 107 ++++ tests/lib/appframework/classloader.php | 45 ++ .../controller/ControllerTest.php | 161 ++++++ .../dependencyinjection/DIContainerTest.php | 98 ++++ .../lib/appframework/http/DispatcherTest.php | 218 ++++++++ .../http/DownloadResponseTest.php | 51 ++ tests/lib/appframework/http/HttpTest.php | 87 +++ .../appframework/http/JSONResponseTest.php | 96 ++++ .../http/RedirectResponseTest.php | 55 ++ tests/lib/appframework/http/RequestTest.php | 78 +++ tests/lib/appframework/http/ResponseTest.php | 119 ++++ .../http/TemplateResponseTest.php | 157 ++++++ .../middleware/MiddlewareDispatcherTest.php | 280 ++++++++++ .../middleware/MiddlewareTest.php | 82 +++ .../security/SecurityMiddlewareTest.php | 388 +++++++++++++ .../lib/appframework/routing/RoutingTest.php | 214 +++++++ .../utility/MethodAnnotationReaderTest.php | 58 ++ 37 files changed, 4963 insertions(+) create mode 100644 lib/appframework/app.php create mode 100644 lib/appframework/controller/controller.php create mode 100644 lib/appframework/core/api.php create mode 100644 lib/appframework/dependencyinjection/dicontainer.php create mode 100644 lib/appframework/http/dispatcher.php create mode 100644 lib/appframework/http/downloadresponse.php create mode 100644 lib/appframework/http/http.php create mode 100644 lib/appframework/http/jsonresponse.php create mode 100644 lib/appframework/http/redirectresponse.php create mode 100644 lib/appframework/http/request.php create mode 100644 lib/appframework/http/response.php create mode 100644 lib/appframework/http/templateresponse.php create mode 100644 lib/appframework/middleware/middleware.php create mode 100644 lib/appframework/middleware/middlewaredispatcher.php create mode 100644 lib/appframework/middleware/security/securityexception.php create mode 100644 lib/appframework/middleware/security/securitymiddleware.php create mode 100644 lib/appframework/routing/routeactionhandler.php create mode 100644 lib/appframework/routing/routeconfig.php create mode 100644 lib/appframework/utility/methodannotationreader.php create mode 100644 lib/appframework/utility/timefactory.php create mode 100644 tests/lib/appframework/AppTest.php create mode 100644 tests/lib/appframework/classloader.php create mode 100644 tests/lib/appframework/controller/ControllerTest.php create mode 100644 tests/lib/appframework/dependencyinjection/DIContainerTest.php create mode 100644 tests/lib/appframework/http/DispatcherTest.php create mode 100644 tests/lib/appframework/http/DownloadResponseTest.php create mode 100644 tests/lib/appframework/http/HttpTest.php create mode 100644 tests/lib/appframework/http/JSONResponseTest.php create mode 100644 tests/lib/appframework/http/RedirectResponseTest.php create mode 100644 tests/lib/appframework/http/RequestTest.php create mode 100644 tests/lib/appframework/http/ResponseTest.php create mode 100644 tests/lib/appframework/http/TemplateResponseTest.php create mode 100644 tests/lib/appframework/middleware/MiddlewareDispatcherTest.php create mode 100644 tests/lib/appframework/middleware/MiddlewareTest.php create mode 100644 tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php create mode 100644 tests/lib/appframework/routing/RoutingTest.php create mode 100644 tests/lib/appframework/utility/MethodAnnotationReaderTest.php diff --git a/lib/appframework/app.php b/lib/appframework/app.php new file mode 100644 index 0000000000..6224b858bb --- /dev/null +++ b/lib/appframework/app.php @@ -0,0 +1,97 @@ +. + * + */ + + +namespace OC\AppFramework; + +use OC\AppFramework\DependencyInjection\DIContainer; + + +/** + * Entry point for every request in your app. You can consider this as your + * public static void main() method + * + * Handles all the dependency injection, controllers and output flow + */ +class App { + + + /** + * 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 + * @param string $methodName the method that you want to call + * @param array $urlParams an array with variables extracted from the routes + * @param DIContainer $container an instance of a pimple container. + */ + public static function main($controllerName, $methodName, array $urlParams, + DIContainer $container) { + $container['urlParams'] = $urlParams; + $controller = $container[$controllerName]; + + // initialize the dispatcher and run all the middleware before the controller + $dispatcher = $container['Dispatcher']; + + list($httpHeaders, $responseHeaders, $output) = + $dispatcher->dispatch($controller, $methodName); + + if(!is_null($httpHeaders)) { + header($httpHeaders); + } + + foreach($responseHeaders as $name => $value) { + header($name . ': ' . $value); + } + + if(!is_null($output)) { + header('Content-Length: ' . strlen($output)); + print($output); + } + + } + + /** + * Shortcut for calling a controller method and printing the result. + * Similar to App:main except that no headers will be sent. + * This should be used for example when registering sections via + * \OC\AppFramework\Core\API::registerAdmin() + * + * @param string $controllerName the name of the controller under which it is + * stored in the DI container + * @param string $methodName the method that you want to call + * @param array $urlParams an array with variables extracted from the routes + * @param DIContainer $container an instance of a pimple container. + */ + public static function part($controllerName, $methodName, array $urlParams, + DIContainer $container){ + + $container['urlParams'] = $urlParams; + $controller = $container[$controllerName]; + + $dispatcher = $container['Dispatcher']; + + list(, , $output) = $dispatcher->dispatch($controller, $methodName); + return $output; + } + +} diff --git a/lib/appframework/controller/controller.php b/lib/appframework/controller/controller.php new file mode 100644 index 0000000000..3e8166050d --- /dev/null +++ b/lib/appframework/controller/controller.php @@ -0,0 +1,154 @@ +. + * + */ + + +namespace OC\AppFramework\Controller; + +use OC\AppFramework\Http\TemplateResponse; +use OC\AppFramework\Http\Request; +use OC\AppFramework\Core\API; + + +/** + * Base class to inherit your controllers from + */ +abstract class Controller { + + /** + * @var API instance of the api layer + */ + protected $api; + + protected $request; + + /** + * @param API $api an api wrapper instance + * @param Request $request an instance of the request + */ + public function __construct(API $api, Request $request){ + $this->api = $api; + $this->request = $request; + } + + + /** + * Lets you access post and get parameters by the index + * @param string $key the key which you want to access in the URL Parameter + * placeholder, $_POST or $_GET array. + * The priority how they're returned is the following: + * 1. URL parameters + * 2. POST parameters + * 3. GET parameters + * @param mixed $default If the key is not found, this value will be returned + * @return mixed the content of the array + */ + public function params($key, $default=null){ + return isset($this->request->parameters[$key]) + ? $this->request->parameters[$key] + : $default; + } + + + /** + * Returns all params that were received, be it from the request + * (as GET or POST) or throuh the URL by the route + * @return array the array with all parameters + */ + public function getParams() { + return $this->request->parameters; + } + + + /** + * Returns the method of the request + * @return string the method of the request (POST, GET, etc) + */ + public function method() { + return $this->request->method; + } + + + /** + * Shortcut for accessing an uploaded file through the $_FILES array + * @param string $key the key that will be taken from the $_FILES array + * @return array the file in the $_FILES element + */ + public function getUploadedFile($key) { + return isset($this->request->files[$key]) ? $this->request->files[$key] : null; + } + + + /** + * Shortcut for getting env variables + * @param string $key the key that will be taken from the $_ENV array + * @return array the value in the $_ENV element + */ + public function env($key) { + return isset($this->request->env[$key]) ? $this->request->env[$key] : null; + } + + + /** + * Shortcut for getting session variables + * @param string $key the key that will be taken from the $_SESSION array + * @return array the value in the $_SESSION element + */ + public function session($key) { + return isset($this->request->session[$key]) ? $this->request->session[$key] : null; + } + + + /** + * Shortcut for getting cookie variables + * @param string $key the key that will be taken from the $_COOKIE array + * @return array the value in the $_COOKIE element + */ + public function cookie($key) { + return isset($this->request->cookies[$key]) ? $this->request->cookies[$key] : null; + } + + + /** + * Shortcut for rendering a template + * @param string $templateName the name of the template + * @param array $params the template parameters in key => value structure + * @param string $renderAs user renders a full page, blank only your template + * admin an entry in the admin settings + * @param array $headers set additional headers in name/value pairs + * @return \OC\AppFramework\Http\TemplateResponse containing the page + */ + public function render($templateName, array $params=array(), + $renderAs='user', array $headers=array()){ + $response = new TemplateResponse($this->api, $templateName); + $response->setParams($params); + $response->renderAs($renderAs); + + foreach($headers as $name => $value){ + $response->addHeader($name, $value); + } + + return $response; + } + + +} diff --git a/lib/appframework/core/api.php b/lib/appframework/core/api.php new file mode 100644 index 0000000000..eb8ee01e5d --- /dev/null +++ b/lib/appframework/core/api.php @@ -0,0 +1,524 @@ +. + * + */ + + +namespace OC\AppFramework\Core; + + +/** + * This is used to wrap the owncloud static api calls into an object to make the + * code better abstractable for use in the dependency injection container + * + * Should you find yourself in need for more methods, simply inherit from this + * class and add your methods + */ +class API { + + private $appName; + + /** + * constructor + * @param string $appName the name of your application + */ + public function __construct($appName){ + $this->appName = $appName; + } + + + /** + * used to return the appname of the set application + * @return string the name of your application + */ + public function getAppName(){ + return $this->appName; + } + + + /** + * Creates a new navigation entry + * @param array $entry containing: id, name, order, icon and href key + */ + public function addNavigationEntry(array $entry){ + \OCP\App::addNavigationEntry($entry); + } + + + /** + * Gets the userid of the current user + * @return string the user id of the current user + */ + public function getUserId(){ + return \OCP\User::getUser(); + } + + + /** + * Sets the current navigation entry to the currently running app + */ + public function activateNavigationEntry(){ + \OCP\App::setActiveNavigationEntry($this->appName); + } + + + /** + * Adds a new javascript file + * @param string $scriptName the name of the javascript in js/ without the suffix + * @param string $appName the name of the app, defaults to the current one + */ + public function addScript($scriptName, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + \OCP\Util::addScript($appName, $scriptName); + } + + + /** + * Adds a new css file + * @param string $styleName the name of the css file in css/without the suffix + * @param string $appName the name of the app, defaults to the current one + */ + public function addStyle($styleName, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + \OCP\Util::addStyle($appName, $styleName); + } + + + /** + * shorthand for addScript for files in the 3rdparty directory + * @param string $name the name of the file without the suffix + */ + public function add3rdPartyScript($name){ + \OCP\Util::addScript($this->appName . '/3rdparty', $name); + } + + + /** + * shorthand for addStyle for files in the 3rdparty directory + * @param string $name the name of the file without the suffix + */ + public function add3rdPartyStyle($name){ + \OCP\Util::addStyle($this->appName . '/3rdparty', $name); + } + + /** + * Looks up a systemwide defined value + * @param string $key the key of the value, under which it was saved + * @return string the saved value + */ + public function getSystemValue($key){ + return \OCP\Config::getSystemValue($key, ''); + } + + + /** + * Sets a new systemwide value + * @param string $key the key of the value, under which will be saved + * @param string $value the value that should be stored + */ + public function setSystemValue($key, $value){ + return \OCP\Config::setSystemValue($key, $value); + } + + + /** + * Looks up an appwide defined value + * @param string $key the key of the value, under which it was saved + * @return string the saved value + */ + public function getAppValue($key, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + return \OCP\Config::getAppValue($appName, $key, ''); + } + + + /** + * Writes a new appwide value + * @param string $key the key of the value, under which will be saved + * @param string $value the value that should be stored + */ + public function setAppValue($key, $value, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + return \OCP\Config::setAppValue($appName, $key, $value); + } + + + + /** + * Shortcut for setting a user defined value + * @param string $key the key under which the value is being stored + * @param string $value the value that you want to store + * @param string $userId the userId of the user that we want to store the value under, defaults to the current one + */ + public function setUserValue($key, $value, $userId=null){ + if($userId === null){ + $userId = $this->getUserId(); + } + \OCP\Config::setUserValue($userId, $this->appName, $key, $value); + } + + + /** + * Shortcut for getting a user defined value + * @param string $key the key under which the value is being stored + * @param string $userId the userId of the user that we want to store the value under, defaults to the current one + */ + public function getUserValue($key, $userId=null){ + if($userId === null){ + $userId = $this->getUserId(); + } + return \OCP\Config::getUserValue($userId, $this->appName, $key); + } + + + /** + * Returns the translation object + * @return \OC_L10N the translation object + */ + public function getTrans(){ + # TODO: use public api + return \OC_L10N::get($this->appName); + } + + + /** + * Used to abstract the owncloud database access away + * @param string $sql the sql query with ? placeholder for params + * @param int $limit the maximum number of rows + * @param int $offset from which row we want to start + * @return \OCP\DB a query object + */ + public function prepareQuery($sql, $limit=null, $offset=null){ + return \OCP\DB::prepare($sql, $limit, $offset); + } + + + /** + * Used to get the id of the just inserted element + * @param string $tableName the name of the table where we inserted the item + * @return int the id of the inserted element + */ + public function getInsertId($tableName){ + return \OCP\DB::insertid($tableName); + } + + + /** + * Returns the URL for a route + * @param string $routeName the name of the route + * @param array $arguments an array with arguments which will be filled into the url + * @return string the url + */ + public function linkToRoute($routeName, $arguments=array()){ + return \OCP\Util::linkToRoute($routeName, $arguments); + } + + + /** + * Returns an URL for an image or file + * @param string $file the name of the file + * @param string $appName the name of the app, defaults to the current one + */ + public function linkTo($file, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + return \OCP\Util::linkTo($appName, $file); + } + + + /** + * Returns the link to an image, like link to but only with prepending img/ + * @param string $file the name of the file + * @param string $appName the name of the app, defaults to the current one + */ + public function imagePath($file, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + return \OCP\Util::imagePath($appName, $file); + } + + + /** + * Makes an URL absolute + * @param string $url the url + * @return string the absolute url + */ + public function getAbsoluteURL($url){ + # TODO: use public api + return \OC_Helper::makeURLAbsolute($url); + } + + + /** + * links to a file + * @param string $file the name of the file + * @param string $appName the name of the app, defaults to the current one + * @deprecated replaced with linkToRoute() + * @return string the url + */ + public function linkToAbsolute($file, $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + return \OCP\Util::linkToAbsolute($appName, $file); + } + + + /** + * Checks if the current user is logged in + * @return bool true if logged in + */ + public function isLoggedIn(){ + return \OCP\User::isLoggedIn(); + } + + + /** + * Checks if a user is an admin + * @param string $userId the id of the user + * @return bool true if admin + */ + public function isAdminUser($userId){ + # TODO: use public api + return \OC_User::isAdminUser($userId); + } + + + /** + * Checks if a user is an subadmin + * @param string $userId the id of the user + * @return bool true if subadmin + */ + public function isSubAdminUser($userId){ + # TODO: use public api + return \OC_SubAdmin::isSubAdmin($userId); + } + + + /** + * Checks if the CSRF check was correct + * @return bool true if CSRF check passed + */ + public function passesCSRFCheck(){ + # TODO: use public api + return \OC_Util::isCallRegistered(); + } + + + /** + * Checks if an app is enabled + * @param string $appName the name of an app + * @return bool true if app is enabled + */ + public function isAppEnabled($appName){ + return \OCP\App::isEnabled($appName); + } + + + /** + * Writes a function into the error log + * @param string $msg the error message to be logged + * @param int $level the error level + */ + public function log($msg, $level=null){ + switch($level){ + case 'debug': + $level = \OCP\Util::DEBUG; + break; + case 'info': + $level = \OCP\Util::INFO; + break; + case 'warn': + $level = \OCP\Util::WARN; + break; + case 'fatal': + $level = \OCP\Util::FATAL; + break; + default: + $level = \OCP\Util::ERROR; + break; + } + \OCP\Util::writeLog($this->appName, $msg, $level); + } + + + /** + * Returns a template + * @param string $templateName the name of the template + * @param string $renderAs how it should be rendered + * @param string $appName the name of the app + * @return \OCP\Template a new template + */ + public function getTemplate($templateName, $renderAs='user', $appName=null){ + if($appName === null){ + $appName = $this->appName; + } + + if($renderAs === 'blank'){ + return new \OCP\Template($appName, $templateName); + } else { + return new \OCP\Template($appName, $templateName, $renderAs); + } + } + + + /** + * turns an owncloud path into a path on the filesystem + * @param string path the path to the file on the oc filesystem + * @return string the filepath in the filesystem + */ + public function getLocalFilePath($path){ + # TODO: use public api + return \OC_Filesystem::getLocalFile($path); + } + + + /** + * used to return and open a new eventsource + * @return \OC_EventSource a new open EventSource class + */ + public function openEventSource(){ + # TODO: use public api + return new \OC_EventSource(); + } + + /** + * @brief connects a function to a hook + * @param string $signalClass class name of emitter + * @param string $signalName name of signal + * @param string $slotClass class name of slot + * @param string $slotName name of slot, in another word, this is the + * name of the method that will be called when registered + * signal is emitted. + * @return bool, always true + */ + public function connectHook($signalClass, $signalName, $slotClass, $slotName) { + return \OCP\Util::connectHook($signalClass, $signalName, $slotClass, $slotName); + } + + /** + * @brief Emits a signal. To get data from the slot use references! + * @param string $signalClass class name of emitter + * @param string $signalName name of signal + * @param array $params defautl: array() array with additional data + * @return bool, true if slots exists or false if not + */ + public function emitHook($signalClass, $signalName, $params = array()) { + return \OCP\Util::emitHook($signalClass, $signalName, $params); + } + + /** + * @brief clear hooks + * @param string $signalClass + * @param string $signalName + */ + public function clearHook($signalClass=false, $signalName=false) { + if ($signalClass) { + \OC_Hook::clear($signalClass, $signalName); + } + } + + /** + * Gets the content of an URL by using CURL or a fallback if it is not + * installed + * @param string $url the url that should be fetched + * @return string the content of the webpage + */ + public function getUrlContent($url) { + return \OC_Util::getUrlContent($url); + } + + /** + * Register a backgroundjob task + * @param string $className full namespace and class name of the class + * @param string $methodName the name of the static method that should be + * called + */ + public function addRegularTask($className, $methodName) { + \OCP\Backgroundjob::addRegularTask($className, $methodName); + } + + /** + * Tells ownCloud to include a template in the admin overview + * @param string $mainPath the path to the main php file without the php + * suffix, relative to your apps directory! not the template directory + * @param string $appName the name of the app, defaults to the current one + */ + public function registerAdmin($mainPath, $appName=null) { + if($appName === null){ + $appName = $this->appName; + } + + \OCP\App::registerAdmin($appName, $mainPath); + } + + /** + * Do a user login + * @param string $user the username + * @param string $password the password + * @return bool true if successful + */ + public function login($user, $password) { + return \OC_User::login($user, $password); + } + + /** + * @brief Loggs the user out including all the session data + * Logout, destroys session + */ + public function logout() { + return \OCP\User::logout(); + } + + /** + * get the filesystem info + * + * @param string $path + * @return array with the following keys: + * - size + * - mtime + * - mimetype + * - encrypted + * - versioned + */ + public function getFileInfo($path) { + return \OC\Files\Filesystem::getFileInfo($path); + } + + /** + * get the view + * + * @return OC\Files\View instance + */ + public function getView() { + return \OC\Files\Filesystem::getView(); + } +} diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php new file mode 100644 index 0000000000..34f64e72cb --- /dev/null +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -0,0 +1,125 @@ +. + * + */ + + +namespace OC\AppFramework\DependencyInjection; + +use OC\AppFramework\Http\Http; +use OC\AppFramework\Http\Request; +use OC\AppFramework\Http\Dispatcher; +use OC\AppFramework\Core\API; +use OC\AppFramework\Middleware\MiddlewareDispatcher; +use OC\AppFramework\Middleware\Http\HttpMiddleware; +use OC\AppFramework\Middleware\Security\SecurityMiddleware; +use OC\AppFramework\Utility\TimeFactory; + +// register 3rdparty autoloaders +require_once __DIR__ . '/../../../../3rdparty/Pimple/Pimple.php'; + + +/** + * This class extends Pimple (http://pimple.sensiolabs.org/) for reusability + * To use this class, extend your own container from this. Should you require it + * you can overwrite the dependencies with your own classes by simply redefining + * a dependency + */ +class DIContainer extends \Pimple { + + + /** + * Put your class dependencies in here + * @param string $appName the name of the app + */ + public function __construct($appName){ + + $this['AppName'] = $appName; + + $this['API'] = $this->share(function($c){ + return new API($c['AppName']); + }); + + /** + * Http + */ + $this['Request'] = $this->share(function($c) { + $params = json_decode(file_get_contents('php://input'), true); + $params = is_array($params) ? $params: array(); + + return new Request( + array( + 'get' => $_GET, + 'post' => $_POST, + 'files' => $_FILES, + 'server' => $_SERVER, + 'env' => $_ENV, + 'session' => $_SESSION, + 'cookies' => $_COOKIE, + 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) + ? $_SERVER['REQUEST_METHOD'] + : null, + 'params' => $params, + 'urlParams' => $c['urlParams'] + ) + ); + }); + + $this['Protocol'] = $this->share(function($c){ + if(isset($_SERVER['SERVER_PROTOCOL'])) { + return new Http($_SERVER, $_SERVER['SERVER_PROTOCOL']); + } else { + return new Http($_SERVER); + } + }); + + $this['Dispatcher'] = $this->share(function($c) { + return new Dispatcher($c['Protocol'], $c['MiddlewareDispatcher']); + }); + + + /** + * Middleware + */ + $this['SecurityMiddleware'] = $this->share(function($c){ + return new SecurityMiddleware($c['API'], $c['Request']); + }); + + $this['MiddlewareDispatcher'] = $this->share(function($c){ + $dispatcher = new MiddlewareDispatcher(); + $dispatcher->registerMiddleware($c['SecurityMiddleware']); + + return $dispatcher; + }); + + + /** + * Utilities + */ + $this['TimeFactory'] = $this->share(function($c){ + return new TimeFactory(); + }); + + + } + + +} diff --git a/lib/appframework/http/dispatcher.php b/lib/appframework/http/dispatcher.php new file mode 100644 index 0000000000..ab5644274f --- /dev/null +++ b/lib/appframework/http/dispatcher.php @@ -0,0 +1,98 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + +use \OC\AppFramework\Controller\Controller; +use \OC\AppFramework\Middleware\MiddlewareDispatcher; + + +/** + * Class to dispatch the request to the middleware disptacher + */ +class Dispatcher { + + private $middlewareDispatcher; + private $protocol; + + + /** + * @param Http $protocol the http protocol with contains all status headers + * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which + * runs the middleware + */ + public function __construct(Http $protocol, + MiddlewareDispatcher $middlewareDispatcher) { + $this->protocol = $protocol; + $this->middlewareDispatcher = $middlewareDispatcher; + } + + + /** + * Handles a request and calls the dispatcher on the controller + * @param Controller $controller the controller which will be called + * @param string $methodName the method name which will be called on + * the controller + * @return array $array[0] contains a string with the http main header, + * $array[1] contains headers in the form: $key => value, $array[2] contains + * the response output + */ + public function dispatch(Controller $controller, $methodName) { + $out = array(null, array(), null); + + try { + + $this->middlewareDispatcher->beforeController($controller, + $methodName); + $response = $controller->$methodName(); + + + // if an exception appears, the middleware checks if it can handle the + // exception and creates a response. If no response is created, it is + // assumed that theres no middleware who can handle it and the error is + // thrown again + } catch(\Exception $exception){ + $response = $this->middlewareDispatcher->afterException( + $controller, $methodName, $exception); + } + + $response = $this->middlewareDispatcher->afterController( + $controller, $methodName, $response); + + // get the output which should be printed and run the after output + // middleware to modify the response + $output = $response->render(); + $out[2] = $this->middlewareDispatcher->beforeOutput( + $controller, $methodName, $output); + + // depending on the cache object the headers need to be changed + $out[0] = $this->protocol->getStatusHeader($response->getStatus(), + $response->getLastModified(), $response->getETag()); + $out[1] = $response->getHeaders(); + + return $out; + } + + +} diff --git a/lib/appframework/http/downloadresponse.php b/lib/appframework/http/downloadresponse.php new file mode 100644 index 0000000000..5a0db325fe --- /dev/null +++ b/lib/appframework/http/downloadresponse.php @@ -0,0 +1,51 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +/** + * Prompts the user to download the a file + */ +abstract class DownloadResponse extends Response { + + private $content; + private $filename; + private $contentType; + + /** + * Creates a response that prompts the user to download the file + * @param string $filename the name that the downloaded file should have + * @param string $contentType the mimetype that the downloaded file should have + */ + public function __construct($filename, $contentType) { + $this->filename = $filename; + $this->contentType = $contentType; + + $this->addHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); + $this->addHeader('Content-Type', $contentType); + } + + +} diff --git a/lib/appframework/http/http.php b/lib/appframework/http/http.php new file mode 100644 index 0000000000..73f32d13b3 --- /dev/null +++ b/lib/appframework/http/http.php @@ -0,0 +1,208 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +class Http { + + const STATUS_CONTINUE = 100; + const STATUS_SWITCHING_PROTOCOLS = 101; + const STATUS_PROCESSING = 102; + const STATUS_OK = 200; + const STATUS_CREATED = 201; + const STATUS_ACCEPTED = 202; + const STATUS_NON_AUTHORATIVE_INFORMATION = 203; + const STATUS_NO_CONTENT = 204; + const STATUS_RESET_CONTENT = 205; + const STATUS_PARTIAL_CONTENT = 206; + const STATUS_MULTI_STATUS = 207; + const STATUS_ALREADY_REPORTED = 208; + const STATUS_IM_USED = 226; + const STATUS_MULTIPLE_CHOICES = 300; + const STATUS_MOVED_PERMANENTLY = 301; + const STATUS_FOUND = 302; + const STATUS_SEE_OTHER = 303; + const STATUS_NOT_MODIFIED = 304; + const STATUS_USE_PROXY = 305; + const STATUS_RESERVED = 306; + const STATUS_TEMPORARY_REDIRECT = 307; + const STATUS_BAD_REQUEST = 400; + const STATUS_UNAUTHORIZED = 401; + const STATUS_PAYMENT_REQUIRED = 402; + const STATUS_FORBIDDEN = 403; + const STATUS_NOT_FOUND = 404; + const STATUS_METHOD_NOT_ALLOWED = 405; + const STATUS_NOT_ACCEPTABLE = 406; + const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; + const STATUS_REQUEST_TIMEOUT = 408; + const STATUS_CONFLICT = 409; + const STATUS_GONE = 410; + const STATUS_LENGTH_REQUIRED = 411; + const STATUS_PRECONDITION_FAILED = 412; + const STATUS_REQUEST_ENTITY_TOO_LARGE = 413; + const STATUS_REQUEST_URI_TOO_LONG = 414; + const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; + const STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416; + const STATUS_EXPECTATION_FAILED = 417; + const STATUS_IM_A_TEAPOT = 418; + const STATUS_UNPROCESSABLE_ENTITY = 422; + const STATUS_LOCKED = 423; + const STATUS_FAILED_DEPENDENCY = 424; + const STATUS_UPGRADE_REQUIRED = 426; + const STATUS_PRECONDITION_REQUIRED = 428; + const STATUS_TOO_MANY_REQUESTS = 429; + const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + const STATUS_INTERNAL_SERVER_ERROR = 500; + const STATUS_NOT_IMPLEMENTED = 501; + const STATUS_BAD_GATEWAY = 502; + const STATUS_SERVICE_UNAVAILABLE = 503; + const STATUS_GATEWAY_TIMEOUT = 504; + const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; + const STATUS_VARIANT_ALSO_NEGOTIATES = 506; + const STATUS_INSUFFICIENT_STORAGE = 507; + const STATUS_LOOP_DETECTED = 508; + const STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509; + const STATUS_NOT_EXTENDED = 510; + const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; + + private $server; + private $protocolVersion; + protected $headers; + + /** + * @param $_SERVER $server + * @param string $protocolVersion the http version to use defaults to HTTP/1.1 + */ + public function __construct($server, $protocolVersion='HTTP/1.1') { + $this->server = $server; + $this->protocolVersion = $protocolVersion; + + $this->headers = array( + self::STATUS_CONTINUE => 'Continue', + self::STATUS_SWITCHING_PROTOCOLS => 'Switching Protocols', + self::STATUS_PROCESSING => 'Processing', + self::STATUS_OK => 'OK', + self::STATUS_CREATED => 'Created', + self::STATUS_ACCEPTED => 'Accepted', + self::STATUS_NON_AUTHORATIVE_INFORMATION => 'Non-Authorative Information', + self::STATUS_NO_CONTENT => 'No Content', + self::STATUS_RESET_CONTENT => 'Reset Content', + self::STATUS_PARTIAL_CONTENT => 'Partial Content', + self::STATUS_MULTI_STATUS => 'Multi-Status', // RFC 4918 + self::STATUS_ALREADY_REPORTED => 'Already Reported', // RFC 5842 + self::STATUS_IM_USED => 'IM Used', // RFC 3229 + self::STATUS_MULTIPLE_CHOICES => 'Multiple Choices', + self::STATUS_MOVED_PERMANENTLY => 'Moved Permanently', + self::STATUS_FOUND => 'Found', + self::STATUS_SEE_OTHER => 'See Other', + self::STATUS_NOT_MODIFIED => 'Not Modified', + self::STATUS_USE_PROXY => 'Use Proxy', + self::STATUS_RESERVED => 'Reserved', + self::STATUS_TEMPORARY_REDIRECT => 'Temporary Redirect', + self::STATUS_BAD_REQUEST => 'Bad request', + self::STATUS_UNAUTHORIZED => 'Unauthorized', + self::STATUS_PAYMENT_REQUIRED => 'Payment Required', + self::STATUS_FORBIDDEN => 'Forbidden', + self::STATUS_NOT_FOUND => 'Not Found', + self::STATUS_METHOD_NOT_ALLOWED => 'Method Not Allowed', + self::STATUS_NOT_ACCEPTABLE => 'Not Acceptable', + self::STATUS_PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required', + self::STATUS_REQUEST_TIMEOUT => 'Request Timeout', + self::STATUS_CONFLICT => 'Conflict', + self::STATUS_GONE => 'Gone', + self::STATUS_LENGTH_REQUIRED => 'Length Required', + self::STATUS_PRECONDITION_FAILED => 'Precondition failed', + self::STATUS_REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large', + self::STATUS_REQUEST_URI_TOO_LONG => 'Request-URI Too Long', + self::STATUS_UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type', + self::STATUS_REQUEST_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable', + self::STATUS_EXPECTATION_FAILED => 'Expectation Failed', + self::STATUS_IM_A_TEAPOT => 'I\'m a teapot', // RFC 2324 + self::STATUS_UNPROCESSABLE_ENTITY => 'Unprocessable Entity', // RFC 4918 + self::STATUS_LOCKED => 'Locked', // RFC 4918 + self::STATUS_FAILED_DEPENDENCY => 'Failed Dependency', // RFC 4918 + self::STATUS_UPGRADE_REQUIRED => 'Upgrade required', + self::STATUS_PRECONDITION_REQUIRED => 'Precondition required', // draft-nottingham-http-new-status + self::STATUS_TOO_MANY_REQUESTS => 'Too Many Requests', // draft-nottingham-http-new-status + self::STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE => 'Request Header Fields Too Large', // draft-nottingham-http-new-status + self::STATUS_INTERNAL_SERVER_ERROR => 'Internal Server Error', + self::STATUS_NOT_IMPLEMENTED => 'Not Implemented', + self::STATUS_BAD_GATEWAY => 'Bad Gateway', + self::STATUS_SERVICE_UNAVAILABLE => 'Service Unavailable', + self::STATUS_GATEWAY_TIMEOUT => 'Gateway Timeout', + self::STATUS_HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version not supported', + self::STATUS_VARIANT_ALSO_NEGOTIATES => 'Variant Also Negotiates', + self::STATUS_INSUFFICIENT_STORAGE => 'Insufficient Storage', // RFC 4918 + self::STATUS_LOOP_DETECTED => 'Loop Detected', // RFC 5842 + self::STATUS_BANDWIDTH_LIMIT_EXCEEDED => 'Bandwidth Limit Exceeded', // non-standard + self::STATUS_NOT_EXTENDED => 'Not extended', + self::STATUS_NETWORK_AUTHENTICATION_REQUIRED => 'Network Authentication Required', // draft-nottingham-http-new-status + ); + } + + + /** + * Gets the correct header + * @param Http::CONSTANT $status the constant from the Http class + * @param \DateTime $lastModified formatted last modified date + * @param string $Etag the etag + */ + public function getStatusHeader($status, \DateTime $lastModified=null, + $ETag=null) { + + if(!is_null($lastModified)) { + $lastModified = $lastModified->format(\DateTime::RFC2822); + } + + // if etag or lastmodified have not changed, return a not modified + if ((isset($this->server['HTTP_IF_NONE_MATCH']) + && trim($this->server['HTTP_IF_NONE_MATCH']) === $ETag) + + || + + (isset($this->server['HTTP_IF_MODIFIED_SINCE']) + && trim($this->server['HTTP_IF_MODIFIED_SINCE']) === + $lastModified)) { + + $status = self::STATUS_NOT_MODIFIED; + } + + // we have one change currently for the http 1.0 header that differs + // from 1.1: STATUS_TEMPORARY_REDIRECT should be STATUS_FOUND + // if this differs any more, we want to create childclasses for this + if($status === self::STATUS_TEMPORARY_REDIRECT + && $this->protocolVersion === 'HTTP/1.0') { + + $status = self::STATUS_FOUND; + } + + return $this->protocolVersion . ' ' . $status . ' ' . + $this->headers[$status]; + } + + +} + + diff --git a/lib/appframework/http/jsonresponse.php b/lib/appframework/http/jsonresponse.php new file mode 100644 index 0000000000..750f8a2ad1 --- /dev/null +++ b/lib/appframework/http/jsonresponse.php @@ -0,0 +1,74 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +/** + * A renderer for JSON calls + */ +class JSONResponse extends Response { + + protected $data; + + + /** + * @param array|object $data the object or array that should be transformed + * @param int $statusCode the Http status code, defaults to 200 + */ + public function __construct($data=array(), $statusCode=Http::STATUS_OK) { + $this->data = $data; + $this->setStatus($statusCode); + $this->addHeader('X-Content-Type-Options', 'nosniff'); + $this->addHeader('Content-type', 'application/json; charset=utf-8'); + } + + + /** + * Returns the rendered json + * @return string the rendered json + */ + public function render(){ + return json_encode($this->data); + } + + /** + * Sets values in the data json array + * @param array|object $params an array or object which will be transformed + * to JSON + */ + public function setData($data){ + $this->data = $data; + } + + + /** + * Used to get the set parameters + * @return array the data + */ + public function getData(){ + return $this->data; + } + +} diff --git a/lib/appframework/http/redirectresponse.php b/lib/appframework/http/redirectresponse.php new file mode 100644 index 0000000000..727e0fb642 --- /dev/null +++ b/lib/appframework/http/redirectresponse.php @@ -0,0 +1,54 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +/** + * Redirects to a different URL + */ +class RedirectResponse extends Response { + + private $redirectURL; + + /** + * Creates a response that redirects to a url + * @param string $redirectURL the url to redirect to + */ + public function __construct($redirectURL) { + $this->redirectURL = $redirectURL; + $this->setStatus(Http::STATUS_TEMPORARY_REDIRECT); + $this->addHeader('Location', $redirectURL); + } + + + /** + * @return string the url to redirect + */ + public function getRedirectURL() { + return $this->redirectURL; + } + + +} diff --git a/lib/appframework/http/request.php b/lib/appframework/http/request.php new file mode 100644 index 0000000000..7d024c8605 --- /dev/null +++ b/lib/appframework/http/request.php @@ -0,0 +1,217 @@ +. + * + */ + +namespace OC\AppFramework\Http; + +/** + * Class for accessing variables in the request. + * This class provides an immutable object with request variables. + */ + +class Request implements \ArrayAccess, \Countable { + + protected $items = array(); + protected $allowedKeys = array( + 'get', + 'post', + 'files', + 'server', + 'env', + 'session', + 'cookies', + 'urlParams', + 'params', + 'parameters', + 'method' + ); + + /** + * @param array $vars An associative array with the following optional values: + * @param array 'params' the parsed json array + * @param array 'urlParams' the parameters which were matched from the URL + * @param array 'get' the $_GET array + * @param array 'post' the $_POST array + * @param array 'files' the $_FILES array + * @param array 'server' the $_SERVER array + * @param array 'env' the $_ENV array + * @param array 'session' the $_SESSION array + * @param array 'cookies' the $_COOKIE array + * @param string 'method' the request method (GET, POST etc) + * @see http://www.php.net/manual/en/reserved.variables.php + */ + public function __construct(array $vars=array()) { + + foreach($this->allowedKeys as $name) { + $this->items[$name] = isset($vars[$name]) + ? $vars[$name] + : array(); + } + + $this->items['parameters'] = array_merge( + $this->items['params'], + $this->items['get'], + $this->items['post'], + $this->items['urlParams'] + ); + + } + + // Countable method. + public function count() { + return count(array_keys($this->items['parameters'])); + } + + /** + * ArrayAccess methods + * + * Gives access to the combined GET, POST and urlParams arrays + * + * Examples: + * + * $var = $request['myvar']; + * + * or + * + * if(!isset($request['myvar']) { + * // Do something + * } + * + * $request['myvar'] = 'something'; // This throws an exception. + * + * @param string $offset The key to lookup + * @return string|null + */ + public function offsetExists($offset) { + return isset($this->items['parameters'][$offset]); + } + + /** + * @see offsetExists + */ + public function offsetGet($offset) { + return isset($this->items['parameters'][$offset]) + ? $this->items['parameters'][$offset] + : null; + } + + /** + * @see offsetExists + */ + public function offsetSet($offset, $value) { + throw new \RuntimeException('You cannot change the contents of the request object'); + } + + /** + * @see offsetExists + */ + public function offsetUnset($offset) { + throw new \RuntimeException('You cannot change the contents of the request object'); + } + + // Magic property accessors + public function __set($name, $value) { + throw new \RuntimeException('You cannot change the contents of the request object'); + } + + /** + * Access request variables by method and name. + * Examples: + * + * $request->post['myvar']; // Only look for POST variables + * $request->myvar; or $request->{'myvar'}; or $request->{$myvar} + * Looks in the combined GET, POST and urlParams array. + * + * if($request->method !== 'POST') { + * throw new Exception('This function can only be invoked using POST'); + * } + * + * @param string $name The key to look for. + * @return mixed|null + */ + public function __get($name) { + switch($name) { + case 'get': + case 'post': + case 'files': + case 'server': + case 'env': + case 'session': + case 'cookies': + case 'parameters': + case 'params': + case 'urlParams': + return isset($this->items[$name]) + ? $this->items[$name] + : null; + break; + case 'method': + return $this->items['method']; + break; + default; + return isset($this[$name]) + ? $this[$name] + : null; + break; + } + } + + + public function __isset($name) { + return isset($this->items['parameters'][$name]); + } + + + public function __unset($id) { + throw new \RunTimeException('You cannot change the contents of the request object'); + } + + /** + * Returns the value for a specific http header. + * + * This method returns null if the header did not exist. + * + * @param string $name + * @return string + */ + public function getHeader($name) { + + $name = strtoupper(str_replace(array('-'),array('_'),$name)); + if (isset($this->server['HTTP_' . $name])) { + return $this->server['HTTP_' . $name]; + } + + // There's a few headers that seem to end up in the top-level + // server array. + switch($name) { + case 'CONTENT_TYPE' : + case 'CONTENT_LENGTH' : + if (isset($this->server[$name])) { + return $this->server[$name]; + } + break; + + } + + return null; + } + +} diff --git a/lib/appframework/http/response.php b/lib/appframework/http/response.php new file mode 100644 index 0000000000..50778105f2 --- /dev/null +++ b/lib/appframework/http/response.php @@ -0,0 +1,169 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +/** + * Base class for responses. Also used to just send headers + */ +class Response { + + /** + * @var array default headers + */ + private $headers = array( + 'Cache-Control' => 'no-cache, must-revalidate' + ); + + + /** + * @var string + */ + private $status = Http::STATUS_OK; + + + /** + * @var \DateTime + */ + private $lastModified; + + + /** + * @var string + */ + private $ETag; + + + /** + * Caches the response + * @param int $cacheSeconds the amount of seconds that should be cached + * if 0 then caching will be disabled + */ + public function cacheFor($cacheSeconds) { + + if($cacheSeconds > 0) { + $this->addHeader('Cache-Control', 'max-age=' . $cacheSeconds . + ', must-revalidate'); + } else { + $this->addHeader('Cache-Control', 'no-cache, must-revalidate'); + } + + } + + + /** + * Adds a new header to the response that will be called before the render + * function + * @param string $name The name of the HTTP header + * @param string $value The value, null will delete it + */ + public function addHeader($name, $value) { + if(is_null($value)) { + unset($this->headers[$name]); + } else { + $this->headers[$name] = $value; + } + } + + + /** + * Returns the set headers + * @return array the headers + */ + public function getHeaders() { + $mergeWith = array(); + + if($this->lastModified) { + $mergeWith['Last-Modified'] = + $this->lastModified->format(\DateTime::RFC2822); + } + + if($this->ETag) { + $mergeWith['ETag'] = '"' . $this->ETag . '"'; + } + + return array_merge($mergeWith, $this->headers); + } + + + /** + * By default renders no output + * @return null + */ + public function render() { + return null; + } + + + /** + * Set response status + * @param int $status a HTTP status code, see also the STATUS constants + */ + public function setStatus($status) { + $this->status = $status; + } + + + /** + * Get response status + */ + public function getStatus() { + return $this->status; + } + + + /** + * @return string the etag + */ + public function getETag() { + return $this->ETag; + } + + + /** + * @return string RFC2822 formatted last modified date + */ + public function getLastModified() { + return $this->lastModified; + } + + + /** + * @param string $ETag + */ + public function setETag($ETag) { + $this->ETag = $ETag; + } + + + /** + * @param \DateTime $lastModified + */ + public function setLastModified($lastModified) { + $this->lastModified = $lastModified; + } + + +} diff --git a/lib/appframework/http/templateresponse.php b/lib/appframework/http/templateresponse.php new file mode 100644 index 0000000000..0a32da4b1b --- /dev/null +++ b/lib/appframework/http/templateresponse.php @@ -0,0 +1,126 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + +use OC\AppFramework\Core\API; + + +/** + * Response for a normal template + */ +class TemplateResponse extends Response { + + protected $templateName; + protected $params; + protected $api; + protected $renderAs; + protected $appName; + + /** + * @param API $api an API instance + * @param string $templateName the name of the template + * @param string $appName optional if you want to include a template from + * a different app + */ + public function __construct(API $api, $templateName, $appName=null) { + $this->templateName = $templateName; + $this->appName = $appName; + $this->api = $api; + $this->params = array(); + $this->renderAs = 'user'; + } + + + /** + * Sets template parameters + * @param array $params an array with key => value structure which sets template + * variables + */ + public function setParams(array $params){ + $this->params = $params; + } + + + /** + * Used for accessing the set parameters + * @return array the params + */ + public function getParams(){ + return $this->params; + } + + + /** + * Used for accessing the name of the set template + * @return string the name of the used template + */ + public function getTemplateName(){ + return $this->templateName; + } + + + /** + * Sets the template page + * @param string $renderAs admin, user or blank. Admin also prints the admin + * settings header and footer, user renders the normal + * normal page including footer and header and blank + * just renders the plain template + */ + public function renderAs($renderAs){ + $this->renderAs = $renderAs; + } + + + /** + * Returns the set renderAs + * @return string the renderAs value + */ + public function getRenderAs(){ + return $this->renderAs; + } + + + /** + * Returns the rendered html + * @return string the rendered html + */ + public function render(){ + + if($this->appName !== null){ + $appName = $this->appName; + } else { + $appName = $this->api->getAppName(); + } + + $template = $this->api->getTemplate($this->templateName, $this->renderAs, $appName); + + foreach($this->params as $key => $value){ + $template->assign($key, $value); + } + + return $template->fetchPage(); + } + +} diff --git a/lib/appframework/middleware/middleware.php b/lib/appframework/middleware/middleware.php new file mode 100644 index 0000000000..4df8849046 --- /dev/null +++ b/lib/appframework/middleware/middleware.php @@ -0,0 +1,100 @@ +. + * + */ + + +namespace OC\AppFramework\Middleware; + +use OC\AppFramework\Http\Response; + + +/** + * Middleware is used to provide hooks before or after controller methods and + * deal with possible exceptions raised in the controller methods. + * They're modeled after Django's middleware system: + * https://docs.djangoproject.com/en/dev/topics/http/middleware/ + */ +abstract class Middleware { + + + /** + * This is being run in normal order before the controller is being + * called which allows several modifications and checks + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + */ + public function beforeController($controller, $methodName){ + + } + + + /** + * This is being run when either the beforeController method or the + * controller method itself is throwing an exception. The middleware is + * asked in reverse order to handle the exception and to return a response. + * If the response is null, it is assumed that the exception could not be + * handled and the error will be thrown again + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param \Exception $exception the thrown exception + * @throws \Exception the passed in exception if it cant handle it + * @return Response a Response object in case that the exception was handled + */ + public function afterException($controller, $methodName, \Exception $exception){ + throw $exception; + } + + + /** + * This is being run after a successful controllermethod call and allows + * the manipulation of a Response object. The middleware is run in reverse order + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param Response $response the generated response from the controller + * @return Response a Response object + */ + public function afterController($controller, $methodName, Response $response){ + return $response; + } + + + /** + * This is being run after the response object has been rendered and + * allows the manipulation of the output. The middleware is run in reverse order + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param string $output the generated output from a response + * @return string the output that should be printed + */ + public function beforeOutput($controller, $methodName, $output){ + return $output; + } + +} diff --git a/lib/appframework/middleware/middlewaredispatcher.php b/lib/appframework/middleware/middlewaredispatcher.php new file mode 100644 index 0000000000..c2d16134dc --- /dev/null +++ b/lib/appframework/middleware/middlewaredispatcher.php @@ -0,0 +1,159 @@ +. + * + */ + + +namespace OC\AppFramework\Middleware; + +use OC\AppFramework\Controller\Controller; +use OC\AppFramework\Http\Response; + + +/** + * This class is used to store and run all the middleware in correct order + */ +class MiddlewareDispatcher { + + /** + * @var array array containing all the middlewares + */ + private $middlewares; + + /** + * @var int counter which tells us what middlware was executed once an + * exception occurs + */ + private $middlewareCounter; + + + /** + * Constructor + */ + public function __construct(){ + $this->middlewares = array(); + $this->middlewareCounter = 0; + } + + + /** + * Adds a new middleware + * @param Middleware $middleware the middleware which will be added + */ + public function registerMiddleware(Middleware $middleWare){ + array_push($this->middlewares, $middleWare); + } + + + /** + * returns an array with all middleware elements + * @return array the middlewares + */ + public function getMiddlewares(){ + return $this->middlewares; + } + + + /** + * This is being run in normal order before the controller is being + * called which allows several modifications and checks + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + */ + public function beforeController(Controller $controller, $methodName){ + // we need to count so that we know which middlewares we have to ask in + // case theres an exception + for($i=0; $imiddlewares); $i++){ + $this->middlewareCounter++; + $middleware = $this->middlewares[$i]; + $middleware->beforeController($controller, $methodName); + } + } + + + /** + * This is being run when either the beforeController method or the + * controller method itself is throwing an exception. The middleware is asked + * in reverse order to handle the exception and to return a response. + * If the response is null, it is assumed that the exception could not be + * handled and the error will be thrown again + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param \Exception $exception the thrown exception + * @return Response a Response object if the middleware can handle the + * exception + * @throws \Exception the passed in exception if it cant handle it + */ + public function afterException(Controller $controller, $methodName, \Exception $exception){ + for($i=$this->middlewareCounter-1; $i>=0; $i--){ + $middleware = $this->middlewares[$i]; + try { + return $middleware->afterException($controller, $methodName, $exception); + } catch(\Exception $exception){ + continue; + } + } + throw $exception; + } + + + /** + * This is being run after a successful controllermethod call and allows + * the manipulation of a Response object. The middleware is run in reverse order + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param Response $response the generated response from the controller + * @return Response a Response object + */ + public function afterController(Controller $controller, $methodName, Response $response){ + for($i=count($this->middlewares)-1; $i>=0; $i--){ + $middleware = $this->middlewares[$i]; + $response = $middleware->afterController($controller, $methodName, $response); + } + return $response; + } + + + /** + * This is being run after the response object has been rendered and + * allows the manipulation of the output. The middleware is run in reverse order + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param string $output the generated output from a response + * @return string the output that should be printed + */ + public function beforeOutput(Controller $controller, $methodName, $output){ + for($i=count($this->middlewares)-1; $i>=0; $i--){ + $middleware = $this->middlewares[$i]; + $output = $middleware->beforeOutput($controller, $methodName, $output); + } + return $output; + } + +} diff --git a/lib/appframework/middleware/security/securityexception.php b/lib/appframework/middleware/security/securityexception.php new file mode 100644 index 0000000000..b32a2769ff --- /dev/null +++ b/lib/appframework/middleware/security/securityexception.php @@ -0,0 +1,41 @@ +. + * + */ + + +namespace OC\AppFramework\Middleware\Security; + + +/** + * Thrown when the security middleware encounters a security problem + */ +class SecurityException extends \Exception { + + /** + * @param string $msg the security error message + * @param bool $ajax true if it resulted because of an ajax request + */ + public function __construct($msg, $code = 0) { + parent::__construct($msg, $code); + } + +} diff --git a/lib/appframework/middleware/security/securitymiddleware.php b/lib/appframework/middleware/security/securitymiddleware.php new file mode 100644 index 0000000000..7a715f309a --- /dev/null +++ b/lib/appframework/middleware/security/securitymiddleware.php @@ -0,0 +1,141 @@ +. + * + */ + + +namespace OC\AppFramework\Middleware\Security; + +use OC\AppFramework\Controller\Controller; +use OC\AppFramework\Http\Http; +use OC\AppFramework\Http\Request; +use OC\AppFramework\Http\Response; +use OC\AppFramework\Http\JSONResponse; +use OC\AppFramework\Http\RedirectResponse; +use OC\AppFramework\Utility\MethodAnnotationReader; +use OC\AppFramework\Middleware\Middleware; +use OC\AppFramework\Core\API; + + +/** + * Used to do all the authentication and checking stuff for a controller method + * It reads out the annotations of a controller method and checks which if + * security things should be checked and also handles errors in case a security + * check fails + */ +class SecurityMiddleware extends Middleware { + + private $api; + + /** + * @var \OC\AppFramework\Http\Request + */ + private $request; + + /** + * @param API $api an instance of the api + */ + public function __construct(API $api, Request $request){ + $this->api = $api; + $this->request = $request; + } + + + /** + * This runs all the security checks before a method call. The + * security checks are determined by inspecting the controller method + * annotations + * @param string/Controller $controller the controllername or string + * @param string $methodName the name of the method + * @throws SecurityException when a security check fails + */ + public function beforeController($controller, $methodName){ + + // get annotations from comments + $annotationReader = new MethodAnnotationReader($controller, $methodName); + + // this will set the current navigation entry of the app, use this only + // for normal HTML requests and not for AJAX requests + $this->api->activateNavigationEntry(); + + // security checks + if(!$annotationReader->hasAnnotation('IsLoggedInExemption')) { + if(!$this->api->isLoggedIn()) { + throw new SecurityException('Current user is not logged in', Http::STATUS_UNAUTHORIZED); + } + } + + if(!$annotationReader->hasAnnotation('IsAdminExemption')) { + if(!$this->api->isAdminUser($this->api->getUserId())) { + throw new SecurityException('Logged in user must be an admin', Http::STATUS_FORBIDDEN); + } + } + + if(!$annotationReader->hasAnnotation('IsSubAdminExemption')) { + if(!$this->api->isSubAdminUser($this->api->getUserId())) { + throw new SecurityException('Logged in user must be a subadmin', Http::STATUS_FORBIDDEN); + } + } + + if(!$annotationReader->hasAnnotation('CSRFExemption')) { + if(!$this->api->passesCSRFCheck()) { + throw new SecurityException('CSRF check failed', Http::STATUS_PRECONDITION_FAILED); + } + } + + } + + + /** + * If an SecurityException is being caught, ajax requests return a JSON error + * response and non ajax requests redirect to the index + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param \Exception $exception the thrown exception + * @throws \Exception the passed in exception if it cant handle it + * @return Response a Response object or null in case that the exception could not be handled + */ + public function afterException($controller, $methodName, \Exception $exception){ + if($exception instanceof SecurityException){ + + if (stripos($this->request->getHeader('Accept'),'html')===false) { + + $response = new JSONResponse( + array('message' => $exception->getMessage()), + $exception->getCode() + ); + $this->api->log($exception->getMessage(), 'debug'); + } else { + + $url = $this->api->linkToAbsolute('index.php', ''); // TODO: replace with link to route + $response = new RedirectResponse($url); + $this->api->log($exception->getMessage(), 'debug'); + } + + return $response; + + } + + throw $exception; + } + +} diff --git a/lib/appframework/routing/routeactionhandler.php b/lib/appframework/routing/routeactionhandler.php new file mode 100644 index 0000000000..7fb56f14ea --- /dev/null +++ b/lib/appframework/routing/routeactionhandler.php @@ -0,0 +1,42 @@ +. + * + */ + +namespace OC\AppFramework\routing; + +use \OC\AppFramework\App; +use \OC\AppFramework\DependencyInjection\DIContainer; + +class RouteActionHandler { + private $controllerName; + private $actionName; + private $container; + + public function __construct(DIContainer $container, $controllerName, $actionName) { + $this->controllerName = $controllerName; + $this->actionName = $actionName; + $this->container = $container; + } + + public function __invoke($params) { + App::main($this->controllerName, $this->actionName, $params, $this->container); + } +} diff --git a/lib/appframework/routing/routeconfig.php b/lib/appframework/routing/routeconfig.php new file mode 100644 index 0000000000..53ab11bf2f --- /dev/null +++ b/lib/appframework/routing/routeconfig.php @@ -0,0 +1,186 @@ +. + * + */ + +namespace OC\AppFramework\routing; + +use OC\AppFramework\DependencyInjection\DIContainer; + +/** + * Class RouteConfig + * @package OC\AppFramework\routing + */ +class RouteConfig { + private $container; + private $router; + private $routes; + private $appName; + + /** + * @param \OC\AppFramework\DependencyInjection\DIContainer $container + * @param \OC_Router $router + * @param string $pathToYml + * @internal param $appName + */ + public function __construct(DIContainer $container, \OC_Router $router, $routes) { + $this->routes = $routes; + $this->container = $container; + $this->router = $router; + $this->appName = $container['AppName']; + } + + /** + * The routes and resource will be registered to the \OC_Router + */ + public function register() { + + // parse simple + $this->processSimpleRoutes($this->routes); + + // parse resources + $this->processResources($this->routes); + } + + /** + * Creates one route base on the give configuration + * @param $routes + * @throws \UnexpectedValueException + */ + private function processSimpleRoutes($routes) + { + $simpleRoutes = isset($routes['routes']) ? $routes['routes'] : array(); + foreach ($simpleRoutes as $simpleRoute) { + $name = $simpleRoute['name']; + $url = $simpleRoute['url']; + $verb = isset($simpleRoute['verb']) ? strtoupper($simpleRoute['verb']) : 'GET'; + + $split = explode('#', $name, 2); + if (count($split) != 2) { + throw new \UnexpectedValueException('Invalid route name'); + } + $controller = $split[0]; + $action = $split[1]; + + $controllerName = $this->buildControllerName($controller); + $actionName = $this->buildActionName($action); + + // register the route + $handler = new RouteActionHandler($this->container, $controllerName, $actionName); + $this->router->create($this->appName.'.'.$controller.'.'.$action, $url)->method($verb)->action($handler); + } + } + + /** + * For a given name and url restful routes are created: + * - index + * - show + * - new + * - create + * - update + * - destroy + * + * @param $routes + */ + private function processResources($routes) + { + // declaration of all restful actions + $actions = array( + array('name' => 'index', 'verb' => 'GET', 'on-collection' => true), + array('name' => 'show', 'verb' => 'GET'), + array('name' => 'create', 'verb' => 'POST', 'on-collection' => true), + array('name' => 'update', 'verb' => 'PUT'), + array('name' => 'destroy', 'verb' => 'DELETE'), + ); + + $resources = isset($routes['resources']) ? $routes['resources'] : array(); + foreach ($resources as $resource => $config) { + + // the url parameter used as id to the resource + $resourceId = $this->buildResourceId($resource); + foreach($actions as $action) { + $url = $config['url']; + $method = $action['name']; + $verb = isset($action['verb']) ? strtoupper($action['verb']) : 'GET'; + $collectionAction = isset($action['on-collection']) ? $action['on-collection'] : false; + if (!$collectionAction) { + $url = $url . '/' . $resourceId; + } + if (isset($action['url-postfix'])) { + $url = $url . '/' . $action['url-postfix']; + } + + $controller = $resource; + + $controllerName = $this->buildControllerName($controller); + $actionName = $this->buildActionName($method); + + $routeName = $this->appName . '.' . strtolower($resource) . '.' . strtolower($method); + + $this->router->create($routeName, $url)->method($verb)->action( + new RouteActionHandler($this->container, $controllerName, $actionName) + ); + } + } + } + + /** + * Based on a given route name the controller name is generated + * @param $controller + * @return string + */ + private function buildControllerName($controller) + { + return $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller'; + } + + /** + * Based on the action part of the route name the controller method name is generated + * @param $action + * @return string + */ + private function buildActionName($action) { + return $this->underScoreToCamelCase($action); + } + + /** + * Generates the id used in the url part o the route url + * @param $resource + * @return string + */ + private function buildResourceId($resource) { + return '{'.$this->underScoreToCamelCase(rtrim($resource, 's')).'Id}'; + } + + /** + * Underscored strings are converted to camel case strings + * @param $str string + * @return string + */ + private function underScoreToCamelCase($str) { + $pattern = "/_[a-z]?/"; + return preg_replace_callback( + $pattern, + function ($matches) { + return strtoupper(ltrim($matches[0], "_")); + }, + $str); + } +} diff --git a/lib/appframework/utility/methodannotationreader.php b/lib/appframework/utility/methodannotationreader.php new file mode 100644 index 0000000000..42060a0852 --- /dev/null +++ b/lib/appframework/utility/methodannotationreader.php @@ -0,0 +1,61 @@ +. + * + */ + + +namespace OC\AppFramework\Utility; + + +/** + * Reads and parses annotations from doc comments + */ +class MethodAnnotationReader { + + private $annotations; + + /** + * @param object $object an object or classname + * @param string $method the method which we want to inspect for annotations + */ + public function __construct($object, $method){ + $this->annotations = array(); + + $reflection = new \ReflectionMethod($object, $method); + $docs = $reflection->getDocComment(); + + // extract everything prefixed by @ and first letter uppercase + preg_match_all('/@([A-Z]\w+)/', $docs, $matches); + $this->annotations = $matches[1]; + } + + + /** + * Check if a method contains an annotation + * @param string $name the name of the annotation + * @return bool true if the annotation is found + */ + public function hasAnnotation($name){ + return in_array($name, $this->annotations); + } + + +} diff --git a/lib/appframework/utility/timefactory.php b/lib/appframework/utility/timefactory.php new file mode 100644 index 0000000000..2c3dd6cf5e --- /dev/null +++ b/lib/appframework/utility/timefactory.php @@ -0,0 +1,42 @@ +. + * + */ + + +namespace OC\AppFramework\Utility; + + +/** + * Needed to mock calls to time() + */ +class TimeFactory { + + + /** + * @return int the result of a call to time() + */ + public function getTime() { + return time(); + } + + +} diff --git a/tests/lib/appframework/AppTest.php b/tests/lib/appframework/AppTest.php new file mode 100644 index 0000000000..000094d07c --- /dev/null +++ b/tests/lib/appframework/AppTest.php @@ -0,0 +1,107 @@ +. + * + */ + + +namespace OC\AppFramework; + +use OC\AppFramework\Http\Request; +use OC\AppFramework\Core\API; +use OC\AppFramework\Middleware\MiddlewareDispatcher; + +// FIXME: loading pimpl correctly from 3rdparty repo +require_once __DIR__ . '/../../../../3rdparty/Pimple/Pimple.php'; +require_once __DIR__ . "/classloader.php"; + + +class AppTest extends \PHPUnit_Framework_TestCase { + + private $container; + private $api; + private $controller; + private $dispatcher; + private $params; + private $headers; + private $output; + private $controllerName; + private $controllerMethod; + + protected function setUp() { + $this->container = new \Pimple(); + $this->controller = $this->getMockBuilder( + 'OC\AppFramework\Controller\Controller') + ->disableOriginalConstructor() + ->getMock(); + $this->dispatcher = $this->getMockBuilder( + 'OC\AppFramework\Http\Dispatcher') + ->disableOriginalConstructor() + ->getMock(); + + + $this->headers = array('key' => 'value'); + $this->output = 'hi'; + $this->controllerName = 'Controller'; + $this->controllerMethod = 'method'; + + $this->container[$this->controllerName] = $this->controller; + $this->container['Dispatcher'] = $this->dispatcher; + } + + + public function testControllerNameAndMethodAreBeingPassed(){ + $return = array(null, array(), null); + $this->dispatcher->expects($this->once()) + ->method('dispatch') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod)) + ->will($this->returnValue($return)); + + $this->expectOutputString(''); + + App::main($this->controllerName, $this->controllerMethod, array(), + $this->container); + } + + + /* + FIXME: this complains about shit headers which are already sent because + of the content length. Would be cool if someone could fix this + + public function testOutputIsPrinted(){ + $return = array(null, array(), $this->output); + $this->dispatcher->expects($this->once()) + ->method('dispatch') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod)) + ->will($this->returnValue($return)); + + $this->expectOutputString($this->output); + + App::main($this->controllerName, $this->controllerMethod, array(), + $this->container); + } + */ + + // FIXME: if someone manages to test the headers output, I'd be grateful + + +} diff --git a/tests/lib/appframework/classloader.php b/tests/lib/appframework/classloader.php new file mode 100644 index 0000000000..ae485e67b2 --- /dev/null +++ b/tests/lib/appframework/classloader.php @@ -0,0 +1,45 @@ +. + * + */ + +// to execute without ownCloud, we need to create our own class loader +spl_autoload_register(function ($className){ + if (strpos($className, 'OC\\AppFramework') === 0) { + $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); + $relPath = __DIR__ . '/../../../lib/' . $path; + + if(file_exists($relPath)){ + require_once $relPath; + } + } + + // FIXME: this will most probably not work anymore + if (strpos($className, 'OCA\\') === 0) { + + $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); + $relPath = __DIR__ . '/../..' . $path; + + if(file_exists($relPath)){ + require_once $relPath; + } + } +}); diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php new file mode 100644 index 0000000000..d8357c2a68 --- /dev/null +++ b/tests/lib/appframework/controller/ControllerTest.php @@ -0,0 +1,161 @@ +. + * + */ + + +namespace Test\AppFramework\Controller; + +use OC\AppFramework\Http\Request; +use OC\AppFramework\Http\JSONResponse; +use OC\AppFramework\Http\TemplateResponse; +use OC\AppFramework\Controller\Controller; + + +require_once(__DIR__ . "/../classloader.php"); + + +class ChildController extends Controller {}; + +class ControllerTest extends \PHPUnit_Framework_TestCase { + + /** + * @var Controller + */ + private $controller; + private $api; + + protected function setUp(){ + $request = new Request( + array( + 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'), + 'post' => array('name' => 'Jane Doe', 'nickname' => 'Janey'), + 'urlParams' => array('name' => 'Johnny Weissmüller'), + 'files' => array('file' => 'filevalue'), + 'env' => array('PATH' => 'daheim'), + 'session' => array('sezession' => 'kein'), + 'method' => 'hi', + ) + ); + + $this->api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName'), array('test')); + $this->api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('apptemplate_advanced')); + + $this->controller = new ChildController($this->api, $request); + } + + + public function testParamsGet(){ + $this->assertEquals('Johnny Weissmüller', $this->controller->params('name', 'Tarzan')); + } + + + public function testParamsGetDefault(){ + $this->assertEquals('Tarzan', $this->controller->params('Ape Man', 'Tarzan')); + } + + + public function testParamsFile(){ + $this->assertEquals('filevalue', $this->controller->params('file', 'filevalue')); + } + + + public function testGetUploadedFile(){ + $this->assertEquals('filevalue', $this->controller->getUploadedFile('file')); + } + + + + public function testGetUploadedFileDefault(){ + $this->assertEquals('default', $this->controller->params('files', 'default')); + } + + + public function testGetParams(){ + $params = array( + 'name' => 'Johnny Weissmüller', + 'nickname' => 'Janey', + ); + + $this->assertEquals($params, $this->controller->getParams()); + } + + + public function testRender(){ + $this->assertTrue($this->controller->render('') instanceof TemplateResponse); + } + + + public function testSetParams(){ + $params = array('john' => 'foo'); + $response = $this->controller->render('home', $params); + + $this->assertEquals($params, $response->getParams()); + } + + + public function testRenderRenderAs(){ + $ocTpl = $this->getMock('Template', array('fetchPage')); + $ocTpl->expects($this->once()) + ->method('fetchPage'); + + $api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName', 'getTemplate'), array('app')); + $api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('app')); + $api->expects($this->once()) + ->method('getTemplate') + ->with($this->equalTo('home'), $this->equalTo('admin'), $this->equalTo('app')) + ->will($this->returnValue($ocTpl)); + + $this->controller = new ChildController($api, new Request()); + $this->controller->render('home', array(), 'admin')->render(); + } + + + public function testRenderHeaders(){ + $headers = array('one', 'two'); + $response = $this->controller->render('', array(), '', $headers); + + $this->assertTrue(in_array($headers[0], $response->getHeaders())); + $this->assertTrue(in_array($headers[1], $response->getHeaders())); + } + + + public function testGetRequestMethod(){ + $this->assertEquals('hi', $this->controller->method()); + } + + + public function testGetEnvVariable(){ + $this->assertEquals('daheim', $this->controller->env('PATH')); + } + + public function testGetSessionVariable(){ + $this->assertEquals('kein', $this->controller->session('sezession')); + } + + +} diff --git a/tests/lib/appframework/dependencyinjection/DIContainerTest.php b/tests/lib/appframework/dependencyinjection/DIContainerTest.php new file mode 100644 index 0000000000..ce346f0a76 --- /dev/null +++ b/tests/lib/appframework/dependencyinjection/DIContainerTest.php @@ -0,0 +1,98 @@ +. + * + */ + + +namespace OC\AppFramework\DependencyInjection; + +use \OC\AppFramework\Http\Request; + + +require_once(__DIR__ . "/../classloader.php"); + + +class DIContainerTest extends \PHPUnit_Framework_TestCase { + + private $container; + + protected function setUp(){ + $this->container = new DIContainer('name'); + $this->api = $this->getMock('OC\AppFramework\Core\API', array('getTrans'), array('hi')); + } + + private function exchangeAPI(){ + $this->api->expects($this->any()) + ->method('getTrans') + ->will($this->returnValue('yo')); + $this->container['API'] = $this->api; + } + + public function testProvidesAPI(){ + $this->assertTrue(isset($this->container['API'])); + } + + + public function testProvidesRequest(){ + $this->assertTrue(isset($this->container['Request'])); + } + + + public function testProvidesSecurityMiddleware(){ + $this->assertTrue(isset($this->container['SecurityMiddleware'])); + } + + + public function testProvidesMiddlewareDispatcher(){ + $this->assertTrue(isset($this->container['MiddlewareDispatcher'])); + } + + + public function testProvidesAppName(){ + $this->assertTrue(isset($this->container['AppName'])); + } + + + public function testAppNameIsSetCorrectly(){ + $this->assertEquals('name', $this->container['AppName']); + } + + + public function testMiddlewareDispatcherIncludesSecurityMiddleware(){ + $this->container['Request'] = new Request(); + $security = $this->container['SecurityMiddleware']; + $dispatcher = $this->container['MiddlewareDispatcher']; + + $this->assertContains($security, $dispatcher->getMiddlewares()); + } + + + public function testMiddlewareDispatcherDoesNotIncludeTwigWhenTplDirectoryNotSet(){ + $this->container['Request'] = new Request(); + $this->exchangeAPI(); + $dispatcher = $this->container['MiddlewareDispatcher']; + + $this->assertEquals(1, count($dispatcher->getMiddlewares())); + } + +} diff --git a/tests/lib/appframework/http/DispatcherTest.php b/tests/lib/appframework/http/DispatcherTest.php new file mode 100644 index 0000000000..2e3db11050 --- /dev/null +++ b/tests/lib/appframework/http/DispatcherTest.php @@ -0,0 +1,218 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + +use OC\AppFramework\Core\API; +use OC\AppFramework\Middleware\MiddlewareDispatcher; + +require_once(__DIR__ . "/../classloader.php"); + + +class DispatcherTest extends \PHPUnit_Framework_TestCase { + + + private $middlewareDispatcher; + private $dispatcher; + private $controllerMethod; + private $response; + private $lastModified; + private $etag; + private $http; + + protected function setUp() { + $this->controllerMethod = 'test'; + + $api = $this->getMockBuilder( + '\OC\AppFramework\Core\API') + ->disableOriginalConstructor() + ->getMock(); + $request = $this->getMockBuilder( + '\OC\AppFramework\Http\Request') + ->disableOriginalConstructor() + ->getMock(); + $this->http = $this->getMockBuilder( + '\OC\AppFramework\Http\Http') + ->disableOriginalConstructor() + ->getMock(); + + $this->middlewareDispatcher = $this->getMockBuilder( + '\OC\AppFramework\Middleware\MiddlewareDispatcher') + ->disableOriginalConstructor() + ->getMock(); + $this->controller = $this->getMock( + '\OC\AppFramework\Controller\Controller', + array($this->controllerMethod), array($api, $request)); + + $this->dispatcher = new Dispatcher( + $this->http, $this->middlewareDispatcher); + + $this->response = $this->getMockBuilder( + '\OC\AppFramework\Http\Response') + ->disableOriginalConstructor() + ->getMock(); + + $this->lastModified = new \DateTime(null, new \DateTimeZone('GMT')); + $this->etag = 'hi'; + } + + + private function setMiddlewareExpections($out=null, + $httpHeaders=null, $responseHeaders=array(), + $ex=false, $catchEx=true) { + + if($ex) { + $exception = new \Exception(); + $this->middlewareDispatcher->expects($this->once()) + ->method('beforeController') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod)) + ->will($this->throwException($exception)); + if($catchEx) { + $this->middlewareDispatcher->expects($this->once()) + ->method('afterException') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod), + $this->equalTo($exception)) + ->will($this->returnValue($this->response)); + } else { + $this->middlewareDispatcher->expects($this->once()) + ->method('afterException') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod), + $this->equalTo($exception)) + ->will($this->returnValue(null)); + return; + } + } else { + $this->middlewareDispatcher->expects($this->once()) + ->method('beforeController') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod)); + $this->controller->expects($this->once()) + ->method($this->controllerMethod) + ->will($this->returnValue($this->response)); + } + + $this->response->expects($this->once()) + ->method('render') + ->will($this->returnValue($out)); + $this->response->expects($this->once()) + ->method('getStatus') + ->will($this->returnValue(Http::STATUS_OK)); + $this->response->expects($this->once()) + ->method('getLastModified') + ->will($this->returnValue($this->lastModified)); + $this->response->expects($this->once()) + ->method('getETag') + ->will($this->returnValue($this->etag)); + $this->response->expects($this->once()) + ->method('getHeaders') + ->will($this->returnValue($responseHeaders)); + $this->http->expects($this->once()) + ->method('getStatusHeader') + ->with($this->equalTo(Http::STATUS_OK), + $this->equalTo($this->lastModified), + $this->equalTo($this->etag)) + ->will($this->returnValue($httpHeaders)); + + $this->middlewareDispatcher->expects($this->once()) + ->method('afterController') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod), + $this->equalTo($this->response)) + ->will($this->returnValue($this->response)); + + $this->middlewareDispatcher->expects($this->once()) + ->method('afterController') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod), + $this->equalTo($this->response)) + ->will($this->returnValue($this->response)); + + $this->middlewareDispatcher->expects($this->once()) + ->method('beforeOutput') + ->with($this->equalTo($this->controller), + $this->equalTo($this->controllerMethod), + $this->equalTo($out)) + ->will($this->returnValue($out)); + + + } + + + public function testDispatcherReturnsArrayWith2Entries() { + $this->setMiddlewareExpections(); + + $response = $this->dispatcher->dispatch($this->controller, + $this->controllerMethod); + $this->assertNull($response[0]); + $this->assertEquals(array(), $response[1]); + $this->assertNull($response[2]); + } + + + public function testHeadersAndOutputAreReturned(){ + $out = 'yo'; + $httpHeaders = 'Http'; + $responseHeaders = array('hell' => 'yeah'); + $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders); + + $response = $this->dispatcher->dispatch($this->controller, + $this->controllerMethod); + + $this->assertEquals($httpHeaders, $response[0]); + $this->assertEquals($responseHeaders, $response[1]); + $this->assertEquals($out, $response[2]); + } + + + public function testExceptionCallsAfterException() { + $out = 'yo'; + $httpHeaders = 'Http'; + $responseHeaders = array('hell' => 'yeah'); + $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders, true); + + $response = $this->dispatcher->dispatch($this->controller, + $this->controllerMethod); + + $this->assertEquals($httpHeaders, $response[0]); + $this->assertEquals($responseHeaders, $response[1]); + $this->assertEquals($out, $response[2]); + } + + + public function testExceptionThrowsIfCanNotBeHandledByAfterException() { + $out = 'yo'; + $httpHeaders = 'Http'; + $responseHeaders = array('hell' => 'yeah'); + $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders, true, false); + + $this->setExpectedException('\Exception'); + $response = $this->dispatcher->dispatch($this->controller, + $this->controllerMethod); + + } + +} diff --git a/tests/lib/appframework/http/DownloadResponseTest.php b/tests/lib/appframework/http/DownloadResponseTest.php new file mode 100644 index 0000000000..103cfe7588 --- /dev/null +++ b/tests/lib/appframework/http/DownloadResponseTest.php @@ -0,0 +1,51 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +require_once(__DIR__ . "/../classloader.php"); + + +class ChildDownloadResponse extends DownloadResponse {}; + + +class DownloadResponseTest extends \PHPUnit_Framework_TestCase { + + protected $response; + + protected function setUp(){ + $this->response = new ChildDownloadResponse('file', 'content'); + } + + + public function testHeaders() { + $headers = $this->response->getHeaders(); + + $this->assertContains('attachment; filename="file"', $headers['Content-Disposition']); + $this->assertContains('content', $headers['Content-Type']); + } + + +} diff --git a/tests/lib/appframework/http/HttpTest.php b/tests/lib/appframework/http/HttpTest.php new file mode 100644 index 0000000000..306bc3caf4 --- /dev/null +++ b/tests/lib/appframework/http/HttpTest.php @@ -0,0 +1,87 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +require_once(__DIR__ . "/../classloader.php"); + + + +class HttpTest extends \PHPUnit_Framework_TestCase { + + private $server; + private $http; + + protected function setUp(){ + $this->server = array(); + $this->http = new Http($this->server); + } + + + public function testProtocol() { + $header = $this->http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT); + $this->assertEquals('HTTP/1.1 307 Temporary Redirect', $header); + } + + + public function testProtocol10() { + $this->http = new Http($this->server, 'HTTP/1.0'); + $header = $this->http->getStatusHeader(Http::STATUS_OK); + $this->assertEquals('HTTP/1.0 200 OK', $header); + } + + + public function testEtagMatchReturnsNotModified() { + $http = new Http(array('HTTP_IF_NONE_MATCH' => 'hi')); + + $header = $http->getStatusHeader(Http::STATUS_OK, null, 'hi'); + $this->assertEquals('HTTP/1.1 304 Not Modified', $header); + } + + + public function testLastModifiedMatchReturnsNotModified() { + $dateTime = new \DateTime(null, new \DateTimeZone('GMT')); + $dateTime->setTimestamp('12'); + + $http = new Http( + array( + 'HTTP_IF_MODIFIED_SINCE' => 'Thu, 01 Jan 1970 00:00:12 +0000') + ); + + $header = $http->getStatusHeader(Http::STATUS_OK, $dateTime); + $this->assertEquals('HTTP/1.1 304 Not Modified', $header); + } + + + + public function testTempRedirectBecomesFoundInHttp10() { + $http = new Http(array(), 'HTTP/1.0'); + + $header = $http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT); + $this->assertEquals('HTTP/1.0 302 Found', $header); + } + // TODO: write unittests for http codes + +} diff --git a/tests/lib/appframework/http/JSONResponseTest.php b/tests/lib/appframework/http/JSONResponseTest.php new file mode 100644 index 0000000000..d15e08f6ce --- /dev/null +++ b/tests/lib/appframework/http/JSONResponseTest.php @@ -0,0 +1,96 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +require_once(__DIR__ . "/../classloader.php"); + + + +class JSONResponseTest extends \PHPUnit_Framework_TestCase { + + /** + * @var JSONResponse + */ + private $json; + + protected function setUp() { + $this->json = new JSONResponse(); + } + + + public function testHeader() { + $headers = $this->json->getHeaders(); + $this->assertEquals('application/json; charset=utf-8', $headers['Content-type']); + } + + + public function testSetData() { + $params = array('hi', 'yo'); + $this->json->setData($params); + + $this->assertEquals(array('hi', 'yo'), $this->json->getData()); + } + + + public function testSetRender() { + $params = array('test' => 'hi'); + $this->json->setData($params); + + $expected = '{"test":"hi"}'; + + $this->assertEquals($expected, $this->json->render()); + } + + + public function testRender() { + $params = array('test' => 'hi'); + $this->json->setData($params); + + $expected = '{"test":"hi"}'; + + $this->assertEquals($expected, $this->json->render()); + } + + + public function testShouldHaveXContentHeaderByDefault() { + $headers = $this->json->getHeaders(); + $this->assertEquals('nosniff', $headers['X-Content-Type-Options']); + } + + + public function testConstructorAllowsToSetData() { + $data = array('hi'); + $code = 300; + $response = new JSONResponse($data, $code); + + $expected = '["hi"]'; + $this->assertEquals($expected, $response->render()); + $this->assertEquals($code, $response->getStatus()); + } + +} diff --git a/tests/lib/appframework/http/RedirectResponseTest.php b/tests/lib/appframework/http/RedirectResponseTest.php new file mode 100644 index 0000000000..a8577feed2 --- /dev/null +++ b/tests/lib/appframework/http/RedirectResponseTest.php @@ -0,0 +1,55 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +require_once(__DIR__ . "/../classloader.php"); + + + +class RedirectResponseTest extends \PHPUnit_Framework_TestCase { + + + protected $response; + + protected function setUp(){ + $this->response = new RedirectResponse('/url'); + } + + + public function testHeaders() { + $headers = $this->response->getHeaders(); + $this->assertEquals('/url', $headers['Location']); + $this->assertEquals(Http::STATUS_TEMPORARY_REDIRECT, + $this->response->getStatus()); + } + + + public function testGetRedirectUrl(){ + $this->assertEquals('/url', $this->response->getRedirectUrl()); + } + + +} diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php new file mode 100644 index 0000000000..c1f56c0163 --- /dev/null +++ b/tests/lib/appframework/http/RequestTest.php @@ -0,0 +1,78 @@ + array('name' => 'John Q. Public', 'nickname' => 'Joey'), + ); + + $request = new Request($vars); + + // Countable + $this->assertEquals(2, count($request)); + // Array access + $this->assertEquals('Joey', $request['nickname']); + // "Magic" accessors + $this->assertEquals('Joey', $request->{'nickname'}); + $this->assertTrue(isset($request['nickname'])); + $this->assertTrue(isset($request->{'nickname'})); + $this->assertEquals(false, isset($request->{'flickname'})); + // Only testing 'get', but same approach for post, files etc. + $this->assertEquals('Joey', $request->get['nickname']); + // Always returns null if variable not set. + $this->assertEquals(null, $request->{'flickname'}); + } + + // urlParams has precedence over POST which has precedence over GET + public function testPrecedence() { + $vars = array( + 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'), + 'post' => array('name' => 'Jane Doe', 'nickname' => 'Janey'), + 'urlParams' => array('user' => 'jw', 'name' => 'Johnny Weissmüller'), + ); + + $request = new Request($vars); + + $this->assertEquals(3, count($request)); + $this->assertEquals('Janey', $request->{'nickname'}); + $this->assertEquals('Johnny Weissmüller', $request->{'name'}); + } + + + /** + * @expectedException RuntimeException + */ + public function testImmutableArrayAccess() { + $vars = array( + 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'), + ); + + $request = new Request($vars); + $request['nickname'] = 'Janey'; + } + + /** + * @expectedException RuntimeException + */ + public function testImmutableMagicAccess() { + $vars = array( + 'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'), + ); + + $request = new Request($vars); + $request->{'nickname'} = 'Janey'; + } + +} diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php new file mode 100644 index 0000000000..621ba66545 --- /dev/null +++ b/tests/lib/appframework/http/ResponseTest.php @@ -0,0 +1,119 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + + +require_once(__DIR__ . "/../classloader.php"); + + + +class ResponseTest extends \PHPUnit_Framework_TestCase { + + + private $childResponse; + + protected function setUp(){ + $this->childResponse = new Response(); + } + + + public function testAddHeader(){ + $this->childResponse->addHeader('hello', 'world'); + $headers = $this->childResponse->getHeaders(); + $this->assertEquals('world', $headers['hello']); + } + + + public function testAddHeaderValueNullDeletesIt(){ + $this->childResponse->addHeader('hello', 'world'); + $this->childResponse->addHeader('hello', null); + $this->assertEquals(1, count($this->childResponse->getHeaders())); + } + + + public function testCacheHeadersAreDisabledByDefault(){ + $headers = $this->childResponse->getHeaders(); + $this->assertEquals('no-cache, must-revalidate', $headers['Cache-Control']); + } + + + public function testRenderReturnNullByDefault(){ + $this->assertEquals(null, $this->childResponse->render()); + } + + + public function testGetStatus() { + $default = $this->childResponse->getStatus(); + + $this->childResponse->setStatus(Http::STATUS_NOT_FOUND); + + $this->assertEquals(Http::STATUS_OK, $default); + $this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus()); + } + + + public function testGetEtag() { + $this->childResponse->setEtag('hi'); + $this->assertEquals('hi', $this->childResponse->getEtag()); + } + + + public function testGetLastModified() { + $lastModified = new \DateTime(null, new \DateTimeZone('GMT')); + $lastModified->setTimestamp(1); + $this->childResponse->setLastModified($lastModified); + $this->assertEquals($lastModified, $this->childResponse->getLastModified()); + } + + + + public function testCacheSecondsZero() { + $this->childResponse->cacheFor(0); + + $headers = $this->childResponse->getHeaders(); + $this->assertEquals('no-cache, must-revalidate', $headers['Cache-Control']); + } + + + public function testCacheSeconds() { + $this->childResponse->cacheFor(33); + + $headers = $this->childResponse->getHeaders(); + $this->assertEquals('max-age=33, must-revalidate', + $headers['Cache-Control']); + } + + + + public function testEtagLastModifiedHeaders() { + $lastModified = new \DateTime(null, new \DateTimeZone('GMT')); + $lastModified->setTimestamp(1); + $this->childResponse->setLastModified($lastModified); + $headers = $this->childResponse->getHeaders(); + $this->assertEquals('Thu, 01 Jan 1970 00:00:01 +0000', $headers['Last-Modified']); + } + + +} diff --git a/tests/lib/appframework/http/TemplateResponseTest.php b/tests/lib/appframework/http/TemplateResponseTest.php new file mode 100644 index 0000000000..30684725b7 --- /dev/null +++ b/tests/lib/appframework/http/TemplateResponseTest.php @@ -0,0 +1,157 @@ +. + * + */ + + +namespace OC\AppFramework\Http; + +use OC\AppFramework\Core\API; + + +require_once(__DIR__ . "/../classloader.php"); + + +class TemplateResponseTest extends \PHPUnit_Framework_TestCase { + + private $tpl; + private $api; + + protected function setUp() { + $this->api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName'), array('test')); + $this->api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('app')); + + $this->tpl = new TemplateResponse($this->api, 'home'); + } + + + public function testSetParams(){ + $params = array('hi' => 'yo'); + $this->tpl->setParams($params); + + $this->assertEquals(array('hi' => 'yo'), $this->tpl->getParams()); + } + + + public function testGetTemplateName(){ + $this->assertEquals('home', $this->tpl->getTemplateName()); + } + + + public function testRender(){ + $ocTpl = $this->getMock('Template', array('fetchPage')); + $ocTpl->expects($this->once()) + ->method('fetchPage'); + + $api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName', 'getTemplate'), array('app')); + $api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('app')); + $api->expects($this->once()) + ->method('getTemplate') + ->with($this->equalTo('home'), $this->equalTo('user'), $this->equalTo('app')) + ->will($this->returnValue($ocTpl)); + + $tpl = new TemplateResponse($api, 'home'); + + $tpl->render(); + } + + + public function testRenderAssignsParams(){ + $params = array('john' => 'doe'); + + $ocTpl = $this->getMock('Template', array('assign', 'fetchPage')); + $ocTpl->expects($this->once()) + ->method('assign') + ->with($this->equalTo('john'), $this->equalTo('doe')); + + $api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName', 'getTemplate'), array('app')); + $api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('app')); + $api->expects($this->once()) + ->method('getTemplate') + ->with($this->equalTo('home'), $this->equalTo('user'), $this->equalTo('app')) + ->will($this->returnValue($ocTpl)); + + $tpl = new TemplateResponse($api, 'home'); + $tpl->setParams($params); + + $tpl->render(); + } + + + public function testRenderDifferentApp(){ + $ocTpl = $this->getMock('Template', array('fetchPage')); + $ocTpl->expects($this->once()) + ->method('fetchPage'); + + $api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName', 'getTemplate'), array('app')); + $api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('app')); + $api->expects($this->once()) + ->method('getTemplate') + ->with($this->equalTo('home'), $this->equalTo('user'), $this->equalTo('app2')) + ->will($this->returnValue($ocTpl)); + + $tpl = new TemplateResponse($api, 'home', 'app2'); + + $tpl->render(); + } + + + public function testRenderDifferentRenderAs(){ + $ocTpl = $this->getMock('Template', array('fetchPage')); + $ocTpl->expects($this->once()) + ->method('fetchPage'); + + $api = $this->getMock('OC\AppFramework\Core\API', + array('getAppName', 'getTemplate'), array('app')); + $api->expects($this->any()) + ->method('getAppName') + ->will($this->returnValue('app')); + $api->expects($this->once()) + ->method('getTemplate') + ->with($this->equalTo('home'), $this->equalTo('admin'), $this->equalTo('app')) + ->will($this->returnValue($ocTpl)); + + $tpl = new TemplateResponse($api, 'home'); + $tpl->renderAs('admin'); + + $tpl->render(); + } + + + public function testGetRenderAs(){ + $render = 'myrender'; + $this->tpl->renderAs($render); + $this->assertEquals($render, $this->tpl->getRenderAs()); + } + +} diff --git a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php new file mode 100644 index 0000000000..bfa54a48ea --- /dev/null +++ b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php @@ -0,0 +1,280 @@ +. + * + */ + + +namespace OC\AppFramework; + +use OC\AppFramework\Controller\Controller; +use OC\AppFramework\Http\Request; +use OC\AppFramework\Http\Response; +use OC\AppFramework\Middleware\Middleware; +use OC\AppFramework\Middleware\MiddlewareDispatcher; + + +require_once(__DIR__ . "/../classloader.php"); + + +// needed to test ordering +class TestMiddleware extends Middleware { + public static $beforeControllerCalled = 0; + public static $afterControllerCalled = 0; + public static $afterExceptionCalled = 0; + public static $beforeOutputCalled = 0; + + public $beforeControllerOrder = 0; + public $afterControllerOrder = 0; + public $afterExceptionOrder = 0; + public $beforeOutputOrder = 0; + + public $controller; + public $methodName; + public $exception; + public $response; + public $output; + + private $beforeControllerThrowsEx; + + public function __construct($beforeControllerThrowsEx) { + self::$beforeControllerCalled = 0; + self::$afterControllerCalled = 0; + self::$afterExceptionCalled = 0; + self::$beforeOutputCalled = 0; + $this->beforeControllerThrowsEx = $beforeControllerThrowsEx; + } + + public function beforeController($controller, $methodName){ + self::$beforeControllerCalled++; + $this->beforeControllerOrder = self::$beforeControllerCalled; + $this->controller = $controller; + $this->methodName = $methodName; + if($this->beforeControllerThrowsEx){ + throw new \Exception(); + } + } + + public function afterException($controller, $methodName, \Exception $exception){ + self::$afterExceptionCalled++; + $this->afterExceptionOrder = self::$afterExceptionCalled; + $this->controller = $controller; + $this->methodName = $methodName; + $this->exception = $exception; + parent::afterException($controller, $methodName, $exception); + } + + public function afterController($controller, $methodName, Response $response){ + self::$afterControllerCalled++; + $this->afterControllerOrder = self::$afterControllerCalled; + $this->controller = $controller; + $this->methodName = $methodName; + $this->response = $response; + return parent::afterController($controller, $methodName, $response); + } + + public function beforeOutput($controller, $methodName, $output){ + self::$beforeOutputCalled++; + $this->beforeOutputOrder = self::$beforeOutputCalled; + $this->controller = $controller; + $this->methodName = $methodName; + $this->output = $output; + return parent::beforeOutput($controller, $methodName, $output); + } +} + + +class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase { + + private $dispatcher; + + + public function setUp() { + $this->dispatcher = new MiddlewareDispatcher(); + $this->controller = $this->getControllerMock(); + $this->method = 'method'; + $this->response = new Response(); + $this->output = 'hi'; + $this->exception = new \Exception(); + } + + + private function getAPIMock(){ + return $this->getMock('OC\AppFramework\Core\API', + array('getAppName'), array('app')); + } + + + private function getControllerMock(){ + return $this->getMock('OC\AppFramework\Controller\Controller', array('method'), + array($this->getAPIMock(), new Request())); + } + + + private function getMiddleware($beforeControllerThrowsEx=false){ + $m1 = new TestMiddleware($beforeControllerThrowsEx); + $this->dispatcher->registerMiddleware($m1); + return $m1; + } + + + public function testAfterExceptionShouldReturnResponseOfMiddleware(){ + $response = new Response(); + $m1 = $this->getMock('\OC\AppFramework\Middleware\Middleware', + array('afterException', 'beforeController')); + $m1->expects($this->never()) + ->method('afterException'); + + $m2 = $this->getMock('OC\AppFramework\Middleware\Middleware', + array('afterException', 'beforeController')); + $m2->expects($this->once()) + ->method('afterException') + ->will($this->returnValue($response)); + + $this->dispatcher->registerMiddleware($m1); + $this->dispatcher->registerMiddleware($m2); + + $this->dispatcher->beforeController($this->controller, $this->method); + $this->assertEquals($response, $this->dispatcher->afterException($this->controller, $this->method, $this->exception)); + } + + + public function testAfterExceptionShouldThrowAgainWhenNotHandled(){ + $m1 = new TestMiddleware(false); + $m2 = new TestMiddleware(true); + + $this->dispatcher->registerMiddleware($m1); + $this->dispatcher->registerMiddleware($m2); + + $this->setExpectedException('\Exception'); + $this->dispatcher->beforeController($this->controller, $this->method); + $this->dispatcher->afterException($this->controller, $this->method, $this->exception); + } + + + public function testBeforeControllerCorrectArguments(){ + $m1 = $this->getMiddleware(); + $this->dispatcher->beforeController($this->controller, $this->method); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + } + + + public function testAfterControllerCorrectArguments(){ + $m1 = $this->getMiddleware(); + + $this->dispatcher->afterController($this->controller, $this->method, $this->response); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + $this->assertEquals($this->response, $m1->response); + } + + + public function testAfterExceptionCorrectArguments(){ + $m1 = $this->getMiddleware(); + + $this->setExpectedException('\Exception'); + + $this->dispatcher->beforeController($this->controller, $this->method); + $this->dispatcher->afterException($this->controller, $this->method, $this->exception); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + $this->assertEquals($this->exception, $m1->exception); + } + + + public function testBeforeOutputCorrectArguments(){ + $m1 = $this->getMiddleware(); + + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + $this->assertEquals($this->output, $m1->output); + } + + + public function testBeforeControllerOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->dispatcher->beforeController($this->controller, $this->method); + + $this->assertEquals(1, $m1->beforeControllerOrder); + $this->assertEquals(2, $m2->beforeControllerOrder); + } + + public function testAfterControllerOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->dispatcher->afterController($this->controller, $this->method, $this->response); + + $this->assertEquals(2, $m1->afterControllerOrder); + $this->assertEquals(1, $m2->afterControllerOrder); + } + + + public function testAfterExceptionOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->setExpectedException('\Exception'); + $this->dispatcher->beforeController($this->controller, $this->method); + $this->dispatcher->afterException($this->controller, $this->method, $this->exception); + + $this->assertEquals(1, $m1->afterExceptionOrder); + $this->assertEquals(1, $m2->afterExceptionOrder); + } + + + public function testBeforeOutputOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output); + + $this->assertEquals(2, $m1->beforeOutputOrder); + $this->assertEquals(1, $m2->beforeOutputOrder); + } + + + public function testExceptionShouldRunAfterExceptionOfOnlyPreviouslyExecutedMiddlewares(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(true); + $m3 = $this->getMock('\OC\AppFramework\Middleware\Middleware'); + $m3->expects($this->never()) + ->method('afterException'); + $m3->expects($this->never()) + ->method('beforeController'); + $m3->expects($this->never()) + ->method('afterController'); + + $this->dispatcher->registerMiddleware($m3); + + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output); + + $this->assertEquals(2, $m1->beforeOutputOrder); + $this->assertEquals(1, $m2->beforeOutputOrder); + } +} diff --git a/tests/lib/appframework/middleware/MiddlewareTest.php b/tests/lib/appframework/middleware/MiddlewareTest.php new file mode 100644 index 0000000000..1adce6b3d4 --- /dev/null +++ b/tests/lib/appframework/middleware/MiddlewareTest.php @@ -0,0 +1,82 @@ +. + * + */ + + +namespace OC\AppFramework; + +use OC\AppFramework\Http\Request; +use OC\AppFramework\Middleware\Middleware; + + +require_once(__DIR__ . "/../classloader.php"); + + +class ChildMiddleware extends Middleware {}; + + +class MiddlewareTest extends \PHPUnit_Framework_TestCase { + + private $middleware; + private $controller; + private $exception; + private $api; + + protected function setUp(){ + $this->middleware = new ChildMiddleware(); + + $this->api = $this->getMock('OC\AppFramework\Core\API', + array(), array('test')); + + $this->controller = $this->getMock('OC\AppFramework\Controller\Controller', + array(), array($this->api, new Request())); + $this->exception = new \Exception(); + $this->response = $this->getMock('OC\AppFramework\Http\Response'); + } + + + public function testBeforeController() { + $this->middleware->beforeController($this->controller, null, $this->exception); + } + + + public function testAfterExceptionRaiseAgainWhenUnhandled() { + $this->setExpectedException('Exception'); + $afterEx = $this->middleware->afterException($this->controller, null, $this->exception); + } + + + public function testAfterControllerReturnResponseWhenUnhandled() { + $response = $this->middleware->afterController($this->controller, null, $this->response); + + $this->assertEquals($this->response, $response); + } + + + public function testBeforeOutputReturnOutputhenUnhandled() { + $output = $this->middleware->beforeOutput($this->controller, null, 'test'); + + $this->assertEquals('test', $output); + } + + +} diff --git a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php new file mode 100644 index 0000000000..0b2103564e --- /dev/null +++ b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php @@ -0,0 +1,388 @@ +. + * + */ + + +namespace OC\AppFramework\Middleware\Security; + +use OC\AppFramework\Http\Http; +use OC\AppFramework\Http\Request; +use OC\AppFramework\Http\RedirectResponse; +use OC\AppFramework\Http\JSONResponse; +use OC\AppFramework\Middleware\Middleware; + + +require_once(__DIR__ . "/../../classloader.php"); + + +class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { + + private $middleware; + private $controller; + private $secException; + private $secAjaxException; + private $request; + + public function setUp() { + $api = $this->getMock('OC\AppFramework\Core\API', array(), array('test')); + $this->controller = $this->getMock('OC\AppFramework\Controller\Controller', + array(), array($api, new Request())); + + $this->request = new Request(); + $this->middleware = new SecurityMiddleware($api, $this->request); + $this->secException = new SecurityException('hey', false); + $this->secAjaxException = new SecurityException('hey', true); + } + + + private function getAPI(){ + return $this->getMock('OC\AppFramework\Core\API', + array('isLoggedIn', 'passesCSRFCheck', 'isAdminUser', + 'isSubAdminUser', 'activateNavigationEntry', + 'getUserId'), + array('app')); + } + + + private function checkNavEntry($method, $shouldBeActivated=false){ + $api = $this->getAPI(); + + if($shouldBeActivated){ + $api->expects($this->once()) + ->method('activateNavigationEntry'); + } else { + $api->expects($this->never()) + ->method('activateNavigationEntry'); + } + + $sec = new SecurityMiddleware($api, $this->request); + $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method); + } + + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testSetNavigationEntry(){ + $this->checkNavEntry('testSetNavigationEntry', true); + } + + + private function ajaxExceptionCheck($method, $shouldBeAjax=false){ + $api = $this->getAPI(); + $api->expects($this->any()) + ->method('passesCSRFCheck') + ->will($this->returnValue(false)); + + $sec = new SecurityMiddleware($api, $this->request); + + try { + $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', + $method); + } catch (SecurityException $ex){ + if($shouldBeAjax){ + $this->assertTrue($ex->isAjax()); + } else { + $this->assertFalse($ex->isAjax()); + } + + } + } + + + /** + * @Ajax + * @IsLoggedInExemption + * @CSRFExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testAjaxException(){ + $this->ajaxExceptionCheck('testAjaxException'); + } + + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testNoAjaxException(){ + $this->ajaxExceptionCheck('testNoAjaxException'); + } + + + private function ajaxExceptionStatus($method, $test, $status) { + $api = $this->getAPI(); + $api->expects($this->any()) + ->method($test) + ->will($this->returnValue(false)); + + $sec = new SecurityMiddleware($api, $this->request); + + try { + $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', + $method); + } catch (SecurityException $ex){ + $this->assertEquals($status, $ex->getCode()); + } + } + + /** + * @Ajax + */ + public function testAjaxStatusLoggedInCheck() { + $this->ajaxExceptionStatus( + 'testAjaxStatusLoggedInCheck', + 'isLoggedIn', + Http::STATUS_UNAUTHORIZED + ); + } + + /** + * @Ajax + * @IsLoggedInExemption + */ + public function testAjaxNotAdminCheck() { + $this->ajaxExceptionStatus( + 'testAjaxNotAdminCheck', + 'isAdminUser', + Http::STATUS_FORBIDDEN + ); + } + + /** + * @Ajax + * @IsLoggedInExemption + * @IsAdminExemption + */ + public function testAjaxNotSubAdminCheck() { + $this->ajaxExceptionStatus( + 'testAjaxNotSubAdminCheck', + 'isSubAdminUser', + Http::STATUS_FORBIDDEN + ); + } + + /** + * @Ajax + * @IsLoggedInExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testAjaxStatusCSRFCheck() { + $this->ajaxExceptionStatus( + 'testAjaxStatusCSRFCheck', + 'passesCSRFCheck', + Http::STATUS_PRECONDITION_FAILED + ); + } + + /** + * @Ajax + * @CSRFExemption + * @IsLoggedInExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testAjaxStatusAllGood() { + $this->ajaxExceptionStatus( + 'testAjaxStatusAllGood', + 'isLoggedIn', + 0 + ); + $this->ajaxExceptionStatus( + 'testAjaxStatusAllGood', + 'isAdminUser', + 0 + ); + $this->ajaxExceptionStatus( + 'testAjaxStatusAllGood', + 'isSubAdminUser', + 0 + ); + $this->ajaxExceptionStatus( + 'testAjaxStatusAllGood', + 'passesCSRFCheck', + 0 + ); + } + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testNoChecks(){ + $api = $this->getAPI(); + $api->expects($this->never()) + ->method('passesCSRFCheck') + ->will($this->returnValue(true)); + $api->expects($this->never()) + ->method('isAdminUser') + ->will($this->returnValue(true)); + $api->expects($this->never()) + ->method('isSubAdminUser') + ->will($this->returnValue(true)); + $api->expects($this->never()) + ->method('isLoggedIn') + ->will($this->returnValue(true)); + + $sec = new SecurityMiddleware($api, $this->request); + $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', + 'testNoChecks'); + } + + + private function securityCheck($method, $expects, $shouldFail=false){ + $api = $this->getAPI(); + $api->expects($this->once()) + ->method($expects) + ->will($this->returnValue(!$shouldFail)); + + $sec = new SecurityMiddleware($api, $this->request); + + if($shouldFail){ + $this->setExpectedException('\OC\AppFramework\Middleware\Security\SecurityException'); + } + + $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method); + } + + + /** + * @IsLoggedInExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testCsrfCheck(){ + $this->securityCheck('testCsrfCheck', 'passesCSRFCheck'); + } + + + /** + * @IsLoggedInExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testFailCsrfCheck(){ + $this->securityCheck('testFailCsrfCheck', 'passesCSRFCheck', true); + } + + + /** + * @CSRFExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testLoggedInCheck(){ + $this->securityCheck('testLoggedInCheck', 'isLoggedIn'); + } + + + /** + * @CSRFExemption + * @IsAdminExemption + * @IsSubAdminExemption + */ + public function testFailLoggedInCheck(){ + $this->securityCheck('testFailLoggedInCheck', 'isLoggedIn', true); + } + + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsSubAdminExemption + */ + public function testIsAdminCheck(){ + $this->securityCheck('testIsAdminCheck', 'isAdminUser'); + } + + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsSubAdminExemption + */ + public function testFailIsAdminCheck(){ + $this->securityCheck('testFailIsAdminCheck', 'isAdminUser', true); + } + + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsAdminExemption + */ + public function testIsSubAdminCheck(){ + $this->securityCheck('testIsSubAdminCheck', 'isSubAdminUser'); + } + + + /** + * @IsLoggedInExemption + * @CSRFExemption + * @IsAdminExemption + */ + public function testFailIsSubAdminCheck(){ + $this->securityCheck('testFailIsSubAdminCheck', 'isSubAdminUser', true); + } + + + + public function testAfterExceptionNotCaughtThrowsItAgain(){ + $ex = new \Exception(); + $this->setExpectedException('\Exception'); + $this->middleware->afterException($this->controller, 'test', $ex); + } + + + public function testAfterExceptionReturnsRedirect(){ + $api = $this->getMock('OC\AppFramework\Core\API', array(), array('test')); + $this->controller = $this->getMock('OC\AppFramework\Controller\Controller', + array(), array($api, new Request())); + + $this->request = new Request( + array('server' => array('HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'))); + $this->middleware = new SecurityMiddleware($api, $this->request); + $response = $this->middleware->afterException($this->controller, 'test', + $this->secException); + + $this->assertTrue($response instanceof RedirectResponse); + } + + + public function testAfterAjaxExceptionReturnsJSONError(){ + $response = $this->middleware->afterException($this->controller, 'test', + $this->secAjaxException); + + $this->assertTrue($response instanceof JSONResponse); + } + + +} diff --git a/tests/lib/appframework/routing/RoutingTest.php b/tests/lib/appframework/routing/RoutingTest.php new file mode 100644 index 0000000000..92ad461471 --- /dev/null +++ b/tests/lib/appframework/routing/RoutingTest.php @@ -0,0 +1,214 @@ + array( + array('name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET') + )); + + $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/folders/{folderId}/open', 'FoldersController', 'open'); + } + + public function testSimpleRouteWithMissingVerb() + { + $routes = array('routes' => array( + array('name' => 'folders#open', 'url' => '/folders/{folderId}/open') + )); + + $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/folders/{folderId}/open', 'FoldersController', 'open'); + } + + public function testSimpleRouteWithLowercaseVerb() + { + $routes = array('routes' => array( + array('name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete') + )); + + $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/folders/{folderId}/open', 'FoldersController', 'open'); + } + + /** + * @expectedException \UnexpectedValueException + */ + public function testSimpleRouteWithBrokenName() + { + $routes = array('routes' => array( + array('name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete') + )); + + // router mock + $router = $this->getMock("\OC_Router", array('create')); + + // load route configuration + $container = new DIContainer('app1'); + $config = new RouteConfig($container, $router, $routes); + + $config->register(); + } + + public function testSimpleRouteWithUnderScoreNames() + { + $routes = array('routes' => array( + array('name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete') + )); + + $this->assertSimpleRoute($routes, 'admin_folders.open_current', 'DELETE', '/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent'); + } + + public function testResource() + { + $routes = array('resources' => array('accounts' => array('url' => '/accounts'))); + + $this->assertResource($routes, 'accounts', '/accounts', 'AccountsController', 'accountId'); + } + + public function testResourceWithUnderScoreName() + { + $routes = array('resources' => array('admin_accounts' => array('url' => '/admin/accounts'))); + + $this->assertResource($routes, 'admin_accounts', '/admin/accounts', 'AdminAccountsController', 'adminAccountId'); + } + + private function assertSimpleRoute($routes, $name, $verb, $url, $controllerName, $actionName) + { + // route mocks + $route = $this->mockRoute($verb, $controllerName, $actionName); + + // router mock + $router = $this->getMock("\OC_Router", array('create')); + + // we expect create to be called once: + $router + ->expects($this->once()) + ->method('create') + ->with($this->equalTo('app1.' . $name), $this->equalTo($url)) + ->will($this->returnValue($route)); + + // load route configuration + $container = new DIContainer('app1'); + $config = new RouteConfig($container, $router, $routes); + + $config->register(); + } + + private function assertResource($yaml, $resourceName, $url, $controllerName, $paramName) + { + // router mock + $router = $this->getMock("\OC_Router", array('create')); + + // route mocks + $indexRoute = $this->mockRoute('GET', $controllerName, 'index'); + $showRoute = $this->mockRoute('GET', $controllerName, 'show'); + $createRoute = $this->mockRoute('POST', $controllerName, 'create'); + $updateRoute = $this->mockRoute('PUT', $controllerName, 'update'); + $destroyRoute = $this->mockRoute('DELETE', $controllerName, 'destroy'); + + $urlWithParam = $url . '/{' . $paramName . '}'; + + // we expect create to be called once: + $router + ->expects($this->at(0)) + ->method('create') + ->with($this->equalTo('app1.' . $resourceName . '.index'), $this->equalTo($url)) + ->will($this->returnValue($indexRoute)); + + $router + ->expects($this->at(1)) + ->method('create') + ->with($this->equalTo('app1.' . $resourceName . '.show'), $this->equalTo($urlWithParam)) + ->will($this->returnValue($showRoute)); + + $router + ->expects($this->at(2)) + ->method('create') + ->with($this->equalTo('app1.' . $resourceName . '.create'), $this->equalTo($url)) + ->will($this->returnValue($createRoute)); + + $router + ->expects($this->at(3)) + ->method('create') + ->with($this->equalTo('app1.' . $resourceName . '.update'), $this->equalTo($urlWithParam)) + ->will($this->returnValue($updateRoute)); + + $router + ->expects($this->at(4)) + ->method('create') + ->with($this->equalTo('app1.' . $resourceName . '.destroy'), $this->equalTo($urlWithParam)) + ->will($this->returnValue($destroyRoute)); + + // load route configuration + $container = new DIContainer('app1'); + $config = new RouteConfig($container, $router, $yaml); + + $config->register(); + } + + /** + * @param $verb + * @param $controllerName + * @param $actionName + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function mockRoute($verb, $controllerName, $actionName) + { + $container = new DIContainer('app1'); + $route = $this->getMock("\OC_Route", array('method', 'action'), array(), '', false); + $route + ->expects($this->exactly(1)) + ->method('method') + ->with($this->equalTo($verb)) + ->will($this->returnValue($route)); + + $route + ->expects($this->exactly(1)) + ->method('action') + ->with($this->equalTo(new RouteActionHandler($container, $controllerName, $actionName))) + ->will($this->returnValue($route)); + return $route; + } + +} + +/* +# +# sample routes.yaml for ownCloud +# +# the section simple describes one route + +routes: + - name: folders#open + url: /folders/{folderId}/open + verb: GET + # controller: name.split()[0] + # action: name.split()[1] + +# for a resource following actions will be generated: +# - index +# - create +# - show +# - update +# - destroy +# - new +resources: + accounts: + url: /accounts + + folders: + url: /accounts/{accountId}/folders + # actions can be used to define additional actions on the resource + actions: + - name: validate + verb: GET + on-collection: false + + * */ diff --git a/tests/lib/appframework/utility/MethodAnnotationReaderTest.php b/tests/lib/appframework/utility/MethodAnnotationReaderTest.php new file mode 100644 index 0000000000..bcdcf3de37 --- /dev/null +++ b/tests/lib/appframework/utility/MethodAnnotationReaderTest.php @@ -0,0 +1,58 @@ +. + * + */ + + +namespace OC\AppFramework\Utility; + + +require_once __DIR__ . "/../classloader.php"; + + +class MethodAnnotationReaderTest extends \PHPUnit_Framework_TestCase { + + + /** + * @Annotation + */ + public function testReadAnnotation(){ + $reader = new MethodAnnotationReader('\OC\AppFramework\Utility\MethodAnnotationReaderTest', + 'testReadAnnotation'); + + $this->assertTrue($reader->hasAnnotation('Annotation')); + } + + + /** + * @Annotation + * @param test + */ + public function testReadAnnotationNoLowercase(){ + $reader = new MethodAnnotationReader('\OC\AppFramework\Utility\MethodAnnotationReaderTest', + 'testReadAnnotationNoLowercase'); + + $this->assertTrue($reader->hasAnnotation('Annotation')); + $this->assertFalse($reader->hasAnnotation('param')); + } + + +} From 3324495a7882cb7957c5ffd498b1b6275a192b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 17 Aug 2013 18:26:53 +0200 Subject: [PATCH 003/180] pulling in 3rdparty submodule --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 8d68fa1eab..75a05d76ab 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 8d68fa1eabe8c1d033cb89676b31f0eaaf99335b +Subproject commit 75a05d76ab86ba7454b4312fd0ff2ca5bd5828cf From 72e1a8d83b3a21875cac6948879471661d120c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 12:47:23 +0200 Subject: [PATCH 004/180] fixing require to Pimple --- lib/appframework/dependencyinjection/dicontainer.php | 2 +- tests/lib/appframework/AppTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 34f64e72cb..d6cf4d5502 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -34,7 +34,7 @@ use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Utility\TimeFactory; // register 3rdparty autoloaders -require_once __DIR__ . '/../../../../3rdparty/Pimple/Pimple.php'; +require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php'; /** diff --git a/tests/lib/appframework/AppTest.php b/tests/lib/appframework/AppTest.php index 000094d07c..6e647f68e6 100644 --- a/tests/lib/appframework/AppTest.php +++ b/tests/lib/appframework/AppTest.php @@ -29,7 +29,7 @@ use OC\AppFramework\Core\API; use OC\AppFramework\Middleware\MiddlewareDispatcher; // FIXME: loading pimpl correctly from 3rdparty repo -require_once __DIR__ . '/../../../../3rdparty/Pimple/Pimple.php'; +require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php'; require_once __DIR__ . "/classloader.php"; From 0fa2e1b3d91d243452ffdfd36dbd0bed3f27e387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 12:48:45 +0200 Subject: [PATCH 005/180] there is no HttpMiddleware --- lib/appframework/dependencyinjection/dicontainer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index d6cf4d5502..69c645b1be 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -29,7 +29,6 @@ use OC\AppFramework\Http\Request; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Core\API; use OC\AppFramework\Middleware\MiddlewareDispatcher; -use OC\AppFramework\Middleware\Http\HttpMiddleware; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Utility\TimeFactory; From 0fa8f380767369b4aa85f5944a8e921009b1ed27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 16:51:12 +0200 Subject: [PATCH 006/180] fixing broken test --- tests/lib/appframework/AppTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/appframework/AppTest.php b/tests/lib/appframework/AppTest.php index 6e647f68e6..dcf0e6f77e 100644 --- a/tests/lib/appframework/AppTest.php +++ b/tests/lib/appframework/AppTest.php @@ -46,7 +46,7 @@ class AppTest extends \PHPUnit_Framework_TestCase { private $controllerMethod; protected function setUp() { - $this->container = new \Pimple(); + $this->container = new \OC\AppFramework\DependencyInjection\DIContainer('test'); $this->controller = $this->getMockBuilder( 'OC\AppFramework\Controller\Controller') ->disableOriginalConstructor() From cdada78aa4acd2880e0344a476d3c1d838645ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 17:20:36 +0200 Subject: [PATCH 007/180] typos & unused var fixed --- lib/appframework/http/dispatcher.php | 11 +++++------ lib/appframework/http/downloadresponse.php | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/appframework/http/dispatcher.php b/lib/appframework/http/dispatcher.php index ab5644274f..183854650f 100644 --- a/lib/appframework/http/dispatcher.php +++ b/lib/appframework/http/dispatcher.php @@ -29,7 +29,7 @@ use \OC\AppFramework\Middleware\MiddlewareDispatcher; /** - * Class to dispatch the request to the middleware disptacher + * Class to dispatch the request to the middleware dispatcher */ class Dispatcher { @@ -67,11 +67,10 @@ class Dispatcher { $methodName); $response = $controller->$methodName(); - - // if an exception appears, the middleware checks if it can handle the - // exception and creates a response. If no response is created, it is - // assumed that theres no middleware who can handle it and the error is - // thrown again + // if an exception appears, the middleware checks if it can handle the + // exception and creates a response. If no response is created, it is + // assumed that theres no middleware who can handle it and the error is + // thrown again } catch(\Exception $exception){ $response = $this->middlewareDispatcher->afterException( $controller, $methodName, $exception); diff --git a/lib/appframework/http/downloadresponse.php b/lib/appframework/http/downloadresponse.php index 5a0db325fe..096e4fc833 100644 --- a/lib/appframework/http/downloadresponse.php +++ b/lib/appframework/http/downloadresponse.php @@ -30,7 +30,6 @@ namespace OC\AppFramework\Http; */ abstract class DownloadResponse extends Response { - private $content; private $filename; private $contentType; From 93194bb39617d4b11a0a84b8cd4caf0491155961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 17:21:14 +0200 Subject: [PATCH 008/180] Introducing IContainer into public api --- .../dependencyinjection/dicontainer.php | 22 +++++----- lib/appframework/utility/simplecontainer.php | 44 +++++++++++++++++++ tests/lib/appframework/classloader.php | 9 ++++ 3 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 lib/appframework/utility/simplecontainer.php diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 69c645b1be..88ad2cd414 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -30,19 +30,11 @@ use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Core\API; use OC\AppFramework\Middleware\MiddlewareDispatcher; use OC\AppFramework\Middleware\Security\SecurityMiddleware; +use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; -// register 3rdparty autoloaders -require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php'; - -/** - * This class extends Pimple (http://pimple.sensiolabs.org/) for reusability - * To use this class, extend your own container from this. Should you require it - * you can overwrite the dependencies with your own classes by simply redefining - * a dependency - */ -class DIContainer extends \Pimple { +class DIContainer extends SimpleContainer { /** @@ -61,8 +53,14 @@ class DIContainer extends \Pimple { * Http */ $this['Request'] = $this->share(function($c) { - $params = json_decode(file_get_contents('php://input'), true); - $params = is_array($params) ? $params: array(); + + $params = array(); + + // we json decode the body only in case of content type json + if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'],'json') === true ) { + $params = json_decode(file_get_contents('php://input'), true); + $params = is_array($params) ? $params: array(); + } return new Request( array( diff --git a/lib/appframework/utility/simplecontainer.php b/lib/appframework/utility/simplecontainer.php new file mode 100644 index 0000000000..04b6cd727b --- /dev/null +++ b/lib/appframework/utility/simplecontainer.php @@ -0,0 +1,44 @@ +offsetGet($name); + } + + function registerParameter($name, $value) + { + $this[$name] = $value; + } + + /** + * The given closure is call the first time the given service is queried. + * The closure has to return the instance for the given service. + * Created instance will be cached in case $shared is true. + * + * @param string $name name of the service to register another backend for + * @param callable $closure the closure to be called on service creation + */ + function registerService($name, \Closure $closure, $shared = true) + { + if ($shared) { + $this[$name] = \Pimple::share($closure); + } else { + $this[$name] = $closure; + } + } +} diff --git a/tests/lib/appframework/classloader.php b/tests/lib/appframework/classloader.php index ae485e67b2..cd9f893df3 100644 --- a/tests/lib/appframework/classloader.php +++ b/tests/lib/appframework/classloader.php @@ -32,6 +32,15 @@ spl_autoload_register(function ($className){ } } + if (strpos($className, 'OCP\\') === 0) { + $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); + $relPath = __DIR__ . '/../../../lib/public' . $path; + + if(file_exists($relPath)){ + require_once $relPath; + } + } + // FIXME: this will most probably not work anymore if (strpos($className, 'OCA\\') === 0) { From 6e1946ab00cca760d555222df008ba92b0185eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 17:22:33 +0200 Subject: [PATCH 009/180] Introducing IContainer into public api --- lib/public/core/icontainer.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 lib/public/core/icontainer.php diff --git a/lib/public/core/icontainer.php b/lib/public/core/icontainer.php new file mode 100644 index 0000000000..a6c93abec6 --- /dev/null +++ b/lib/public/core/icontainer.php @@ -0,0 +1,19 @@ + Date: Tue, 20 Aug 2013 17:53:58 +0200 Subject: [PATCH 010/180] Introducing IRequest --- lib/appframework/http/request.php | 111 +++++++++++++++++++++++++++++- lib/public/core/irequest.php | 88 +++++++++++++++++++++++ 2 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 lib/public/core/irequest.php diff --git a/lib/appframework/http/request.php b/lib/appframework/http/request.php index 7d024c8605..ab72a8db69 100644 --- a/lib/appframework/http/request.php +++ b/lib/appframework/http/request.php @@ -22,12 +22,14 @@ namespace OC\AppFramework\Http; +use OCP\Core\IRequest; + /** * Class for accessing variables in the request. * This class provides an immutable object with request variables. */ -class Request implements \ArrayAccess, \Countable { +class Request implements \ArrayAccess, \Countable, IRequest { protected $items = array(); protected $allowedKeys = array( @@ -214,4 +216,111 @@ class Request implements \ArrayAccess, \Countable { return null; } + /** + * Lets you access post and get parameters by the index + * In case of json requests the encoded json body is accessed + * + * @param string $key the key which you want to access in the URL Parameter + * placeholder, $_POST or $_GET array. + * The priority how they're returned is the following: + * 1. URL parameters + * 2. POST parameters + * 3. GET parameters + * @param mixed $default If the key is not found, this value will be returned + * @return mixed the content of the array + */ + public function getParam($key, $default = null) + { + return isset($this->parameters[$key]) + ? $this->parameters[$key] + : $default; + } + + /** + * Returns all params that were received, be it from the request + * (as GET or POST) or throuh the URL by the route + * @return array the array with all parameters + */ + public function getParams() + { + return $this->parameters; + } + + /** + * Returns the method of the request + * @return string the method of the request (POST, GET, etc) + */ + public function getMethod() + { + return $this->method; + } + + /** + * Shortcut for accessing an uploaded file through the $_FILES array + * @param string $key the key that will be taken from the $_FILES array + * @return array the file in the $_FILES element + */ + public function getUploadedFile($key) + { + return isset($this->files[$key]) ? $this->files[$key] : null; + } + + /** + * Shortcut for getting env variables + * @param string $key the key that will be taken from the $_ENV array + * @return array the value in the $_ENV element + */ + public function getEnv($key) + { + return isset($this->env[$key]) ? $this->env[$key] : null; + } + + /** + * Shortcut for getting session variables + * @param string $key the key that will be taken from the $_SESSION array + * @return array the value in the $_SESSION element + */ + function getSession($key) + { + return isset($this->session[$key]) ? $this->session[$key] : null; + } + + /** + * Shortcut for getting cookie variables + * @param string $key the key that will be taken from the $_COOKIE array + * @return array the value in the $_COOKIE element + */ + function getCookie($key) + { + return isset($this->cookies[$key]) ? $this->cookies[$key] : null; + } + + /** + * Returns the request body content. + * + * @param Boolean $asResource If true, a resource will be returned + * + * @return string|resource The request body content or a resource to read the body stream. + * + * @throws \LogicException + */ + function getContent($asResource = false) + { + return null; +// if (false === $this->content || (true === $asResource && null !== $this->content)) { +// throw new \LogicException('getContent() can only be called once when using the resource return type.'); +// } +// +// if (true === $asResource) { +// $this->content = false; +// +// return fopen('php://input', 'rb'); +// } +// +// if (null === $this->content) { +// $this->content = file_get_contents('php://input'); +// } +// +// return $this->content; + } } diff --git a/lib/public/core/irequest.php b/lib/public/core/irequest.php new file mode 100644 index 0000000000..f283e9cb25 --- /dev/null +++ b/lib/public/core/irequest.php @@ -0,0 +1,88 @@ + Date: Tue, 20 Aug 2013 21:05:55 +0200 Subject: [PATCH 011/180] controller reuses IRequest methods --- lib/appframework/controller/controller.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/appframework/controller/controller.php b/lib/appframework/controller/controller.php index 3e8166050d..f6f34618ec 100644 --- a/lib/appframework/controller/controller.php +++ b/lib/appframework/controller/controller.php @@ -63,9 +63,7 @@ abstract class Controller { * @return mixed the content of the array */ public function params($key, $default=null){ - return isset($this->request->parameters[$key]) - ? $this->request->parameters[$key] - : $default; + return $this->request->getParam($key, $default); } @@ -75,7 +73,7 @@ abstract class Controller { * @return array the array with all parameters */ public function getParams() { - return $this->request->parameters; + return $this->request->getParams(); } @@ -84,7 +82,7 @@ abstract class Controller { * @return string the method of the request (POST, GET, etc) */ public function method() { - return $this->request->method; + return $this->request->getMethod(); } @@ -94,7 +92,7 @@ abstract class Controller { * @return array the file in the $_FILES element */ public function getUploadedFile($key) { - return isset($this->request->files[$key]) ? $this->request->files[$key] : null; + return $this->request->getUploadedFile($key); } @@ -104,7 +102,7 @@ abstract class Controller { * @return array the value in the $_ENV element */ public function env($key) { - return isset($this->request->env[$key]) ? $this->request->env[$key] : null; + return $this->request->getEnv($key); } @@ -114,7 +112,7 @@ abstract class Controller { * @return array the value in the $_SESSION element */ public function session($key) { - return isset($this->request->session[$key]) ? $this->request->session[$key] : null; + return $this->request->getSession($key); } @@ -124,7 +122,7 @@ abstract class Controller { * @return array the value in the $_COOKIE element */ public function cookie($key) { - return isset($this->request->cookies[$key]) ? $this->request->cookies[$key] : null; + return $this->request->getCookie($key); } From 395deacc6760564544a76338023d9b0bf39e0bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 20 Aug 2013 21:21:21 +0200 Subject: [PATCH 012/180] reducing controller annotations to: @PublicPage - No user logon is expected @NoAdminRequired - the login user requires no admin rights @NoCSRFRequired - the incoming request will not check for CSRF token --- .../security/securitymiddleware.php | 19 +-- .../security/SecurityMiddlewareTest.php | 156 ++++-------------- 2 files changed, 41 insertions(+), 134 deletions(-) diff --git a/lib/appframework/middleware/security/securitymiddleware.php b/lib/appframework/middleware/security/securitymiddleware.php index 7a715f309a..52818b1b53 100644 --- a/lib/appframework/middleware/security/securitymiddleware.php +++ b/lib/appframework/middleware/security/securitymiddleware.php @@ -77,25 +77,20 @@ class SecurityMiddleware extends Middleware { $this->api->activateNavigationEntry(); // security checks - if(!$annotationReader->hasAnnotation('IsLoggedInExemption')) { + $isPublicPage = $annotationReader->hasAnnotation('PublicPage'); + if(!$isPublicPage) { if(!$this->api->isLoggedIn()) { throw new SecurityException('Current user is not logged in', Http::STATUS_UNAUTHORIZED); } - } - if(!$annotationReader->hasAnnotation('IsAdminExemption')) { - if(!$this->api->isAdminUser($this->api->getUserId())) { - throw new SecurityException('Logged in user must be an admin', Http::STATUS_FORBIDDEN); + if(!$annotationReader->hasAnnotation('NoAdminRequired')) { + if(!$this->api->isAdminUser($this->api->getUserId())) { + throw new SecurityException('Logged in user must be an admin', Http::STATUS_FORBIDDEN); + } } } - if(!$annotationReader->hasAnnotation('IsSubAdminExemption')) { - if(!$this->api->isSubAdminUser($this->api->getUserId())) { - throw new SecurityException('Logged in user must be a subadmin', Http::STATUS_FORBIDDEN); - } - } - - if(!$annotationReader->hasAnnotation('CSRFExemption')) { + if(!$annotationReader->hasAnnotation('NoCSRFRequired')) { if(!$this->api->passesCSRFCheck()) { throw new SecurityException('CSRF check failed', Http::STATUS_PRECONDITION_FAILED); } diff --git a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php index 0b2103564e..90a19c9999 100644 --- a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php +++ b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php @@ -80,67 +80,27 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @PublicPage + * @NoCSRFRequired */ public function testSetNavigationEntry(){ $this->checkNavEntry('testSetNavigationEntry', true); } - private function ajaxExceptionCheck($method, $shouldBeAjax=false){ - $api = $this->getAPI(); - $api->expects($this->any()) - ->method('passesCSRFCheck') - ->will($this->returnValue(false)); - - $sec = new SecurityMiddleware($api, $this->request); - - try { - $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', - $method); - } catch (SecurityException $ex){ - if($shouldBeAjax){ - $this->assertTrue($ex->isAjax()); - } else { - $this->assertFalse($ex->isAjax()); - } - - } - } - - - /** - * @Ajax - * @IsLoggedInExemption - * @CSRFExemption - * @IsAdminExemption - * @IsSubAdminExemption - */ - public function testAjaxException(){ - $this->ajaxExceptionCheck('testAjaxException'); - } - - - /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsAdminExemption - * @IsSubAdminExemption - */ - public function testNoAjaxException(){ - $this->ajaxExceptionCheck('testNoAjaxException'); - } - - private function ajaxExceptionStatus($method, $test, $status) { $api = $this->getAPI(); $api->expects($this->any()) ->method($test) ->will($this->returnValue(false)); + // isAdminUser requires isLoggedIn call to return true + if ($test === 'isAdminUser') { + $api->expects($this->any()) + ->method('isLoggedIn') + ->will($this->returnValue(true)); + } + $sec = new SecurityMiddleware($api, $this->request); try { @@ -151,9 +111,6 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { } } - /** - * @Ajax - */ public function testAjaxStatusLoggedInCheck() { $this->ajaxExceptionStatus( 'testAjaxStatusLoggedInCheck', @@ -163,8 +120,8 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { } /** - * @Ajax - * @IsLoggedInExemption + * @NoCSRFRequired + * @NoAdminRequired */ public function testAjaxNotAdminCheck() { $this->ajaxExceptionStatus( @@ -175,23 +132,7 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { } /** - * @Ajax - * @IsLoggedInExemption - * @IsAdminExemption - */ - public function testAjaxNotSubAdminCheck() { - $this->ajaxExceptionStatus( - 'testAjaxNotSubAdminCheck', - 'isSubAdminUser', - Http::STATUS_FORBIDDEN - ); - } - - /** - * @Ajax - * @IsLoggedInExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @PublicPage */ public function testAjaxStatusCSRFCheck() { $this->ajaxExceptionStatus( @@ -202,11 +143,8 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { } /** - * @Ajax - * @CSRFExemption - * @IsLoggedInExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @PublicPage + * @NoCSRFRequired */ public function testAjaxStatusAllGood() { $this->ajaxExceptionStatus( @@ -231,11 +169,10 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { ); } + /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @PublicPage + * @NoCSRFRequired */ public function testNoChecks(){ $api = $this->getAPI(); @@ -245,9 +182,6 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { $api->expects($this->never()) ->method('isAdminUser') ->will($this->returnValue(true)); - $api->expects($this->never()) - ->method('isSubAdminUser') - ->will($this->returnValue(true)); $api->expects($this->never()) ->method('isLoggedIn') ->will($this->returnValue(true)); @@ -264,10 +198,19 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { ->method($expects) ->will($this->returnValue(!$shouldFail)); + // admin check requires login + if ($expects === 'isAdminUser') { + $api->expects($this->once()) + ->method('isLoggedIn') + ->will($this->returnValue(true)); + } + $sec = new SecurityMiddleware($api, $this->request); if($shouldFail){ $this->setExpectedException('\OC\AppFramework\Middleware\Security\SecurityException'); + } else { + $this->setExpectedException(null); } $sec->beforeController('\OC\AppFramework\Middleware\Security\SecurityMiddlewareTest', $method); @@ -275,9 +218,7 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @IsLoggedInExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @PublicPage */ public function testCsrfCheck(){ $this->securityCheck('testCsrfCheck', 'passesCSRFCheck'); @@ -285,9 +226,7 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @IsLoggedInExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @PublicPage */ public function testFailCsrfCheck(){ $this->securityCheck('testFailCsrfCheck', 'passesCSRFCheck', true); @@ -295,9 +234,8 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @CSRFExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @NoCSRFRequired + * @NoAdminRequired */ public function testLoggedInCheck(){ $this->securityCheck('testLoggedInCheck', 'isLoggedIn'); @@ -305,9 +243,8 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @CSRFExemption - * @IsAdminExemption - * @IsSubAdminExemption + * @NoCSRFRequired + * @NoAdminRequired */ public function testFailLoggedInCheck(){ $this->securityCheck('testFailLoggedInCheck', 'isLoggedIn', true); @@ -315,9 +252,7 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsSubAdminExemption + * @NoCSRFRequired */ public function testIsAdminCheck(){ $this->securityCheck('testIsAdminCheck', 'isAdminUser'); @@ -325,36 +260,13 @@ class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsSubAdminExemption + * @NoCSRFRequired */ public function testFailIsAdminCheck(){ $this->securityCheck('testFailIsAdminCheck', 'isAdminUser', true); } - /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsAdminExemption - */ - public function testIsSubAdminCheck(){ - $this->securityCheck('testIsSubAdminCheck', 'isSubAdminUser'); - } - - - /** - * @IsLoggedInExemption - * @CSRFExemption - * @IsAdminExemption - */ - public function testFailIsSubAdminCheck(){ - $this->securityCheck('testFailIsSubAdminCheck', 'isSubAdminUser', true); - } - - - public function testAfterExceptionNotCaughtThrowsItAgain(){ $ex = new \Exception(); $this->setExpectedException('\Exception'); From 33db8a3089760947eec93149a2029164b676eae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 21 Aug 2013 00:41:20 +0200 Subject: [PATCH 013/180] kill superfluent classloader from tests - this approach might be of interest within the apps --- tests/lib/appframework/AppTest.php | 2 +- tests/lib/appframework/classloader.php | 54 ------------------- .../controller/ControllerTest.php | 5 +- .../dependencyinjection/DIContainerTest.php | 2 +- .../lib/appframework/http/DispatcherTest.php | 6 +-- .../http/DownloadResponseTest.php | 2 +- tests/lib/appframework/http/HttpTest.php | 2 +- .../appframework/http/JSONResponseTest.php | 4 +- .../http/RedirectResponseTest.php | 2 +- tests/lib/appframework/http/RequestTest.php | 2 - tests/lib/appframework/http/ResponseTest.php | 7 +-- .../http/TemplateResponseTest.php | 12 +++-- .../middleware/MiddlewareDispatcherTest.php | 6 +-- .../middleware/MiddlewareTest.php | 11 ++-- .../security/SecurityMiddlewareTest.php | 6 +-- .../lib/appframework/routing/RoutingTest.php | 1 - .../utility/MethodAnnotationReaderTest.php | 3 -- 17 files changed, 33 insertions(+), 94 deletions(-) delete mode 100644 tests/lib/appframework/classloader.php diff --git a/tests/lib/appframework/AppTest.php b/tests/lib/appframework/AppTest.php index dcf0e6f77e..e8ae8c8f67 100644 --- a/tests/lib/appframework/AppTest.php +++ b/tests/lib/appframework/AppTest.php @@ -30,7 +30,7 @@ use OC\AppFramework\Middleware\MiddlewareDispatcher; // FIXME: loading pimpl correctly from 3rdparty repo require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php'; -require_once __DIR__ . "/classloader.php"; +//require_once __DIR__ . "/classloader.php"; class AppTest extends \PHPUnit_Framework_TestCase { diff --git a/tests/lib/appframework/classloader.php b/tests/lib/appframework/classloader.php deleted file mode 100644 index cd9f893df3..0000000000 --- a/tests/lib/appframework/classloader.php +++ /dev/null @@ -1,54 +0,0 @@ -. - * - */ - -// to execute without ownCloud, we need to create our own class loader -spl_autoload_register(function ($className){ - if (strpos($className, 'OC\\AppFramework') === 0) { - $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); - $relPath = __DIR__ . '/../../../lib/' . $path; - - if(file_exists($relPath)){ - require_once $relPath; - } - } - - if (strpos($className, 'OCP\\') === 0) { - $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); - $relPath = __DIR__ . '/../../../lib/public' . $path; - - if(file_exists($relPath)){ - require_once $relPath; - } - } - - // FIXME: this will most probably not work anymore - if (strpos($className, 'OCA\\') === 0) { - - $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); - $relPath = __DIR__ . '/../..' . $path; - - if(file_exists($relPath)){ - require_once $relPath; - } - } -}); diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php index d8357c2a68..246371d249 100644 --- a/tests/lib/appframework/controller/ControllerTest.php +++ b/tests/lib/appframework/controller/ControllerTest.php @@ -25,12 +25,11 @@ namespace Test\AppFramework\Controller; use OC\AppFramework\Http\Request; -use OC\AppFramework\Http\JSONResponse; -use OC\AppFramework\Http\TemplateResponse; use OC\AppFramework\Controller\Controller; +use OCP\AppFramework\Http\TemplateResponse; -require_once(__DIR__ . "/../classloader.php"); +//require_once __DIR__ . "/../classloader.php"; class ChildController extends Controller {}; diff --git a/tests/lib/appframework/dependencyinjection/DIContainerTest.php b/tests/lib/appframework/dependencyinjection/DIContainerTest.php index ce346f0a76..25fdd20283 100644 --- a/tests/lib/appframework/dependencyinjection/DIContainerTest.php +++ b/tests/lib/appframework/dependencyinjection/DIContainerTest.php @@ -29,7 +29,7 @@ namespace OC\AppFramework\DependencyInjection; use \OC\AppFramework\Http\Request; -require_once(__DIR__ . "/../classloader.php"); +//require_once(__DIR__ . "/../classloader.php"); class DIContainerTest extends \PHPUnit_Framework_TestCase { diff --git a/tests/lib/appframework/http/DispatcherTest.php b/tests/lib/appframework/http/DispatcherTest.php index 2e3db11050..849b0ca97a 100644 --- a/tests/lib/appframework/http/DispatcherTest.php +++ b/tests/lib/appframework/http/DispatcherTest.php @@ -27,7 +27,7 @@ namespace OC\AppFramework\Http; use OC\AppFramework\Core\API; use OC\AppFramework\Middleware\MiddlewareDispatcher; -require_once(__DIR__ . "/../classloader.php"); +//require_once(__DIR__ . "/../classloader.php"); class DispatcherTest extends \PHPUnit_Framework_TestCase { @@ -69,7 +69,7 @@ class DispatcherTest extends \PHPUnit_Framework_TestCase { $this->http, $this->middlewareDispatcher); $this->response = $this->getMockBuilder( - '\OC\AppFramework\Http\Response') + '\OCP\AppFramework\Http\Response') ->disableOriginalConstructor() ->getMock(); @@ -207,7 +207,7 @@ class DispatcherTest extends \PHPUnit_Framework_TestCase { $out = 'yo'; $httpHeaders = 'Http'; $responseHeaders = array('hell' => 'yeah'); - $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders, true, false); + $this->setMiddlewareExpections($out, $httpHeaders, $responseHeaders, true, false); $this->setExpectedException('\Exception'); $response = $this->dispatcher->dispatch($this->controller, diff --git a/tests/lib/appframework/http/DownloadResponseTest.php b/tests/lib/appframework/http/DownloadResponseTest.php index 103cfe7588..64fe7992b6 100644 --- a/tests/lib/appframework/http/DownloadResponseTest.php +++ b/tests/lib/appframework/http/DownloadResponseTest.php @@ -25,7 +25,7 @@ namespace OC\AppFramework\Http; -require_once(__DIR__ . "/../classloader.php"); +//require_once(__DIR__ . "/../classloader.php"); class ChildDownloadResponse extends DownloadResponse {}; diff --git a/tests/lib/appframework/http/HttpTest.php b/tests/lib/appframework/http/HttpTest.php index 306bc3caf4..382d511b11 100644 --- a/tests/lib/appframework/http/HttpTest.php +++ b/tests/lib/appframework/http/HttpTest.php @@ -25,7 +25,7 @@ namespace OC\AppFramework\Http; -require_once(__DIR__ . "/../classloader.php"); +//require_once(__DIR__ . "/../classloader.php"); diff --git a/tests/lib/appframework/http/JSONResponseTest.php b/tests/lib/appframework/http/JSONResponseTest.php index d15e08f6ce..534c54cbce 100644 --- a/tests/lib/appframework/http/JSONResponseTest.php +++ b/tests/lib/appframework/http/JSONResponseTest.php @@ -27,7 +27,9 @@ namespace OC\AppFramework\Http; -require_once(__DIR__ . "/../classloader.php"); +use OCP\AppFramework\Http\JSONResponse; + +//require_once(__DIR__ . "/../classloader.php"); diff --git a/tests/lib/appframework/http/RedirectResponseTest.php b/tests/lib/appframework/http/RedirectResponseTest.php index a8577feed2..1946655b0f 100644 --- a/tests/lib/appframework/http/RedirectResponseTest.php +++ b/tests/lib/appframework/http/RedirectResponseTest.php @@ -25,7 +25,7 @@ namespace OC\AppFramework\Http; -require_once(__DIR__ . "/../classloader.php"); +//require_once(__DIR__ . "/../classloader.php"); diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php index c1f56c0163..0371c870cf 100644 --- a/tests/lib/appframework/http/RequestTest.php +++ b/tests/lib/appframework/http/RequestTest.php @@ -9,8 +9,6 @@ namespace OC\AppFramework\Http; -require_once(__DIR__ . "/../classloader.php"); - class RequestTest extends \PHPUnit_Framework_TestCase { public function testRequestAccessors() { diff --git a/tests/lib/appframework/http/ResponseTest.php b/tests/lib/appframework/http/ResponseTest.php index 621ba66545..7e09086f80 100644 --- a/tests/lib/appframework/http/ResponseTest.php +++ b/tests/lib/appframework/http/ResponseTest.php @@ -25,13 +25,14 @@ namespace OC\AppFramework\Http; -require_once(__DIR__ . "/../classloader.php"); - +use OCP\AppFramework\Http\Response; class ResponseTest extends \PHPUnit_Framework_TestCase { - + /** + * @var \OCP\AppFramework\Http\Response + */ private $childResponse; protected function setUp(){ diff --git a/tests/lib/appframework/http/TemplateResponseTest.php b/tests/lib/appframework/http/TemplateResponseTest.php index 30684725b7..3c6d29cd33 100644 --- a/tests/lib/appframework/http/TemplateResponseTest.php +++ b/tests/lib/appframework/http/TemplateResponseTest.php @@ -24,15 +24,19 @@ namespace OC\AppFramework\Http; -use OC\AppFramework\Core\API; - - -require_once(__DIR__ . "/../classloader.php"); +use OCP\AppFramework\Http\TemplateResponse; class TemplateResponseTest extends \PHPUnit_Framework_TestCase { + /** + * @var \OCP\AppFramework\Http\TemplateResponse + */ private $tpl; + + /** + * @var \OCP\AppFramework\IApi + */ private $api; protected function setUp() { diff --git a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php index bfa54a48ea..d1b2fedee5 100644 --- a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php +++ b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php @@ -24,14 +24,10 @@ namespace OC\AppFramework; -use OC\AppFramework\Controller\Controller; use OC\AppFramework\Http\Request; -use OC\AppFramework\Http\Response; use OC\AppFramework\Middleware\Middleware; use OC\AppFramework\Middleware\MiddlewareDispatcher; - - -require_once(__DIR__ . "/../classloader.php"); +use OCP\AppFramework\Http\Response; // needed to test ordering diff --git a/tests/lib/appframework/middleware/MiddlewareTest.php b/tests/lib/appframework/middleware/MiddlewareTest.php index 1adce6b3d4..5e2930ac6a 100644 --- a/tests/lib/appframework/middleware/MiddlewareTest.php +++ b/tests/lib/appframework/middleware/MiddlewareTest.php @@ -28,14 +28,14 @@ use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\Middleware; -require_once(__DIR__ . "/../classloader.php"); - - class ChildMiddleware extends Middleware {}; class MiddlewareTest extends \PHPUnit_Framework_TestCase { + /** + * @var Middleware + */ private $middleware; private $controller; private $exception; @@ -50,12 +50,13 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase { $this->controller = $this->getMock('OC\AppFramework\Controller\Controller', array(), array($this->api, new Request())); $this->exception = new \Exception(); - $this->response = $this->getMock('OC\AppFramework\Http\Response'); + $this->response = $this->getMock('OCP\AppFramework\Http\Response'); } public function testBeforeController() { - $this->middleware->beforeController($this->controller, null, $this->exception); + $this->middleware->beforeController($this->controller, null); + $this->assertNull(null); } diff --git a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php index 90a19c9999..3ed44282a7 100644 --- a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php +++ b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php @@ -27,11 +27,7 @@ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Http\Http; use OC\AppFramework\Http\Request; use OC\AppFramework\Http\RedirectResponse; -use OC\AppFramework\Http\JSONResponse; -use OC\AppFramework\Middleware\Middleware; - - -require_once(__DIR__ . "/../../classloader.php"); +use OCP\AppFramework\Http\JSONResponse; class SecurityMiddlewareTest extends \PHPUnit_Framework_TestCase { diff --git a/tests/lib/appframework/routing/RoutingTest.php b/tests/lib/appframework/routing/RoutingTest.php index 92ad461471..a7aa922db1 100644 --- a/tests/lib/appframework/routing/RoutingTest.php +++ b/tests/lib/appframework/routing/RoutingTest.php @@ -5,7 +5,6 @@ namespace OC\AppFramework\Routing; use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\routing\RouteConfig; -require_once(__DIR__ . "/../classloader.php"); class RouteConfigTest extends \PHPUnit_Framework_TestCase { diff --git a/tests/lib/appframework/utility/MethodAnnotationReaderTest.php b/tests/lib/appframework/utility/MethodAnnotationReaderTest.php index bcdcf3de37..c68812aa5c 100644 --- a/tests/lib/appframework/utility/MethodAnnotationReaderTest.php +++ b/tests/lib/appframework/utility/MethodAnnotationReaderTest.php @@ -25,9 +25,6 @@ namespace OC\AppFramework\Utility; -require_once __DIR__ . "/../classloader.php"; - - class MethodAnnotationReaderTest extends \PHPUnit_Framework_TestCase { From aa979f5dff4234a3db9e6fb1ddc50335c04c194b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 21 Aug 2013 00:44:39 +0200 Subject: [PATCH 014/180] cleanup of tests --- tests/lib/appframework/AppTest.php | 8 -------- .../middleware/MiddlewareDispatcherTest.php | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/lib/appframework/AppTest.php b/tests/lib/appframework/AppTest.php index e8ae8c8f67..80abaefc43 100644 --- a/tests/lib/appframework/AppTest.php +++ b/tests/lib/appframework/AppTest.php @@ -24,14 +24,6 @@ namespace OC\AppFramework; -use OC\AppFramework\Http\Request; -use OC\AppFramework\Core\API; -use OC\AppFramework\Middleware\MiddlewareDispatcher; - -// FIXME: loading pimpl correctly from 3rdparty repo -require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php'; -//require_once __DIR__ . "/classloader.php"; - class AppTest extends \PHPUnit_Framework_TestCase { diff --git a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php index d1b2fedee5..43727846dc 100644 --- a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php +++ b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php @@ -99,6 +99,15 @@ class TestMiddleware extends Middleware { class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase { + public $exception; + public $response; + private $out; + private $method; + private $controller; + + /** + * @var MiddlewareDispatcher + */ private $dispatcher; @@ -107,7 +116,7 @@ class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase { $this->controller = $this->getControllerMock(); $this->method = 'method'; $this->response = new Response(); - $this->output = 'hi'; + $this->out = 'hi'; $this->exception = new \Exception(); } @@ -202,11 +211,11 @@ class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase { public function testBeforeOutputCorrectArguments(){ $m1 = $this->getMiddleware(); - $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output); + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); $this->assertEquals($this->controller, $m1->controller); $this->assertEquals($this->method, $m1->methodName); - $this->assertEquals($this->output, $m1->output); + $this->assertEquals($this->out, $m1->output); } @@ -248,7 +257,7 @@ class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase { $m1 = $this->getMiddleware(); $m2 = $this->getMiddleware(); - $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output); + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); $this->assertEquals(2, $m1->beforeOutputOrder); $this->assertEquals(1, $m2->beforeOutputOrder); @@ -268,7 +277,7 @@ class MiddlewareDispatcherTest extends \PHPUnit_Framework_TestCase { $this->dispatcher->registerMiddleware($m3); - $this->dispatcher->beforeOutput($this->controller, $this->method, $this->output); + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); $this->assertEquals(2, $m1->beforeOutputOrder); $this->assertEquals(1, $m2->beforeOutputOrder); From ba029ef4b27cfeabbc67523131fa473397b77f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 21 Aug 2013 00:58:15 +0200 Subject: [PATCH 015/180] initial setup of the server container --- lib/base.php | 8 ++++++++ lib/public/core/iservercontainer.php | 14 ++++++++++++++ lib/server.php | 15 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 lib/public/core/iservercontainer.php create mode 100644 lib/server.php diff --git a/lib/base.php b/lib/base.php index eaee842465..a81f1a59b8 100644 --- a/lib/base.php +++ b/lib/base.php @@ -84,6 +84,11 @@ class OC { */ public static $loader = null; + /** + * @var \OC\Server + */ + public static $server = null; + public static function initPaths() { // calculate the root directories OC::$SERVERROOT = str_replace("\\", '/', substr(__DIR__, 0, -4)); @@ -361,6 +366,9 @@ class OC { self::$loader->registerPrefix('Patchwork', '3rdparty'); spl_autoload_register(array(self::$loader, 'load')); + // setup the basic server + self::$server = new \OC\Server(); + // set some stuff //ob_start(); error_reporting(E_ALL | E_STRICT); diff --git a/lib/public/core/iservercontainer.php b/lib/public/core/iservercontainer.php new file mode 100644 index 0000000000..df744ab6fd --- /dev/null +++ b/lib/public/core/iservercontainer.php @@ -0,0 +1,14 @@ + Date: Wed, 21 Aug 2013 00:58:33 +0200 Subject: [PATCH 016/180] typo --- lib/public/core/irequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/core/irequest.php b/lib/public/core/irequest.php index f283e9cb25..fc2004d183 100644 --- a/lib/public/core/irequest.php +++ b/lib/public/core/irequest.php @@ -32,7 +32,7 @@ interface IRequest { /** * Returns all params that were received, be it from the request - * (as GET or POST) or throuh the URL by the route + * (as GET or POST) or through the URL by the route * @return array the array with all parameters */ public function getParams(); From 911bd3c16f508eb8f3cb9b03a5a21e2aa72ebf79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 21 Aug 2013 01:00:26 +0200 Subject: [PATCH 017/180] moving response classes over to OCP --- lib/appframework/controller/controller.php | 4 +- lib/appframework/http/dispatcher.php | 3 + lib/appframework/http/downloadresponse.php | 2 +- lib/appframework/http/http.php | 62 +------------ lib/appframework/http/redirectresponse.php | 2 + lib/appframework/middleware/middleware.php | 2 +- .../middleware/middlewaredispatcher.php | 2 +- .../security/securitymiddleware.php | 4 +- lib/public/appframework/http/http.php | 89 +++++++++++++++++++ .../appframework/http/jsonresponse.php | 2 +- .../appframework/http/response.php | 2 +- .../appframework/http/templateresponse.php | 2 +- 12 files changed, 105 insertions(+), 71 deletions(-) create mode 100644 lib/public/appframework/http/http.php rename lib/{ => public}/appframework/http/jsonresponse.php (98%) rename lib/{ => public}/appframework/http/response.php (98%) rename lib/{ => public}/appframework/http/templateresponse.php (98%) diff --git a/lib/appframework/controller/controller.php b/lib/appframework/controller/controller.php index f6f34618ec..a7498ba0e1 100644 --- a/lib/appframework/controller/controller.php +++ b/lib/appframework/controller/controller.php @@ -24,9 +24,9 @@ namespace OC\AppFramework\Controller; -use OC\AppFramework\Http\TemplateResponse; use OC\AppFramework\Http\Request; use OC\AppFramework\Core\API; +use OCP\AppFramework\Http\TemplateResponse; /** @@ -133,7 +133,7 @@ abstract class Controller { * @param string $renderAs user renders a full page, blank only your template * admin an entry in the admin settings * @param array $headers set additional headers in name/value pairs - * @return \OC\AppFramework\Http\TemplateResponse containing the page + * @return \OCP\AppFramework\Http\TemplateResponse containing the page */ public function render($templateName, array $params=array(), $renderAs='user', array $headers=array()){ diff --git a/lib/appframework/http/dispatcher.php b/lib/appframework/http/dispatcher.php index 183854650f..ea57a6860c 100644 --- a/lib/appframework/http/dispatcher.php +++ b/lib/appframework/http/dispatcher.php @@ -74,6 +74,9 @@ class Dispatcher { } catch(\Exception $exception){ $response = $this->middlewareDispatcher->afterException( $controller, $methodName, $exception); + if (is_null($response)) { + throw $exception; + } } $response = $this->middlewareDispatcher->afterController( diff --git a/lib/appframework/http/downloadresponse.php b/lib/appframework/http/downloadresponse.php index 096e4fc833..67b9542dba 100644 --- a/lib/appframework/http/downloadresponse.php +++ b/lib/appframework/http/downloadresponse.php @@ -28,7 +28,7 @@ namespace OC\AppFramework\Http; /** * Prompts the user to download the a file */ -abstract class DownloadResponse extends Response { +class DownloadResponse extends \OCP\AppFramework\Http\Response { private $filename; private $contentType; diff --git a/lib/appframework/http/http.php b/lib/appframework/http/http.php index 73f32d13b3..e00dc9cdc4 100644 --- a/lib/appframework/http/http.php +++ b/lib/appframework/http/http.php @@ -25,67 +25,7 @@ namespace OC\AppFramework\Http; -class Http { - - const STATUS_CONTINUE = 100; - const STATUS_SWITCHING_PROTOCOLS = 101; - const STATUS_PROCESSING = 102; - const STATUS_OK = 200; - const STATUS_CREATED = 201; - const STATUS_ACCEPTED = 202; - const STATUS_NON_AUTHORATIVE_INFORMATION = 203; - const STATUS_NO_CONTENT = 204; - const STATUS_RESET_CONTENT = 205; - const STATUS_PARTIAL_CONTENT = 206; - const STATUS_MULTI_STATUS = 207; - const STATUS_ALREADY_REPORTED = 208; - const STATUS_IM_USED = 226; - const STATUS_MULTIPLE_CHOICES = 300; - const STATUS_MOVED_PERMANENTLY = 301; - const STATUS_FOUND = 302; - const STATUS_SEE_OTHER = 303; - const STATUS_NOT_MODIFIED = 304; - const STATUS_USE_PROXY = 305; - const STATUS_RESERVED = 306; - const STATUS_TEMPORARY_REDIRECT = 307; - const STATUS_BAD_REQUEST = 400; - const STATUS_UNAUTHORIZED = 401; - const STATUS_PAYMENT_REQUIRED = 402; - const STATUS_FORBIDDEN = 403; - const STATUS_NOT_FOUND = 404; - const STATUS_METHOD_NOT_ALLOWED = 405; - const STATUS_NOT_ACCEPTABLE = 406; - const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; - const STATUS_REQUEST_TIMEOUT = 408; - const STATUS_CONFLICT = 409; - const STATUS_GONE = 410; - const STATUS_LENGTH_REQUIRED = 411; - const STATUS_PRECONDITION_FAILED = 412; - const STATUS_REQUEST_ENTITY_TOO_LARGE = 413; - const STATUS_REQUEST_URI_TOO_LONG = 414; - const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; - const STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416; - const STATUS_EXPECTATION_FAILED = 417; - const STATUS_IM_A_TEAPOT = 418; - const STATUS_UNPROCESSABLE_ENTITY = 422; - const STATUS_LOCKED = 423; - const STATUS_FAILED_DEPENDENCY = 424; - const STATUS_UPGRADE_REQUIRED = 426; - const STATUS_PRECONDITION_REQUIRED = 428; - const STATUS_TOO_MANY_REQUESTS = 429; - const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; - const STATUS_INTERNAL_SERVER_ERROR = 500; - const STATUS_NOT_IMPLEMENTED = 501; - const STATUS_BAD_GATEWAY = 502; - const STATUS_SERVICE_UNAVAILABLE = 503; - const STATUS_GATEWAY_TIMEOUT = 504; - const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; - const STATUS_VARIANT_ALSO_NEGOTIATES = 506; - const STATUS_INSUFFICIENT_STORAGE = 507; - const STATUS_LOOP_DETECTED = 508; - const STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509; - const STATUS_NOT_EXTENDED = 510; - const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; +class Http extends \OCP\AppFramework\Http\Http{ private $server; private $protocolVersion; diff --git a/lib/appframework/http/redirectresponse.php b/lib/appframework/http/redirectresponse.php index 727e0fb642..688447f161 100644 --- a/lib/appframework/http/redirectresponse.php +++ b/lib/appframework/http/redirectresponse.php @@ -24,6 +24,8 @@ namespace OC\AppFramework\Http; +use OCP\AppFramework\Http\Response; + /** * Redirects to a different URL diff --git a/lib/appframework/middleware/middleware.php b/lib/appframework/middleware/middleware.php index 4df8849046..b12c03c3eb 100644 --- a/lib/appframework/middleware/middleware.php +++ b/lib/appframework/middleware/middleware.php @@ -24,7 +24,7 @@ namespace OC\AppFramework\Middleware; -use OC\AppFramework\Http\Response; +use OCP\AppFramework\Http\Response; /** diff --git a/lib/appframework/middleware/middlewaredispatcher.php b/lib/appframework/middleware/middlewaredispatcher.php index c2d16134dc..70ab108e6b 100644 --- a/lib/appframework/middleware/middlewaredispatcher.php +++ b/lib/appframework/middleware/middlewaredispatcher.php @@ -25,7 +25,7 @@ namespace OC\AppFramework\Middleware; use OC\AppFramework\Controller\Controller; -use OC\AppFramework\Http\Response; +use OCP\AppFramework\Http\Response; /** diff --git a/lib/appframework/middleware/security/securitymiddleware.php b/lib/appframework/middleware/security/securitymiddleware.php index 52818b1b53..4f1447e1af 100644 --- a/lib/appframework/middleware/security/securitymiddleware.php +++ b/lib/appframework/middleware/security/securitymiddleware.php @@ -27,12 +27,12 @@ namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Controller\Controller; use OC\AppFramework\Http\Http; use OC\AppFramework\Http\Request; -use OC\AppFramework\Http\Response; -use OC\AppFramework\Http\JSONResponse; use OC\AppFramework\Http\RedirectResponse; use OC\AppFramework\Utility\MethodAnnotationReader; use OC\AppFramework\Middleware\Middleware; use OC\AppFramework\Core\API; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\JSONResponse; /** diff --git a/lib/public/appframework/http/http.php b/lib/public/appframework/http/http.php new file mode 100644 index 0000000000..9eafe78272 --- /dev/null +++ b/lib/public/appframework/http/http.php @@ -0,0 +1,89 @@ +. + * + */ + + +namespace OCP\AppFramework\Http; + + +class Http { + + const STATUS_CONTINUE = 100; + const STATUS_SWITCHING_PROTOCOLS = 101; + const STATUS_PROCESSING = 102; + const STATUS_OK = 200; + const STATUS_CREATED = 201; + const STATUS_ACCEPTED = 202; + const STATUS_NON_AUTHORATIVE_INFORMATION = 203; + const STATUS_NO_CONTENT = 204; + const STATUS_RESET_CONTENT = 205; + const STATUS_PARTIAL_CONTENT = 206; + const STATUS_MULTI_STATUS = 207; + const STATUS_ALREADY_REPORTED = 208; + const STATUS_IM_USED = 226; + const STATUS_MULTIPLE_CHOICES = 300; + const STATUS_MOVED_PERMANENTLY = 301; + const STATUS_FOUND = 302; + const STATUS_SEE_OTHER = 303; + const STATUS_NOT_MODIFIED = 304; + const STATUS_USE_PROXY = 305; + const STATUS_RESERVED = 306; + const STATUS_TEMPORARY_REDIRECT = 307; + const STATUS_BAD_REQUEST = 400; + const STATUS_UNAUTHORIZED = 401; + const STATUS_PAYMENT_REQUIRED = 402; + const STATUS_FORBIDDEN = 403; + const STATUS_NOT_FOUND = 404; + const STATUS_METHOD_NOT_ALLOWED = 405; + const STATUS_NOT_ACCEPTABLE = 406; + const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; + const STATUS_REQUEST_TIMEOUT = 408; + const STATUS_CONFLICT = 409; + const STATUS_GONE = 410; + const STATUS_LENGTH_REQUIRED = 411; + const STATUS_PRECONDITION_FAILED = 412; + const STATUS_REQUEST_ENTITY_TOO_LARGE = 413; + const STATUS_REQUEST_URI_TOO_LONG = 414; + const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; + const STATUS_REQUEST_RANGE_NOT_SATISFIABLE = 416; + const STATUS_EXPECTATION_FAILED = 417; + const STATUS_IM_A_TEAPOT = 418; + const STATUS_UNPROCESSABLE_ENTITY = 422; + const STATUS_LOCKED = 423; + const STATUS_FAILED_DEPENDENCY = 424; + const STATUS_UPGRADE_REQUIRED = 426; + const STATUS_PRECONDITION_REQUIRED = 428; + const STATUS_TOO_MANY_REQUESTS = 429; + const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; + const STATUS_INTERNAL_SERVER_ERROR = 500; + const STATUS_NOT_IMPLEMENTED = 501; + const STATUS_BAD_GATEWAY = 502; + const STATUS_SERVICE_UNAVAILABLE = 503; + const STATUS_GATEWAY_TIMEOUT = 504; + const STATUS_HTTP_VERSION_NOT_SUPPORTED = 505; + const STATUS_VARIANT_ALSO_NEGOTIATES = 506; + const STATUS_INSUFFICIENT_STORAGE = 507; + const STATUS_LOOP_DETECTED = 508; + const STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509; + const STATUS_NOT_EXTENDED = 510; + const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; +} diff --git a/lib/appframework/http/jsonresponse.php b/lib/public/appframework/http/jsonresponse.php similarity index 98% rename from lib/appframework/http/jsonresponse.php rename to lib/public/appframework/http/jsonresponse.php index 750f8a2ad1..085fdbed2f 100644 --- a/lib/appframework/http/jsonresponse.php +++ b/lib/public/appframework/http/jsonresponse.php @@ -22,7 +22,7 @@ */ -namespace OC\AppFramework\Http; +namespace OCP\AppFramework\Http; /** diff --git a/lib/appframework/http/response.php b/lib/public/appframework/http/response.php similarity index 98% rename from lib/appframework/http/response.php rename to lib/public/appframework/http/response.php index 50778105f2..6447725894 100644 --- a/lib/appframework/http/response.php +++ b/lib/public/appframework/http/response.php @@ -22,7 +22,7 @@ */ -namespace OC\AppFramework\Http; +namespace OCP\AppFramework\Http; /** diff --git a/lib/appframework/http/templateresponse.php b/lib/public/appframework/http/templateresponse.php similarity index 98% rename from lib/appframework/http/templateresponse.php rename to lib/public/appframework/http/templateresponse.php index 0a32da4b1b..97678c96cb 100644 --- a/lib/appframework/http/templateresponse.php +++ b/lib/public/appframework/http/templateresponse.php @@ -22,7 +22,7 @@ */ -namespace OC\AppFramework\Http; +namespace OCP\AppFramework\Http; use OC\AppFramework\Core\API; From 38f9df429397619482e3e3f7ffb0db5274222e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 21 Aug 2013 01:02:15 +0200 Subject: [PATCH 018/180] introducing OCP\AppFramework\App --- lib/appframework/app.php | 3 +- lib/appframework/core/api.php | 3 +- .../dependencyinjection/dicontainer.php | 21 +- lib/public/appframework/App.php | 61 +++++ lib/public/appframework/iapi.php | 238 ++++++++++++++++++ lib/public/appframework/iappcontainer.php | 25 ++ 6 files changed, 348 insertions(+), 3 deletions(-) create mode 100644 lib/public/appframework/App.php create mode 100644 lib/public/appframework/iapi.php create mode 100644 lib/public/appframework/iappcontainer.php diff --git a/lib/appframework/app.php b/lib/appframework/app.php index 6224b858bb..7ff55bb809 100644 --- a/lib/appframework/app.php +++ b/lib/appframework/app.php @@ -25,6 +25,7 @@ namespace OC\AppFramework; use OC\AppFramework\DependencyInjection\DIContainer; +use OCP\AppFramework\IAppContainer; /** @@ -45,7 +46,7 @@ class App { * @param DIContainer $container an instance of a pimple container. */ public static function main($controllerName, $methodName, array $urlParams, - DIContainer $container) { + IAppContainer $container) { $container['urlParams'] = $urlParams; $controller = $container[$controllerName]; diff --git a/lib/appframework/core/api.php b/lib/appframework/core/api.php index eb8ee01e5d..337e3b57d6 100644 --- a/lib/appframework/core/api.php +++ b/lib/appframework/core/api.php @@ -23,6 +23,7 @@ namespace OC\AppFramework\Core; +use OCP\AppFramework\IApi; /** @@ -32,7 +33,7 @@ namespace OC\AppFramework\Core; * Should you find yourself in need for more methods, simply inherit from this * class and add your methods */ -class API { +class API implements IApi{ private $appName; diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 88ad2cd414..43f6eee29b 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -32,9 +32,11 @@ use OC\AppFramework\Middleware\MiddlewareDispatcher; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; +use OCP\AppFramework\IApi; +use OCP\AppFramework\IAppContainer; -class DIContainer extends SimpleContainer { +class DIContainer extends SimpleContainer implements IAppContainer{ /** @@ -45,6 +47,8 @@ class DIContainer extends SimpleContainer { $this['AppName'] = $appName; + $this->registerParameter('ServerContainer', \OC::$server); + $this['API'] = $this->share(function($c){ return new API($c['AppName']); }); @@ -119,4 +123,19 @@ class DIContainer extends SimpleContainer { } + /** + * @return IApi + */ + function getCoreApi() + { + return $this->query('API'); + } + + /** + * @return \OCP\Core\IServerContainer + */ + function getServer() + { + return $this->query('ServerContainer'); + } } diff --git a/lib/public/appframework/App.php b/lib/public/appframework/App.php new file mode 100644 index 0000000000..0c27fcb2ac --- /dev/null +++ b/lib/public/appframework/App.php @@ -0,0 +1,61 @@ +container = new \OC\AppFramework\DependencyInjection\DIContainer($appName); + } + + private $container; + + /** + * @return IAppContainer + */ + public function getContainer() { + return $this->container; + } + + /** + * This function is called by the routing component to fire up the frameworks dispatch mechanism. + * + * Example code in routes.php of the task app: + * $this->create('tasks_index', '/')->get()->action( + * function($params){ + * $app = new TaskApp(); + * $app->dispatch('PageController', 'index', $params); + * } + * ); + * + * + * Example for for TaskApp implementation: + * class TaskApp extends \OCP\AppFramework\App { + * + * public function __construct(){ + * parent::__construct('tasks'); + * + * $this->getContainer()->registerService('PageController', function(IAppContainer $c){ + * $a = $c->query('API'); + * $r = $c->query('Request'); + * return new PageController($a, $r); + * }); + * } + * } + * + * @param string $controllerName the name of the controller under which it is + * stored in the DI container + * @param string $methodName the method that you want to call + * @param array $urlParams an array with variables extracted from the routes + */ + public function dispatch($controllerName, $methodName, array $urlParams) { + \OC\AppFramework\App::main($controllerName, $methodName, $urlParams, $this->container); + } +} diff --git a/lib/public/appframework/iapi.php b/lib/public/appframework/iapi.php new file mode 100644 index 0000000000..5374f0dcaf --- /dev/null +++ b/lib/public/appframework/iapi.php @@ -0,0 +1,238 @@ +. + * + */ + + +namespace OCP\AppFramework; + + +/** + * A few very basic and frequently used API functions are combined in here + */ +interface IApi { + + /** + * used to return the appname of the set application + * @return string the name of your application + */ + function getAppName(); + + + /** + * Creates a new navigation entry + * @param array $entry containing: id, name, order, icon and href key + */ + function addNavigationEntry(array $entry); + + + /** + * Gets the userid of the current user + * @return string the user id of the current user + */ + function getUserId(); + + + /** + * Sets the current navigation entry to the currently running app + */ + function activateNavigationEntry(); + + + /** + * Adds a new javascript file + * @param string $scriptName the name of the javascript in js/ without the suffix + * @param string $appName the name of the app, defaults to the current one + */ + function addScript($scriptName, $appName = null); + + + /** + * Adds a new css file + * @param string $styleName the name of the css file in css/without the suffix + * @param string $appName the name of the app, defaults to the current one + */ + function addStyle($styleName, $appName = null); + + + /** + * shorthand for addScript for files in the 3rdparty directory + * @param string $name the name of the file without the suffix + */ + function add3rdPartyScript($name); + + + /** + * shorthand for addStyle for files in the 3rdparty directory + * @param string $name the name of the file without the suffix + */ + function add3rdPartyStyle($name); + + /** + * Looks up a system-wide defined value + * @param string $key the key of the value, under which it was saved + * @return string the saved value + */ + function getSystemValue($key); + + /** + * Sets a new system-wide value + * @param string $key the key of the value, under which will be saved + * @param string $value the value that should be stored + */ + function setSystemValue($key, $value); + + + /** + * Looks up an app-specific defined value + * @param string $key the key of the value, under which it was saved + * @return string the saved value + */ + function getAppValue($key, $appName = null); + + + /** + * Writes a new app-specific value + * @param string $key the key of the value, under which will be saved + * @param string $value the value that should be stored + */ + function setAppValue($key, $value, $appName = null); + + + /** + * Shortcut for setting a user defined value + * @param string $key the key under which the value is being stored + * @param string $value the value that you want to store + * @param string $userId the userId of the user that we want to store the value under, defaults to the current one + */ + function setUserValue($key, $value, $userId = null); + + + /** + * Shortcut for getting a user defined value + * @param string $key the key under which the value is being stored + * @param string $userId the userId of the user that we want to store the value under, defaults to the current one + */ + function getUserValue($key, $userId = null); + + /** + * Returns the translation object + * @return \OC_L10N the translation object + * + * FIXME: returns private object / should be retrieved from teh ServerContainer + */ + function getTrans(); + + + /** + * Used to abstract the owncloud database access away + * @param string $sql the sql query with ? placeholder for params + * @param int $limit the maximum number of rows + * @param int $offset from which row we want to start + * @return \OCP\DB a query object + * + * FIXME: returns non public interface / object + */ + function prepareQuery($sql, $limit=null, $offset=null); + + + /** + * Used to get the id of the just inserted element + * @param string $tableName the name of the table where we inserted the item + * @return int the id of the inserted element + * + * FIXME: move to db object + */ + function getInsertId($tableName); + + + /** + * Returns the URL for a route + * @param string $routeName the name of the route + * @param array $arguments an array with arguments which will be filled into the url + * @return string the url + */ + function linkToRoute($routeName, $arguments=array()); + + + /** + * Returns an URL for an image or file + * @param string $file the name of the file + * @param string $appName the name of the app, defaults to the current one + */ + function linkTo($file, $appName=null); + + + /** + * Returns the link to an image, like link to but only with prepending img/ + * @param string $file the name of the file + * @param string $appName the name of the app, defaults to the current one + */ + function imagePath($file, $appName = null); + + + /** + * Makes an URL absolute + * @param string $url the url + * @return string the absolute url + * + * FIXME: function should live in Request / Response + */ + function getAbsoluteURL($url); + + + /** + * links to a file + * @param string $file the name of the file + * @param string $appName the name of the app, defaults to the current one + * @deprecated replaced with linkToRoute() + * @return string the url + */ + function linkToAbsolute($file, $appName = null); + + + /** + * Checks if an app is enabled + * @param string $appName the name of an app + * @return bool true if app is enabled + */ + public function isAppEnabled($appName); + + + /** + * Writes a function into the error log + * @param string $msg the error message to be logged + * @param int $level the error level + * + * FIXME: add logger instance to ServerContainer + */ + function log($msg, $level = null); + + + /** + * Returns a template + * @param string $templateName the name of the template + * @param string $renderAs how it should be rendered + * @param string $appName the name of the app + * @return \OCP\Template a new template + */ + function getTemplate($templateName, $renderAs='user', $appName=null); +} diff --git a/lib/public/appframework/iappcontainer.php b/lib/public/appframework/iappcontainer.php new file mode 100644 index 0000000000..c2faea07b9 --- /dev/null +++ b/lib/public/appframework/iappcontainer.php @@ -0,0 +1,25 @@ + Date: Mon, 26 Aug 2013 23:48:18 +0200 Subject: [PATCH 019/180] as a quick example the public contacts API has been ported over as a service hosted within the server container --- lib/contactsmanager.php | 145 +++++++++++++++++++++++++ lib/public/contacts.php | 55 +++------- lib/public/core/contacts/imanager.php | 150 ++++++++++++++++++++++++++ lib/public/core/iservercontainer.php | 4 + lib/server.php | 15 ++- 5 files changed, 329 insertions(+), 40 deletions(-) create mode 100644 lib/contactsmanager.php create mode 100644 lib/public/core/contacts/imanager.php diff --git a/lib/contactsmanager.php b/lib/contactsmanager.php new file mode 100644 index 0000000000..59c413ec03 --- /dev/null +++ b/lib/contactsmanager.php @@ -0,0 +1,145 @@ +. + * + */ + +namespace OC { + + class ContactsManager implements \OCP\Core\Contacts\IManager { + + /** + * This function is used to search and find contacts within the users address books. + * In case $pattern is empty all contacts will be returned. + * + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - for future use. One should always have options! + * @return array of contacts which are arrays of key-value-pairs + */ + public function search($pattern, $searchProperties = array(), $options = array()) { + $result = array(); + foreach($this->address_books as $address_book) { + $r = $address_book->search($pattern, $searchProperties, $options); + $result = array_merge($result, $r); + } + + return $result; + } + + /** + * This function can be used to delete the contact identified by the given id + * + * @param object $id the unique identifier to a contact + * @param $address_book_key + * @return bool successful or not + */ + public function delete($id, $address_book_key) { + if (!array_key_exists($address_book_key, $this->address_books)) + return null; + + $address_book = $this->address_books[$address_book_key]; + if ($address_book->getPermissions() & \OCP\PERMISSION_DELETE) + return null; + + return $address_book->delete($id); + } + + /** + * This function is used to create a new contact if 'id' is not given or not present. + * Otherwise the contact will be updated by replacing the entire data set. + * + * @param array $properties this array if key-value-pairs defines a contact + * @param $address_book_key string to identify the address book in which the contact shall be created or updated + * @return array representing the contact just created or updated + */ + public function createOrUpdate($properties, $address_book_key) { + + if (!array_key_exists($address_book_key, $this->address_books)) + return null; + + $address_book = $this->address_books[$address_book_key]; + if ($address_book->getPermissions() & \OCP\PERMISSION_CREATE) + return null; + + return $address_book->createOrUpdate($properties); + } + + /** + * Check if contacts are available (e.g. contacts app enabled) + * + * @return bool true if enabled, false if not + */ + public function isEnabled() { + return !empty($this->address_books); + } + + /** + * @param \OCP\IAddressBook $address_book + */ + public function registerAddressBook(\OCP\IAddressBook $address_book) { + $this->address_books[$address_book->getKey()] = $address_book; + } + + /** + * @param \OCP\IAddressBook $address_book + */ + public function unregisterAddressBook(\OCP\IAddressBook $address_book) { + unset($this->address_books[$address_book->getKey()]); + } + + /** + * @return array + */ + public function getAddressBooks() { + $result = array(); + foreach($this->address_books as $address_book) { + $result[$address_book->getKey()] = $address_book->getDisplayName(); + } + + return $result; + } + + /** + * removes all registered address book instances + */ + public function clear() { + $this->address_books = array(); + } + + /** + * @var \OCP\IAddressBook[] which holds all registered address books + */ + private $address_books = array(); + + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * address books are actually requested + * + * @param string $key + * @param \Closure $callable + */ + function register($key, \Closure $callable) + { + // + //TODO: implement me + // + } + } +} diff --git a/lib/public/contacts.php b/lib/public/contacts.php index 88d812e735..1b61d7aa4f 100644 --- a/lib/public/contacts.php +++ b/lib/public/contacts.php @@ -90,13 +90,8 @@ namespace OCP { * @return array of contacts which are arrays of key-value-pairs */ public static function search($pattern, $searchProperties = array(), $options = array()) { - $result = array(); - foreach(self::$address_books as $address_book) { - $r = $address_book->search($pattern, $searchProperties, $options); - $result = array_merge($result, $r); - } - - return $result; + $cm = \OC::$server->getContactsManager(); + return $cm->search($pattern, $searchProperties, $options); } /** @@ -107,14 +102,8 @@ namespace OCP { * @return bool successful or not */ public static function delete($id, $address_book_key) { - if (!array_key_exists($address_book_key, self::$address_books)) - return null; - - $address_book = self::$address_books[$address_book_key]; - if ($address_book->getPermissions() & \OCP\PERMISSION_DELETE) - return null; - - return $address_book->delete($id); + $cm = \OC::$server->getContactsManager(); + return $cm->delete($id, $address_book_key); } /** @@ -126,15 +115,8 @@ namespace OCP { * @return array representing the contact just created or updated */ public static function createOrUpdate($properties, $address_book_key) { - - if (!array_key_exists($address_book_key, self::$address_books)) - return null; - - $address_book = self::$address_books[$address_book_key]; - if ($address_book->getPermissions() & \OCP\PERMISSION_CREATE) - return null; - - return $address_book->createOrUpdate($properties); + $cm = \OC::$server->getContactsManager(); + return $cm->search($properties, $address_book_key); } /** @@ -143,45 +125,40 @@ namespace OCP { * @return bool true if enabled, false if not */ public static function isEnabled() { - return !empty(self::$address_books); + $cm = \OC::$server->getContactsManager(); + return $cm->isEnabled(); } /** * @param \OCP\IAddressBook $address_book */ public static function registerAddressBook(\OCP\IAddressBook $address_book) { - self::$address_books[$address_book->getKey()] = $address_book; + $cm = \OC::$server->getContactsManager(); + return $cm->registerAddressBook($address_book); } /** * @param \OCP\IAddressBook $address_book */ public static function unregisterAddressBook(\OCP\IAddressBook $address_book) { - unset(self::$address_books[$address_book->getKey()]); + $cm = \OC::$server->getContactsManager(); + return $cm->unregisterAddressBook($address_book); } /** * @return array */ public static function getAddressBooks() { - $result = array(); - foreach(self::$address_books as $address_book) { - $result[$address_book->getKey()] = $address_book->getDisplayName(); - } - - return $result; + $cm = \OC::$server->getContactsManager(); + return $cm->getAddressBooks(); } /** * removes all registered address book instances */ public static function clear() { - self::$address_books = array(); + $cm = \OC::$server->getContactsManager(); + $cm->clear(); } - - /** - * @var \OCP\IAddressBook[] which holds all registered address books - */ - private static $address_books = array(); } } diff --git a/lib/public/core/contacts/imanager.php b/lib/public/core/contacts/imanager.php new file mode 100644 index 0000000000..4ae9d5766e --- /dev/null +++ b/lib/public/core/contacts/imanager.php @@ -0,0 +1,150 @@ +. + * + */ + +/** + * Public interface of ownCloud for apps to use. + * Contacts Class + * + */ + +// use OCP namespace for all classes that are considered public. +// This means that they should be used by apps instead of the internal ownCloud classes +namespace OCP\Core\Contacts { + + /** + * This class provides access to the contacts app. Use this class exclusively if you want to access contacts. + * + * Contacts in general will be expressed as an array of key-value-pairs. + * The keys will match the property names defined in https://tools.ietf.org/html/rfc2426#section-1 + * + * Proposed workflow for working with contacts: + * - search for the contacts + * - manipulate the results array + * - createOrUpdate will save the given contacts overwriting the existing data + * + * For updating it is mandatory to keep the id. + * Without an id a new contact will be created. + * + */ + interface IManager { + + /** + * This function is used to search and find contacts within the users address books. + * In case $pattern is empty all contacts will be returned. + * + * Example: + * Following function shows how to search for contacts for the name and the email address. + * + * public static function getMatchingRecipient($term) { + * $cm = \OC:$server->getContactsManager(); + * // The API is not active -> nothing to do + * if (!$cm->isEnabled()) { + * return array(); + * } + * + * $result = $cm->search($term, array('FN', 'EMAIL')); + * $receivers = array(); + * foreach ($result as $r) { + * $id = $r['id']; + * $fn = $r['FN']; + * $email = $r['EMAIL']; + * if (!is_array($email)) { + * $email = array($email); + * } + * + * // loop through all email addresses of this contact + * foreach ($email as $e) { + * $displayName = $fn . " <$e>"; + * $receivers[] = array( + * 'id' => $id, + * 'label' => $displayName, + * 'value' => $displayName); + * } + * } + * + * return $receivers; + * } + * + * + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - for future use. One should always have options! + * @return array of contacts which are arrays of key-value-pairs + */ + function search($pattern, $searchProperties = array(), $options = array()); + + /** + * This function can be used to delete the contact identified by the given id + * + * @param object $id the unique identifier to a contact + * @param $address_book_key + * @return bool successful or not + */ + function delete($id, $address_book_key); + + /** + * This function is used to create a new contact if 'id' is not given or not present. + * Otherwise the contact will be updated by replacing the entire data set. + * + * @param array $properties this array if key-value-pairs defines a contact + * @param $address_book_key string to identify the address book in which the contact shall be created or updated + * @return array representing the contact just created or updated + */ + function createOrUpdate($properties, $address_book_key); + + /** + * Check if contacts are available (e.g. contacts app enabled) + * + * @return bool true if enabled, false if not + */ + function isEnabled(); + + /** + * @param \OCP\IAddressBook $address_book + */ + function registerAddressBook(\OCP\IAddressBook $address_book); + + /** + * @param \OCP\IAddressBook $address_book + */ + function unregisterAddressBook(\OCP\IAddressBook $address_book); + + /** + * In order to improve lazy loading a closure can be registered which will be called in case + * address books are actually requested + * + * @param string $key + * @param \Closure $callable + */ + function register($key, \Closure $callable); + + /** + * @return array + */ + function getAddressBooks(); + + /** + * removes all registered address book instances + */ + function clear(); + } +} diff --git a/lib/public/core/iservercontainer.php b/lib/public/core/iservercontainer.php index df744ab6fd..464da19864 100644 --- a/lib/public/core/iservercontainer.php +++ b/lib/public/core/iservercontainer.php @@ -11,4 +11,8 @@ namespace OCP\Core; */ interface IServerContainer { + /** + * @return \OCP\Core\Contacts\IManager + */ + function getContactsManager(); } diff --git a/lib/server.php b/lib/server.php index f8f25c046d..72c82efe16 100644 --- a/lib/server.php +++ b/lib/server.php @@ -2,6 +2,7 @@ namespace OC; +use OC\AppFramework\Utility\SimpleContainer; use OCP\Core\IServerContainer; /** @@ -10,6 +11,18 @@ use OCP\Core\IServerContainer; * * TODO: hookup all manager classes */ -class Server implements IServerContainer { +class Server extends SimpleContainer implements IServerContainer { + function __construct() { + $this->registerService('ContactsManager', function($c){ + return new ContactsManager(); + }); + } + + /** + * @return \OCP\Core\Contacts\IManager + */ + function getContactsManager() { + return $this->query('ContactsManager'); + } } From 14b67d6c5f1e3cd538714a8b5f512dd34847e795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 29 Aug 2013 16:25:25 +0200 Subject: [PATCH 020/180] fixing typo --- lib/public/core/contacts/imanager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/core/contacts/imanager.php b/lib/public/core/contacts/imanager.php index 4ae9d5766e..e8bb7bfd8e 100644 --- a/lib/public/core/contacts/imanager.php +++ b/lib/public/core/contacts/imanager.php @@ -55,7 +55,7 @@ namespace OCP\Core\Contacts { * Following function shows how to search for contacts for the name and the email address. * * public static function getMatchingRecipient($term) { - * $cm = \OC:$server->getContactsManager(); + * $cm = \OC::$server->getContactsManager(); * // The API is not active -> nothing to do * if (!$cm->isEnabled()) { * return array(); From d7dca966a2a926be8b45ab337488143eac3ce9ba Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 30 Aug 2013 10:17:50 +0200 Subject: [PATCH 021/180] improved error messaging, initial commit --- apps/files_encryption/appinfo/database.xml | 7 +++ apps/files_encryption/appinfo/version | 2 +- apps/files_encryption/files/error.php | 2 +- apps/files_encryption/hooks/hooks.php | 29 +++++++----- apps/files_encryption/lib/helper.php | 44 ++++++++++++------ apps/files_encryption/lib/stream.php | 34 +++++++------- apps/files_encryption/lib/util.php | 52 ++++++++++++++++++++++ 7 files changed, 126 insertions(+), 44 deletions(-) diff --git a/apps/files_encryption/appinfo/database.xml b/apps/files_encryption/appinfo/database.xml index 4587930da0..cd5434b8c2 100644 --- a/apps/files_encryption/appinfo/database.xml +++ b/apps/files_encryption/appinfo/database.xml @@ -34,6 +34,13 @@ 0 Whether encryption migration has been performed + + initialized + integer + true + 0 + Did the user initialized the encryption app at least once + \ No newline at end of file diff --git a/apps/files_encryption/appinfo/version b/apps/files_encryption/appinfo/version index bd73f47072..2eb3c4fe4e 100644 --- a/apps/files_encryption/appinfo/version +++ b/apps/files_encryption/appinfo/version @@ -1 +1 @@ -0.4 +0.5 diff --git a/apps/files_encryption/files/error.php b/apps/files_encryption/files/error.php index 2dd27257ab..7a2bb1a281 100644 --- a/apps/files_encryption/files/error.php +++ b/apps/files_encryption/files/error.php @@ -4,7 +4,7 @@ if (!isset($_)) { //also provide standalone error page $l = OC_L10N::get('files_encryption'); - $errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); + $errorMsg = $l->t('Your private key is not valid! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app. If this doesn\'t help maybe your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); if(isset($_GET['p']) && $_GET['p'] === '1') { header('HTTP/1.0 404 ' . $errorMsg); diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index de306462d7..aefb274e1c 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -70,9 +70,11 @@ class Hooks { // If migration not yet done if ($ready) { + $util->setInitialized(Util::ENCRYPTION_INITIALIZED); + $userView = new \OC_FilesystemView('/' . $params['uid']); - // Set legacy encryption key if it exists, to support + // Set legacy encryption key if it exists, to support // depreciated encryption system if ( $userView->file_exists('encryption.key') @@ -143,6 +145,7 @@ class Hooks { * @brief If the password can't be changed within ownCloud, than update the key password in advance. */ public static function preSetPassphrase($params) { + return true; if ( ! \OC_User::canUserChangePassword($params['uid']) ) { self::setPassphrase($params); } @@ -153,7 +156,7 @@ class Hooks { * @param array $params keys: uid, password */ public static function setPassphrase($params) { - + return true; // Only attempt to change passphrase if server-side encryption // is in use (client-side encryption does not have access to // the necessary keys) @@ -248,7 +251,7 @@ class Hooks { $params['run'] = false; $params['error'] = $l->t('Following users are not set up for encryption:') . ' ' . join(', ' , $notConfigured); } - + } /** @@ -259,7 +262,7 @@ class Hooks { // NOTE: $params has keys: // [itemType] => file // itemSource -> int, filecache file ID - // [parent] => + // [parent] => // [itemTarget] => /13 // shareWith -> string, uid of user being shared to // fileTarget -> path of file being shared @@ -300,13 +303,13 @@ class Hooks { // NOTE: parent is folder but shared was a file! // we try to rebuild the missing path // some examples we face here - // user1 share folder1 with user2 folder1 has - // the following structure + // user1 share folder1 with user2 folder1 has + // the following structure // /folder1/subfolder1/subsubfolder1/somefile.txt // user2 re-share subfolder2 with user3 // user3 re-share somefile.txt user4 - // so our path should be - // /Shared/subfolder1/subsubfolder1/somefile.txt + // so our path should be + // /Shared/subfolder1/subsubfolder1/somefile.txt // while user3 is sharing if ($params['itemType'] === 'file') { @@ -537,14 +540,18 @@ class Hooks { } /** - * set migration status back to '0' so that all new files get encrypted + * set migration status and the init status back to '0' so that all new files get encrypted * if the app gets enabled again * @param array $params contains the app ID */ public static function preDisable($params) { if ($params['app'] === 'files_encryption') { - $query = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0'); - $query->execute(); + + $setMigrationStatus = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0'); + $setMigrationStatus->execute(); + + $setInitStatus = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `initialized`=0'); + $setInitStatus->execute(); } } diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 0209a5d18b..105c5357e9 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -199,12 +199,12 @@ class Helper { public static function stripUserFilesPath($path) { $trimmed = ltrim($path, '/'); $split = explode('/', $trimmed); - + // it is not a file relative to data/user/files if (count($split) < 3 || $split[1] !== 'files') { return false; } - + $sliced = array_slice($split, 2); $relPath = implode('/', $sliced); @@ -219,30 +219,46 @@ class Helper { public static function getPathToRealFile($path) { $trimmed = ltrim($path, '/'); $split = explode('/', $trimmed); - + if (count($split) < 3 || $split[1] !== "files_versions") { return false; } - + $sliced = array_slice($split, 2); $realPath = implode('/', $sliced); //remove the last .v $realPath = substr($realPath, 0, strrpos($realPath, '.v')); return $realPath; - } - + } + /** * @brief redirect to a error page */ - public static function redirectToErrorPage() { - $location = \OC_Helper::linkToAbsolute('apps/files_encryption/files', 'error.php'); - $post = 0; - if(count($_POST) > 0) { - $post = 1; + public static function redirectToErrorPage($util) { + + $l = \OC_L10N::get('files_encryption'); + + if ($util->getInitialized() === false) { + $errorMsg = $l->t('Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app.'); + } else { + $errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); } - header('Location: ' . $location . '?p=' . $post); - exit(); + + if(count($_POST) > 0) { + header('HTTP/1.0 404 ' . $errorMsg); + } + + // check if ajax request + if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { + \OCP\JSON::error(array('data' => array('message' => $errorMsg))); + } else { + header('HTTP/1.0 404 ' . $errorMsg); + $tmpl = new OC_Template('files_encryption', 'invalid_private_key', 'guest'); + $tmpl->printPage(); + } + + exit; } /** @@ -259,7 +275,7 @@ class Helper { return (bool) $result; } - + /** * check some common errors if the server isn't configured properly for encryption * @return bool true if configuration seems to be OK diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 335ea3733e..87b8dc3ee2 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -81,7 +81,7 @@ class Stream { * @return bool */ public function stream_open($path, $mode, $options, &$opened_path) { - + // assume that the file already exist before we decide it finally in getKey() $this->newFile = false; @@ -106,12 +106,12 @@ class Stream { if ($this->relPath === false) { $this->relPath = Helper::getPathToRealFile($this->rawPath); } - + if($this->relPath === false) { \OCP\Util::writeLog('Encryption library', 'failed to open file "' . $this->rawPath . '" expecting a path to user/files or to user/files_versions', \OCP\Util::ERROR); return false; } - + // Disable fileproxies so we can get the file size and open the source file without recursive encryption $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; @@ -131,7 +131,7 @@ class Stream { if($this->privateKey === false) { // if private key is not valid redirect user to a error page - \OCA\Encryption\Helper::redirectToErrorPage(); + \OCA\Encryption\Helper::redirectToErrorPage($util); } $this->size = $this->rootView->filesize($this->rawPath, $mode); @@ -272,7 +272,7 @@ class Stream { } else { $this->newFile = true; - + return false; } @@ -296,9 +296,9 @@ class Stream { return strlen($data); } - // Disable the file proxies so that encryption is not - // automatically attempted when the file is written to disk - - // we are handling that separately here and we don't want to + // Disable the file proxies so that encryption is not + // automatically attempted when the file is written to disk - + // we are handling that separately here and we don't want to // get into an infinite loop $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; @@ -311,7 +311,7 @@ class Stream { $pointer = ftell($this->handle); // Get / generate the keyfile for the file we're handling - // If we're writing a new file (not overwriting an existing + // If we're writing a new file (not overwriting an existing // one), save the newly generated keyfile if (!$this->getKey()) { @@ -319,7 +319,7 @@ class Stream { } - // If extra data is left over from the last round, make sure it + // If extra data is left over from the last round, make sure it // is integrated into the next 6126 / 8192 block if ($this->writeCache) { @@ -344,12 +344,12 @@ class Stream { if ($remainingLength < 6126) { // Set writeCache to contents of $data - // The writeCache will be carried over to the - // next write round, and added to the start of - // $data to ensure that written blocks are - // always the correct length. If there is still - // data in writeCache after the writing round - // has finished, then the data will be written + // The writeCache will be carried over to the + // next write round, and added to the start of + // $data to ensure that written blocks are + // always the correct length. If there is still + // data in writeCache after the writing round + // has finished, then the data will be written // to disk by $this->flush(). $this->writeCache = $data; @@ -363,7 +363,7 @@ class Stream { $encrypted = $this->preWriteEncrypt($chunk, $this->plainKey); - // Write the data chunk to disk. This will be + // Write the data chunk to disk. This will be // attended to the last data chunk if the file // being handled totals more than 6126 bytes fwrite($this->handle, $encrypted); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index b8d6862349..edb9564e73 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -37,6 +37,8 @@ class Util { const MIGRATION_IN_PROGRESS = -1; // migration is running const MIGRATION_OPEN = 0; // user still needs to be migrated + const ENCRYPTION_INITIALIZED = 1; + const ENCRYPTION_NOT_INITIALIZED = 0; private $view; // OC_FilesystemView object for filesystem operations private $userId; // ID of the currently logged-in user @@ -1216,6 +1218,56 @@ class Util { return $return; } + /** + * set remember if the encryption app was already initialized or not + * @param type $status + */ + public function setInitialized($status) { + $sql = 'UPDATE `*PREFIX*encryption` SET `initialized` = ? WHERE `uid` = ?'; + $args = array($status, $this->userId); + $query = \OCP\DB::prepare($sql); + $query->execute($args); + } + + /** + * set remember if the encryption app was already initialized or not + */ + public function getInitialized() { + $sql = 'SELECT `initialized` FROM `*PREFIX*encryption` WHERE `uid` = ?'; + $args = array($this->userId); + $query = \OCP\DB::prepare($sql); + + $result = $query->execute($args); + $initializedStatus = null; + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); + } else { + if ($result->numRows() > 0) { + $row = $result->fetchRow(); + if (isset($row['initialized'])) { + $initializedStatus = (int)$row['initialized']; + } + } + } + + // If no record is found + if (empty($initializedStatus)) { + \OCP\Util::writeLog('Encryption library', "Could not get initialized status for " . $this->userId . ", no record found", \OCP\Util::ERROR); + return false; + // If a record is found + } else { + return (bool)$initializedStatus; + } + + + + $sql = 'UPDATE `*PREFIX*encryption` SET `initialized` = ? WHERE `uid` = ?'; + $args = array($status, $this->userId); + $query = \OCP\DB::prepare($sql); + $query->execute($args); + } + /** * @brief close migration mode after users data has been encrypted successfully * @return boolean From 77adaee6457c3e17d0f0b32c74da4cdbfce60164 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 30 Aug 2013 13:53:49 +0200 Subject: [PATCH 022/180] enable user to inform recipients about a shared file by mail --- apps/files/index.php | 3 +- apps/files/templates/index.php | 1 + core/ajax/share.php | 106 +++++++++++++++++++++++++++++++-- core/css/share.css | 4 +- core/js/share.js | 39 ++++++++++-- db_structure.xml | 8 +++ lib/defaults.php | 31 +++++++++- lib/public/defaults.php | 19 ++++++ lib/public/share.php | 81 ++++++++++++++++++++++--- lib/util.php | 16 ++--- settings/admin.php | 5 +- settings/templates/admin.php | 10 +++- 12 files changed, 292 insertions(+), 31 deletions(-) diff --git a/apps/files/index.php b/apps/files/index.php index e4d8e35385..7f5f9ec474 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -131,7 +131,7 @@ if ($needUpgrade) { if ($trashEnabled) { $trashEmpty = \OCA\Files_Trashbin\Trashbin::isEmpty($user); } - + OCP\Util::addscript('files', 'fileactions'); OCP\Util::addscript('files', 'files'); OCP\Util::addscript('files', 'keyboardshortcuts'); @@ -151,5 +151,6 @@ if ($needUpgrade) { $tmpl->assign('isPublic', false); $tmpl->assign('publicUploadEnabled', $publicUploadEnabled); $tmpl->assign("encryptedFiles", \OCP\Util::encryptedFiles()); + $tmpl->assign("mailNotificationEnabled", \OC_Appconfig::getValue('core', 'shareapi_allow_mail_notification', 'yes')); $tmpl->printPage(); } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 360874103f..e3fcecbe47 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -120,3 +120,4 @@ + diff --git a/core/ajax/share.php b/core/ajax/share.php index d3c6a8456a..3f6a995326 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -23,6 +23,8 @@ OC_JSON::checkLoggedIn(); OCP\JSON::callCheck(); OC_App::loadApps(); +$defaults = new \OCP\Defaults(); + if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSource'])) { switch ($_POST['action']) { case 'share': @@ -33,7 +35,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith == '') { $shareWith = null; } - + $token = OCP\Share::shareItem( $_POST['itemType'], $_POST['itemSource'], @@ -41,7 +43,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $shareWith, $_POST['permissions'] ); - + if (is_string($token)) { OC_JSON::success(array('data' => array('token' => $token))); } else { @@ -81,6 +83,102 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo ($return) ? OC_JSON::success() : OC_JSON::error(); } break; + case 'informRecipients': + + $l = OC_L10N::get('core'); + + $shareType = (int) $_POST['shareType']; + $itemType = $_POST['itemType']; + $itemSource = $_POST['itemSource']; + $recipient = $_POST['recipient']; + $from = \OCP\Util::getDefaultEmailAddress('sharing-noreply'); + $subject = $defaults->getShareNotificationSubject($itemType); + + $noMail = array(); + $recipientList = array(); + + if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $users = \OC_Group::usersInGroup($recipient); + foreach ($users as $user) { + $email = OC_Preferences::getValue($user, 'settings', 'email', ''); + if ($email !== '' || $recipient === \OCP\User::getUser()) { + $recipientList[] = array( + 'email' => $email, + 'displayName' => \OCP\User::getDisplayName($user), + 'uid' => $user, + ); + } else { + $noMail[] = \OCP\User::getDisplayName($user); + } + } + } else { // shared to a single user + $email = OC_Preferences::getValue($recipient, 'settings', 'email', ''); + if ($email !== '') { + $recipientList[] = array( + 'email' => $email, + 'displayName' => \OCP\User::getDisplayName($recipient), + 'uid' => $recipient, + ); + } else { + $noMail[] = \OCP\User::getDisplayName($recipient); + } + } + + // send mail to all recipients with an email address + foreach ($recipientList as $recipient) { + //get correct target folder name + + $users = \OCP\Share::getItemSharedWithUser($itemType, $itemSource, $recipient['uid']); + $targetName = $users[0]['file_target']; + + //$share = $shareManager->getShares($itemType, array('shareWith' => $recipient['uid'], 'isShareWithUser' => true, 'itemSource' => $itemSource)); + //$targetName = $share[0]->getItemTarget(); + if ($itemType === 'folder') { + $foldername = "/Shared/" . $targetName; + $filename = $targetName; + } else { + // if it is a file we can just link to the Shared folder, + // that's the place where the user will find the file + $foldername = "/Shared"; + $filename = $targetName; + } + + $url = \OCP\Util::linkToAbsolute('files', 'index.php', array("dir" => $foldername)); + $text = $defaults->getShareNotificationText(\OCP\User::getDisplayName(), $filename, $itemType, $url); + + try { + OCP\Util::sendMail( + $recipient['email'], + $recipient['displayName'], + $subject, + $text, + $from, + \OCP\User::getDisplayName() + ); + } catch (Exception $exception) { + $noMail[] = \OCP\User::getDisplayName($recipient['displayName']); + } + } + + \OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, true); + + if (empty($noMail)) { + OCP\JSON::success(); + } else { + OCP\JSON::error(array('data' => array('message' => $l->t("Couldn't send mail to following users: %s ", implode(', ', $noMail))))); + } + break; + case 'informRecipientsDisabled': + $itemSource = $_POST['itemSource']; + $itemType = $_POST['itemType']; + $recipient = $_POST['recipient']; + //$share = $shareManager->getShares($itemType, array('shareWith' => $recipient, 'isShareWithUser' => true, 'itemSource' => $itemSource)); + //$share[0]->setMailSend(false); + //$shareManager->update($share[0]); + //write status to db + OCP\JSON::success(); + break; + case 'email': // read post variables $user = OCP\USER::getUser(); @@ -213,10 +311,10 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } } $count = 0; - + // enable l10n support $l = OC_L10N::get('core'); - + foreach ($groups as $group) { if ($count < 15) { if (stripos($group, $_GET['search']) !== false diff --git a/core/css/share.css b/core/css/share.css index 2d6849b4bb..b6c5a0c139 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -11,7 +11,7 @@ margin-right:7em; position:absolute; right:0; - width:19em; + width:25em; z-index:500; padding:1em; } @@ -24,7 +24,7 @@ #shareWithList li { padding-top:.1em; } - + #shareWithList li:first-child { white-space:normal; } diff --git a/core/js/share.js b/core/js/share.js index 27c16f38b9..c806d83f10 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -217,9 +217,9 @@ OC.Share={ OC.Share.showLink(share.token, share.share_with, itemSource); } else { if (share.collection) { - OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.collection); + OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.mail_send, share.collection); } else { - OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, false); + OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.mail_send, share.permissions, possiblePermissions, share.mail_send, false); } } if (share.expiration != null) { @@ -299,7 +299,7 @@ OC.Share={ } }); }, - addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, collection) { + addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend, collection) { if (!OC.Share.itemShares[shareType]) { OC.Share.itemShares[shareType] = []; } @@ -341,6 +341,14 @@ OC.Share={ }else{ html += escapeHTML(shareWithDisplayName); } + mailNotificationEnabled = $('input:hidden[name=mailNotificationEnabled]').val(); + if (mailNotificationEnabled === 'yes') { + checked = ''; + if (mailSend === true) { + checked = 'checked'; + } + html += ''+t('core', 'notify user by email')+''; + } if (possiblePermissions & OC.PERMISSION_CREATE || possiblePermissions & OC.PERMISSION_UPDATE || possiblePermissions & OC.PERMISSION_DELETE) { if (editChecked == '') { html += ''; diff --git a/lib/public/share.php b/lib/public/share.php index eac6fab2b6..c2dd0096ab 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -1030,19 +1030,19 @@ class Share { if ($format == self::FORMAT_STATUSES) { if ($itemType == 'file' || $itemType == 'folder') { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,' - .' `share_type`, `file_source`, `path`, `expiration`, `storage`'; + .' `share_type`, `file_source`, `path`, `expiration`, `storage`, `mail_send`'; } else { - $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`'; + $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `expiration`, `mail_send`'; } } else { if (isset($uidOwner)) { if ($itemType == 'file' || $itemType == 'folder') { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,' .' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,' - .' `expiration`, `token`, `storage`'; + .' `expiration`, `token`, `storage`, `storage`, `mail_send`'; } else { $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,' - .' `stime`, `file_source`, `expiration`, `token`'; + .' `stime`, `file_source`, `expiration`, `token`, `storage`, `mail_send`'; } } else { if ($fileDependent) { @@ -1053,11 +1053,11 @@ class Share { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, ' .'`share_type`, `share_with`, `file_source`, `path`, `file_target`, ' .'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' - .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`'; + .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `storage`, `mail_send`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, - `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`'; + `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `storage`, `mail_send`'; } } else { $select = '*'; From 36574241f821f0cbef2f52032b8187b99c5fce94 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 30 Aug 2013 16:21:52 +0200 Subject: [PATCH 024/180] some clean-up --- core/ajax/share.php | 85 ++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/core/ajax/share.php b/core/ajax/share.php index 3f6a995326..9727f7d02e 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -97,66 +97,47 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $noMail = array(); $recipientList = array(); - if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { - $users = \OC_Group::usersInGroup($recipient); - foreach ($users as $user) { - $email = OC_Preferences::getValue($user, 'settings', 'email', ''); - if ($email !== '' || $recipient === \OCP\User::getUser()) { - $recipientList[] = array( - 'email' => $email, - 'displayName' => \OCP\User::getDisplayName($user), - 'uid' => $user, - ); - } else { - $noMail[] = \OCP\User::getDisplayName($user); - } - } - } else { // shared to a single user - $email = OC_Preferences::getValue($recipient, 'settings', 'email', ''); - if ($email !== '') { - $recipientList[] = array( - 'email' => $email, - 'displayName' => \OCP\User::getDisplayName($recipient), - 'uid' => $recipient, - ); - } else { - $noMail[] = \OCP\User::getDisplayName($recipient); - } + if($shareType === \OCP\Share::SHARE_TYPE_USER) { + $recipientList[] = $recipient; + } elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $recipientList = \OC_Group::usersInGroup($recipient); } // send mail to all recipients with an email address foreach ($recipientList as $recipient) { //get correct target folder name + $email = OC_Preferences::getValue($recipient, 'settings', 'email', ''); - $users = \OCP\Share::getItemSharedWithUser($itemType, $itemSource, $recipient['uid']); - $targetName = $users[0]['file_target']; + if ($email !== '') { + $displayName = \OCP\User::getDisplayName($recipient); + $items = \OCP\Share::getItemSharedWithUser($itemType, $itemSource, $recipient); + $filename = $items[0]['file_target']; - //$share = $shareManager->getShares($itemType, array('shareWith' => $recipient['uid'], 'isShareWithUser' => true, 'itemSource' => $itemSource)); - //$targetName = $share[0]->getItemTarget(); - if ($itemType === 'folder') { - $foldername = "/Shared/" . $targetName; - $filename = $targetName; + if ($itemType === 'folder') { + $foldername = "/Shared/" . $filename; + } else { + // if it is a file we can just link to the Shared folder, + // that's the place where the user will find the file + $foldername = "/Shared"; + } + + $url = \OCP\Util::linkToAbsolute('files', 'index.php', array("dir" => $foldername)); + $text = $defaults->getShareNotificationText(\OCP\User::getDisplayName(), $filename, $itemType, $url); + + try { + OCP\Util::sendMail( + $email, + $displayName, + $subject, + $text, + $from, + \OCP\User::getDisplayName() + ); + } catch (Exception $exception) { + $noMail[] = \OCP\User::getDisplayName($recipient['displayName']); + } } else { - // if it is a file we can just link to the Shared folder, - // that's the place where the user will find the file - $foldername = "/Shared"; - $filename = $targetName; - } - - $url = \OCP\Util::linkToAbsolute('files', 'index.php', array("dir" => $foldername)); - $text = $defaults->getShareNotificationText(\OCP\User::getDisplayName(), $filename, $itemType, $url); - - try { - OCP\Util::sendMail( - $recipient['email'], - $recipient['displayName'], - $subject, - $text, - $from, - \OCP\User::getDisplayName() - ); - } catch (Exception $exception) { - $noMail[] = \OCP\User::getDisplayName($recipient['displayName']); + $noMail[] = \OCP\User::getDisplayName($recipient); } } From 65ddefc89d6730f3c32727bddae2895232c66a62 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 30 Aug 2013 16:29:22 +0200 Subject: [PATCH 025/180] set sendMail status back to false --- core/ajax/share.php | 8 +++----- core/js/share.js | 4 +--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/core/ajax/share.php b/core/ajax/share.php index 9727f7d02e..76a67f5472 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -134,7 +134,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo \OCP\User::getDisplayName() ); } catch (Exception $exception) { - $noMail[] = \OCP\User::getDisplayName($recipient['displayName']); + $noMail[] = \OCP\User::getDisplayName($recipient); } } else { $noMail[] = \OCP\User::getDisplayName($recipient); @@ -151,12 +151,10 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; case 'informRecipientsDisabled': $itemSource = $_POST['itemSource']; + $shareType = $_POST['shareType']; $itemType = $_POST['itemType']; $recipient = $_POST['recipient']; - //$share = $shareManager->getShares($itemType, array('shareWith' => $recipient, 'isShareWithUser' => true, 'itemSource' => $itemSource)); - //$share[0]->setMailSend(false); - //$shareManager->update($share[0]); - //write status to db + \OCP\Share::setSendMailStatus($itemType, $itemSource, $shareType, false); OCP\JSON::success(); break; diff --git a/core/js/share.js b/core/js/share.js index 7d7f580c9b..e253f77ef2 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -695,9 +695,7 @@ $(document).ready(function() { } }); - $(document).on('click', '#dropdown input[name=mailNotification]', function(event) { - event.preventDefault(); - event.stopPropagation(); + $(document).on('click', '#dropdown input[name=mailNotification]', function() { var li = $(this).parent(); var itemType = $('#dropdown').data('item-type'); var itemSource = $('#dropdown').data('item-source'); From e7959f4fd23ff3354db7b876db2c3e595044bc4c Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 30 Aug 2013 16:52:06 +0200 Subject: [PATCH 026/180] don't send mail to the user who shared the file --- core/ajax/share.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/ajax/share.php b/core/ajax/share.php index 76a67f5472..0cf4b246f9 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -103,6 +103,9 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo $recipientList = \OC_Group::usersInGroup($recipient); } + // don't send a mail to the user who shared the file + array_diff($recipientList, [\OCP\User::getUser()]); + // send mail to all recipients with an email address foreach ($recipientList as $recipient) { //get correct target folder name From 4bbefdf608fdf930fa6fd1f783d6f58267752394 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 30 Aug 2013 17:20:10 +0200 Subject: [PATCH 027/180] add expiration date if it is already set --- core/ajax/share.php | 8 ++++++-- lib/defaults.php | 11 ++++++++--- lib/public/defaults.php | 5 +++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/core/ajax/share.php b/core/ajax/share.php index 0cf4b246f9..8b5191e655 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -114,7 +114,11 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo if ($email !== '') { $displayName = \OCP\User::getDisplayName($recipient); $items = \OCP\Share::getItemSharedWithUser($itemType, $itemSource, $recipient); - $filename = $items[0]['file_target']; + $filename = trim($items[0]['file_target'], '/'); + $expiration = null; + if (isset($items[0]['expiration'])) { + $expiration = $items[0]['expiration']; + } if ($itemType === 'folder') { $foldername = "/Shared/" . $filename; @@ -125,7 +129,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } $url = \OCP\Util::linkToAbsolute('files', 'index.php', array("dir" => $foldername)); - $text = $defaults->getShareNotificationText(\OCP\User::getDisplayName(), $filename, $itemType, $url); + $text = $defaults->getShareNotificationText(\OCP\User::getDisplayName(), $filename, $itemType, $url, $expiration); try { OCP\Util::sendMail( diff --git a/lib/defaults.php b/lib/defaults.php index 26f417ae2a..0685fbb16c 100644 --- a/lib/defaults.php +++ b/lib/defaults.php @@ -65,12 +65,17 @@ class OC_Defaults { * @param string $itemName name of the file/folder * @param string $itemType typically "file" or "folder" * @param string $link link directly to the file/folder in your ownCloud + * @param string $expiration expiration date */ - public function getShareNotificationText($sender, $itemName, $itemType, $link) { + public function getShareNotificationText($sender, $itemName, $itemType, $link, $expiration=null) { if ($this->themeExist('getShareNotificationText')) { - return $this->theme->getShareNotificationText($sender, $itemName, $itemType, $link); + return $this->theme->getShareNotificationText($sender, $itemName, $itemType, $link, $expiration); } else { - return $this->l->t("%s shared a %s called %s with you. You can find the %s here: %s", array($sender, $itemType, $itemName, $itemType, $link)); + if ($expiration) { + return $this->l->t("%s shared a %s called %s with you. The share will expire at %s. You can find the %s here: %s", array($sender, $itemType, $itemName, $expiration, $itemType, $link)); + } else { + return $this->l->t("%s shared a %s called %s with you. You can find the %s here: %s", array($sender, $itemType, $itemName, $itemType, $link)); + } } } diff --git a/lib/public/defaults.php b/lib/public/defaults.php index 9c8c3c0bda..573831e8ea 100644 --- a/lib/public/defaults.php +++ b/lib/public/defaults.php @@ -48,9 +48,10 @@ class Defaults { * @param string $itemName name of the file/folder * @param string $itemType typically "file" or "folder" * @param string $link link directly to the file/folder in your ownCloud + * @param string $expiration expiration date */ - public function getShareNotificationText($sender, $itemName, $itemType, $link) { - return $this->defaults->getShareNotificationText($sender, $itemName, $itemType, $link); + public function getShareNotificationText($sender, $itemName, $itemType, $link, $expiration) { + return $this->defaults->getShareNotificationText($sender, $itemName, $itemType, $link, $expiration); } /** From ec9b7d1e845527957aaaf6b235227b4e5c3f033d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 31 Aug 2013 01:41:24 +0200 Subject: [PATCH 028/180] fixing file header --- lib/public/appframework/App.php | 20 ++++++++++++++++++++ lib/public/appframework/iappcontainer.php | 20 ++++++++++++++++++++ lib/public/core/icontainer.php | 20 ++++++++++++++++++++ lib/public/core/irequest.php | 23 ++++++++++++++++++----- lib/public/core/iservercontainer.php | 20 ++++++++++++++++++++ 5 files changed, 98 insertions(+), 5 deletions(-) diff --git a/lib/public/appframework/App.php b/lib/public/appframework/App.php index 0c27fcb2ac..d97c5c8184 100644 --- a/lib/public/appframework/App.php +++ b/lib/public/appframework/App.php @@ -1,4 +1,24 @@ . + * + */ namespace OCP\AppFramework; diff --git a/lib/public/appframework/iappcontainer.php b/lib/public/appframework/iappcontainer.php index c2faea07b9..db909241e5 100644 --- a/lib/public/appframework/iappcontainer.php +++ b/lib/public/appframework/iappcontainer.php @@ -1,4 +1,24 @@ . + * + */ namespace OCP\AppFramework; diff --git a/lib/public/core/icontainer.php b/lib/public/core/icontainer.php index a6c93abec6..8c4a63424b 100644 --- a/lib/public/core/icontainer.php +++ b/lib/public/core/icontainer.php @@ -1,4 +1,24 @@ . + * + */ namespace OCP\Core; diff --git a/lib/public/core/irequest.php b/lib/public/core/irequest.php index fc2004d183..6103215842 100644 --- a/lib/public/core/irequest.php +++ b/lib/public/core/irequest.php @@ -1,10 +1,23 @@ . + * */ namespace OCP\Core; diff --git a/lib/public/core/iservercontainer.php b/lib/public/core/iservercontainer.php index 464da19864..e169990a3f 100644 --- a/lib/public/core/iservercontainer.php +++ b/lib/public/core/iservercontainer.php @@ -1,4 +1,24 @@ . + * + */ namespace OCP\Core; From 97bdf008b1cefaa092e23fc5a9bad787e755ed77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 31 Aug 2013 20:57:16 +0200 Subject: [PATCH 029/180] PHPDoc added to existing interfaces --- lib/public/core/icontainer.php | 25 +++++++++++++++++++++++++ lib/public/core/irequest.php | 8 ++++++-- lib/public/core/iservercontainer.php | 13 +++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/public/core/icontainer.php b/lib/public/core/icontainer.php index 8c4a63424b..88ebc6cf64 100644 --- a/lib/public/core/icontainer.php +++ b/lib/public/core/icontainer.php @@ -31,9 +31,34 @@ namespace OCP\Core; */ interface IContainer { + /** + * Look up a service for a given name in the container. + * + * @param string $name + * @return mixed + */ function query($name); + /** + * A value is stored in the container with it's corresponding name + * + * @param string $name + * @param mixed $value + * @return void + */ function registerParameter($name, $value); + /** + * A service is registered in the container where a closure is passed in which will actually + * create the service on demand. + * In case the parameter $shared is set to true (the default usage) the once created service will remain in + * memory and be reused on subsequent calls. + * In case the parameter is false the service will be recreated on every call. + * + * @param string $name + * @param callable $closure + * @param bool $shared + * @return void + */ function registerService($name, \Closure $closure, $shared = true); } diff --git a/lib/public/core/irequest.php b/lib/public/core/irequest.php index 6103215842..be60978a3c 100644 --- a/lib/public/core/irequest.php +++ b/lib/public/core/irequest.php @@ -45,6 +45,7 @@ interface IRequest { /** * Returns all params that were received, be it from the request + * * (as GET or POST) or through the URL by the route * @return array the array with all parameters */ @@ -52,12 +53,14 @@ interface IRequest { /** * Returns the method of the request + * * @return string the method of the request (POST, GET, etc) */ public function getMethod(); /** * Shortcut for accessing an uploaded file through the $_FILES array + * * @param string $key the key that will be taken from the $_FILES array * @return array the file in the $_FILES element */ @@ -66,6 +69,7 @@ interface IRequest { /** * Shortcut for getting env variables + * * @param string $key the key that will be taken from the $_ENV array * @return array the value in the $_ENV element */ @@ -74,6 +78,7 @@ interface IRequest { /** * Shortcut for getting session variables + * * @param string $key the key that will be taken from the $_SESSION array * @return array the value in the $_SESSION element */ @@ -82,6 +87,7 @@ interface IRequest { /** * Shortcut for getting cookie variables + * * @param string $key the key that will be taken from the $_COOKIE array * @return array the value in the $_COOKIE element */ @@ -92,9 +98,7 @@ interface IRequest { * Returns the request body content. * * @param Boolean $asResource If true, a resource will be returned - * * @return string|resource The request body content or a resource to read the body stream. - * * @throws \LogicException */ function getContent($asResource = false); diff --git a/lib/public/core/iservercontainer.php b/lib/public/core/iservercontainer.php index e169990a3f..0517cc53e0 100644 --- a/lib/public/core/iservercontainer.php +++ b/lib/public/core/iservercontainer.php @@ -32,7 +32,20 @@ namespace OCP\Core; interface IServerContainer { /** + * The contacts manager will act as a broker between consumers for contacts information and + * providers which actual deliver the contact information. + * * @return \OCP\Core\Contacts\IManager */ function getContactsManager(); + + /** + * The current request object holding all information about the request currently being processed + * is returned from this method. + * In case the current execution was not initiated by a web request null is returned + * + * @return \OCP\Core\IRequest|null + */ + function getRequest(); + } From 206f83941b26b16f89e695ae84b998e9cf11132a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 31 Aug 2013 21:34:29 +0200 Subject: [PATCH 030/180] move new interfaces into lib/public and OCP --- .../dependencyinjection/dicontainer.php | 2 +- lib/appframework/http/request.php | 2 +- lib/appframework/utility/simplecontainer.php | 2 +- lib/contactsmanager.php | 2 +- lib/public/appframework/iappcontainer.php | 4 ++-- lib/public/{core => }/contacts/imanager.php | 2 +- lib/public/{core => }/icontainer.php | 4 ++-- lib/public/{core => }/irequest.php | 2 +- lib/public/{core => }/iservercontainer.php | 8 ++++---- lib/server.php | 16 ++++++++++++++-- 10 files changed, 28 insertions(+), 16 deletions(-) rename lib/public/{core => }/contacts/imanager.php (99%) rename lib/public/{core => }/icontainer.php (97%) rename lib/public/{core => }/irequest.php (99%) rename lib/public/{core => }/iservercontainer.php (92%) diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 43f6eee29b..2ef885d7b2 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -132,7 +132,7 @@ class DIContainer extends SimpleContainer implements IAppContainer{ } /** - * @return \OCP\Core\IServerContainer + * @return \OCP\IServerContainer */ function getServer() { diff --git a/lib/appframework/http/request.php b/lib/appframework/http/request.php index ab72a8db69..4f1775182a 100644 --- a/lib/appframework/http/request.php +++ b/lib/appframework/http/request.php @@ -22,7 +22,7 @@ namespace OC\AppFramework\Http; -use OCP\Core\IRequest; +use OCP\IRequest; /** * Class for accessing variables in the request. diff --git a/lib/appframework/utility/simplecontainer.php b/lib/appframework/utility/simplecontainer.php index 04b6cd727b..a51ace83a3 100644 --- a/lib/appframework/utility/simplecontainer.php +++ b/lib/appframework/utility/simplecontainer.php @@ -10,7 +10,7 @@ require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php'; * * SimpleContainer is a simple implementation of IContainer on basis of \Pimple */ -class SimpleContainer extends \Pimple implements \OCP\Core\IContainer { +class SimpleContainer extends \Pimple implements \OCP\IContainer { /** * @param string $name name of the service to query for diff --git a/lib/contactsmanager.php b/lib/contactsmanager.php index 59c413ec03..fc6745b450 100644 --- a/lib/contactsmanager.php +++ b/lib/contactsmanager.php @@ -22,7 +22,7 @@ namespace OC { - class ContactsManager implements \OCP\Core\Contacts\IManager { + class ContactsManager implements \OCP\Contacts\IManager { /** * This function is used to search and find contacts within the users address books. diff --git a/lib/public/appframework/iappcontainer.php b/lib/public/appframework/iappcontainer.php index db909241e5..c8f6229dd9 100644 --- a/lib/public/appframework/iappcontainer.php +++ b/lib/public/appframework/iappcontainer.php @@ -23,7 +23,7 @@ namespace OCP\AppFramework; use OCP\AppFramework\IApi; -use OCP\Core\IContainer; +use OCP\IContainer; /** * Class IAppContainer @@ -39,7 +39,7 @@ interface IAppContainer extends IContainer{ function getCoreApi(); /** - * @return \OCP\Core\IServerContainer + * @return \OCP\IServerContainer */ function getServer(); } diff --git a/lib/public/core/contacts/imanager.php b/lib/public/contacts/imanager.php similarity index 99% rename from lib/public/core/contacts/imanager.php rename to lib/public/contacts/imanager.php index e8bb7bfd8e..3bfbca7be5 100644 --- a/lib/public/core/contacts/imanager.php +++ b/lib/public/contacts/imanager.php @@ -28,7 +28,7 @@ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes -namespace OCP\Core\Contacts { +namespace OCP\Contacts { /** * This class provides access to the contacts app. Use this class exclusively if you want to access contacts. diff --git a/lib/public/core/icontainer.php b/lib/public/icontainer.php similarity index 97% rename from lib/public/core/icontainer.php rename to lib/public/icontainer.php index 88ebc6cf64..d43c1c90f1 100644 --- a/lib/public/core/icontainer.php +++ b/lib/public/icontainer.php @@ -20,14 +20,14 @@ * */ -namespace OCP\Core; +namespace OCP; /** * Class IContainer * * IContainer is the basic interface to be used for any internal dependency injection mechanism * - * @package OCP\Core + * @package OCP */ interface IContainer { diff --git a/lib/public/core/irequest.php b/lib/public/irequest.php similarity index 99% rename from lib/public/core/irequest.php rename to lib/public/irequest.php index be60978a3c..cd39855950 100644 --- a/lib/public/core/irequest.php +++ b/lib/public/irequest.php @@ -20,7 +20,7 @@ * */ -namespace OCP\Core; +namespace OCP; interface IRequest { diff --git a/lib/public/core/iservercontainer.php b/lib/public/iservercontainer.php similarity index 92% rename from lib/public/core/iservercontainer.php rename to lib/public/iservercontainer.php index 0517cc53e0..5f5b967754 100644 --- a/lib/public/core/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -20,12 +20,12 @@ * */ -namespace OCP\Core; +namespace OCP; /** * Class IServerContainer - * @package OCP\Core + * @package OCP * * This container holds all ownCloud services */ @@ -35,7 +35,7 @@ interface IServerContainer { * The contacts manager will act as a broker between consumers for contacts information and * providers which actual deliver the contact information. * - * @return \OCP\Core\Contacts\IManager + * @return \OCP\Contacts\IManager */ function getContactsManager(); @@ -44,7 +44,7 @@ interface IServerContainer { * is returned from this method. * In case the current execution was not initiated by a web request null is returned * - * @return \OCP\Core\IRequest|null + * @return \OCP\IRequest|null */ function getRequest(); diff --git a/lib/server.php b/lib/server.php index 72c82efe16..ad955bf5c6 100644 --- a/lib/server.php +++ b/lib/server.php @@ -3,7 +3,7 @@ namespace OC; use OC\AppFramework\Utility\SimpleContainer; -use OCP\Core\IServerContainer; +use OCP\IServerContainer; /** * Class Server @@ -20,9 +20,21 @@ class Server extends SimpleContainer implements IServerContainer { } /** - * @return \OCP\Core\Contacts\IManager + * @return \OCP\Contacts\IManager */ function getContactsManager() { return $this->query('ContactsManager'); } + + /** + * The current request object holding all information about the request currently being processed + * is returned from this method. + * In case the current execution was not initiated by a web request null is returned + * + * @return \OCP\IRequest|null + */ + function getRequest() + { + return $this->query('Request'); + } } From bab63c22eea058ea619de5c021d16803ba48ab8d Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 11:26:11 +0200 Subject: [PATCH 031/180] encryption error messages, distinguish between a re-enabled encryption app and a password change from outside --- apps/files_encryption/appinfo/database.xml | 7 --- apps/files_encryption/hooks/hooks.php | 8 +-- apps/files_encryption/lib/helper.php | 4 +- apps/files_encryption/lib/session.php | 27 +++++++++ apps/files_encryption/lib/stream.php | 2 +- apps/files_encryption/lib/util.php | 60 ++----------------- apps/files_encryption/settings-personal.php | 5 +- .../templates/settings-personal.php | 10 ++-- settings/ajax/changepassword.php | 2 +- settings/templates/personal.php | 2 +- 10 files changed, 48 insertions(+), 79 deletions(-) diff --git a/apps/files_encryption/appinfo/database.xml b/apps/files_encryption/appinfo/database.xml index cd5434b8c2..4587930da0 100644 --- a/apps/files_encryption/appinfo/database.xml +++ b/apps/files_encryption/appinfo/database.xml @@ -34,13 +34,6 @@ 0 Whether encryption migration has been performed - - initialized - integer - true - 0 - Did the user initialized the encryption app at least once - \ No newline at end of file diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index aefb274e1c..4c6122b7c2 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -70,8 +70,6 @@ class Hooks { // If migration not yet done if ($ready) { - $util->setInitialized(Util::ENCRYPTION_INITIALIZED); - $userView = new \OC_FilesystemView('/' . $params['uid']); // Set legacy encryption key if it exists, to support @@ -145,7 +143,6 @@ class Hooks { * @brief If the password can't be changed within ownCloud, than update the key password in advance. */ public static function preSetPassphrase($params) { - return true; if ( ! \OC_User::canUserChangePassword($params['uid']) ) { self::setPassphrase($params); } @@ -156,7 +153,6 @@ class Hooks { * @param array $params keys: uid, password */ public static function setPassphrase($params) { - return true; // Only attempt to change passphrase if server-side encryption // is in use (client-side encryption does not have access to // the necessary keys) @@ -550,8 +546,8 @@ class Hooks { $setMigrationStatus = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `migration_status`=0'); $setMigrationStatus->execute(); - $setInitStatus = \OC_DB::prepare('UPDATE `*PREFIX*encryption` SET `initialized`=0'); - $setInitStatus->execute(); + $session = new \OCA\Encryption\Session(new \OC\Files\View('/')); + $session->setInitialized(false); } } diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 105c5357e9..7d466b8852 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -235,11 +235,11 @@ class Helper { /** * @brief redirect to a error page */ - public static function redirectToErrorPage($util) { + public static function redirectToErrorPage($session) { $l = \OC_L10N::get('files_encryption'); - if ($util->getInitialized() === false) { + if ($session->getInitialized() === false) { $errorMsg = $l->t('Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app.'); } else { $errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index 1911386cd1..f5ce7083af 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -112,6 +112,33 @@ class Session { } + /** + * @brief Sets status if we tried to initialize the encyption app + * @param bool $privateKey true=initialized false=not initialized + * @return bool + */ + public function setInitialized($init) { + + \OC::$session->set('encryptionInitialized', $init); + + return true; + + } + + + /** + * @brief Gets status if we already tried to initialize the encryption app + * @returns bool + * + */ + public function getInitialized() { + if (!is_null(\OC::$session->get('encryptionInitialized'))) { + return \OC::$session->get('encryptionInitialized'); + } else { + return false; + } + } + /** * @brief Gets user or public share private key from session * @returns string $privateKey The user's plaintext private key diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 87b8dc3ee2..9215352aa7 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -131,7 +131,7 @@ class Stream { if($this->privateKey === false) { // if private key is not valid redirect user to a error page - \OCA\Encryption\Helper::redirectToErrorPage($util); + \OCA\Encryption\Helper::redirectToErrorPage($this->session); } $this->size = $this->rootView->filesize($this->rawPath, $mode); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index edb9564e73..17096a787f 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -37,9 +37,6 @@ class Util { const MIGRATION_IN_PROGRESS = -1; // migration is running const MIGRATION_OPEN = 0; // user still needs to be migrated - const ENCRYPTION_INITIALIZED = 1; - const ENCRYPTION_NOT_INITIALIZED = 0; - private $view; // OC_FilesystemView object for filesystem operations private $userId; // ID of the currently logged-in user private $client; // Client side encryption mode flag @@ -1218,56 +1215,6 @@ class Util { return $return; } - /** - * set remember if the encryption app was already initialized or not - * @param type $status - */ - public function setInitialized($status) { - $sql = 'UPDATE `*PREFIX*encryption` SET `initialized` = ? WHERE `uid` = ?'; - $args = array($status, $this->userId); - $query = \OCP\DB::prepare($sql); - $query->execute($args); - } - - /** - * set remember if the encryption app was already initialized or not - */ - public function getInitialized() { - $sql = 'SELECT `initialized` FROM `*PREFIX*encryption` WHERE `uid` = ?'; - $args = array($this->userId); - $query = \OCP\DB::prepare($sql); - - $result = $query->execute($args); - $initializedStatus = null; - - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR); - } else { - if ($result->numRows() > 0) { - $row = $result->fetchRow(); - if (isset($row['initialized'])) { - $initializedStatus = (int)$row['initialized']; - } - } - } - - // If no record is found - if (empty($initializedStatus)) { - \OCP\Util::writeLog('Encryption library', "Could not get initialized status for " . $this->userId . ", no record found", \OCP\Util::ERROR); - return false; - // If a record is found - } else { - return (bool)$initializedStatus; - } - - - - $sql = 'UPDATE `*PREFIX*encryption` SET `initialized` = ? WHERE `uid` = ?'; - $args = array($status, $this->userId); - $query = \OCP\DB::prepare($sql); - $query->execute($args); - } - /** * @brief close migration mode after users data has been encrypted successfully * @return boolean @@ -1774,6 +1721,11 @@ class Util { */ public function initEncryption($params) { + $session = new \OCA\Encryption\Session($this->view); + + // we tried to initialize the encryption app for this session + $session->setInitialized(true); + $encryptedKey = Keymanager::getPrivateKey($this->view, $params['uid']); $privateKey = Crypt::decryptPrivateKey($encryptedKey, $params['password']); @@ -1784,8 +1736,6 @@ class Util { return false; } - $session = new \OCA\Encryption\Session($this->view); - $session->setPrivateKey($privateKey); return $session; diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php index 589219f32a..c0c91bdf65 100644 --- a/apps/files_encryption/settings-personal.php +++ b/apps/files_encryption/settings-personal.php @@ -16,7 +16,9 @@ $view = new \OC_FilesystemView('/'); $util = new \OCA\Encryption\Util($view, $user); $session = new \OCA\Encryption\Session($view); -$privateKeySet = $session->getPrivateKey() !== false; +$privateKeySet = $session->getPrivateKey() !== false; +// was the key successfully initialized during log-in +$initialized = $session->getInitialized(); $recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'); $recoveryEnabledForUser = $util->recoveryEnabledForUser(); @@ -31,6 +33,7 @@ if ($recoveryAdminEnabled || !$privateKeySet) { $tmpl->assign('recoveryEnabled', $recoveryAdminEnabled); $tmpl->assign('recoveryEnabledForUser', $recoveryEnabledForUser); $tmpl->assign('privateKeySet', $privateKeySet); + $tmpl->assign('initialized', $initialized); $result = $tmpl->fetchPage(); } diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php index 3851245320..ff04556dd5 100644 --- a/apps/files_encryption/templates/settings-personal.php +++ b/apps/files_encryption/templates/settings-personal.php @@ -4,7 +4,7 @@ t( 'Encryption' ) ); ?> - +


t( "Enabling this option will allow you to reobtain access to your encrypted files in case of password loss" ) ); ?>
- /> t( "Enabled" ) ); ?>
- - t('Your password was changed');?>

- From 5e508f1ccbd3b83ed11f7eab35fea43e1583caf3 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 11:34:28 +0200 Subject: [PATCH 032/180] improved documentation of the methods --- apps/files_encryption/lib/session.php | 3 +++ apps/files_encryption/settings-personal.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index f5ce7083af..648e6e9ab0 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -116,6 +116,8 @@ class Session { * @brief Sets status if we tried to initialize the encyption app * @param bool $privateKey true=initialized false=not initialized * @return bool + * + * @note this doesn not indicate of the init was successful, we just remeber the try! */ public function setInitialized($init) { @@ -130,6 +132,7 @@ class Session { * @brief Gets status if we already tried to initialize the encryption app * @returns bool * + * @note this doesn not indicate of the init was successful, we just remeber the try! */ public function getInitialized() { if (!is_null(\OC::$session->get('encryptionInitialized'))) { diff --git a/apps/files_encryption/settings-personal.php b/apps/files_encryption/settings-personal.php index c0c91bdf65..ffcb99602e 100644 --- a/apps/files_encryption/settings-personal.php +++ b/apps/files_encryption/settings-personal.php @@ -17,7 +17,7 @@ $util = new \OCA\Encryption\Util($view, $user); $session = new \OCA\Encryption\Session($view); $privateKeySet = $session->getPrivateKey() !== false; -// was the key successfully initialized during log-in +// did we tried to initialize the keys for this session? $initialized = $session->getInitialized(); $recoveryAdminEnabled = OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'); From 6572ca811fc56c71b4efc970668741630acbd63c Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 11:36:20 +0200 Subject: [PATCH 033/180] error.php no longer needed --- apps/files_encryption/files/error.php | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 apps/files_encryption/files/error.php diff --git a/apps/files_encryption/files/error.php b/apps/files_encryption/files/error.php deleted file mode 100644 index 7a2bb1a281..0000000000 --- a/apps/files_encryption/files/error.php +++ /dev/null @@ -1,23 +0,0 @@ -t('Your private key is not valid! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app. If this doesn\'t help maybe your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); - - if(isset($_GET['p']) && $_GET['p'] === '1') { - header('HTTP/1.0 404 ' . $errorMsg); - } - - // check if ajax request - if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { - \OCP\JSON::error(array('data' => array('message' => $errorMsg))); - } else { - header('HTTP/1.0 404 ' . $errorMsg); - $tmpl = new OC_Template('files_encryption', 'invalid_private_key', 'guest'); - $tmpl->printPage(); - } - - exit; -} From 983da0d78fe13814fb771eb90dd2f10a89e0bcc6 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 17:01:10 +0200 Subject: [PATCH 034/180] fix db queries --- lib/public/share.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/share.php b/lib/public/share.php index c2dd0096ab..cb55c5c975 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -1053,11 +1053,11 @@ class Share { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, ' .'`share_type`, `share_with`, `file_source`, `path`, `file_target`, ' .'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' - .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `storage`, `mail_send`'; + .'`name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`, `mail_send`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, - `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `storage`, `mail_send`'; + `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`, `storage`, `mail_send`'; } } else { $select = '*'; From 931e90634e905816e5ec8db3d10f9446c1b1eacc Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 17:03:35 +0200 Subject: [PATCH 035/180] fix db queries --- lib/public/share.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/share.php b/lib/public/share.php index cb55c5c975..4461a1d421 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -1039,10 +1039,10 @@ class Share { if ($itemType == 'file' || $itemType == 'folder') { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`,' .' `share_type`, `share_with`, `file_source`, `path`, `permissions`, `stime`,' - .' `expiration`, `token`, `storage`, `storage`, `mail_send`'; + .' `expiration`, `token`, `storage`, `mail_send`'; } else { $select = '`id`, `item_type`, `item_source`, `parent`, `share_type`, `share_with`, `permissions`,' - .' `stime`, `file_source`, `expiration`, `token`, `storage`, `mail_send`'; + .' `stime`, `file_source`, `expiration`, `token`, `mail_send`'; } } else { if ($fileDependent) { From 7ce54f7b3a86c4cc1301cb6d96f3029c7047a95b Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 17:09:26 +0200 Subject: [PATCH 036/180] revert submodule reference changes --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 21b466b72c..dc87ea6302 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 21b466b72cdd4c823c011669593ecef1defb1f3c +Subproject commit dc87ea630287f27502eba825fbb19fcc33c34c86 From 3ce4bf5ec70ed4cfd0e6d619e2f7ae0a1bfdb06c Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 2 Sep 2013 17:14:11 +0200 Subject: [PATCH 037/180] use css class hidden to hide setting elements --- settings/templates/admin.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/settings/templates/admin.php b/settings/templates/admin.php index 22cf946803..72e93e78da 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -128,7 +128,7 @@ if (!$_['internetconnectionworking']) { - > + > />
@@ -137,7 +137,7 @@ if (!$_['internetconnectionworking']) { - > + > />
@@ -146,7 +146,7 @@ if (!$_['internetconnectionworking']) { - > + > />
@@ -154,7 +154,7 @@ if (!$_['internetconnectionworking']) { - > + > />
@@ -164,7 +164,7 @@ if (!$_['internetconnectionworking']) { - > + > />
From fd7469db9e1cd1fd85e3a8a18aac87c7040ec8e7 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 3 Sep 2013 13:37:06 +0200 Subject: [PATCH 038/180] coding-style fixes --- core/ajax/share.php | 16 ++++++++++++++-- core/js/share.js | 2 +- lib/defaults.php | 9 +++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/core/ajax/share.php b/core/ajax/share.php index 1e954ac4f9..8f5432a0fc 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -129,7 +129,13 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } $url = \OCP\Util::linkToAbsolute('files', 'index.php', array("dir" => $foldername)); - $text = $defaults->getShareNotificationText(\OCP\User::getDisplayName(), $filename, $itemType, $url, $expiration); + $text = $defaults->getShareNotificationText( + \OCP\User::getDisplayName(), + $filename, + $itemType, + $url, + $expiration + ); try { OCP\Util::sendMail( @@ -153,7 +159,13 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo if (empty($noMail)) { OCP\JSON::success(); } else { - OCP\JSON::error(array('data' => array('message' => $l->t("Couldn't send mail to following users: %s ", implode(', ', $noMail))))); + OCP\JSON::error(array( + 'data' => array( + 'message' => $l->t("Couldn't send mail to following users: %s ", + implode(', ', $noMail) + ) + ) + )); } break; case 'informRecipientsDisabled': diff --git a/core/js/share.js b/core/js/share.js index e253f77ef2..763713e7cf 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -493,7 +493,7 @@ $(document).ready(function() { $('input:[type=checkbox]', this).hide(); $('label', this).hide(); } - } else { + } else { $('a.unshare', this).hide(); } }); diff --git a/lib/defaults.php b/lib/defaults.php index 0685fbb16c..efb6c2c7b3 100644 --- a/lib/defaults.php +++ b/lib/defaults.php @@ -72,9 +72,14 @@ class OC_Defaults { return $this->theme->getShareNotificationText($sender, $itemName, $itemType, $link, $expiration); } else { if ($expiration) { - return $this->l->t("%s shared a %s called %s with you. The share will expire at %s. You can find the %s here: %s", array($sender, $itemType, $itemName, $expiration, $itemType, $link)); + return $this->l->t("%s shared a %s called %s with you. " . + "The share will expire at %s. ". + "You can find the %s here: %s", + array($sender, $itemType, $itemName, $expiration, $itemType, $link)); } else { - return $this->l->t("%s shared a %s called %s with you. You can find the %s here: %s", array($sender, $itemType, $itemName, $itemType, $link)); + return $this->l->t("%s shared a %s called %s with you. ". + "You can find the %s here: %s", + array($sender, $itemType, $itemName, $itemType, $link)); } } } From 823b4cce603d1d0a404d8b93fcca6101ff839767 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 4 Sep 2013 08:16:27 +0200 Subject: [PATCH 039/180] More trimming --- lib/vcategories.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/vcategories.php b/lib/vcategories.php index 8403695835..a7e4c54be2 100644 --- a/lib/vcategories.php +++ b/lib/vcategories.php @@ -179,6 +179,7 @@ class OC_VCategories { if(is_numeric($category)) { $catid = $category; } elseif(is_string($category)) { + $category = trim($category); $catid = $this->array_searchi($category, $this->categories); } OCP\Util::writeLog('core', __METHOD__.', category: '.$catid.' '.$category, OCP\Util::DEBUG); @@ -240,6 +241,7 @@ class OC_VCategories { if(is_numeric($category)) { $catid = $category; } elseif(is_string($category)) { + $category = trim($category); $catid = $this->array_searchi($category, $this->categories); } OCP\Util::writeLog('core', __METHOD__.', category: '.$catid.' '.$category, OCP\Util::DEBUG); @@ -301,6 +303,7 @@ class OC_VCategories { * @returns int the id of the added category or false if it already exists. */ public function add($name) { + $name = trim($name); OCP\Util::writeLog('core', __METHOD__.', name: ' . $name, OCP\Util::DEBUG); if($this->hasCategory($name)) { OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', OCP\Util::DEBUG); @@ -331,6 +334,8 @@ class OC_VCategories { * @returns bool */ public function rename($from, $to) { + $from = trim($from); + $to = trim($to); $id = $this->array_searchi($from, $this->categories); if($id === false) { OCP\Util::writeLog('core', __METHOD__.', category: ' . $from. ' does not exist', OCP\Util::DEBUG); @@ -656,6 +661,7 @@ class OC_VCategories { public function addToCategory($objid, $category, $type = null) { $type = is_null($type) ? $this->type : $type; if(is_string($category) && !is_numeric($category)) { + $category = trim($category); if(!$this->hasCategory($category)) { $this->add($category, true); } @@ -688,9 +694,13 @@ class OC_VCategories { */ public function removeFromCategory($objid, $category, $type = null) { $type = is_null($type) ? $this->type : $type; - $categoryid = (is_string($category) && !is_numeric($category)) - ? $this->array_searchi($category, $this->categories) - : $category; + if(is_string($category) && !is_numeric($category)) { + $category = trim($category); + $categoryid = $this->array_searchi($category, $this->categories); + } else { + $categoryid = $category; + } + try { $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?'; @@ -716,6 +726,8 @@ class OC_VCategories { $names = array($names); } + $names = array_map('trim', $names); + OC_Log::write('core', __METHOD__ . ', before: ' . print_r($this->categories, true), OC_Log::DEBUG); foreach($names as $name) { From 7618cf3005f8bda8375183010711a9a2cdfb1fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 4 Sep 2013 23:45:11 +0200 Subject: [PATCH 040/180] adding public interface for preview --- lib/preview.php | 5 ++++- lib/previewmanager.php | 38 +++++++++++++++++++++++++++++++ lib/public/ipreview.php | 35 +++++++++++++++++++++++++++++ lib/public/iservercontainer.php | 6 +++++ lib/public/preview.php | 34 ---------------------------- lib/server.php | 40 +++++++++++++++++++++++++++++++++ 6 files changed, 123 insertions(+), 35 deletions(-) create mode 100755 lib/previewmanager.php create mode 100644 lib/public/ipreview.php delete mode 100644 lib/public/preview.php diff --git a/lib/preview.php b/lib/preview.php index b40ba191fb..266f7795f1 100755 --- a/lib/preview.php +++ b/lib/preview.php @@ -42,6 +42,9 @@ class Preview { private $scalingup; //preview images object + /** + * @var \OC_Image + */ private $preview; //preview providers @@ -624,4 +627,4 @@ class Preview { } return false; } -} \ No newline at end of file +} diff --git a/lib/previewmanager.php b/lib/previewmanager.php new file mode 100755 index 0000000000..ac9a866a75 --- /dev/null +++ b/lib/previewmanager.php @@ -0,0 +1,38 @@ +getPreview(); + } + + /** + * @brief returns true if the passed mime type is supported + * @param string $mimeType + * @return boolean + */ + function isMimeSupported($mimeType = '*') + { + return \OC\Preview::isMimeSupported($mimeType); + } +} diff --git a/lib/public/ipreview.php b/lib/public/ipreview.php new file mode 100644 index 0000000000..b01e7f5b53 --- /dev/null +++ b/lib/public/ipreview.php @@ -0,0 +1,35 @@ +registerService('ContactsManager', function($c){ return new ContactsManager(); }); + $this->registerService('Request', function($c){ + $params = array(); + + // we json decode the body only in case of content type json + if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'],'json') === true ) { + $params = json_decode(file_get_contents('php://input'), true); + $params = is_array($params) ? $params: array(); + } + + return new Request( + array( + 'get' => $_GET, + 'post' => $_POST, + 'files' => $_FILES, + 'server' => $_SERVER, + 'env' => $_ENV, + 'session' => $_SESSION, + 'cookies' => $_COOKIE, + 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) + ? $_SERVER['REQUEST_METHOD'] + : null, + 'params' => $params, + 'urlParams' => $c['urlParams'] + ) + ); + }); + $this->registerService('PreviewManager', function($c){ + return new PreviewManager(); + }); } /** @@ -37,4 +67,14 @@ class Server extends SimpleContainer implements IServerContainer { { return $this->query('Request'); } + + /** + * Returns the preview manager which can create preview images for a given file + * + * @return \OCP\IPreview + */ + function getPreviewManager() + { + return $this->query('PreviewManager'); + } } From 69b1625f0e368e65771fef473f4b4d4a13456354 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 6 Sep 2013 12:27:25 +0200 Subject: [PATCH 041/180] re-added error.php --- apps/files_encryption/files/error.php | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 apps/files_encryption/files/error.php diff --git a/apps/files_encryption/files/error.php b/apps/files_encryption/files/error.php new file mode 100644 index 0000000000..ac0c026916 --- /dev/null +++ b/apps/files_encryption/files/error.php @@ -0,0 +1,33 @@ +t('Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app.'); + $init = '0'; + } else { + $errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); + $init = '1'; + } + + if (isset($_GET['p']) && $_GET['p'] === '1') { + header('HTTP/1.0 404 ' . $errorMsg); + } + +// check if ajax request + if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { + \OCP\JSON::error(array('data' => array('message' => $errorMsg))); + } else { + header('HTTP/1.0 404 ' . $errorMsg); + $tmpl = new OC_Template('files_encryption', 'invalid_private_key', 'guest'); + $tmpl->assign('message', $errorMsg); + $tmpl->assign('init', $init); + $tmpl->printPage(); + } + + exit; +} + From fb462e83ccde5c46565c23545c5eb894acbd6fd3 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 6 Sep 2013 12:27:40 +0200 Subject: [PATCH 042/180] no longer enforce log out, but provide useful errors/warnings instead --- apps/files/index.php | 7 ++++- apps/files/js/files.js | 9 +++++++ apps/files/templates/index.php | 1 + .../ajax/updatePrivateKeyPassword.php | 1 + apps/files_encryption/appinfo/app.php | 17 ------------ apps/files_encryption/hooks/hooks.php | 2 +- apps/files_encryption/lib/helper.php | 27 +++++-------------- apps/files_encryption/lib/session.php | 15 +++++++---- apps/files_encryption/lib/stream.php | 2 +- apps/files_encryption/lib/util.php | 3 ++- .../templates/invalid_private_key.php | 6 +++-- 11 files changed, 42 insertions(+), 48 deletions(-) diff --git a/apps/files/index.php b/apps/files/index.php index f1e120c872..b81ba2bdde 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -124,8 +124,12 @@ if ($needUpgrade) { $storageInfo=OC_Helper::getStorageInfo($dir); $maxUploadFilesize=OCP\Util::maxUploadFilesize($dir); $publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes'); + // if the encryption app is disabled, than everything is fine + $encryptionInitStatus = \OCA\Encryption\Session::INIT_SUCCESSFUL; if (OC_App::isEnabled('files_encryption')) { $publicUploadEnabled = 'no'; + $session = new \OCA\Encryption\Session(new \OC\Files\View('/')); + $encryptionInitStatus = $session->getInitialized(); } $trashEnabled = \OCP\App::isEnabled('files_trashbin'); @@ -133,7 +137,7 @@ if ($needUpgrade) { if ($trashEnabled) { $trashEmpty = \OCA\Files_Trashbin\Trashbin::isEmpty($user); } - + OCP\Util::addscript('files', 'fileactions'); OCP\Util::addscript('files', 'files'); OCP\Util::addscript('files', 'keyboardshortcuts'); @@ -153,5 +157,6 @@ if ($needUpgrade) { $tmpl->assign('isPublic', false); $tmpl->assign('publicUploadEnabled', $publicUploadEnabled); $tmpl->assign("encryptedFiles", \OCP\Util::encryptedFiles()); + $tmpl->assign("encryptionInitStatus", $encryptionInitStatus); $tmpl->printPage(); } diff --git a/apps/files/js/files.js b/apps/files/js/files.js index d729077ea7..63c3544b53 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -90,6 +90,15 @@ Files={ } var encryptedFiles = $('#encryptedFiles').val(); + var initStatus = $('#encryptionInitStatus').val(); + if (initStatus === '0') { // enc not initialized, but should be + OC.Notification.show(t('files_encryption', 'Encryption App is enabled but your keys are not initialized, please log-out and log-in again')); + return; + } + if (initStatus === '1') { // encryption tried to init but failed + OC.Notification.show(t('files_encryption', 'Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.')); + return; + } if (encryptedFiles === '1') { OC.Notification.show(t('files_encryption', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.')); return; diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 24cb8c2fe5..e17273e47b 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -123,3 +123,4 @@ + \ No newline at end of file diff --git a/apps/files_encryption/ajax/updatePrivateKeyPassword.php b/apps/files_encryption/ajax/updatePrivateKeyPassword.php index 1e6644da57..29c72952ae 100644 --- a/apps/files_encryption/ajax/updatePrivateKeyPassword.php +++ b/apps/files_encryption/ajax/updatePrivateKeyPassword.php @@ -48,6 +48,7 @@ if ($decryptedKey) { // success or failure if ($return) { + $session->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL); \OCP\JSON::success(array('data' => array('message' => $l->t('Private key password successfully updated.')))); } else { \OCP\JSON::error(array('data' => array('message' => $l->t('Could not update the private key password. Maybe the old password was not correct.')))); diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index 90a9984e27..cd26cd10cd 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -41,23 +41,6 @@ if (!OC_Config::getValue('maintenance', false)) { if($sessionReady) { $session = new \OCA\Encryption\Session($view); } - - $user = \OCP\USER::getUser(); - // check if user has a private key - if ($sessionReady === false - || (!$view->file_exists('/' . $user . '/files_encryption/' . $user . '.private.key') - && OCA\Encryption\Crypt::mode() === 'server') - ) { - - // Force the user to log-in again if the encryption key isn't unlocked - // (happens when a user is logged in before the encryption app is - // enabled) - OCP\User::logout(); - - header("Location: " . OC::$WEBROOT . '/'); - - exit(); - } } } else { // logout user if we are in maintenance to force re-login diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 4c6122b7c2..c945deeea0 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -547,7 +547,7 @@ class Hooks { $setMigrationStatus->execute(); $session = new \OCA\Encryption\Session(new \OC\Files\View('/')); - $session->setInitialized(false); + $session->setInitialized(\OCA\Encryption\Session::NOT_INITIALIZED); } } diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 7d466b8852..048473ce84 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -237,28 +237,15 @@ class Helper { */ public static function redirectToErrorPage($session) { - $l = \OC_L10N::get('files_encryption'); - - if ($session->getInitialized() === false) { - $errorMsg = $l->t('Encryption app not initialized! Maybe the encryption app was re-enabled during your session. Please try to log out and log back in to initialize the encryption app.'); - } else { - $errorMsg = $l->t('Your private key is not valid! Likely your password was changed outside the ownCloud system (e.g. your corporate directory). You can update your private key password in your personal settings to recover access to your encrypted files.'); - } + $init = $session->getInitialized(); + $location = \OC_Helper::linkToAbsolute('apps/files_encryption/files', 'error.php'); + $post = 0; if(count($_POST) > 0) { - header('HTTP/1.0 404 ' . $errorMsg); - } - - // check if ajax request - if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') { - \OCP\JSON::error(array('data' => array('message' => $errorMsg))); - } else { - header('HTTP/1.0 404 ' . $errorMsg); - $tmpl = new OC_Template('files_encryption', 'invalid_private_key', 'guest'); - $tmpl->printPage(); - } - - exit; + $post = 1; + } + header('Location: ' . $location . '?p=' . $post . '&i=' . $init); + exit(); } /** diff --git a/apps/files_encryption/lib/session.php b/apps/files_encryption/lib/session.php index 648e6e9ab0..25f2198181 100644 --- a/apps/files_encryption/lib/session.php +++ b/apps/files_encryption/lib/session.php @@ -30,6 +30,11 @@ class Session { private $view; + const NOT_INITIALIZED = '0'; + const INIT_EXECUTED = '1'; + const INIT_SUCCESSFUL = '2'; + + /** * @brief if session is started, check if ownCloud key pair is set up, if not create it * @param \OC_FilesystemView $view @@ -113,10 +118,10 @@ class Session { } /** - * @brief Sets status if we tried to initialize the encyption app - * @param bool $privateKey true=initialized false=not initialized + * @brief Sets status of encryption app + * @param string $init INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INOITIALIZED * @return bool - * + * * @note this doesn not indicate of the init was successful, we just remeber the try! */ public function setInitialized($init) { @@ -130,7 +135,7 @@ class Session { /** * @brief Gets status if we already tried to initialize the encryption app - * @returns bool + * @returns init status INIT_SUCCESSFUL, INIT_EXECUTED, NOT_INOITIALIZED * * @note this doesn not indicate of the init was successful, we just remeber the try! */ @@ -138,7 +143,7 @@ class Session { if (!is_null(\OC::$session->get('encryptionInitialized'))) { return \OC::$session->get('encryptionInitialized'); } else { - return false; + return self::NOT_INITIALIZED; } } diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index 9215352aa7..c6db10ce40 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -128,7 +128,7 @@ class Stream { $this->unencryptedSize = 0; } else { - +\OCA\Encryption\Helper::redirectToErrorPage($this->session); if($this->privateKey === false) { // if private key is not valid redirect user to a error page \OCA\Encryption\Helper::redirectToErrorPage($this->session); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index 17096a787f..7a19f95464 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -1724,7 +1724,7 @@ class Util { $session = new \OCA\Encryption\Session($this->view); // we tried to initialize the encryption app for this session - $session->setInitialized(true); + $session->setInitialized(\OCA\Encryption\Session::INIT_EXECUTED); $encryptedKey = Keymanager::getPrivateKey($this->view, $params['uid']); @@ -1737,6 +1737,7 @@ class Util { } $session->setPrivateKey($privateKey); + $session->setInitialized(\OCA\Encryption\Session::INIT_SUCCESSFUL); return $session; } diff --git a/apps/files_encryption/templates/invalid_private_key.php b/apps/files_encryption/templates/invalid_private_key.php index 5c086d6514..9af65f831b 100644 --- a/apps/files_encryption/templates/invalid_private_key.php +++ b/apps/files_encryption/templates/invalid_private_key.php @@ -2,9 +2,11 @@
  • - t('Your private key is not valid! Maybe the your password was changed from outside.')); ?> +
    - t('You can unlock your private key in your ')); ?>
    t('personal settings')); ?>. + + p($l->t('Go directly to your ')); ?> t('personal settings')); ?>. +
  • From 1558cb860c2fb26fdde14fce2a16acbb29d12b3e Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 6 Sep 2013 13:16:48 +0200 Subject: [PATCH 043/180] remove test code --- apps/files_encryption/lib/stream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index c6db10ce40..9215352aa7 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -128,7 +128,7 @@ class Stream { $this->unencryptedSize = 0; } else { -\OCA\Encryption\Helper::redirectToErrorPage($this->session); + if($this->privateKey === false) { // if private key is not valid redirect user to a error page \OCA\Encryption\Helper::redirectToErrorPage($this->session); From 5acb3c4c0d570b2bf7b209d61e5e7849f4f3a363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 15 Sep 2013 21:20:22 +0200 Subject: [PATCH 044/180] first log the exception --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 90fd3efcc9..40063fa6e0 100755 --- a/index.php +++ b/index.php @@ -31,7 +31,7 @@ try { } catch (Exception $ex) { //show the user a detailed error page - OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR); \OCP\Util::writeLog('index', $ex->getMessage(), \OCP\Util::FATAL); + OC_Response::setStatus(OC_Response::STATUS_INTERNAL_SERVER_ERROR); OC_Template::printExceptionErrorPage($ex); } From af0069bf032d2045abd18abf2e133835fc360481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 15 Sep 2013 22:24:57 +0200 Subject: [PATCH 045/180] adding getRootFolder() to server container and hooking up the new files api --- lib/public/iservercontainer.php | 8 ++++++++ lib/server.php | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 144c1a5b3b..d88330698d 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -54,4 +54,12 @@ interface IServerContainer { * @return \OCP\IPreview */ function getPreviewManager(); + + /** + * Returns the root folder of ownCloud's data directory + * + * @return \OCP\Files\Folder + */ + function getRootFolder(); + } diff --git a/lib/server.php b/lib/server.php index d85996612e..9e87bd3190 100644 --- a/lib/server.php +++ b/lib/server.php @@ -4,6 +4,8 @@ namespace OC; use OC\AppFramework\Http\Request; use OC\AppFramework\Utility\SimpleContainer; +use OC\Files\Node\Root; +use OC\Files\View; use OCP\IServerContainer; /** @@ -47,6 +49,14 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('PreviewManager', function($c){ return new PreviewManager(); }); + $this->registerService('RootFolder', function($c){ + // TODO: get user and user manager from container as well + $user = \OC_User::getUser(); + $user = \OC_User::getManager()->get($user); + $manager = \OC\Files\Filesystem::getMountManager(); + $view = new View(); + return new Root($manager, $view, $user); + }); } /** @@ -77,4 +87,14 @@ class Server extends SimpleContainer implements IServerContainer { { return $this->query('PreviewManager'); } + + /** + * Returns the root folder of ownCloud's data directory + * + * @return \OCP\Files\Folder + */ + function getRootFolder() + { + return $this->query('RootFolder'); + } } From 5d4e9e0d25831cbe1b9c2fef52016c6ed1bbcb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sun, 15 Sep 2013 23:07:18 +0200 Subject: [PATCH 046/180] /OC/Server has created too early causing issues with config operations as OC:$SERVERPATH was not yet initialized This fixes unit test execution --- lib/base.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/base.php b/lib/base.php index 05d151439a..1720a5fd7e 100644 --- a/lib/base.php +++ b/lib/base.php @@ -376,9 +376,6 @@ class OC { self::$loader->registerPrefix('Patchwork', '3rdparty'); spl_autoload_register(array(self::$loader, 'load')); - // setup the basic server - self::$server = new \OC\Server(); - // set some stuff //ob_start(); error_reporting(E_ALL | E_STRICT); @@ -458,6 +455,9 @@ class OC { stream_wrapper_register('quota', 'OC\Files\Stream\Quota'); stream_wrapper_register('oc', 'OC\Files\Stream\OC'); + // setup the basic server + self::$server = new \OC\Server(); + self::initTemplateEngine(); if (!self::$CLI) { self::initSession(); From 981a41e2cdb0848bea6c433577a7ae60d2920a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 17 Sep 2013 00:26:55 +0200 Subject: [PATCH 047/180] adding interface for middleware --- lib/public/appframework/imiddleware.php | 88 +++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 lib/public/appframework/imiddleware.php diff --git a/lib/public/appframework/imiddleware.php b/lib/public/appframework/imiddleware.php new file mode 100644 index 0000000000..9340034fcc --- /dev/null +++ b/lib/public/appframework/imiddleware.php @@ -0,0 +1,88 @@ +. + * + */ + + +namespace OCP\AppFramework; +use OCP\AppFramework\Http\Response; + + +/** + * Middleware is used to provide hooks before or after controller methods and + * deal with possible exceptions raised in the controller methods. + * They're modeled after Django's middleware system: + * https://docs.djangoproject.com/en/dev/topics/http/middleware/ + */ +interface MiddleWare { + + + /** + * This is being run in normal order before the controller is being + * called which allows several modifications and checks + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + */ + function beforeController($controller, $methodName); + + + /** + * This is being run when either the beforeController method or the + * controller method itself is throwing an exception. The middleware is + * asked in reverse order to handle the exception and to return a response. + * If the response is null, it is assumed that the exception could not be + * handled and the error will be thrown again + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param \Exception $exception the thrown exception + * @throws \Exception the passed in exception if it cant handle it + * @return Response a Response object in case that the exception was handled + */ + function afterException($controller, $methodName, \Exception $exception); + + /** + * This is being run after a successful controller method call and allows + * the manipulation of a Response object. The middleware is run in reverse order + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param Response $response the generated response from the controller + * @return Response a Response object + */ + function afterController($controller, $methodName, Response $response); + + /** + * This is being run after the response object has been rendered and + * allows the manipulation of the output. The middleware is run in reverse order + * + * @param Controller $controller the controller that is being called + * @param string $methodName the name of the method that will be called on + * the controller + * @param string $output the generated output from a response + * @return string the output that should be printed + */ + function beforeOutput($controller, $methodName, $output); +} From 822daa8f8adb9c31b9bfeac67ff165c18dc321c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 17 Sep 2013 00:27:22 +0200 Subject: [PATCH 048/180] class files have to be lowercase --- lib/public/appframework/{App.php => app.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/public/appframework/{App.php => app.php} (100%) diff --git a/lib/public/appframework/App.php b/lib/public/appframework/app.php similarity index 100% rename from lib/public/appframework/App.php rename to lib/public/appframework/app.php From b9e943f5d52d1bf888233fdc2288477322591c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 17 Sep 2013 09:42:14 +0200 Subject: [PATCH 049/180] fix naming --- lib/public/appframework/imiddleware.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/appframework/imiddleware.php b/lib/public/appframework/imiddleware.php index 9340034fcc..1e76d3bbe4 100644 --- a/lib/public/appframework/imiddleware.php +++ b/lib/public/appframework/imiddleware.php @@ -32,7 +32,7 @@ use OCP\AppFramework\Http\Response; * They're modeled after Django's middleware system: * https://docs.djangoproject.com/en/dev/topics/http/middleware/ */ -interface MiddleWare { +interface IMiddleWare { /** From 9b420e8660404de27e3af629bfca188ae90cf7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 17 Sep 2013 13:33:47 +0200 Subject: [PATCH 050/180] use \OC::$server->getPreviewManager() instead of \OCP\Preview --- apps/files/ajax/rawlist.php | 6 +++--- apps/files/lib/helper.php | 2 +- apps/files_sharing/public.php | 2 +- apps/files_trashbin/lib/helper.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index 9ccd4cc299..802a308353 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -26,7 +26,7 @@ $files = array(); if($mimetypes && !in_array('httpd/unix-directory', $mimetypes)) { foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, 'httpd/unix-directory' ) as $file ) { $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($file['mimetype']); + $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); $file["date"] = OCP\Util::formatDate($file["mtime"]); $file['mimetype_icon'] = \OCA\files\lib\Helper::determineIcon($file); $files[] = $file; @@ -37,7 +37,7 @@ if (is_array($mimetypes) && count($mimetypes)) { foreach ($mimetypes as $mimetype) { foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $file ) { $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($file['mimetype']); + $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); $file["date"] = OCP\Util::formatDate($file["mtime"]); $file['mimetype_icon'] = \OCA\files\lib\Helper::determineIcon($file); $files[] = $file; @@ -46,7 +46,7 @@ if (is_array($mimetypes) && count($mimetypes)) { } else { foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $file ) { $file['directory'] = $dir; - $file['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($file['mimetype']); + $file['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($file['mimetype']); $file["date"] = OCP\Util::formatDate($file["mtime"]); $file['mimetype_icon'] = \OCA\files\lib\Helper::determineIcon($file); $files[] = $file; diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index 3c13b8ea6e..f0d3560b87 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -84,7 +84,7 @@ class Helper } } $i['directory'] = $dir; - $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($i['mimetype']); + $i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']); $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); $files[] = $i; } diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 6d3a07a9d0..8d474e87b4 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -187,7 +187,7 @@ if (isset($path)) { } else { $i['extension'] = ''; } - $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($i['mimetype']); + $i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']); } $i['directory'] = $getPath; $i['permissions'] = OCP\PERMISSION_READ; diff --git a/apps/files_trashbin/lib/helper.php b/apps/files_trashbin/lib/helper.php index 098fc0b54b..4cb5e8a390 100644 --- a/apps/files_trashbin/lib/helper.php +++ b/apps/files_trashbin/lib/helper.php @@ -61,7 +61,7 @@ class Helper $i['directory'] = ''; } $i['permissions'] = \OCP\PERMISSION_READ; - $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($r['mime']); + $i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($r['mime']); $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); $files[] = $i; } From fe86182dac387817258942a46905f2b801862d4d Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 17 Sep 2013 17:46:33 +0200 Subject: [PATCH 051/180] OC_Cache namespace changes and add UserCache to server container. Refs #4863 --- lib/base.php | 4 +- lib/cache.php | 8 ++-- lib/cache/broker.php | 4 +- lib/cache/file.php | 13 +++--- lib/cache/fileglobal.php | 7 +-- lib/cache/fileglobalgc.php | 5 ++- lib/cache/usercache.php | 77 +++++++++++++++++++++++++++++++++ lib/filechunking.php | 2 +- lib/public/icache.php | 55 +++++++++++++++++++++++ lib/public/iservercontainer.php | 7 +++ lib/server.php | 34 ++++++++++----- tests/lib/cache/file.php | 30 +++++++------ tests/lib/cache/usercache.php | 68 +++++++++++++++++++++++++++++ 13 files changed, 270 insertions(+), 44 deletions(-) create mode 100644 lib/cache/usercache.php create mode 100644 lib/public/icache.php create mode 100644 tests/lib/cache/usercache.php diff --git a/lib/base.php b/lib/base.php index 1720a5fd7e..520be11bc5 100644 --- a/lib/base.php +++ b/lib/base.php @@ -564,11 +564,11 @@ class OC { if (OC_Config::getValue('installed', false)) { //don't try to do this before we are properly setup // register cache cleanup jobs try { //if this is executed before the upgrade to the new backgroundjob system is completed it will throw an exception - \OCP\BackgroundJob::registerJob('OC_Cache_FileGlobalGC'); + \OCP\BackgroundJob::registerJob('OC\Cache\FileGlobalGC'); } catch (Exception $e) { } - OC_Hook::connect('OC_User', 'post_login', 'OC_Cache_File', 'loginListener'); + OC_Hook::connect('OC_User', 'post_login', 'OC\Cache\File', 'loginListener'); } } diff --git a/lib/cache.php b/lib/cache.php index 48b9964ba9..c99663a0ca 100644 --- a/lib/cache.php +++ b/lib/cache.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class OC_Cache { +namespace OC\Cache; + +class Cache { /** * @var OC_Cache $user_cache */ @@ -22,7 +24,7 @@ class OC_Cache { */ static public function getGlobalCache() { if (!self::$global_cache) { - self::$global_cache = new OC_Cache_FileGlobal(); + self::$global_cache = new FileGlobal(); } return self::$global_cache; } @@ -33,7 +35,7 @@ class OC_Cache { */ static public function getUserCache() { if (!self::$user_cache) { - self::$user_cache = new OC_Cache_File(); + self::$user_cache = new File(); } return self::$user_cache; } diff --git a/lib/cache/broker.php b/lib/cache/broker.php index a161dbfa3b..b7f1b67a6d 100644 --- a/lib/cache/broker.php +++ b/lib/cache/broker.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class OC_Cache_Broker { +namespace OC\Cache; + +class Broker { protected $fast_cache; protected $slow_cache; diff --git a/lib/cache/file.php b/lib/cache/file.php index 361138e473..2ab914d17b 100644 --- a/lib/cache/file.php +++ b/lib/cache/file.php @@ -6,24 +6,25 @@ * See the COPYING-README file. */ +namespace OC\Cache; -class OC_Cache_File{ +class File { protected $storage; protected function getStorage() { if (isset($this->storage)) { return $this->storage; } - if(OC_User::isLoggedIn()) { - \OC\Files\Filesystem::initMountPoints(OC_User::getUser()); + if(\OC_User::isLoggedIn()) { + \OC\Files\Filesystem::initMountPoints(\OC_User::getUser()); $subdir = 'cache'; - $view = new \OC\Files\View('/'.OC_User::getUser()); + $view = new \OC\Files\View('/' . \OC_User::getUser()); if(!$view->file_exists($subdir)) { $view->mkdir($subdir); } - $this->storage = new \OC\Files\View('/'.OC_User::getUser().'/'.$subdir); + $this->storage = new \OC\Files\View('/' . \OC_User::getUser().'/'.$subdir); return $this->storage; }else{ - OC_Log::write('core', 'Can\'t get cache storage, user not logged in', OC_Log::ERROR); + \OC_Log::write('core', 'Can\'t get cache storage, user not logged in', \OC_Log::ERROR); return false; } } diff --git a/lib/cache/fileglobal.php b/lib/cache/fileglobal.php index c0bd8e45f3..9ca1740293 100644 --- a/lib/cache/fileglobal.php +++ b/lib/cache/fileglobal.php @@ -6,8 +6,9 @@ * See the COPYING-README file. */ +namespace OC\Cache; -class OC_Cache_FileGlobal{ +class FileGlobal { static protected function getCacheDir() { $cache_dir = get_temp_dir().'/owncloud-'.OC_Util::getInstanceId().'/'; if (!is_dir($cache_dir)) { @@ -80,13 +81,13 @@ class OC_Cache_FileGlobal{ } static public function gc() { - $last_run = OC_AppConfig::getValue('core', 'global_cache_gc_lastrun', 0); + $last_run = \OC_AppConfig::getValue('core', 'global_cache_gc_lastrun', 0); $now = time(); if (($now - $last_run) < 300) { // only do cleanup every 5 minutes return; } - OC_AppConfig::setValue('core', 'global_cache_gc_lastrun', $now); + \OC_AppConfig::setValue('core', 'global_cache_gc_lastrun', $now); $cache_dir = self::getCacheDir(); if($cache_dir and is_dir($cache_dir)) { $dh=opendir($cache_dir); diff --git a/lib/cache/fileglobalgc.php b/lib/cache/fileglobalgc.php index a29c31f906..399dd5e6f9 100644 --- a/lib/cache/fileglobalgc.php +++ b/lib/cache/fileglobalgc.php @@ -1,8 +1,9 @@ userCache = new File(); + } + + /** + * Get a value from the user cache + * + * @param string $key + * @return mixed + */ + public function get($key) { + return $this->userCache->get($key); + } + + /** + * Set a value in the user cache + * + * @param string $key + * @param mixed $value + * @param int $ttl Time To Live in seconds. Defaults to 60*60*24 + * @return bool + */ + public function set($key, $value, $ttl = 0) { + if (empty($key)) { + return false; + } + return $this->userCache->set($key, $value, $ttl); + } + + /** + * Check if a value is set in the user cache + * + * @param string $key + * @return bool + */ + public function hasKey($key) { + return $this->userCache->hasKey($key); + } + + /** + * Remove an item from the user cache + * + * @param string $key + * @return bool + */ + public function remove($key) { + return $this->userCache->remove($key); + } + + /** + * clear the user cache of all entries starting with a prefix + * @param string prefix (optional) + * @return bool + */ + public function clear($prefix = '') { + return $this->userCache->clear($prefix); + } +} diff --git a/lib/filechunking.php b/lib/filechunking.php index e6d69273a4..313a6ee87d 100644 --- a/lib/filechunking.php +++ b/lib/filechunking.php @@ -29,7 +29,7 @@ class OC_FileChunking { protected function getCache() { if (!isset($this->cache)) { - $this->cache = new OC_Cache_File(); + $this->cache = new \OC\Cache\File(); } return $this->cache; } diff --git a/lib/public/icache.php b/lib/public/icache.php new file mode 100644 index 0000000000..202459f7c2 --- /dev/null +++ b/lib/public/icache.php @@ -0,0 +1,55 @@ +registerService('ContactsManager', function($c){ + $this->registerService('ContactsManager', function($c) { return new ContactsManager(); }); - $this->registerService('Request', function($c){ + $this->registerService('Request', function($c) { $params = array(); // we json decode the body only in case of content type json @@ -46,10 +46,10 @@ class Server extends SimpleContainer implements IServerContainer { ) ); }); - $this->registerService('PreviewManager', function($c){ + $this->registerService('PreviewManager', function($c) { return new PreviewManager(); }); - $this->registerService('RootFolder', function($c){ + $this->registerService('RootFolder', function($c) { // TODO: get user and user manager from container as well $user = \OC_User::getUser(); $user = \OC_User::getManager()->get($user); @@ -57,6 +57,9 @@ class Server extends SimpleContainer implements IServerContainer { $view = new View(); return new Root($manager, $view, $user); }); + $this->registerService('UserCache', function($c) { + return new UserCache(); + }); } /** @@ -67,14 +70,13 @@ class Server extends SimpleContainer implements IServerContainer { } /** - * The current request object holding all information about the request currently being processed - * is returned from this method. + * The current request object holding all information about the request + * currently being processed is returned from this method. * In case the current execution was not initiated by a web request null is returned * * @return \OCP\IRequest|null */ - function getRequest() - { + function getRequest() { return $this->query('Request'); } @@ -83,8 +85,7 @@ class Server extends SimpleContainer implements IServerContainer { * * @return \OCP\IPreview */ - function getPreviewManager() - { + function getPreviewManager() { return $this->query('PreviewManager'); } @@ -93,8 +94,17 @@ class Server extends SimpleContainer implements IServerContainer { * * @return \OCP\Files\Folder */ - function getRootFolder() - { + function getRootFolder() { return $this->query('RootFolder'); } + + /** + * Returns an ICache instance + * + * @return \OCP\ICache + */ + function getCache() { + return $this->query('UserCache'); + } + } diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php index 038cb21b25..3767c83fcb 100644 --- a/tests/lib/cache/file.php +++ b/tests/lib/cache/file.php @@ -20,7 +20,9 @@ * */ -class Test_Cache_File extends Test_Cache { +namespace Test\Cache; + +class FileCache extends \Test_Cache { private $user; private $datadir; @@ -30,8 +32,8 @@ class Test_Cache_File extends Test_Cache { public function setUp() { //clear all proxies and hooks so we can do clean testing - OC_FileProxy::clearProxies(); - OC_Hook::clear('OC_Filesystem'); + \OC_FileProxy::clearProxies(); + \OC_Hook::clear('OC_Filesystem'); //disabled atm //enable only the encryption hook if needed @@ -44,27 +46,27 @@ class Test_Cache_File extends Test_Cache { $storage = new \OC\Files\Storage\Temporary(array()); \OC\Files\Filesystem::mount($storage,array(),'/'); $datadir = str_replace('local::', '', $storage->getId()); - $this->datadir = OC_Config::getValue('datadirectory', OC::$SERVERROOT.'/data'); - OC_Config::setValue('datadirectory', $datadir); + $this->datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data'); + \OC_Config::setValue('datadirectory', $datadir); - OC_User::clearBackends(); - OC_User::useBackend(new OC_User_Dummy()); + \OC_User::clearBackends(); + \OC_User::useBackend(new \OC_User_Dummy()); //login - OC_User::createUser('test', 'test'); + \OC_User::createUser('test', 'test'); - $this->user=OC_User::getUser(); - OC_User::setUserId('test'); + $this->user = \OC_User::getUser(); + \OC_User::setUserId('test'); //set up the users dir - $rootView=new \OC\Files\View(''); + $rootView = new \OC\Files\View(''); $rootView->mkdir('/test'); - $this->instance=new OC_Cache_File(); + $this->instance=new \OC\Cache\File(); } public function tearDown() { - OC_User::setUserId($this->user); - OC_Config::setValue('datadirectory', $this->datadir); + \OC_User::setUserId($this->user); + \OC_Config::setValue('datadirectory', $this->datadir); } } diff --git a/tests/lib/cache/usercache.php b/tests/lib/cache/usercache.php new file mode 100644 index 0000000000..21b7f848ab --- /dev/null +++ b/tests/lib/cache/usercache.php @@ -0,0 +1,68 @@ +. +* +*/ + +namespace Test\Cache; + +class UserCache extends \Test_Cache { + private $user; + private $datadir; + + public function setUp() { + //clear all proxies and hooks so we can do clean testing + \OC_FileProxy::clearProxies(); + \OC_Hook::clear('OC_Filesystem'); + + //disabled atm + //enable only the encryption hook if needed + //if(OC_App::isEnabled('files_encryption')) { + // OC_FileProxy::register(new OC_FileProxy_Encryption()); + //} + + //set up temporary storage + \OC\Files\Filesystem::clearMounts(); + $storage = new \OC\Files\Storage\Temporary(array()); + \OC\Files\Filesystem::mount($storage,array(),'/'); + $datadir = str_replace('local::', '', $storage->getId()); + $this->datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data'); + \OC_Config::setValue('datadirectory', $datadir); + + \OC_User::clearBackends(); + \OC_User::useBackend(new \OC_User_Dummy()); + + //login + \OC_User::createUser('test', 'test'); + + $this->user = \OC_User::getUser(); + \OC_User::setUserId('test'); + + //set up the users dir + $rootView=new \OC\Files\View(''); + $rootView->mkdir('/test'); + + $this->instance=new \OC\Cache\UserCache(); + } + + public function tearDown() { + \OC_User::setUserId($this->user); + \OC_Config::setValue('datadirectory', $this->datadir); + } +} From 1a130627012bb17ed9edc4583a4d8250ff4e2882 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 17 Sep 2013 18:02:37 +0200 Subject: [PATCH 052/180] Add legacy wrapper --- lib/legacy/cache.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lib/legacy/cache.php diff --git a/lib/legacy/cache.php b/lib/legacy/cache.php new file mode 100644 index 0000000000..83b214170f --- /dev/null +++ b/lib/legacy/cache.php @@ -0,0 +1,10 @@ + Date: Tue, 17 Sep 2013 18:31:14 +0200 Subject: [PATCH 053/180] Add interface for Session and add getter in server container. --- lib/public/iservercontainer.php | 7 ++++++ lib/public/isession.php | 44 +++++++++++++++++++++++++++++++++ lib/server.php | 10 ++++++++ lib/session/session.php | 2 +- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 lib/public/isession.php diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index d88330698d..ec7212b306 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -62,4 +62,11 @@ interface IServerContainer { */ function getRootFolder(); + /** + * Returns the current session + * + * @return \OCP\ISession + */ + function getSession(); + } diff --git a/lib/public/isession.php b/lib/public/isession.php new file mode 100644 index 0000000000..5f9ce32f3b --- /dev/null +++ b/lib/public/isession.php @@ -0,0 +1,44 @@ +query('RootFolder'); } + + /** + * Returns the current session + * + * @return \OCP\ISession + */ + function getSession() { + return \OC::$session; + } + } diff --git a/lib/session/session.php b/lib/session/session.php index 55515f57a8..c55001ecca 100644 --- a/lib/session/session.php +++ b/lib/session/session.php @@ -8,7 +8,7 @@ namespace OC\Session; -abstract class Session implements \ArrayAccess { +abstract class Session implements \ArrayAccess, \OCP\ISession { /** * $name serves as a namespace for the session keys * From 5bddb5377a40c987223804e8c3846437b6cf120a Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 17 Sep 2013 18:38:18 +0200 Subject: [PATCH 054/180] Purge session from Request - and fix some styles --- lib/appframework/http/request.php | 51 ++++++++++--------------------- lib/public/irequest.php | 9 ------ lib/server.php | 1 - 3 files changed, 16 insertions(+), 45 deletions(-) diff --git a/lib/appframework/http/request.php b/lib/appframework/http/request.php index 4f1775182a..34605acdfe 100644 --- a/lib/appframework/http/request.php +++ b/lib/appframework/http/request.php @@ -33,16 +33,15 @@ class Request implements \ArrayAccess, \Countable, IRequest { protected $items = array(); protected $allowedKeys = array( - 'get', - 'post', - 'files', - 'server', - 'env', - 'session', - 'cookies', - 'urlParams', - 'params', - 'parameters', + 'get', + 'post', + 'files', + 'server', + 'env', + 'cookies', + 'urlParams', + 'params', + 'parameters', 'method' ); @@ -156,7 +155,6 @@ class Request implements \ArrayAccess, \Countable, IRequest { case 'files': case 'server': case 'env': - case 'session': case 'cookies': case 'parameters': case 'params': @@ -229,8 +227,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @param mixed $default If the key is not found, this value will be returned * @return mixed the content of the array */ - public function getParam($key, $default = null) - { + public function getParam($key, $default = null) { return isset($this->parameters[$key]) ? $this->parameters[$key] : $default; @@ -241,8 +238,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { * (as GET or POST) or throuh the URL by the route * @return array the array with all parameters */ - public function getParams() - { + public function getParams() { return $this->parameters; } @@ -250,8 +246,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { * Returns the method of the request * @return string the method of the request (POST, GET, etc) */ - public function getMethod() - { + public function getMethod() { return $this->method; } @@ -260,8 +255,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @param string $key the key that will be taken from the $_FILES array * @return array the file in the $_FILES element */ - public function getUploadedFile($key) - { + public function getUploadedFile($key) { return isset($this->files[$key]) ? $this->files[$key] : null; } @@ -270,28 +264,16 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @param string $key the key that will be taken from the $_ENV array * @return array the value in the $_ENV element */ - public function getEnv($key) - { + public function getEnv($key) { return isset($this->env[$key]) ? $this->env[$key] : null; } - /** - * Shortcut for getting session variables - * @param string $key the key that will be taken from the $_SESSION array - * @return array the value in the $_SESSION element - */ - function getSession($key) - { - return isset($this->session[$key]) ? $this->session[$key] : null; - } - /** * Shortcut for getting cookie variables * @param string $key the key that will be taken from the $_COOKIE array * @return array the value in the $_COOKIE element */ - function getCookie($key) - { + function getCookie($key) { return isset($this->cookies[$key]) ? $this->cookies[$key] : null; } @@ -304,8 +286,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { * * @throws \LogicException */ - function getContent($asResource = false) - { + function getContent($asResource = false) { return null; // if (false === $this->content || (true === $asResource && null !== $this->content)) { // throw new \LogicException('getContent() can only be called once when using the resource return type.'); diff --git a/lib/public/irequest.php b/lib/public/irequest.php index cd39855950..9f335b06f2 100644 --- a/lib/public/irequest.php +++ b/lib/public/irequest.php @@ -76,15 +76,6 @@ interface IRequest { public function getEnv($key); - /** - * Shortcut for getting session variables - * - * @param string $key the key that will be taken from the $_SESSION array - * @return array the value in the $_SESSION element - */ - function getSession($key); - - /** * Shortcut for getting cookie variables * diff --git a/lib/server.php b/lib/server.php index 0124ad72c0..0eee3e0f73 100644 --- a/lib/server.php +++ b/lib/server.php @@ -36,7 +36,6 @@ class Server extends SimpleContainer implements IServerContainer { 'files' => $_FILES, 'server' => $_SERVER, 'env' => $_ENV, - 'session' => $_SESSION, 'cookies' => $_COOKIE, 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) ? $_SERVER['REQUEST_METHOD'] From 8b4f4a79e22dc08cf7c13a91c926c229676d6522 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 17 Sep 2013 19:46:08 +0200 Subject: [PATCH 055/180] Still some session leftovers. --- lib/appframework/controller/controller.php | 10 ---------- tests/lib/appframework/controller/ControllerTest.php | 5 ----- 2 files changed, 15 deletions(-) diff --git a/lib/appframework/controller/controller.php b/lib/appframework/controller/controller.php index a7498ba0e1..0ea0a38cc0 100644 --- a/lib/appframework/controller/controller.php +++ b/lib/appframework/controller/controller.php @@ -106,16 +106,6 @@ abstract class Controller { } - /** - * Shortcut for getting session variables - * @param string $key the key that will be taken from the $_SESSION array - * @return array the value in the $_SESSION element - */ - public function session($key) { - return $this->request->getSession($key); - } - - /** * Shortcut for getting cookie variables * @param string $key the key that will be taken from the $_COOKIE array diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php index 246371d249..4441bddfca 100644 --- a/tests/lib/appframework/controller/ControllerTest.php +++ b/tests/lib/appframework/controller/ControllerTest.php @@ -152,9 +152,4 @@ class ControllerTest extends \PHPUnit_Framework_TestCase { $this->assertEquals('daheim', $this->controller->env('PATH')); } - public function testGetSessionVariable(){ - $this->assertEquals('kein', $this->controller->session('sezession')); - } - - } From b0762ad3bf5121ccd300ec6c22641c3bf323ba61 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 00:37:00 +0200 Subject: [PATCH 056/180] OC_VCategories=>OC\Tags. Public interface + getter in server container --- lib/public/iservercontainer.php | 7 + lib/public/itags.php | 173 +++++++ lib/server.php | 12 + lib/tags.php | 621 ++++++++++++++++++++++++ lib/vcategories.php | 833 -------------------------------- tests/lib/tags.php | 133 +++++ tests/lib/vcategories.php | 128 ----- 7 files changed, 946 insertions(+), 961 deletions(-) create mode 100644 lib/public/itags.php create mode 100644 lib/tags.php delete mode 100644 lib/vcategories.php create mode 100644 tests/lib/tags.php delete mode 100644 tests/lib/vcategories.php diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index d88330698d..e44acee653 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -55,6 +55,13 @@ interface IServerContainer { */ function getPreviewManager(); + /** + * Returns the tag manager which can get and set tags for different object types + * + * @return \OCP\ITags + */ + function getTagManager(); + /** * Returns the root folder of ownCloud's data directory * diff --git a/lib/public/itags.php b/lib/public/itags.php new file mode 100644 index 0000000000..047d4f5f40 --- /dev/null +++ b/lib/public/itags.php @@ -0,0 +1,173 @@ + +* +* 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 . +* +*/ + +namespace OCP; + +// FIXME: Where should I put this? Or should it be implemented as a Listener? +\OC_Hook::connect('OC_User', 'post_deleteUser', 'OC\Tags', 'post_deleteUser'); + +/** + * Class for easily tagging objects by their id + * + * A tag can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or + * anything else that is either parsed from a vobject or that the user chooses + * to add. + * Tag names are not case-sensitive, but will be saved with the case they + * are entered in. If a user already has a tag 'family' for a type, and + * tries to add a tag named 'Family' it will be silently ignored. + */ + +interface ITags { + + /** + * Load tags from db. + * + * @param string $type The type identifier e.g. 'contact' or 'event'. + * @param array $defaultTags An array of default tags to be used if none are stored. + * @return \OCP\ITags + */ + public function loadTagsFor($type, $defaultTags=array()); + + /** + * Check if any tags are saved for this type and user. + * + * @return boolean. + */ + public function isEmpty(); + + /** + * Get the tags for a specific user. + * + * This returns an array with id/name maps: + * [ + * ['id' => 0, 'name' = 'First tag'], + * ['id' => 1, 'name' = 'Second tag'], + * ] + * + * @returns array + */ + public function tags(); + + /** + * Get the a list if items tagged with $tag. + * + * Throws an exception if the tag could not be found. + * + * @param string|integer $tag Tag id or name. + * @return array An array of object ids or false on error. + */ + public function idsForTag($tag); + + /** + * Checks whether a tag is already saved. + * + * @param string $name The name to check for. + * @return bool + */ + public function hasTag($name); + + /** + * Add a new tag. + * + * @param string $name A string with a name of the tag + * @return int the id of the added tag or false if it already exists. + */ + public function add($name); + + /** + * Rename tag. + * + * @param string $from The name of the existing tag + * @param string $to The new name of the tag. + * @return bool + */ + public function rename($from, $to); + + /** + * Add a list of new tags. + * + * @param string[] $names A string with a name or an array of strings containing + * the name(s) of the to add. + * @param bool $sync When true, save the tags + * @param int|null $id int Optional object id to add to this|these tag(s) + * @return bool Returns false on error. + */ + public function addMulti($names, $sync=false, $id = null); + + /** + * Delete tag/object relations from the db + * + * @param array $ids The ids of the objects + * @return boolean Returns false on error. + */ + public function purgeObjects(array $ids); + + /** + * Get favorites for an object type + * + * @return array An array of object ids. + */ + public function getFavorites(); + + /** + * Add an object to favorites + * + * @param int $objid The id of the object + * @return boolean + */ + public function addToFavorites($objid); + + /** + * Remove an object from favorites + * + * @param int $objid The id of the object + * @return boolean + */ + public function removeFromFavorites($objid); + + /** + * Creates a tag/object relation. + * + * @param int $objid The id of the object + * @param int|string $tag The id or name of the tag + * @return boolean Returns false on database error. + */ + public function tagAs($objid, $tag); + + /** + * Delete single tag/object relation from the db + * + * @param int $objid The id of the object + * @param int|string $tag The id or name of the tag + * @return boolean + */ + public function unTag($objid, $tag); + + /** + * Delete tags from the + * + * @param string[] $names An array of tags to delete + * @return bool Returns false on error + */ + public function delete($names); + +} \ No newline at end of file diff --git a/lib/server.php b/lib/server.php index 9e87bd3190..f25216b746 100644 --- a/lib/server.php +++ b/lib/server.php @@ -49,6 +49,9 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('PreviewManager', function($c){ return new PreviewManager(); }); + $this->registerService('TagManager', function($c){ + return new Tags(); + }); $this->registerService('RootFolder', function($c){ // TODO: get user and user manager from container as well $user = \OC_User::getUser(); @@ -88,6 +91,15 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('PreviewManager'); } + /** + * Returns the tag manager which can get and set tags for different object types + * + * @return \OCP\ITags + */ + function getTagManager() { + return $this->query('TagManager'); + } + /** * Returns the root folder of ownCloud's data directory * diff --git a/lib/tags.php b/lib/tags.php new file mode 100644 index 0000000000..4aafff8e1b --- /dev/null +++ b/lib/tags.php @@ -0,0 +1,621 @@ + +* @copyright 2012 Bart Visscher bartv@thisnet.nl +* +* 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 . +* +*/ + +/** + * Class for easily tagging objects by their id + * + * A tag can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or + * anything else that is either parsed from a vobject or that the user chooses + * to add. + * Tag names are not case-sensitive, but will be saved with the case they + * are entered in. If a user already has a tag 'family' for a type, and + * tries to add a tag named 'Family' it will be silently ignored. + */ + +namespace OC; + +class Tags implements \OCP\ITags { + + /** + * Tags + */ + private $tags = array(); + + /** + * Used for storing objectid/categoryname pairs while rescanning. + */ + private static $relations = array(); + + private $type = null; + private $user = null; + + const TAG_TABLE = '*PREFIX*vcategory'; + const RELATION_TABLE = '*PREFIX*vcategory_to_object'; + + const TAG_FAVORITE = '_$!!$_'; + + /** + * Constructor. + * + * @param string $user The user whos data the object will operate on. + */ + public function __construct($user) { + + $this->user = $user; + + } + + /** + * Load tags from db. + * + * @param string $type The type identifier e.g. 'contact' or 'event'. + * @param array $defaultTags An array of default tags to be used if none are stored. + * @return \OCP\ITags + */ + public function loadTagsFor($type, $defaultTags=array()) { + $this->type = $type; + $this->tags = array(); + $result = null; + $sql = 'SELECT `id`, `category` FROM `' . self::TAG_TABLE . '` ' + . 'WHERE `uid` = ? AND `type` = ? ORDER BY `category`'; + try { + $stmt = \OCP\DB::prepare($sql); + $result = $stmt->execute(array($this->user, $this->type)); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + } + + if(!is_null($result)) { + while( $row = $result->fetchRow()) { + $this->tags[$row['id']] = $row['category']; + } + } + + if(count($defaultTags) > 0 && count($this->tags) === 0) { + $this->addMulti($defaultTags, true); + } + \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true), + \OCP\Util::DEBUG); + + return $this; + } + + /** + * Check if any tags are saved for this type and user. + * + * @return boolean. + */ + public function isEmpty() { + $sql = 'SELECT COUNT(*) FROM `' . self::TAG_TABLE . '` ' + . 'WHERE `uid` = ? AND `type` = ?'; + try { + $stmt = OCP\DB::prepare($sql); + $result = $stmt->execute(array($this->user, $this->type)); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + return false; + } + return ($result->numRows() === 0); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + } + + /** + * Get the tags for a specific user. + * + * This returns an array with id/name maps: + * [ + * ['id' => 0, 'name' = 'First tag'], + * ['id' => 1, 'name' = 'Second tag'], + * ] + * + * @return array + */ + public function tags() { + if(!count($this->tags)) { + return array(); + } + + $tags = array_values($this->tags); + uasort($tags, 'strnatcasecmp'); + $tagMap = array(); + + foreach($tags as $tag) { + if($tag !== self::TAG_FAVORITE) { + $tagMap[] = array( + 'id' => $this->array_searchi($tag, $this->tags), + 'name' => $tag + ); + } + } + return $tagMap; + + } + + /** + * Get the a list if items tagged with $tag. + * + * Throws an exception if the tag could not be found. + * + * @param string|integer $tag Tag id or name. + * @return array An array of object ids or false on error. + */ + public function idsForTag($tag) { + $result = null; + if(is_numeric($tag)) { + $tagId = $tag; + } elseif(is_string($tag)) { + $tag = trim($tag); + $tagId = $this->array_searchi($tag, $this->tags); + } + + if($tagId === false) { + $l10n = \OC_L10N::get('core'); + throw new \Exception( + $l10n->t('Could not find category "%s"', $tag) + ); + } + + $ids = array(); + $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE + . '` WHERE `categoryid` = ?'; + + try { + $stmt = \OCP\DB::prepare($sql); + $result = $stmt->execute(array($tagId)); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + return false; + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + + if(!is_null($result)) { + while( $row = $result->fetchRow()) { + $ids[] = (int)$row['objid']; + } + } + + return $ids; + } + + /** + * Checks whether a tag is already saved. + * + * @param string $name The name to check for. + * @return bool + */ + public function hasTag($name) { + return $this->in_arrayi($name, $this->tags); + } + + /** + * Add a new tag. + * + * @param string $name A string with a name of the tag + * @return int the id of the added tag or false if it already exists. + */ + public function add($name) { + $name = trim($name); + + if($this->hasTag($name)) { + \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', \OCP\Util::DEBUG); + return false; + } + try { + \OCP\DB::insertIfNotExist(self::TAG_TABLE, + array( + 'uid' => $this->user, + 'type' => $this->type, + 'category' => $name, + )); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + $id = \OCP\DB::insertid(self::TAG_TABLE); + \OCP\Util::writeLog('core', __METHOD__.', id: ' . $id, \OCP\Util::DEBUG); + $this->tags[$id] = $name; + return $id; + } + + /** + * Rename tag. + * + * @param string $from The name of the existing tag + * @param string $to The new name of the tag. + * @return bool + */ + public function rename($from, $to) { + $from = trim($from); + $to = trim($to); + $id = $this->array_searchi($from, $this->tags); + if($id === false) { + \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', \OCP\Util::DEBUG); + return false; + } + + $sql = 'UPDATE `' . self::TAG_TABLE . '` SET `category` = ? ' + . 'WHERE `uid` = ? AND `type` = ? AND `id` = ?'; + try { + $stmt = \OCP\DB::prepare($sql); + $result = $stmt->execute(array($to, $this->user, $this->type, $id)); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + return false; + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + $this->tags[$id] = $to; + return true; + } + + /** + * Add a list of new tags. + * + * @param string[] $names A string with a name or an array of strings containing + * the name(s) of the to add. + * @param bool $sync When true, save the tags + * @param int|null $id int Optional object id to add to this|these tag(s) + * @return bool Returns false on error. + */ + public function addMulti($names, $sync=false, $id = null) { + if(!is_array($names)) { + $names = array($names); + } + $names = array_map('trim', $names); + $newones = array(); + foreach($names as $name) { + if(($this->in_arrayi( + $name, $this->tags) == false) && $name !== '') { + $newones[] = $name; + } + if(!is_null($id) ) { + // Insert $objectid, $categoryid pairs if not exist. + self::$relations[] = array('objid' => $id, 'tag' => $name); + } + } + $this->tags = array_merge($this->tags, $newones); + if($sync === true) { + $this->save(); + } + + return true; + } + + /** + * Save the list of tags and their object relations + */ + protected function save() { + if(is_array($this->tags)) { + foreach($this->tags as $tag) { + try { + \OCP\DB::insertIfNotExist(self::TAG_TABLE, + array( + 'uid' => $this->user, + 'type' => $this->type, + 'category' => $tag, + )); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + } + } + // reload tags to get the proper ids. + $this->loadTagsFor($this->type); + // Loop through temporarily cached objectid/tagname pairs + // and save relations. + $tags = $this->tags; + // For some reason this is needed or array_search(i) will return 0..? + ksort($tags); + foreach(self::$relations as $relation) { + $tagId = $this->array_searchi($relation['tag'], $tags); + \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, \OCP\Util::DEBUG); + if($tagId) { + try { + \OCP\DB::insertIfNotExist(self::RELATION_TABLE, + array( + 'objid' => $relation['objid'], + 'categoryid' => $tagId, + 'type' => $this->type, + )); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + } + } + } + self::$relations = array(); // reset + } else { + \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! ' + . print_r($this->tags, true), \OCP\Util::ERROR); + } + } + + /** + * Delete tags and tag/object relations for a user. + * + * For hooking up on post_deleteUser + * + * @param array + */ + public static function post_deleteUser($arguments) { + // Find all objectid/tagId pairs. + $result = null; + try { + $stmt = \OCP\DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` ' + . 'WHERE `uid` = ?'); + $result = $stmt->execute(array($arguments['uid'])); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + } + + if(!is_null($result)) { + try { + $stmt = \OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `categoryid` = ?'); + while( $row = $result->fetchRow()) { + try { + $stmt->execute(array($row['id'])); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + } + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + } + } + try { + $stmt = \OCP\DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` ' + . 'WHERE `uid` = ?'); + $result = $stmt->execute(array($arguments['uid'])); + if (OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + } + } catch(\Exception $e) { + OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), OCP\Util::ERROR); + } + } + + /** + * Delete tag/object relations from the db + * + * @param array $ids The ids of the objects + * @return boolean Returns false on error. + */ + public function purgeObjects(array $ids) { + if(count($ids) === 0) { + // job done ;) + return true; + } + $updates = $ids; + try { + $query = 'DELETE FROM `' . self::RELATION_TABLE . '` '; + $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) '; + $query .= 'AND `type`= ?'; + $updates[] = $this->type; + $stmt = OCP\DB::prepare($query); + $result = $stmt->execute($updates); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + return false; + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), + \OCP\Util::ERROR); + return false; + } + return true; + } + + /** + * Get favorites for an object type + * + * @return array An array of object ids. + */ + public function getFavorites() { + try { + return $this->idsForTag(self::TAG_FAVORITE); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), + \OCP\Util::ERROR); + return array(); + } + } + + /** + * Add an object to favorites + * + * @param int $objid The id of the object + * @return boolean + */ + public function addToFavorites($objid) { + if(!$this->hasCategory(self::TAG_FAVORITE)) { + $this->add(self::TAG_FAVORITE, true); + } + return $this->tagAs($objid, self::TAG_FAVORITE, $this->type); + } + + /** + * Remove an object from favorites + * + * @param int $objid The id of the object + * @return boolean + */ + public function removeFromFavorites($objid) { + return $this->unTag($objid, self::TAG_FAVORITE, $this->type); + } + + /** + * Creates a tag/object relation. + * + * @param int $objid The id of the object + * @param int|string $tag The id or name of the tag + * @return boolean Returns false on database error. + */ + public function tagAs($objid, $tag) { + if(is_string($tag) && !is_numeric($tag)) { + $tag = trim($tag); + if(!$this->hasTag($tag)) { + $this->add($tag, true); + } + $tagId = $this->array_searchi($tag, $this->tags); + } else { + $tagId = $tag; + } + try { + \OCP\DB::insertIfNotExist(self::RELATION_TABLE, + array( + 'objid' => $objid, + 'categoryid' => $tagId, + 'type' => $this->type, + )); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + return true; + } + + /** + * Delete single tag/object relation from the db + * + * @param int $objid The id of the object + * @param int|string $tag The id or name of the tag + * @return boolean + */ + public function unTag($objid, $tag) { + if(is_string($tag) && !is_numeric($tag)) { + $tag = trim($tag); + $tagId = $this->array_searchi($tag, $this->tags); + } else { + $tagId = $tag; + } + + try { + $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?'; + $stmt = \OCP\DB::prepare($sql); + $stmt->execute(array($objid, $tagId, $this->type)); + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + return true; + } + + /** + * Delete tags from the + * + * @param string[] $names An array of tags to delete + * @return bool Returns false on error + */ + public function delete($names) { + if(!is_array($names)) { + $names = array($names); + } + + $names = array_map('trim', $names); + + \OCP\Util::writeLog('core', __METHOD__ . ', before: ' + . print_r($this->tags, true), \OCP\Util::DEBUG); + foreach($names as $name) { + $id = null; + + if($this->hasTag($name)) { + $id = $this->array_searchi($name, $this->tags); + unset($this->tags[$id]); + } + try { + $stmt = \OCP\DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` WHERE ' + . '`uid` = ? AND `type` = ? AND `category` = ?'); + $result = $stmt->execute(array($this->user, $this->type, $name)); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), \OCP\Util::ERROR); + return false; + } + if(!is_null($id) && $id !== false) { + try { + $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' + . 'WHERE `categoryid` = ?'; + $stmt = \OCP\DB::prepare($sql); + $result = $stmt->execute(array($id)); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', + __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), + \OCP\Util::ERROR); + return false; + } + } catch(\Exception $e) { + \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), + \OCP\Util::ERROR); + return false; + } + } + } + return true; + } + + // case-insensitive in_array + private function in_arrayi($needle, $haystack) { + if(!is_array($haystack)) { + return false; + } + return in_array(strtolower($needle), array_map('strtolower', $haystack)); + } + + // case-insensitive array_search + private function array_searchi($needle, $haystack) { + if(!is_array($haystack)) { + return false; + } + return array_search(strtolower($needle), array_map('strtolower', $haystack)); + } +} diff --git a/lib/vcategories.php b/lib/vcategories.php deleted file mode 100644 index a7e4c54be2..0000000000 --- a/lib/vcategories.php +++ /dev/null @@ -1,833 +0,0 @@ - -* @copyright 2012 Bart Visscher bartv@thisnet.nl -* -* 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 . -* -*/ - -OC_Hook::connect('OC_User', 'post_deleteUser', 'OC_VCategories', 'post_deleteUser'); - -/** - * Class for easy access to categories in VCARD, VEVENT, VTODO and VJOURNAL. - * A Category can be e.g. 'Family', 'Work', 'Chore', 'Special Occation' or - * anything else that is either parsed from a vobject or that the user chooses - * to add. - * Category names are not case-sensitive, but will be saved with the case they - * are entered in. If a user already has a category 'family' for a type, and - * tries to add a category named 'Family' it will be silently ignored. - */ -class OC_VCategories { - - /** - * Categories - */ - private $categories = array(); - - /** - * Used for storing objectid/categoryname pairs while rescanning. - */ - private static $relations = array(); - - private $type = null; - private $user = null; - - const CATEGORY_TABLE = '*PREFIX*vcategory'; - const RELATION_TABLE = '*PREFIX*vcategory_to_object'; - - const CATEGORY_FAVORITE = '_$!!$_'; - - const FORMAT_LIST = 0; - const FORMAT_MAP = 1; - - /** - * @brief Constructor. - * @param $type The type identifier e.g. 'contact' or 'event'. - * @param $user The user whos data the object will operate on. This - * parameter should normally be omitted but to make an app able to - * update categories for all users it is made possible to provide it. - * @param $defcategories An array of default categories to be used if none is stored. - */ - public function __construct($type, $user=null, $defcategories=array()) { - $this->type = $type; - $this->user = is_null($user) ? OC_User::getUser() : $user; - - $this->loadCategories(); - OCP\Util::writeLog('core', __METHOD__ . ', categories: ' - . print_r($this->categories, true), - OCP\Util::DEBUG - ); - - if($defcategories && count($this->categories) === 0) { - $this->addMulti($defcategories, true); - } - } - - /** - * @brief Load categories from db. - */ - private function loadCategories() { - $this->categories = array(); - $result = null; - $sql = 'SELECT `id`, `category` FROM `' . self::CATEGORY_TABLE . '` ' - . 'WHERE `uid` = ? AND `type` = ? ORDER BY `category`'; - try { - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array($this->user, $this->type)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - - if(!is_null($result)) { - while( $row = $result->fetchRow()) { - // The keys are prefixed because array_search wouldn't work otherwise :-/ - $this->categories[$row['id']] = $row['category']; - } - } - OCP\Util::writeLog('core', __METHOD__.', categories: ' . print_r($this->categories, true), - OCP\Util::DEBUG); - } - - - /** - * @brief Check if any categories are saved for this type and user. - * @returns boolean. - * @param $type The type identifier e.g. 'contact' or 'event'. - * @param $user The user whos categories will be checked. If not set current user will be used. - */ - public static function isEmpty($type, $user = null) { - $user = is_null($user) ? OC_User::getUser() : $user; - $sql = 'SELECT COUNT(*) FROM `' . self::CATEGORY_TABLE . '` ' - . 'WHERE `uid` = ? AND `type` = ?'; - try { - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array($user, $type)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - return ($result->numRows() === 0); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - } - - /** - * @brief Get the categories for a specific user. - * @param - * @returns array containing the categories as strings. - */ - public function categories($format = null) { - if(!$this->categories) { - return array(); - } - $categories = array_values($this->categories); - uasort($categories, 'strnatcasecmp'); - if($format == self::FORMAT_MAP) { - $catmap = array(); - foreach($categories as $category) { - if($category !== self::CATEGORY_FAVORITE) { - $catmap[] = array( - 'id' => $this->array_searchi($category, $this->categories), - 'name' => $category - ); - } - } - return $catmap; - } - - // Don't add favorites to normal categories. - $favpos = array_search(self::CATEGORY_FAVORITE, $categories); - if($favpos !== false) { - return array_splice($categories, $favpos); - } else { - return $categories; - } - } - - /** - * Get the a list if items belonging to $category. - * - * Throws an exception if the category could not be found. - * - * @param string|integer $category Category id or name. - * @returns array An array of object ids or false on error. - */ - public function idsForCategory($category) { - $result = null; - if(is_numeric($category)) { - $catid = $category; - } elseif(is_string($category)) { - $category = trim($category); - $catid = $this->array_searchi($category, $this->categories); - } - OCP\Util::writeLog('core', __METHOD__.', category: '.$catid.' '.$category, OCP\Util::DEBUG); - if($catid === false) { - $l10n = OC_L10N::get('core'); - throw new Exception( - $l10n->t('Could not find category "%s"', $category) - ); - } - - $ids = array(); - $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE - . '` WHERE `categoryid` = ?'; - - try { - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array($catid)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - - if(!is_null($result)) { - while( $row = $result->fetchRow()) { - $ids[] = (int)$row['objid']; - } - } - - return $ids; - } - - /** - * Get the a list if items belonging to $category. - * - * Throws an exception if the category could not be found. - * - * @param string|integer $category Category id or name. - * @param array $tableinfo Array in the form {'tablename' => table, 'fields' => ['field1', 'field2']} - * @param int $limit - * @param int $offset - * - * This generic method queries a table assuming that the id - * field is called 'id' and the table name provided is in - * the form '*PREFIX*table_name'. - * - * If the category name cannot be resolved an exception is thrown. - * - * TODO: Maybe add the getting permissions for objects? - * - * @returns array containing the resulting items or false on error. - */ - public function itemsForCategory($category, $tableinfo, $limit = null, $offset = null) { - $result = null; - if(is_numeric($category)) { - $catid = $category; - } elseif(is_string($category)) { - $category = trim($category); - $catid = $this->array_searchi($category, $this->categories); - } - OCP\Util::writeLog('core', __METHOD__.', category: '.$catid.' '.$category, OCP\Util::DEBUG); - if($catid === false) { - $l10n = OC_L10N::get('core'); - throw new Exception( - $l10n->t('Could not find category "%s"', $category) - ); - } - $fields = ''; - foreach($tableinfo['fields'] as $field) { - $fields .= '`' . $tableinfo['tablename'] . '`.`' . $field . '`,'; - } - $fields = substr($fields, 0, -1); - - $items = array(); - $sql = 'SELECT `' . self::RELATION_TABLE . '`.`categoryid`, ' . $fields - . ' FROM `' . $tableinfo['tablename'] . '` JOIN `' - . self::RELATION_TABLE . '` ON `' . $tableinfo['tablename'] - . '`.`id` = `' . self::RELATION_TABLE . '`.`objid` WHERE `' - . self::RELATION_TABLE . '`.`categoryid` = ?'; - - try { - $stmt = OCP\DB::prepare($sql, $limit, $offset); - $result = $stmt->execute(array($catid)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - - if(!is_null($result)) { - while( $row = $result->fetchRow()) { - $items[] = $row; - } - } - //OCP\Util::writeLog('core', __METHOD__.', count: ' . count($items), OCP\Util::DEBUG); - //OCP\Util::writeLog('core', __METHOD__.', sql: ' . $sql, OCP\Util::DEBUG); - - return $items; - } - - /** - * @brief Checks whether a category is already saved. - * @param $name The name to check for. - * @returns bool - */ - public function hasCategory($name) { - return $this->in_arrayi($name, $this->categories); - } - - /** - * @brief Add a new category. - * @param $name A string with a name of the category - * @returns int the id of the added category or false if it already exists. - */ - public function add($name) { - $name = trim($name); - OCP\Util::writeLog('core', __METHOD__.', name: ' . $name, OCP\Util::DEBUG); - if($this->hasCategory($name)) { - OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', OCP\Util::DEBUG); - return false; - } - try { - OCP\DB::insertIfNotExist(self::CATEGORY_TABLE, - array( - 'uid' => $this->user, - 'type' => $this->type, - 'category' => $name, - )); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - $id = OCP\DB::insertid(self::CATEGORY_TABLE); - OCP\Util::writeLog('core', __METHOD__.', id: ' . $id, OCP\Util::DEBUG); - $this->categories[$id] = $name; - return $id; - } - - /** - * @brief Rename category. - * @param string $from The name of the existing category - * @param string $to The new name of the category. - * @returns bool - */ - public function rename($from, $to) { - $from = trim($from); - $to = trim($to); - $id = $this->array_searchi($from, $this->categories); - if($id === false) { - OCP\Util::writeLog('core', __METHOD__.', category: ' . $from. ' does not exist', OCP\Util::DEBUG); - return false; - } - - $sql = 'UPDATE `' . self::CATEGORY_TABLE . '` SET `category` = ? ' - . 'WHERE `uid` = ? AND `type` = ? AND `id` = ?'; - try { - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array($to, $this->user, $this->type, $id)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - $this->categories[$id] = $to; - return true; - } - - /** - * @brief Add a new category. - * @param $names A string with a name or an array of strings containing - * the name(s) of the categor(y|ies) to add. - * @param $sync bool When true, save the categories - * @param $id int Optional object id to add to this|these categor(y|ies) - * @returns bool Returns false on error. - */ - public function addMulti($names, $sync=false, $id = null) { - if(!is_array($names)) { - $names = array($names); - } - $names = array_map('trim', $names); - $newones = array(); - foreach($names as $name) { - if(($this->in_arrayi( - $name, $this->categories) == false) && $name != '') { - $newones[] = $name; - } - if(!is_null($id) ) { - // Insert $objectid, $categoryid pairs if not exist. - self::$relations[] = array('objid' => $id, 'category' => $name); - } - } - $this->categories = array_merge($this->categories, $newones); - if($sync === true) { - $this->save(); - } - - return true; - } - - /** - * @brief Extracts categories from a vobject and add the ones not already present. - * @param $vobject The instance of OC_VObject to load the categories from. - */ - public function loadFromVObject($id, $vobject, $sync=false) { - $this->addMulti($vobject->getAsArray('CATEGORIES'), $sync, $id); - } - - /** - * @brief Reset saved categories and rescan supplied vobjects for categories. - * @param $objects An array of vobjects (as text). - * To get the object array, do something like: - * // For Addressbook: - * $categories = new OC_VCategories('contacts'); - * $stmt = OC_DB::prepare( 'SELECT `carddata` FROM `*PREFIX*contacts_cards`' ); - * $result = $stmt->execute(); - * $objects = array(); - * if(!is_null($result)) { - * while( $row = $result->fetchRow()){ - * $objects[] = array($row['id'], $row['carddata']); - * } - * } - * $categories->rescan($objects); - */ - public function rescan($objects, $sync=true, $reset=true) { - - if($reset === true) { - $result = null; - // Find all objectid/categoryid pairs. - try { - $stmt = OCP\DB::prepare('SELECT `id` FROM `' . self::CATEGORY_TABLE . '` ' - . 'WHERE `uid` = ? AND `type` = ?'); - $result = $stmt->execute(array($this->user, $this->type)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - - // And delete them. - if(!is_null($result)) { - $stmt = OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' - . 'WHERE `categoryid` = ? AND `type`= ?'); - while( $row = $result->fetchRow()) { - $stmt->execute(array($row['id'], $this->type)); - } - } - try { - $stmt = OCP\DB::prepare('DELETE FROM `' . self::CATEGORY_TABLE . '` ' - . 'WHERE `uid` = ? AND `type` = ?'); - $result = $stmt->execute(array($this->user, $this->type)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return; - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__ . ', exception: ' - . $e->getMessage(), OCP\Util::ERROR); - return; - } - $this->categories = array(); - } - // Parse all the VObjects - foreach($objects as $object) { - $vobject = OC_VObject::parse($object[1]); - if(!is_null($vobject)) { - // Load the categories - $this->loadFromVObject($object[0], $vobject, $sync); - } else { - OC_Log::write('core', __METHOD__ . ', unable to parse. ID: ' . ', ' - . substr($object, 0, 100) . '(...)', OC_Log::DEBUG); - } - } - $this->save(); - } - - /** - * @brief Save the list with categories - */ - private function save() { - if(is_array($this->categories)) { - foreach($this->categories as $category) { - try { - OCP\DB::insertIfNotExist(self::CATEGORY_TABLE, - array( - 'uid' => $this->user, - 'type' => $this->type, - 'category' => $category, - )); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - } - // reload categories to get the proper ids. - $this->loadCategories(); - // Loop through temporarily cached objectid/categoryname pairs - // and save relations. - $categories = $this->categories; - // For some reason this is needed or array_search(i) will return 0..? - ksort($categories); - foreach(self::$relations as $relation) { - $catid = $this->array_searchi($relation['category'], $categories); - OC_Log::write('core', __METHOD__ . 'catid, ' . $relation['category'] . ' ' . $catid, OC_Log::DEBUG); - if($catid) { - try { - OCP\DB::insertIfNotExist(self::RELATION_TABLE, - array( - 'objid' => $relation['objid'], - 'categoryid' => $catid, - 'type' => $this->type, - )); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - } - } - self::$relations = array(); // reset - } else { - OC_Log::write('core', __METHOD__.', $this->categories is not an array! ' - . print_r($this->categories, true), OC_Log::ERROR); - } - } - - /** - * @brief Delete categories and category/object relations for a user. - * For hooking up on post_deleteUser - * @param string $uid The user id for which entries should be purged. - */ - public static function post_deleteUser($arguments) { - // Find all objectid/categoryid pairs. - $result = null; - try { - $stmt = OCP\DB::prepare('SELECT `id` FROM `' . self::CATEGORY_TABLE . '` ' - . 'WHERE `uid` = ?'); - $result = $stmt->execute(array($arguments['uid'])); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - - if(!is_null($result)) { - try { - $stmt = OCP\DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' - . 'WHERE `categoryid` = ?'); - while( $row = $result->fetchRow()) { - try { - $stmt->execute(array($row['id'])); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - } - } - try { - $stmt = OCP\DB::prepare('DELETE FROM `' . self::CATEGORY_TABLE . '` ' - . 'WHERE `uid` = ?'); - $result = $stmt->execute(array($arguments['uid'])); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__ . ', exception: ' - . $e->getMessage(), OCP\Util::ERROR); - } - } - - /** - * @brief Delete category/object relations from the db - * @param array $ids The ids of the objects - * @param string $type The type of object (event/contact/task/journal). - * Defaults to the type set in the instance - * @returns boolean Returns false on error. - */ - public function purgeObjects(array $ids, $type = null) { - $type = is_null($type) ? $this->type : $type; - if(count($ids) === 0) { - // job done ;) - return true; - } - $updates = $ids; - try { - $query = 'DELETE FROM `' . self::RELATION_TABLE . '` '; - $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) '; - $query .= 'AND `type`= ?'; - $updates[] = $type; - $stmt = OCP\DB::prepare($query); - $result = $stmt->execute($updates); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), - OCP\Util::ERROR); - return false; - } - return true; - } - - /** - * Get favorites for an object type - * - * @param string $type The type of object (event/contact/task/journal). - * Defaults to the type set in the instance - * @returns array An array of object ids. - */ - public function getFavorites($type = null) { - $type = is_null($type) ? $this->type : $type; - - try { - return $this->idsForCategory(self::CATEGORY_FAVORITE); - } catch(Exception $e) { - // No favorites - return array(); - } - } - - /** - * Add an object to favorites - * - * @param int $objid The id of the object - * @param string $type The type of object (event/contact/task/journal). - * Defaults to the type set in the instance - * @returns boolean - */ - public function addToFavorites($objid, $type = null) { - $type = is_null($type) ? $this->type : $type; - if(!$this->hasCategory(self::CATEGORY_FAVORITE)) { - $this->add(self::CATEGORY_FAVORITE, true); - } - return $this->addToCategory($objid, self::CATEGORY_FAVORITE, $type); - } - - /** - * Remove an object from favorites - * - * @param int $objid The id of the object - * @param string $type The type of object (event/contact/task/journal). - * Defaults to the type set in the instance - * @returns boolean - */ - public function removeFromFavorites($objid, $type = null) { - $type = is_null($type) ? $this->type : $type; - return $this->removeFromCategory($objid, self::CATEGORY_FAVORITE, $type); - } - - /** - * @brief Creates a category/object relation. - * @param int $objid The id of the object - * @param int|string $category The id or name of the category - * @param string $type The type of object (event/contact/task/journal). - * Defaults to the type set in the instance - * @returns boolean Returns false on database error. - */ - public function addToCategory($objid, $category, $type = null) { - $type = is_null($type) ? $this->type : $type; - if(is_string($category) && !is_numeric($category)) { - $category = trim($category); - if(!$this->hasCategory($category)) { - $this->add($category, true); - } - $categoryid = $this->array_searchi($category, $this->categories); - } else { - $categoryid = $category; - } - try { - OCP\DB::insertIfNotExist(self::RELATION_TABLE, - array( - 'objid' => $objid, - 'categoryid' => $categoryid, - 'type' => $type, - )); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - return true; - } - - /** - * @brief Delete single category/object relation from the db - * @param int $objid The id of the object - * @param int|string $category The id or name of the category - * @param string $type The type of object (event/contact/task/journal). - * Defaults to the type set in the instance - * @returns boolean - */ - public function removeFromCategory($objid, $category, $type = null) { - $type = is_null($type) ? $this->type : $type; - if(is_string($category) && !is_numeric($category)) { - $category = trim($category); - $categoryid = $this->array_searchi($category, $this->categories); - } else { - $categoryid = $category; - } - - try { - $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' - . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?'; - OCP\Util::writeLog('core', __METHOD__.', sql: ' . $objid . ' ' . $categoryid . ' ' . $type, - OCP\Util::DEBUG); - $stmt = OCP\DB::prepare($sql); - $stmt->execute(array($objid, $categoryid, $type)); - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - return true; - } - - /** - * @brief Delete categories from the db and from all the vobject supplied - * @param $names An array of categories to delete - * @param $objects An array of arrays with [id,vobject] (as text) pairs suitable for updating the apps object table. - */ - public function delete($names, array &$objects=null) { - if(!is_array($names)) { - $names = array($names); - } - - $names = array_map('trim', $names); - - OC_Log::write('core', __METHOD__ . ', before: ' - . print_r($this->categories, true), OC_Log::DEBUG); - foreach($names as $name) { - $id = null; - OC_Log::write('core', __METHOD__.', '.$name, OC_Log::DEBUG); - if($this->hasCategory($name)) { - $id = $this->array_searchi($name, $this->categories); - unset($this->categories[$id]); - } - try { - $stmt = OCP\DB::prepare('DELETE FROM `' . self::CATEGORY_TABLE . '` WHERE ' - . '`uid` = ? AND `type` = ? AND `category` = ?'); - $result = $stmt->execute(array($this->user, $this->type, $name)); - if (OC_DB::isError($result)) { - OC_Log::write('core', __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), OC_Log::ERROR); - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__ . ', exception: ' - . $e->getMessage(), OCP\Util::ERROR); - } - if(!is_null($id) && $id !== false) { - try { - $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` ' - . 'WHERE `categoryid` = ?'; - $stmt = OCP\DB::prepare($sql); - $result = $stmt->execute(array($id)); - if (OC_DB::isError($result)) { - OC_Log::write('core', - __METHOD__. 'DB error: ' . OC_DB::getErrorMessage($result), - OC_Log::ERROR); - } - } catch(Exception $e) { - OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - OCP\Util::ERROR); - return false; - } - } - } - OC_Log::write('core', __METHOD__.', after: ' - . print_r($this->categories, true), OC_Log::DEBUG); - if(!is_null($objects)) { - foreach($objects as $key=>&$value) { - $vobject = OC_VObject::parse($value[1]); - if(!is_null($vobject)) { - $object = null; - $componentname = ''; - if (isset($vobject->VEVENT)) { - $object = $vobject->VEVENT; - $componentname = 'VEVENT'; - } else - if (isset($vobject->VTODO)) { - $object = $vobject->VTODO; - $componentname = 'VTODO'; - } else - if (isset($vobject->VJOURNAL)) { - $object = $vobject->VJOURNAL; - $componentname = 'VJOURNAL'; - } else { - $object = $vobject; - } - $categories = $object->getAsArray('CATEGORIES'); - foreach($names as $name) { - $idx = $this->array_searchi($name, $categories); - if($idx !== false) { - OC_Log::write('core', __METHOD__ - .', unsetting: ' - . $categories[$this->array_searchi($name, $categories)], - OC_Log::DEBUG); - unset($categories[$this->array_searchi($name, $categories)]); - } - } - - $object->setString('CATEGORIES', implode(',', $categories)); - if($vobject !== $object) { - $vobject[$componentname] = $object; - } - $value[1] = $vobject->serialize(); - $objects[$key] = $value; - } else { - OC_Log::write('core', __METHOD__ - .', unable to parse. ID: ' . $value[0] . ', ' - . substr($value[1], 0, 50) . '(...)', OC_Log::DEBUG); - } - } - } - } - - // case-insensitive in_array - private function in_arrayi($needle, $haystack) { - if(!is_array($haystack)) { - return false; - } - return in_array(strtolower($needle), array_map('strtolower', $haystack)); - } - - // case-insensitive array_search - private function array_searchi($needle, $haystack) { - if(!is_array($haystack)) { - return false; - } - return array_search(strtolower($needle), array_map('strtolower', $haystack)); - } -} diff --git a/tests/lib/tags.php b/tests/lib/tags.php new file mode 100644 index 0000000000..06baebc0af --- /dev/null +++ b/tests/lib/tags.php @@ -0,0 +1,133 @@ +. +* +*/ + +class Test_Tags extends PHPUnit_Framework_TestCase { + + protected $objectType; + protected $user; + protected $backupGlobals = FALSE; + + public function setUp() { + + OC_User::clearBackends(); + OC_User::useBackend('dummy'); + $this->user = uniqid('user_'); + $this->objectType = uniqid('type_'); + OC_User::createUser($this->user, 'pass'); + OC_User::setUserId($this->user); + + } + + public function tearDown() { + //$query = OC_DB::prepare('DELETE FROM `*PREFIX*vcategories` WHERE `item_type` = ?'); + //$query->execute(array('test')); + } + + public function testInstantiateWithDefaults() { + $defaultTags = array('Friends', 'Family', 'Work', 'Other'); + + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType, $defaultTags); + + $this->assertEquals(4, count($tagMgr->tags())); + } + + public function testAddTags() { + $tags = array('Friends', 'Family', 'Work', 'Other'); + + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + foreach($tags as $tag) { + $result = $tagMgr->add($tag); + $this->assertTrue((bool)$result); + } + + $this->assertFalse($tagMgr->add('Family')); + $this->assertFalse($tagMgr->add('fAMILY')); + + $this->assertEquals(4, count($tagMgr->tags())); + } + + public function testdeleteTags() { + $defaultTags = array('Friends', 'Family', 'Work', 'Other'); + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType, $defaultTags); + + $this->assertEquals(4, count($tagMgr->tags())); + + $tagMgr->delete('family'); + $this->assertEquals(3, count($tagMgr->tags())); + + $tagMgr->delete(array('Friends', 'Work', 'Other')); + $this->assertEquals(0, count($tagMgr->tags())); + + } + + public function testRenameTag() { + $defaultTags = array('Friends', 'Family', 'Wrok', 'Other'); + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType, $defaultTags); + + $this->assertTrue($tagMgr->rename('Wrok', 'Work')); + $this->assertTrue($tagMgr->hasTag('Work')); + $this->assertFalse($tagMgr->hastag('Wrok')); + $this->assertFalse($tagMgr->rename('Wrok', 'Work')); + + } + + public function testTagAs() { + $objids = array(1, 2, 3, 4, 5, 6, 7, 8, 9); + + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + foreach($objids as $id) { + $tagMgr->tagAs($id, 'Family'); + } + + $this->assertEquals(1, count($tagMgr->tags())); + $this->assertEquals(9, count($tagMgr->idsForTag('Family'))); + } + + /** + * @depends testTagAs + */ + public function testUnTag() { + $objIds = array(1, 2, 3, 4, 5, 6, 7, 8, 9); + + // Is this "legal"? + $this->testTagAs(); + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + foreach($objIds as $id) { + $this->assertTrue(in_array($id, $tagMgr->idsForTag('Family'))); + $tagMgr->unTag($id, 'Family'); + $this->assertFalse(in_array($id, $tagMgr->idsForTag('Family'))); + } + + $this->assertEquals(1, count($tagMgr->tags())); + $this->assertEquals(0, count($tagMgr->idsForTag('Family'))); + } + +} diff --git a/tests/lib/vcategories.php b/tests/lib/vcategories.php deleted file mode 100644 index df5f600f20..0000000000 --- a/tests/lib/vcategories.php +++ /dev/null @@ -1,128 +0,0 @@ -. -* -*/ - -//require_once("../lib/template.php"); - -class Test_VCategories extends PHPUnit_Framework_TestCase { - - protected $objectType; - protected $user; - protected $backupGlobals = FALSE; - - public function setUp() { - - OC_User::clearBackends(); - OC_User::useBackend('dummy'); - $this->user = uniqid('user_'); - $this->objectType = uniqid('type_'); - OC_User::createUser($this->user, 'pass'); - OC_User::setUserId($this->user); - - } - - public function tearDown() { - //$query = OC_DB::prepare('DELETE FROM `*PREFIX*vcategories` WHERE `item_type` = ?'); - //$query->execute(array('test')); - } - - public function testInstantiateWithDefaults() { - $defcategories = array('Friends', 'Family', 'Work', 'Other'); - - $catmgr = new OC_VCategories($this->objectType, $this->user, $defcategories); - - $this->assertEquals(4, count($catmgr->categories())); - } - - public function testAddCategories() { - $categories = array('Friends', 'Family', 'Work', 'Other'); - - $catmgr = new OC_VCategories($this->objectType, $this->user); - - foreach($categories as $category) { - $result = $catmgr->add($category); - $this->assertTrue((bool)$result); - } - - $this->assertFalse($catmgr->add('Family')); - $this->assertFalse($catmgr->add('fAMILY')); - - $this->assertEquals(4, count($catmgr->categories())); - } - - public function testdeleteCategories() { - $defcategories = array('Friends', 'Family', 'Work', 'Other'); - $catmgr = new OC_VCategories($this->objectType, $this->user, $defcategories); - $this->assertEquals(4, count($catmgr->categories())); - - $catmgr->delete('family'); - $this->assertEquals(3, count($catmgr->categories())); - - $catmgr->delete(array('Friends', 'Work', 'Other')); - $this->assertEquals(0, count($catmgr->categories())); - - } - - public function testrenameCategory() { - $defcategories = array('Friends', 'Family', 'Wrok', 'Other'); - $catmgr = new OC_VCategories($this->objectType, $this->user, $defcategories); - - $this->assertTrue($catmgr->rename('Wrok', 'Work')); - $this->assertTrue($catmgr->hasCategory('Work')); - $this->assertFalse($catmgr->hasCategory('Wrok')); - $this->assertFalse($catmgr->rename('Wrok', 'Work')); - - } - - public function testAddToCategory() { - $objids = array(1, 2, 3, 4, 5, 6, 7, 8, 9); - - $catmgr = new OC_VCategories($this->objectType, $this->user); - - foreach($objids as $id) { - $catmgr->addToCategory($id, 'Family'); - } - - $this->assertEquals(1, count($catmgr->categories())); - $this->assertEquals(9, count($catmgr->idsForCategory('Family'))); - } - - /** - * @depends testAddToCategory - */ - public function testRemoveFromCategory() { - $objids = array(1, 2, 3, 4, 5, 6, 7, 8, 9); - - // Is this "legal"? - $this->testAddToCategory(); - $catmgr = new OC_VCategories($this->objectType, $this->user); - - foreach($objids as $id) { - $this->assertTrue(in_array($id, $catmgr->idsForCategory('Family'))); - $catmgr->removeFromCategory($id, 'Family'); - $this->assertFalse(in_array($id, $catmgr->idsForCategory('Family'))); - } - - $this->assertEquals(1, count($catmgr->categories())); - $this->assertEquals(0, count($catmgr->idsForCategory('Family'))); - } - -} From 5ae4d675406b310df0940c8b9a43dcf556010e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 18 Sep 2013 11:06:00 +0200 Subject: [PATCH 057/180] in case the cache cannot be initialized within the autoloader we just shoul not use it --- lib/autoloader.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/autoloader.php b/lib/autoloader.php index 01841f831b..2c19350e90 100644 --- a/lib/autoloader.php +++ b/lib/autoloader.php @@ -117,7 +117,11 @@ class Autoloader { // Does this PHP have an in-memory cache? We cache the paths there if ($this->constructingMemoryCache && !$this->memoryCache) { $this->constructingMemoryCache = false; - $this->memoryCache = \OC\Memcache\Factory::createLowLatency('Autoloader'); + try { + $this->memoryCache = \OC\Memcache\Factory::createLowLatency('Autoloader'); + } catch(\Exception $ex) { + // no caching then - fine with me + } } if ($this->memoryCache) { $pathsToRequire = $this->memoryCache->get($class); From ce58c32c901fb874264922dabec1692f9d05123d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 18 Sep 2013 11:07:19 +0200 Subject: [PATCH 058/180] using OC_Config::$object->setValue in order to get the underlying exception thrown up to the caller --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index 41f5f1d16b..b9ff07a03f 100755 --- a/lib/util.php +++ b/lib/util.php @@ -552,7 +552,7 @@ class OC_Util { if(is_null($id)) { // We need to guarantee at least one letter in instanceid so it can be used as the session_name $id = 'oc' . self::generateRandomBytes(10); - OC_Config::setValue('instanceid', $id); + OC_Config::$object->setValue('instanceid', $id); } return $id; } From d3f88ceeb49b9b86d32124163b0cea82567a4911 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 12:01:01 +0200 Subject: [PATCH 059/180] Add some docs to the sessions interface. --- lib/public/isession.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/public/isession.php b/lib/public/isession.php index 5f9ce32f3b..0a77b0c823 100644 --- a/lib/public/isession.php +++ b/lib/public/isession.php @@ -10,34 +10,46 @@ namespace OCP; +/** + * Interface ISession + * + * wrap PHP's internal session handling into the ISession interface + */ interface ISession { + /** + * Set a value in the session + * * @param string $key * @param mixed $value */ public function set($key, $value); /** + * Get a value from the session + * * @param string $key * @return mixed should return null if $key does not exist */ public function get($key); /** + * Check if a named key exists in the session + * * @param string $key * @return bool */ public function exists($key); /** - * should not throw any errors if $key does not exist + * Remove a $key/$value pair from the session * * @param string $key */ public function remove($key); /** - * removes all entries within the cache namespace + * Reset and recreate the session */ public function clear(); From 6ba23912a7c969ce24a3b295c55a60ea640ca690 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 12:34:10 +0200 Subject: [PATCH 060/180] Add getUserFolder/getAppFolder to Server. --- lib/public/iservercontainer.php | 14 +++++++++++++ lib/server.php | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index ec7212b306..89e71db8d1 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -62,6 +62,20 @@ interface IServerContainer { */ function getRootFolder(); + /** + * Returns a view to ownCloud's files folder + * + * @return \OCP\Files\Folder + */ + function getUserFolder(); + + /** + * Returns an app-specific view in ownClouds data directory + * + * @return \OCP\Files\Folder + */ + function getAppFolder(); + /** * Returns the current session * diff --git a/lib/server.php b/lib/server.php index 0eee3e0f73..9525fce9fd 100644 --- a/lib/server.php +++ b/lib/server.php @@ -56,6 +56,17 @@ class Server extends SimpleContainer implements IServerContainer { $view = new View(); return new Root($manager, $view, $user); }); + $this->registerService('CustomFolder', function($c) { + $dir = $c['CustomFolderPath']; + $root = $this->getRootFolder(); + $folder = null; + if(!$root->nodeExists($dir)) { + $folder = $root->newFolder($dir); + } else { + $folder = $root->get($dir); + } + return $folder; + }); } /** @@ -97,6 +108,30 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('RootFolder'); } + /** + * Returns a view to ownCloud's files folder + * + * @return \OCP\Files\Folder + */ + function getUserFolder() { + + $this->registerParameter('CustomFolderPath', '/files'); + return $this->query('CustomFolder'); + + } + + /** + * Returns an app-specific view in ownClouds data directory + * + * @return \OCP\Files\Folder + */ + function getAppFolder() { + + $this->registerParameter('CustomFolderPath', '/' . \OC_App::getCurrentApp()); + return $this->query('CustomFolder'); + + } + /** * Returns the current session * From 442a2e074cea694ce0d361b5433eb5473be438e6 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 12:35:46 +0200 Subject: [PATCH 061/180] Update to adhere to the coding guidelines. --- lib/server.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/server.php b/lib/server.php index 9525fce9fd..6b1cb9c38d 100644 --- a/lib/server.php +++ b/lib/server.php @@ -17,10 +17,10 @@ use OCP\IServerContainer; class Server extends SimpleContainer implements IServerContainer { function __construct() { - $this->registerService('ContactsManager', function($c){ + $this->registerService('ContactsManager', function($c) { return new ContactsManager(); }); - $this->registerService('Request', function($c){ + $this->registerService('Request', function($c) { $params = array(); // we json decode the body only in case of content type json @@ -45,10 +45,10 @@ class Server extends SimpleContainer implements IServerContainer { ) ); }); - $this->registerService('PreviewManager', function($c){ + $this->registerService('PreviewManager', function($c) { return new PreviewManager(); }); - $this->registerService('RootFolder', function($c){ + $this->registerService('RootFolder', function($c) { // TODO: get user and user manager from container as well $user = \OC_User::getUser(); $user = \OC_User::getManager()->get($user); @@ -83,8 +83,7 @@ class Server extends SimpleContainer implements IServerContainer { * * @return \OCP\IRequest|null */ - function getRequest() - { + function getRequest() { return $this->query('Request'); } @@ -93,8 +92,7 @@ class Server extends SimpleContainer implements IServerContainer { * * @return \OCP\IPreview */ - function getPreviewManager() - { + function getPreviewManager() { return $this->query('PreviewManager'); } @@ -103,8 +101,7 @@ class Server extends SimpleContainer implements IServerContainer { * * @return \OCP\Files\Folder */ - function getRootFolder() - { + function getRootFolder() { return $this->query('RootFolder'); } From 534933ee9bf6837fc75a389e4ed3aad4ffe1ab0f Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 13:15:38 +0200 Subject: [PATCH 062/180] Use new emitter system --- lib/base.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index 520be11bc5..ce307b2bdd 100644 --- a/lib/base.php +++ b/lib/base.php @@ -568,7 +568,9 @@ class OC { } catch (Exception $e) { } - OC_Hook::connect('OC_User', 'post_login', 'OC\Cache\File', 'loginListener'); + // NOTE: This will be replaced to use OCP + $userSession = \OC_User::getUserSession(); + $userSession->listen('postLogin', array('OC\Cache\File', 'loginListener')) } } From 2ef0b58ff6434254510c8be9c940126883022d76 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 14:25:12 +0200 Subject: [PATCH 063/180] Don't try to be clever --- lib/server.php | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/server.php b/lib/server.php index 6b1cb9c38d..3454622425 100644 --- a/lib/server.php +++ b/lib/server.php @@ -56,17 +56,6 @@ class Server extends SimpleContainer implements IServerContainer { $view = new View(); return new Root($manager, $view, $user); }); - $this->registerService('CustomFolder', function($c) { - $dir = $c['CustomFolderPath']; - $root = $this->getRootFolder(); - $folder = null; - if(!$root->nodeExists($dir)) { - $folder = $root->newFolder($dir); - } else { - $folder = $root->get($dir); - } - return $folder; - }); } /** @@ -112,8 +101,15 @@ class Server extends SimpleContainer implements IServerContainer { */ function getUserFolder() { - $this->registerParameter('CustomFolderPath', '/files'); - return $this->query('CustomFolder'); + $dir = '/files'; + $root = $this->getRootFolder(); + $folder = null; + if(!$root->nodeExists($dir)) { + $folder = $root->newFolder($dir); + } else { + $folder = $root->get($dir); + } + return $folder; } @@ -124,8 +120,15 @@ class Server extends SimpleContainer implements IServerContainer { */ function getAppFolder() { - $this->registerParameter('CustomFolderPath', '/' . \OC_App::getCurrentApp()); - return $this->query('CustomFolder'); + $dir = '/' . \OC_App::getCurrentApp(); + $root = $this->getRootFolder(); + $folder = null; + if(!$root->nodeExists($dir)) { + $folder = $root->newFolder($dir); + } else { + $folder = $root->get($dir); + } + return $folder; } From 79cd655920ae3346725539df2f443a66e51c5726 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 14:50:21 +0200 Subject: [PATCH 064/180] Note to self: Test before pushing!!! --- lib/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index ce307b2bdd..fd31cfd9f2 100644 --- a/lib/base.php +++ b/lib/base.php @@ -570,7 +570,7 @@ class OC { } // NOTE: This will be replaced to use OCP $userSession = \OC_User::getUserSession(); - $userSession->listen('postLogin', array('OC\Cache\File', 'loginListener')) + $userSession->listen('postLogin', array('OC\Cache\File', 'loginListener')); } } From 09d043729a41a0e8966ed3bb81567ed1009a37b6 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 15:02:25 +0200 Subject: [PATCH 065/180] Note to self 2: Do as you preach. Test! --- lib/base.php | 2 +- lib/cache.php | 6 +++--- lib/cache/fileglobal.php | 2 +- lib/legacy/cache.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/base.php b/lib/base.php index fd31cfd9f2..0650361be9 100644 --- a/lib/base.php +++ b/lib/base.php @@ -570,7 +570,7 @@ class OC { } // NOTE: This will be replaced to use OCP $userSession = \OC_User::getUserSession(); - $userSession->listen('postLogin', array('OC\Cache\File', 'loginListener')); + $userSession->listen('postLogin', '\OC\Cache\File', 'loginListener'); } } diff --git a/lib/cache.php b/lib/cache.php index c99663a0ca..a4fa8be710 100644 --- a/lib/cache.php +++ b/lib/cache.php @@ -6,7 +6,7 @@ * See the COPYING-README file. */ -namespace OC\Cache; +namespace OC; class Cache { /** @@ -24,7 +24,7 @@ class Cache { */ static public function getGlobalCache() { if (!self::$global_cache) { - self::$global_cache = new FileGlobal(); + self::$global_cache = new Cache\FileGlobal(); } return self::$global_cache; } @@ -35,7 +35,7 @@ class Cache { */ static public function getUserCache() { if (!self::$user_cache) { - self::$user_cache = new File(); + self::$user_cache = new Cache\File(); } return self::$user_cache; } diff --git a/lib/cache/fileglobal.php b/lib/cache/fileglobal.php index 9ca1740293..bd049bba4d 100644 --- a/lib/cache/fileglobal.php +++ b/lib/cache/fileglobal.php @@ -10,7 +10,7 @@ namespace OC\Cache; class FileGlobal { static protected function getCacheDir() { - $cache_dir = get_temp_dir().'/owncloud-'.OC_Util::getInstanceId().'/'; + $cache_dir = get_temp_dir().'/owncloud-' . \OC_Util::getInstanceId().'/'; if (!is_dir($cache_dir)) { mkdir($cache_dir); } diff --git a/lib/legacy/cache.php b/lib/legacy/cache.php index 83b214170f..f915eb516b 100644 --- a/lib/legacy/cache.php +++ b/lib/legacy/cache.php @@ -6,5 +6,5 @@ * See the COPYING-README file. */ -class Cache extends OC\Cache { +class OC_Cache extends \OC\Cache { } \ No newline at end of file From 5fb0e257a4e7b16024389261cfe924f53deb69ae Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 18 Sep 2013 16:03:53 +0200 Subject: [PATCH 066/180] let user repeat the recovery key password to prevent typos --- apps/files_encryption/js/settings-admin.js | 36 ++++++++++--------- .../templates/settings-admin.php | 28 +++++++++------ 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/apps/files_encryption/js/settings-admin.js b/apps/files_encryption/js/settings-admin.js index 6647c621e7..82fdb51708 100644 --- a/apps/files_encryption/js/settings-admin.js +++ b/apps/files_encryption/js/settings-admin.js @@ -1,6 +1,6 @@ /** - * Copyright (c) 2013, Sam Tuke , Robin Appelman - * + * Copyright (c) 2013, Sam Tuke , Robin Appelman + * , Bjoern Schiessle * This file is licensed under the Affero General Public License version 3 or later. * See the COPYING-README file. */ @@ -31,22 +31,23 @@ $(document).ready(function(){ // Trigger ajax on recoveryAdmin status change var enabledStatus = $('#adminEnableRecovery').val(); - $('input:password[name="recoveryPassword"]').keyup(function(event) { - var recoveryPassword = $( '#recoveryPassword' ).val(); + $('input:password[name="encryptionRecoveryPassword"]').keyup(function(event) { + var recoveryPassword = $( '#encryptionRecoveryPassword' ).val(); + var recoveryPasswordRepeated = $( '#repeatEncryptionRecoveryPassword' ).val(); var checkedButton = $('input:radio[name="adminEnableRecovery"]:checked').val(); var uncheckedValue = (1+parseInt(checkedButton)) % 2; - if (recoveryPassword != '' ) { + if (recoveryPassword !== '' && recoveryPassword === recoveryPasswordRepeated) { $('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').removeAttr("disabled"); } else { $('input:radio[name="adminEnableRecovery"][value="'+uncheckedValue.toString()+'"]').attr("disabled", "true"); } }); - $( 'input:radio[name="adminEnableRecovery"]' ).change( + $( 'input:radio[name="adminEnableRecovery"]' ).change( function() { var recoveryStatus = $( this ).val(); var oldStatus = (1+parseInt(recoveryStatus)) % 2; - var recoveryPassword = $( '#recoveryPassword' ).val(); + var recoveryPassword = $( '#encryptionRecoveryPassword' ).val(); $.post( OC.filePath( 'files_encryption', 'ajax', 'adminrecovery.php' ) , { adminEnableRecovery: recoveryStatus, recoveryPassword: recoveryPassword } @@ -57,11 +58,10 @@ $(document).ready(function(){ } else { OC.Notification.hide(); if (recoveryStatus === "0") { - $('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true"); - $('input:password[name="changeRecoveryPassword"]').attr("disabled", "true"); - $('input:password[name="changeRecoveryPassword"]').val(""); + $('p[name="changeRecoveryPasswordBlock"]').attr("class", "hidden"); } else { - $('input:password[name="changeRecoveryPassword"]').removeAttr("disabled"); + $('input:password[name="changeRecoveryPassword"]').val(""); + $('p[name="changeRecoveryPasswordBlock"]').removeAttr("class"); } } } @@ -72,9 +72,11 @@ $(document).ready(function(){ // change recovery password $('input:password[name="changeRecoveryPassword"]').keyup(function(event) { - var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val(); - var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val(); - if (newRecoveryPassword != '' && oldRecoveryPassword != '' ) { + var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val(); + var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val(); + var newRecoveryPasswordRepeated = $('#repeatedNewEncryptionRecoveryPassword').val(); + console.log("new: " + newRecoveryPassword + " - repeated: " + newRecoveryPasswordRepeated); + if (newRecoveryPassword !== '' && oldRecoveryPassword !== '' && newRecoveryPassword === newRecoveryPasswordRepeated) { $('button:button[name="submitChangeRecoveryKey"]').removeAttr("disabled"); } else { $('button:button[name="submitChangeRecoveryKey"]').attr("disabled", "true"); @@ -83,8 +85,8 @@ $(document).ready(function(){ $('button:button[name="submitChangeRecoveryKey"]').click(function() { - var oldRecoveryPassword = $('input:password[id="oldRecoveryPassword"]').val(); - var newRecoveryPassword = $('input:password[id="newRecoveryPassword"]').val(); + var oldRecoveryPassword = $('#oldEncryptionRecoveryPassword').val(); + var newRecoveryPassword = $('#newEncryptionRecoveryPassword').val(); OC.msg.startSaving('#encryption .msg'); $.post( OC.filePath( 'files_encryption', 'ajax', 'changeRecoveryPassword.php' ) @@ -98,5 +100,5 @@ $(document).ready(function(){ } ); }); - + }); diff --git a/apps/files_encryption/templates/settings-admin.php b/apps/files_encryption/templates/settings-admin.php index f5f7582c2a..3a6adc09f4 100644 --- a/apps/files_encryption/templates/settings-admin.php +++ b/apps/files_encryption/templates/settings-admin.php @@ -10,14 +10,17 @@ t("Enable recovery key (allow to recover users files in case of password loss):")); ?>

    - +
    + + +
    /> + /> t("Enabled")); ?>
    @@ -25,27 +28,32 @@ type='radio' name='adminEnableRecovery' value='0' - /> + /> t("Disabled")); ?>



    -

    +

    > t("Change recovery key password:")); ?>

    /> - + id="oldEncryptionRecoveryPassword" + +

    /> - + id="newEncryptionRecoveryPassword" + +
    + t("Repeat New Recovery key password")); ?>