Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f53814b2f9 | |||
| 1987e4d39b | |||
| 908bafc5d5 | |||
| 815fe7fb8b | |||
| ed1d8d162c |
@@ -13,6 +13,9 @@
|
||||
The Nextcloud Dashboard is your starting point of the day, giving you an overview of your upcoming appointments, urgent emails, chat messages, incoming tickets, latest tweets and much more! People can add the widgets they like and change the background to their liking.]]>
|
||||
</description>
|
||||
<version>7.10.0</version>
|
||||
<api-version>1</api-version>
|
||||
<api-version>2</api-version>
|
||||
<api-version>3</api-version>
|
||||
<licence>agpl</licence>
|
||||
<author>Julius Härtl</author>
|
||||
<namespace>Dashboard</namespace>
|
||||
|
||||
@@ -7,6 +7,8 @@ $baseDir = $vendorDir;
|
||||
|
||||
return array(
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'OCA\\Dashboard\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\Dashboard\\Capabilities' => $baseDir . '/../lib/Capabilities.php',
|
||||
'OCA\\Dashboard\\Controller\\DashboardApiController' => $baseDir . '/../lib/Controller/DashboardApiController.php',
|
||||
'OCA\\Dashboard\\Controller\\DashboardController' => $baseDir . '/../lib/Controller/DashboardController.php',
|
||||
'OCA\\Dashboard\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
|
||||
|
||||
@@ -22,6 +22,8 @@ class ComposerStaticInitDashboard
|
||||
|
||||
public static $classMap = array (
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'OCA\\Dashboard\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
|
||||
'OCA\\Dashboard\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php',
|
||||
'OCA\\Dashboard\\Controller\\DashboardApiController' => __DIR__ . '/..' . '/../lib/Controller/DashboardApiController.php',
|
||||
'OCA\\Dashboard\\Controller\\DashboardController' => __DIR__ . '/..' . '/../lib/Controller/DashboardController.php',
|
||||
'OCA\\Dashboard\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Dashboard\AppInfo;
|
||||
|
||||
use OCA\Dashboard\Capabilities;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||
|
||||
class Application extends App implements IBootstrap {
|
||||
public const APP_ID = 'dashboard';
|
||||
|
||||
public function __construct(array $urlParams = []) {
|
||||
parent::__construct(self::APP_ID, $urlParams);
|
||||
}
|
||||
|
||||
public function register(IRegistrationContext $context): void {
|
||||
$context->registerCapability(Capabilities::class);
|
||||
}
|
||||
|
||||
public function boot(IBootContext $context): void {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Dashboard;
|
||||
|
||||
use OCP\Capabilities\ICapability;
|
||||
use OCP\Capabilities\IFeature;
|
||||
|
||||
class Capabilities implements ICapability, IFeature {
|
||||
/**
|
||||
* @return array{dashboard: array{enabled: bool}}
|
||||
*/
|
||||
public function getCapabilities(): array {
|
||||
return [
|
||||
'dashboard' => [
|
||||
'enabled' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getFeatures(): array {
|
||||
return [
|
||||
'dashboard' => [
|
||||
'widgets-v1',
|
||||
'widget-items-v1',
|
||||
'widget-items-v2',
|
||||
'layout-v3',
|
||||
'statuses-v3',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,25 @@
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"Capabilities": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dashboard"
|
||||
],
|
||||
"properties": {
|
||||
"dashboard": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enabled"
|
||||
],
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"OCSMeta": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
*/
|
||||
namespace OCA\User_LDAP\Controller;
|
||||
|
||||
use OC\CapabilitiesManager;
|
||||
use OC\Core\Controller\OCSController;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OCA\User_LDAP\Configuration;
|
||||
use OCA\User_LDAP\ConnectionFactory;
|
||||
use OCA\User_LDAP\Helper;
|
||||
@@ -16,31 +13,19 @@ use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCS\OCSBadRequestException;
|
||||
use OCP\AppFramework\OCS\OCSException;
|
||||
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class ConfigAPIController extends OCSController {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
CapabilitiesManager $capabilitiesManager,
|
||||
IUserSession $userSession,
|
||||
IUserManager $userManager,
|
||||
Manager $keyManager,
|
||||
private Helper $ldapHelper,
|
||||
private LoggerInterface $logger,
|
||||
private ConnectionFactory $connectionFactory
|
||||
) {
|
||||
parent::__construct(
|
||||
$appName,
|
||||
$request,
|
||||
$capabilitiesManager,
|
||||
$userSession,
|
||||
$userManager,
|
||||
$keyManager
|
||||
);
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\App\AppManager;
|
||||
use OC\CapabilitiesManager;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OC_App;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\ApiRoute;
|
||||
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
||||
@@ -23,6 +25,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
|
||||
private IUserSession $userSession,
|
||||
private IUserManager $userManager,
|
||||
private Manager $keyManager,
|
||||
private AppManager $appManager,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
@@ -49,7 +52,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
|
||||
*
|
||||
* Get the capabilities
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{version: array{major: int, minor: int, micro: int, string: string, edition: '', extendedSupport: bool}, capabilities: array<string, mixed>}, array{}>
|
||||
* @return DataResponse<Http::STATUS_OK, array{version: array{major: int, minor: int, micro: int, string: string, edition: '', extendedSupport: bool}, capabilities: array<string, mixed>, features: array<string, list<string>>, apps: array<string, array{version: string, api_versions: list<string>}>}, array{}>
|
||||
*
|
||||
* 200: Capabilities returned
|
||||
*/
|
||||
@@ -72,6 +75,15 @@ class OCSController extends \OCP\AppFramework\OCSController {
|
||||
$result['capabilities'] = $this->capabilitiesManager->getCapabilities(true);
|
||||
}
|
||||
|
||||
$result['features'] = $this->capabilitiesManager->getFeatures();
|
||||
|
||||
$result['apps'] = [];
|
||||
foreach (OC_App::getEnabledApps() as $app) {
|
||||
$info = $this->appManager->getAppInfo($app);
|
||||
$result['apps'][$app]['version'] = (string)$info['version'];
|
||||
$result['apps'][$app]['api_versions'] = array_values(array_map(static fn ($apiVersion) => (string)$apiVersion, (array)$info['api-version']));
|
||||
}
|
||||
|
||||
$response = new DataResponse($result);
|
||||
$response->setETag(md5(json_encode($result)));
|
||||
return $response;
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
*/
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\CapabilitiesManager;
|
||||
use OC\Security\IdentityProof\Manager;
|
||||
use OC\Updater\ChangesCheck;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\ApiRoute;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\Defaults;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use OCP\L10N\IFactory;
|
||||
|
||||
@@ -23,16 +21,13 @@ class WhatsNewController extends OCSController {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
CapabilitiesManager $capabilitiesManager,
|
||||
private IUserSession $userSession,
|
||||
IUserManager $userManager,
|
||||
Manager $keyManager,
|
||||
private IConfig $config,
|
||||
private ChangesCheck $whatsNewService,
|
||||
private IFactory $langFactory,
|
||||
private Defaults $defaults,
|
||||
) {
|
||||
parent::__construct($appName, $request, $capabilitiesManager, $userSession, $userManager, $keyManager);
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+33
-1
@@ -2724,7 +2724,9 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version",
|
||||
"capabilities"
|
||||
"capabilities",
|
||||
"features",
|
||||
"apps"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
@@ -2766,6 +2768,36 @@
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version",
|
||||
"api_versions"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"api_versions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+33
-1
@@ -2724,7 +2724,9 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version",
|
||||
"capabilities"
|
||||
"capabilities",
|
||||
"features",
|
||||
"apps"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
@@ -2766,6 +2768,36 @@
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version",
|
||||
"api_versions"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"api_versions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ return array(
|
||||
'OCP\\Calendar\\Room\\IRoom' => $baseDir . '/lib/public/Calendar/Room/IRoom.php',
|
||||
'OCP\\Calendar\\Room\\IRoomMetadata' => $baseDir . '/lib/public/Calendar/Room/IRoomMetadata.php',
|
||||
'OCP\\Capabilities\\ICapability' => $baseDir . '/lib/public/Capabilities/ICapability.php',
|
||||
'OCP\\Capabilities\\IFeature' => $baseDir . '/lib/public/Capabilities/IFeature.php',
|
||||
'OCP\\Capabilities\\IInitialStateExcludedCapability' => $baseDir . '/lib/public/Capabilities/IInitialStateExcludedCapability.php',
|
||||
'OCP\\Capabilities\\IPublicCapability' => $baseDir . '/lib/public/Capabilities/IPublicCapability.php',
|
||||
'OCP\\Collaboration\\AutoComplete\\AutoCompleteEvent' => $baseDir . '/lib/public/Collaboration/AutoComplete/AutoCompleteEvent.php',
|
||||
|
||||
@@ -211,6 +211,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
||||
'OCP\\Calendar\\Room\\IRoom' => __DIR__ . '/../../..' . '/lib/public/Calendar/Room/IRoom.php',
|
||||
'OCP\\Calendar\\Room\\IRoomMetadata' => __DIR__ . '/../../..' . '/lib/public/Calendar/Room/IRoomMetadata.php',
|
||||
'OCP\\Capabilities\\ICapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/ICapability.php',
|
||||
'OCP\\Capabilities\\IFeature' => __DIR__ . '/../../..' . '/lib/public/Capabilities/IFeature.php',
|
||||
'OCP\\Capabilities\\IInitialStateExcludedCapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/IInitialStateExcludedCapability.php',
|
||||
'OCP\\Capabilities\\IPublicCapability' => __DIR__ . '/../../..' . '/lib/public/Capabilities/IPublicCapability.php',
|
||||
'OCP\\Collaboration\\AutoComplete\\AutoCompleteEvent' => __DIR__ . '/../../..' . '/lib/public/Collaboration/AutoComplete/AutoCompleteEvent.php',
|
||||
|
||||
@@ -119,6 +119,9 @@ class InfoParser {
|
||||
if (!array_key_exists('backend', $array['dependencies'])) {
|
||||
$array['dependencies']['backend'] = [];
|
||||
}
|
||||
if (!array_key_exists('api-version', $array)) {
|
||||
$array['api-version'] = [];
|
||||
}
|
||||
|
||||
if (array_key_exists('types', $array)) {
|
||||
if (is_array($array['types'])) {
|
||||
|
||||
@@ -6,10 +6,12 @@ declare(strict_types=1);
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
namespace OC;
|
||||
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Capabilities\ICapability;
|
||||
use OCP\Capabilities\IFeature;
|
||||
use OCP\Capabilities\IInitialStateExcludedCapability;
|
||||
use OCP\Capabilities\IPublicCapability;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@@ -35,10 +37,10 @@ class CapabilitiesManager {
|
||||
* Get an array of al the capabilities that are registered at this manager
|
||||
*
|
||||
* @param bool $public get public capabilities only
|
||||
* @throws \InvalidArgumentException
|
||||
* @return array<string, mixed>
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getCapabilities(bool $public = false, bool $initialState = false) : array {
|
||||
public function getCapabilities(bool $public = false, bool $initialState = false): array {
|
||||
$capabilities = [];
|
||||
foreach ($this->capabilities as $capability) {
|
||||
try {
|
||||
@@ -87,6 +89,33 @@ class CapabilitiesManager {
|
||||
return $capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, list<string>>
|
||||
*/
|
||||
public function getFeatures(): array {
|
||||
$features = [];
|
||||
foreach ($this->capabilities as $capability) {
|
||||
try {
|
||||
$c = $capability();
|
||||
} catch (QueryException $e) {
|
||||
$this->logger->error('CapabilitiesManager', [
|
||||
'exception' => $e,
|
||||
]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($c instanceof ICapability) {
|
||||
if ($c instanceof IFeature) {
|
||||
$features = array_merge_recursive($features, $c->getFeatures());
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException('The given Capability (' . get_class($c) . ') does not implement the ICapability interface');
|
||||
}
|
||||
}
|
||||
|
||||
return $features;
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to improve lazy loading a closure can be registered which will be called in case
|
||||
* capabilities are actually requested
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace OCP\Capabilities;
|
||||
|
||||
/**
|
||||
* Interface for apps to expose their available features.
|
||||
*
|
||||
* @since 30.0.0
|
||||
*/
|
||||
interface IFeature {
|
||||
/**
|
||||
* Returns the available features.
|
||||
*
|
||||
* ```php
|
||||
* return [
|
||||
* 'myapp' => [
|
||||
* 'feature1',
|
||||
* 'feature2',
|
||||
* ],
|
||||
* 'otherapp' => [
|
||||
* 'feature3',
|
||||
* ],
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* @return array<string, list<string>>
|
||||
* @since 30.0.0
|
||||
*/
|
||||
public function getFeatures(): array;
|
||||
}
|
||||
@@ -18,6 +18,8 @@
|
||||
maxOccurs="unbounded"/>
|
||||
<xs:element name="version" type="semver"
|
||||
minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element name="api-version" type="semver"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="licence" type="licence" minOccurs="1"
|
||||
maxOccurs="unbounded"/>
|
||||
<xs:element name="author" type="author" minOccurs="1"
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
maxOccurs="unbounded"/>
|
||||
<xs:element name="version" type="semver"
|
||||
minOccurs="1" maxOccurs="1"/>
|
||||
<xs:element name="api-version" type="semver"
|
||||
minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xs:element name="licence" type="licence" minOccurs="1"
|
||||
maxOccurs="unbounded"/>
|
||||
<xs:element name="author" type="author" minOccurs="1"
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
},
|
||||
"background-jobs": [],
|
||||
"two-factor-providers": [],
|
||||
"api-version": [1, 2],
|
||||
"commands": [],
|
||||
"activity": {
|
||||
"filters": [],
|
||||
|
||||
@@ -82,5 +82,6 @@
|
||||
"live-migration": [],
|
||||
"uninstall": []
|
||||
},
|
||||
"two-factor-providers": []
|
||||
"two-factor-providers": [],
|
||||
"api-version": []
|
||||
}
|
||||
|
||||
@@ -88,5 +88,6 @@
|
||||
"live-migration": [],
|
||||
"uninstall": []
|
||||
},
|
||||
"two-factor-providers": []
|
||||
"two-factor-providers": [],
|
||||
"api-version": []
|
||||
}
|
||||
|
||||
@@ -36,4 +36,6 @@
|
||||
<owncloud min-version="7.0.1" max-version="8" />
|
||||
<backend>caldav</backend>
|
||||
</dependencies>
|
||||
<api-version>1</api-version>
|
||||
<api-version>2</api-version>
|
||||
</info>
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
|
||||
namespace Test;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use OC\CapabilitiesManager;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\Capabilities\ICapability;
|
||||
use OCP\Capabilities\IFeature;
|
||||
use OCP\Capabilities\IPublicCapability;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
@@ -68,7 +70,7 @@ class CapabilitiesManagerTest extends TestCase {
|
||||
* Test that we need something that implents ICapability
|
||||
*/
|
||||
public function testNoICapability() {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('The given Capability (Test\\NoCapability) does not implement the ICapability interface');
|
||||
|
||||
$this->manager->registerCapability(function () {
|
||||
@@ -140,6 +142,24 @@ class CapabilitiesManagerTest extends TestCase {
|
||||
|
||||
$this->assertEquals([], $res);
|
||||
}
|
||||
|
||||
|
||||
public function testFeatures() {
|
||||
$this->manager->registerCapability(fn () => new FeatureFoo());
|
||||
|
||||
$res = $this->manager->getFeatures();
|
||||
$this->assertEquals(['foo' => ['123', '456']], $res);
|
||||
|
||||
$this->manager->registerCapability(fn () => new FeatureBar());
|
||||
|
||||
$res = $this->manager->getFeatures();
|
||||
$this->assertEquals(['foo' => ['123', '456'], 'bar' => ['789']], $res);
|
||||
|
||||
$this->manager->registerCapability(fn () => new FeatureFooBar());
|
||||
|
||||
$res = $this->manager->getFeatures();
|
||||
$this->assertEquals(['foo' => ['123', '456', '789'], 'bar' => ['789', '123', '456']], $res);
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleCapability implements ICapability {
|
||||
@@ -193,3 +213,50 @@ class DeepCapability implements ICapability {
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureFoo implements ICapability, IFeature {
|
||||
public function getCapabilities() {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFeatures(): array {
|
||||
return [
|
||||
'foo' => [
|
||||
'123',
|
||||
'456',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureBar implements ICapability, IFeature {
|
||||
public function getCapabilities() {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFeatures(): array {
|
||||
return [
|
||||
'bar' => [
|
||||
'789',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureFooBar implements ICapability, IFeature {
|
||||
public function getCapabilities() {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFeatures(): array {
|
||||
return [
|
||||
'foo' => [
|
||||
'789',
|
||||
],
|
||||
'bar' => [
|
||||
'123',
|
||||
'456',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user