From 0b1fb180a53fc4e533e2f194977293bc509c5df3 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 19 Sep 2016 19:43:47 +0200 Subject: Make AppConfig part of the public API Signed-off-by: Joas Schilling --- apps/provisioning_api/appinfo/routes.php | 17 +-- .../lib/Controller/AppConfigController.php | 158 +++++++++++++++++++++ core/ajax/appconfig.php | 70 --------- core/js/config.js | 94 +++++++----- core/js/core.json | 1 + core/js/js.js | 3 +- core/js/public/appconfig.js | 113 +++++++++++++++ core/routes.php | 3 - core/shipped.json | 1 + lib/private/legacy/template.php | 1 + settings/js/admin.js | 31 ++-- tests/lib/App/ManagerTest.php | 14 +- tests/lib/AppTest.php | 9 +- 13 files changed, 374 insertions(+), 141 deletions(-) create mode 100644 apps/provisioning_api/lib/Controller/AppConfigController.php delete mode 100644 core/ajax/appconfig.php create mode 100644 core/js/public/appconfig.js diff --git a/apps/provisioning_api/appinfo/routes.php b/apps/provisioning_api/appinfo/routes.php index a7366a32a06..04a34fba903 100644 --- a/apps/provisioning_api/appinfo/routes.php +++ b/apps/provisioning_api/appinfo/routes.php @@ -26,12 +26,7 @@ * */ -use OCA\Provisioning_API\Apps; -use OCA\Provisioning_API\Users; -use OCP\API; - -$app = new \OCA\Provisioning_API\AppInfo\Application(); -$app->registerRoutes($this, [ +return [ 'ocs' => [ // Apps ['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'], @@ -46,7 +41,7 @@ $app->registerRoutes($this, [ ['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE'], ['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET'], - //Users + // Users ['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'], ['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'], ['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'], @@ -61,5 +56,11 @@ $app->registerRoutes($this, [ ['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'], ['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'], + // Config + ['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'], + ['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'], + ['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'], + ['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'], + ['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'], ], -]); +]; diff --git a/apps/provisioning_api/lib/Controller/AppConfigController.php b/apps/provisioning_api/lib/Controller/AppConfigController.php new file mode 100644 index 00000000000..05c86c17ac2 --- /dev/null +++ b/apps/provisioning_api/lib/Controller/AppConfigController.php @@ -0,0 +1,158 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Provisioning_API\Controller; + + +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IRequest; + +class AppConfigController extends OCSController { + + /** @var IAppConfig */ + protected $config; + + /** @var IConfig */ + protected $appConfig; + + /** + * @param string $appName + * @param IRequest $request + * @param IConfig $config + * @param IAppConfig $appConfig + */ + public function __construct($appName, + IRequest $request, + IConfig $config, + IAppConfig $appConfig) { + parent::__construct($appName, $request); + $this->config = $config; + $this->appConfig = $appConfig; + } + + /** + * @return DataResponse + */ + public function getApps() { + return new DataResponse([ + 'data' => $this->appConfig->getApps(), + ]); + } + + /** + * @param string $app + * @return DataResponse + */ + public function getKeys($app) { + try { + $this->verifyAppId($app); + } catch (\InvalidArgumentException $e) { + return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); + } + return new DataResponse([ + 'data' => $this->config->getAppKeys($app), + ]); + } + + /** + * @param string $app + * @param string $key + * @param string $defaultValue + * @return DataResponse + */ + public function getValue($app, $key, $defaultValue = '') { + try { + $this->verifyAppId($app); + } catch (\InvalidArgumentException $e) { + return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); + } + return new DataResponse([ + 'data' => $this->config->getAppValue($app, $key, $defaultValue), + ]); + } + + /** + * @PasswordConfirmationRequired + * @param string $app + * @param string $key + * @param string $value + * @return DataResponse + */ + public function setValue($app, $key, $value) { + try { + $this->verifyAppId($app); + $this->verifyConfigKey($app, $key); + } catch (\InvalidArgumentException $e) { + return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); + } + + $this->config->setAppValue($app, $key, $value); + return new DataResponse(); + } + + /** + * @PasswordConfirmationRequired + * @param string $app + * @param string $key + * @return DataResponse + */ + public function deleteKey($app, $key) { + try { + $this->verifyAppId($app); + $this->verifyConfigKey($app, $key); + } catch (\InvalidArgumentException $e) { + return new DataResponse(['data' => ['message' => $e->getMessage()]], Http::STATUS_FORBIDDEN); + } + + return new DataResponse([ + 'data' => $this->config->deleteAppValue($app, $key), + ]); + } + + /** + * @param string $app + * @throws \InvalidArgumentException + */ + protected function verifyAppId($app) { + if (\OC_App::cleanAppId($app) !== $app) { + throw new \InvalidArgumentException('Invalid app id given'); + } + } + + /** + * @param string $app + * @param string $key + * @throws \InvalidArgumentException + */ + protected function verifyConfigKey($app, $key) { + if (in_array($key, ['installed_version', 'enabled', 'types'])) { + throw new \InvalidArgumentException('The given key can not be set'); + } + + if ($app === 'core' && (strpos($key, 'public_') === 0 || strpos($key, 'remote_') === 0)) { + throw new \InvalidArgumentException('The given key can not be set'); + } + } +} diff --git a/core/ajax/appconfig.php b/core/ajax/appconfig.php deleted file mode 100644 index 6bb70e4c393..00000000000 --- a/core/ajax/appconfig.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @author Morris Jobke - * @author Robin Appelman - * @author Thomas Müller - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -OC_Util::checkAdminUser(); -OCP\JSON::callCheck(); - -$action=isset($_POST['action'])?$_POST['action']:$_GET['action']; - -if(isset($_POST['app']) || isset($_GET['app'])) { - $app=OC_App::cleanAppId(isset($_POST['app'])? (string)$_POST['app']: (string)$_GET['app']); -} - -// An admin should not be able to add remote and public services -// on its own. This should only be possible programmatically. -// This change is due the fact that an admin may not be expected -// to execute arbitrary code in every environment. -if($app === 'core' && isset($_POST['key']) &&(substr((string)$_POST['key'],0,7) === 'remote_' || substr((string)$_POST['key'],0,7) === 'public_')) { - OC_JSON::error(array('data' => array('message' => 'Unexpected error!'))); - return; -} - -$result=false; -$appConfig = \OC::$server->getAppConfig(); -switch($action) { - case 'getValue': - $result=$appConfig->getValue($app, (string)$_GET['key'], (string)$_GET['defaultValue']); - break; - case 'setValue': - $result=$appConfig->setValue($app, (string)$_POST['key'], (string)$_POST['value']); - break; - case 'getApps': - $result=$appConfig->getApps(); - break; - case 'getKeys': - $result=$appConfig->getKeys($app); - break; - case 'hasKey': - $result=$appConfig->hasKey($app, (string)$_GET['key']); - break; - case 'deleteKey': - $result=$appConfig->deleteKey($app, (string)$_POST['key']); - break; - case 'deleteApp': - $result=$appConfig->deleteApp($app); - break; -} -OC_JSON::success(array('data'=>$result)); - diff --git a/core/js/config.js b/core/js/config.js index b034b7e8cd3..7dc742f7567 100644 --- a/core/js/config.js +++ b/core/js/config.js @@ -1,58 +1,80 @@ /** - * Copyright (c) 2011, Robin Appelman - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. + * @copyright Copyright (c) 2016 Joas Schilling + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * */ /** * @namespace + * @deprecated Use OCP.AppConfig instead */ OC.AppConfig={ - url:OC.filePath('core','ajax','appconfig.php'), - getCall:function(action,data,callback){ - data.action=action; - $.getJSON(OC.AppConfig.url,data,function(result){ - if(result.status==='success'){ - if(callback){ - callback(result.data); - } - } - }); - }, - postCall:function(action,data,callback){ - data.action=action; - $.post(OC.AppConfig.url,data,function(result){ - if(result.status==='success'){ - if(callback){ - callback(result.data); - } - } - },'json'); - }, + /** + * @deprecated Use OCP.AppConfig.getValue() instead + */ getValue:function(app,key,defaultValue,callback){ - if(typeof defaultValue=='function'){ - callback=defaultValue; - defaultValue=null; - } - OC.AppConfig.getCall('getValue',{app:app,key:key,defaultValue:defaultValue},callback); + OCP.AppConfig.getValue(app, key, defaultValue, { + success: callback + }); }, + + /** + * @deprecated Use OCP.AppConfig.setValue() instead + */ setValue:function(app,key,value){ - OC.AppConfig.postCall('setValue',{app:app,key:key,value:value}); + OCP.AppConfig.setValue(app, key, value); }, + + /** + * @deprecated Use OCP.AppConfig.getApps() instead + */ getApps:function(callback){ - OC.AppConfig.getCall('getApps',{},callback); + OCP.AppConfig.getApps({ + success: callback + }); }, + + /** + * @deprecated Use OCP.AppConfig.getKeys() instead + */ getKeys:function(app,callback){ - OC.AppConfig.getCall('getKeys',{app:app},callback); + OCP.AppConfig.getKeys(app, { + success: callback + }); }, + + /** + * @deprecated + */ hasKey:function(app,key,callback){ - OC.AppConfig.getCall('hasKey',{app:app,key:key},callback); + console.error('OC.AppConfig.hasKey is not supported anymore. Use OCP.AppConfig.getValue instead.'); }, + + /** + * @deprecated Use OCP.AppConfig.deleteKey() instead + */ deleteKey:function(app,key){ - OC.AppConfig.postCall('deleteKey',{app:app,key:key}); + OCP.AppConfig.deleteKey(app, key); }, + + /** + * @deprecated + */ deleteApp:function(app){ - OC.AppConfig.postCall('deleteApp',{app:app}); + console.error('OC.AppConfig.deleteApp is not supported anymore.'); } }; -//TODO OC.Preferences diff --git a/core/js/core.json b/core/js/core.json index c98928d0fed..7e72234aaa4 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -39,6 +39,7 @@ "octemplate.js", "eventsource.js", "config.js", + "public/appconfig.js", "multiselect.js", "oc-requesttoken.js", "setupchecks.js", diff --git a/core/js/js.js b/core/js/js.js index 16da273c8e1..87b4370914c 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -58,7 +58,8 @@ function fileDownloadPath(dir, file) { } /** @namespace */ -var OC={ +var OCP = {}, + OC = { PERMISSION_CREATE:4, PERMISSION_READ:1, PERMISSION_UPDATE:2, diff --git a/core/js/public/appconfig.js b/core/js/public/appconfig.js new file mode 100644 index 00000000000..82f479d75b2 --- /dev/null +++ b/core/js/public/appconfig.js @@ -0,0 +1,113 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/** + * @namespace + * @since 9.2.0 + */ +OCP.AppConfig = { + /** + * @param {string} method + * @param {string} endpoint + * @param {Object} [options] + * @param {Object} [options.data] + * @param {function} [options.success] + * @param {function} [options.error] + * @internal + */ + _call: function(method, endpoint, options) { + + $.ajax({ + type: method.toUpperCase(), + url: OC.linkToOCS('apps/provisioning_api/api/v1', 2) + 'config/apps' + endpoint, + data: options.data || {}, + success: options.success, + error: options.error + }) + }, + + /** + * @param {Object} [options] + * @param {function} [options.success] + * @since 9.2.0 + */ + getApps: function(options) { + this._call('get', '', options); + }, + + /** + * @param {string} app + * @param {Object} [options] + * @param {function} [options.success] + * @param {function} [options.error] + * @since 9.2.0 + */ + getKeys: function(app, options) { + this._call('get', '/' + app, options); + }, + + /** + * @param {string} app + * @param {string} key + * @param {string|function} defaultValue + * @param {Object} [options] + * @param {function} [options.success] + * @param {function} [options.error] + * @since 9.2.0 + */ + getValue: function(app, key, defaultValue, options) { + options = options || {}; + options['data'] = { + defaultValue: defaultValue + }; + + this._call('get', '/' + app + '/' + key, options); + }, + + /** + * @param {string} app + * @param {string} key + * @param {string} value + * @param {Object} [options] + * @param {function} [options.success] + * @param {function} [options.error] + * @since 9.2.0 + */ + setValue: function(app, key, value, options) { + options = options || {}; + options['data'] = { + value: value + }; + + this._call('post', '/' + app + '/' + key, options); + }, + + /** + * @param {string} app + * @param {string} key + * @param {Object} [options] + * @param {function} [options.success] + * @param {function} [options.error] + * @since 9.2.0 + */ + deleteKey: function(app, key, options) { + this._call('delete', '/' + app + '/' + key, options); + } +}; diff --git a/core/routes.php b/core/routes.php index 337f6fb27c3..3ca83815ad4 100644 --- a/core/routes.php +++ b/core/routes.php @@ -66,9 +66,6 @@ $application->registerRoutes($this, [ // Search $this->create('search_ajax_search', '/core/search') ->actionInclude('core/search/ajax/search.php'); -// AppConfig -$this->create('core_ajax_appconfig', '/core/ajax/appconfig.php') - ->actionInclude('core/ajax/appconfig.php'); // oC JS config $this->create('js_config', '/core/js/oc.js') ->actionInclude('core/js/config.php'); diff --git a/core/shipped.json b/core/shipped.json index 0ddaf68eb64..ee8a2b05ef6 100644 --- a/core/shipped.json +++ b/core/shipped.json @@ -40,6 +40,7 @@ "files", "dav", "federatedfilesharing", + "provisioning_api", "twofactor_backupcodes", "workflowengine" ] diff --git a/lib/private/legacy/template.php b/lib/private/legacy/template.php index b43b4da839a..9483eaf5f55 100644 --- a/lib/private/legacy/template.php +++ b/lib/private/legacy/template.php @@ -135,6 +135,7 @@ class OC_Template extends \OC\Template\Base { OC_Util::addScript("oc-requesttoken", null, true); OC_Util::addScript('search', 'search', true); OC_Util::addScript("config", null, true); + OC_Util::addScript("public/appconfig", null, true); OC_Util::addScript("eventsource", null, true); OC_Util::addScript("octemplate", null, true); OC_Util::addTranslations("core", null, true); diff --git a/settings/js/admin.js b/settings/js/admin.js index a2a1cef531c..430947e5956 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -114,31 +114,22 @@ $(document).ready(function(){ }); var savePublicShareDisclaimerText = _.debounce(function(value) { - var data = { - app:'core', - key:'shareapi_public_link_disclaimertext' + var options = { + success: function() { + OC.msg.finishedSuccess('#publicShareDisclaimerStatus', t('core', 'Saved')); + }, + error: function() { + OC.msg.finishedError('#publicShareDisclaimerStatus', t('core', 'Not saved')); + } }; + + OC.msg.startSaving('#publicShareDisclaimerStatus'); if (_.isString(value) && value !== '') { - data['action'] = 'setValue'; - data['value'] = value; + OCP.AppConfig.setValue('core', 'shareapi_public_link_disclaimertext', value, options); } else { - data['action'] = 'deleteKey'; $('#publicShareDisclaimerText').val(''); + OCP.AppConfig.deleteKey('core', 'shareapi_public_link_disclaimertext', options); } - - OC.msg.startSaving('#publicShareDisclaimerStatus'); - $.post( - OC.AppConfig.url, - data, - function(result){ - if(result.status === 'success'){ - OC.msg.finishedSuccess('#publicShareDisclaimerStatus', t('core', 'Saved')) - } else { - OC.msg.finishedError('#publicShareDisclaimerStatus', t('core', 'Not saved')) - } - }, - 'json' - ); }, 500); $('#publicShareDisclaimerText').on('change, keyup', function() { diff --git a/tests/lib/App/ManagerTest.php b/tests/lib/App/ManagerTest.php index f3a91d4f5f4..e04f7c82375 100644 --- a/tests/lib/App/ManagerTest.php +++ b/tests/lib/App/ManagerTest.php @@ -306,7 +306,16 @@ class ManagerTest extends TestCase { $this->appConfig->setValue('test1', 'enabled', 'yes'); $this->appConfig->setValue('test2', 'enabled', 'no'); $this->appConfig->setValue('test3', 'enabled', '["foo"]'); - $apps = ['dav', 'federatedfilesharing', 'files', 'test1', 'test3', 'twofactor_backupcodes', 'workflowengine']; + $apps = [ + 'dav', + 'federatedfilesharing', + 'files', + 'provisioning_api', + 'test1', + 'test3', + 'twofactor_backupcodes', + 'workflowengine', + ]; $this->assertEquals($apps, $this->manager->getInstalledApps()); } @@ -325,6 +334,7 @@ class ManagerTest extends TestCase { 'dav', 'federatedfilesharing', 'files', + 'provisioning_api', 'test1', 'test3', 'twofactor_backupcodes', @@ -343,6 +353,7 @@ class ManagerTest extends TestCase { 'dav' => ['id' => 'dav'], 'files' => ['id' => 'files'], 'federatedfilesharing' => ['id' => 'federatedfilesharing'], + 'provisioning_api' => ['id' => 'provisioning_api'], 'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '9.0.0'], 'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'], 'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'], @@ -386,6 +397,7 @@ class ManagerTest extends TestCase { 'dav' => ['id' => 'dav'], 'files' => ['id' => 'files'], 'federatedfilesharing' => ['id' => 'federatedfilesharing'], + 'provisioning_api' => ['id' => 'provisioning_api'], 'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '8.0.0'], 'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'], 'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'], diff --git a/tests/lib/AppTest.php b/tests/lib/AppTest.php index ea4f328b63a..b7263adb78b 100644 --- a/tests/lib/AppTest.php +++ b/tests/lib/AppTest.php @@ -317,6 +317,7 @@ class AppTest extends \Test\TestCase { 'appforgroup12', 'dav', 'federatedfilesharing', + 'provisioning_api', 'twofactor_backupcodes', 'workflowengine', ), @@ -333,6 +334,7 @@ class AppTest extends \Test\TestCase { 'appforgroup2', 'dav', 'federatedfilesharing', + 'provisioning_api', 'twofactor_backupcodes', 'workflowengine', ), @@ -350,6 +352,7 @@ class AppTest extends \Test\TestCase { 'appforgroup2', 'dav', 'federatedfilesharing', + 'provisioning_api', 'twofactor_backupcodes', 'workflowengine', ), @@ -367,6 +370,7 @@ class AppTest extends \Test\TestCase { 'appforgroup2', 'dav', 'federatedfilesharing', + 'provisioning_api', 'twofactor_backupcodes', 'workflowengine', ), @@ -384,6 +388,7 @@ class AppTest extends \Test\TestCase { 'appforgroup2', 'dav', 'federatedfilesharing', + 'provisioning_api', 'twofactor_backupcodes', 'workflowengine', ), @@ -463,11 +468,11 @@ class AppTest extends \Test\TestCase { ); $apps = \OC_App::getEnabledApps(); - $this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'twofactor_backupcodes', 'workflowengine'), $apps); + $this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'provisioning_api', 'twofactor_backupcodes', 'workflowengine'), $apps); // mock should not be called again here $apps = \OC_App::getEnabledApps(); - $this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'twofactor_backupcodes', 'workflowengine'), $apps); + $this->assertEquals(array('files', 'app3', 'dav', 'federatedfilesharing', 'provisioning_api', 'twofactor_backupcodes', 'workflowengine'), $apps); $this->restoreAppConfig(); \OC_User::setUserId(null); -- cgit v1.2.3