From: Richard Steinmetz Date: Thu, 17 Aug 2023 13:09:30 +0000 (+0200) Subject: feat(dashboard): implement widget item api v2 X-Git-Tag: v27.1.0beta3~10^2~2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=3fd18ce450c62719d12810732d0ef6ac083f8736;p=nextcloud-server.git feat(dashboard): implement widget item api v2 This API enables the dashboard to render all widgets from the API data alone without having apps to provide their own bundles. This saves a lot of traffic and execution time as a lot less javascript has to be parsed on the frontend. Signed-off-by: Richard Steinmetz --- diff --git a/apps/dashboard/appinfo/routes.php b/apps/dashboard/appinfo/routes.php index c6891837384..e872c47084b 100644 --- a/apps/dashboard/appinfo/routes.php +++ b/apps/dashboard/appinfo/routes.php @@ -7,6 +7,7 @@ declare(strict_types=1); * * @author Julien Veyssier * @author Julius Härtl + * @author Richard Steinmetz * * @license GNU AGPL version 3 or any later version * @@ -33,5 +34,6 @@ return [ 'ocs' => [ ['name' => 'dashboardApi#getWidgets', 'url' => '/api/v1/widgets', 'verb' => 'GET'], ['name' => 'dashboardApi#getWidgetItems', 'url' => '/api/v1/widget-items', 'verb' => 'GET'], + ['name' => 'dashboardApi#getWidgetItemsV2', 'url' => '/api/v2/widget-items', 'verb' => 'GET'], ] ]; diff --git a/apps/dashboard/lib/Controller/DashboardApiController.php b/apps/dashboard/lib/Controller/DashboardApiController.php index 1062cf1bdba..0b2b05e0df6 100644 --- a/apps/dashboard/lib/Controller/DashboardApiController.php +++ b/apps/dashboard/lib/Controller/DashboardApiController.php @@ -6,6 +6,7 @@ declare(strict_types=1); * @copyright Copyright (c) 2021 Julien Veyssier * * @author Julien Veyssier + * @author Richard Steinmetz * * @license GNU AGPL version 3 or any later version * @@ -32,6 +33,7 @@ use OCP\Dashboard\IButtonWidget; use OCP\Dashboard\IIconWidget; use OCP\Dashboard\IOptionWidget; use OCP\Dashboard\IManager; +use OCP\Dashboard\IReloadableWidget; use OCP\Dashboard\IWidget; use OCP\Dashboard\Model\WidgetButton; use OCP\Dashboard\Model\WidgetOptions; @@ -39,7 +41,9 @@ use OCP\IConfig; use OCP\IRequest; use OCP\Dashboard\IAPIWidget; +use OCP\Dashboard\IAPIWidgetV2; use OCP\Dashboard\Model\WidgetItem; +use OCP\Dashboard\Model\WidgetItems; class DashboardApiController extends OCSController { @@ -68,6 +72,24 @@ class DashboardApiController extends OCSController { * Example request with Curl: * curl -u user:passwd http://my.nc/ocs/v2.php/apps/dashboard/api/v1/widget-items -H Content-Type:application/json -X GET -d '{"sinceIds":{"github_notifications":"2021-03-22T15:01:10Z"}}' * + * @param string[] $widgetIds Limit widgets to given ids + * @return IWidget[] + */ + private function getShownWidgets(array $widgetIds): array { + if (empty($widgetIds)) { + $systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar'); + $widgetIds = explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)); + } + + return array_filter( + $this->dashboardManager->getWidgets(), + static function (IWidget $widget) use ($widgetIds) { + return in_array($widget->getId(), $widgetIds); + }, + ); + } + + /** * @param array $sinceIds Array indexed by widget Ids, contains date/id from which we want the new items * @param int $limit Limit number of result items per widget * @param string[] $widgets Limit results to specific widgets @@ -76,18 +98,11 @@ class DashboardApiController extends OCSController { * @NoCSRFRequired */ public function getWidgetItems(array $sinceIds = [], int $limit = 7, array $widgets = []): DataResponse { - $showWidgets = $widgets; $items = []; - - if (empty($showWidgets)) { - $systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar'); - $showWidgets = explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)); - } - - $widgets = $this->dashboardManager->getWidgets(); + $widgets = $this->getShownWidgets($widgets); foreach ($widgets as $widget) { - if ($widget instanceof IAPIWidget && in_array($widget->getId(), $showWidgets)) { - $items[$widget->getId()] = array_map(function (WidgetItem $item) { + if ($widget instanceof IAPIWidget) { + $items[$widget->getId()] = array_map(static function (WidgetItem $item) { return $item->jsonSerialize(); }, $widget->getItems($this->userId, $sinceIds[$widget->getId()] ?? null, $limit)); } @@ -102,6 +117,33 @@ class DashboardApiController extends OCSController { * * @NoAdminRequired * @NoCSRFRequired + * + * Get the items for the widgets + * + * @param array $sinceIds Array indexed by widget Ids, contains date/id from which we want the new items + * @param int $limit Limit number of result items per widget + * @param string[] $widgets Limit results to specific widgets + * @return DataResponse, array{}> + */ + public function getWidgetItemsV2(array $sinceIds = [], int $limit = 7, array $widgets = []): DataResponse { + $items = []; + $widgets = $this->getShownWidgets($widgets); + foreach ($widgets as $widget) { + if ($widget instanceof IAPIWidgetV2) { + $items[$widget->getId()] = $widget + ->getItemsV2($this->userId, $sinceIds[$widget->getId()] ?? null, $limit) + ->jsonSerialize(); + } + } + + return new DataResponse($items); + } + + /** + * Get the widgets + * + * @NoAdminRequired + * @NoCSRFRequired */ public function getWidgets(): DataResponse { $widgets = $this->dashboardManager->getWidgets(); @@ -116,6 +158,8 @@ class DashboardApiController extends OCSController { 'icon_url' => ($widget instanceof IIconWidget) ? $widget->getIconUrl() : '', 'widget_url' => $widget->getUrl(), 'item_icons_round' => $options->withRoundItemIcons(), + 'item_api_versions' => [], + 'reload_interval' => 0, ]; if ($widget instanceof IButtonWidget) { $data += [ @@ -128,6 +172,15 @@ class DashboardApiController extends OCSController { }, $widget->getWidgetButtons($this->userId)), ]; } + if ($widget instanceof IReloadableWidget) { + $data['reload_interval'] = $widget->getReloadInterval(); + } + if ($widget instanceof IAPIWidget) { + $data['item_api_versions'][] = 1; + } + if ($widget instanceof IAPIWidgetV2) { + $data['item_api_versions'][] = 2; + } return $data; }, $widgets); diff --git a/apps/dashboard/openapi.json b/apps/dashboard/openapi.json deleted file mode 100644 index cf706f1f55d..00000000000 --- a/apps/dashboard/openapi.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "openapi": "3.0.3", - "info": { - "title": "dashboard", - "version": "0.0.1", - "description": "Dashboard app", - "license": { - "name": "agpl" - } - }, - "components": { - "securitySchemes": { - "basic_auth": { - "type": "http", - "scheme": "basic" - }, - "bearer_auth": { - "type": "http", - "scheme": "bearer" - } - }, - "schemas": { - "OCSMeta": { - "type": "object", - "required": [ - "status", - "statuscode" - ], - "properties": { - "status": { - "type": "string" - }, - "statuscode": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "totalitems": { - "type": "string" - }, - "itemsperpage": { - "type": "string" - } - } - }, - "Widget": { - "type": "object", - "required": [ - "id", - "title", - "order", - "icon_class", - "icon_url", - "widget_url", - "item_icons_round" - ], - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "order": { - "type": "integer", - "format": "int64" - }, - "icon_class": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "widget_url": { - "type": "string", - "nullable": true - }, - "item_icons_round": { - "type": "boolean" - }, - "buttons": { - "type": "array", - "items": { - "type": "object", - "required": [ - "type", - "text", - "link" - ], - "properties": { - "type": { - "type": "string" - }, - "text": { - "type": "string" - }, - "link": { - "type": "string" - } - } - } - } - } - }, - "WidgetItem": { - "type": "object", - "required": [ - "subtitle", - "title", - "link", - "iconUrl", - "sinceId" - ], - "properties": { - "subtitle": { - "type": "string" - }, - "title": { - "type": "string" - }, - "link": { - "type": "string" - }, - "iconUrl": { - "type": "string" - }, - "sinceId": { - "type": "string" - } - } - } - } - }, - "paths": { - "/ocs/v2.php/apps/dashboard/api/v1/widgets": { - "get": { - "operationId": "dashboard_api-get-widgets", - "summary": "Get the widgets", - "tags": [ - "dashboard_api" - ], - "security": [ - { - "bearer_auth": [] - }, - { - "basic_auth": [] - } - ], - "parameters": [ - { - "name": "OCS-APIRequest", - "in": "header", - "required": true, - "schema": { - "type": "string", - "default": "true" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Widget" - } - } - } - } - } - } - } - } - } - } - } - }, - "/ocs/v2.php/apps/dashboard/api/v1/widget-items": { - "get": { - "operationId": "dashboard_api-get-widget-items", - "summary": "Get the items for the widgets", - "tags": [ - "dashboard_api" - ], - "security": [ - { - "bearer_auth": [] - }, - { - "basic_auth": [] - } - ], - "parameters": [ - { - "name": "sinceIds", - "in": "query", - "description": "Array indexed by widget Ids, contains date/id from which we want the new items", - "schema": { - "type": "string" - } - }, - { - "name": "limit", - "in": "query", - "description": "Limit number of result items per widget", - "schema": { - "type": "integer", - "format": "int64", - "default": 7 - } - }, - { - "name": "widgets", - "in": "query", - "description": "Limit results to specific widgets", - "schema": { - "type": "string" - } - }, - { - "name": "OCS-APIRequest", - "in": "header", - "required": true, - "schema": { - "type": "string", - "default": "true" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "ocs" - ], - "properties": { - "ocs": { - "type": "object", - "required": [ - "meta", - "data" - ], - "properties": { - "meta": { - "$ref": "#/components/schemas/OCSMeta" - }, - "data": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WidgetItem" - } - } - } - } - } - } - } - } - } - } - } - } - } - }, - "tags": [] -} \ No newline at end of file diff --git a/apps/dashboard/src/DashboardApp.vue b/apps/dashboard/src/DashboardApp.vue index f0c1dfbd734..ffdf368cb2c 100644 --- a/apps/dashboard/src/DashboardApp.vue +++ b/apps/dashboard/src/DashboardApp.vue @@ -14,21 +14,44 @@ v-bind="{swapThreshold: 0.30, delay: 500, delayOnTouchOnly: true, touchStartThreshold: 3}" handle=".panel--header" @end="saveLayout"> -
-
-

-

- {{ t('dashboard', '"{title} icon"', { title: panels[panelId].title }) }} +