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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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/150] 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 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 021/150] 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 022/150] 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 023/150] 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 823b4cce603d1d0a404d8b93fcca6101ff839767 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 4 Sep 2013 08:16:27 +0200 Subject: [PATCH 024/150] 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 025/150] 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 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 026/150] 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 027/150] 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 028/150] /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 3c026b7cf601c0b83dd02436f17714fcf48cb9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 16 Sep 2013 12:09:15 +0200 Subject: [PATCH 029/150] recreate an etag within the scanner if the cache contains an empty etag --- lib/files/cache/scanner.php | 8 +++++++- tests/lib/files/cache/scanner.php | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 9d180820e9..78cab6ed2d 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -97,13 +97,19 @@ class Scanner extends BasicEmitter { } $newData = $data; if ($reuseExisting and $cacheData = $this->cache->get($file)) { + // prevent empty etag + $etag = $cacheData['etag']; + if (empty($etag)) { + $etag = $data['etag']; + } + // only reuse data if the file hasn't explicitly changed if (isset($data['mtime']) && isset($cacheData['mtime']) && $data['mtime'] === $cacheData['mtime']) { if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { $data['size'] = $cacheData['size']; } if ($reuseExisting & self::REUSE_ETAG) { - $data['etag'] = $cacheData['etag']; + $data['etag'] = $etag; } } // Only update metadata that has changed diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index f6deb93a49..fa1b340604 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -184,6 +184,23 @@ class Scanner extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->cache->inCache('folder/bar.txt')); } + public function testETagRecreation() { + $this->fillTestFolders(); + + $this->scanner->scan(''); + + // manipulate etag to simulate an empty etag + $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); + $data['etag'] = ''; + $this->cache->put('', $data); + + // rescan + $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); + $newData = $this->cache->get(''); + $this->assertNotEmpty($newData['etag']); + + } + function setUp() { $this->storage = new \OC\Files\Storage\Temporary(array()); $this->scanner = new \OC\Files\Cache\Scanner($this->storage); From 07714d9a72bbc4d9bdc25a8c42d83b5a70fb5be3 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Sat, 14 Sep 2013 17:56:55 +0200 Subject: [PATCH 030/150] Tests whether expired/valid link share is still accessible. --- tests/lib/share/share.php | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index e02b0e4354..8e9eef65d3 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -535,4 +535,52 @@ class Test_Share extends PHPUnit_Framework_TestCase { 'Failed asserting that user 3 still has access to test.txt after expiration date has been set.' ); } + + protected function getShareByValidToken($token) { + $row = OCP\Share::getShareByToken($token); + $this->assertInternalType( + 'array', + $row, + "Failed asserting that a share for token $token exists." + ); + return $row; + } + + public function testShareItemWithLink() { + OC_User::setUserId($this->user1); + $token = OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_LINK, null, OCP\PERMISSION_READ); + $this->assertInternalType( + 'string', + $token, + 'Failed asserting that user 1 successfully shared text.txt as link with token.' + ); + + // testGetShareByTokenNoExpiration + $row = $this->getShareByValidToken($token); + $this->assertEmpty( + $row['expiration'], + 'Failed asserting that the returned row does not have an expiration date.' + ); + + // testGetShareByTokenExpirationValid + $this->assertTrue( + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture), + 'Failed asserting that user 1 successfully set a future expiration date for the test.txt share.' + ); + $row = $this->getShareByValidToken($token); + $this->assertNotEmpty( + $row['expiration'], + 'Failed asserting that the returned row has an expiration date.' + ); + + // testGetShareByTokenExpirationExpired + $this->assertTrue( + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast), + 'Failed asserting that user 1 successfully set a past expiration date for the test.txt share.' + ); + $this->assertFalse( + OCP\Share::getShareByToken($token), + 'Failed asserting that an expired share could not be found.' + ); + } } From a92d4c2c0932f5c662ed846763e3059ebdcde07c Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Sat, 14 Sep 2013 18:44:28 +0200 Subject: [PATCH 031/150] Perform expiration date checking before returning share data for token. --- lib/public/share.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/public/share.php b/lib/public/share.php index 9ab956d84b..cc3c4de620 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -293,7 +293,18 @@ class Share { if (\OC_DB::isError($result)) { \OC_Log::write('OCP\Share', \OC_DB::getErrorMessage($result) . ', token=' . $token, \OC_Log::ERROR); } - return $result->fetchRow(); + $row = $result->fetchRow(); + + if (!empty($row['expiration'])) { + $now = new \DateTime(); + $expirationDate = new \DateTime($row['expiration'], new \DateTimeZone('UTC')); + if ($now > $expirationDate) { + self::delete($row['id']); + return false; + } + } + + return $row; } /** From c8f9efeb94b136ed0906cefe946629a091796ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 16 Sep 2013 23:32:17 +0200 Subject: [PATCH 032/150] etag changes are now propagated up the file tree --- lib/files/cache/scanner.php | 14 ++++++++++++++ tests/lib/files/cache/scanner.php | 21 +++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 78cab6ed2d..fdbce0d51f 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -99,8 +99,10 @@ class Scanner extends BasicEmitter { if ($reuseExisting and $cacheData = $this->cache->get($file)) { // prevent empty etag $etag = $cacheData['etag']; + $propagateETagChange = false; if (empty($etag)) { $etag = $data['etag']; + $propagateETagChange = true; } // only reuse data if the file hasn't explicitly changed @@ -110,6 +112,18 @@ class Scanner extends BasicEmitter { } if ($reuseExisting & self::REUSE_ETAG) { $data['etag'] = $etag; + if ($propagateETagChange) { + $parent = $file; + while ($parent !== '') { + $parent = dirname($parent); + if ($parent === '.') { + $parent = ''; + } + $parentCacheData = $this->cache->get($parent); + $parentCacheData['etag'] = $this->storage->getETag($parent); + $this->cache->put($parent, $parentCacheData); + } + } } } // Only update metadata that has changed diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index fa1b340604..b137799bbc 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -187,17 +187,26 @@ class Scanner extends \PHPUnit_Framework_TestCase { public function testETagRecreation() { $this->fillTestFolders(); - $this->scanner->scan(''); + $this->scanner->scan('folder/bar.txt'); // manipulate etag to simulate an empty etag $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); - $data['etag'] = ''; - $this->cache->put('', $data); + $data0 = $this->cache->get('folder/bar.txt'); + $data1 = $this->cache->get('folder'); + $data2 = $this->cache->get(''); + $data0['etag'] = ''; + $this->cache->put('folder/bar.txt', $data0); // rescan - $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); - $newData = $this->cache->get(''); - $this->assertNotEmpty($newData['etag']); + $this->scanner->scan('folder/bar.txt', \OC\Files\Cache\Scanner::SCAN_SHALLOW, \OC\Files\Cache\Scanner::REUSE_ETAG); + + // verify cache content + $newData0 = $this->cache->get('folder/bar.txt'); + $newData1 = $this->cache->get('folder'); + $newData2 = $this->cache->get(''); + $this->assertNotEmpty($newData0['etag']); + $this->assertNotEquals($data1['etag'], $newData1['etag']); + $this->assertNotEquals($data2['etag'], $newData2['etag']); } 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 033/150] 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 034/150] 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 035/150] 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 036/150] 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 037/150] 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 038/150] 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:11:43 +0200 Subject: [PATCH 039/150] check only permission from link-share to decide if public upload is enabled or disabled --- core/js/share.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/js/share.js b/core/js/share.js index 5d34faf8a5..250f410072 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -177,7 +177,9 @@ OC.Share={ if (allowPublicUploadStatus) { return true; } - allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false; + if (value.share_type === OC.Share.SHARE_TYPE_LINK) { + allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false; + } }); html += ''; From 642b064c5b98990f6ac0e3ba344db8cd1fe4d1f8 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 17 Sep 2013 18:18:23 +0200 Subject: [PATCH 040/150] we can leave the loop if the permission of the link share was checked --- core/js/share.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/js/share.js b/core/js/share.js index 250f410072..641252a4d7 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -174,11 +174,9 @@ OC.Share={ var allowPublicUploadStatus = false; $.each(data.shares, function(key, value) { - if (allowPublicUploadStatus) { - return true; - } if (value.share_type === OC.Share.SHARE_TYPE_LINK) { allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false; + return true; } }); From 5c19b995db6dab9aae579274db82413117cce67b Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 17 Sep 2013 18:31:14 +0200 Subject: [PATCH 041/150] 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 042/150] 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 043/150] 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 044/150] 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 d3f88ceeb49b9b86d32124163b0cea82567a4911 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 12:01:01 +0200 Subject: [PATCH 045/150] 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 046/150] 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 047/150] 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 048/150] 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 049/150] 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 050/150] 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 051/150] 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 1274d6116dc59a8238ad9d02d467260387fe2eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 18 Sep 2013 22:22:51 +0200 Subject: [PATCH 052/150] updating php docs --- lib/cache.php | 15 ++++++++++----- lib/cache/broker.php | 8 ++++++++ lib/cache/usercache.php | 4 ++-- lib/public/icache.php | 2 +- tests/lib/cache.php | 2 +- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/cache.php b/lib/cache.php index a4fa8be710..a311f10a00 100644 --- a/lib/cache.php +++ b/lib/cache.php @@ -10,17 +10,17 @@ namespace OC; class Cache { /** - * @var OC_Cache $user_cache + * @var Cache $user_cache */ static protected $user_cache; /** - * @var OC_Cache $global_cache + * @var Cache $global_cache */ static protected $global_cache; /** * get the global cache - * @return OC_Cache + * @return Cache */ static public function getGlobalCache() { if (!self::$global_cache) { @@ -31,7 +31,7 @@ class Cache { /** * get the user cache - * @return OC_Cache + * @return Cache */ static public function getUserCache() { if (!self::$user_cache) { @@ -87,7 +87,7 @@ class Cache { /** * clear the user cache of all entries starting with a prefix - * @param string prefix (optional) + * @param string $prefix (optional) * @return bool */ static public function clear($prefix='') { @@ -95,6 +95,11 @@ class Cache { return $user_cache->clear($prefix); } + /** + * creates cache key based on the files given + * @param $files + * @return string + */ static public function generateCacheKeyFromFiles($files) { $key = ''; sort($files); diff --git a/lib/cache/broker.php b/lib/cache/broker.php index b7f1b67a6d..9b7e837e1b 100644 --- a/lib/cache/broker.php +++ b/lib/cache/broker.php @@ -9,7 +9,15 @@ namespace OC\Cache; class Broker { + + /** + * @var \OC\Cache + */ protected $fast_cache; + + /** + * @var \OC\Cache + */ protected $slow_cache; public function __construct($fast_cache, $slow_cache) { diff --git a/lib/cache/usercache.php b/lib/cache/usercache.php index aac3b39af3..baa8820700 100644 --- a/lib/cache/usercache.php +++ b/lib/cache/usercache.php @@ -13,7 +13,7 @@ namespace OC\Cache; class UserCache implements \OCP\ICache { /** - * @var OC\Cache\File $userCache + * @var \OC\Cache\File $userCache */ protected $userCache; @@ -68,7 +68,7 @@ class UserCache implements \OCP\ICache { /** * clear the user cache of all entries starting with a prefix - * @param string prefix (optional) + * @param string $prefix (optional) * @return bool */ public function clear($prefix = '') { diff --git a/lib/public/icache.php b/lib/public/icache.php index 202459f7c2..436ee71b2b 100644 --- a/lib/public/icache.php +++ b/lib/public/icache.php @@ -48,7 +48,7 @@ interface ICache { /** * clear the user cache of all entries starting with a prefix - * @param string prefix (optional) + * @param string $prefix (optional) * @return bool */ public function clear($prefix = ''); diff --git a/tests/lib/cache.php b/tests/lib/cache.php index 3dcf39f7d6..8fefa25f65 100644 --- a/tests/lib/cache.php +++ b/tests/lib/cache.php @@ -8,7 +8,7 @@ abstract class Test_Cache extends PHPUnit_Framework_TestCase { /** - * @var OC_Cache cache; + * @var \OC\Cache cache; */ protected $instance; From 76f8be3b7aadbb2c78f6c341ef8919973275efc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 18 Sep 2013 22:49:09 +0200 Subject: [PATCH 053/150] fixing namespaces and rename hasCategory to hasTag --- lib/tags.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/tags.php b/lib/tags.php index 4aafff8e1b..3320d9ea1a 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -113,7 +113,7 @@ class Tags implements \OCP\ITags { $sql = 'SELECT COUNT(*) FROM `' . self::TAG_TABLE . '` ' . 'WHERE `uid` = ? AND `type` = ?'; try { - $stmt = OCP\DB::prepare($sql); + $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); @@ -191,7 +191,7 @@ class Tags implements \OCP\ITags { $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); + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; } } catch(\Exception $e) { @@ -381,7 +381,7 @@ class Tags implements \OCP\ITags { . '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); + \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(), @@ -409,12 +409,12 @@ class Tags implements \OCP\ITags { $stmt = \OCP\DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` ' . 'WHERE `uid` = ?'); $result = $stmt->execute(array($arguments['uid'])); - if (OCP\DB::isError($result)) { + 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); + \OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), \OCP\Util::ERROR); } } @@ -435,7 +435,7 @@ class Tags implements \OCP\ITags { $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) '; $query .= 'AND `type`= ?'; $updates[] = $this->type; - $stmt = OCP\DB::prepare($query); + $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); @@ -471,7 +471,7 @@ class Tags implements \OCP\ITags { * @return boolean */ public function addToFavorites($objid) { - if(!$this->hasCategory(self::TAG_FAVORITE)) { + if(!$this->hasTag(self::TAG_FAVORITE)) { $this->add(self::TAG_FAVORITE, true); } return $this->tagAs($objid, self::TAG_FAVORITE, $this->type); @@ -574,7 +574,7 @@ class Tags implements \OCP\ITags { . '`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); + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__ . ', exception: ' From 314ca843e81c2e26e83d58391e313503e0f30ebd Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 19 Sep 2013 11:27:13 +0200 Subject: [PATCH 054/150] Updated method names and added a few more tests. --- lib/public/itags.php | 6 ++--- lib/tags.php | 12 ++++----- tests/lib/tags.php | 59 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/lib/public/itags.php b/lib/public/itags.php index 047d4f5f40..1264340054 100644 --- a/lib/public/itags.php +++ b/lib/public/itags.php @@ -65,7 +65,7 @@ interface ITags { * * @returns array */ - public function tags(); + public function getTags(); /** * Get the a list if items tagged with $tag. @@ -75,7 +75,7 @@ interface ITags { * @param string|integer $tag Tag id or name. * @return array An array of object ids or false on error. */ - public function idsForTag($tag); + public function getIdsForTag($tag); /** * Checks whether a tag is already saved. @@ -111,7 +111,7 @@ interface ITags { * @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); + public function addMultiple($names, $sync=false, $id = null); /** * Delete tag/object relations from the db diff --git a/lib/tags.php b/lib/tags.php index 3320d9ea1a..2eaa603c1a 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -96,7 +96,7 @@ class Tags implements \OCP\ITags { } if(count($defaultTags) > 0 && count($this->tags) === 0) { - $this->addMulti($defaultTags, true); + $this->addMultiple($defaultTags, true); } \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true), \OCP\Util::DEBUG); @@ -119,7 +119,7 @@ class Tags implements \OCP\ITags { \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; } - return ($result->numRows() === 0); + return ((int)$result->numRows() === 0); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); @@ -138,7 +138,7 @@ class Tags implements \OCP\ITags { * * @return array */ - public function tags() { + public function getTags() { if(!count($this->tags)) { return array(); } @@ -167,7 +167,7 @@ class Tags implements \OCP\ITags { * @param string|integer $tag Tag id or name. * @return array An array of object ids or false on error. */ - public function idsForTag($tag) { + public function getIdsForTag($tag) { $result = null; if(is_numeric($tag)) { $tagId = $tag; @@ -293,7 +293,7 @@ class Tags implements \OCP\ITags { * @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) { + public function addMultiple($names, $sync=false, $id = null) { if(!is_array($names)) { $names = array($names); } @@ -456,7 +456,7 @@ class Tags implements \OCP\ITags { */ public function getFavorites() { try { - return $this->idsForTag(self::TAG_FAVORITE); + return $this->getIdsForTag(self::TAG_FAVORITE); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), \OCP\Util::ERROR); diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 06baebc0af..16a03f5645 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -48,7 +48,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr = new OC\Tags($this->user); $tagMgr->loadTagsFor($this->objectType, $defaultTags); - $this->assertEquals(4, count($tagMgr->tags())); + $this->assertEquals(4, count($tagMgr->getTags())); } public function testAddTags() { @@ -65,7 +65,37 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertFalse($tagMgr->add('Family')); $this->assertFalse($tagMgr->add('fAMILY')); - $this->assertEquals(4, count($tagMgr->tags())); + $this->assertEquals(4, count($tagMgr->getTags())); + } + + public function testAddMultiple() { + $tags = array('Friends', 'Family', 'Work', 'Other'); + + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + foreach($tags as $tag) { + $this->assertFalse($tagMgr->hasTag($tag)); + } + + $result = $tagMgr->addMultiple($tags); + $this->assertTrue((bool)$result); + + foreach($tags as $tag) { + $this->assertTrue($tagMgr->hasTag($tag)); + } + + $this->assertEquals(4, count($tagMgr->getTags())); + } + + public function testIsEmpty() { + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + $this->assertEquals(0, count($tagMgr->getTags())); + $this->assertTrue($tagMgr->isEmpty()); + $tagMgr->add('Tag'); + $this->assertFalse($tagMgr->isEmpty()); } public function testdeleteTags() { @@ -73,13 +103,13 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr = new OC\Tags($this->user); $tagMgr->loadTagsFor($this->objectType, $defaultTags); - $this->assertEquals(4, count($tagMgr->tags())); + $this->assertEquals(4, count($tagMgr->getTags())); $tagMgr->delete('family'); - $this->assertEquals(3, count($tagMgr->tags())); + $this->assertEquals(3, count($tagMgr->getTags())); $tagMgr->delete(array('Friends', 'Work', 'Other')); - $this->assertEquals(0, count($tagMgr->tags())); + $this->assertEquals(0, count($tagMgr->getTags())); } @@ -105,8 +135,8 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr->tagAs($id, 'Family'); } - $this->assertEquals(1, count($tagMgr->tags())); - $this->assertEquals(9, count($tagMgr->idsForTag('Family'))); + $this->assertEquals(1, count($tagMgr->getTags())); + $this->assertEquals(9, count($tagMgr->getIdsForTag('Family'))); } /** @@ -121,13 +151,20 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr->loadTagsFor($this->objectType); foreach($objIds as $id) { - $this->assertTrue(in_array($id, $tagMgr->idsForTag('Family'))); + $this->assertTrue(in_array($id, $tagMgr->getIdsForTag('Family'))); $tagMgr->unTag($id, 'Family'); - $this->assertFalse(in_array($id, $tagMgr->idsForTag('Family'))); + $this->assertFalse(in_array($id, $tagMgr->getIdsForTag('Family'))); } - $this->assertEquals(1, count($tagMgr->tags())); - $this->assertEquals(0, count($tagMgr->idsForTag('Family'))); + $this->assertEquals(1, count($tagMgr->getTags())); + $this->assertEquals(0, count($tagMgr->getIdsForTag('Family'))); + } + + public function testFavorite() { + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + $this->assertTrue($tagMgr->addToFavorites(1)); + $this->assertTrue($tagMgr->removeFromFavorites(1)); } } From 9e4d13858c92e3b42e99152cee7b899a7ddb926b Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 19 Sep 2013 13:27:41 +0200 Subject: [PATCH 055/150] Fix syntax error --- lib/server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server.php b/lib/server.php index 5c386593f1..4f5bcfbe67 100644 --- a/lib/server.php +++ b/lib/server.php @@ -113,7 +113,6 @@ class Server extends SimpleContainer implements IServerContainer { $folder = $root->get($dir); } return $folder; - } /** @@ -132,6 +131,7 @@ class Server extends SimpleContainer implements IServerContainer { $folder = $root->get($dir); } return $folder; + } /** * Returns an ICache instance From de81210bec4b08034e130cd5db4a426fe2f7820e Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 19 Sep 2013 13:55:45 +0200 Subject: [PATCH 056/150] Add another test. --- tests/lib/tags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 16a03f5645..92a96a1477 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -94,7 +94,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertEquals(0, count($tagMgr->getTags())); $this->assertTrue($tagMgr->isEmpty()); - $tagMgr->add('Tag'); + $this->assertNotEquals(false, $tagMgr->add('Tag')); $this->assertFalse($tagMgr->isEmpty()); } From e8bf576184fdafbe74f3394dc253a56c07be6507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Fri, 26 Jul 2013 14:11:59 +0200 Subject: [PATCH 057/150] add initial search in shared files --- apps/files_sharing/lib/cache.php | 26 +++++++++++++++++++++++++- lib/public/share.php | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 33cd142889..28e3cbdb2e 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -226,7 +226,31 @@ class Shared_Cache extends Cache { * @return array of file data */ public function search($pattern) { - // TODO + + // normalize pattern + $pattern = $this->normalize($pattern); + + $ids = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + foreach ($ids as $file) { + $folderBackend = \OCP\Share::getBackend('folder'); + $children = $folderBackend->getChildren($file); + foreach ($children as $child) { + $ids[] = (int)$child['source']; + } + + } + $placeholders = join(',', array_fill(0, count($ids), '?')); + + $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` + FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `fileid` IN (' . $placeholders . ')'; + $result = \OC_DB::executeAudited($sql, array_merge(array($pattern), $ids)); + $files = array(); + while ($row = $result->fetchRow()) { + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); + $files[] = $row; + } + return $files; } /** diff --git a/lib/public/share.php b/lib/public/share.php index 9ab956d84b..10922965ea 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -748,7 +748,7 @@ class Share { * @param string Item type * @return Sharing backend object */ - private static function getBackend($itemType) { + public static function getBackend($itemType) { if (isset(self::$backends[$itemType])) { return self::$backends[$itemType]; } else if (isset(self::$backendTypes[$itemType]['class'])) { From 27511d9187a5ffd4d5a087602a9c648e9ec1c06e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 31 Jul 2013 15:13:00 +0200 Subject: [PATCH 058/150] divide ids into chunks of 1k --- apps/files_sharing/lib/cache.php | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 28e3cbdb2e..6386f1d2c6 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -239,16 +239,25 @@ class Shared_Cache extends Cache { } } - $placeholders = join(',', array_fill(0, count($ids), '?')); - - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` - FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `fileid` IN (' . $placeholders . ')'; - $result = \OC_DB::executeAudited($sql, array_merge(array($pattern), $ids)); + $files = array(); - while ($row = $result->fetchRow()) { - $row['mimetype'] = $this->getMimetype($row['mimetype']); - $row['mimepart'] = $this->getMimetype($row['mimepart']); - $files[] = $row; + + // divide into 1k chunks + $chunks = array_chunk($ids, 1000); + + foreach ($chunks as $chunk) { + $placeholders = join(',', array_fill(0, count($chunk), '?')); + + $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` + FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `fileid` IN (' . $placeholders . ')'; + + $result = \OC_DB::executeAudited($sql, array_merge(array($pattern), $chunk)); + + while ($row = $result->fetchRow()) { + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); + $files[] = $row; + } } return $files; } From 392c6b6832edfc37a2956ef3a77fab6020a7a746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 7 Aug 2013 18:18:40 +0200 Subject: [PATCH 059/150] return fixed path, skip shared files outside of 'files' --- apps/files_sharing/lib/cache.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 6386f1d2c6..a440246448 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -254,9 +254,12 @@ class Shared_Cache extends Cache { $result = \OC_DB::executeAudited($sql, array_merge(array($pattern), $chunk)); while ($row = $result->fetchRow()) { - $row['mimetype'] = $this->getMimetype($row['mimetype']); - $row['mimepart'] = $this->getMimetype($row['mimepart']); - $files[] = $row; + if (substr($row['path'], 0, 6)==='files/') { + $row['path'] = substr($row['path'],6); // remove 'files/' from path as it's relative to '/Shared' + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); + $files[] = $row; + } // else skip results out of the files folder } } return $files; From 466fd8acda010ad330930055925ce26ede1fbf06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 28 Aug 2013 12:39:43 +0200 Subject: [PATCH 060/150] fix getAll(), refactor search by mime & search --- apps/files_sharing/lib/cache.php | 55 +++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index a440246448..82588df42d 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -230,15 +230,7 @@ class Shared_Cache extends Cache { // normalize pattern $pattern = $this->normalize($pattern); - $ids = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); - foreach ($ids as $file) { - $folderBackend = \OCP\Share::getBackend('folder'); - $children = $folderBackend->getChildren($file); - foreach ($children as $child) { - $ids[] = (int)$child['source']; - } - - } + $ids = $this->getAll(); $files = array(); @@ -248,7 +240,8 @@ class Shared_Cache extends Cache { foreach ($chunks as $chunk) { $placeholders = join(',', array_fill(0, count($chunk), '?')); - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `unencrypted_size`, `etag` + $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, + `encrypted`, `unencrypted_size`, `etag` FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `fileid` IN (' . $placeholders . ')'; $result = \OC_DB::executeAudited($sql, array_merge(array($pattern), $chunk)); @@ -280,13 +273,30 @@ class Shared_Cache extends Cache { } $mimetype = $this->getMimetypeId($mimetype); $ids = $this->getAll(); - $placeholders = join(',', array_fill(0, count($ids), '?')); - $query = \OC_DB::prepare(' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` - FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')' - ); - $result = $query->execute(array_merge(array($mimetype), $ids)); - return $result->fetchAll(); + + $files = array(); + + // divide into 1k chunks + $chunks = array_chunk($ids, 1000); + + foreach ($chunks as $chunk) { + $placeholders = join(',', array_fill(0, count($ids), '?')); + $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, + `encrypted`, `unencrypted_size`, `etag` + FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')'; + + $result = \OC_DB::executeAudited($sql, array_merge(array($mimetype), $chunk)); + + while ($row = $result->fetchRow()) { + if (substr($row['path'], 0, 6)==='files/') { + $row['path'] = substr($row['path'],6); // remove 'files/' from path as it's relative to '/Shared' + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); + $files[] = $row; + } // else skip results out of the files folder + } + } + return $files; } /** @@ -308,7 +318,16 @@ class Shared_Cache extends Cache { * @return int[] */ public function getAll() { - return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + $ids = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + $folderBackend = \OCP\Share::getBackend('folder'); + foreach ($ids as $file) { + $children = $folderBackend->getChildren($file); + foreach ($children as $child) { + $ids[] = (int)$child['source']; + } + + } + return $ids; } /** From 3b4020e81131cd526a6bbffe14bc14f1cac4fd60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 28 Aug 2013 21:04:21 +0200 Subject: [PATCH 061/150] add all results, sharing cache also returns entries for shared files in external storages --- apps/files_sharing/lib/cache.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 82588df42d..acb064f31a 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -249,10 +249,10 @@ class Shared_Cache extends Cache { while ($row = $result->fetchRow()) { if (substr($row['path'], 0, 6)==='files/') { $row['path'] = substr($row['path'],6); // remove 'files/' from path as it's relative to '/Shared' - $row['mimetype'] = $this->getMimetype($row['mimetype']); - $row['mimepart'] = $this->getMimetype($row['mimepart']); - $files[] = $row; - } // else skip results out of the files folder + } + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); + $files[] = $row; } } return $files; From 6aeb0a99daf28ecb68b010d96369636a99ad77be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 28 Aug 2013 21:10:06 +0200 Subject: [PATCH 062/150] same for search by mime --- apps/files_sharing/lib/cache.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index acb064f31a..51e8713b97 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -290,10 +290,10 @@ class Shared_Cache extends Cache { while ($row = $result->fetchRow()) { if (substr($row['path'], 0, 6)==='files/') { $row['path'] = substr($row['path'],6); // remove 'files/' from path as it's relative to '/Shared' - $row['mimetype'] = $this->getMimetype($row['mimetype']); - $row['mimepart'] = $this->getMimetype($row['mimepart']); - $files[] = $row; - } // else skip results out of the files folder + } + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); + $files[] = $row; } } return $files; From ac73ce1b2a9cbcafce29d9f6be768b0629f68ddb Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 20 Sep 2013 12:45:56 +0200 Subject: [PATCH 063/150] Add UserSession to server container --- lib/public/iservercontainer.php | 7 +++++ lib/public/iusersession.php | 30 ++++++++++++++++++ lib/server.php | 55 +++++++++++++++++++++++++++++++++ lib/user.php | 43 ++------------------------ 4 files changed, 94 insertions(+), 41 deletions(-) create mode 100644 lib/public/iusersession.php diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 1725b7c74e..ad71427666 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -62,6 +62,13 @@ interface IServerContainer { */ function getRootFolder(); + /** + * Returns the user session + * + * @return \OCP\IUserSession + */ + function getUserSession(); + /** * Returns an ICache instance * diff --git a/lib/public/iusersession.php b/lib/public/iusersession.php new file mode 100644 index 0000000000..5dc1ecf71e --- /dev/null +++ b/lib/public/iusersession.php @@ -0,0 +1,30 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OCP; + +/** + * User session + */ +interface IUserSession { + /** + * Do a user login + * @param string $user the username + * @param string $password the password + * @return bool true if successful + */ + public function login($user, $password); + + /** + * @brief Logs the user out including all the session data + * Logout, destroys session + */ + public function logout(); + +} diff --git a/lib/server.php b/lib/server.php index f4dc22a2be..316ed39665 100644 --- a/lib/server.php +++ b/lib/server.php @@ -56,6 +56,47 @@ class Server extends SimpleContainer implements IServerContainer { $view = new View(); return new Root($manager, $view, $user); }); + $this->registerService('UserManager', function($c) { + return new \OC\User\Manager(); + }); + $this->registerService('UserSession', function($c) { + $manager = $c->query('UserManager'); + $userSession = new \OC\User\Session($manager, \OC::$session); + $userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) { + \OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password)); + }); + $userSession->listen('\OC\User', 'postCreateUser', function ($user, $password) { + /** @var $user \OC\User\User */ + \OC_Hook::emit('OC_User', 'post_createUser', array('uid' => $user->getUID(), 'password' => $password)); + }); + $userSession->listen('\OC\User', 'preDelete', function ($user) { + /** @var $user \OC\User\User */ + \OC_Hook::emit('OC_User', 'pre_deleteUser', array('run' => true, 'uid' => $user->getUID())); + }); + $userSession->listen('\OC\User', 'postDelete', function ($user) { + /** @var $user \OC\User\User */ + \OC_Hook::emit('OC_User', 'post_deleteUser', array('uid' => $user->getUID())); + }); + $userSession->listen('\OC\User', 'preSetPassword', function ($user, $password, $recoveryPassword) { + /** @var $user \OC\User\User */ + \OC_Hook::emit('OC_User', 'pre_setPassword', array('run' => true, 'uid' => $user->getUID(), 'password' => $password, 'recoveryPassword' => $recoveryPassword)); + }); + $userSession->listen('\OC\User', 'postSetPassword', function ($user, $password, $recoveryPassword) { + /** @var $user \OC\User\User */ + \OC_Hook::emit('OC_User', 'post_setPassword', array('run' => true, 'uid' => $user->getUID(), 'password' => $password, 'recoveryPassword' => $recoveryPassword)); + }); + $userSession->listen('\OC\User', 'preLogin', function ($uid, $password) { + \OC_Hook::emit('OC_User', 'pre_login', array('run' => true, 'uid' => $uid, 'password' => $password)); + }); + $userSession->listen('\OC\User', 'postLogin', function ($user, $password) { + /** @var $user \OC\User\User */ + \OC_Hook::emit('OC_User', 'post_login', array('run' => true, 'uid' => $user->getUID(), 'password' => $password)); + }); + $userSession->listen('\OC\User', 'logout', function () { + \OC_Hook::emit('OC_User', 'logout', array()); + }); + return $userSession; + }); $this->registerService('UserCache', function($c) { return new UserCache(); }); @@ -97,6 +138,20 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('RootFolder'); } + /** + * @return \OC\User\Manager + */ + function getUserManager() { + return $this->query('UserManager'); + } + + /** + * @return \OC\User\Session + */ + function getUserSession() { + return $this->query('UserSession'); + } + /** * Returns an ICache instance * diff --git a/lib/user.php b/lib/user.php index 0f6f40aec9..7f6a296c3e 100644 --- a/lib/user.php +++ b/lib/user.php @@ -37,54 +37,15 @@ * logout() */ class OC_User { - public static $userSession = null; - public static function getUserSession() { - if (!self::$userSession) { - $manager = new \OC\User\Manager(); - self::$userSession = new \OC\User\Session($manager, \OC::$session); - self::$userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) { - \OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password)); - }); - self::$userSession->listen('\OC\User', 'postCreateUser', function ($user, $password) { - /** @var $user \OC\User\User */ - \OC_Hook::emit('OC_User', 'post_createUser', array('uid' => $user->getUID(), 'password' => $password)); - }); - self::$userSession->listen('\OC\User', 'preDelete', function ($user) { - /** @var $user \OC\User\User */ - \OC_Hook::emit('OC_User', 'pre_deleteUser', array('run' => true, 'uid' => $user->getUID())); - }); - self::$userSession->listen('\OC\User', 'postDelete', function ($user) { - /** @var $user \OC\User\User */ - \OC_Hook::emit('OC_User', 'post_deleteUser', array('uid' => $user->getUID())); - }); - self::$userSession->listen('\OC\User', 'preSetPassword', function ($user, $password, $recoveryPassword) { - /** @var $user \OC\User\User */ - OC_Hook::emit('OC_User', 'pre_setPassword', array('run' => true, 'uid' => $user->getUID(), 'password' => $password, 'recoveryPassword' => $recoveryPassword)); - }); - self::$userSession->listen('\OC\User', 'postSetPassword', function ($user, $password, $recoveryPassword) { - /** @var $user \OC\User\User */ - OC_Hook::emit('OC_User', 'post_setPassword', array('run' => true, 'uid' => $user->getUID(), 'password' => $password, 'recoveryPassword' => $recoveryPassword)); - }); - self::$userSession->listen('\OC\User', 'preLogin', function ($uid, $password) { - \OC_Hook::emit('OC_User', 'pre_login', array('run' => true, 'uid' => $uid, 'password' => $password)); - }); - self::$userSession->listen('\OC\User', 'postLogin', function ($user, $password) { - /** @var $user \OC\User\User */ - \OC_Hook::emit('OC_User', 'post_login', array('run' => true, 'uid' => $user->getUID(), 'password' => $password)); - }); - self::$userSession->listen('\OC\User', 'logout', function () { - \OC_Hook::emit('OC_User', 'logout', array()); - }); - } - return self::$userSession; + return OC::$server->getUserSession(); } /** * @return \OC\User\Manager */ public static function getManager() { - return self::getUserSession()->getManager(); + return OC::$server->getUserManager(); } private static $_backends = array(); From aa8a85f77d0c6705ee727d182e95d288ba7b7917 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 20 Sep 2013 14:33:45 +0200 Subject: [PATCH 064/150] Add DBConnection to server container --- lib/db/connection.php | 2 +- lib/public/idbconnection.php | 71 +++++++++++++++++++++++++++++++++ lib/public/iservercontainer.php | 7 ++++ lib/server.php | 8 ++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 lib/public/idbconnection.php diff --git a/lib/db/connection.php b/lib/db/connection.php index 2581969dbd..2d3193a148 100644 --- a/lib/db/connection.php +++ b/lib/db/connection.php @@ -12,7 +12,7 @@ use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\Common\EventManager; -class Connection extends \Doctrine\DBAL\Connection { +class Connection extends \Doctrine\DBAL\Connection implements \OCP\IDBConnection { /** * @var string $tablePrefix */ diff --git a/lib/public/idbconnection.php b/lib/public/idbconnection.php new file mode 100644 index 0000000000..67dd7ccfc3 --- /dev/null +++ b/lib/public/idbconnection.php @@ -0,0 +1,71 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OCP; + +/** + * TODO: Description + */ +interface IDBConnection { + /** + * 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 \Doctrine\DBAL\Driver\Statement The prepared statement. + */ + public function prepare($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 + */ + public function lastInsertId($table = null); + + /** + * @brief Insert a row if a matching row doesn't exists. + * @param $table string The table name (will replace *PREFIX*) to perform the replace on. + * @param $input array + * + * The input array if in the form: + * + * array ( 'id' => array ( 'value' => 6, + * 'key' => true + * ), + * 'name' => array ('value' => 'Stoyan'), + * 'family' => array ('value' => 'Stefanov'), + * 'birth_date' => array ('value' => '1975-06-20') + * ); + * @return bool + * + */ + public function insertIfNotExist($table, $input); + + /** + * @brief Start a transaction + */ + public function beginTransaction(); + + /** + * @brief Commit the database changes done during a transaction that is in progress + */ + public function commit(); + + /** + * @brief Rollback the database changes done during a transaction that is in progress + */ + public function rollBack(); + + /** + * returns the error code and message as a string for logging + * @return string + */ + public function getError(); +} diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index ad71427666..5481cd6ce6 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -83,4 +83,11 @@ interface IServerContainer { */ function getSession(); + /** + * Returns the current session + * + * @return \OCP\IDBConnection + */ + function getDatabaseConnection(); + } diff --git a/lib/server.php b/lib/server.php index 316ed39665..a5288fa148 100644 --- a/lib/server.php +++ b/lib/server.php @@ -170,4 +170,12 @@ class Server extends SimpleContainer implements IServerContainer { return \OC::$session; } + /** + * Returns the current session + * + * @return \OCP\IDBConnection + */ + function getDatabaseConnection() { + return \OC_DB::getConnection(); + } } From e3013c580108e6b0cc332ba14976f3ffb4b7b274 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 20 Sep 2013 17:34:33 +0200 Subject: [PATCH 065/150] Add Navigation class to server container --- lib/app.php | 31 ++++++--------- lib/navigationmanager.php | 63 +++++++++++++++++++++++++++++++ lib/public/inavigationmanager.php | 27 +++++++++++++ lib/public/iservercontainer.php | 5 +++ lib/server.php | 10 +++++ 5 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 lib/navigationmanager.php create mode 100644 lib/public/inavigationmanager.php diff --git a/lib/app.php b/lib/app.php index d98af2dc29..0ab1ee57f6 100644 --- a/lib/app.php +++ b/lib/app.php @@ -27,8 +27,6 @@ * upgrading and removing apps. */ class OC_App{ - static private $activeapp = ''; - static private $navigation = array(); static private $settingsForms = array(); static private $adminForms = array(); static private $personalForms = array(); @@ -271,7 +269,7 @@ class OC_App{ /** * @brief adds an entry to the navigation - * @param string $data array containing the data + * @param array $data array containing the data * @return bool * * This function adds a new entry to the navigation visible to users. $data @@ -287,11 +285,7 @@ class OC_App{ * the navigation. Lower values come first. */ public static function addNavigationEntry( $data ) { - $data['active']=false; - if(!isset($data['icon'])) { - $data['icon']=''; - } - OC_App::$navigation[] = $data; + OC::$server->getNavigationManager()->add($data); return true; } @@ -305,9 +299,7 @@ class OC_App{ * highlighting the current position of the user. */ public static function setActiveNavigationEntry( $id ) { - // load all the apps, to make sure we have all the navigation entries - self::loadApps(); - self::$activeapp = $id; + OC::$server->getNavigationManager()->setActiveEntry($id); return true; } @@ -315,15 +307,14 @@ class OC_App{ * @brief Get the navigation entries for the $app * @param string $app app * @return array of the $data added with addNavigationEntry + * + * Warning: destroys the existing entries */ public static function getAppNavigationEntries($app) { if(is_file(self::getAppPath($app).'/appinfo/app.php')) { - $save = self::$navigation; - self::$navigation = array(); + OC::$server->getNavigationManager()->clear(); require $app.'/appinfo/app.php'; - $app_entries = self::$navigation; - self::$navigation = $save; - return $app_entries; + return OC::$server->getNavigationManager()->getAll(); } return array(); } @@ -336,7 +327,7 @@ class OC_App{ * setActiveNavigationEntry */ public static function getActiveNavigationEntry() { - return self::$activeapp; + return OC::$server->getNavigationManager()->getActiveEntry(); } /** @@ -419,8 +410,9 @@ class OC_App{ // This is private as well. It simply works, so don't ask for more details private static function proceedNavigation( $list ) { + $activeapp = OC::$server->getNavigationManager()->getActiveEntry(); foreach( $list as &$naventry ) { - if( $naventry['id'] == self::$activeapp ) { + if( $naventry['id'] == $activeapp ) { $naventry['active'] = true; } else{ @@ -572,7 +564,8 @@ class OC_App{ * - active: boolean, signals if the user is on this navigation entry */ public static function getNavigation() { - $navigation = self::proceedNavigation( self::$navigation ); + $entries = OC::$server->getNavigationManager()->getAll(); + $navigation = self::proceedNavigation( $entries ); return $navigation; } diff --git a/lib/navigationmanager.php b/lib/navigationmanager.php new file mode 100644 index 0000000000..f032afd1fc --- /dev/null +++ b/lib/navigationmanager.php @@ -0,0 +1,63 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OC; + +/** + * Manages the owncloud navigation + */ +class NavigationManager { + protected $entries = array(); + + /** + * Creates a new navigation entry + * @param array $entry containing: id, name, order, icon and href key + */ + public function add(array $entry) { + $entry['active'] = false; + if(!isset($entry['icon'])) { + $entry['icon'] = ''; + } + $this->entries[] = $entry; + } + + /** + * @brief returns all the added Menu entries + * @return array of the added entries + */ + public function getAll() { + return $this->entries; + } + + /** + * @brief removes all the entries + */ + public function clear() { + $this->entries = array(); + } + + /** + * Sets the current navigation entry of the currently running app + * @param string $id of the app entry to activate (from added $entry) + */ + public function setActiveEntry($id) { + $this->activeEntry = $id; + } + + /** + * @brief gets the active Menu entry + * @return string id or empty string + * + * This function returns the id of the active navigation entry (set by + * setActiveEntry + */ + public function getActiveEntry() { + return $this->activeEntry; + } +} diff --git a/lib/public/inavigationmanager.php b/lib/public/inavigationmanager.php new file mode 100644 index 0000000000..21744dd1c2 --- /dev/null +++ b/lib/public/inavigationmanager.php @@ -0,0 +1,27 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OCP; + +/** + * Manages the owncloud navigation + */ +interface INavigationManager { + /** + * Creates a new navigation entry + * @param array $entry containing: id, name, order, icon and href key + */ + public function add(array $entry); + + /** + * Sets the current navigation entry of the currently running app + * @param string $appId id of the app entry to activate (from added $entry) + */ + public function setActiveEntry($appId); +} \ No newline at end of file diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 5481cd6ce6..ebcc0fe4cd 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -69,6 +69,11 @@ interface IServerContainer { */ function getUserSession(); + /** + * @return \OCP\INavigationManager + */ + function getNavigationManager(); + /** * Returns an ICache instance * diff --git a/lib/server.php b/lib/server.php index a5288fa148..13bda2dc30 100644 --- a/lib/server.php +++ b/lib/server.php @@ -97,6 +97,9 @@ class Server extends SimpleContainer implements IServerContainer { }); return $userSession; }); + $this->registerService('NavigationManager', function($c) { + return new \OC\NavigationManager(); + }); $this->registerService('UserCache', function($c) { return new UserCache(); }); @@ -152,6 +155,13 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('UserSession'); } + /** + * @return \OC\NavigationManager + */ + function getNavigationManager() { + return $this->query('NavigationManager'); + } + /** * Returns an ICache instance * From e92abfd4d8dfb9dbb42ccfc0c23193b1ddde3bbf Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 20 Sep 2013 20:21:24 +0200 Subject: [PATCH 066/150] Add Config container class to server container --- lib/allconfig.php | 78 +++++++++++++++++++++++++++++++++ lib/public/iconfig.php | 59 +++++++++++++++++++++++++ lib/public/iservercontainer.php | 5 +++ lib/server.php | 9 ++++ 4 files changed, 151 insertions(+) create mode 100644 lib/allconfig.php create mode 100644 lib/public/iconfig.php diff --git a/lib/allconfig.php b/lib/allconfig.php new file mode 100644 index 0000000000..81c8892348 --- /dev/null +++ b/lib/allconfig.php @@ -0,0 +1,78 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OC; + +/** + * Class to combine all the configuration options ownCloud offers + */ +class AllConfig implements \OCP\IConfig { + /** + * 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 + * @todo need a use case for this + */ +// public function setSystemValue($key, $value) { +// \OCP\Config::setSystemValue($key, $value); +// } + + /** + * 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, ''); + } + + + /** + * Writes a new appwide value + * @param string $appName the appName that we want to store the value under + * @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($appName, $key, $value) { + \OCP\Config::setAppValue($appName, $key, $value); + } + + /** + * Looks up an appwide defined value + * @param string $appName the appName that we stored the value under + * @param string $key the key of the value, under which it was saved + * @return string the saved value + */ + public function getAppValue($appName, $key) { + return \OCP\Config::getAppValue($appName, $key, ''); + } + + + /** + * Set a user defined value + * @param string $userId the userId of the user that we want to store the value under + * @param string $appName the appName that we want to store the value under + * @param string $key the key under which the value is being stored + * @param string $value the value that you want to store + */ + public function setUserValue($userId, $appName, $key, $value) { + \OCP\Config::setUserValue($userId, $appName, $key, $value); + } + + + /** + * Shortcut for getting a user defined value + * @param string $userId the userId of the user that we want to store the value under + * @param string $appName the appName that we stored the value under + * @param string $key the key under which the value is being stored + */ + public function getUserValue($userId, $appName, $key){ + return \OCP\Config::getUserValue($userId, $appName, $key); + } +} diff --git a/lib/public/iconfig.php b/lib/public/iconfig.php new file mode 100644 index 0000000000..0c150ec351 --- /dev/null +++ b/lib/public/iconfig.php @@ -0,0 +1,59 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ +namespace OCP; + +/** + * Access to all the configuration options ownCloud offers + */ +interface IConfig { + /** + * 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 + * @todo need a use case for this + */ +// public function setSystemValue($key, $value); + + /** + * 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); + + + /** + * 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); + + /** + * 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); + + + /** + * 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); + + /** + * 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); +} diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index ebcc0fe4cd..4478a4e8a6 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -74,6 +74,11 @@ interface IServerContainer { */ function getNavigationManager(); + /** + * @return \OCP\IConfig + */ + function getConfig(); + /** * Returns an ICache instance * diff --git a/lib/server.php b/lib/server.php index 13bda2dc30..57e7f4ab4f 100644 --- a/lib/server.php +++ b/lib/server.php @@ -100,6 +100,9 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('NavigationManager', function($c) { return new \OC\NavigationManager(); }); + $this->registerService('AllConfig', function($c) { + return new \OC\AllConfig(); + }); $this->registerService('UserCache', function($c) { return new UserCache(); }); @@ -162,6 +165,12 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('NavigationManager'); } + /** + * @return \OC\Config + */ + function getConfig() { + return $this->query('AllConfig'); + } /** * Returns an ICache instance * From 45a7b0dbac307df9ea38a914fd3f1cfb9202a994 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 20 Sep 2013 20:29:15 +0200 Subject: [PATCH 067/150] Fix the apps enabling/disabling in settings --- settings/js/apps.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/settings/js/apps.js b/settings/js/apps.js index 54810776d2..a55c55e24c 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -84,6 +84,7 @@ OC.Settings.Apps = OC.Settings.Apps || { } else { appitem.data('active',false); + element.data('active',false); OC.Settings.Apps.removeNavigation(appid); appitem.removeClass('active'); element.val(t('settings','Enable')); @@ -104,6 +105,7 @@ OC.Settings.Apps = OC.Settings.Apps || { } else { OC.Settings.Apps.addNavigation(appid); appitem.data('active',true); + element.data('active',true); appitem.addClass('active'); element.val(t('settings','Disable')); } @@ -158,7 +160,7 @@ OC.Settings.Apps = OC.Settings.Apps || { if(response.status === 'success'){ var navIds=response.nav_ids; for(var i=0; i< navIds.length; i++){ - $('#apps').children('li[data-id="'+navIds[i]+'"]').remove(); + $('#apps .wrapper').children('li[data-id="'+navIds[i]+'"]').remove(); } } }); From d84d548618651c0a66bd2696d6547b33ca6b8e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 20 Sep 2013 20:34:17 +0200 Subject: [PATCH 068/150] when storing back the data field 'encrypted' it is necessary to cast the boolean to an integer to make pg happy --- lib/files/cache/scanner.php | 2 ++ tests/lib/files/cache/scanner.php | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index fdbce0d51f..d296c60686 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -121,6 +121,8 @@ class Scanner extends BasicEmitter { } $parentCacheData = $this->cache->get($parent); $parentCacheData['etag'] = $this->storage->getETag($parent); + // the boolean to int conversion is necessary to make pg happy + $parentCacheData['encrypted'] = $parentCacheData['encrypted'] ? 1 : 0; $this->cache->put($parent, $parentCacheData); } } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index b137799bbc..8112eada17 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -195,6 +195,7 @@ class Scanner extends \PHPUnit_Framework_TestCase { $data1 = $this->cache->get('folder'); $data2 = $this->cache->get(''); $data0['etag'] = ''; + $data0['encrypted'] = $data0['encrypted'] ? 1: 0; $this->cache->put('folder/bar.txt', $data0); // rescan From 9116303cfc5259f05ec348809bfbc8757d4657cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 20 Sep 2013 21:40:54 +0200 Subject: [PATCH 069/150] fixing typos --- lib/allconfig.php | 8 ++++---- lib/public/iconfig.php | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/allconfig.php b/lib/allconfig.php index 81c8892348..353608e5d2 100644 --- a/lib/allconfig.php +++ b/lib/allconfig.php @@ -14,7 +14,7 @@ namespace OC; */ class AllConfig implements \OCP\IConfig { /** - * Sets a new systemwide value + * 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 * @todo need a use case for this @@ -24,7 +24,7 @@ class AllConfig implements \OCP\IConfig { // } /** - * Looks up a systemwide defined value + * 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 */ @@ -34,7 +34,7 @@ class AllConfig implements \OCP\IConfig { /** - * Writes a new appwide value + * Writes a new app wide value * @param string $appName the appName that we want to store the value under * @param string $key the key of the value, under which will be saved * @param string $value the value that should be stored @@ -44,7 +44,7 @@ class AllConfig implements \OCP\IConfig { } /** - * Looks up an appwide defined value + * Looks up an app wide defined value * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @return string the saved value diff --git a/lib/public/iconfig.php b/lib/public/iconfig.php index 0c150ec351..dab4590969 100644 --- a/lib/public/iconfig.php +++ b/lib/public/iconfig.php @@ -12,7 +12,7 @@ namespace OCP; */ interface IConfig { /** - * Sets a new systemwide value + * 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 * @todo need a use case for this @@ -20,7 +20,7 @@ interface IConfig { // public function setSystemValue($key, $value); /** - * Looks up a systemwide defined value + * 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 */ @@ -28,14 +28,14 @@ interface IConfig { /** - * Writes a new appwide value + * Writes a new app wide 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); /** - * Looks up an appwide defined value + * Looks up an app wide defined value * @param string $key the key of the value, under which it was saved * @return string the saved value */ From e31f6c01e85bea52e973df65a7610d490bf6336c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 20 Sep 2013 21:43:17 +0200 Subject: [PATCH 070/150] fixing PHPDoc --- lib/public/app.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/public/app.php b/lib/public/app.php index a1ecf524cc..0a5721b334 100644 --- a/lib/public/app.php +++ b/lib/public/app.php @@ -35,10 +35,10 @@ namespace OCP; */ class App { /** - * @brief Makes owncloud aware of this app + * @brief Makes ownCloud aware of this app * @brief This call is deprecated and not necessary to use. * @param $data array with all information - * @returns true/false + * @returns boolean * * @deprecated this method is deprecated * Do not call it anymore @@ -52,7 +52,7 @@ class App { /** * @brief adds an entry to the navigation * @param $data array containing the data - * @returns true/false + * @returns boolean * * This function adds a new entry to the navigation visible to users. $data * is an associative array. @@ -72,8 +72,8 @@ class App { /** * @brief marks a navigation entry as active - * @param $id id of the entry - * @returns true/false + * @param $id string id of the entry + * @returns boolean * * This function sets a navigation entry as active and removes the 'active' * property from all other entries. The templates can use this for @@ -104,7 +104,7 @@ class App { /** * @brief Read app metadata from the info.xml file * @param string $app id of the app or the path of the info.xml file - * @param boolean path (optional) + * @param boolean $path (optional) * @returns array */ public static function getAppInfo( $app, $path=false ) { @@ -114,7 +114,7 @@ class App { /** * @brief checks whether or not an app is enabled * @param $app app - * @returns true/false + * @returns boolean * * This function checks whether or not an app is enabled. */ @@ -133,7 +133,7 @@ class App { /** * @brief Get the last version of the app, either from appinfo/version or from appinfo/info.xml * @param $app app - * @returns true/false + * @returns boolean */ public static function getAppVersion( $app ) { return \OC_App::getAppVersion( $app ); From f83f32326949d6bc16c2b0d7aefcdbb48f9119d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 20 Sep 2013 21:45:27 +0200 Subject: [PATCH 071/150] fixing typos + adding missing filed $activeEntry --- lib/navigationmanager.php | 3 ++- lib/public/inavigationmanager.php | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/navigationmanager.php b/lib/navigationmanager.php index f032afd1fc..cf3bf84d99 100644 --- a/lib/navigationmanager.php +++ b/lib/navigationmanager.php @@ -10,10 +10,11 @@ namespace OC; /** - * Manages the owncloud navigation + * Manages the ownCloud navigation */ class NavigationManager { protected $entries = array(); + protected $activeEntry; /** * Creates a new navigation entry diff --git a/lib/public/inavigationmanager.php b/lib/public/inavigationmanager.php index 21744dd1c2..f89e790c1d 100644 --- a/lib/public/inavigationmanager.php +++ b/lib/public/inavigationmanager.php @@ -10,7 +10,7 @@ namespace OCP; /** - * Manages the owncloud navigation + * Manages the ownCloud navigation */ interface INavigationManager { /** @@ -24,4 +24,4 @@ interface INavigationManager { * @param string $appId id of the app entry to activate (from added $entry) */ public function setActiveEntry($appId); -} \ No newline at end of file +} From d3d52dd23f3da9a3d9ed2b50b1abd1a229dc4be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 20 Sep 2013 21:57:48 +0200 Subject: [PATCH 072/150] PHPDoc & get UserManager from container for RooFolder --- lib/server.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/server.php b/lib/server.php index 57e7f4ab4f..804af6b0ea 100644 --- a/lib/server.php +++ b/lib/server.php @@ -4,6 +4,7 @@ namespace OC; use OC\AppFramework\Http\Request; use OC\AppFramework\Utility\SimpleContainer; +use OC\Cache\UserCache; use OC\Files\Node\Root; use OC\Files\View; use OCP\IServerContainer; @@ -49,9 +50,11 @@ class Server extends SimpleContainer implements IServerContainer { return new PreviewManager(); }); $this->registerService('RootFolder', function($c) { - // TODO: get user and user manager from container as well + // TODO: get user from container as well $user = \OC_User::getUser(); - $user = \OC_User::getManager()->get($user); + /** @var $c SimpleContainer */ + $userManager = $c->query('UserManager'); + $user = $userManager->get($user); $manager = \OC\Files\Filesystem::getMountManager(); $view = new View(); return new Root($manager, $view, $user); @@ -60,6 +63,7 @@ class Server extends SimpleContainer implements IServerContainer { return new \OC\User\Manager(); }); $this->registerService('UserSession', function($c) { + /** @var $c SimpleContainer */ $manager = $c->query('UserManager'); $userSession = new \OC\User\Session($manager, \OC::$session); $userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) { From 0c6dcdba6b890dc170f59a461ab80961a85a53db Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 20 Sep 2013 22:45:22 +0200 Subject: [PATCH 073/150] Add missing implements and fix parameters in IConfig --- lib/allconfig.php | 1 - lib/navigationmanager.php | 2 +- lib/public/iconfig.php | 22 ++++++++++++++-------- lib/public/idbconnection.php | 3 +++ lib/user/session.php | 2 +- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/allconfig.php b/lib/allconfig.php index 353608e5d2..72aabf6079 100644 --- a/lib/allconfig.php +++ b/lib/allconfig.php @@ -65,7 +65,6 @@ class AllConfig implements \OCP\IConfig { \OCP\Config::setUserValue($userId, $appName, $key, $value); } - /** * Shortcut for getting a user defined value * @param string $userId the userId of the user that we want to store the value under diff --git a/lib/navigationmanager.php b/lib/navigationmanager.php index cf3bf84d99..1f657b9ad8 100644 --- a/lib/navigationmanager.php +++ b/lib/navigationmanager.php @@ -12,7 +12,7 @@ namespace OC; /** * Manages the ownCloud navigation */ -class NavigationManager { +class NavigationManager implements \OCP\INavigationManager { protected $entries = array(); protected $activeEntry; diff --git a/lib/public/iconfig.php b/lib/public/iconfig.php index dab4590969..850bddf693 100644 --- a/lib/public/iconfig.php +++ b/lib/public/iconfig.php @@ -1,10 +1,12 @@ +/** + * Copyright (c) 2013 Bart Visscher * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. * */ + namespace OCP; /** @@ -29,31 +31,35 @@ interface IConfig { /** * Writes a new app wide value + * @param string $appName the appName that we want to store the value under * @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); + public function setAppValue($appName, $key, $value); /** * Looks up an app wide defined value + * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved * @return string the saved value */ - public function getAppValue($key, $appName=null); + public function getAppValue($appName, $key); /** - * Shortcut for setting a user defined value + * Set a user defined value + * @param string $userId the userId of the user that we want to store the value under + * @param string $appName the appName that we want to store the value under * @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); + public function setUserValue($userId, $appName, $key, $value); /** * Shortcut for getting a user defined value + * @param string $userId the userId of the user that we want to store the value under + * @param string $appName the appName that we stored the value under * @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); + public function getUserValue($userId, $appName, $key); } diff --git a/lib/public/idbconnection.php b/lib/public/idbconnection.php index 67dd7ccfc3..c741a0f061 100644 --- a/lib/public/idbconnection.php +++ b/lib/public/idbconnection.php @@ -50,16 +50,19 @@ interface IDBConnection { /** * @brief Start a transaction + * @return bool TRUE on success or FALSE on failure */ public function beginTransaction(); /** * @brief Commit the database changes done during a transaction that is in progress + * @return bool TRUE on success or FALSE on failure */ public function commit(); /** * @brief Rollback the database changes done during a transaction that is in progress + * @return bool TRUE on success or FALSE on failure */ public function rollBack(); diff --git a/lib/user/session.php b/lib/user/session.php index 9a6c669e93..98a24d5bb0 100644 --- a/lib/user/session.php +++ b/lib/user/session.php @@ -27,7 +27,7 @@ use OC\Hooks\Emitter; * * @package OC\User */ -class Session implements Emitter { +class Session implements Emitter, \OCP\IUserSession { /** * @var \OC\User\Manager $manager */ From a2f82da572eaf9cebfe4de53b329af700d63e93f Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Fri, 20 Sep 2013 23:52:05 +0200 Subject: [PATCH 074/150] Use update() instead of put(). --- lib/files/cache/scanner.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index d296c60686..3f1970fb4a 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -121,9 +121,7 @@ class Scanner extends BasicEmitter { } $parentCacheData = $this->cache->get($parent); $parentCacheData['etag'] = $this->storage->getETag($parent); - // the boolean to int conversion is necessary to make pg happy - $parentCacheData['encrypted'] = $parentCacheData['encrypted'] ? 1 : 0; - $this->cache->put($parent, $parentCacheData); + $this->cache->update($parentCacheData['fileid'], $parentCacheData); } } } From 011bca7b7f0a67c9cf23773b625ee334db1e6c06 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Fri, 20 Sep 2013 23:53:02 +0200 Subject: [PATCH 075/150] Only update the etag. Do not re-submit any other unchanged data. --- lib/files/cache/scanner.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 3f1970fb4a..fcb8ccdc8d 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -120,8 +120,9 @@ class Scanner extends BasicEmitter { $parent = ''; } $parentCacheData = $this->cache->get($parent); - $parentCacheData['etag'] = $this->storage->getETag($parent); - $this->cache->update($parentCacheData['fileid'], $parentCacheData); + $this->cache->update($parentCacheData['fileid'], array( + 'etag' => $this->storage->getETag($parent), + )); } } } From de2e6e137b3be966622b0b608a3b69f6282e2e56 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Sat, 21 Sep 2013 00:12:13 +0200 Subject: [PATCH 076/150] Do not convert boolean to integer in tests. put() already does this. --- tests/lib/files/cache/scanner.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 8112eada17..b137799bbc 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -195,7 +195,6 @@ class Scanner extends \PHPUnit_Framework_TestCase { $data1 = $this->cache->get('folder'); $data2 = $this->cache->get(''); $data0['etag'] = ''; - $data0['encrypted'] = $data0['encrypted'] ? 1: 0; $this->cache->put('folder/bar.txt', $data0); // rescan From 2a17025d537c41b9366c9592c985b911d9394337 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Sat, 21 Sep 2013 02:20:01 +0200 Subject: [PATCH 077/150] Move bool to int conversion to buildParts(), so it also happens for update(). --- lib/files/cache/cache.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 39e36684b7..e69733727a 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -201,7 +201,6 @@ class Cache { $data['path'] = $file; $data['parent'] = $this->getParentId($file); $data['name'] = \OC_Util::basename($file); - $data['encrypted'] = isset($data['encrypted']) ? ((int)$data['encrypted']) : 0; list($queryParts, $params) = $this->buildParts($data); $queryParts[] = '`storage`'; @@ -265,6 +264,9 @@ class Cache { $params[] = $value; $queryParts[] = '`mtime`'; } + } elseif ($name === 'encrypted') { + // Boolean to integer conversion + $value = $value ? 1 : 0; } $params[] = $value; $queryParts[] = '`' . $name . '`'; From edd38e59481a98c56645a43ca6c9048fe1458d20 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Mon, 23 Sep 2013 10:26:46 +0200 Subject: [PATCH 078/150] fix previews in shared folders --- apps/files/lib/helper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index 08c807d7f7..1d431df04f 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -39,8 +39,8 @@ class Helper } if($file['isPreviewAvailable']) { - $relativePath = substr($file['path'], 6); - return \OC_Helper::previewIcon($relativePath); + $pathForPreview = $file['directory'] . '/' . $file['name']; + return \OC_Helper::previewIcon($pathForPreview); } return \OC_Helper::mimetypeIcon($file['mimetype']); } From 8a1618bce56a32e311626cd9f0e322dd7cf330c4 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Mon, 23 Sep 2013 12:27:05 +0200 Subject: [PATCH 079/150] implement previews for public upload --- apps/files/js/files.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 8ccb448abf..ec688eaf63 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -644,7 +644,11 @@ function lazyLoadPreview(path, mime, ready, width, height) { if ( ! height ) { height = $('#filestable').data('preview-y'); } - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + if( $('#publicUploadButtonMock').length ) { + var previewURL = OC.Router.generate('core_ajax_public_preview', {file: encodeURIComponent(path), x:width, y:height, t:$('#dirToken').val()}); + } else { + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + } $.get(previewURL, function() { previewURL = previewURL.replace('(', '%28'); previewURL = previewURL.replace(')', '%29'); From d9a36ee82ec3bffb83515248b69c287f5fd0170f Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 23 Sep 2013 12:45:02 +0200 Subject: [PATCH 080/150] Move setUp() and tearDown() up in tests/lib/files/cache/scanner.php. --- tests/lib/files/cache/scanner.php | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 6956e7aa94..3f3a045377 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -24,6 +24,21 @@ class Scanner extends \PHPUnit_Framework_TestCase { */ private $cache; + function setUp() { + $this->storage = new \OC\Files\Storage\Temporary(array()); + $this->scanner = new \OC\Files\Cache\Scanner($this->storage); + $this->cache = new \OC\Files\Cache\Cache($this->storage); + } + + function tearDown() { + if ($this->cache) { + $ids = $this->cache->getAll(); + $permissionsCache = $this->storage->getPermissionsCache(); + $permissionsCache->removeMultiple($ids, \OC_User::getUser()); + $this->cache->clear(); + } + } + function testFile() { $data = "dummy file data\n"; $this->storage->file_put_contents('foo.txt', $data); @@ -218,19 +233,4 @@ class Scanner extends \PHPUnit_Framework_TestCase { $this->assertNotEquals($data1['etag'], $newData1['etag']); $this->assertNotEquals($data2['etag'], $newData2['etag']); } - - function setUp() { - $this->storage = new \OC\Files\Storage\Temporary(array()); - $this->scanner = new \OC\Files\Cache\Scanner($this->storage); - $this->cache = new \OC\Files\Cache\Cache($this->storage); - } - - function tearDown() { - if ($this->cache) { - $ids = $this->cache->getAll(); - $permissionsCache = $this->storage->getPermissionsCache(); - $permissionsCache->removeMultiple($ids, \OC_User::getUser()); - $this->cache->clear(); - } - } } From 0b4de847a93357160dc4acb3de651a7ee08a10df Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 13:27:43 +0200 Subject: [PATCH 081/150] Added more error checking in add() --- lib/tags.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/tags.php b/lib/tags.php index 2eaa603c1a..e2e1a83dd9 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -233,17 +233,25 @@ class Tags implements \OCP\ITags { return false; } try { - \OCP\DB::insertIfNotExist(self::TAG_TABLE, + $result = \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); + ) + ); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; + } elseif((int)$result === 0) { + \OCP\Util::writeLog('core', __METHOD__.', Tag already exists: ' . $name, \OCP\Util::DEBUG); } + } 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; From 93258e11701205b47b3775dbbb5f187a3a76266b Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 13:29:21 +0200 Subject: [PATCH 082/150] Forgot to return false if add() didn't add anything. --- lib/tags.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tags.php b/lib/tags.php index e2e1a83dd9..955eb3cd36 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -246,6 +246,7 @@ class Tags implements \OCP\ITags { return false; } elseif((int)$result === 0) { \OCP\Util::writeLog('core', __METHOD__.', Tag already exists: ' . $name, \OCP\Util::DEBUG); + return false; } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), From 910a0338bb296e2e51d6c9d37d0483f8d05b1c5c Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 15:52:06 +0200 Subject: [PATCH 083/150] Use fetchOne() instead of numRows() when doing a COUNT(*). --- lib/tags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tags.php b/lib/tags.php index 955eb3cd36..ff9f35ebc9 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -119,7 +119,7 @@ class Tags implements \OCP\ITags { \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; } - return ((int)$result->numRows() === 0); + return ((int)$result->fetchOne() === 0); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); From d27416edf767219f77a828d22bb070345a179631 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 15:52:50 +0200 Subject: [PATCH 084/150] Moar tests. --- tests/lib/tags.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 92a96a1477..75db9f50f7 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -59,13 +59,14 @@ class Test_Tags extends PHPUnit_Framework_TestCase { foreach($tags as $tag) { $result = $tagMgr->add($tag); + $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0'); $this->assertTrue((bool)$result); } $this->assertFalse($tagMgr->add('Family')); $this->assertFalse($tagMgr->add('fAMILY')); - $this->assertEquals(4, count($tagMgr->getTags())); + $this->assertCount(4, $tagMgr->getTags(), 'Wrong number of added tags'); } public function testAddMultiple() { @@ -85,7 +86,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertTrue($tagMgr->hasTag($tag)); } - $this->assertEquals(4, count($tagMgr->getTags())); + $this->assertCount(4, $tagMgr->getTags(), 'Not all tags added'); } public function testIsEmpty() { @@ -94,7 +95,10 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertEquals(0, count($tagMgr->getTags())); $this->assertTrue($tagMgr->isEmpty()); - $this->assertNotEquals(false, $tagMgr->add('Tag')); + + $result = $tagMgr->add('Tag'); + $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0'); + $this->assertNotEquals(false, $result, 'add() returned false'); $this->assertFalse($tagMgr->isEmpty()); } From d7409547aa0c6fe23cb408e266a09392b4752a72 Mon Sep 17 00:00:00 2001 From: kondou Date: Mon, 23 Sep 2013 16:39:42 +0200 Subject: [PATCH 085/150] Fix not displaying "Upload something!" message Fix #4940 --- apps/files/templates/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index bd991c3fcb..96a8073898 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -47,7 +47,7 @@ -
0 or !$_['ajaxLoad']):?>class="hidden">t('Nothing in here. Upload something!'))?>
+
0 or $_['ajaxLoad']):?>class="hidden">t('Nothing in here. Upload something!'))?>
From e55f25b64df09a3ba6535274eaf738e82910e1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 23 Sep 2013 22:04:37 +0200 Subject: [PATCH 086/150] handle error situation of rename proper --- lib/connector/sabre/directory.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 3181a4b310..9e0fe5e64e 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -88,7 +88,12 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } // rename to correct path - \OC\Files\Filesystem::rename($partpath, $newPath); + $renameOkay = \OC\Files\Filesystem::rename($partpath, $newPath); + if (!$renameOkay) { + \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); + \OC\Files\Filesystem::unlink($partpath); + throw new Sabre_DAV_Exception(); + } // allow sync clients to send the mtime along in a header $mtime = OC_Request::hasModificationTime(); From 5ca181eb23de7d3436b22ba924788db62976a059 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 4 Sep 2013 08:16:27 +0200 Subject: [PATCH 087/150] 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 45f73feb6967b878b2e39fc4413aca92f0dbaf9f Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 00:37:00 +0200 Subject: [PATCH 088/150] OC_VCategories=>OC\Tags. Public interface + getter in server container --- lib/public/iservercontainer.php | 7 + lib/public/itags.php | 173 +++++++ lib/server.php | 16 +- lib/tags.php | 621 ++++++++++++++++++++++++ lib/vcategories.php | 833 -------------------------------- tests/lib/tags.php | 133 +++++ tests/lib/vcategories.php | 128 ----- 7 files changed, 948 insertions(+), 963 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 4478a4e8a6..0df746a28e 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 804af6b0ea..b09d380b0e 100644 --- a/lib/server.php +++ b/lib/server.php @@ -49,8 +49,11 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('PreviewManager', function($c) { return new PreviewManager(); }); - $this->registerService('RootFolder', function($c) { - // TODO: get user from container as well + $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(); /** @var $c SimpleContainer */ $userManager = $c->query('UserManager'); @@ -139,6 +142,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 b63acdb12599c4ad062d7509c0079fad0b1ccfa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 18 Sep 2013 22:49:09 +0200 Subject: [PATCH 089/150] fixing namespaces and rename hasCategory to hasTag --- lib/tags.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/tags.php b/lib/tags.php index 4aafff8e1b..3320d9ea1a 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -113,7 +113,7 @@ class Tags implements \OCP\ITags { $sql = 'SELECT COUNT(*) FROM `' . self::TAG_TABLE . '` ' . 'WHERE `uid` = ? AND `type` = ?'; try { - $stmt = OCP\DB::prepare($sql); + $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); @@ -191,7 +191,7 @@ class Tags implements \OCP\ITags { $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); + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; } } catch(\Exception $e) { @@ -381,7 +381,7 @@ class Tags implements \OCP\ITags { . '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); + \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(), @@ -409,12 +409,12 @@ class Tags implements \OCP\ITags { $stmt = \OCP\DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` ' . 'WHERE `uid` = ?'); $result = $stmt->execute(array($arguments['uid'])); - if (OCP\DB::isError($result)) { + 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); + \OCP\Util::writeLog('core', __METHOD__ . ', exception: ' + . $e->getMessage(), \OCP\Util::ERROR); } } @@ -435,7 +435,7 @@ class Tags implements \OCP\ITags { $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) '; $query .= 'AND `type`= ?'; $updates[] = $this->type; - $stmt = OCP\DB::prepare($query); + $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); @@ -471,7 +471,7 @@ class Tags implements \OCP\ITags { * @return boolean */ public function addToFavorites($objid) { - if(!$this->hasCategory(self::TAG_FAVORITE)) { + if(!$this->hasTag(self::TAG_FAVORITE)) { $this->add(self::TAG_FAVORITE, true); } return $this->tagAs($objid, self::TAG_FAVORITE, $this->type); @@ -574,7 +574,7 @@ class Tags implements \OCP\ITags { . '`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); + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__ . ', exception: ' From 1bbeb12e2e8acc47b94c23d6a17332fb45c2a97e Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 19 Sep 2013 11:27:13 +0200 Subject: [PATCH 090/150] Updated method names and added a few more tests. --- lib/public/itags.php | 6 ++--- lib/tags.php | 12 ++++----- tests/lib/tags.php | 59 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/lib/public/itags.php b/lib/public/itags.php index 047d4f5f40..1264340054 100644 --- a/lib/public/itags.php +++ b/lib/public/itags.php @@ -65,7 +65,7 @@ interface ITags { * * @returns array */ - public function tags(); + public function getTags(); /** * Get the a list if items tagged with $tag. @@ -75,7 +75,7 @@ interface ITags { * @param string|integer $tag Tag id or name. * @return array An array of object ids or false on error. */ - public function idsForTag($tag); + public function getIdsForTag($tag); /** * Checks whether a tag is already saved. @@ -111,7 +111,7 @@ interface ITags { * @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); + public function addMultiple($names, $sync=false, $id = null); /** * Delete tag/object relations from the db diff --git a/lib/tags.php b/lib/tags.php index 3320d9ea1a..2eaa603c1a 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -96,7 +96,7 @@ class Tags implements \OCP\ITags { } if(count($defaultTags) > 0 && count($this->tags) === 0) { - $this->addMulti($defaultTags, true); + $this->addMultiple($defaultTags, true); } \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true), \OCP\Util::DEBUG); @@ -119,7 +119,7 @@ class Tags implements \OCP\ITags { \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; } - return ($result->numRows() === 0); + return ((int)$result->numRows() === 0); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); @@ -138,7 +138,7 @@ class Tags implements \OCP\ITags { * * @return array */ - public function tags() { + public function getTags() { if(!count($this->tags)) { return array(); } @@ -167,7 +167,7 @@ class Tags implements \OCP\ITags { * @param string|integer $tag Tag id or name. * @return array An array of object ids or false on error. */ - public function idsForTag($tag) { + public function getIdsForTag($tag) { $result = null; if(is_numeric($tag)) { $tagId = $tag; @@ -293,7 +293,7 @@ class Tags implements \OCP\ITags { * @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) { + public function addMultiple($names, $sync=false, $id = null) { if(!is_array($names)) { $names = array($names); } @@ -456,7 +456,7 @@ class Tags implements \OCP\ITags { */ public function getFavorites() { try { - return $this->idsForTag(self::TAG_FAVORITE); + return $this->getIdsForTag(self::TAG_FAVORITE); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: ' . $e->getMessage(), \OCP\Util::ERROR); diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 06baebc0af..16a03f5645 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -48,7 +48,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr = new OC\Tags($this->user); $tagMgr->loadTagsFor($this->objectType, $defaultTags); - $this->assertEquals(4, count($tagMgr->tags())); + $this->assertEquals(4, count($tagMgr->getTags())); } public function testAddTags() { @@ -65,7 +65,37 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertFalse($tagMgr->add('Family')); $this->assertFalse($tagMgr->add('fAMILY')); - $this->assertEquals(4, count($tagMgr->tags())); + $this->assertEquals(4, count($tagMgr->getTags())); + } + + public function testAddMultiple() { + $tags = array('Friends', 'Family', 'Work', 'Other'); + + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + foreach($tags as $tag) { + $this->assertFalse($tagMgr->hasTag($tag)); + } + + $result = $tagMgr->addMultiple($tags); + $this->assertTrue((bool)$result); + + foreach($tags as $tag) { + $this->assertTrue($tagMgr->hasTag($tag)); + } + + $this->assertEquals(4, count($tagMgr->getTags())); + } + + public function testIsEmpty() { + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + + $this->assertEquals(0, count($tagMgr->getTags())); + $this->assertTrue($tagMgr->isEmpty()); + $tagMgr->add('Tag'); + $this->assertFalse($tagMgr->isEmpty()); } public function testdeleteTags() { @@ -73,13 +103,13 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr = new OC\Tags($this->user); $tagMgr->loadTagsFor($this->objectType, $defaultTags); - $this->assertEquals(4, count($tagMgr->tags())); + $this->assertEquals(4, count($tagMgr->getTags())); $tagMgr->delete('family'); - $this->assertEquals(3, count($tagMgr->tags())); + $this->assertEquals(3, count($tagMgr->getTags())); $tagMgr->delete(array('Friends', 'Work', 'Other')); - $this->assertEquals(0, count($tagMgr->tags())); + $this->assertEquals(0, count($tagMgr->getTags())); } @@ -105,8 +135,8 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr->tagAs($id, 'Family'); } - $this->assertEquals(1, count($tagMgr->tags())); - $this->assertEquals(9, count($tagMgr->idsForTag('Family'))); + $this->assertEquals(1, count($tagMgr->getTags())); + $this->assertEquals(9, count($tagMgr->getIdsForTag('Family'))); } /** @@ -121,13 +151,20 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagMgr->loadTagsFor($this->objectType); foreach($objIds as $id) { - $this->assertTrue(in_array($id, $tagMgr->idsForTag('Family'))); + $this->assertTrue(in_array($id, $tagMgr->getIdsForTag('Family'))); $tagMgr->unTag($id, 'Family'); - $this->assertFalse(in_array($id, $tagMgr->idsForTag('Family'))); + $this->assertFalse(in_array($id, $tagMgr->getIdsForTag('Family'))); } - $this->assertEquals(1, count($tagMgr->tags())); - $this->assertEquals(0, count($tagMgr->idsForTag('Family'))); + $this->assertEquals(1, count($tagMgr->getTags())); + $this->assertEquals(0, count($tagMgr->getIdsForTag('Family'))); + } + + public function testFavorite() { + $tagMgr = new OC\Tags($this->user); + $tagMgr->loadTagsFor($this->objectType); + $this->assertTrue($tagMgr->addToFavorites(1)); + $this->assertTrue($tagMgr->removeFromFavorites(1)); } } From 8fab9eef28d2146c2c65f7e7f1aa826216915301 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Thu, 19 Sep 2013 13:55:45 +0200 Subject: [PATCH 091/150] Add another test. --- tests/lib/tags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 16a03f5645..92a96a1477 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -94,7 +94,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertEquals(0, count($tagMgr->getTags())); $this->assertTrue($tagMgr->isEmpty()); - $tagMgr->add('Tag'); + $this->assertNotEquals(false, $tagMgr->add('Tag')); $this->assertFalse($tagMgr->isEmpty()); } From 8a02afd87ae1e9a8d223f20ca2df35145a16ce74 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 13:27:43 +0200 Subject: [PATCH 092/150] Added more error checking in add() --- lib/tags.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/tags.php b/lib/tags.php index 2eaa603c1a..e2e1a83dd9 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -233,17 +233,25 @@ class Tags implements \OCP\ITags { return false; } try { - \OCP\DB::insertIfNotExist(self::TAG_TABLE, + $result = \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); + ) + ); + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; + } elseif((int)$result === 0) { + \OCP\Util::writeLog('core', __METHOD__.', Tag already exists: ' . $name, \OCP\Util::DEBUG); } + } 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; From be402fab53bb4eb3ce027ef9d6edb089e356a820 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 13:29:21 +0200 Subject: [PATCH 093/150] Forgot to return false if add() didn't add anything. --- lib/tags.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tags.php b/lib/tags.php index e2e1a83dd9..955eb3cd36 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -246,6 +246,7 @@ class Tags implements \OCP\ITags { return false; } elseif((int)$result === 0) { \OCP\Util::writeLog('core', __METHOD__.', Tag already exists: ' . $name, \OCP\Util::DEBUG); + return false; } } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), From 60bff6c5896c22b31ee7864fa48026a1de5ce3fb Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 15:52:06 +0200 Subject: [PATCH 094/150] Use fetchOne() instead of numRows() when doing a COUNT(*). --- lib/tags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tags.php b/lib/tags.php index 955eb3cd36..ff9f35ebc9 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -119,7 +119,7 @@ class Tags implements \OCP\ITags { \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); return false; } - return ((int)$result->numRows() === 0); + return ((int)$result->fetchOne() === 0); } catch(\Exception $e) { \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR); From f022ea752ddd86feccaaf5f1f83f808fd6df5e20 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 23 Sep 2013 15:52:50 +0200 Subject: [PATCH 095/150] Moar tests. --- tests/lib/tags.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 92a96a1477..75db9f50f7 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -59,13 +59,14 @@ class Test_Tags extends PHPUnit_Framework_TestCase { foreach($tags as $tag) { $result = $tagMgr->add($tag); + $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0'); $this->assertTrue((bool)$result); } $this->assertFalse($tagMgr->add('Family')); $this->assertFalse($tagMgr->add('fAMILY')); - $this->assertEquals(4, count($tagMgr->getTags())); + $this->assertCount(4, $tagMgr->getTags(), 'Wrong number of added tags'); } public function testAddMultiple() { @@ -85,7 +86,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertTrue($tagMgr->hasTag($tag)); } - $this->assertEquals(4, count($tagMgr->getTags())); + $this->assertCount(4, $tagMgr->getTags(), 'Not all tags added'); } public function testIsEmpty() { @@ -94,7 +95,10 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->assertEquals(0, count($tagMgr->getTags())); $this->assertTrue($tagMgr->isEmpty()); - $this->assertNotEquals(false, $tagMgr->add('Tag')); + + $result = $tagMgr->add('Tag'); + $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0'); + $this->assertNotEquals(false, $result, 'add() returned false'); $this->assertFalse($tagMgr->isEmpty()); } From 4d3e7fa78a3c4692a7dc8587dd6e126866fc9870 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 12:34:10 +0200 Subject: [PATCH 096/150] Add getUserFolder/getAppFolder to Server. --- lib/public/iservercontainer.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 4478a4e8a6..dcf8c162e6 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -86,6 +86,20 @@ interface IServerContainer { */ function getCache(); + /** + * 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 * From f2de5a34eff78add366b7a89c93a8128a097996f Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 18 Sep 2013 14:25:12 +0200 Subject: [PATCH 097/150] Don't try to be clever --- lib/server.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/server.php b/lib/server.php index 804af6b0ea..dcfd0a2db9 100644 --- a/lib/server.php +++ b/lib/server.php @@ -148,6 +148,42 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('RootFolder'); } + /** + * Returns a view to ownCloud's files folder + * + * @return \OCP\Files\Folder + */ + function getUserFolder() { + + $dir = '/files'; + $root = $this->getRootFolder(); + $folder = null; + if(!$root->nodeExists($dir)) { + $folder = $root->newFolder($dir); + } else { + $folder = $root->get($dir); + } + return $folder; + } + + /** + * Returns an app-specific view in ownClouds data directory + * + * @return \OCP\Files\Folder + */ + function getAppFolder() { + + $dir = '/' . \OC_App::getCurrentApp(); + $root = $this->getRootFolder(); + $folder = null; + if(!$root->nodeExists($dir)) { + $folder = $root->newFolder($dir); + } else { + $folder = $root->get($dir); + } + return $folder; + } + /** * @return \OC\User\Manager */ From 8c469394e61b0f13c4e111a7804e27273cf62458 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 24 Sep 2013 00:12:23 +0200 Subject: [PATCH 098/150] Remove duplicate method definitions --- lib/public/iservercontainer.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index d3ee5d15dc..6d29132195 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -75,6 +75,7 @@ interface IServerContainer { * @return \OCP\Files\Folder */ function getAppFolder(); + /** * Returns the user session * @@ -99,20 +100,6 @@ interface IServerContainer { */ function getCache(); - /** - * 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 * From 235517f111a6d570e43cff1cd3701553412fc1a3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 19 Sep 2013 21:37:52 +0200 Subject: [PATCH 099/150] clear permissions cache when scanning a file --- lib/files/cache/scanner.php | 15 ++++++++++++--- tests/lib/files/cache/permissions.php | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index a986c1ca72..af819c47c6 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -36,6 +36,11 @@ class Scanner extends BasicEmitter { */ private $cache; + /** + * @var \OC\Files\Cache\Permissions $permissionsCache + */ + private $permissionsCache; + const SCAN_RECURSIVE = true; const SCAN_SHALLOW = false; @@ -46,6 +51,7 @@ class Scanner extends BasicEmitter { $this->storage = $storage; $this->storageId = $this->storage->getId(); $this->cache = $storage->getCache(); + $this->permissionsCache = $storage->getPermissionsCache(); } /** @@ -96,7 +102,11 @@ class Scanner extends BasicEmitter { } } $newData = $data; - if ($reuseExisting and $cacheData = $this->cache->get($file)) { + $cacheData = $this->cache->get($file); + if ($cacheData) { + $this->permissionsCache->remove($cacheData['fileid']); + } + if ($reuseExisting and $cacheData) { // prevent empty etag $etag = $cacheData['etag']; $propagateETagChange = false; @@ -104,7 +114,6 @@ class Scanner extends BasicEmitter { $etag = $data['etag']; $propagateETagChange = true; } - // only reuse data if the file hasn't explicitly changed if (isset($data['mtime']) && isset($cacheData['mtime']) && $data['mtime'] === $cacheData['mtime']) { if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { @@ -182,7 +191,7 @@ class Scanner extends BasicEmitter { $newChildren = array(); if ($this->storage->is_dir($path) && ($dh = $this->storage->opendir($path))) { \OC_DB::beginTransaction(); - if(is_resource($dh)) { + if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { $child = ($path) ? $path . '/' . $file : $file; if (!Filesystem::isIgnoredDir($file)) { diff --git a/tests/lib/files/cache/permissions.php b/tests/lib/files/cache/permissions.php index 7e6e11e2eb..4b284c2c8e 100644 --- a/tests/lib/files/cache/permissions.php +++ b/tests/lib/files/cache/permissions.php @@ -8,6 +8,8 @@ namespace Test\Files\Cache; +use OC\Files\Storage\Temporary; + class Permissions extends \PHPUnit_Framework_TestCase { /*** * @var \OC\Files\Cache\Permissions $permissionsCache @@ -55,4 +57,19 @@ class Permissions extends \PHPUnit_Framework_TestCase { $this->permissionsCache->removeMultiple($ids, $user); } + + public function testUpdatePermissionsOnRescan() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $permissionsCache = $storage->getPermissionsCache(); + + $storage->file_put_contents('foo.txt', 'bar'); + $scanner->scan(''); + $id = $cache->getId('foo.txt'); + $permissionsCache->set($id, 'test', 1); + + $scanner->scan(''); + $this->assertEquals(-1, $permissionsCache->get($id, 'test')); + } } From 21299745846eaa87988dcc5acd6f5604b363b03e Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Tue, 24 Sep 2013 00:59:23 +0200 Subject: [PATCH 100/150] Do not recheck $cacheData. Move if($reuseExisting) under if($cacheData). --- lib/files/cache/scanner.php | 54 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index af819c47c6..96f84609cf 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -105,39 +105,39 @@ class Scanner extends BasicEmitter { $cacheData = $this->cache->get($file); if ($cacheData) { $this->permissionsCache->remove($cacheData['fileid']); - } - if ($reuseExisting and $cacheData) { - // prevent empty etag - $etag = $cacheData['etag']; - $propagateETagChange = false; - if (empty($etag)) { - $etag = $data['etag']; - $propagateETagChange = true; - } - // only reuse data if the file hasn't explicitly changed - if (isset($data['mtime']) && isset($cacheData['mtime']) && $data['mtime'] === $cacheData['mtime']) { - if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { - $data['size'] = $cacheData['size']; + if ($reuseExisting) { + // prevent empty etag + $etag = $cacheData['etag']; + $propagateETagChange = false; + if (empty($etag)) { + $etag = $data['etag']; + $propagateETagChange = true; } - if ($reuseExisting & self::REUSE_ETAG) { - $data['etag'] = $etag; - if ($propagateETagChange) { - $parent = $file; - while ($parent !== '') { - $parent = dirname($parent); - if ($parent === '.') { - $parent = ''; + // only reuse data if the file hasn't explicitly changed + if (isset($data['mtime']) && isset($cacheData['mtime']) && $data['mtime'] === $cacheData['mtime']) { + if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { + $data['size'] = $cacheData['size']; + } + if ($reuseExisting & self::REUSE_ETAG) { + $data['etag'] = $etag; + if ($propagateETagChange) { + $parent = $file; + while ($parent !== '') { + $parent = dirname($parent); + if ($parent === '.') { + $parent = ''; + } + $parentCacheData = $this->cache->get($parent); + $this->cache->update($parentCacheData['fileid'], array( + 'etag' => $this->storage->getETag($parent), + )); } - $parentCacheData = $this->cache->get($parent); - $this->cache->update($parentCacheData['fileid'], array( - 'etag' => $this->storage->getETag($parent), - )); } } } + // Only update metadata that has changed + $newData = array_diff($data, $cacheData); } - // Only update metadata that has changed - $newData = array_diff($data, $cacheData); } if (!empty($newData)) { $this->cache->put($file, $newData); From 00c998c5bb611ea775f2b1a294ef4f2029a52c89 Mon Sep 17 00:00:00 2001 From: Boris Rybalkin Date: Mon, 23 Sep 2013 21:08:58 -0400 Subject: [PATCH 101/150] fixing typo Typo in comment "feature" => "future" --- cron.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cron.php b/cron.php index d39800c884..8e1a3376d5 100644 --- a/cron.php +++ b/cron.php @@ -79,7 +79,7 @@ try { // We call ownCloud from the CLI (aka cron) if ($appmode != 'cron') { - // Use cron in feature! + // Use cron in future! OC_BackgroundJob::setExecutionType('cron'); } From cd2421c7ee04e6d46481a1d0ae36387757b204fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 24 Sep 2013 10:37:58 +0200 Subject: [PATCH 102/150] adding PHPDoc comments to getBackend ensure getChildren() is called on an instance of Share_Backend_Collection --- apps/files_sharing/lib/cache.php | 15 ++++++++++----- lib/public/share.php | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 51e8713b97..123268e240 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -20,6 +20,7 @@ */ namespace OC\Files\Cache; +use OCP\Share_Backend_Collection; /** * Metadata cache for shared files @@ -320,13 +321,17 @@ class Shared_Cache extends Cache { public function getAll() { $ids = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); $folderBackend = \OCP\Share::getBackend('folder'); - foreach ($ids as $file) { - $children = $folderBackend->getChildren($file); - foreach ($children as $child) { - $ids[] = (int)$child['source']; + if ($folderBackend instanceof Share_Backend_Collection) { + foreach ($ids as $file) { + /** @var $folderBackend Share_Backend_Collection */ + $children = $folderBackend->getChildren($file); + foreach ($children as $child) { + $ids[] = (int)$child['source']; + } + } - } + return $ids; } diff --git a/lib/public/share.php b/lib/public/share.php index 10922965ea..41f5ccbf40 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -745,8 +745,8 @@ class Share { /** * @brief Get the backend class for the specified item type - * @param string Item type - * @return Sharing backend object + * @param string $itemType + * @return Share_Backend */ public static function getBackend($itemType) { if (isset(self::$backends[$itemType])) { From 31d2048eb83d60007183cec43a54d7a112d7a3b2 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Tue, 24 Sep 2013 11:00:08 +0200 Subject: [PATCH 103/150] add blacklist to txt preview backend --- lib/preview/txt.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/preview/txt.php b/lib/preview/txt.php index a487330691..77e728eb36 100644 --- a/lib/preview/txt.php +++ b/lib/preview/txt.php @@ -9,11 +9,21 @@ namespace OC\Preview; class TXT extends Provider { + private static $blacklist = array( + 'text/calendar', + 'text/vcard', + ); + public function getMimeType() { return '/text\/.*/'; } public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $mimetype = $fileview->getMimeType($path); + if(in_array($mimetype, self::$blacklist)) { + return false; + } + $content = $fileview->fopen($path, 'r'); $content = stream_get_contents($content); From ee1f627155cad4153f3da3160ca6040c137841d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 24 Sep 2013 13:26:12 +0200 Subject: [PATCH 104/150] adding privilege check on move and rename operations --- lib/connector/sabre/node.php | 11 +++++++++++ lib/connector/sabre/objecttree.php | 24 +++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index 0bffa58af7..29b7f9e53a 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -78,6 +78,11 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr */ public function setName($name) { + // rename is only allowed if the update privilege is granted + if (!\OC\Files\Filesystem::isUpdatable($this->path)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); @@ -135,6 +140,12 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * Even if the modification time is set to a custom value the access time is set to now. */ public function touch($mtime) { + + // touch is only allowed if the update privilege is granted + if (!\OC\Files\Filesystem::isUpdatable($this->path)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + \OC\Files\Filesystem::touch($this->path, $mtime); } diff --git a/lib/connector/sabre/objecttree.php b/lib/connector/sabre/objecttree.php index acff45ed5e..7accf98c8e 100644 --- a/lib/connector/sabre/objecttree.php +++ b/lib/connector/sabre/objecttree.php @@ -64,7 +64,29 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($sourceDir,) = \Sabre_DAV_URLUtil::splitPath($sourcePath); list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destinationPath); - Filesystem::rename($sourcePath, $destinationPath); + // check update privileges + if ($sourceDir === $destinationDir) { + // for renaming it's enough to check if the sourcePath can be updated + if (!\OC\Files\Filesystem::isUpdatable($sourcePath)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + } else { + // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir + if (!\OC\Files\Filesystem::isUpdatable($sourcePath)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + if (!\OC\Files\Filesystem::isUpdatable($sourceDir)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + if (!\OC\Files\Filesystem::isUpdatable($destinationDir)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + } + + $renameOkay = Filesystem::rename($sourcePath, $destinationPath); + if (!$renameOkay) { + throw new \Sabre_DAV_Exception_Forbidden(''); + } $this->markDirty($sourceDir); $this->markDirty($destinationDir); From 52f1d5856dfaa9186a05d529674c2dbab9cc90b7 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Tue, 24 Sep 2013 13:26:51 +0200 Subject: [PATCH 105/150] add test data for cal and contact preview --- tests/data/testcal.ics | 13 +++++++++++++ tests/data/testcontact.vcf | 6 ++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/data/testcal.ics create mode 100644 tests/data/testcontact.vcf diff --git a/tests/data/testcal.ics b/tests/data/testcal.ics new file mode 100644 index 0000000000..e05f01ba1c --- /dev/null +++ b/tests/data/testcal.ics @@ -0,0 +1,13 @@ +BEGIN:VCALENDAR +PRODID:-//some random cal software//EN +VERSION:2.0 +BEGIN:VEVENT +CREATED:20130102T120000Z +LAST-MODIFIED:20130102T120000Z +DTSTAMP:20130102T120000Z +UID:f106ecdf-c716-43ef-9d94-4e6f19f2fcfb +SUMMARY:a test cal file +DTSTART;VALUE=DATE:20130101 +DTEND;VALUE=DATE:20130102 +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/tests/data/testcontact.vcf b/tests/data/testcontact.vcf new file mode 100644 index 0000000000..2af963d691 --- /dev/null +++ b/tests/data/testcontact.vcf @@ -0,0 +1,6 @@ +BEGIN:VCARD +VERSION:3.0 +PRODID:-//some random contact software//EN +N:def;abc;;; +FN:abc def +END:VCARD \ No newline at end of file From d101ff42f16ef7288b40666eba20c69621481ea4 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 16 Sep 2013 14:15:35 +0200 Subject: [PATCH 106/150] User: move checkPassword from User to Manager to not break API --- lib/public/user.php | 2 +- lib/user.php | 14 +++++--------- lib/user/http.php | 6 +++++- lib/user/manager.php | 17 +++++++++++++++++ lib/user/session.php | 19 ++++++++++--------- lib/user/user.php | 18 ------------------ tests/lib/user/session.php | 32 ++++++++++---------------------- 7 files changed, 48 insertions(+), 60 deletions(-) diff --git a/lib/public/user.php b/lib/public/user.php index 23ff991642..576a64d704 100644 --- a/lib/public/user.php +++ b/lib/public/user.php @@ -102,7 +102,7 @@ class User { * @brief Check if the password is correct * @param $uid The username * @param $password The password - * @returns true/false + * @returns mixed username on success, false otherwise * * Check if the password is correct without logging in the user */ diff --git a/lib/user.php b/lib/user.php index 0f6f40aec9..8868428ce2 100644 --- a/lib/user.php +++ b/lib/user.php @@ -416,16 +416,12 @@ class OC_User { * returns the user id or false */ public static function checkPassword($uid, $password) { - $user = self::getManager()->get($uid); - if ($user) { - if ($user->checkPassword($password)) { - return $user->getUID(); - } else { - return false; - } - } else { - return false; + $manager = self::getManager(); + $username = $manager->checkPassword($uid, $password); + if ($username !== false) { + return $manger->get($username); } + return false; } /** diff --git a/lib/user/http.php b/lib/user/http.php index 1e044ed418..ea14cb57c9 100644 --- a/lib/user/http.php +++ b/lib/user/http.php @@ -79,7 +79,11 @@ class OC_User_HTTP extends OC_User_Backend { curl_close($ch); - return $status==200; + if($status == 200) { + return $uid; + } + + return false; } /** diff --git a/lib/user/manager.php b/lib/user/manager.php index 8dc9bfe272..2de694a3d9 100644 --- a/lib/user/manager.php +++ b/lib/user/manager.php @@ -118,6 +118,23 @@ class Manager extends PublicEmitter { return ($user !== null); } + /** + * Check if the password is valid for the user + * + * @param $loginname + * @param $password + * @return mixed the User object on success, false otherwise + */ + public function checkPassword($loginname, $password) { + foreach ($this->backends as $backend) { + $uid = $backend->checkPassword($loginname, $password); + if ($uid !== false) { + return $this->getUserObject($uid, $backend); + } + } + return null; + } + /** * search by user id * diff --git a/lib/user/session.php b/lib/user/session.php index 9a6c669e93..b5e9385234 100644 --- a/lib/user/session.php +++ b/lib/user/session.php @@ -121,15 +121,16 @@ class Session implements Emitter { */ public function login($uid, $password) { $this->manager->emit('\OC\User', 'preLogin', array($uid, $password)); - $user = $this->manager->get($uid); - if ($user) { - $result = $user->checkPassword($password); - if ($result and $user->isEnabled()) { - $this->setUser($user); - $this->manager->emit('\OC\User', 'postLogin', array($user, $password)); - return true; - } else { - return false; + $user = $this->manager->checkPassword($uid, $password); + if($user !== false) { + if (!is_null($user)) { + if ($user->isEnabled()) { + $this->setUser($user); + $this->manager->emit('\OC\User', 'postLogin', array($user, $password)); + return true; + } else { + return false; + } } } else { return false; diff --git a/lib/user/user.php b/lib/user/user.php index 8115c43198..e5f842944f 100644 --- a/lib/user/user.php +++ b/lib/user/user.php @@ -105,24 +105,6 @@ class User { return !($result === false); } - /** - * Check if the password is valid for the user - * - * @param $password - * @return bool - */ - public function checkPassword($password) { - if ($this->backend->implementsActions(\OC_USER_BACKEND_CHECK_PASSWORD)) { - $result = $this->backend->checkPassword($this->uid, $password); - if ($result !== false) { - $this->uid = $result; - } - return !($result === false); - } else { - return false; - } - } - /** * Set the password of the user * diff --git a/tests/lib/user/session.php b/tests/lib/user/session.php index 274e9e2831..e457a7bda3 100644 --- a/tests/lib/user/session.php +++ b/tests/lib/user/session.php @@ -61,10 +61,6 @@ class Session extends \PHPUnit_Framework_TestCase { $backend = $this->getMock('OC_User_Dummy'); $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); - $user->expects($this->once()) - ->method('checkPassword') - ->with('bar') - ->will($this->returnValue(true)); $user->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(true)); @@ -73,8 +69,8 @@ class Session extends \PHPUnit_Framework_TestCase { ->will($this->returnValue('foo')); $manager->expects($this->once()) - ->method('get') - ->with('foo') + ->method('checkPassword') + ->with('foo', 'bar') ->will($this->returnValue($user)); $userSession = new \OC\User\Session($manager, $session); @@ -92,17 +88,13 @@ class Session extends \PHPUnit_Framework_TestCase { $backend = $this->getMock('OC_User_Dummy'); $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); - $user->expects($this->once()) - ->method('checkPassword') - ->with('bar') - ->will($this->returnValue(true)); $user->expects($this->once()) ->method('isEnabled') ->will($this->returnValue(false)); $manager->expects($this->once()) - ->method('get') - ->with('foo') + ->method('checkPassword') + ->with('foo', 'bar') ->will($this->returnValue($user)); $userSession = new \OC\User\Session($manager, $session); @@ -119,17 +111,13 @@ class Session extends \PHPUnit_Framework_TestCase { $backend = $this->getMock('OC_User_Dummy'); $user = $this->getMock('\OC\User\User', array(), array('foo', $backend)); - $user->expects($this->once()) - ->method('checkPassword') - ->with('bar') - ->will($this->returnValue(false)); $user->expects($this->never()) ->method('isEnabled'); $manager->expects($this->once()) - ->method('get') - ->with('foo') - ->will($this->returnValue($user)); + ->method('checkPassword') + ->with('foo', 'bar') + ->will($this->returnValue(false)); $userSession = new \OC\User\Session($manager, $session); $userSession->login('foo', 'bar'); @@ -145,9 +133,9 @@ class Session extends \PHPUnit_Framework_TestCase { $backend = $this->getMock('OC_User_Dummy'); $manager->expects($this->once()) - ->method('get') - ->with('foo') - ->will($this->returnValue(null)); + ->method('checkPassword') + ->with('foo', 'bar') + ->will($this->returnValue(false)); $userSession = new \OC\User\Session($manager, $session); $userSession->login('foo', 'bar'); From fe88a62d6e9ee4bb138ac4fb0fe81d8fbe082e09 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 24 Sep 2013 13:51:33 +0200 Subject: [PATCH 107/150] === not == --- lib/user/http.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/user/http.php b/lib/user/http.php index ea14cb57c9..e99afe59ba 100644 --- a/lib/user/http.php +++ b/lib/user/http.php @@ -79,7 +79,7 @@ class OC_User_HTTP extends OC_User_Backend { curl_close($ch); - if($status == 200) { + if($status === 200) { return $uid; } From 6c5466a540340a22e487d71a2605dcda3498a658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 24 Sep 2013 13:53:32 +0200 Subject: [PATCH 108/150] adding file_exists check just to be on the save side --- lib/connector/sabre/directory.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 9e0fe5e64e..a50098df79 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -89,7 +89,8 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa // rename to correct path $renameOkay = \OC\Files\Filesystem::rename($partpath, $newPath); - if (!$renameOkay) { + $fileExists = \OC\Files\Filesystem::file_exists($newPath); + if ($renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); \OC\Files\Filesystem::unlink($partpath); throw new Sabre_DAV_Exception(); From e9eb34f1872a0237b7474496e8c759a4a3ef1156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 24 Sep 2013 13:54:18 +0200 Subject: [PATCH 109/150] duplicate code :sigh: - will fix this in a second pr --- lib/connector/sabre/file.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php index 61bdcd5e0a..433b114855 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -74,7 +74,14 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D } // rename to correct path - \OC\Files\Filesystem::rename($partpath, $this->path); + $renameOkay = \OC\Files\Filesystem::rename($partpath, $this->path); + $fileExists = \OC\Files\Filesystem::file_exists($this->path); + if ($renameOkay === false || $fileExists === false) { + \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); + \OC\Files\Filesystem::unlink($partpath); + throw new Sabre_DAV_Exception(); + } + //allow sync clients to send the mtime along in a header $mtime = OC_Request::hasModificationTime(); From 0a7ee7c3f7b6825815a5aae1e41725481dbbfb08 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 24 Sep 2013 14:11:47 +0200 Subject: [PATCH 110/150] Fix return value from User object to User ID --- lib/user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/user.php b/lib/user.php index 8868428ce2..ba6036bad9 100644 --- a/lib/user.php +++ b/lib/user.php @@ -419,7 +419,7 @@ class OC_User { $manager = self::getManager(); $username = $manager->checkPassword($uid, $password); if ($username !== false) { - return $manger->get($username); + return $manager->get($username)->getUID(); } return false; } From 63324e23472071f34746e2f6132a6246babe7e74 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 24 Sep 2013 14:12:44 +0200 Subject: [PATCH 111/150] Fix doc --- lib/user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/user.php b/lib/user.php index ba6036bad9..da774ff86f 100644 --- a/lib/user.php +++ b/lib/user.php @@ -410,7 +410,7 @@ class OC_User { * @brief Check if the password is correct * @param string $uid The username * @param string $password The password - * @return bool + * @return mixed user id a string on success, false otherwise * * Check if the password is correct without logging in the user * returns the user id or false From 14a160e176135cb86191eed46b4cc3b3b0e58f44 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 24 Sep 2013 17:10:01 +0200 Subject: [PATCH 112/150] Adjust Tests and satisfy them --- lib/user/manager.php | 10 +++++---- tests/lib/user/manager.php | 45 ++++++++++++++++++++++++++++++++++++++ tests/lib/user/user.php | 40 --------------------------------- 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/lib/user/manager.php b/lib/user/manager.php index 2de694a3d9..13286bc28a 100644 --- a/lib/user/manager.php +++ b/lib/user/manager.php @@ -127,12 +127,14 @@ class Manager extends PublicEmitter { */ public function checkPassword($loginname, $password) { foreach ($this->backends as $backend) { - $uid = $backend->checkPassword($loginname, $password); - if ($uid !== false) { - return $this->getUserObject($uid, $backend); + if($backend->implementsActions(\OC_USER_BACKEND_CHECK_PASSWORD)) { + $uid = $backend->checkPassword($loginname, $password); + if ($uid !== false) { + return $this->getUserObject($uid, $backend); + } } } - return null; + return false; } /** diff --git a/tests/lib/user/manager.php b/tests/lib/user/manager.php index bc49f6db4b..00901dd411 100644 --- a/tests/lib/user/manager.php +++ b/tests/lib/user/manager.php @@ -98,6 +98,51 @@ class Manager extends \PHPUnit_Framework_TestCase { $this->assertTrue($manager->userExists('foo')); } + public function testCheckPassword() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('checkPassword') + ->with($this->equalTo('foo'), $this->equalTo('bar')) + ->will($this->returnValue(true)); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_CHECK_PASSWORD) { + return true; + } else { + return false; + } + })); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $user = $manager->checkPassword('foo', 'bar'); + $this->assertTrue($user instanceof \OC\User\User); + } + + public function testCheckPasswordNotSupported() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->never()) + ->method('checkPassword'); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnValue(false)); + + $manager = new \OC\User\Manager(); + $manager->registerBackend($backend); + + $this->assertFalse($manager->checkPassword('foo', 'bar')); + } + public function testGetOneBackendExists() { /** * @var \OC_User_Dummy | \PHPUnit_Framework_MockObject_MockObject $backend diff --git a/tests/lib/user/user.php b/tests/lib/user/user.php index b0d170cbfc..de5ccbf38c 100644 --- a/tests/lib/user/user.php +++ b/tests/lib/user/user.php @@ -100,46 +100,6 @@ class User extends \PHPUnit_Framework_TestCase { $this->assertTrue($user->delete()); } - public function testCheckPassword() { - /** - * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend - */ - $backend = $this->getMock('\OC_User_Dummy'); - $backend->expects($this->once()) - ->method('checkPassword') - ->with($this->equalTo('foo'), $this->equalTo('bar')) - ->will($this->returnValue(true)); - - $backend->expects($this->any()) - ->method('implementsActions') - ->will($this->returnCallback(function ($actions) { - if ($actions === \OC_USER_BACKEND_CHECK_PASSWORD) { - return true; - } else { - return false; - } - })); - - $user = new \OC\User\User('foo', $backend); - $this->assertTrue($user->checkPassword('bar')); - } - - public function testCheckPasswordNotSupported() { - /** - * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend - */ - $backend = $this->getMock('\OC_User_Dummy'); - $backend->expects($this->never()) - ->method('checkPassword'); - - $backend->expects($this->any()) - ->method('implementsActions') - ->will($this->returnValue(false)); - - $user = new \OC\User\User('foo', $backend); - $this->assertFalse($user->checkPassword('bar')); - } - public function testGetHome() { /** * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend From aaed871cee445633cb81f0aa230ba2f65c667803 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Tue, 24 Sep 2013 17:10:01 +0200 Subject: [PATCH 113/150] Add factory class for the server container. --- lib/public/iservercontainer.php | 3 +- lib/public/itagmanager.php | 48 +++++++++++++++++ lib/public/itags.php | 9 ---- lib/server.php | 3 +- lib/tagmanager.php | 68 ++++++++++++++++++++++++ lib/tags.php | 28 +++++++--- tests/lib/tags.php | 92 +++++++++++++++------------------ 7 files changed, 182 insertions(+), 69 deletions(-) create mode 100644 lib/public/itagmanager.php create mode 100644 lib/tagmanager.php diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 0df746a28e..f37bdb10f8 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -58,7 +58,8 @@ interface IServerContainer { /** * Returns the tag manager which can get and set tags for different object types * - * @return \OCP\ITags + * @see \OCP\ITagManager::load() + * @return \OCP\ITagManager */ function getTagManager(); diff --git a/lib/public/itagmanager.php b/lib/public/itagmanager.php new file mode 100644 index 0000000000..07e1d12fc0 --- /dev/null +++ b/lib/public/itagmanager.php @@ -0,0 +1,48 @@ + +* +* 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 . +* +*/ + +/** + * Factory class creating instances of \OCP\ITags + * + * 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 OCP; + +interface ITagManager { + + /** + * Create a new \OCP\ITags instance and load tags from db. + * + * @see \OCP\ITags + * @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 load($type, $defaultTags=array()); + +} \ No newline at end of file diff --git a/lib/public/itags.php b/lib/public/itags.php index 1264340054..5b1ebd189d 100644 --- a/lib/public/itags.php +++ b/lib/public/itags.php @@ -38,15 +38,6 @@ namespace OCP; 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. * diff --git a/lib/server.php b/lib/server.php index f3c79aa797..c47f0e2b46 100644 --- a/lib/server.php +++ b/lib/server.php @@ -146,7 +146,8 @@ class Server extends SimpleContainer implements IServerContainer { /** * Returns the tag manager which can get and set tags for different object types * - * @return \OCP\ITags + * @see \OCP\ITagManager::load() + * @return \OCP\ITagManager */ function getTagManager() { return $this->query('TagManager'); diff --git a/lib/tagmanager.php b/lib/tagmanager.php new file mode 100644 index 0000000000..9a371a1125 --- /dev/null +++ b/lib/tagmanager.php @@ -0,0 +1,68 @@ + +* +* 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 . +* +*/ + +/** + * Factory class creating instances of \OCP\ITags + * + * 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 TagManager implements \OCP\ITagManager { + + /** + * User + * + * @var string + */ + private $user = null; + + /** + * Constructor. + * + * @param string $user The user whos data the object will operate on. + */ + public function __construct($user) { + + $this->user = $user; + + } + + /** + * Create a new \OCP\ITags instance and load tags from db. + * + * @see \OCP\ITags + * @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 load($type, $defaultTags=array()) { + return new Tags($this->user, $type, $defaultTags); + } + +} \ No newline at end of file diff --git a/lib/tags.php b/lib/tags.php index ff9f35ebc9..9fdb35a7d6 100644 --- a/lib/tags.php +++ b/lib/tags.php @@ -38,15 +38,30 @@ class Tags implements \OCP\ITags { /** * Tags + * + * @var array */ private $tags = array(); /** * Used for storing objectid/categoryname pairs while rescanning. + * + * @var array */ private static $relations = array(); + /** + * Type + * + * @var string + */ private $type = null; + + /** + * User + * + * @var string + */ private $user = null; const TAG_TABLE = '*PREFIX*vcategory'; @@ -59,10 +74,10 @@ class Tags implements \OCP\ITags { * * @param string $user The user whos data the object will operate on. */ - public function __construct($user) { - + public function __construct($user, $type, $defaultTags = array()) { $this->user = $user; - + $this->type = $type; + $this->loadTags($defaultTags); } /** @@ -70,10 +85,8 @@ class Tags implements \OCP\ITags { * * @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; + protected function loadTags($defaultTags=array()) { $this->tags = array(); $result = null; $sql = 'SELECT `id`, `category` FROM `' . self::TAG_TABLE . '` ' @@ -101,7 +114,6 @@ class Tags implements \OCP\ITags { \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true), \OCP\Util::DEBUG); - return $this; } /** @@ -345,7 +357,7 @@ class Tags implements \OCP\ITags { } } // reload tags to get the proper ids. - $this->loadTagsFor($this->type); + $this->loadTags(); // Loop through temporarily cached objectid/tagname pairs // and save relations. $tags = $this->tags; diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 75db9f50f7..97e3734cfd 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -34,6 +34,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $this->objectType = uniqid('type_'); OC_User::createUser($this->user, 'pass'); OC_User::setUserId($this->user); + $this->tagMgr = new OC\TagManager($this->user); } @@ -45,102 +46,95 @@ class Test_Tags extends PHPUnit_Framework_TestCase { public function testInstantiateWithDefaults() { $defaultTags = array('Friends', 'Family', 'Work', 'Other'); - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType, $defaultTags); + $tagger = $this->tagMgr->load($this->objectType, $defaultTags); - $this->assertEquals(4, count($tagMgr->getTags())); + $this->assertEquals(4, count($tagger->getTags())); } public function testAddTags() { $tags = array('Friends', 'Family', 'Work', 'Other'); - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType); + $tagger = $this->tagMgr->load($this->objectType); foreach($tags as $tag) { - $result = $tagMgr->add($tag); + $result = $tagger->add($tag); $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0'); $this->assertTrue((bool)$result); } - $this->assertFalse($tagMgr->add('Family')); - $this->assertFalse($tagMgr->add('fAMILY')); + $this->assertFalse($tagger->add('Family')); + $this->assertFalse($tagger->add('fAMILY')); - $this->assertCount(4, $tagMgr->getTags(), 'Wrong number of added tags'); + $this->assertCount(4, $tagger->getTags(), 'Wrong number of added tags'); } public function testAddMultiple() { $tags = array('Friends', 'Family', 'Work', 'Other'); - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType); + $tagger = $this->tagMgr->load($this->objectType); foreach($tags as $tag) { - $this->assertFalse($tagMgr->hasTag($tag)); + $this->assertFalse($tagger->hasTag($tag)); } - $result = $tagMgr->addMultiple($tags); + $result = $tagger->addMultiple($tags); $this->assertTrue((bool)$result); foreach($tags as $tag) { - $this->assertTrue($tagMgr->hasTag($tag)); + $this->assertTrue($tagger->hasTag($tag)); } - $this->assertCount(4, $tagMgr->getTags(), 'Not all tags added'); + $this->assertCount(4, $tagger->getTags(), 'Not all tags added'); } public function testIsEmpty() { - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType); + $tagger = $this->tagMgr->load($this->objectType); - $this->assertEquals(0, count($tagMgr->getTags())); - $this->assertTrue($tagMgr->isEmpty()); + $this->assertEquals(0, count($tagger->getTags())); + $this->assertTrue($tagger->isEmpty()); - $result = $tagMgr->add('Tag'); + $result = $tagger->add('Tag'); $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0'); $this->assertNotEquals(false, $result, 'add() returned false'); - $this->assertFalse($tagMgr->isEmpty()); + $this->assertFalse($tagger->isEmpty()); } public function testdeleteTags() { $defaultTags = array('Friends', 'Family', 'Work', 'Other'); - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType, $defaultTags); + $tagger = $this->tagMgr->load($this->objectType, $defaultTags); - $this->assertEquals(4, count($tagMgr->getTags())); + $this->assertEquals(4, count($tagger->getTags())); - $tagMgr->delete('family'); - $this->assertEquals(3, count($tagMgr->getTags())); + $tagger->delete('family'); + $this->assertEquals(3, count($tagger->getTags())); - $tagMgr->delete(array('Friends', 'Work', 'Other')); - $this->assertEquals(0, count($tagMgr->getTags())); + $tagger->delete(array('Friends', 'Work', 'Other')); + $this->assertEquals(0, count($tagger->getTags())); } public function testRenameTag() { $defaultTags = array('Friends', 'Family', 'Wrok', 'Other'); - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType, $defaultTags); + $tagger = $this->tagMgr->load($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')); + $this->assertTrue($tagger->rename('Wrok', 'Work')); + $this->assertTrue($tagger->hasTag('Work')); + $this->assertFalse($tagger->hastag('Wrok')); + $this->assertFalse($tagger->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); + $tagger = $this->tagMgr->load($this->objectType); foreach($objids as $id) { - $tagMgr->tagAs($id, 'Family'); + $tagger->tagAs($id, 'Family'); } - $this->assertEquals(1, count($tagMgr->getTags())); - $this->assertEquals(9, count($tagMgr->getIdsForTag('Family'))); + $this->assertEquals(1, count($tagger->getTags())); + $this->assertEquals(9, count($tagger->getIdsForTag('Family'))); } /** @@ -151,24 +145,22 @@ class Test_Tags extends PHPUnit_Framework_TestCase { // Is this "legal"? $this->testTagAs(); - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType); + $tagger = $this->tagMgr->load($this->objectType); foreach($objIds as $id) { - $this->assertTrue(in_array($id, $tagMgr->getIdsForTag('Family'))); - $tagMgr->unTag($id, 'Family'); - $this->assertFalse(in_array($id, $tagMgr->getIdsForTag('Family'))); + $this->assertTrue(in_array($id, $tagger->getIdsForTag('Family'))); + $tagger->unTag($id, 'Family'); + $this->assertFalse(in_array($id, $tagger->getIdsForTag('Family'))); } - $this->assertEquals(1, count($tagMgr->getTags())); - $this->assertEquals(0, count($tagMgr->getIdsForTag('Family'))); + $this->assertEquals(1, count($tagger->getTags())); + $this->assertEquals(0, count($tagger->getIdsForTag('Family'))); } public function testFavorite() { - $tagMgr = new OC\Tags($this->user); - $tagMgr->loadTagsFor($this->objectType); - $this->assertTrue($tagMgr->addToFavorites(1)); - $this->assertTrue($tagMgr->removeFromFavorites(1)); + $tagger = $this->tagMgr->load($this->objectType); + $this->assertTrue($tagger->addToFavorites(1)); + $this->assertTrue($tagger->removeFromFavorites(1)); } } From 2d12e52769a30ba37d5760b1194f613bcc71035b Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Tue, 24 Sep 2013 12:59:48 -0400 Subject: [PATCH 114/150] [tx-robot] updated from transifex --- apps/files/l10n/ca.php | 5 +++ apps/files/l10n/en_GB.php | 7 +++- apps/files/l10n/fr.php | 5 +++ apps/files/l10n/gl.php | 5 +++ apps/files/l10n/nn_NO.php | 5 +++ core/l10n/en_GB.php | 9 ++++- core/l10n/fr.php | 6 +++- core/l10n/gl.php | 9 ++++- core/l10n/nn_NO.php | 16 ++++++++- l10n/ca/files.po | 18 +++++----- l10n/da/settings.po | 34 +++++++++---------- l10n/en_GB/core.po | 38 ++++++++++----------- l10n/en_GB/files.po | 20 +++++------ l10n/fr/core.po | 32 +++++++++--------- l10n/fr/files.po | 18 +++++----- l10n/gl/core.po | 38 ++++++++++----------- l10n/gl/files.po | 18 +++++----- l10n/nn_NO/core.po | 52 ++++++++++++++--------------- l10n/nn_NO/files.po | 19 ++++++----- l10n/nn_NO/lib.po | 14 ++++---- l10n/nn_NO/settings.po | 52 ++++++++++++++--------------- l10n/templates/core.pot | 16 ++++----- l10n/templates/files.pot | 2 +- l10n/templates/files_encryption.pot | 2 +- l10n/templates/files_external.pot | 2 +- l10n/templates/files_sharing.pot | 2 +- l10n/templates/files_trashbin.pot | 2 +- l10n/templates/files_versions.pot | 2 +- l10n/templates/lib.pot | 2 +- l10n/templates/settings.pot | 20 +++++------ l10n/templates/user_ldap.pot | 2 +- l10n/templates/user_webdavauth.pot | 2 +- lib/l10n/nn_NO.php | 2 ++ settings/l10n/da.php | 5 +++ settings/l10n/nn_NO.php | 14 ++++++++ 35 files changed, 287 insertions(+), 208 deletions(-) diff --git a/apps/files/l10n/ca.php b/apps/files/l10n/ca.php index 8fd72ac0a6..5c2cade8d6 100644 --- a/apps/files/l10n/ca.php +++ b/apps/files/l10n/ca.php @@ -13,10 +13,14 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Falta un fitxer temporal", "Failed to write to disk" => "Ha fallat en escriure al disc", "Not enough storage available" => "No hi ha prou espai disponible", +"Upload failed. Could not get file info." => "La pujada ha fallat. No s'ha pogut obtenir informació del fitxer.", +"Upload failed. Could not find uploaded file" => "La pujada ha fallat. El fitxer pujat no s'ha trobat.", "Invalid directory." => "Directori no vàlid.", "Files" => "Fitxers", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "No es pot pujar {filename} perquè és una carpeta o té 0 bytes", "Not enough space available" => "No hi ha prou espai disponible", "Upload cancelled." => "La pujada s'ha cancel·lat.", +"Could not get result from server." => "No hi ha resposta del servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "Hi ha una pujada en curs. Si abandoneu la pàgina la pujada es cancel·larà.", "URL cannot be empty." => "La URL no pot ser buida", "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nom de carpeta no vàlid. L'ús de 'Shared' està reservat per Owncloud", @@ -42,6 +46,7 @@ $TRANSLATIONS = array( "Your storage is almost full ({usedSpacePercent}%)" => "El vostre espai d'emmagatzemament és gairebé ple ({usedSpacePercent}%)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "L'encriptació s'ha desactivat però els vostres fitxers segueixen encriptats. Aneu a la vostra configuració personal per desencriptar els vostres fitxers.", "Your download is being prepared. This might take some time if the files are big." => "S'està preparant la baixada. Pot trigar una estona si els fitxers són grans.", +"Error moving file" => "Error en moure el fitxer", "Name" => "Nom", "Size" => "Mida", "Modified" => "Modificat", diff --git a/apps/files/l10n/en_GB.php b/apps/files/l10n/en_GB.php index e67719efba..c747555e40 100644 --- a/apps/files/l10n/en_GB.php +++ b/apps/files/l10n/en_GB.php @@ -13,10 +13,14 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Missing a temporary folder", "Failed to write to disk" => "Failed to write to disk", "Not enough storage available" => "Not enough storage available", +"Upload failed. Could not get file info." => "Upload failed. Could not get file info.", +"Upload failed. Could not find uploaded file" => "Upload failed. Could not find uploaded file", "Invalid directory." => "Invalid directory.", "Files" => "Files", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "Unable to upload {filename} as it is a directory or has 0 bytes", "Not enough space available" => "Not enough space available", "Upload cancelled." => "Upload cancelled.", +"Could not get result from server." => "Could not get result from server.", "File upload is in progress. Leaving the page now will cancel the upload." => "File upload is in progress. Leaving the page now will cancel the upload.", "URL cannot be empty." => "URL cannot be empty.", "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Invalid folder name. Usage of 'Shared' is reserved by ownCloud", @@ -37,11 +41,12 @@ $TRANSLATIONS = array( "_Uploading %n file_::_Uploading %n files_" => array("Uploading %n file","Uploading %n files"), "'.' is an invalid file name." => "'.' is an invalid file name.", "File name cannot be empty." => "File name cannot be empty.", -"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.", +"Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Invalid name: '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.", "Your storage is full, files can not be updated or synced anymore!" => "Your storage is full, files can not be updated or synced anymore!", "Your storage is almost full ({usedSpacePercent}%)" => "Your storage is almost full ({usedSpacePercent}%)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.", "Your download is being prepared. This might take some time if the files are big." => "Your download is being prepared. This might take some time if the files are big.", +"Error moving file" => "Error moving file", "Name" => "Name", "Size" => "Size", "Modified" => "Modified", diff --git a/apps/files/l10n/fr.php b/apps/files/l10n/fr.php index d647045808..03505a2a26 100644 --- a/apps/files/l10n/fr.php +++ b/apps/files/l10n/fr.php @@ -13,10 +13,14 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Absence de dossier temporaire.", "Failed to write to disk" => "Erreur d'écriture sur le disque", "Not enough storage available" => "Plus assez d'espace de stockage disponible", +"Upload failed. Could not get file info." => "L'envoi a échoué. Impossible d'obtenir les informations du fichier.", +"Upload failed. Could not find uploaded file" => "L'envoi a échoué. Impossible de trouver le fichier envoyé.", "Invalid directory." => "Dossier invalide.", "Files" => "Fichiers", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "Impossible d'envoyer {filename} car il s'agit d'un répertoire ou d'un fichier de taille nulle", "Not enough space available" => "Espace disponible insuffisant", "Upload cancelled." => "Envoi annulé.", +"Could not get result from server." => "Ne peut recevoir les résultats du serveur.", "File upload is in progress. Leaving the page now will cancel the upload." => "L'envoi du fichier est en cours. Quitter cette page maintenant annulera l'envoi du fichier.", "URL cannot be empty." => "L'URL ne peut-être vide", "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nom de dossier invalide. L'utilisation du mot 'Shared' est réservée à Owncloud", @@ -42,6 +46,7 @@ $TRANSLATIONS = array( "Your storage is almost full ({usedSpacePercent}%)" => "Votre espace de stockage est presque plein ({usedSpacePercent}%)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Le chiffrement était désactivé mais vos fichiers sont toujours chiffrés. Veuillez vous rendre sur vos Paramètres personnels pour déchiffrer vos fichiers.", "Your download is being prepared. This might take some time if the files are big." => "Votre téléchargement est cours de préparation. Ceci peut nécessiter un certain temps si les fichiers sont volumineux.", +"Error moving file" => "Erreur lors du déplacement du fichier", "Name" => "Nom", "Size" => "Taille", "Modified" => "Modifié", diff --git a/apps/files/l10n/gl.php b/apps/files/l10n/gl.php index 0eba94f7d6..2766478650 100644 --- a/apps/files/l10n/gl.php +++ b/apps/files/l10n/gl.php @@ -13,10 +13,14 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Falta o cartafol temporal", "Failed to write to disk" => "Produciuse un erro ao escribir no disco", "Not enough storage available" => "Non hai espazo de almacenamento abondo", +"Upload failed. Could not get file info." => "O envío fracasou. Non foi posíbel obter información do ficheiro.", +"Upload failed. Could not find uploaded file" => "O envío fracasou. Non foi posíbel atopar o ficheiro enviado", "Invalid directory." => "O directorio é incorrecto.", "Files" => "Ficheiros", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "Non é posíbel enviar {filename}, xa que ou é un directorio ou ten 0 bytes", "Not enough space available" => "O espazo dispoñíbel é insuficiente", "Upload cancelled." => "Envío cancelado.", +"Could not get result from server." => "Non foi posíbel obter o resultado do servidor.", "File upload is in progress. Leaving the page now will cancel the upload." => "O envío do ficheiro está en proceso. Saír agora da páxina cancelará o envío.", "URL cannot be empty." => "O URL non pode quedar baleiro.", "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Nome de cartafol incorrecto. O uso de «Compartido» e «Shared» está reservado para o ownClod", @@ -42,6 +46,7 @@ $TRANSLATIONS = array( "Your storage is almost full ({usedSpacePercent}%)" => "O seu espazo de almacenamento está case cheo ({usedSpacePercent}%)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "O cifrado foi desactivado, mais os ficheiros están cifrados. Vaia á configuración persoal para descifrar os ficheiros.", "Your download is being prepared. This might take some time if the files are big." => "Está a prepararse a súa descarga. Isto pode levar bastante tempo se os ficheiros son grandes.", +"Error moving file" => "Produciuse un erro ao mover o ficheiro", "Name" => "Nome", "Size" => "Tamaño", "Modified" => "Modificado", diff --git a/apps/files/l10n/nn_NO.php b/apps/files/l10n/nn_NO.php index 04c47c31fb..e29b1d3ad3 100644 --- a/apps/files/l10n/nn_NO.php +++ b/apps/files/l10n/nn_NO.php @@ -13,10 +13,14 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Manglar ei mellombels mappe", "Failed to write to disk" => "Klarte ikkje skriva til disk", "Not enough storage available" => "Ikkje nok lagringsplass tilgjengeleg", +"Upload failed. Could not get file info." => "Feil ved opplasting. Klarte ikkje å henta filinfo.", +"Upload failed. Could not find uploaded file" => "Feil ved opplasting. Klarte ikkje å finna opplasta fil.", "Invalid directory." => "Ugyldig mappe.", "Files" => "Filer", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "Klarte ikkje å lasta opp {filename} sidan det er ei mappe eller er 0 byte.", "Not enough space available" => "Ikkje nok lagringsplass tilgjengeleg", "Upload cancelled." => "Opplasting avbroten.", +"Could not get result from server." => "Klarte ikkje å henta resultat frå tenaren.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fila lastar no opp. Viss du forlèt sida no vil opplastinga verta avbroten.", "URL cannot be empty." => "Nettadressa kan ikkje vera tom.", "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Ugyldig mappenamn. Mappa «Shared» er reservert av ownCloud", @@ -42,6 +46,7 @@ $TRANSLATIONS = array( "Your storage is almost full ({usedSpacePercent}%)" => "Lagringa di er nesten full ({usedSpacePercent} %)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Kryptering er skrudd av, men filene dine er enno krypterte. Du kan dekryptera filene i personlege innstillingar.", "Your download is being prepared. This might take some time if the files are big." => "Gjer klar nedlastinga di. Dette kan ta ei stund viss filene er store.", +"Error moving file" => "Feil ved flytting av fil", "Name" => "Namn", "Size" => "Storleik", "Modified" => "Endra", diff --git a/core/l10n/en_GB.php b/core/l10n/en_GB.php index feeacd481a..bb26f1469d 100644 --- a/core/l10n/en_GB.php +++ b/core/l10n/en_GB.php @@ -58,8 +58,15 @@ $TRANSLATIONS = array( "No" => "No", "Ok" => "OK", "Error loading message template: {error}" => "Error loading message template: {error}", -"_{count} file conflict_::_{count} file conflicts_" => array("",""), +"_{count} file conflict_::_{count} file conflicts_" => array("{count} file conflict","{count} file conflicts"), +"One file conflict" => "One file conflict", +"Which files do you want to keep?" => "Which files do you wish to keep?", +"If you select both versions, the copied file will have a number added to its name." => "If you select both versions, the copied file will have a number added to its name.", "Cancel" => "Cancel", +"Continue" => "Continue", +"(all selected)" => "(all selected)", +"({count} selected)" => "({count} selected)", +"Error loading file exists template" => "Error loading file exists template", "The object type is not specified." => "The object type is not specified.", "Error" => "Error", "The app name is not specified." => "The app name is not specified.", diff --git a/core/l10n/fr.php b/core/l10n/fr.php index d3229ddf99..29489e86b7 100644 --- a/core/l10n/fr.php +++ b/core/l10n/fr.php @@ -56,8 +56,12 @@ $TRANSLATIONS = array( "No" => "Non", "Ok" => "Ok", "Error loading message template: {error}" => "Erreur de chargement du modèle de message : {error}", -"_{count} file conflict_::_{count} file conflicts_" => array("",""), +"_{count} file conflict_::_{count} file conflicts_" => array("{count} fichier en conflit","{count} fichiers en conflit"), +"One file conflict" => "Un conflit de fichier", +"Which files do you want to keep?" => "Quels fichiers désirez-vous garder ?", +"If you select both versions, the copied file will have a number added to its name." => "Si vous sélectionnez les deux versions, un nombre sera ajouté au nom du fichier copié.", "Cancel" => "Annuler", +"({count} selected)" => "({count} sélectionnés)", "The object type is not specified." => "Le type d'objet n'est pas spécifié.", "Error" => "Erreur", "The app name is not specified." => "Le nom de l'application n'est pas spécifié.", diff --git a/core/l10n/gl.php b/core/l10n/gl.php index 9ba5ab645a..e3be94537e 100644 --- a/core/l10n/gl.php +++ b/core/l10n/gl.php @@ -58,8 +58,15 @@ $TRANSLATIONS = array( "No" => "Non", "Ok" => "Aceptar", "Error loading message template: {error}" => "Produciuse un erro ao cargar o modelo da mensaxe: {error}", -"_{count} file conflict_::_{count} file conflicts_" => array("",""), +"_{count} file conflict_::_{count} file conflicts_" => array("{count} conflito de ficheiro","{count} conflitos de ficheiros"), +"One file conflict" => "Un conflito de ficheiro", +"Which files do you want to keep?" => "Que ficheiros quere conservar?", +"If you select both versions, the copied file will have a number added to its name." => "Se selecciona ambas versións, o ficheiro copiado terá un número engadido ao nome.", "Cancel" => "Cancelar", +"Continue" => "Continuar", +"(all selected)" => "(todo o seleccionado)", +"({count} selected)" => "({count} seleccionados)", +"Error loading file exists template" => "Produciuse un erro ao cargar o modelo de ficheiro existente", "The object type is not specified." => "Non se especificou o tipo de obxecto.", "Error" => "Erro", "The app name is not specified." => "Non se especificou o nome do aplicativo.", diff --git a/core/l10n/nn_NO.php b/core/l10n/nn_NO.php index 8ec3892a8a..d596605dbc 100644 --- a/core/l10n/nn_NO.php +++ b/core/l10n/nn_NO.php @@ -16,6 +16,11 @@ $TRANSLATIONS = array( "Error adding %s to favorites." => "Klarte ikkje leggja til %s i favorittar.", "No categories selected for deletion." => "Ingen kategoriar valt for sletting.", "Error removing %s from favorites." => "Klarte ikkje fjerna %s frå favorittar.", +"No image or file provided" => "Inga bilete eller fil gitt", +"Unknown filetype" => "Ukjend filtype", +"Invalid image" => "Ugyldig bilete", +"No temporary profile picture available, try again" => "Inga midlertidig profilbilete tilgjengeleg, prøv igjen", +"No crop data provided" => "Ingen beskjeringsdata gitt", "Sunday" => "Søndag", "Monday" => "Måndag", "Tuesday" => "Tysdag", @@ -48,11 +53,20 @@ $TRANSLATIONS = array( "last year" => "i fjor", "years ago" => "år sidan", "Choose" => "Vel", +"Error loading file picker template: {error}" => "Klarte ikkje å lasta filplukkarmal: {error}", "Yes" => "Ja", "No" => "Nei", "Ok" => "Greitt", -"_{count} file conflict_::_{count} file conflicts_" => array("",""), +"Error loading message template: {error}" => "Klarte ikkje å lasta meldingsmal: {error}", +"_{count} file conflict_::_{count} file conflicts_" => array("{count} filkonflikt","{count} filkonfliktar"), +"One file conflict" => "Éin filkonflikt", +"Which files do you want to keep?" => "Kva filer vil du spara?", +"If you select both versions, the copied file will have a number added to its name." => "Viss du vel begge utgåvene, vil den kopierte fila få eit tal lagt til namnet.", "Cancel" => "Avbryt", +"Continue" => "Gå vidare", +"(all selected)" => "(alle valte)", +"({count} selected)" => "({count} valte)", +"Error loading file exists template" => "Klarte ikkje å lasta fil-finst-mal", "The object type is not specified." => "Objekttypen er ikkje spesifisert.", "Error" => "Feil", "The app name is not specified." => "Programnamnet er ikkje spesifisert.", diff --git a/l10n/ca/files.po b/l10n/ca/files.po index 173aeb30ac..82091cf75b 100644 --- a/l10n/ca/files.po +++ b/l10n/ca/files.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 15:10+0000\n" +"Last-Translator: rogerc\n" "Language-Team: Catalan (http://www.transifex.com/projects/p/owncloud/language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -78,23 +78,23 @@ msgstr "No hi ha prou espai disponible" #: ajax/upload.php:120 ajax/upload.php:143 msgid "Upload failed. Could not get file info." -msgstr "" +msgstr "La pujada ha fallat. No s'ha pogut obtenir informació del fitxer." #: ajax/upload.php:136 msgid "Upload failed. Could not find uploaded file" -msgstr "" +msgstr "La pujada ha fallat. El fitxer pujat no s'ha trobat." #: ajax/upload.php:160 msgid "Invalid directory." msgstr "Directori no vàlid." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Fitxers" #: js/file-upload.js:244 msgid "Unable to upload {filename} as it is a directory or has 0 bytes" -msgstr "" +msgstr "No es pot pujar {filename} perquè és una carpeta o té 0 bytes" #: js/file-upload.js:255 msgid "Not enough space available" @@ -106,7 +106,7 @@ msgstr "La pujada s'ha cancel·lat." #: js/file-upload.js:356 msgid "Could not get result from server." -msgstr "" +msgstr "No hi ha resposta del servidor." #: js/file-upload.js:446 msgid "" @@ -223,7 +223,7 @@ msgstr "S'està preparant la baixada. Pot trigar una estona si els fitxers són #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Error en moure el fitxer" #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/da/settings.po b/l10n/da/settings.po index accacfac32..7251a13d4f 100644 --- a/l10n/da/settings.po +++ b/l10n/da/settings.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:45-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 16:59+0000\n" +"Last-Translator: Sappe\n" "Language-Team: Danish (http://www.transifex.com/projects/p/owncloud/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,28 +89,28 @@ msgstr "Kunne ikke opdatere app'en." #: changepassword/controller.php:20 msgid "Wrong password" -msgstr "" +msgstr "Forkert kodeord" #: changepassword/controller.php:42 msgid "No user supplied" -msgstr "" +msgstr "Intet brugernavn givet" #: changepassword/controller.php:74 msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" -msgstr "" +msgstr "Angiv venligst en admininstrator gendannelseskode, ellers vil alt brugerdata gå tabt" #: changepassword/controller.php:79 msgid "" "Wrong admin recovery password. Please check the password and try again." -msgstr "" +msgstr "Forkert admin gendannelseskode. Se venligst koden efter og prøv igen." #: changepassword/controller.php:87 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." -msgstr "" +msgstr "Serveren understøtter ikke kodeordsskifte, men brugernes krypteringsnøgle blev opdateret." #: changepassword/controller.php:92 changepassword/controller.php:103 msgid "Unable to change password" @@ -120,11 +120,11 @@ msgstr "" msgid "Update to {appversion}" msgstr "Opdatér til {appversion}" -#: js/apps.js:49 js/apps.js:82 js/apps.js:108 +#: js/apps.js:49 js/apps.js:82 js/apps.js:110 msgid "Disable" msgstr "Deaktiver" -#: js/apps.js:49 js/apps.js:89 js/apps.js:102 js/apps.js:117 +#: js/apps.js:49 js/apps.js:90 js/apps.js:103 js/apps.js:119 msgid "Enable" msgstr "Aktiver" @@ -132,31 +132,31 @@ msgstr "Aktiver" msgid "Please wait...." msgstr "Vent venligst..." -#: js/apps.js:79 js/apps.js:80 js/apps.js:100 +#: js/apps.js:79 js/apps.js:80 js/apps.js:101 msgid "Error while disabling app" msgstr "Kunne ikke deaktivere app" -#: js/apps.js:99 js/apps.js:112 js/apps.js:113 +#: js/apps.js:100 js/apps.js:114 js/apps.js:115 msgid "Error while enabling app" msgstr "Kunne ikke aktivere app" -#: js/apps.js:123 +#: js/apps.js:125 msgid "Updating...." msgstr "Opdaterer...." -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error while updating app" msgstr "Der opstod en fejl under app opgraderingen" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error" msgstr "Fejl" -#: js/apps.js:127 templates/apps.php:43 +#: js/apps.js:129 templates/apps.php:43 msgid "Update" msgstr "Opdater" -#: js/apps.js:130 +#: js/apps.js:132 msgid "Updated" msgstr "Opdateret" diff --git a/l10n/en_GB/core.po b/l10n/en_GB/core.po index 5ac5d34f56..f9b0f0d24f 100644 --- a/l10n/en_GB/core.po +++ b/l10n/en_GB/core.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-23 16:10+0000\n" +"Last-Translator: mnestis \n" "Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/owncloud/language/en_GB/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -270,22 +270,22 @@ msgstr "Error loading message template: {error}" #: js/oc-dialogs.js:347 msgid "{count} file conflict" msgid_plural "{count} file conflicts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{count} file conflict" +msgstr[1] "{count} file conflicts" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "" +msgstr "One file conflict" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" -msgstr "" +msgstr "Which files do you wish to keep?" #: js/oc-dialogs.js:368 msgid "" "If you select both versions, the copied file will have a number added to its" " name." -msgstr "" +msgstr "If you select both versions, the copied file will have a number added to its name." #: js/oc-dialogs.js:376 msgid "Cancel" @@ -293,19 +293,19 @@ msgstr "Cancel" #: js/oc-dialogs.js:386 msgid "Continue" -msgstr "" +msgstr "Continue" #: js/oc-dialogs.js:433 js/oc-dialogs.js:446 msgid "(all selected)" -msgstr "" +msgstr "(all selected)" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" -msgstr "" +msgstr "({count} selected)" #: js/oc-dialogs.js:457 msgid "Error loading file exists template" -msgstr "" +msgstr "Error loading file exists template" #: js/oc-vcategories.js:5 js/oc-vcategories.js:85 js/oc-vcategories.js:102 #: js/oc-vcategories.js:117 js/oc-vcategories.js:132 js/oc-vcategories.js:162 @@ -316,7 +316,7 @@ msgstr "The object type is not specified." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Error" @@ -336,7 +336,7 @@ msgstr "Shared" msgid "Share" msgstr "Share" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Error whilst sharing" @@ -436,23 +436,23 @@ msgstr "delete" msgid "share" msgstr "share" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Password protected" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Error unsetting expiration date" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Error setting expiration date" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Sending ..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "Email sent" diff --git a/l10n/en_GB/files.po b/l10n/en_GB/files.po index fe7922ffe9..bf8e937434 100644 --- a/l10n/en_GB/files.po +++ b/l10n/en_GB/files.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 16:00+0000\n" +"Last-Translator: mnestis \n" "Language-Team: English (United Kingdom) (http://www.transifex.com/projects/p/owncloud/language/en_GB/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,23 +77,23 @@ msgstr "Not enough storage available" #: ajax/upload.php:120 ajax/upload.php:143 msgid "Upload failed. Could not get file info." -msgstr "" +msgstr "Upload failed. Could not get file info." #: ajax/upload.php:136 msgid "Upload failed. Could not find uploaded file" -msgstr "" +msgstr "Upload failed. Could not find uploaded file" #: ajax/upload.php:160 msgid "Invalid directory." msgstr "Invalid directory." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Files" #: js/file-upload.js:244 msgid "Unable to upload {filename} as it is a directory or has 0 bytes" -msgstr "" +msgstr "Unable to upload {filename} as it is a directory or has 0 bytes" #: js/file-upload.js:255 msgid "Not enough space available" @@ -105,7 +105,7 @@ msgstr "Upload cancelled." #: js/file-upload.js:356 msgid "Could not get result from server." -msgstr "" +msgstr "Could not get result from server." #: js/file-upload.js:446 msgid "" @@ -198,7 +198,7 @@ msgstr "File name cannot be empty." msgid "" "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not " "allowed." -msgstr "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." +msgstr "Invalid name: '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." #: js/files.js:51 msgid "Your storage is full, files can not be updated or synced anymore!" @@ -222,7 +222,7 @@ msgstr "Your download is being prepared. This might take some time if the files #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Error moving file" #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/fr/core.po b/l10n/fr/core.po index 76dc658c71..4c7f6be045 100644 --- a/l10n/fr/core.po +++ b/l10n/fr/core.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-23 19:40+0000\n" +"Last-Translator: ogre_sympathique \n" "Language-Team: French (http://www.transifex.com/projects/p/owncloud/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -275,22 +275,22 @@ msgstr "Erreur de chargement du modèle de message : {error}" #: js/oc-dialogs.js:347 msgid "{count} file conflict" msgid_plural "{count} file conflicts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{count} fichier en conflit" +msgstr[1] "{count} fichiers en conflit" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "" +msgstr "Un conflit de fichier" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" -msgstr "" +msgstr "Quels fichiers désirez-vous garder ?" #: js/oc-dialogs.js:368 msgid "" "If you select both versions, the copied file will have a number added to its" " name." -msgstr "" +msgstr "Si vous sélectionnez les deux versions, un nombre sera ajouté au nom du fichier copié." #: js/oc-dialogs.js:376 msgid "Cancel" @@ -306,7 +306,7 @@ msgstr "" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" -msgstr "" +msgstr "({count} sélectionnés)" #: js/oc-dialogs.js:457 msgid "Error loading file exists template" @@ -321,7 +321,7 @@ msgstr "Le type d'objet n'est pas spécifié." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Erreur" @@ -341,7 +341,7 @@ msgstr "Partagé" msgid "Share" msgstr "Partager" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Erreur lors de la mise en partage" @@ -441,23 +441,23 @@ msgstr "supprimer" msgid "share" msgstr "partager" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Protégé par un mot de passe" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Une erreur est survenue pendant la suppression de la date d'expiration" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Erreur lors de la spécification de la date d'expiration" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "En cours d'envoi ..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "Email envoyé" diff --git a/l10n/fr/files.po b/l10n/fr/files.po index fcbbbad254..72963e3573 100644 --- a/l10n/fr/files.po +++ b/l10n/fr/files.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-23 19:30+0000\n" +"Last-Translator: ogre_sympathique \n" "Language-Team: French (http://www.transifex.com/projects/p/owncloud/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -80,23 +80,23 @@ msgstr "Plus assez d'espace de stockage disponible" #: ajax/upload.php:120 ajax/upload.php:143 msgid "Upload failed. Could not get file info." -msgstr "" +msgstr "L'envoi a échoué. Impossible d'obtenir les informations du fichier." #: ajax/upload.php:136 msgid "Upload failed. Could not find uploaded file" -msgstr "" +msgstr "L'envoi a échoué. Impossible de trouver le fichier envoyé." #: ajax/upload.php:160 msgid "Invalid directory." msgstr "Dossier invalide." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Fichiers" #: js/file-upload.js:244 msgid "Unable to upload {filename} as it is a directory or has 0 bytes" -msgstr "" +msgstr "Impossible d'envoyer {filename} car il s'agit d'un répertoire ou d'un fichier de taille nulle" #: js/file-upload.js:255 msgid "Not enough space available" @@ -108,7 +108,7 @@ msgstr "Envoi annulé." #: js/file-upload.js:356 msgid "Could not get result from server." -msgstr "" +msgstr "Ne peut recevoir les résultats du serveur." #: js/file-upload.js:446 msgid "" @@ -225,7 +225,7 @@ msgstr "Votre téléchargement est cours de préparation. Ceci peut nécessiter #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Erreur lors du déplacement du fichier" #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/gl/core.po b/l10n/gl/core.po index 72986b81c8..b8bcfd4eea 100644 --- a/l10n/gl/core.po +++ b/l10n/gl/core.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-23 10:30+0000\n" +"Last-Translator: mbouzada \n" "Language-Team: Galician (http://www.transifex.com/projects/p/owncloud/language/gl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -270,22 +270,22 @@ msgstr "Produciuse un erro ao cargar o modelo da mensaxe: {error}" #: js/oc-dialogs.js:347 msgid "{count} file conflict" msgid_plural "{count} file conflicts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{count} conflito de ficheiro" +msgstr[1] "{count} conflitos de ficheiros" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "" +msgstr "Un conflito de ficheiro" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" -msgstr "" +msgstr "Que ficheiros quere conservar?" #: js/oc-dialogs.js:368 msgid "" "If you select both versions, the copied file will have a number added to its" " name." -msgstr "" +msgstr "Se selecciona ambas versións, o ficheiro copiado terá un número engadido ao nome." #: js/oc-dialogs.js:376 msgid "Cancel" @@ -293,19 +293,19 @@ msgstr "Cancelar" #: js/oc-dialogs.js:386 msgid "Continue" -msgstr "" +msgstr "Continuar" #: js/oc-dialogs.js:433 js/oc-dialogs.js:446 msgid "(all selected)" -msgstr "" +msgstr "(todo o seleccionado)" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" -msgstr "" +msgstr "({count} seleccionados)" #: js/oc-dialogs.js:457 msgid "Error loading file exists template" -msgstr "" +msgstr "Produciuse un erro ao cargar o modelo de ficheiro existente" #: js/oc-vcategories.js:5 js/oc-vcategories.js:85 js/oc-vcategories.js:102 #: js/oc-vcategories.js:117 js/oc-vcategories.js:132 js/oc-vcategories.js:162 @@ -316,7 +316,7 @@ msgstr "Non se especificou o tipo de obxecto." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Erro" @@ -336,7 +336,7 @@ msgstr "Compartir" msgid "Share" msgstr "Compartir" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Produciuse un erro ao compartir" @@ -436,23 +436,23 @@ msgstr "eliminar" msgid "share" msgstr "compartir" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Protexido con contrasinal" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Produciuse un erro ao retirar a data de caducidade" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Produciuse un erro ao definir a data de caducidade" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Enviando..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "Correo enviado" diff --git a/l10n/gl/files.po b/l10n/gl/files.po index 45a9129ab2..33e76e8487 100644 --- a/l10n/gl/files.po +++ b/l10n/gl/files.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-23 10:30+0000\n" +"Last-Translator: mbouzada \n" "Language-Team: Galician (http://www.transifex.com/projects/p/owncloud/language/gl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,23 +77,23 @@ msgstr "Non hai espazo de almacenamento abondo" #: ajax/upload.php:120 ajax/upload.php:143 msgid "Upload failed. Could not get file info." -msgstr "" +msgstr "O envío fracasou. Non foi posíbel obter información do ficheiro." #: ajax/upload.php:136 msgid "Upload failed. Could not find uploaded file" -msgstr "" +msgstr "O envío fracasou. Non foi posíbel atopar o ficheiro enviado" #: ajax/upload.php:160 msgid "Invalid directory." msgstr "O directorio é incorrecto." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Ficheiros" #: js/file-upload.js:244 msgid "Unable to upload {filename} as it is a directory or has 0 bytes" -msgstr "" +msgstr "Non é posíbel enviar {filename}, xa que ou é un directorio ou ten 0 bytes" #: js/file-upload.js:255 msgid "Not enough space available" @@ -105,7 +105,7 @@ msgstr "Envío cancelado." #: js/file-upload.js:356 msgid "Could not get result from server." -msgstr "" +msgstr "Non foi posíbel obter o resultado do servidor." #: js/file-upload.js:446 msgid "" @@ -222,7 +222,7 @@ msgstr "Está a prepararse a súa descarga. Isto pode levar bastante tempo se os #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Produciuse un erro ao mover o ficheiro" #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/nn_NO/core.po b/l10n/nn_NO/core.po index 40a6de51ba..e16776a098 100644 --- a/l10n/nn_NO/core.po +++ b/l10n/nn_NO/core.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 08:30+0000\n" +"Last-Translator: unhammer \n" "Language-Team: Norwegian Nynorsk (Norway) (http://www.transifex.com/projects/p/owncloud/language/nn_NO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -95,23 +95,23 @@ msgstr "Klarte ikkje fjerna %s frå favorittar." #: avatar/controller.php:62 msgid "No image or file provided" -msgstr "" +msgstr "Inga bilete eller fil gitt" #: avatar/controller.php:81 msgid "Unknown filetype" -msgstr "" +msgstr "Ukjend filtype" #: avatar/controller.php:85 msgid "Invalid image" -msgstr "" +msgstr "Ugyldig bilete" #: avatar/controller.php:115 avatar/controller.php:142 msgid "No temporary profile picture available, try again" -msgstr "" +msgstr "Inga midlertidig profilbilete tilgjengeleg, prøv igjen" #: avatar/controller.php:135 msgid "No crop data provided" -msgstr "" +msgstr "Ingen beskjeringsdata gitt" #: js/config.php:32 msgid "Sunday" @@ -251,7 +251,7 @@ msgstr "Vel" #: js/oc-dialogs.js:146 msgid "Error loading file picker template: {error}" -msgstr "" +msgstr "Klarte ikkje å lasta filplukkarmal: {error}" #: js/oc-dialogs.js:172 msgid "Yes" @@ -267,27 +267,27 @@ msgstr "Greitt" #: js/oc-dialogs.js:219 msgid "Error loading message template: {error}" -msgstr "" +msgstr "Klarte ikkje å lasta meldingsmal: {error}" #: js/oc-dialogs.js:347 msgid "{count} file conflict" msgid_plural "{count} file conflicts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{count} filkonflikt" +msgstr[1] "{count} filkonfliktar" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "" +msgstr "Éin filkonflikt" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" -msgstr "" +msgstr "Kva filer vil du spara?" #: js/oc-dialogs.js:368 msgid "" "If you select both versions, the copied file will have a number added to its" " name." -msgstr "" +msgstr "Viss du vel begge utgåvene, vil den kopierte fila få eit tal lagt til namnet." #: js/oc-dialogs.js:376 msgid "Cancel" @@ -295,19 +295,19 @@ msgstr "Avbryt" #: js/oc-dialogs.js:386 msgid "Continue" -msgstr "" +msgstr "Gå vidare" #: js/oc-dialogs.js:433 js/oc-dialogs.js:446 msgid "(all selected)" -msgstr "" +msgstr "(alle valte)" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" -msgstr "" +msgstr "({count} valte)" #: js/oc-dialogs.js:457 msgid "Error loading file exists template" -msgstr "" +msgstr "Klarte ikkje å lasta fil-finst-mal" #: js/oc-vcategories.js:5 js/oc-vcategories.js:85 js/oc-vcategories.js:102 #: js/oc-vcategories.js:117 js/oc-vcategories.js:132 js/oc-vcategories.js:162 @@ -318,7 +318,7 @@ msgstr "Objekttypen er ikkje spesifisert." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Feil" @@ -338,7 +338,7 @@ msgstr "Delt" msgid "Share" msgstr "Del" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Feil ved deling" @@ -438,23 +438,23 @@ msgstr "slett" msgid "share" msgstr "del" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Passordverna" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Klarte ikkje fjerna utløpsdato" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Klarte ikkje setja utløpsdato" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Sender …" -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "E-post sendt" diff --git a/l10n/nn_NO/files.po b/l10n/nn_NO/files.po index 0573ecf295..dc82bdca1b 100644 --- a/l10n/nn_NO/files.po +++ b/l10n/nn_NO/files.po @@ -5,13 +5,14 @@ # Translators: # unhammer , 2013 # unhammer , 2013 +# unhammer , 2013 msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 08:20+0000\n" +"Last-Translator: unhammer \n" "Language-Team: Norwegian Nynorsk (Norway) (http://www.transifex.com/projects/p/owncloud/language/nn_NO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -78,23 +79,23 @@ msgstr "Ikkje nok lagringsplass tilgjengeleg" #: ajax/upload.php:120 ajax/upload.php:143 msgid "Upload failed. Could not get file info." -msgstr "" +msgstr "Feil ved opplasting. Klarte ikkje å henta filinfo." #: ajax/upload.php:136 msgid "Upload failed. Could not find uploaded file" -msgstr "" +msgstr "Feil ved opplasting. Klarte ikkje å finna opplasta fil." #: ajax/upload.php:160 msgid "Invalid directory." msgstr "Ugyldig mappe." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Filer" #: js/file-upload.js:244 msgid "Unable to upload {filename} as it is a directory or has 0 bytes" -msgstr "" +msgstr "Klarte ikkje å lasta opp {filename} sidan det er ei mappe eller er 0 byte." #: js/file-upload.js:255 msgid "Not enough space available" @@ -106,7 +107,7 @@ msgstr "Opplasting avbroten." #: js/file-upload.js:356 msgid "Could not get result from server." -msgstr "" +msgstr "Klarte ikkje å henta resultat frå tenaren." #: js/file-upload.js:446 msgid "" @@ -223,7 +224,7 @@ msgstr "Gjer klar nedlastinga di. Dette kan ta ei stund viss filene er store." #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Feil ved flytting av fil" #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/nn_NO/lib.po b/l10n/nn_NO/lib.po index dd499893e3..9e73f6fe6a 100644 --- a/l10n/nn_NO/lib.po +++ b/l10n/nn_NO/lib.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-16 11:33-0400\n" -"PO-Revision-Date: 2013-09-16 15:34+0000\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 08:30+0000\n" "Last-Translator: I Robot \n" "Language-Team: Norwegian Nynorsk (Norway) (http://www.transifex.com/projects/p/owncloud/language/nn_NO/)\n" "MIME-Version: 1.0\n" @@ -61,11 +61,11 @@ msgstr "" #: avatar.php:64 msgid "Unknown filetype" -msgstr "" +msgstr "Ukjend filtype" #: avatar.php:69 msgid "Invalid image" -msgstr "" +msgstr "Ugyldig bilete" #: defaults.php:35 msgid "web services under your control" @@ -166,15 +166,15 @@ msgstr "Feil i autentisering" msgid "Token expired. Please reload page." msgstr "" -#: search/provider/file.php:17 search/provider/file.php:35 +#: search/provider/file.php:18 search/provider/file.php:36 msgid "Files" msgstr "Filer" -#: search/provider/file.php:26 search/provider/file.php:33 +#: search/provider/file.php:27 search/provider/file.php:34 msgid "Text" msgstr "Tekst" -#: search/provider/file.php:29 +#: search/provider/file.php:30 msgid "Images" msgstr "" diff --git a/l10n/nn_NO/settings.po b/l10n/nn_NO/settings.po index 6100216bbd..761b9f4627 100644 --- a/l10n/nn_NO/settings.po +++ b/l10n/nn_NO/settings.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:45-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"PO-Revision-Date: 2013-09-24 08:30+0000\n" +"Last-Translator: unhammer \n" "Language-Team: Norwegian Nynorsk (Norway) (http://www.transifex.com/projects/p/owncloud/language/nn_NO/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,42 +89,42 @@ msgstr "Klarte ikkje oppdatera programmet." #: changepassword/controller.php:20 msgid "Wrong password" -msgstr "" +msgstr "Feil passord" #: changepassword/controller.php:42 msgid "No user supplied" -msgstr "" +msgstr "Ingen brukar gitt" #: changepassword/controller.php:74 msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" -msgstr "" +msgstr "Ver venleg og gi eit admingjenopprettingspassord, elles vil all brukardata gå tapt." #: changepassword/controller.php:79 msgid "" "Wrong admin recovery password. Please check the password and try again." -msgstr "" +msgstr "Feil admingjenopprettingspassord. Ver venleg og sjekk passordet og prøv igjen." #: changepassword/controller.php:87 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." -msgstr "" +msgstr "Bakstykket støttar ikkje passordendring, men krypteringsnøkkelen til brukaren blei oppdatert." #: changepassword/controller.php:92 changepassword/controller.php:103 msgid "Unable to change password" -msgstr "" +msgstr "Klarte ikkje å endra passordet" #: js/apps.js:43 msgid "Update to {appversion}" msgstr "Oppdater til {appversion}" -#: js/apps.js:49 js/apps.js:82 js/apps.js:108 +#: js/apps.js:49 js/apps.js:82 js/apps.js:110 msgid "Disable" msgstr "Slå av" -#: js/apps.js:49 js/apps.js:89 js/apps.js:102 js/apps.js:117 +#: js/apps.js:49 js/apps.js:90 js/apps.js:103 js/apps.js:119 msgid "Enable" msgstr "Slå på" @@ -132,37 +132,37 @@ msgstr "Slå på" msgid "Please wait...." msgstr "Ver venleg og vent …" -#: js/apps.js:79 js/apps.js:80 js/apps.js:100 +#: js/apps.js:79 js/apps.js:80 js/apps.js:101 msgid "Error while disabling app" msgstr "Klarte ikkje å skru av programmet" -#: js/apps.js:99 js/apps.js:112 js/apps.js:113 +#: js/apps.js:100 js/apps.js:114 js/apps.js:115 msgid "Error while enabling app" msgstr "Klarte ikkje å skru på programmet" -#: js/apps.js:123 +#: js/apps.js:125 msgid "Updating...." msgstr "Oppdaterer …" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error while updating app" msgstr "Feil ved oppdatering av app" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error" msgstr "Feil" -#: js/apps.js:127 templates/apps.php:43 +#: js/apps.js:129 templates/apps.php:43 msgid "Update" msgstr "Oppdater" -#: js/apps.js:130 +#: js/apps.js:132 msgid "Updated" msgstr "Oppdatert" #: js/personal.js:220 msgid "Select a profile picture" -msgstr "" +msgstr "Vel eit profilbilete" #: js/personal.js:265 msgid "Decrypting files... Please wait, this can take some time." @@ -492,31 +492,31 @@ msgstr "Fyll inn e-postadressa di for å gjera passordgjenoppretting mogleg" #: templates/personal.php:86 msgid "Profile picture" -msgstr "" +msgstr "Profilbilete" #: templates/personal.php:90 msgid "Upload new" -msgstr "" +msgstr "Last opp ny" #: templates/personal.php:92 msgid "Select new from Files" -msgstr "" +msgstr "Vel ny frå Filer" #: templates/personal.php:93 msgid "Remove image" -msgstr "" +msgstr "Fjern bilete" #: templates/personal.php:94 msgid "Either png or jpg. Ideally square but you will be able to crop it." -msgstr "" +msgstr "Anten PNG eller JPG. Helst kvadratisk, men du får moglegheita til å beskjera det." #: templates/personal.php:97 msgid "Abort" -msgstr "" +msgstr "Avbryt" #: templates/personal.php:98 msgid "Choose as profile image" -msgstr "" +msgstr "Vel som profilbilete" #: templates/personal.php:106 templates/personal.php:107 msgid "Language" diff --git a/l10n/templates/core.pot b/l10n/templates/core.pot index e3ee79caef..a57486f5ed 100644 --- a/l10n/templates/core.pot +++ b/l10n/templates/core.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -316,7 +316,7 @@ msgstr "" #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "" @@ -336,7 +336,7 @@ msgstr "" msgid "Share" msgstr "" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "" @@ -436,23 +436,23 @@ msgstr "" msgid "share" msgstr "" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "" -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "" diff --git a/l10n/templates/files.pot b/l10n/templates/files.pot index 6dd2e8281c..23d0cd0b17 100644 --- a/l10n/templates/files.pot +++ b/l10n/templates/files.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:51-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_encryption.pot b/l10n/templates/files_encryption.pot index 17a33f8792..44e17a2fcb 100644 --- a/l10n/templates/files_encryption.pot +++ b/l10n/templates/files_encryption.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:51-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_external.pot b/l10n/templates/files_external.pot index 5d1b69c53a..abdd985cfd 100644 --- a/l10n/templates/files_external.pot +++ b/l10n/templates/files_external.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:54-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_sharing.pot b/l10n/templates/files_sharing.pot index bc0fb489f7..34ed992660 100644 --- a/l10n/templates/files_sharing.pot +++ b/l10n/templates/files_sharing.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:54-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_trashbin.pot b/l10n/templates/files_trashbin.pot index c0b82eeb69..45fa700a43 100644 --- a/l10n/templates/files_trashbin.pot +++ b/l10n/templates/files_trashbin.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:54-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_versions.pot b/l10n/templates/files_versions.pot index 42221b0028..2e73cce980 100644 --- a/l10n/templates/files_versions.pot +++ b/l10n/templates/files_versions.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:54-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/lib.pot b/l10n/templates/lib.pot index 15b4f1c6d0..0733e0c273 100644 --- a/l10n/templates/lib.pot +++ b/l10n/templates/lib.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:56-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/settings.pot b/l10n/templates/settings.pot index 4602bc52d6..66c00629bd 100644 --- a/l10n/templates/settings.pot +++ b/l10n/templates/settings.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:56-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -116,11 +116,11 @@ msgstr "" msgid "Update to {appversion}" msgstr "" -#: js/apps.js:49 js/apps.js:82 js/apps.js:108 +#: js/apps.js:49 js/apps.js:82 js/apps.js:110 msgid "Disable" msgstr "" -#: js/apps.js:49 js/apps.js:89 js/apps.js:102 js/apps.js:117 +#: js/apps.js:49 js/apps.js:90 js/apps.js:103 js/apps.js:119 msgid "Enable" msgstr "" @@ -128,31 +128,31 @@ msgstr "" msgid "Please wait...." msgstr "" -#: js/apps.js:79 js/apps.js:80 js/apps.js:100 +#: js/apps.js:79 js/apps.js:80 js/apps.js:101 msgid "Error while disabling app" msgstr "" -#: js/apps.js:99 js/apps.js:112 js/apps.js:113 +#: js/apps.js:100 js/apps.js:114 js/apps.js:115 msgid "Error while enabling app" msgstr "" -#: js/apps.js:123 +#: js/apps.js:125 msgid "Updating...." msgstr "" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error while updating app" msgstr "" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error" msgstr "" -#: js/apps.js:127 templates/apps.php:43 +#: js/apps.js:129 templates/apps.php:43 msgid "Update" msgstr "" -#: js/apps.js:130 +#: js/apps.js:132 msgid "Updated" msgstr "" diff --git a/l10n/templates/user_ldap.pot b/l10n/templates/user_ldap.pot index c27848c366..919a39b405 100644 --- a/l10n/templates/user_ldap.pot +++ b/l10n/templates/user_ldap.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:54-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/user_webdavauth.pot b/l10n/templates/user_webdavauth.pot index e23c0a1dc5..dfb732ed0c 100644 --- a/l10n/templates/user_webdavauth.pot +++ b/l10n/templates/user_webdavauth.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-22 12:54-0400\n" +"POT-Creation-Date: 2013-09-24 12:58-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/lib/l10n/nn_NO.php b/lib/l10n/nn_NO.php index d5da8c6441..e8bf8dfdef 100644 --- a/lib/l10n/nn_NO.php +++ b/lib/l10n/nn_NO.php @@ -5,6 +5,8 @@ $TRANSLATIONS = array( "Settings" => "Innstillingar", "Users" => "Brukarar", "Admin" => "Administrer", +"Unknown filetype" => "Ukjend filtype", +"Invalid image" => "Ugyldig bilete", "web services under your control" => "Vev tjenester under din kontroll", "Authentication error" => "Feil i autentisering", "Files" => "Filer", diff --git a/settings/l10n/da.php b/settings/l10n/da.php index 9872d3f5e0..fcff9dbcfd 100644 --- a/settings/l10n/da.php +++ b/settings/l10n/da.php @@ -16,6 +16,11 @@ $TRANSLATIONS = array( "Unable to add user to group %s" => "Brugeren kan ikke tilføjes til gruppen %s", "Unable to remove user from group %s" => "Brugeren kan ikke fjernes fra gruppen %s", "Couldn't update app." => "Kunne ikke opdatere app'en.", +"Wrong password" => "Forkert kodeord", +"No user supplied" => "Intet brugernavn givet", +"Please provide an admin recovery password, otherwise all user data will be lost" => "Angiv venligst en admininstrator gendannelseskode, ellers vil alt brugerdata gå tabt", +"Wrong admin recovery password. Please check the password and try again." => "Forkert admin gendannelseskode. Se venligst koden efter og prøv igen.", +"Back-end doesn't support password change, but the users encryption key was successfully updated." => "Serveren understøtter ikke kodeordsskifte, men brugernes krypteringsnøgle blev opdateret.", "Update to {appversion}" => "Opdatér til {appversion}", "Disable" => "Deaktiver", "Enable" => "Aktiver", diff --git a/settings/l10n/nn_NO.php b/settings/l10n/nn_NO.php index 822a17e783..9eb31a887b 100644 --- a/settings/l10n/nn_NO.php +++ b/settings/l10n/nn_NO.php @@ -16,6 +16,12 @@ $TRANSLATIONS = array( "Unable to add user to group %s" => "Klarte ikkje leggja til brukaren til gruppa %s", "Unable to remove user from group %s" => "Klarte ikkje fjerna brukaren frå gruppa %s", "Couldn't update app." => "Klarte ikkje oppdatera programmet.", +"Wrong password" => "Feil passord", +"No user supplied" => "Ingen brukar gitt", +"Please provide an admin recovery password, otherwise all user data will be lost" => "Ver venleg og gi eit admingjenopprettingspassord, elles vil all brukardata gå tapt.", +"Wrong admin recovery password. Please check the password and try again." => "Feil admingjenopprettingspassord. Ver venleg og sjekk passordet og prøv igjen.", +"Back-end doesn't support password change, but the users encryption key was successfully updated." => "Bakstykket støttar ikkje passordendring, men krypteringsnøkkelen til brukaren blei oppdatert.", +"Unable to change password" => "Klarte ikkje å endra passordet", "Update to {appversion}" => "Oppdater til {appversion}", "Disable" => "Slå av", "Enable" => "Slå på", @@ -27,6 +33,7 @@ $TRANSLATIONS = array( "Error" => "Feil", "Update" => "Oppdater", "Updated" => "Oppdatert", +"Select a profile picture" => "Vel eit profilbilete", "Decrypting files... Please wait, this can take some time." => "Dekrypterer filer … Ver venleg og vent, dette kan ta ei stund.", "Saving..." => "Lagrar …", "deleted" => "sletta", @@ -100,6 +107,13 @@ $TRANSLATIONS = array( "Email" => "E-post", "Your email address" => "Di epost-adresse", "Fill in an email address to enable password recovery" => "Fyll inn e-postadressa di for å gjera passordgjenoppretting mogleg", +"Profile picture" => "Profilbilete", +"Upload new" => "Last opp ny", +"Select new from Files" => "Vel ny frå Filer", +"Remove image" => "Fjern bilete", +"Either png or jpg. Ideally square but you will be able to crop it." => "Anten PNG eller JPG. Helst kvadratisk, men du får moglegheita til å beskjera det.", +"Abort" => "Avbryt", +"Choose as profile image" => "Vel som profilbilete", "Language" => "Språk", "Help translate" => "Hjelp oss å omsetja", "WebDAV" => "WebDAV", From 9e4fe103291133ea78427af18693d93bd78d2bd0 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 25 Sep 2013 10:20:40 +0200 Subject: [PATCH 115/150] add test for txt blacklist --- tests/lib/preview.php | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/lib/preview.php b/tests/lib/preview.php index bebdc12b50..c40b2d03ef 100644 --- a/tests/lib/preview.php +++ b/tests/lib/preview.php @@ -92,6 +92,44 @@ class Preview extends \PHPUnit_Framework_TestCase { $this->assertEquals($image->height(), $maxY); } + public function testTxtBlacklist() { + $user = $this->initFS(); + + $x = 32; + $y = 32; + + $txt = 'random text file'; + $ics = file_get_contents(__DIR__ . '/../data/testcal.ics'); + $vcf = file_get_contents(__DIR__ . '/../data/testcontact.vcf'); + + $rootView = new \OC\Files\View(''); + $rootView->mkdir('/'.$user); + $rootView->mkdir('/'.$user.'/files'); + + $toTest = array('txt', + 'ics', + 'vcf'); + + foreach($toTest as $test) { + $sample = '/'.$user.'/files/test.'.$test; + $rootView->file_put_contents($sample, ${$test}); + $preview = new \OC\Preview($user, 'files/', 'test.'.$test, $x, $y); + $image = $preview->getPreview(); + $resource = $image->resource(); + + //http://stackoverflow.com/questions/5702953/imagecolorat-and-transparency + $colorIndex = imagecolorat($resource, 1, 1); + $colorInfo = imagecolorsforindex($resource, $colorIndex); + $isTransparent = ($colorInfo['alpha'] === 127); + + if($test === 'txt') { + $this->assertEquals($isTransparent, false); + } else { + $this->assertEquals($isTransparent, true); + } + } + } + private function initFS() { if(\OC\Files\Filesystem::getView()){ $user = \OC_User::getUser(); From b2ef978d1069d5e7e172806a9e2426de2717a1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 10:30:48 +0200 Subject: [PATCH 116/150] AppFramework: - get request from the server container - implement registerMiddleWare() - adding getAppName() to app container --- .../dependencyinjection/dicontainer.php | 50 +++++++++---------- lib/public/appframework/iappcontainer.php | 12 +++++ 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 2ef885d7b2..5487826693 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -34,6 +34,8 @@ use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; use OCP\AppFramework\IApi; use OCP\AppFramework\IAppContainer; +use OCP\AppFramework\IMiddleWare; +use OCP\IServerContainer; class DIContainer extends SimpleContainer implements IAppContainer{ @@ -57,31 +59,10 @@ class DIContainer extends SimpleContainer implements IAppContainer{ * Http */ $this['Request'] = $this->share(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'] - ) - ); + /** @var $c SimpleContainer */ + /** @var $server IServerContainer */ + $server = $c->query('ServerContainer'); + return $server->getRequest(); }); $this['Protocol'] = $this->share(function($c){ @@ -138,4 +119,23 @@ class DIContainer extends SimpleContainer implements IAppContainer{ { return $this->query('ServerContainer'); } + + /** + * @param IMiddleWare $middleWare + * @return boolean + */ + function registerMiddleWare(IMiddleWare $middleWare) { + /** @var $dispatcher MiddlewareDispatcher */ + $dispatcher = $this->query('MiddlewareDispatcher'); + $dispatcher->registerMiddleware($middleWare); + + } + + /** + * used to return the appname of the set application + * @return string the name of your application + */ + function getAppName() { + return $this->query('AppName'); + } } diff --git a/lib/public/appframework/iappcontainer.php b/lib/public/appframework/iappcontainer.php index c8f6229dd9..7d3b4b3bac 100644 --- a/lib/public/appframework/iappcontainer.php +++ b/lib/public/appframework/iappcontainer.php @@ -33,6 +33,12 @@ use OCP\IContainer; */ interface IAppContainer extends IContainer{ + /** + * used to return the appname of the set application + * @return string the name of your application + */ + function getAppName(); + /** * @return IApi */ @@ -42,4 +48,10 @@ interface IAppContainer extends IContainer{ * @return \OCP\IServerContainer */ function getServer(); + + /** + * @param IMiddleWare $middleWare + * @return boolean + */ + function registerMiddleWare(IMiddleWare $middleWare); } From b168d5aa3b16501e9cb4eaa3b665939dde5de52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 11:05:24 +0200 Subject: [PATCH 117/150] class API decommissioning part 1 --- lib/appframework/core/api.php | 217 +++---------------------------- lib/public/appframework/iapi.php | 89 +------------ 2 files changed, 21 insertions(+), 285 deletions(-) diff --git a/lib/appframework/core/api.php b/lib/appframework/core/api.php index 337e3b57d6..39522ee3dd 100644 --- a/lib/appframework/core/api.php +++ b/lib/appframework/core/api.php @@ -46,24 +46,6 @@ class API implements IApi{ } - /** - * 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 @@ -73,14 +55,6 @@ class API implements IApi{ } - /** - * 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 @@ -124,79 +98,6 @@ class API implements IApi{ \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 @@ -208,28 +109,6 @@ class API implements IApi{ } - /** - * 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 @@ -293,37 +172,6 @@ class API implements IApi{ } - /** - * 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 @@ -371,26 +219,6 @@ class API implements IApi{ } - /** - * 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 @@ -467,6 +295,26 @@ class API implements IApi{ \OCP\Backgroundjob::addRegularTask($className, $methodName); } + /** + * 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); + } + } + + /** * Tells ownCloud to include a template in the admin overview * @param string $mainPath the path to the main php file without the php @@ -481,23 +329,6 @@ class API implements IApi{ \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 @@ -514,12 +345,4 @@ class API implements IApi{ 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/public/appframework/iapi.php b/lib/public/appframework/iapi.php index 5374f0dcaf..fa6af5f596 100644 --- a/lib/public/appframework/iapi.php +++ b/lib/public/appframework/iapi.php @@ -30,19 +30,6 @@ namespace OCP\AppFramework; */ 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 @@ -51,12 +38,6 @@ interface IApi { 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 @@ -86,53 +67,6 @@ interface IApi { */ 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 @@ -142,28 +76,6 @@ interface IApi { 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 @@ -235,4 +147,5 @@ interface IApi { * @return \OCP\Template a new template */ function getTemplate($templateName, $renderAs='user', $appName=null); + } From 30286c06ab50a04424c415d1007bac66d452208d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 11:05:59 +0200 Subject: [PATCH 118/150] stripos return value check --- lib/server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server.php b/lib/server.php index fccb8fad4d..9de9f5a056 100644 --- a/lib/server.php +++ b/lib/server.php @@ -25,7 +25,7 @@ class Server extends SimpleContainer implements IServerContainer { $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 ) { + if (isset($_SERVER['CONTENT_TYPE']) && stripos($_SERVER['CONTENT_TYPE'],'json') !== false ) { $params = json_decode(file_get_contents('php://input'), true); $params = is_array($params) ? $params: array(); } From 24eb41548eb6fc08849619b9725cbb61679f04f6 Mon Sep 17 00:00:00 2001 From: kondou Date: Wed, 25 Sep 2013 12:57:41 +0200 Subject: [PATCH 119/150] Make it possible to have a different color than the username for placeholder --- core/js/placeholder.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/js/placeholder.js b/core/js/placeholder.js index d63730547d..3c7b11ef46 100644 --- a/core/js/placeholder.js +++ b/core/js/placeholder.js @@ -36,10 +36,22 @@ * *
T
* + * You may also call it like this, to have a different background, than the seed: + * + * $('#albumart').placeholder('The Album Title', 'Album Title'); + * + * Resulting in: + * + *
A
+ * */ (function ($) { - $.fn.placeholder = function(seed) { + $.fn.placeholder = function(seed, text) { + if (typeof(text) === "undefined") { + text = seed; + } + var hash = md5(seed), maxRange = parseInt('ffffffffffffffffffffffffffffffff', 16), hue = parseInt(hash, 16) / maxRange * 256, @@ -56,7 +68,7 @@ this.css('font-size', (height * 0.55) + 'px'); if(seed !== null && seed.length) { - this.html(seed[0].toUpperCase()); + this.html(text[0].toUpperCase()); } }; }(jQuery)); From 0486dc24adef5caf4582f54da00b8d1ee251aae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 14:41:03 +0200 Subject: [PATCH 120/150] collect coverage for all databases again - ci.owncloud.org has more RAM available --- autotest.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/autotest.sh b/autotest.sh index a343f6a25a..83f184fa9c 100755 --- a/autotest.sh +++ b/autotest.sh @@ -142,12 +142,7 @@ EOF rm -rf coverage-html-$1 mkdir coverage-html-$1 php -f enable_all.php - if [ "$1" == "sqlite" ] ; then - # coverage only with sqlite - causes segfault on ci.tmit.eu - reason unknown - phpunit --configuration phpunit-autotest.xml --log-junit autotest-results-$1.xml --coverage-clover autotest-clover-$1.xml --coverage-html coverage-html-$1 $2 $3 - else - phpunit --configuration phpunit-autotest.xml --log-junit autotest-results-$1.xml $2 $3 - fi + phpunit --configuration phpunit-autotest.xml --log-junit autotest-results-$1.xml --coverage-clover autotest-clover-$1.xml --coverage-html coverage-html-$1 $2 $3 } # From 1c976a7c9bd27f1fe3f2b1834fd3cbdf5f235bc1 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 25 Sep 2013 15:03:22 +0200 Subject: [PATCH 121/150] manager checkPassword now returns User object, adjust internal user class accordingly. --- lib/user.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/user.php b/lib/user.php index da774ff86f..b5fd418acd 100644 --- a/lib/user.php +++ b/lib/user.php @@ -419,7 +419,7 @@ class OC_User { $manager = self::getManager(); $username = $manager->checkPassword($uid, $password); if ($username !== false) { - return $manager->get($username)->getUID(); + return $username->getUID(); } return false; } From 09b64535a9099bdf9c71fa96b3aab2e49206ffde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 17:00:20 +0200 Subject: [PATCH 122/150] fixing copyright and add class documentation --- apps/files/appinfo/remote.php | 1 + .../sabre/aborteduploaddetectionplugin.php | 105 ++++++++++++++++++ lib/connector/sabre/directory.php | 13 --- 3 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 lib/connector/sabre/aborteduploaddetectionplugin.php diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 9b114ca2e3..0c1f2e6580 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -48,6 +48,7 @@ $defaults = new OC_Defaults(); $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, $defaults->getName())); $server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend)); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload +$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin()); $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); diff --git a/lib/connector/sabre/aborteduploaddetectionplugin.php b/lib/connector/sabre/aborteduploaddetectionplugin.php new file mode 100644 index 0000000000..745c4a9942 --- /dev/null +++ b/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -0,0 +1,105 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * Class OC_Connector_Sabre_AbortedUploadDetectionPlugin + * + * This plugin will verify if the uploaded data has been stored completely. + * This is done by comparing the content length of the request with the file size on storage. + */ +class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPlugin { + + /** + * Reference to main server object + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * is kept public to allow overwrite for unit testing + * + * @var \OC\Files\View + */ + public $fileView; + + /** + * This initializes the plugin. + * + * This function is called by Sabre_DAV_Server, after + * addPlugin is called. + * + * This method should set up the requires event subscriptions. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + + $server->subscribeEvent('afterCreateFile', array($this, 'afterCreateFile'), 10); + $server->subscribeEvent('afterWriteContent', array($this, 'afterWriteContent'), 10); + } + + function afterCreateFile($path, Sabre_DAV_ICollection $parent) { + + $this->verifyContentLength($path); + + } + + function afterWriteContent($path, Sabre_DAV_IFile $node) { + $path = $path .'/'.$node->getName(); + $this->verifyContentLength($path); + } + + function verifyContentLength($filePath) { + + // ownCloud chunked upload will be handled in it's own plugin + $chunkHeader = $this->server->httpRequest->getHeader('OC-Chunked'); + if ($chunkHeader) { + return; + } + + // compare expected and actual size + $expected = $this->getLength(); + $actual = $this->getFileView()->filesize($filePath); + if ($actual != $expected) { + $this->getFileView()->unlink($filePath); + throw new Sabre_DAV_Exception_BadRequest('expected filesize ' . $expected . ' got ' . $actual); + } + + } + + /** + * @return string + */ + public function getLength() + { + $req = $this->server->httpRequest; + $length = $req->getHeader('X-Expected-Entity-Length'); + if (!$length) { + $length = $req->getHeader('Content-Length'); + } + + return $length; + } + + /** + * @return \OC\Files\View + */ + public function getFileView() + { + if (is_null($this->fileView)) { + // initialize fileView + $this->fileView = \OC\Files\Filesystem::getView(); + } + + return $this->fileView; + } +} diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 3181a4b310..29374f7a6c 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -74,19 +74,6 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa \OC\Files\Filesystem::file_put_contents($partpath, $data); - //detect aborted upload - if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT' ) { - if (isset($_SERVER['CONTENT_LENGTH'])) { - $expected = $_SERVER['CONTENT_LENGTH']; - $actual = \OC\Files\Filesystem::filesize($partpath); - if ($actual != $expected) { - \OC\Files\Filesystem::unlink($partpath); - throw new Sabre_DAV_Exception_BadRequest( - 'expected filesize ' . $expected . ' got ' . $actual); - } - } - } - // rename to correct path \OC\Files\Filesystem::rename($partpath, $newPath); From 5e27ac4b1ade3a78941ced1eef5aaa2d2df0cedf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 17:17:29 +0200 Subject: [PATCH 123/150] $path already contains the full path to the file --- lib/connector/sabre/aborteduploaddetectionplugin.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/connector/sabre/aborteduploaddetectionplugin.php b/lib/connector/sabre/aborteduploaddetectionplugin.php index 745c4a9942..1173ff2f9a 100644 --- a/lib/connector/sabre/aborteduploaddetectionplugin.php +++ b/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -54,7 +54,6 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl } function afterWriteContent($path, Sabre_DAV_IFile $node) { - $path = $path .'/'.$node->getName(); $this->verifyContentLength($path); } From 5e7a7b3f6187365c60e63dfed8699e525be45a0b Mon Sep 17 00:00:00 2001 From: kondou Date: Wed, 25 Sep 2013 17:19:38 +0200 Subject: [PATCH 124/150] Shorten optional text-argument processing --- core/js/placeholder.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/js/placeholder.js b/core/js/placeholder.js index 3c7b11ef46..ee2a8ce84c 100644 --- a/core/js/placeholder.js +++ b/core/js/placeholder.js @@ -48,9 +48,8 @@ (function ($) { $.fn.placeholder = function(seed, text) { - if (typeof(text) === "undefined") { - text = seed; - } + // set optional argument "text" to value of "seed" if undefined + text = text || seed; var hash = md5(seed), maxRange = parseInt('ffffffffffffffffffffffffffffffff', 16), From 0c44cdd4eaa82687bce4dbbb24338b16a3f4ed13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 17:28:45 +0200 Subject: [PATCH 125/150] remove unneccessary code --- .../sabre/aborteduploaddetectionplugin.php | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/connector/sabre/aborteduploaddetectionplugin.php b/lib/connector/sabre/aborteduploaddetectionplugin.php index 1173ff2f9a..74c26f41b6 100644 --- a/lib/connector/sabre/aborteduploaddetectionplugin.php +++ b/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -43,21 +43,16 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl $this->server = $server; - $server->subscribeEvent('afterCreateFile', array($this, 'afterCreateFile'), 10); - $server->subscribeEvent('afterWriteContent', array($this, 'afterWriteContent'), 10); + $server->subscribeEvent('afterCreateFile', array($this, 'verifyContentLength'), 10); + $server->subscribeEvent('afterWriteContent', array($this, 'verifyContentLength'), 10); } - function afterCreateFile($path, Sabre_DAV_ICollection $parent) { - - $this->verifyContentLength($path); - - } - - function afterWriteContent($path, Sabre_DAV_IFile $node) { - $this->verifyContentLength($path); - } - - function verifyContentLength($filePath) { + /** + * @param $filePath + * @param Sabre_DAV_INode $node + * @throws Sabre_DAV_Exception_BadRequest + */ + public function verifyContentLength($filePath, Sabre_DAV_INode $node = null) { // ownCloud chunked upload will be handled in it's own plugin $chunkHeader = $this->server->httpRequest->getHeader('OC-Chunked'); @@ -67,6 +62,9 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl // compare expected and actual size $expected = $this->getLength(); + if (!$expected) { + return; + } $actual = $this->getFileView()->filesize($filePath); if ($actual != $expected) { $this->getFileView()->unlink($filePath); From 3fa5271f10369645564f19a5706e23e0660bbbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 17:34:28 +0200 Subject: [PATCH 126/150] adding unit tests --- .../sabre/aborteduploaddetectionplugin.php | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 tests/lib/connector/sabre/aborteduploaddetectionplugin.php diff --git a/tests/lib/connector/sabre/aborteduploaddetectionplugin.php b/tests/lib/connector/sabre/aborteduploaddetectionplugin.php new file mode 100644 index 0000000000..8237bdbd9e --- /dev/null +++ b/tests/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -0,0 +1,93 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Framework_TestCase { + + /** + * @var Sabre_DAV_Server + */ + private $server; + + /** + * @var OC_Connector_Sabre_AbortedUploadDetectionPlugin + */ + private $plugin; + + public function setUp() { + $this->server = new Sabre_DAV_Server(); + $this->plugin = new OC_Connector_Sabre_AbortedUploadDetectionPlugin(); + $this->plugin->initialize($this->server); + } + + /** + * @dataProvider lengthProvider + */ + public function testLength($expected, $headers) + { + $this->server->httpRequest = new Sabre_HTTP_Request($headers); + $length = $this->plugin->getLength(); + $this->assertEquals($expected, $length); + } + + /** + * @dataProvider verifyContentLengthProvider + */ + public function testVerifyContentLength($fileSize, $headers) + { + $this->plugin->fileView = $this->buildFileViewMock($fileSize); + + $this->server->httpRequest = new Sabre_HTTP_Request($headers); + $this->plugin->verifyContentLength('foo.txt'); + $this->assertTrue(true); + } + + /** + * @dataProvider verifyContentLengthFailedProvider + * @expectedException Sabre_DAV_Exception_BadRequest + */ + public function testVerifyContentLengthFailed($fileSize, $headers) + { + $this->plugin->fileView = $this->buildFileViewMock($fileSize); + + $this->server->httpRequest = new Sabre_HTTP_Request($headers); + $this->plugin->verifyContentLength('foo.txt'); + } + + public function verifyContentLengthProvider() { + return array( + array(1024, array()), + array(1024, array('HTTP_X_EXPECTED_ENTITY_LENGTH' => '1024')), + array(512, array('HTTP_CONTENT_LENGTH' => '512')), + ); + } + + public function verifyContentLengthFailedProvider() { + return array( + array(1025, array('HTTP_X_EXPECTED_ENTITY_LENGTH' => '1024')), + array(525, array('HTTP_CONTENT_LENGTH' => '512')), + ); + } + + public function lengthProvider() { + return array( + array(null, array()), + array(1024, array('HTTP_X_EXPECTED_ENTITY_LENGTH' => '1024')), + array(512, array('HTTP_CONTENT_LENGTH' => '512')), + array(2048, array('HTTP_X_EXPECTED_ENTITY_LENGTH' => '2048', 'HTTP_CONTENT_LENGTH' => '1024')), + ); + } + + private function buildFileViewMock($fileSize) { + // mock filesysten + $view = $this->getMock('\OC\Files\View', array('filesize', 'unlink'), array(), '', FALSE); + $view->expects($this->any())->method('filesize')->withAnyParameters()->will($this->returnValue($fileSize)); + + return $view; + } + +} From 826c6bec8f9a8137e0a2e7660389c728b59f95d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 25 Sep 2013 17:41:16 +0200 Subject: [PATCH 127/150] expect unlinkto be called --- tests/lib/connector/sabre/aborteduploaddetectionplugin.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/lib/connector/sabre/aborteduploaddetectionplugin.php b/tests/lib/connector/sabre/aborteduploaddetectionplugin.php index 8237bdbd9e..bef0e4c4d7 100644 --- a/tests/lib/connector/sabre/aborteduploaddetectionplugin.php +++ b/tests/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -54,6 +54,10 @@ class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Frame { $this->plugin->fileView = $this->buildFileViewMock($fileSize); + // we expect unlink to be called + $this->plugin->fileView->expects($this->once())->method('unlink'); + + $this->server->httpRequest = new Sabre_HTTP_Request($headers); $this->plugin->verifyContentLength('foo.txt'); } From 71bbb2ea8bd76697cc1785fe4324b54973c410b9 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 25 Sep 2013 17:44:05 +0200 Subject: [PATCH 128/150] check if key exists before reading it --- apps/files_encryption/lib/keymanager.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 9be3dda7ce..7143fcff0f 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -40,11 +40,14 @@ class Keymanager { public static function getPrivateKey(\OC_FilesystemView $view, $user) { $path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key'; + $key = false; $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - $key = $view->file_get_contents($path); + if ($view->file_exists($path)) { + $key = $view->file_get_contents($path); + } \OC_FileProxy::$enabled = $proxyStatus; From b5ac672864b6d7ac892de0b4c36debd61e4a1588 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 25 Sep 2013 19:15:27 +0200 Subject: [PATCH 129/150] Missing Test for the previous commit --- tests/lib/user.php | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/lib/user.php diff --git a/tests/lib/user.php b/tests/lib/user.php new file mode 100644 index 0000000000..66c7f3f0d7 --- /dev/null +++ b/tests/lib/user.php @@ -0,0 +1,43 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test; + +use OC\Hooks\PublicEmitter; + +class User extends \PHPUnit_Framework_TestCase { + + public function testCheckPassword() { + /** + * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend + */ + $backend = $this->getMock('\OC_User_Dummy'); + $backend->expects($this->once()) + ->method('checkPassword') + ->with($this->equalTo('foo'), $this->equalTo('bar')) + ->will($this->returnValue('foo')); + + $backend->expects($this->any()) + ->method('implementsActions') + ->will($this->returnCallback(function ($actions) { + if ($actions === \OC_USER_BACKEND_CHECK_PASSWORD) { + return true; + } else { + return false; + } + })); + + $manager = \OC_User::getManager(); + $manager->registerBackend($backend); + + $uid = \OC_User::checkPassword('foo', 'bar'); + $this->assertEquals($uid, 'foo'); + } + +} \ No newline at end of file From 0b98427536d9ff951577273af096c0a4ab219a83 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 25 Sep 2013 19:23:07 +0200 Subject: [PATCH 130/150] fix check if app is enabled --- apps/files_encryption/lib/proxy.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index eb7ba60cb9..4ec810a519 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -50,9 +50,8 @@ class Proxy extends \OC_FileProxy { private static function shouldEncrypt($path) { if (is_null(self::$enableEncryption)) { - if ( - \OCP\Config::getAppValue('files_encryption', 'enable_encryption', 'true') === 'true' + \OCP\App::isEnabled('files_encryption') === true && Crypt::mode() === 'server' ) { @@ -200,7 +199,7 @@ class Proxy extends \OC_FileProxy { */ public function preUnlink($path) { - // let the trashbin handle this + // let the trashbin handle this if (\OCP\App::isEnabled('files_trashbin')) { return true; } @@ -291,7 +290,7 @@ class Proxy extends \OC_FileProxy { // Close the original encrypted file fclose($result); - // Open the file using the crypto stream wrapper + // Open the file using the crypto stream wrapper // protocol and let it do the decryption work instead $result = fopen('crypt://' . $path, $meta['mode']); From b11d8799c17b24e7823de8d8dc171fb78f9b8442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 26 Sep 2013 10:50:15 +0200 Subject: [PATCH 131/150] adding unit tests for ObjectTree::move() --- lib/connector/sabre/objecttree.php | 44 +++++++--- tests/lib/connector/sabre/objecttree.php | 104 +++++++++++++++++++++++ 2 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 tests/lib/connector/sabre/objecttree.php diff --git a/lib/connector/sabre/objecttree.php b/lib/connector/sabre/objecttree.php index 7accf98c8e..80c3840b99 100644 --- a/lib/connector/sabre/objecttree.php +++ b/lib/connector/sabre/objecttree.php @@ -11,6 +11,14 @@ namespace OC\Connector\Sabre; use OC\Files\Filesystem; class ObjectTree extends \Sabre_DAV_ObjectTree { + + /** + * keep this public to allow mock injection during unit test + * + * @var \OC\Files\View + */ + public $fileView; + /** * Returns the INode object for the requested path * @@ -21,14 +29,16 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { public function getNodeForPath($path) { $path = trim($path, '/'); - if (isset($this->cache[$path])) return $this->cache[$path]; + if (isset($this->cache[$path])) { + return $this->cache[$path]; + } // Is it the root node? if (!strlen($path)) { return $this->rootNode; } - $info = Filesystem::getFileInfo($path); + $info = $this->getFileView()->getFileInfo($path); if (!$info) { throw new \Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); @@ -65,25 +75,21 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destinationPath); // check update privileges - if ($sourceDir === $destinationDir) { - // for renaming it's enough to check if the sourcePath can be updated - if (!\OC\Files\Filesystem::isUpdatable($sourcePath)) { - throw new \Sabre_DAV_Exception_Forbidden(); - } - } else { + $fs = $this->getFileView(); + if (!$fs->isUpdatable($sourcePath)) { + throw new \Sabre_DAV_Exception_Forbidden(); + } + if ($sourceDir !== $destinationDir) { // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir - if (!\OC\Files\Filesystem::isUpdatable($sourcePath)) { + if (!$fs->isUpdatable($sourceDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } - if (!\OC\Files\Filesystem::isUpdatable($sourceDir)) { - throw new \Sabre_DAV_Exception_Forbidden(); - } - if (!\OC\Files\Filesystem::isUpdatable($destinationDir)) { + if (!$fs->isUpdatable($destinationDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } } - $renameOkay = Filesystem::rename($sourcePath, $destinationPath); + $renameOkay = $fs->rename($sourcePath, $destinationPath); if (!$renameOkay) { throw new \Sabre_DAV_Exception_Forbidden(''); } @@ -123,4 +129,14 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destination); $this->markDirty($destinationDir); } + + /** + * @return \OC\Files\View + */ + public function getFileView() { + if (is_null($this->fileView)) { + $this->fileView = \OC\Files\Filesystem::getView(); + } + return $this->fileView; + } } diff --git a/tests/lib/connector/sabre/objecttree.php b/tests/lib/connector/sabre/objecttree.php new file mode 100644 index 0000000000..c920441c8b --- /dev/null +++ b/tests/lib/connector/sabre/objecttree.php @@ -0,0 +1,104 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\OC\Connector\Sabre; + + +use OC_Connector_Sabre_Directory; +use PHPUnit_Framework_TestCase; +use Sabre_DAV_Exception_Forbidden; + +class TestDoubleFileView extends \OC\Files\View{ + + public function __construct($updatables, $canRename = true) { + $this->updatables = $updatables; + $this->canRename = $canRename; + } + + public function isUpdatable($path) { + return $this->updatables[$path]; + } + + public function rename($path1, $path2) { + return $this->canRename; + } +} + +class ObjectTree extends PHPUnit_Framework_TestCase { + + /** + * @dataProvider moveFailedProvider + * @expectedException Sabre_DAV_Exception_Forbidden + */ + public function testMoveFailed($source, $dest, $updatables) { + $rootDir = new OC_Connector_Sabre_Directory(''); + $objectTree = $this->getMock('\OC\Connector\Sabre\ObjectTree', + array('nodeExists', 'getNodeForPath'), + array($rootDir)); + + $objectTree->expects($this->once()) + ->method('getNodeForPath') + ->with($this->identicalTo('a/b')) + ->will($this->returnValue(false)); + + /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ + $objectTree->fileView = new TestDoubleFileView($updatables); + $objectTree->move($source, $dest); + } + + /** + * @dataProvider moveSuccessProvider + */ + public function testMoveSuccess($source, $dest, $updatables) { + $rootDir = new OC_Connector_Sabre_Directory(''); + $objectTree = $this->getMock('\OC\Connector\Sabre\ObjectTree', + array('nodeExists', 'getNodeForPath'), + array($rootDir)); + + $objectTree->expects($this->once()) + ->method('getNodeForPath') + ->with($this->identicalTo('a/b')) + ->will($this->returnValue(false)); + + /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ + $objectTree->fileView = new TestDoubleFileView($updatables); + $objectTree->move($source, $dest); + $this->assertTrue(true); + } + + function moveFailedProvider() { + return array( + array('a/b', 'a/c', array('a' => false, 'a/b' => false, 'a/c' => false)), + array('a/b', 'b/b', array('a' => false, 'a/b' => false, 'b' => false, 'b/b' => false)), + array('a/b', 'b/b', array('a' => false, 'a/b' => true, 'b' => false, 'b/b' => false)), + array('a/b', 'b/b', array('a' => true, 'a/b' => true, 'b' => false, 'b/b' => false)), + ); + } + + function moveSuccessProvider() { + return array( + array('a/b', 'a/c', array('a' => false, 'a/b' => true, 'a/c' => false)), + array('a/b', 'b/b', array('a' => true, 'a/b' => true, 'b' => true, 'b/b' => false)), + ); + } + +// private function buildFileViewMock($updatables) { +// // mock filesysten +// $view = $this->getMock('\OC\Files\View', array('isUpdatable'), array(), '', FALSE); +// +// foreach ($updatables as $path => $updatable) { +// $view->expects($this->any()) +// ->method('isUpdatable') +// ->with($this->identicalTo($path)) +// ->will($this->returnValue($updatable)); +// } +// +// return $view; +// } + +} From d1b5d65622122b71dfb46cd5d1addf514552032a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 26 Sep 2013 12:02:06 +0200 Subject: [PATCH 132/150] run unit tests for apps as well --- tests/bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index fb667263e4..d273676f4c 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,7 +1,7 @@ Date: Thu, 26 Sep 2013 13:34:47 +0200 Subject: [PATCH 133/150] prelogin apps have to be loaded within setupBackend() otherwise required classes cannot be loaded --- lib/user.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/user.php b/lib/user.php index da774ff86f..62c2f318cc 100644 --- a/lib/user.php +++ b/lib/user.php @@ -177,6 +177,7 @@ class OC_User { * setup the configured backends in config.php */ public static function setupBackends() { + OC_App::loadApps(array('prelogin')); $backends = OC_Config::getValue('user_backends', array()); foreach ($backends as $i => $config) { $class = $config['class']; From f8933eaf922c82c369b48275510ad6920ac70b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 26 Sep 2013 14:03:04 +0200 Subject: [PATCH 134/150] Remove $RUNTIME_NOAPPS - setting to false was not enough --- tests/bootstrap.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d273676f4c..581cfcff9f 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,7 +1,5 @@ Date: Thu, 26 Sep 2013 19:05:47 +0200 Subject: [PATCH 135/150] phpunit.xml: Port code coverage excludes from autotest to dist. --- tests/phpunit.xml.dist | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index 25dfc64cfe..71a4ff2762 100644 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -11,7 +11,21 @@ .. ../3rdparty + ../apps/files/l10n + ../apps/files_external/l10n + ../apps/files_external/3rdparty + ../apps/files_versions/l10n + ../apps/files_encryption/l10n + ../apps/files_encryption/3rdparty + ../apps/files_sharing/l10n + ../apps/files_trashbin/l10n + ../apps/user_ldap/l10n + ../apps/user_webdavauth/l10n ../lib/MDB2 + ../lib/l10n + ../core/l10n + ../settings/l10n + ../tests From f31d31844e0a498ccb0364fa618d55b33cc30236 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Fri, 27 Sep 2013 00:02:30 -0400 Subject: [PATCH 136/150] [tx-robot] updated from transifex --- apps/files/l10n/el.php | 1 + apps/files/l10n/hu_HU.php | 13 ++++++++--- apps/files_trashbin/l10n/hu_HU.php | 4 ++-- apps/user_ldap/l10n/hu_HU.php | 8 +++++++ core/l10n/cs_CZ.php | 1 + core/l10n/da.php | 2 ++ core/l10n/fr.php | 5 +++++ l10n/cs_CZ/core.po | 22 +++++++++---------- l10n/da/core.po | 22 +++++++++---------- l10n/da/lib.po | 18 +++++++-------- l10n/da/settings.po | 6 ++--- l10n/el/files.po | 11 +++++----- l10n/fr/core.po | 16 +++++++------- l10n/fr/settings.po | 30 ++++++++++++------------- l10n/hu_HU/files.po | 34 ++++++++++++++--------------- l10n/hu_HU/files_trashbin.po | 30 ++++++++++++------------- l10n/hu_HU/user_ldap.po | 22 +++++++++---------- l10n/templates/core.pot | 2 +- l10n/templates/files.pot | 2 +- l10n/templates/files_encryption.pot | 2 +- l10n/templates/files_external.pot | 2 +- l10n/templates/files_sharing.pot | 2 +- l10n/templates/files_trashbin.pot | 2 +- l10n/templates/files_versions.pot | 2 +- l10n/templates/lib.pot | 2 +- l10n/templates/settings.pot | 2 +- l10n/templates/user_ldap.pot | 2 +- l10n/templates/user_webdavauth.pot | 2 +- lib/l10n/da.php | 3 +++ settings/l10n/da.php | 1 + settings/l10n/fr.php | 3 +++ 31 files changed, 153 insertions(+), 121 deletions(-) diff --git a/apps/files/l10n/el.php b/apps/files/l10n/el.php index 37a61c6b95..de524f4dd9 100644 --- a/apps/files/l10n/el.php +++ b/apps/files/l10n/el.php @@ -41,6 +41,7 @@ $TRANSLATIONS = array( "Your storage is almost full ({usedSpacePercent}%)" => "Ο αποθηκευτικός χώρος είναι σχεδόν γεμάτος ({usedSpacePercent}%)", "Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "Η κρυπτογράφηση απενεργοποιήθηκε, αλλά τα αρχεία σας είναι ακόμα κρυπτογραφημένα. Παρακαλούμε απενεργοποιήσετε την κρυπτογράφηση αρχείων από τις προσωπικές σας ρυθμίσεις", "Your download is being prepared. This might take some time if the files are big." => "Η λήψη προετοιμάζεται. Αυτό μπορεί να πάρει ώρα εάν τα αρχεία έχουν μεγάλο μέγεθος.", +"Error moving file" => "Σφάλμα κατά τη μετακίνηση του αρχείου", "Name" => "Όνομα", "Size" => "Μέγεθος", "Modified" => "Τροποποιήθηκε", diff --git a/apps/files/l10n/hu_HU.php b/apps/files/l10n/hu_HU.php index 5d313ff248..4dd6a13d32 100644 --- a/apps/files/l10n/hu_HU.php +++ b/apps/files/l10n/hu_HU.php @@ -13,10 +13,14 @@ $TRANSLATIONS = array( "Missing a temporary folder" => "Hiányzik egy ideiglenes mappa", "Failed to write to disk" => "Nem sikerült a lemezre történő írás", "Not enough storage available" => "Nincs elég szabad hely.", +"Upload failed. Could not get file info." => "A feltöltés nem sikerült. Az állományt leíró információk nem érhetők el.", +"Upload failed. Could not find uploaded file" => "A feltöltés nem sikerült. Nem található a feltöltendő állomány.", "Invalid directory." => "Érvénytelen mappa.", "Files" => "Fájlok", +"Unable to upload {filename} as it is a directory or has 0 bytes" => "A(z) {filename} állomány nem tölthető fel, mert ez vagy egy mappa, vagy pedig 0 bájtból áll.", "Not enough space available" => "Nincs elég szabad hely", "Upload cancelled." => "A feltöltést megszakítottuk.", +"Could not get result from server." => "A kiszolgálótól nem kapható meg az eredmény.", "File upload is in progress. Leaving the page now will cancel the upload." => "Fájlfeltöltés van folyamatban. Az oldal elhagyása megszakítja a feltöltést.", "URL cannot be empty." => "Az URL nem lehet semmi.", "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Érvénytelen mappanév. A 'Shared' az ownCloud számára fenntartott elnevezés", @@ -31,15 +35,18 @@ $TRANSLATIONS = array( "cancel" => "mégse", "replaced {new_name} with {old_name}" => "{new_name} fájlt kicseréltük ezzel: {old_name}", "undo" => "visszavonás", -"_%n folder_::_%n folders_" => array("",""), -"_%n file_::_%n files_" => array("",""), -"_Uploading %n file_::_Uploading %n files_" => array("",""), +"_%n folder_::_%n folders_" => array("%n mappa","%n mappa"), +"_%n file_::_%n files_" => array("%n állomány","%n állomány"), +"{dirs} and {files}" => "{dirs} és {files}", +"_Uploading %n file_::_Uploading %n files_" => array("%n állomány feltöltése","%n állomány feltöltése"), "'.' is an invalid file name." => "'.' fájlnév érvénytelen.", "File name cannot be empty." => "A fájlnév nem lehet semmi.", "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed." => "Érvénytelen elnevezés. Ezek a karakterek nem használhatók: '\\', '/', '<', '>', ':', '\"', '|', '?' és '*'", "Your storage is full, files can not be updated or synced anymore!" => "A tároló tele van, a fájlok nem frissíthetőek vagy szinkronizálhatóak a jövőben.", "Your storage is almost full ({usedSpacePercent}%)" => "A tároló majdnem tele van ({usedSpacePercent}%)", +"Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files." => "A titkosítási funkciót kikapcsolták, de az Ön állományai még mindig titkosított állapotban vannak. A személyes beállításoknál tudja a titkosítást feloldani.", "Your download is being prepared. This might take some time if the files are big." => "Készül a letöltendő állomány. Ez eltarthat egy ideig, ha nagyok a fájlok.", +"Error moving file" => "Az állomány áthelyezése nem sikerült.", "Name" => "Név", "Size" => "Méret", "Modified" => "Módosítva", diff --git a/apps/files_trashbin/l10n/hu_HU.php b/apps/files_trashbin/l10n/hu_HU.php index aac6cf7800..766ddcbce4 100644 --- a/apps/files_trashbin/l10n/hu_HU.php +++ b/apps/files_trashbin/l10n/hu_HU.php @@ -8,8 +8,8 @@ $TRANSLATIONS = array( "Delete permanently" => "Végleges törlés", "Name" => "Név", "Deleted" => "Törölve", -"_%n folder_::_%n folders_" => array("",""), -"_%n file_::_%n files_" => array("",""), +"_%n folder_::_%n folders_" => array("","%n mappa"), +"_%n file_::_%n files_" => array("","%n állomány"), "restored" => "visszaállítva", "Nothing in here. Your trash bin is empty!" => "Itt nincs semmi. Az Ön szemetes mappája üres!", "Restore" => "Visszaállítás", diff --git a/apps/user_ldap/l10n/hu_HU.php b/apps/user_ldap/l10n/hu_HU.php index 6961869f3e..b41cf98e2b 100644 --- a/apps/user_ldap/l10n/hu_HU.php +++ b/apps/user_ldap/l10n/hu_HU.php @@ -16,6 +16,7 @@ $TRANSLATIONS = array( "Connection test failed" => "A kapcsolatellenőrzés eredménye: nem sikerült", "Do you really want to delete the current Server Configuration?" => "Tényleg törölni szeretné a kiszolgáló beállításait?", "Confirm Deletion" => "A törlés megerősítése", +"Warning: Apps user_ldap and user_webdavauth are incompatible. You may experience unexpected behavior. Please ask your system administrator to disable one of them." => "Figyelem: a user_ldap és user_webdavauth alkalmazások nem kompatibilisek. Együttes használatuk váratlan eredményekhez vezethet. Kérje meg a rendszergazdát, hogy a kettő közül kapcsolja ki az egyiket.", "Warning: The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it." => "Figyelmeztetés: Az LDAP PHP modul nincs telepítve, ezért ez az alrendszer nem fog működni. Kérje meg a rendszergazdát, hogy telepítse!", "Server configuration" => "A kiszolgálók beállításai", "Add Server Configuration" => "Új kiszolgáló beállításának hozzáadása", @@ -29,8 +30,11 @@ $TRANSLATIONS = array( "Password" => "Jelszó", "For anonymous access, leave DN and Password empty." => "Bejelentkezés nélküli eléréshez ne töltse ki a DN és Jelszó mezőket!", "User Login Filter" => "Szűrő a bejelentkezéshez", +"Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" => "Ez a szűrő érvényes a bejelentkezés megkísérlésekor. Ekkor az %%uid változó helyére a bejelentkezési név kerül. Például: \"uid=%%uid\"", "User List Filter" => "A felhasználók szűrője", +"Defines the filter to apply, when retrieving users (no placeholders). Example: \"objectClass=person\"" => "Ez a szűrő érvényes a felhasználók listázásakor (nincs helyettesíthető változó). Például: \"objectClass=person\"", "Group Filter" => "A csoportok szűrője", +"Defines the filter to apply, when retrieving groups (no placeholders). Example: \"objectClass=posixGroup\"" => "Ez a szűrő érvényes a csoportok listázásakor (nincs helyettesíthető változó). Például: \"objectClass=posixGroup\"", "Connection Settings" => "Kapcsolati beállítások", "Configuration Active" => "A beállítás aktív", "When unchecked, this configuration will be skipped." => "Ha nincs kipipálva, ez a beállítás kihagyódik.", @@ -39,19 +43,23 @@ $TRANSLATIONS = array( "Give an optional backup host. It must be a replica of the main LDAP/AD server." => "Adjon meg egy opcionális másodkiszolgálót. Ez a fő LDAP/AD kiszolgáló szinkron másolata (replikája) kell legyen.", "Backup (Replica) Port" => "A másodkiszolgáló (replika) portszáma", "Disable Main Server" => "A fő szerver kihagyása", +"Only connect to the replica server." => "Csak a másodlagos (másolati) kiszolgálóhoz kapcsolódjunk.", "Use TLS" => "Használjunk TLS-t", "Do not use it additionally for LDAPS connections, it will fail." => "LDAPS kapcsolatok esetén ne kapcsoljuk be, mert nem fog működni.", "Case insensitve LDAP server (Windows)" => "Az LDAP-kiszolgáló nem tesz különbséget a kis- és nagybetűk között (Windows)", "Turn off SSL certificate validation." => "Ne ellenőrizzük az SSL-tanúsítvány érvényességét", +"Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Használata nem javasolt (kivéve tesztelési céllal). Ha a kapcsolat csak ezzel a beállítással működik, akkor importálja az LDAP-kiszolgáló SSL tanúsítványát a(z) %s kiszolgálóra!", "Cache Time-To-Live" => "A gyorsítótár tárolási időtartama", "in seconds. A change empties the cache." => "másodpercben. A változtatás törli a cache tartalmát.", "Directory Settings" => "Címtár beállítások", "User Display Name Field" => "A felhasználónév mezője", +"The LDAP attribute to use to generate the user's display name." => "Ebből az LDAP attribútumból képződik a felhasználó megjelenítendő neve.", "Base User Tree" => "A felhasználói fa gyökere", "One User Base DN per line" => "Soronként egy felhasználói fa gyökerét adhatjuk meg", "User Search Attributes" => "A felhasználók lekérdezett attribútumai", "Optional; one attribute per line" => "Nem kötelező megadni, soronként egy attribútum", "Group Display Name Field" => "A csoport nevének mezője", +"The LDAP attribute to use to generate the groups's display name." => "Ebből az LDAP attribútumból képződik a csoport megjelenítendő neve.", "Base Group Tree" => "A csoportfa gyökere", "One Group Base DN per line" => "Soronként egy csoportfa gyökerét adhatjuk meg", "Group Search Attributes" => "A csoportok lekérdezett attribútumai", diff --git a/core/l10n/cs_CZ.php b/core/l10n/cs_CZ.php index 449a49f568..abed4a0fda 100644 --- a/core/l10n/cs_CZ.php +++ b/core/l10n/cs_CZ.php @@ -59,6 +59,7 @@ $TRANSLATIONS = array( "Ok" => "Ok", "Error loading message template: {error}" => "Chyba při nahrávání šablony zprávy: {error}", "_{count} file conflict_::_{count} file conflicts_" => array("","",""), +"One file conflict" => "Jeden konflikt souboru", "Cancel" => "Zrušit", "The object type is not specified." => "Není určen typ objektu.", "Error" => "Chyba", diff --git a/core/l10n/da.php b/core/l10n/da.php index e2399fdc5c..8938f2107f 100644 --- a/core/l10n/da.php +++ b/core/l10n/da.php @@ -16,6 +16,8 @@ $TRANSLATIONS = array( "Error adding %s to favorites." => "Fejl ved tilføjelse af %s til favoritter.", "No categories selected for deletion." => "Ingen kategorier valgt", "Error removing %s from favorites." => "Fejl ved fjernelse af %s fra favoritter.", +"Unknown filetype" => "Ukendt filtype", +"Invalid image" => "Ugyldigt billede", "Sunday" => "Søndag", "Monday" => "Mandag", "Tuesday" => "Tirsdag", diff --git a/core/l10n/fr.php b/core/l10n/fr.php index 29489e86b7..e7cb75e53f 100644 --- a/core/l10n/fr.php +++ b/core/l10n/fr.php @@ -19,6 +19,8 @@ $TRANSLATIONS = array( "No image or file provided" => "Aucune image ou fichier fourni", "Unknown filetype" => "Type de fichier inconnu", "Invalid image" => "Image invalide", +"No temporary profile picture available, try again" => "Aucune image temporaire disponible pour le profil. Essayez à nouveau.", +"No crop data provided" => "Aucune donnée de culture fournie", "Sunday" => "Dimanche", "Monday" => "Lundi", "Tuesday" => "Mardi", @@ -61,7 +63,10 @@ $TRANSLATIONS = array( "Which files do you want to keep?" => "Quels fichiers désirez-vous garder ?", "If you select both versions, the copied file will have a number added to its name." => "Si vous sélectionnez les deux versions, un nombre sera ajouté au nom du fichier copié.", "Cancel" => "Annuler", +"Continue" => "Poursuivre", +"(all selected)" => "(tous sélectionnés)", "({count} selected)" => "({count} sélectionnés)", +"Error loading file exists template" => "Erreur de chargement du modèle de fichier existant", "The object type is not specified." => "Le type d'objet n'est pas spécifié.", "Error" => "Erreur", "The app name is not specified." => "Le nom de l'application n'est pas spécifié.", diff --git a/l10n/cs_CZ/core.po b/l10n/cs_CZ/core.po index 61a7e861b2..40b693b988 100644 --- a/l10n/cs_CZ/core.po +++ b/l10n/cs_CZ/core.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-25 10:50+0000\n" +"Last-Translator: pstast \n" "Language-Team: Czech (Czech Republic) (http://www.transifex.com/projects/p/owncloud/language/cs_CZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -284,7 +284,7 @@ msgstr[2] "" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "" +msgstr "Jeden konflikt souboru" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" @@ -325,7 +325,7 @@ msgstr "Není určen typ objektu." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Chyba" @@ -345,7 +345,7 @@ msgstr "Sdílené" msgid "Share" msgstr "Sdílet" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Chyba při sdílení" @@ -445,23 +445,23 @@ msgstr "smazat" msgid "share" msgstr "sdílet" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Chráněno heslem" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Chyba při odstraňování data vypršení platnosti" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Chyba při nastavení data vypršení platnosti" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Odesílám ..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "E-mail odeslán" diff --git a/l10n/da/core.po b/l10n/da/core.po index 690bb46c21..89530e0745 100644 --- a/l10n/da/core.po +++ b/l10n/da/core.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-24 17:20+0000\n" "Last-Translator: I Robot \n" "Language-Team: Danish (http://www.transifex.com/projects/p/owncloud/language/da/)\n" "MIME-Version: 1.0\n" @@ -100,11 +100,11 @@ msgstr "" #: avatar/controller.php:81 msgid "Unknown filetype" -msgstr "" +msgstr "Ukendt filtype" #: avatar/controller.php:85 msgid "Invalid image" -msgstr "" +msgstr "Ugyldigt billede" #: avatar/controller.php:115 avatar/controller.php:142 msgid "No temporary profile picture available, try again" @@ -319,7 +319,7 @@ msgstr "Objekttypen er ikke angivet." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Fejl" @@ -339,7 +339,7 @@ msgstr "Delt" msgid "Share" msgstr "Del" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Fejl under deling" @@ -439,23 +439,23 @@ msgstr "slet" msgid "share" msgstr "del" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Beskyttet med adgangskode" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Fejl ved fjernelse af udløbsdato" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Fejl under sætning af udløbsdato" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Sender ..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "E-mail afsendt" diff --git a/l10n/da/lib.po b/l10n/da/lib.po index 0bacfcf5e8..7d01d7a969 100644 --- a/l10n/da/lib.po +++ b/l10n/da/lib.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-16 11:33-0400\n" -"PO-Revision-Date: 2013-09-16 15:34+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-24 17:20+0000\n" +"Last-Translator: Sappe\n" "Language-Team: Danish (http://www.transifex.com/projects/p/owncloud/language/da/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -58,15 +58,15 @@ msgstr "Upgradering af \"%s\" fejlede" #: avatar.php:56 msgid "Custom profile pictures don't work with encryption yet" -msgstr "" +msgstr "Personligt profilbillede virker endnu ikke sammen med kryptering" #: avatar.php:64 msgid "Unknown filetype" -msgstr "" +msgstr "Ukendt filtype" #: avatar.php:69 msgid "Invalid image" -msgstr "" +msgstr "Ugyldigt billede" #: defaults.php:35 msgid "web services under your control" @@ -167,15 +167,15 @@ msgstr "Adgangsfejl" msgid "Token expired. Please reload page." msgstr "Adgang er udløbet. Genindlæs siden." -#: search/provider/file.php:17 search/provider/file.php:35 +#: search/provider/file.php:18 search/provider/file.php:36 msgid "Files" msgstr "Filer" -#: search/provider/file.php:26 search/provider/file.php:33 +#: search/provider/file.php:27 search/provider/file.php:34 msgid "Text" msgstr "SMS" -#: search/provider/file.php:29 +#: search/provider/file.php:30 msgid "Images" msgstr "Billeder" diff --git a/l10n/da/settings.po b/l10n/da/settings.po index 7251a13d4f..e5f2b57e88 100644 --- a/l10n/da/settings.po +++ b/l10n/da/settings.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" -"PO-Revision-Date: 2013-09-24 16:59+0000\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-24 17:00+0000\n" "Last-Translator: Sappe\n" "Language-Team: Danish (http://www.transifex.com/projects/p/owncloud/language/da/)\n" "MIME-Version: 1.0\n" @@ -114,7 +114,7 @@ msgstr "Serveren understøtter ikke kodeordsskifte, men brugernes krypteringsnø #: changepassword/controller.php:92 changepassword/controller.php:103 msgid "Unable to change password" -msgstr "" +msgstr "Kunne ikke ændre kodeord" #: js/apps.js:43 msgid "Update to {appversion}" diff --git a/l10n/el/files.po b/l10n/el/files.po index f6b7bcf82a..87c112acc6 100644 --- a/l10n/el/files.po +++ b/l10n/el/files.po @@ -5,14 +5,15 @@ # Translators: # Efstathios Iosifidis , 2013 # Efstathios Iosifidis , 2013 +# gtsamis , 2013 # frerisp , 2013 msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-25 12:10+0000\n" +"Last-Translator: gtsamis \n" "Language-Team: Greek (http://www.transifex.com/projects/p/owncloud/language/el/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -89,7 +90,7 @@ msgstr "" msgid "Invalid directory." msgstr "Μη έγκυρος φάκελος." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Αρχεία" @@ -224,7 +225,7 @@ msgstr "Η λήψη προετοιμάζεται. Αυτό μπορεί να π #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Σφάλμα κατά τη μετακίνηση του αρχείου" #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/fr/core.po b/l10n/fr/core.po index 4c7f6be045..bce932689a 100644 --- a/l10n/fr/core.po +++ b/l10n/fr/core.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" -"PO-Revision-Date: 2013-09-23 19:40+0000\n" -"Last-Translator: ogre_sympathique \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-26 15:10+0000\n" +"Last-Translator: Christophe Lherieau \n" "Language-Team: French (http://www.transifex.com/projects/p/owncloud/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -110,11 +110,11 @@ msgstr "Image invalide" #: avatar/controller.php:115 avatar/controller.php:142 msgid "No temporary profile picture available, try again" -msgstr "" +msgstr "Aucune image temporaire disponible pour le profil. Essayez à nouveau." #: avatar/controller.php:135 msgid "No crop data provided" -msgstr "" +msgstr "Aucune donnée de culture fournie" #: js/config.php:32 msgid "Sunday" @@ -298,11 +298,11 @@ msgstr "Annuler" #: js/oc-dialogs.js:386 msgid "Continue" -msgstr "" +msgstr "Poursuivre" #: js/oc-dialogs.js:433 js/oc-dialogs.js:446 msgid "(all selected)" -msgstr "" +msgstr "(tous sélectionnés)" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" @@ -310,7 +310,7 @@ msgstr "({count} sélectionnés)" #: js/oc-dialogs.js:457 msgid "Error loading file exists template" -msgstr "" +msgstr "Erreur de chargement du modèle de fichier existant" #: js/oc-vcategories.js:5 js/oc-vcategories.js:85 js/oc-vcategories.js:102 #: js/oc-vcategories.js:117 js/oc-vcategories.js:132 js/oc-vcategories.js:162 diff --git a/l10n/fr/settings.po b/l10n/fr/settings.po index 529e2e8c35..21918cf68d 100644 --- a/l10n/fr/settings.po +++ b/l10n/fr/settings.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:45-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-26 15:00+0000\n" +"Last-Translator: Christophe Lherieau \n" "Language-Team: French (http://www.transifex.com/projects/p/owncloud/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -102,18 +102,18 @@ msgstr "Aucun utilisateur fourni" msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" -msgstr "" +msgstr "Veuillez fournir un mot de passe administrateur de récupération de données, sinon toutes les données de l'utilisateur seront perdues" #: changepassword/controller.php:79 msgid "" "Wrong admin recovery password. Please check the password and try again." -msgstr "" +msgstr "Mot de passe administrateur de récupération de données invalide. Veuillez vérifier le mot de passe et essayer à nouveau." #: changepassword/controller.php:87 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." -msgstr "" +msgstr "L'infrastructure d'arrière-plan ne supporte pas la modification de mot de passe, mais la clef de chiffrement des utilisateurs a été mise à jour avec succès." #: changepassword/controller.php:92 changepassword/controller.php:103 msgid "Unable to change password" @@ -123,11 +123,11 @@ msgstr "Impossible de modifier le mot de passe" msgid "Update to {appversion}" msgstr "Mettre à jour vers {appversion}" -#: js/apps.js:49 js/apps.js:82 js/apps.js:108 +#: js/apps.js:49 js/apps.js:82 js/apps.js:110 msgid "Disable" msgstr "Désactiver" -#: js/apps.js:49 js/apps.js:89 js/apps.js:102 js/apps.js:117 +#: js/apps.js:49 js/apps.js:90 js/apps.js:103 js/apps.js:119 msgid "Enable" msgstr "Activer" @@ -135,31 +135,31 @@ msgstr "Activer" msgid "Please wait...." msgstr "Veuillez patienter…" -#: js/apps.js:79 js/apps.js:80 js/apps.js:100 +#: js/apps.js:79 js/apps.js:80 js/apps.js:101 msgid "Error while disabling app" msgstr "Erreur lors de la désactivation de l'application" -#: js/apps.js:99 js/apps.js:112 js/apps.js:113 +#: js/apps.js:100 js/apps.js:114 js/apps.js:115 msgid "Error while enabling app" msgstr "Erreur lors de l'activation de l'application" -#: js/apps.js:123 +#: js/apps.js:125 msgid "Updating...." msgstr "Mise à jour..." -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error while updating app" msgstr "Erreur lors de la mise à jour de l'application" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error" msgstr "Erreur" -#: js/apps.js:127 templates/apps.php:43 +#: js/apps.js:129 templates/apps.php:43 msgid "Update" msgstr "Mettre à jour" -#: js/apps.js:130 +#: js/apps.js:132 msgid "Updated" msgstr "Mise à jour effectuée avec succès" diff --git a/l10n/hu_HU/files.po b/l10n/hu_HU/files.po index 51bd0806da..e61d77e5cb 100644 --- a/l10n/hu_HU/files.po +++ b/l10n/hu_HU/files.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-24 18:40+0000\n" +"Last-Translator: Laszlo Tornoci \n" "Language-Team: Hungarian (Hungary) (http://www.transifex.com/projects/p/owncloud/language/hu_HU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -77,23 +77,23 @@ msgstr "Nincs elég szabad hely." #: ajax/upload.php:120 ajax/upload.php:143 msgid "Upload failed. Could not get file info." -msgstr "" +msgstr "A feltöltés nem sikerült. Az állományt leíró információk nem érhetők el." #: ajax/upload.php:136 msgid "Upload failed. Could not find uploaded file" -msgstr "" +msgstr "A feltöltés nem sikerült. Nem található a feltöltendő állomány." #: ajax/upload.php:160 msgid "Invalid directory." msgstr "Érvénytelen mappa." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Fájlok" #: js/file-upload.js:244 msgid "Unable to upload {filename} as it is a directory or has 0 bytes" -msgstr "" +msgstr "A(z) {filename} állomány nem tölthető fel, mert ez vagy egy mappa, vagy pedig 0 bájtból áll." #: js/file-upload.js:255 msgid "Not enough space available" @@ -105,7 +105,7 @@ msgstr "A feltöltést megszakítottuk." #: js/file-upload.js:356 msgid "Could not get result from server." -msgstr "" +msgstr "A kiszolgálótól nem kapható meg az eredmény." #: js/file-upload.js:446 msgid "" @@ -167,24 +167,24 @@ msgstr "visszavonás" #: js/filelist.js:533 js/filelist.js:599 js/files.js:576 msgid "%n folder" msgid_plural "%n folders" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%n mappa" +msgstr[1] "%n mappa" #: js/filelist.js:534 js/filelist.js:600 js/files.js:582 msgid "%n file" msgid_plural "%n files" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%n állomány" +msgstr[1] "%n állomány" #: js/filelist.js:541 msgid "{dirs} and {files}" -msgstr "" +msgstr "{dirs} és {files}" #: js/filelist.js:731 js/filelist.js:769 msgid "Uploading %n file" msgid_plural "Uploading %n files" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "%n állomány feltöltése" +msgstr[1] "%n állomány feltöltése" #: js/files.js:25 msgid "'.' is an invalid file name." @@ -212,7 +212,7 @@ msgstr "A tároló majdnem tele van ({usedSpacePercent}%)" msgid "" "Encryption was disabled but your files are still encrypted. Please go to " "your personal settings to decrypt your files." -msgstr "" +msgstr "A titkosítási funkciót kikapcsolták, de az Ön állományai még mindig titkosított állapotban vannak. A személyes beállításoknál tudja a titkosítást feloldani." #: js/files.js:296 msgid "" @@ -222,7 +222,7 @@ msgstr "Készül a letöltendő állomány. Ez eltarthat egy ideig, ha nagyok a #: js/files.js:507 js/files.js:545 msgid "Error moving file" -msgstr "" +msgstr "Az állomány áthelyezése nem sikerült." #: js/files.js:558 templates/index.php:61 msgid "Name" diff --git a/l10n/hu_HU/files_trashbin.po b/l10n/hu_HU/files_trashbin.po index 4e5d7207c9..d0d6f3bc51 100644 --- a/l10n/hu_HU/files_trashbin.po +++ b/l10n/hu_HU/files_trashbin.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-08-15 04:47-0400\n" -"PO-Revision-Date: 2013-08-15 08:48+0000\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-24 18:40+0000\n" "Last-Translator: I Robot \n" "Language-Team: Hungarian (Hungary) (http://www.transifex.com/projects/p/owncloud/language/hu_HU/)\n" "MIME-Version: 1.0\n" @@ -28,43 +28,43 @@ msgstr "Nem sikerült %s végleges törlése" msgid "Couldn't restore %s" msgstr "Nem sikerült %s visszaállítása" -#: js/trash.js:7 js/trash.js:100 +#: js/trash.js:7 js/trash.js:102 msgid "perform restore operation" msgstr "a visszaállítás végrehajtása" -#: js/trash.js:20 js/trash.js:48 js/trash.js:118 js/trash.js:146 +#: js/trash.js:20 js/trash.js:49 js/trash.js:120 js/trash.js:148 msgid "Error" msgstr "Hiba" -#: js/trash.js:36 +#: js/trash.js:37 msgid "delete file permanently" msgstr "az állomány végleges törlése" -#: js/trash.js:127 +#: js/trash.js:129 msgid "Delete permanently" msgstr "Végleges törlés" -#: js/trash.js:182 templates/index.php:17 +#: js/trash.js:190 templates/index.php:21 msgid "Name" msgstr "Név" -#: js/trash.js:183 templates/index.php:27 +#: js/trash.js:191 templates/index.php:31 msgid "Deleted" msgstr "Törölve" -#: js/trash.js:191 +#: js/trash.js:199 msgid "%n folder" msgid_plural "%n folders" msgstr[0] "" -msgstr[1] "" +msgstr[1] "%n mappa" -#: js/trash.js:197 +#: js/trash.js:205 msgid "%n file" msgid_plural "%n files" msgstr[0] "" -msgstr[1] "" +msgstr[1] "%n állomány" -#: lib/trash.php:819 lib/trash.php:821 +#: lib/trashbin.php:814 lib/trashbin.php:816 msgid "restored" msgstr "visszaállítva" @@ -72,11 +72,11 @@ msgstr "visszaállítva" msgid "Nothing in here. Your trash bin is empty!" msgstr "Itt nincs semmi. Az Ön szemetes mappája üres!" -#: templates/index.php:20 templates/index.php:22 +#: templates/index.php:24 templates/index.php:26 msgid "Restore" msgstr "Visszaállítás" -#: templates/index.php:30 templates/index.php:31 +#: templates/index.php:34 templates/index.php:35 msgid "Delete" msgstr "Törlés" diff --git a/l10n/hu_HU/user_ldap.po b/l10n/hu_HU/user_ldap.po index 5b24b22ffd..87a2b14046 100644 --- a/l10n/hu_HU/user_ldap.po +++ b/l10n/hu_HU/user_ldap.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-07 04:40-0400\n" -"PO-Revision-Date: 2013-09-05 11:51+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"PO-Revision-Date: 2013-09-24 19:00+0000\n" +"Last-Translator: Laszlo Tornoci \n" "Language-Team: Hungarian (Hungary) (http://www.transifex.com/projects/p/owncloud/language/hu_HU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -92,7 +92,7 @@ msgid "" "Warning: Apps user_ldap and user_webdavauth are incompatible. You may" " experience unexpected behavior. Please ask your system administrator to " "disable one of them." -msgstr "" +msgstr "Figyelem: a user_ldap és user_webdavauth alkalmazások nem kompatibilisek. Együttes használatuk váratlan eredményekhez vezethet. Kérje meg a rendszergazdát, hogy a kettő közül kapcsolja ki az egyiket." #: templates/settings.php:12 msgid "" @@ -157,7 +157,7 @@ msgstr "Szűrő a bejelentkezéshez" msgid "" "Defines the filter to apply, when login is attempted. %%uid replaces the " "username in the login action. Example: \"uid=%%uid\"" -msgstr "" +msgstr "Ez a szűrő érvényes a bejelentkezés megkísérlésekor. Ekkor az %%uid változó helyére a bejelentkezési név kerül. Például: \"uid=%%uid\"" #: templates/settings.php:55 msgid "User List Filter" @@ -167,7 +167,7 @@ msgstr "A felhasználók szűrője" msgid "" "Defines the filter to apply, when retrieving users (no placeholders). " "Example: \"objectClass=person\"" -msgstr "" +msgstr "Ez a szűrő érvényes a felhasználók listázásakor (nincs helyettesíthető változó). Például: \"objectClass=person\"" #: templates/settings.php:59 msgid "Group Filter" @@ -177,7 +177,7 @@ msgstr "A csoportok szűrője" msgid "" "Defines the filter to apply, when retrieving groups (no placeholders). " "Example: \"objectClass=posixGroup\"" -msgstr "" +msgstr "Ez a szűrő érvényes a csoportok listázásakor (nincs helyettesíthető változó). Például: \"objectClass=posixGroup\"" #: templates/settings.php:66 msgid "Connection Settings" @@ -215,7 +215,7 @@ msgstr "A fő szerver kihagyása" #: templates/settings.php:72 msgid "Only connect to the replica server." -msgstr "" +msgstr "Csak a másodlagos (másolati) kiszolgálóhoz kapcsolódjunk." #: templates/settings.php:73 msgid "Use TLS" @@ -238,7 +238,7 @@ msgstr "Ne ellenőrizzük az SSL-tanúsítvány érvényességét" msgid "" "Not recommended, use it for testing only! If connection only works with this" " option, import the LDAP server's SSL certificate in your %s server." -msgstr "" +msgstr "Használata nem javasolt (kivéve tesztelési céllal). Ha a kapcsolat csak ezzel a beállítással működik, akkor importálja az LDAP-kiszolgáló SSL tanúsítványát a(z) %s kiszolgálóra!" #: templates/settings.php:76 msgid "Cache Time-To-Live" @@ -258,7 +258,7 @@ msgstr "A felhasználónév mezője" #: templates/settings.php:80 msgid "The LDAP attribute to use to generate the user's display name." -msgstr "" +msgstr "Ebből az LDAP attribútumból képződik a felhasználó megjelenítendő neve." #: templates/settings.php:81 msgid "Base User Tree" @@ -282,7 +282,7 @@ msgstr "A csoport nevének mezője" #: templates/settings.php:83 msgid "The LDAP attribute to use to generate the groups's display name." -msgstr "" +msgstr "Ebből az LDAP attribútumból képződik a csoport megjelenítendő neve." #: templates/settings.php:84 msgid "Base Group Tree" diff --git a/l10n/templates/core.pot b/l10n/templates/core.pot index a57486f5ed..ba60bef371 100644 --- a/l10n/templates/core.pot +++ b/l10n/templates/core.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files.pot b/l10n/templates/files.pot index 23d0cd0b17..601c663392 100644 --- a/l10n/templates/files.pot +++ b/l10n/templates/files.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_encryption.pot b/l10n/templates/files_encryption.pot index 44e17a2fcb..52e8feed91 100644 --- a/l10n/templates/files_encryption.pot +++ b/l10n/templates/files_encryption.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_external.pot b/l10n/templates/files_external.pot index abdd985cfd..a99fbf35c8 100644 --- a/l10n/templates/files_external.pot +++ b/l10n/templates/files_external.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_sharing.pot b/l10n/templates/files_sharing.pot index 34ed992660..10bee89c27 100644 --- a/l10n/templates/files_sharing.pot +++ b/l10n/templates/files_sharing.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_trashbin.pot b/l10n/templates/files_trashbin.pot index 45fa700a43..cfc69f2b2e 100644 --- a/l10n/templates/files_trashbin.pot +++ b/l10n/templates/files_trashbin.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_versions.pot b/l10n/templates/files_versions.pot index 2e73cce980..284aca099f 100644 --- a/l10n/templates/files_versions.pot +++ b/l10n/templates/files_versions.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/lib.pot b/l10n/templates/lib.pot index 0733e0c273..0103b348e9 100644 --- a/l10n/templates/lib.pot +++ b/l10n/templates/lib.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/settings.pot b/l10n/templates/settings.pot index 66c00629bd..c14bb4c3e5 100644 --- a/l10n/templates/settings.pot +++ b/l10n/templates/settings.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/user_ldap.pot b/l10n/templates/user_ldap.pot index 919a39b405..822d615af9 100644 --- a/l10n/templates/user_ldap.pot +++ b/l10n/templates/user_ldap.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/user_webdavauth.pot b/l10n/templates/user_webdavauth.pot index dfb732ed0c..3a0f240e93 100644 --- a/l10n/templates/user_webdavauth.pot +++ b/l10n/templates/user_webdavauth.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-24 12:58-0400\n" +"POT-Creation-Date: 2013-09-27 00:01-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/lib/l10n/da.php b/lib/l10n/da.php index 2690314276..05a43f42ed 100644 --- a/lib/l10n/da.php +++ b/lib/l10n/da.php @@ -8,6 +8,9 @@ $TRANSLATIONS = array( "Users" => "Brugere", "Admin" => "Admin", "Failed to upgrade \"%s\"." => "Upgradering af \"%s\" fejlede", +"Custom profile pictures don't work with encryption yet" => "Personligt profilbillede virker endnu ikke sammen med kryptering", +"Unknown filetype" => "Ukendt filtype", +"Invalid image" => "Ugyldigt billede", "web services under your control" => "Webtjenester under din kontrol", "cannot open \"%s\"" => "Kan ikke åbne \"%s\"", "ZIP download is turned off." => "ZIP-download er slået fra.", diff --git a/settings/l10n/da.php b/settings/l10n/da.php index fcff9dbcfd..f86559d675 100644 --- a/settings/l10n/da.php +++ b/settings/l10n/da.php @@ -21,6 +21,7 @@ $TRANSLATIONS = array( "Please provide an admin recovery password, otherwise all user data will be lost" => "Angiv venligst en admininstrator gendannelseskode, ellers vil alt brugerdata gå tabt", "Wrong admin recovery password. Please check the password and try again." => "Forkert admin gendannelseskode. Se venligst koden efter og prøv igen.", "Back-end doesn't support password change, but the users encryption key was successfully updated." => "Serveren understøtter ikke kodeordsskifte, men brugernes krypteringsnøgle blev opdateret.", +"Unable to change password" => "Kunne ikke ændre kodeord", "Update to {appversion}" => "Opdatér til {appversion}", "Disable" => "Deaktiver", "Enable" => "Aktiver", diff --git a/settings/l10n/fr.php b/settings/l10n/fr.php index 55c0e7fe9a..10a7d764bc 100644 --- a/settings/l10n/fr.php +++ b/settings/l10n/fr.php @@ -18,6 +18,9 @@ $TRANSLATIONS = array( "Couldn't update app." => "Impossible de mettre à jour l'application", "Wrong password" => "Mot de passe incorrect", "No user supplied" => "Aucun utilisateur fourni", +"Please provide an admin recovery password, otherwise all user data will be lost" => "Veuillez fournir un mot de passe administrateur de récupération de données, sinon toutes les données de l'utilisateur seront perdues", +"Wrong admin recovery password. Please check the password and try again." => "Mot de passe administrateur de récupération de données invalide. Veuillez vérifier le mot de passe et essayer à nouveau.", +"Back-end doesn't support password change, but the users encryption key was successfully updated." => "L'infrastructure d'arrière-plan ne supporte pas la modification de mot de passe, mais la clef de chiffrement des utilisateurs a été mise à jour avec succès.", "Unable to change password" => "Impossible de modifier le mot de passe", "Update to {appversion}" => "Mettre à jour vers {appversion}", "Disable" => "Désactiver", From aa8a145ba8fe5a429698ac9c508704952a454358 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 27 Sep 2013 09:59:04 +0200 Subject: [PATCH 137/150] use dataProvider for txt blacklist test --- tests/lib/preview.php | 47 +++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/tests/lib/preview.php b/tests/lib/preview.php index c40b2d03ef..5dc4a93acc 100644 --- a/tests/lib/preview.php +++ b/tests/lib/preview.php @@ -95,9 +95,6 @@ class Preview extends \PHPUnit_Framework_TestCase { public function testTxtBlacklist() { $user = $this->initFS(); - $x = 32; - $y = 32; - $txt = 'random text file'; $ics = file_get_contents(__DIR__ . '/../data/testcal.ics'); $vcf = file_get_contents(__DIR__ . '/../data/testcontact.vcf'); @@ -106,28 +103,34 @@ class Preview extends \PHPUnit_Framework_TestCase { $rootView->mkdir('/'.$user); $rootView->mkdir('/'.$user.'/files'); - $toTest = array('txt', - 'ics', - 'vcf'); + return array( + array('txt', $txt, $user, $rootView, false), + array('ics', $ics, $user, $rootView, true), + array('vcf', $vcf, $user, $rootView, true), + ); + } - foreach($toTest as $test) { - $sample = '/'.$user.'/files/test.'.$test; - $rootView->file_put_contents($sample, ${$test}); - $preview = new \OC\Preview($user, 'files/', 'test.'.$test, $x, $y); - $image = $preview->getPreview(); - $resource = $image->resource(); + /** + * @dataProvider testTxtBlacklist + */ + public function testIsTransparent($test, $data, $user, $rootView, $expectedResult) { + $x = 32; + $y = 32; - //http://stackoverflow.com/questions/5702953/imagecolorat-and-transparency - $colorIndex = imagecolorat($resource, 1, 1); - $colorInfo = imagecolorsforindex($resource, $colorIndex); - $isTransparent = ($colorInfo['alpha'] === 127); + $sample = '/'.$user.'/files/test.'.$test; + $rootView->file_put_contents($sample, $data); + $preview = new \OC\Preview($user, 'files/', 'test.'.$test, $x, $y); + $image = $preview->getPreview(); + $resource = $image->resource(); - if($test === 'txt') { - $this->assertEquals($isTransparent, false); - } else { - $this->assertEquals($isTransparent, true); - } - } + //http://stackoverflow.com/questions/5702953/imagecolorat-and-transparency + $colorIndex = imagecolorat($resource, 1, 1); + $colorInfo = imagecolorsforindex($resource, $colorIndex); + $this->assertEquals( + $expectedResult, + $colorInfo['alpha'] === 127, + 'Failed asserting that only previews for text files are transparent.' + ); } private function initFS() { From 1b13101096493c0c8f02826e373af761fd5cfac6 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 27 Sep 2013 11:01:47 +0200 Subject: [PATCH 138/150] move fileView object initialization to testIsTransparent --- tests/lib/preview.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/lib/preview.php b/tests/lib/preview.php index 5dc4a93acc..98659a7e88 100644 --- a/tests/lib/preview.php +++ b/tests/lib/preview.php @@ -93,27 +93,27 @@ class Preview extends \PHPUnit_Framework_TestCase { } public function testTxtBlacklist() { - $user = $this->initFS(); - $txt = 'random text file'; $ics = file_get_contents(__DIR__ . '/../data/testcal.ics'); $vcf = file_get_contents(__DIR__ . '/../data/testcontact.vcf'); - $rootView = new \OC\Files\View(''); - $rootView->mkdir('/'.$user); - $rootView->mkdir('/'.$user.'/files'); - return array( - array('txt', $txt, $user, $rootView, false), - array('ics', $ics, $user, $rootView, true), - array('vcf', $vcf, $user, $rootView, true), + array('txt', $txt, false), + array('ics', $ics, true), + array('vcf', $vcf, true), ); } /** * @dataProvider testTxtBlacklist */ - public function testIsTransparent($test, $data, $user, $rootView, $expectedResult) { + public function testIsTransparent($test, $data, $expectedResult) { + $user = $this->initFS(); + + $rootView = new \OC\Files\View(''); + $rootView->mkdir('/'.$user); + $rootView->mkdir('/'.$user.'/files'); + $x = 32; $y = 32; From 4e9296a484fbec0a4c725f32ef47716f9e1b56bc Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 27 Sep 2013 11:33:37 +0200 Subject: [PATCH 139/150] rename testTxtBlacklist to txtBlacklist --- tests/lib/preview.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/lib/preview.php b/tests/lib/preview.php index 98659a7e88..01d8a57fc6 100644 --- a/tests/lib/preview.php +++ b/tests/lib/preview.php @@ -92,7 +92,7 @@ class Preview extends \PHPUnit_Framework_TestCase { $this->assertEquals($image->height(), $maxY); } - public function testTxtBlacklist() { + public function txtBlacklist() { $txt = 'random text file'; $ics = file_get_contents(__DIR__ . '/../data/testcal.ics'); $vcf = file_get_contents(__DIR__ . '/../data/testcontact.vcf'); @@ -105,7 +105,7 @@ class Preview extends \PHPUnit_Framework_TestCase { } /** - * @dataProvider testTxtBlacklist + * @dataProvider txtBlacklist */ public function testIsTransparent($test, $data, $expectedResult) { $user = $this->initFS(); @@ -146,4 +146,4 @@ class Preview extends \PHPUnit_Framework_TestCase { return $user; } -} \ No newline at end of file +} From 79da35b698a398bef59f83f222de3055ddbb5a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 27 Sep 2013 13:41:23 +0200 Subject: [PATCH 140/150] code cleanup --- tests/lib/connector/sabre/objecttree.php | 61 +++++++++--------------- 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/tests/lib/connector/sabre/objecttree.php b/tests/lib/connector/sabre/objecttree.php index c920441c8b..1d76bb5967 100644 --- a/tests/lib/connector/sabre/objecttree.php +++ b/tests/lib/connector/sabre/objecttree.php @@ -36,38 +36,14 @@ class ObjectTree extends PHPUnit_Framework_TestCase { * @expectedException Sabre_DAV_Exception_Forbidden */ public function testMoveFailed($source, $dest, $updatables) { - $rootDir = new OC_Connector_Sabre_Directory(''); - $objectTree = $this->getMock('\OC\Connector\Sabre\ObjectTree', - array('nodeExists', 'getNodeForPath'), - array($rootDir)); - - $objectTree->expects($this->once()) - ->method('getNodeForPath') - ->with($this->identicalTo('a/b')) - ->will($this->returnValue(false)); - - /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ - $objectTree->fileView = new TestDoubleFileView($updatables); - $objectTree->move($source, $dest); + $this->moveTest($source, $dest, $updatables); } /** * @dataProvider moveSuccessProvider */ public function testMoveSuccess($source, $dest, $updatables) { - $rootDir = new OC_Connector_Sabre_Directory(''); - $objectTree = $this->getMock('\OC\Connector\Sabre\ObjectTree', - array('nodeExists', 'getNodeForPath'), - array($rootDir)); - - $objectTree->expects($this->once()) - ->method('getNodeForPath') - ->with($this->identicalTo('a/b')) - ->will($this->returnValue(false)); - - /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ - $objectTree->fileView = new TestDoubleFileView($updatables); - $objectTree->move($source, $dest); + $this->moveTest($source, $dest, $updatables); $this->assertTrue(true); } @@ -87,18 +63,25 @@ class ObjectTree extends PHPUnit_Framework_TestCase { ); } -// private function buildFileViewMock($updatables) { -// // mock filesysten -// $view = $this->getMock('\OC\Files\View', array('isUpdatable'), array(), '', FALSE); -// -// foreach ($updatables as $path => $updatable) { -// $view->expects($this->any()) -// ->method('isUpdatable') -// ->with($this->identicalTo($path)) -// ->will($this->returnValue($updatable)); -// } -// -// return $view; -// } + /** + * @param $source + * @param $dest + * @param $updatables + */ + private function moveTest($source, $dest, $updatables) { + $rootDir = new OC_Connector_Sabre_Directory(''); + $objectTree = $this->getMock('\OC\Connector\Sabre\ObjectTree', + array('nodeExists', 'getNodeForPath'), + array($rootDir)); + + $objectTree->expects($this->once()) + ->method('getNodeForPath') + ->with($this->identicalTo($source)) + ->will($this->returnValue(false)); + + /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ + $objectTree->fileView = new TestDoubleFileView($updatables); + $objectTree->move($source, $dest); + } } From c5bcefe4dbd5a237693b1a7435a7041c7e85abd4 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 27 Sep 2013 14:55:37 +0200 Subject: [PATCH 141/150] rename variable in testIsTransparent --- tests/lib/preview.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/lib/preview.php b/tests/lib/preview.php index 01d8a57fc6..d0cdd2c44f 100644 --- a/tests/lib/preview.php +++ b/tests/lib/preview.php @@ -107,7 +107,7 @@ class Preview extends \PHPUnit_Framework_TestCase { /** * @dataProvider txtBlacklist */ - public function testIsTransparent($test, $data, $expectedResult) { + public function testIsTransparent($extension, $data, $expectedResult) { $user = $this->initFS(); $rootView = new \OC\Files\View(''); @@ -117,9 +117,9 @@ class Preview extends \PHPUnit_Framework_TestCase { $x = 32; $y = 32; - $sample = '/'.$user.'/files/test.'.$test; + $sample = '/'.$user.'/files/test.'.$extension; $rootView->file_put_contents($sample, $data); - $preview = new \OC\Preview($user, 'files/', 'test.'.$test, $x, $y); + $preview = new \OC\Preview($user, 'files/', 'test.'.$extension, $x, $y); $image = $preview->getPreview(); $resource = $image->resource(); From adff34cb8a7c2e2a6046e4fe28da3a77cd6492ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 27 Sep 2013 17:08:48 +0200 Subject: [PATCH 142/150] fixing error in initialization of TagManager --- lib/server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/server.php b/lib/server.php index 2391952ea6..cabb15324e 100644 --- a/lib/server.php +++ b/lib/server.php @@ -51,7 +51,7 @@ class Server extends SimpleContainer implements IServerContainer { }); $this->registerService('TagManager', function($c) { $user = \OC_User::getUser(); - return new Tags($user); + return new TagManager($user); }); $this->registerService('RootFolder', function($c) { // TODO: get user and user manager from container as well From 57f37c876b59d11dae8b4325bed5fa57de52ecd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 27 Sep 2013 17:15:26 +0200 Subject: [PATCH 143/150] delay middleware registrations --- .../dependencyinjection/dicontainer.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 5487826693..380a0ee6d4 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -40,6 +40,10 @@ use OCP\IServerContainer; class DIContainer extends SimpleContainer implements IAppContainer{ + /** + * @var array + */ + private $middleWares; /** * Put your class dependencies in here @@ -89,6 +93,10 @@ class DIContainer extends SimpleContainer implements IAppContainer{ $dispatcher = new MiddlewareDispatcher(); $dispatcher->registerMiddleware($c['SecurityMiddleware']); + foreach($this->middleWares as $middleWare) { + $dispatcher->registerMiddleware($middleWare); + } + return $dispatcher; }); @@ -125,10 +133,7 @@ class DIContainer extends SimpleContainer implements IAppContainer{ * @return boolean */ function registerMiddleWare(IMiddleWare $middleWare) { - /** @var $dispatcher MiddlewareDispatcher */ - $dispatcher = $this->query('MiddlewareDispatcher'); - $dispatcher->registerMiddleware($middleWare); - + array_push($this->middleWares, $middleWare); } /** From 4907685405ead5df56ef0a5ac0b9af7c86885487 Mon Sep 17 00:00:00 2001 From: kondou Date: Sat, 28 Sep 2013 16:46:53 +0200 Subject: [PATCH 144/150] Base defaultavatar text on displayname Fix #4876 --- core/avatar/controller.php | 2 +- core/js/jquery.avatar.js | 6 +++++- settings/js/personal.js | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/avatar/controller.php b/core/avatar/controller.php index 9f7c0517c4..2269382446 100644 --- a/core/avatar/controller.php +++ b/core/avatar/controller.php @@ -33,7 +33,7 @@ class Controller { $image->show(); } else { // Signalizes $.avatar() to display a defaultavatar - \OC_JSON::success(); + \OC_JSON::success(array("data"=> array("displayname"=> \OC_User::getDisplayName($user)) )); } } diff --git a/core/js/jquery.avatar.js b/core/js/jquery.avatar.js index f1382fd7d2..88a4c25d1e 100644 --- a/core/js/jquery.avatar.js +++ b/core/js/jquery.avatar.js @@ -69,7 +69,11 @@ var url = OC.Router.generate('core_avatar_get', {user: user, size: size})+'?requesttoken='+oc_requesttoken; $.get(url, function(result) { if (typeof(result) === 'object') { - $div.placeholder(user); + if (result.data && result.data.displayname) { + $div.placeholder(user, result.data.displayname); + } else { + $div.placeholder(user); + } } else { if (ie8fix === true) { $div.html(''); diff --git a/settings/js/personal.js b/settings/js/personal.js index eaaca32f5d..8944a7afa3 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -34,6 +34,7 @@ function changeDisplayName(){ $('#oldDisplayName').text($('#displayName').val()); // update displayName on the top right expand button $('#expandDisplayName').text($('#displayName').val()); + updateAvatar(); } else{ $('#newdisplayname').val(data.data.displayName); From 24d092c4ba4264deb2b6b57299da2acc2971cedc Mon Sep 17 00:00:00 2001 From: kondou Date: Sat, 28 Sep 2013 17:49:07 +0200 Subject: [PATCH 145/150] Have uniform (wrong) indention --- settings/js/personal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/js/personal.js b/settings/js/personal.js index 8944a7afa3..a923b47573 100644 --- a/settings/js/personal.js +++ b/settings/js/personal.js @@ -34,7 +34,7 @@ function changeDisplayName(){ $('#oldDisplayName').text($('#displayName').val()); // update displayName on the top right expand button $('#expandDisplayName').text($('#displayName').val()); - updateAvatar(); + updateAvatar(); } else{ $('#newdisplayname').val(data.data.displayName); From adcb738e47566d94a173116e86e3f5abe249dac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Sat, 28 Sep 2013 20:40:25 +0200 Subject: [PATCH 146/150] initialize $middleWares --- lib/appframework/dependencyinjection/dicontainer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/appframework/dependencyinjection/dicontainer.php b/lib/appframework/dependencyinjection/dicontainer.php index 380a0ee6d4..3755d45fa0 100644 --- a/lib/appframework/dependencyinjection/dicontainer.php +++ b/lib/appframework/dependencyinjection/dicontainer.php @@ -43,7 +43,7 @@ class DIContainer extends SimpleContainer implements IAppContainer{ /** * @var array */ - private $middleWares; + private $middleWares = array(); /** * Put your class dependencies in here From 59e4ff7d24b5df96998fbc65676b56d524af5ba6 Mon Sep 17 00:00:00 2001 From: Jenkins for ownCloud Date: Sun, 29 Sep 2013 00:03:26 -0400 Subject: [PATCH 147/150] [tx-robot] updated from transifex --- apps/files/l10n/uk.php | 5 ++- apps/files_encryption/l10n/uk.php | 1 + core/l10n/it.php | 10 ++--- core/l10n/sv.php | 16 +++++++- l10n/it/core.po | 30 +++++++-------- l10n/sv/core.po | 53 ++++++++++++++------------- l10n/sv/settings.po | 57 +++++++++++++++-------------- l10n/templates/core.pot | 2 +- l10n/templates/files.pot | 2 +- l10n/templates/files_encryption.pot | 2 +- l10n/templates/files_external.pot | 2 +- l10n/templates/files_sharing.pot | 2 +- l10n/templates/files_trashbin.pot | 2 +- l10n/templates/files_versions.pot | 2 +- l10n/templates/lib.pot | 2 +- l10n/templates/settings.pot | 8 ++-- l10n/templates/user_ldap.pot | 2 +- l10n/templates/user_webdavauth.pot | 2 +- l10n/uk/files.po | 18 ++++----- l10n/uk/files_encryption.po | 6 +-- l10n/uk/settings.po | 35 +++++++++--------- settings/l10n/sv.php | 13 +++++++ settings/l10n/uk.php | 4 +- 23 files changed, 154 insertions(+), 122 deletions(-) diff --git a/apps/files/l10n/uk.php b/apps/files/l10n/uk.php index fac7cea529..4aaeca2554 100644 --- a/apps/files/l10n/uk.php +++ b/apps/files/l10n/uk.php @@ -18,6 +18,7 @@ $TRANSLATIONS = array( "Upload cancelled." => "Завантаження перервано.", "File upload is in progress. Leaving the page now will cancel the upload." => "Виконується завантаження файлу. Закриття цієї сторінки приведе до відміни завантаження.", "URL cannot be empty." => "URL не може бути пустим.", +"Invalid folder name. Usage of 'Shared' is reserved by ownCloud" => "Неправильне ім'я теки. Використання 'Shared' зарезервовано ownCloud", "Error" => "Помилка", "Share" => "Поділитися", "Delete permanently" => "Видалити назавжди", @@ -29,7 +30,7 @@ $TRANSLATIONS = array( "cancel" => "відміна", "replaced {new_name} with {old_name}" => "замінено {new_name} на {old_name}", "undo" => "відмінити", -"_%n folder_::_%n folders_" => array("","",""), +"_%n folder_::_%n folders_" => array("%n тека","%n тека","%n теки"), "_%n file_::_%n files_" => array("","",""), "_Uploading %n file_::_Uploading %n files_" => array("","",""), "'.' is an invalid file name." => "'.' це невірне ім'я файлу.", @@ -53,7 +54,7 @@ $TRANSLATIONS = array( "Save" => "Зберегти", "New" => "Створити", "Text file" => "Текстовий файл", -"Folder" => "Папка", +"Folder" => "Тека", "From link" => "З посилання", "Deleted files" => "Видалено файлів", "Cancel upload" => "Перервати завантаження", diff --git a/apps/files_encryption/l10n/uk.php b/apps/files_encryption/l10n/uk.php index e4fb053a71..5260dd3f2f 100644 --- a/apps/files_encryption/l10n/uk.php +++ b/apps/files_encryption/l10n/uk.php @@ -1,6 +1,7 @@ "Зберігаю...", +"personal settings" => "особисті налаштування", "Encryption" => "Шифрування", "Change Password" => "Змінити Пароль" ); diff --git a/core/l10n/it.php b/core/l10n/it.php index 94395b0226..bd2fad79c8 100644 --- a/core/l10n/it.php +++ b/core/l10n/it.php @@ -53,18 +53,18 @@ $TRANSLATIONS = array( "last year" => "anno scorso", "years ago" => "anni fa", "Choose" => "Scegli", -"Error loading file picker template: {error}" => "Errore nel caricamento del modello del selettore file: {error}", +"Error loading file picker template: {error}" => "Errore durante il caricamento del modello del selettore file: {error}", "Yes" => "Sì", "No" => "No", "Ok" => "Ok", -"Error loading message template: {error}" => "Errore nel caricamento del modello di messaggio: {error}", +"Error loading message template: {error}" => "Errore durante il caricamento del modello di messaggio: {error}", "_{count} file conflict_::_{count} file conflicts_" => array("{count} file in conflitto","{count} file in conflitto"), -"One file conflict" => "Un conflitto tra file", +"One file conflict" => "Un file in conflitto", "Which files do you want to keep?" => "Quali file vuoi mantenere?", -"If you select both versions, the copied file will have a number added to its name." => "Se selezioni entrambe le versioni, verrà aggiunto un numero al nome del file copiato.", +"If you select both versions, the copied file will have a number added to its name." => "Se selezioni entrambe le versioni, sarà aggiunto un numero al nome del file copiato.", "Cancel" => "Annulla", "Continue" => "Continua", -"(all selected)" => "(tutti selezionati)", +"(all selected)" => "(tutti i selezionati)", "({count} selected)" => "({count} selezionati)", "Error loading file exists template" => "Errore durante il caricamento del modello del file esistente", "The object type is not specified." => "Il tipo di oggetto non è specificato.", diff --git a/core/l10n/sv.php b/core/l10n/sv.php index 660cab0a62..0ea3259df6 100644 --- a/core/l10n/sv.php +++ b/core/l10n/sv.php @@ -16,6 +16,11 @@ $TRANSLATIONS = array( "Error adding %s to favorites." => "Fel vid tillägg av %s till favoriter.", "No categories selected for deletion." => "Inga kategorier valda för radering.", "Error removing %s from favorites." => "Fel vid borttagning av %s från favoriter.", +"No image or file provided" => "Ingen bild eller fil har tillhandahållits", +"Unknown filetype" => "Okänd filtyp", +"Invalid image" => "Ogiltig bild", +"No temporary profile picture available, try again" => "Ingen temporär profilbild finns tillgänglig, försök igen", +"No crop data provided" => "Ingen beskärdata har angivits", "Sunday" => "Söndag", "Monday" => "Måndag", "Tuesday" => "Tisdag", @@ -48,11 +53,20 @@ $TRANSLATIONS = array( "last year" => "förra året", "years ago" => "år sedan", "Choose" => "Välj", +"Error loading file picker template: {error}" => "Fel uppstod för filväljarmall: {error}", "Yes" => "Ja", "No" => "Nej", "Ok" => "Ok", -"_{count} file conflict_::_{count} file conflicts_" => array("",""), +"Error loading message template: {error}" => "Fel uppstod under inläsningen av meddelandemallen: {error}", +"_{count} file conflict_::_{count} file conflicts_" => array("{count} filkonflikt","{count} filkonflikter"), +"One file conflict" => "En filkonflikt", +"Which files do you want to keep?" => "Vilken fil vill du behålla?", +"If you select both versions, the copied file will have a number added to its name." => "Om du väljer båda versionerna kommer de kopierade filerna ha nummer tillagda i filnamnet.", "Cancel" => "Avbryt", +"Continue" => "Fortsätt", +"(all selected)" => "(Alla valda)", +"({count} selected)" => "({count} valda)", +"Error loading file exists template" => "Fel uppstod filmall existerar", "The object type is not specified." => "Objekttypen är inte specificerad.", "Error" => "Fel", "The app name is not specified." => " Namnet på appen är inte specificerad.", diff --git a/l10n/it/core.po b/l10n/it/core.po index d587e95953..a5586707e4 100644 --- a/l10n/it/core.po +++ b/l10n/it/core.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-22 13:40+0000\n" -"Last-Translator: nappo \n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" +"PO-Revision-Date: 2013-09-27 18:30+0000\n" +"Last-Translator: Vincenzo Reale \n" "Language-Team: Italian (http://www.transifex.com/projects/p/owncloud/language/it/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -252,7 +252,7 @@ msgstr "Scegli" #: js/oc-dialogs.js:146 msgid "Error loading file picker template: {error}" -msgstr "Errore nel caricamento del modello del selettore file: {error}" +msgstr "Errore durante il caricamento del modello del selettore file: {error}" #: js/oc-dialogs.js:172 msgid "Yes" @@ -268,7 +268,7 @@ msgstr "Ok" #: js/oc-dialogs.js:219 msgid "Error loading message template: {error}" -msgstr "Errore nel caricamento del modello di messaggio: {error}" +msgstr "Errore durante il caricamento del modello di messaggio: {error}" #: js/oc-dialogs.js:347 msgid "{count} file conflict" @@ -278,7 +278,7 @@ msgstr[1] "{count} file in conflitto" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "Un conflitto tra file" +msgstr "Un file in conflitto" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" @@ -288,7 +288,7 @@ msgstr "Quali file vuoi mantenere?" msgid "" "If you select both versions, the copied file will have a number added to its" " name." -msgstr "Se selezioni entrambe le versioni, verrà aggiunto un numero al nome del file copiato." +msgstr "Se selezioni entrambe le versioni, sarà aggiunto un numero al nome del file copiato." #: js/oc-dialogs.js:376 msgid "Cancel" @@ -300,7 +300,7 @@ msgstr "Continua" #: js/oc-dialogs.js:433 js/oc-dialogs.js:446 msgid "(all selected)" -msgstr "(tutti selezionati)" +msgstr "(tutti i selezionati)" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" @@ -319,7 +319,7 @@ msgstr "Il tipo di oggetto non è specificato." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Errore" @@ -339,7 +339,7 @@ msgstr "Condivisi" msgid "Share" msgstr "Condividi" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Errore durante la condivisione" @@ -439,23 +439,23 @@ msgstr "elimina" msgid "share" msgstr "condividi" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Protetta da password" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Errore durante la rimozione della data di scadenza" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Errore durante l'impostazione della data di scadenza" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Invio in corso..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "Messaggio inviato" diff --git a/l10n/sv/core.po b/l10n/sv/core.po index 6495949816..3d94872f70 100644 --- a/l10n/sv/core.po +++ b/l10n/sv/core.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Daniel Sandman , 2013 # Gunnar Norin , 2013 # medialabs, 2013 # Magnus Höglund , 2013 @@ -11,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-22 12:55-0400\n" -"PO-Revision-Date: 2013-09-20 15:01+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" +"PO-Revision-Date: 2013-09-28 02:02+0000\n" +"Last-Translator: Daniel Sandman \n" "Language-Team: Swedish (http://www.transifex.com/projects/p/owncloud/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -96,23 +97,23 @@ msgstr "Fel vid borttagning av %s från favoriter." #: avatar/controller.php:62 msgid "No image or file provided" -msgstr "" +msgstr "Ingen bild eller fil har tillhandahållits" #: avatar/controller.php:81 msgid "Unknown filetype" -msgstr "" +msgstr "Okänd filtyp" #: avatar/controller.php:85 msgid "Invalid image" -msgstr "" +msgstr "Ogiltig bild" #: avatar/controller.php:115 avatar/controller.php:142 msgid "No temporary profile picture available, try again" -msgstr "" +msgstr "Ingen temporär profilbild finns tillgänglig, försök igen" #: avatar/controller.php:135 msgid "No crop data provided" -msgstr "" +msgstr "Ingen beskärdata har angivits" #: js/config.php:32 msgid "Sunday" @@ -252,7 +253,7 @@ msgstr "Välj" #: js/oc-dialogs.js:146 msgid "Error loading file picker template: {error}" -msgstr "" +msgstr "Fel uppstod för filväljarmall: {error}" #: js/oc-dialogs.js:172 msgid "Yes" @@ -268,27 +269,27 @@ msgstr "Ok" #: js/oc-dialogs.js:219 msgid "Error loading message template: {error}" -msgstr "" +msgstr "Fel uppstod under inläsningen av meddelandemallen: {error}" #: js/oc-dialogs.js:347 msgid "{count} file conflict" msgid_plural "{count} file conflicts" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "{count} filkonflikt" +msgstr[1] "{count} filkonflikter" #: js/oc-dialogs.js:361 msgid "One file conflict" -msgstr "" +msgstr "En filkonflikt" #: js/oc-dialogs.js:367 msgid "Which files do you want to keep?" -msgstr "" +msgstr "Vilken fil vill du behålla?" #: js/oc-dialogs.js:368 msgid "" "If you select both versions, the copied file will have a number added to its" " name." -msgstr "" +msgstr "Om du väljer båda versionerna kommer de kopierade filerna ha nummer tillagda i filnamnet." #: js/oc-dialogs.js:376 msgid "Cancel" @@ -296,19 +297,19 @@ msgstr "Avbryt" #: js/oc-dialogs.js:386 msgid "Continue" -msgstr "" +msgstr "Fortsätt" #: js/oc-dialogs.js:433 js/oc-dialogs.js:446 msgid "(all selected)" -msgstr "" +msgstr "(Alla valda)" #: js/oc-dialogs.js:436 js/oc-dialogs.js:449 msgid "({count} selected)" -msgstr "" +msgstr "({count} valda)" #: js/oc-dialogs.js:457 msgid "Error loading file exists template" -msgstr "" +msgstr "Fel uppstod filmall existerar" #: js/oc-vcategories.js:5 js/oc-vcategories.js:85 js/oc-vcategories.js:102 #: js/oc-vcategories.js:117 js/oc-vcategories.js:132 js/oc-vcategories.js:162 @@ -319,7 +320,7 @@ msgstr "Objekttypen är inte specificerad." #: js/oc-vcategories.js:110 js/oc-vcategories.js:125 js/oc-vcategories.js:136 #: js/oc-vcategories.js:172 js/oc-vcategories.js:189 js/oc-vcategories.js:195 #: js/oc-vcategories.js:199 js/share.js:129 js/share.js:142 js/share.js:149 -#: js/share.js:645 js/share.js:657 +#: js/share.js:656 js/share.js:668 msgid "Error" msgstr "Fel" @@ -339,7 +340,7 @@ msgstr "Delad" msgid "Share" msgstr "Dela" -#: js/share.js:131 js/share.js:685 +#: js/share.js:131 js/share.js:696 msgid "Error while sharing" msgstr "Fel vid delning" @@ -439,23 +440,23 @@ msgstr "radera" msgid "share" msgstr "dela" -#: js/share.js:400 js/share.js:632 +#: js/share.js:400 js/share.js:643 msgid "Password protected" msgstr "Lösenordsskyddad" -#: js/share.js:645 +#: js/share.js:656 msgid "Error unsetting expiration date" msgstr "Fel vid borttagning av utgångsdatum" -#: js/share.js:657 +#: js/share.js:668 msgid "Error setting expiration date" msgstr "Fel vid sättning av utgångsdatum" -#: js/share.js:672 +#: js/share.js:683 msgid "Sending ..." msgstr "Skickar ..." -#: js/share.js:683 +#: js/share.js:694 msgid "Email sent" msgstr "E-post skickat" diff --git a/l10n/sv/settings.po b/l10n/sv/settings.po index 284b1287a8..a0a193043a 100644 --- a/l10n/sv/settings.po +++ b/l10n/sv/settings.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Daniel Sandman , 2013 # Gunnar Norin , 2013 # Jan Busk, 2013 # Jan Busk, 2013 @@ -13,9 +14,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:45-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" +"PO-Revision-Date: 2013-09-28 01:44+0000\n" +"Last-Translator: Daniel Sandman \n" "Language-Team: Swedish (http://www.transifex.com/projects/p/owncloud/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -92,42 +93,42 @@ msgstr "Kunde inte uppdatera appen." #: changepassword/controller.php:20 msgid "Wrong password" -msgstr "" +msgstr "Fel lösenord" #: changepassword/controller.php:42 msgid "No user supplied" -msgstr "" +msgstr "Ingen användare angiven" #: changepassword/controller.php:74 msgid "" "Please provide an admin recovery password, otherwise all user data will be " "lost" -msgstr "" +msgstr "Ange ett återställningslösenord för administratören. Annars kommer all användardata förloras" #: changepassword/controller.php:79 msgid "" "Wrong admin recovery password. Please check the password and try again." -msgstr "" +msgstr "Felaktigt återställningslösenord för administratör. Kolla lösenordet och prova igen." #: changepassword/controller.php:87 msgid "" "Back-end doesn't support password change, but the users encryption key was " "successfully updated." -msgstr "" +msgstr "Gränssnittet stödjer inte byte av lösenord, men användarnas krypteringsnyckel blev uppdaterad." #: changepassword/controller.php:92 changepassword/controller.php:103 msgid "Unable to change password" -msgstr "" +msgstr "Kunde inte ändra lösenord" #: js/apps.js:43 msgid "Update to {appversion}" msgstr "Uppdatera till {appversion}" -#: js/apps.js:49 js/apps.js:82 js/apps.js:108 +#: js/apps.js:49 js/apps.js:82 js/apps.js:110 msgid "Disable" msgstr "Deaktivera" -#: js/apps.js:49 js/apps.js:89 js/apps.js:102 js/apps.js:117 +#: js/apps.js:49 js/apps.js:90 js/apps.js:103 js/apps.js:119 msgid "Enable" msgstr "Aktivera" @@ -135,43 +136,43 @@ msgstr "Aktivera" msgid "Please wait...." msgstr "Var god vänta..." -#: js/apps.js:79 js/apps.js:80 js/apps.js:100 +#: js/apps.js:79 js/apps.js:80 js/apps.js:101 msgid "Error while disabling app" msgstr "Fel vid inaktivering av app" -#: js/apps.js:99 js/apps.js:112 js/apps.js:113 +#: js/apps.js:100 js/apps.js:114 js/apps.js:115 msgid "Error while enabling app" msgstr "Fel vid aktivering av app" -#: js/apps.js:123 +#: js/apps.js:125 msgid "Updating...." msgstr "Uppdaterar..." -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error while updating app" msgstr "Fel uppstod vid uppdatering av appen" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error" msgstr "Fel" -#: js/apps.js:127 templates/apps.php:43 +#: js/apps.js:129 templates/apps.php:43 msgid "Update" msgstr "Uppdatera" -#: js/apps.js:130 +#: js/apps.js:132 msgid "Updated" msgstr "Uppdaterad" -#: js/personal.js:220 +#: js/personal.js:221 msgid "Select a profile picture" -msgstr "" +msgstr "Välj en profilbild" -#: js/personal.js:265 +#: js/personal.js:266 msgid "Decrypting files... Please wait, this can take some time." msgstr "Dekrypterar filer... Vänligen vänta, detta kan ta en stund." -#: js/personal.js:287 +#: js/personal.js:288 msgid "Saving..." msgstr "Sparar..." @@ -499,27 +500,27 @@ msgstr "Profilbild" #: templates/personal.php:90 msgid "Upload new" -msgstr "" +msgstr "Ladda upp ny" #: templates/personal.php:92 msgid "Select new from Files" -msgstr "" +msgstr "Välj ny från filer" #: templates/personal.php:93 msgid "Remove image" -msgstr "" +msgstr "Radera bild" #: templates/personal.php:94 msgid "Either png or jpg. Ideally square but you will be able to crop it." -msgstr "" +msgstr "Antingen png eller jpg. Helst fyrkantig, men du kommer att kunna beskära den." #: templates/personal.php:97 msgid "Abort" -msgstr "" +msgstr "Avbryt" #: templates/personal.php:98 msgid "Choose as profile image" -msgstr "" +msgstr "Välj som profilbild" #: templates/personal.php:106 templates/personal.php:107 msgid "Language" diff --git a/l10n/templates/core.pot b/l10n/templates/core.pot index ba60bef371..afb3a410c4 100644 --- a/l10n/templates/core.pot +++ b/l10n/templates/core.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files.pot b/l10n/templates/files.pot index 601c663392..1e72820c52 100644 --- a/l10n/templates/files.pot +++ b/l10n/templates/files.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_encryption.pot b/l10n/templates/files_encryption.pot index 52e8feed91..aea2b2282e 100644 --- a/l10n/templates/files_encryption.pot +++ b/l10n/templates/files_encryption.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_external.pot b/l10n/templates/files_external.pot index a99fbf35c8..ad32cfb2fc 100644 --- a/l10n/templates/files_external.pot +++ b/l10n/templates/files_external.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_sharing.pot b/l10n/templates/files_sharing.pot index 10bee89c27..e9a596f361 100644 --- a/l10n/templates/files_sharing.pot +++ b/l10n/templates/files_sharing.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_trashbin.pot b/l10n/templates/files_trashbin.pot index cfc69f2b2e..044fb375df 100644 --- a/l10n/templates/files_trashbin.pot +++ b/l10n/templates/files_trashbin.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/files_versions.pot b/l10n/templates/files_versions.pot index 284aca099f..a61952105b 100644 --- a/l10n/templates/files_versions.pot +++ b/l10n/templates/files_versions.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/lib.pot b/l10n/templates/lib.pot index 0103b348e9..2f54bdc233 100644 --- a/l10n/templates/lib.pot +++ b/l10n/templates/lib.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/settings.pot b/l10n/templates/settings.pot index c14bb4c3e5..a669dad822 100644 --- a/l10n/templates/settings.pot +++ b/l10n/templates/settings.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -156,15 +156,15 @@ msgstr "" msgid "Updated" msgstr "" -#: js/personal.js:220 +#: js/personal.js:221 msgid "Select a profile picture" msgstr "" -#: js/personal.js:265 +#: js/personal.js:266 msgid "Decrypting files... Please wait, this can take some time." msgstr "" -#: js/personal.js:287 +#: js/personal.js:288 msgid "Saving..." msgstr "" diff --git a/l10n/templates/user_ldap.pot b/l10n/templates/user_ldap.pot index 822d615af9..0848d99353 100644 --- a/l10n/templates/user_ldap.pot +++ b/l10n/templates/user_ldap.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/templates/user_webdavauth.pot b/l10n/templates/user_webdavauth.pot index 3a0f240e93..ec5a60a6ec 100644 --- a/l10n/templates/user_webdavauth.pot +++ b/l10n/templates/user_webdavauth.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 5.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2013-09-27 00:01-0400\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/l10n/uk/files.po b/l10n/uk/files.po index 23349e0800..d269f620d9 100644 --- a/l10n/uk/files.po +++ b/l10n/uk/files.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:44-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" +"PO-Revision-Date: 2013-09-27 19:42+0000\n" +"Last-Translator: zubr139 \n" "Language-Team: Ukrainian (http://www.transifex.com/projects/p/owncloud/language/uk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -87,7 +87,7 @@ msgstr "" msgid "Invalid directory." msgstr "Невірний каталог." -#: appinfo/app.php:12 +#: appinfo/app.php:11 msgid "Files" msgstr "Файли" @@ -118,7 +118,7 @@ msgstr "URL не може бути пустим." #: js/file-upload.js:525 lib/app.php:53 msgid "Invalid folder name. Usage of 'Shared' is reserved by ownCloud" -msgstr "" +msgstr "Неправильне ім'я теки. Використання 'Shared' зарезервовано ownCloud" #: js/file-upload.js:557 js/file-upload.js:573 js/files.js:507 js/files.js:545 msgid "Error" @@ -167,9 +167,9 @@ msgstr "відмінити" #: js/filelist.js:533 js/filelist.js:599 js/files.js:576 msgid "%n folder" msgid_plural "%n folders" -msgstr[0] "" -msgstr[1] "" -msgstr[2] "" +msgstr[0] "%n тека" +msgstr[1] "%n тека" +msgstr[2] "%n теки" #: js/filelist.js:534 js/filelist.js:600 js/files.js:582 msgid "%n file" @@ -290,7 +290,7 @@ msgstr "Текстовий файл" #: templates/index.php:11 msgid "Folder" -msgstr "Папка" +msgstr "Тека" #: templates/index.php:13 msgid "From link" diff --git a/l10n/uk/files_encryption.po b/l10n/uk/files_encryption.po index 46c45f13fe..6957679f93 100644 --- a/l10n/uk/files_encryption.po +++ b/l10n/uk/files_encryption.po @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-18 11:46-0400\n" -"PO-Revision-Date: 2013-09-17 13:05+0000\n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" +"PO-Revision-Date: 2013-09-27 19:12+0000\n" "Last-Translator: zubr139 \n" "Language-Team: Ukrainian (http://www.transifex.com/projects/p/owncloud/language/uk/)\n" "MIME-Version: 1.0\n" @@ -93,7 +93,7 @@ msgstr "" #: templates/invalid_private_key.php:7 msgid "personal settings" -msgstr "" +msgstr "особисті налаштування" #: templates/settings-admin.php:5 templates/settings-personal.php:4 msgid "Encryption" diff --git a/l10n/uk/settings.po b/l10n/uk/settings.po index a96bef5878..912467cac9 100644 --- a/l10n/uk/settings.po +++ b/l10n/uk/settings.po @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# zubr139 , 2013 msgid "" msgstr "" "Project-Id-Version: ownCloud\n" "Report-Msgid-Bugs-To: http://bugs.owncloud.org/\n" -"POT-Creation-Date: 2013-09-20 10:45-0400\n" -"PO-Revision-Date: 2013-09-20 14:45+0000\n" -"Last-Translator: I Robot \n" +"POT-Creation-Date: 2013-09-29 00:02-0400\n" +"PO-Revision-Date: 2013-09-27 19:43+0000\n" +"Last-Translator: zubr139 \n" "Language-Team: Ukrainian (http://www.transifex.com/projects/p/owncloud/language/uk/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -32,7 +33,7 @@ msgstr "" #: ajax/changedisplayname.php:34 msgid "Unable to change display name" -msgstr "Не вдалося змінити зображене ім'я" +msgstr "Не вдалося змінити ім'я" #: ajax/creategroup.php:10 msgid "Group already exists" @@ -117,11 +118,11 @@ msgstr "" msgid "Update to {appversion}" msgstr "Оновити до {appversion}" -#: js/apps.js:49 js/apps.js:82 js/apps.js:108 +#: js/apps.js:49 js/apps.js:82 js/apps.js:110 msgid "Disable" msgstr "Вимкнути" -#: js/apps.js:49 js/apps.js:89 js/apps.js:102 js/apps.js:117 +#: js/apps.js:49 js/apps.js:90 js/apps.js:103 js/apps.js:119 msgid "Enable" msgstr "Включити" @@ -129,43 +130,43 @@ msgstr "Включити" msgid "Please wait...." msgstr "Зачекайте, будь ласка..." -#: js/apps.js:79 js/apps.js:80 js/apps.js:100 +#: js/apps.js:79 js/apps.js:80 js/apps.js:101 msgid "Error while disabling app" msgstr "" -#: js/apps.js:99 js/apps.js:112 js/apps.js:113 +#: js/apps.js:100 js/apps.js:114 js/apps.js:115 msgid "Error while enabling app" msgstr "" -#: js/apps.js:123 +#: js/apps.js:125 msgid "Updating...." msgstr "Оновлюється..." -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error while updating app" msgstr "Помилка при оновленні програми" -#: js/apps.js:126 +#: js/apps.js:128 msgid "Error" msgstr "Помилка" -#: js/apps.js:127 templates/apps.php:43 +#: js/apps.js:129 templates/apps.php:43 msgid "Update" msgstr "Оновити" -#: js/apps.js:130 +#: js/apps.js:132 msgid "Updated" msgstr "Оновлено" -#: js/personal.js:220 +#: js/personal.js:221 msgid "Select a profile picture" msgstr "" -#: js/personal.js:265 +#: js/personal.js:266 msgid "Decrypting files... Please wait, this can take some time." msgstr "" -#: js/personal.js:287 +#: js/personal.js:288 msgid "Saving..." msgstr "Зберігаю..." @@ -590,7 +591,7 @@ msgstr "Сховище" #: templates/users.php:108 msgid "change display name" -msgstr "змінити зображене ім'я" +msgstr "змінити ім'я" #: templates/users.php:112 msgid "set new password" diff --git a/settings/l10n/sv.php b/settings/l10n/sv.php index 5f6313f182..4f8ad376db 100644 --- a/settings/l10n/sv.php +++ b/settings/l10n/sv.php @@ -16,6 +16,12 @@ $TRANSLATIONS = array( "Unable to add user to group %s" => "Kan inte lägga till användare i gruppen %s", "Unable to remove user from group %s" => "Kan inte radera användare från gruppen %s", "Couldn't update app." => "Kunde inte uppdatera appen.", +"Wrong password" => "Fel lösenord", +"No user supplied" => "Ingen användare angiven", +"Please provide an admin recovery password, otherwise all user data will be lost" => "Ange ett återställningslösenord för administratören. Annars kommer all användardata förloras", +"Wrong admin recovery password. Please check the password and try again." => "Felaktigt återställningslösenord för administratör. Kolla lösenordet och prova igen.", +"Back-end doesn't support password change, but the users encryption key was successfully updated." => "Gränssnittet stödjer inte byte av lösenord, men användarnas krypteringsnyckel blev uppdaterad.", +"Unable to change password" => "Kunde inte ändra lösenord", "Update to {appversion}" => "Uppdatera till {appversion}", "Disable" => "Deaktivera", "Enable" => "Aktivera", @@ -27,6 +33,7 @@ $TRANSLATIONS = array( "Error" => "Fel", "Update" => "Uppdatera", "Updated" => "Uppdaterad", +"Select a profile picture" => "Välj en profilbild", "Decrypting files... Please wait, this can take some time." => "Dekrypterar filer... Vänligen vänta, detta kan ta en stund.", "Saving..." => "Sparar...", "deleted" => "raderad", @@ -101,6 +108,12 @@ $TRANSLATIONS = array( "Your email address" => "Din e-postadress", "Fill in an email address to enable password recovery" => "Fyll i en e-postadress för att aktivera återställning av lösenord", "Profile picture" => "Profilbild", +"Upload new" => "Ladda upp ny", +"Select new from Files" => "Välj ny från filer", +"Remove image" => "Radera bild", +"Either png or jpg. Ideally square but you will be able to crop it." => "Antingen png eller jpg. Helst fyrkantig, men du kommer att kunna beskära den.", +"Abort" => "Avbryt", +"Choose as profile image" => "Välj som profilbild", "Language" => "Språk", "Help translate" => "Hjälp att översätta", "WebDAV" => "WebDAV", diff --git a/settings/l10n/uk.php b/settings/l10n/uk.php index 314b7de657..adb46e3ee8 100644 --- a/settings/l10n/uk.php +++ b/settings/l10n/uk.php @@ -2,7 +2,7 @@ $TRANSLATIONS = array( "Unable to load list from App Store" => "Не вдалося завантажити список з App Store", "Authentication error" => "Помилка автентифікації", -"Unable to change display name" => "Не вдалося змінити зображене ім'я", +"Unable to change display name" => "Не вдалося змінити ім'я", "Group already exists" => "Група вже існує", "Unable to add group" => "Не вдалося додати групу", "Email saved" => "Адресу збережено", @@ -97,7 +97,7 @@ $TRANSLATIONS = array( "Other" => "Інше", "Username" => "Ім'я користувача", "Storage" => "Сховище", -"change display name" => "змінити зображене ім'я", +"change display name" => "змінити ім'я", "set new password" => "встановити новий пароль", "Default" => "За замовчуванням" ); From 710d3f139f45f7e82991364f561b31e71e4d787f Mon Sep 17 00:00:00 2001 From: Alessandro Cosentino Date: Sun, 29 Sep 2013 01:46:23 -0400 Subject: [PATCH 148/150] followup of #4032 - makes the settings-button bigger and adds again padding at bottom of app-navigation --- core/css/apps.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/css/apps.css b/core/css/apps.css index de63495e50..f6c20e6cc6 100644 --- a/core/css/apps.css +++ b/core/css/apps.css @@ -16,6 +16,7 @@ -moz-box-sizing: border-box; box-sizing: border-box; background-color: #f8f8f8; border-right: 1px solid #ccc; + padding-bottom: 44px; } #app-navigation > ul { height: 100%; @@ -192,7 +193,7 @@ .settings-button { display: block; - height: 32px; + height: 44px; width: 100%; padding: 0; margin: 0; From b6fc143074ae55bc6f9732d825571681f88f46ed Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Fri, 31 May 2013 18:21:31 +0200 Subject: [PATCH 149/150] cURL does not honour default_socket_timeout SabreDAV uses cURL for the requests and as this does not honour default_socket_timeout, setting it is useless and confusing as people will expect the request to timeout faster than it actually will do. One has to use curl_setopt($curl, CURLOPT_TIMEOUT, x) or curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, x) but there is currently no way to make SabreDAV pass this to cURL. --- lib/util.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/util.php b/lib/util.php index 41f5f1d16b..d4f4eed1ca 100755 --- a/lib/util.php +++ b/lib/util.php @@ -730,12 +730,6 @@ class OC_Util { 'baseUri' => OC_Helper::linkToRemote('webdav'), ); - // save the old timeout so that we can restore it later - $oldTimeout = ini_get("default_socket_timeout"); - - // use a 5 sec timeout for the check. Should be enough for local requests. - ini_set("default_socket_timeout", 5); - $client = new \Sabre_DAV_Client($settings); // for this self test we don't care if the ssl certificate is self signed and the peer cannot be verified. @@ -752,9 +746,6 @@ class OC_Util { $return = false; } - // restore the original timeout - ini_set("default_socket_timeout", $oldTimeout); - return $return; } From aaba0d83b5e9f8b67a739e74172da9464fa562b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 30 Sep 2013 10:03:07 +0200 Subject: [PATCH 150/150] fixing PHPDoc & typo --- lib/connector/sabre/aborteduploaddetectionplugin.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/connector/sabre/aborteduploaddetectionplugin.php b/lib/connector/sabre/aborteduploaddetectionplugin.php index 74c26f41b6..15dca3a680 100644 --- a/lib/connector/sabre/aborteduploaddetectionplugin.php +++ b/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -37,7 +37,6 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl * This method should set up the requires event subscriptions. * * @param Sabre_DAV_Server $server - * @return void */ public function initialize(Sabre_DAV_Server $server) { @@ -54,7 +53,7 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl */ public function verifyContentLength($filePath, Sabre_DAV_INode $node = null) { - // ownCloud chunked upload will be handled in it's own plugin + // ownCloud chunked upload will be handled in its own plugin $chunkHeader = $this->server->httpRequest->getHeader('OC-Chunked'); if ($chunkHeader) { return;