Merge pull request #8095 from nextcloud/webapps-capabilities

Expose navigation entries as API endpoint
This commit is contained in:
Morris Jobke 2018-02-15 16:55:12 +01:00 committed by GitHub
commit 14bc9b1714
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 292 additions and 48 deletions

View File

@ -0,0 +1,92 @@
<?php
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Core\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IURLGenerator;
class NavigationController extends OCSController {
/** @var INavigationManager */
private $navigationManager;
/** @var IURLGenerator */
private $urlGenerator;
public function __construct(string $appName, IRequest $request, INavigationManager $navigationManager, IURLGenerator $urlGenerator) {
parent::__construct($appName, $request);
$this->navigationManager = $navigationManager;
$this->urlGenerator = $urlGenerator;
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param bool $absolute
* @return DataResponse
*/
public function getAppsNavigation(bool $absolute = false): DataResponse {
$navigation = $this->navigationManager->getAll();
if ($absolute) {
$navigation = $this->rewriteToAbsoluteUrls($navigation);
}
return new DataResponse($navigation);
}
/**
* @NoAdminRequired
* @NoCSRFRequired
*
* @param bool $absolute
* @return DataResponse
*/
public function getSettingsNavigation(bool $absolute = false): DataResponse {
$navigation = $this->navigationManager->getAll('settings');
if ($absolute) {
$navigation = $this->rewriteToAbsoluteUrls($navigation);
}
return new DataResponse($navigation);
}
/**
* Rewrite href attribute of navigation entries to an absolute URL
*
* @param array $navigation
* @return array
*/
private function rewriteToAbsoluteUrls(array $navigation): array {
foreach ($navigation as &$entry) {
if (0 !== strpos($entry['href'], $this->urlGenerator->getBaseUrl())) {
$entry['href'] = $this->urlGenerator->getAbsoluteURL($entry['href']);
}
if (0 !== strpos($entry['icon'], $this->urlGenerator->getBaseUrl())) {
$entry['icon'] = $this->urlGenerator->getAbsoluteURL($entry['icon']);
}
}
return $navigation;
}
}

View File

@ -71,6 +71,8 @@ $application->registerRoutes($this, [
['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'], ['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'],
['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'], ['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'],
['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'], ['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'],
['root' => '/core', 'name' => 'Navigation#getAppsNavigation', 'url' => '/navigation/apps', 'verb' => 'GET'],
['root' => '/core', 'name' => 'Navigation#getSettingsNavigation', 'url' => '/navigation/settings', 'verb' => 'GET'],
], ],
]); ]);

View File

@ -521,6 +521,7 @@ return array(
'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php', 'OC\\Core\\Controller\\JsController' => $baseDir . '/core/Controller/JsController.php',
'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LoginController' => $baseDir . '/core/Controller/LoginController.php',
'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\LostController' => $baseDir . '/core/Controller/LostController.php',
'OC\\Core\\Controller\\NavigationController' => $baseDir . '/core/Controller/NavigationController.php',
'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php', 'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php',
'OC\\Core\\Controller\\OCSController' => $baseDir . '/core/Controller/OCSController.php', 'OC\\Core\\Controller\\OCSController' => $baseDir . '/core/Controller/OCSController.php',
'OC\\Core\\Controller\\PreviewController' => $baseDir . '/core/Controller/PreviewController.php', 'OC\\Core\\Controller\\PreviewController' => $baseDir . '/core/Controller/PreviewController.php',

View File

@ -551,6 +551,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php', 'OC\\Core\\Controller\\JsController' => __DIR__ . '/../../..' . '/core/Controller/JsController.php',
'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php', 'OC\\Core\\Controller\\LoginController' => __DIR__ . '/../../..' . '/core/Controller/LoginController.php',
'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php', 'OC\\Core\\Controller\\LostController' => __DIR__ . '/../../..' . '/core/Controller/LostController.php',
'OC\\Core\\Controller\\NavigationController' => __DIR__ . '/../../..' . '/core/Controller/NavigationController.php',
'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php', 'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php',
'OC\\Core\\Controller\\OCSController' => __DIR__ . '/../../..' . '/core/Controller/OCSController.php', 'OC\\Core\\Controller\\OCSController' => __DIR__ . '/../../..' . '/core/Controller/OCSController.php',
'OC\\Core\\Controller\\PreviewController' => __DIR__ . '/../../..' . '/core/Controller/PreviewController.php', 'OC\\Core\\Controller\\PreviewController' => __DIR__ . '/../../..' . '/core/Controller/PreviewController.php',

View File

@ -104,26 +104,61 @@ class NavigationManager implements INavigationManager {
} }
/** /**
* returns all the added Menu entries * Get a list of navigation entries
* @param string $type *
* @return array an array of the added entries * @param string $type type of the navigation entries
* @return array
*/ */
public function getAll($type = 'link') { public function getAll(string $type = 'link'): array {
$this->init(); $this->init();
foreach ($this->closureEntries as $c) { foreach ($this->closureEntries as $c) {
$this->add($c()); $this->add($c());
} }
$this->closureEntries = array(); $this->closureEntries = array();
if ($type === 'all') { $result = $this->entries;
return $this->entries; if ($type !== 'all') {
} $result = array_filter($this->entries, function($entry) use ($type) {
return array_filter($this->entries, function($entry) use ($type) {
return $entry['type'] === $type; return $entry['type'] === $type;
}); });
} }
return $this->proceedNavigation($result);
}
/**
* Sort navigation entries by order, name and set active flag
*
* @param array $list
* @return array
*/
private function proceedNavigation(array $list): array {
usort($list, function($a, $b) {
if (isset($a['order']) && isset($b['order'])) {
return ($a['order'] < $b['order']) ? -1 : 1;
} else if (isset($a['order']) || isset($b['order'])) {
return isset($a['order']) ? -1 : 1;
} else {
return ($a['name'] < $b['name']) ? -1 : 1;
}
});
$activeApp = $this->getActiveEntry();
if ($activeApp !== null) {
foreach ($list as $index => &$navEntry) {
if ($navEntry['id'] == $activeApp) {
$navEntry['active'] = true;
} else {
$navEntry['active'] = false;
}
}
unset($navEntry);
}
return $list;
}
/** /**
* removes all the entries * removes all the entries
*/ */

View File

@ -80,9 +80,9 @@ class TemplateLayout extends \OC_Template {
// Add navigation entry // Add navigation entry
$this->assign( 'application', ''); $this->assign( 'application', '');
$this->assign( 'appid', $appId ); $this->assign( 'appid', $appId );
$navigation = \OC_App::getNavigation(); $navigation = \OC::$server->getNavigationManager()->getAll();
$this->assign( 'navigation', $navigation); $this->assign( 'navigation', $navigation);
$settingsNavigation = \OC_App::getSettingsNavigation(); $settingsNavigation = \OC::$server->getNavigationManager()->getAll('settings');
$this->assign( 'settingsnavigation', $settingsNavigation); $this->assign( 'settingsnavigation', $settingsNavigation);
foreach($navigation as $entry) { foreach($navigation as $entry) {
if ($entry['active']) { if ($entry['active']) {

View File

@ -445,31 +445,6 @@ class OC_App {
$appManager->disableApp($app); $appManager->disableApp($app);
} }
// This is private as well. It simply works, so don't ask for more details
private static function proceedNavigation($list) {
usort($list, function($a, $b) {
if (isset($a['order']) && isset($b['order'])) {
return ($a['order'] < $b['order']) ? -1 : 1;
} else if (isset($a['order']) || isset($b['order'])) {
return isset($a['order']) ? -1 : 1;
} else {
return ($a['name'] < $b['name']) ? -1 : 1;
}
});
$activeApp = OC::$server->getNavigationManager()->getActiveEntry();
foreach ($list as $index => &$navEntry) {
if ($navEntry['id'] == $activeApp) {
$navEntry['active'] = true;
} else {
$navEntry['active'] = false;
}
}
unset($navEntry);
return $list;
}
/** /**
* Get the path where to install apps * Get the path where to install apps
* *
@ -613,6 +588,7 @@ class OC_App {
* Returns the navigation * Returns the navigation
* *
* @return array * @return array
* @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll()
* *
* This function returns an array containing all entries added. The * This function returns an array containing all entries added. The
* entries are sorted by the key 'order' ascending. Additional to the keys * entries are sorted by the key 'order' ascending. Additional to the keys
@ -620,21 +596,20 @@ class OC_App {
* - active: boolean, signals if the user is on this navigation entry * - active: boolean, signals if the user is on this navigation entry
*/ */
public static function getNavigation() { public static function getNavigation() {
$entries = OC::$server->getNavigationManager()->getAll(); return OC::$server->getNavigationManager()->getAll();
return self::proceedNavigation($entries);
} }
/** /**
* Returns the Settings Navigation * Returns the Settings Navigation
* *
* @return string[] * @return string[]
* @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings')
* *
* This function returns an array containing all settings pages added. The * This function returns an array containing all settings pages added. The
* entries are sorted by the key 'order' ascending. * entries are sorted by the key 'order' ascending.
*/ */
public static function getSettingsNavigation() { public static function getSettingsNavigation() {
$entries = OC::$server->getNavigationManager()->getAll('settings'); return OC::$server->getNavigationManager()->getAll('settings');
return self::proceedNavigation($entries);
} }
/** /**

View File

@ -57,4 +57,13 @@ interface INavigationManager {
* @since 6.0.0 * @since 6.0.0
*/ */
public function setActiveEntry($appId); public function setActiveEntry($appId);
/**
* Get a list of navigation entries
*
* @param string $type type of the navigation entries
* @return array
* @since 14.0.0
*/
public function getAll(string $type = 'link'): array;
} }

View File

@ -23,6 +23,6 @@
OC_Util::checkAdminUser(); OC_Util::checkAdminUser();
OCP\JSON::callCheck(); OCP\JSON::callCheck();
$navigation = \OC_App::getNavigation(); $navigation = \OC::$server->getNavigationManager()->getAll();
OCP\JSON::success(['nav_entries' => $navigation]); OCP\JSON::success(['nav_entries' => $navigation]);

View File

@ -0,0 +1,129 @@
<?php
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace Tests\Core\Controller;
use OC\Core\Controller\NavigationController;
use OCP\AppFramework\Http\DataResponse;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IURLGenerator;
use Test\TestCase;
class NavigationControllerTest extends TestCase {
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
private $request;
/** @var INavigationManager|\PHPUnit_Framework_MockObject_MockObject */
private $navigationManager;
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
private $urlGenerator;
/** @var NavigationController */
private $controller;
public function setUp() {
parent::setUp();
$this->request = $this->createMock(IRequest::class);
$this->navigationManager = $this->createMock(INavigationManager::class);
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->controller = new NavigationController(
'core',
$this->request,
$this->navigationManager,
$this->urlGenerator
);
}
public function dataGetNavigation() {
return [
[false], [true]
];
}
/** @dataProvider dataGetNavigation */
public function testGetAppNavigation($absolute) {
$this->navigationManager->expects($this->once())
->method('getAll')
->with('link')
->willReturn([ ['id' => 'files', 'href' => '/index.php/apps/files', 'icon' => 'icon' ] ]);
if ($absolute) {
$this->urlGenerator->expects($this->any())
->method('getBaseURL')
->willReturn('http://localhost/');
$this->urlGenerator->expects($this->at(1))
->method('getAbsoluteURL')
->with('/index.php/apps/files')
->willReturn('http://localhost/index.php/apps/files');
$this->urlGenerator->expects($this->at(3))
->method('getAbsoluteURL')
->with('icon')
->willReturn('http://localhost/icon');
$actual = $this->controller->getAppsNavigation($absolute);
$this->assertInstanceOf(DataResponse::class, $actual);
$this->assertEquals('http://localhost/index.php/apps/files', $actual->getData()[0]['href']);
$this->assertEquals('http://localhost/icon', $actual->getData()[0]['icon']);
} else {
$actual = $this->controller->getAppsNavigation($absolute);
$this->assertInstanceOf(DataResponse::class, $actual);
$this->assertEquals('/index.php/apps/files', $actual->getData()[0]['href']);
$this->assertEquals('icon', $actual->getData()[0]['icon']);
}
}
/** @dataProvider dataGetNavigation */
public function testGetSettingsNavigation($absolute) {
$this->navigationManager->expects($this->once())
->method('getAll')
->with('settings')
->willReturn([ ['id' => 'settings', 'href' => '/index.php/settings/user', 'icon' => '/core/img/settings.svg'] ]);
if ($absolute) {
$this->urlGenerator->expects($this->any())
->method('getBaseURL')
->willReturn('http://localhost/');
$this->urlGenerator->expects($this->at(1))
->method('getAbsoluteURL')
->with('/index.php/settings/user')
->willReturn('http://localhost/index.php/settings/user');
$this->urlGenerator->expects($this->at(3))
->method('getAbsoluteURL')
->with('/core/img/settings.svg')
->willReturn('http://localhost/core/img/settings.svg');
$actual = $this->controller->getSettingsNavigation($absolute);
$this->assertInstanceOf(DataResponse::class, $actual);
$this->assertEquals('http://localhost/index.php/settings/user', $actual->getData()[0]['href']);
$this->assertEquals('http://localhost/core/img/settings.svg', $actual->getData()[0]['icon']);
} else {
$actual = $this->controller->getSettingsNavigation($absolute);
$this->assertInstanceOf(DataResponse::class, $actual);
$this->assertEquals('/index.php/settings/user', $actual->getData()[0]['href']);
$this->assertEquals('/core/img/settings.svg', $actual->getData()[0]['icon']);
}
}
}

View File

@ -284,7 +284,7 @@ class NavigationManagerTest extends TestCase {
], ],
]; ];
return [ return [
'minimalistic' => [array_merge($defaults, [[ 'minimalistic' => [array_merge([$defaults[0]], [[
'id' => 'test', 'id' => 'test',
'order' => 100, 'order' => 100,
'href' => '/apps/test/', 'href' => '/apps/test/',
@ -293,8 +293,8 @@ class NavigationManagerTest extends TestCase {
'active' => false, 'active' => false,
'type' => 'link', 'type' => 'link',
'classes' => '', 'classes' => '',
]]), ['navigations' => [['route' => 'test.page.index', 'name' => 'Test']]]], ]], [$defaults[1]]), ['navigations' => [['route' => 'test.page.index', 'name' => 'Test']]]],
'minimalistic-settings' => [array_merge($defaults, [[ 'minimalistic-settings' => [array_merge([$defaults[0]], [[
'id' => 'test', 'id' => 'test',
'order' => 100, 'order' => 100,
'href' => '/apps/test/', 'href' => '/apps/test/',
@ -303,8 +303,8 @@ class NavigationManagerTest extends TestCase {
'active' => false, 'active' => false,
'type' => 'settings', 'type' => 'settings',
'classes' => '', 'classes' => '',
]]), ['navigations' => [['route' => 'test.page.index', 'name' => 'Test', 'type' => 'settings']]]], ]], [$defaults[1]]), ['navigations' => [['route' => 'test.page.index', 'name' => 'Test', 'type' => 'settings']]]],
'admin' => [array_merge($apps, $defaults, [[ 'admin' => [array_merge([$defaults[0]], $apps, [[
'id' => 'test', 'id' => 'test',
'order' => 100, 'order' => 100,
'href' => '/apps/test/', 'href' => '/apps/test/',
@ -313,8 +313,8 @@ class NavigationManagerTest extends TestCase {
'active' => false, 'active' => false,
'type' => 'link', 'type' => 'link',
'classes' => '', 'classes' => '',
]]), ['navigations' => [['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']]], true], ]], [$defaults[1]]), ['navigations' => [['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']]], true],
'no name' => [array_merge($apps, $defaults), ['navigations' => [['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index']]], true], 'no name' => [array_merge([$defaults[0]], $apps, [$defaults[1]]), ['navigations' => [['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index']]], true],
'no admin' => [$defaults, ['navigations' => [['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']]]] 'no admin' => [$defaults, ['navigations' => [['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']]]]
]; ];
} }