Skip to content

Commit 8b95bd2

Browse files
authored
Merge pull request #2822 from nextcloud/add-navigation-via-info.xml
Add navigation via info.xml (#26785)
2 parents 05884bc + 27f8a83 commit 8b95bd2

6 files changed

Lines changed: 188 additions & 20 deletions

File tree

apps/files/appinfo/app.php

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,10 @@
2626
* along with this program. If not, see <http://www.gnu.org/licenses/>
2727
*
2828
*/
29+
// required for translation purpose
30+
// t('Files')
2931
$l = \OC::$server->getL10N('files');
3032

31-
\OC::$server->getNavigationManager()->add(function () {
32-
$urlGenerator = \OC::$server->getURLGenerator();
33-
$l = \OC::$server->getL10N('files');
34-
return [
35-
'id' => 'files_index',
36-
'order' => 0,
37-
'href' => $urlGenerator->linkToRoute('files.view.index'),
38-
'icon' => $urlGenerator->imagePath('core', 'places/files.svg'),
39-
'name' => $l->t('Files'),
40-
];
41-
});
42-
4333
\OC::$server->getSearch()->registerProvider('OC\Search\Provider\File', array('apps' => array('files')));
4434

4535
$templateManager = \OC_Helper::getFileTemplateManager();

apps/files/appinfo/info.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,11 @@
5353
<command>OCA\Files\Command\DeleteOrphanedFiles</command>
5454
<command>OCA\Files\Command\TransferOwnership</command>
5555
</commands>
56+
57+
<navigation>
58+
<name>Files</name>
59+
<route>files.view.index</route>
60+
<order>0</order>
61+
</navigation>
62+
5663
</info>

apps/files/img/app.svg

Lines changed: 6 additions & 0 deletions
Loading

lib/private/NavigationManager.php

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
/**
3-
* @copyright Copyright (c) 2016, ownCloud, Inc.
3+
* @copyright Copyright (c) 2016, ownCloud GmbH
44
*
55
* @author Bart Visscher <bartv@thisnet.nl>
66
* @author Joas Schilling <coding@schilljs.com>
@@ -26,13 +26,46 @@
2626

2727
namespace OC;
2828

29+
use OC\App\AppManager;
30+
use OCP\App\IAppManager;
31+
use OCP\IGroupManager;
32+
use OCP\INavigationManager;
33+
use OCP\IURLGenerator;
34+
use OCP\IUserSession;
35+
use OCP\L10N\IFactory;
36+
2937
/**
3038
* Manages the ownCloud navigation
3139
*/
32-
class NavigationManager implements \OCP\INavigationManager {
33-
protected $entries = array();
34-
protected $closureEntries = array();
40+
41+
class NavigationManager implements INavigationManager {
42+
protected $entries = [];
43+
protected $closureEntries = [];
3544
protected $activeEntry;
45+
/** @var bool */
46+
protected $init = false;
47+
/** @var IAppManager|AppManager */
48+
protected $appManager;
49+
/** @var IURLGenerator */
50+
private $urlGenerator;
51+
/** @var IFactory */
52+
private $l10nFac;
53+
/** @var IUserSession */
54+
private $userSession;
55+
/** @var IGroupManager */
56+
private $groupManager;
57+
58+
public function __construct(IAppManager $appManager = null,
59+
IURLGenerator $urlGenerator = null,
60+
IFactory $l10nFac = null,
61+
IUserSession $userSession = null,
62+
IGroupManager$groupManager = null) {
63+
$this->appManager = $appManager;
64+
$this->urlGenerator = $urlGenerator;
65+
$this->l10nFac = $l10nFac;
66+
$this->userSession = $userSession;
67+
$this->groupManager = $groupManager;
68+
}
3669

3770
/**
3871
* Creates a new navigation entry
@@ -60,6 +93,7 @@ public function add($entry) {
6093
* @return array an array of the added entries
6194
*/
6295
public function getAll() {
96+
$this->init();
6397
foreach ($this->closureEntries as $c) {
6498
$this->add($c());
6599
}
@@ -71,8 +105,9 @@ public function getAll() {
71105
* removes all the entries
72106
*/
73107
public function clear() {
74-
$this->entries = array();
75-
$this->closureEntries = array();
108+
$this->entries = [];
109+
$this->closureEntries = [];
110+
$this->init = false;
76111
}
77112

78113
/**
@@ -93,4 +128,64 @@ public function setActiveEntry($id) {
93128
public function getActiveEntry() {
94129
return $this->activeEntry;
95130
}
131+
132+
private function init() {
133+
if ($this->init) {
134+
return;
135+
}
136+
$this->init = true;
137+
if (is_null($this->appManager)) {
138+
return;
139+
}
140+
foreach ($this->appManager->getInstalledApps() as $app) {
141+
// load plugins and collections from info.xml
142+
$info = $this->appManager->getAppInfo($app);
143+
if (!isset($info['navigation'])) {
144+
continue;
145+
}
146+
$nav = $info['navigation'];
147+
if (!isset($nav['name'])) {
148+
continue;
149+
}
150+
if (!isset($nav['route'])) {
151+
continue;
152+
}
153+
$role = isset($nav['@attributes']['role']) ? $nav['@attributes']['role'] : 'all';
154+
if ($role === 'admin' && !$this->isAdmin()) {
155+
continue;
156+
}
157+
$l = $this->l10nFac->get($app);
158+
$order = isset($nav['order']) ? $nav['order'] : 100;
159+
$route = $this->urlGenerator->linkToRoute($nav['route']);
160+
$icon = isset($nav['icon']) ? $nav['icon'] : 'app.svg';
161+
foreach ([$icon, "$app.svg"] as $i) {
162+
try {
163+
$icon = $this->urlGenerator->imagePath($app, $i);
164+
break;
165+
} catch (\RuntimeException $ex) {
166+
// no icon? - ignore it then
167+
}
168+
}
169+
if (is_null($icon)) {
170+
$icon = $this->urlGenerator->imagePath('core', 'default-app-icon');
171+
}
172+
173+
$this->add([
174+
'id' => $app,
175+
'order' => $order,
176+
'href' => $route,
177+
'icon' => $icon,
178+
'name' => $l->t($nav['name']),
179+
]);
180+
}
181+
}
182+
183+
private function isAdmin() {
184+
$user = $this->userSession->getUser();
185+
if ($user !== null) {
186+
return $this->groupManager->isAdmin($user->getUID());
187+
}
188+
return false;
189+
}
190+
96191
}

lib/private/Server.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,12 @@ public function __construct($webRoot, \OC\Config $config) {
316316
return new \OC\Authentication\TwoFactorAuth\Manager($c->getAppManager(), $c->getSession(), $c->getConfig(), $c->getActivityManager(), $c->getLogger());
317317
});
318318

319-
$this->registerService('NavigationManager', function ($c) {
320-
return new \OC\NavigationManager();
319+
$this->registerService('NavigationManager', function (Server $c) {
320+
return new \OC\NavigationManager($c->getAppManager(),
321+
$c->getURLGenerator(),
322+
$c->getL10NFactory(),
323+
$c->getUserSession(),
324+
$c->getGroupManager());
321325
});
322326
$this->registerService('AllConfig', function (Server $c) {
323327
return new \OC\AllConfig(

tests/lib/NavigationManagerTest.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@
1212

1313
namespace Test;
1414

15+
use OC\App\AppManager;
1516
use OC\NavigationManager;
17+
use OCP\App\IAppManager;
18+
use OCP\IGroupManager;
19+
use OCP\IL10N;
20+
use OCP\IURLGenerator;
21+
use OCP\IUser;
22+
use OCP\IUserSession;
23+
use OCP\L10N\IFactory;
1624

1725
class NavigationManagerTest extends TestCase {
1826
/** @var \OC\NavigationManager */
@@ -157,4 +165,62 @@ public function testAddClosureClearGetAll() {
157165
$this->assertEmpty($this->navigationManager->getAll(), 'Expected no navigation entry exists after clear()');
158166
$this->assertEquals(0, $testAddClosureNumberOfCalls, 'Expected that the closure is not called by getAll()');
159167
}
168+
169+
/**
170+
* @dataProvider providesNavigationConfig
171+
*/
172+
public function testWithAppManager($expected, $config, $isAdmin = false) {
173+
174+
$appManager = $this->createMock(AppManager::class);
175+
$urlGenerator = $this->createMock(IURLGenerator::class);
176+
$l10nFac = $this->createMock(IFactory::class);
177+
$userSession = $this->createMock(IUserSession::class);
178+
$groupManager = $this->createMock(IGroupManager::class);
179+
$l = $this->createMock(IL10N::class);
180+
$l->expects($this->any())->method('t')->willReturnCallback(function($text, $parameters = []) {
181+
return vsprintf($text, $parameters);
182+
});
183+
184+
$appManager->expects($this->once())->method('getInstalledApps')->willReturn(['test']);
185+
$appManager->expects($this->once())->method('getAppInfo')->with('test')->willReturn($config);
186+
$l10nFac->expects($this->exactly(count($expected)))->method('get')->with('test')->willReturn($l);
187+
$urlGenerator->expects($this->any())->method('imagePath')->willReturnCallback(function($appName, $file) {
188+
return "/apps/$appName/img/$file";
189+
});
190+
$urlGenerator->expects($this->exactly(count($expected)))->method('linkToRoute')->willReturnCallback(function($route) {
191+
return "/apps/test/";
192+
});
193+
$user = $this->createMock(IUser::class);
194+
$user->expects($this->any())->method('getUID')->willReturn('user001');
195+
$userSession->expects($this->any())->method('getUser')->willReturn($user);
196+
$groupManager->expects($this->any())->method('isAdmin')->willReturn($isAdmin);
197+
198+
$navigationManager = new NavigationManager($appManager, $urlGenerator, $l10nFac, $userSession, $groupManager);
199+
200+
$entries = $navigationManager->getAll();
201+
$this->assertEquals($expected, $entries);
202+
}
203+
204+
public function providesNavigationConfig() {
205+
return [
206+
'minimalistic' => [[[
207+
'id' => 'test',
208+
'order' => 100,
209+
'href' => '/apps/test/',
210+
'icon' => '/apps/test/img/app.svg',
211+
'name' => 'Test',
212+
'active' => false
213+
]], ['navigation' => ['route' => 'test.page.index', 'name' => 'Test']]],
214+
'no admin' => [[[
215+
'id' => 'test',
216+
'order' => 100,
217+
'href' => '/apps/test/',
218+
'icon' => '/apps/test/img/app.svg',
219+
'name' => 'Test',
220+
'active' => false
221+
]], ['navigation' => ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']], true],
222+
'no name' => [[], ['navigation' => ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index']], true],
223+
'admin' => [[], ['navigation' => ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']]]
224+
];
225+
}
160226
}

0 commit comments

Comments
 (0)