Kate 2 weeks ago committed by GitHub
commit a9773067e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,39 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
*
* @author Julien Veyssier <eneiluj@posteo.net>
* @author Julius Härtl <jus@bitgrid.net>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @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/>.
*
*/
return [
'routes' => [
['name' => 'dashboard#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'dashboard#updateLayout', 'url' => '/layout', 'verb' => 'POST'],
['name' => 'dashboard#updateStatuses', 'url' => '/statuses', 'verb' => 'POST'],
],
'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'],
]
];

@ -9,6 +9,6 @@ return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'OCA\\Dashboard\\Controller\\DashboardApiController' => $baseDir . '/../lib/Controller/DashboardApiController.php',
'OCA\\Dashboard\\Controller\\DashboardController' => $baseDir . '/../lib/Controller/DashboardController.php',
'OCA\\Dashboard\\Controller\\LayoutApiController' => $baseDir . '/../lib/Controller/LayoutApiController.php',
'OCA\\Dashboard\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
'OCA\\Dashboard\\Service\\DashboardService' => $baseDir . '/../lib/Service/DashboardService.php',
);

@ -24,8 +24,8 @@ class ComposerStaticInitDashboard
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'OCA\\Dashboard\\Controller\\DashboardApiController' => __DIR__ . '/..' . '/../lib/Controller/DashboardApiController.php',
'OCA\\Dashboard\\Controller\\DashboardController' => __DIR__ . '/..' . '/../lib/Controller/DashboardController.php',
'OCA\\Dashboard\\Controller\\LayoutApiController' => __DIR__ . '/..' . '/../lib/Controller/LayoutApiController.php',
'OCA\\Dashboard\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
'OCA\\Dashboard\\Service\\DashboardService' => __DIR__ . '/..' . '/../lib/Service/DashboardService.php',
);
public static function getInitializer(ClassLoader $loader)

@ -29,7 +29,9 @@ declare(strict_types=1);
namespace OCA\Dashboard\Controller;
use OCA\Dashboard\ResponseDefinitions;
use OCA\Dashboard\Service\DashboardService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\Dashboard\IAPIWidget;
@ -60,6 +62,7 @@ class DashboardApiController extends OCSController {
private IManager $dashboardManager,
private IConfig $config,
private ?string $userId,
private DashboardService $service,
) {
parent::__construct($appName, $request);
}
@ -96,6 +99,7 @@ class DashboardApiController extends OCSController {
*
* 200: Widget items returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v1/widget-items')]
public function getWidgetItems(array $sinceIds = [], int $limit = 7, array $widgets = []): DataResponse {
$items = [];
$widgets = $this->getShownWidgets($widgets);
@ -124,6 +128,7 @@ class DashboardApiController extends OCSController {
*
* 200: Widget items returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v2/widget-items')]
public function getWidgetItemsV2(array $sinceIds = [], int $limit = 7, array $widgets = []): DataResponse {
$items = [];
$widgets = $this->getShownWidgets($widgets);
@ -148,6 +153,7 @@ class DashboardApiController extends OCSController {
*
* 200: Widgets returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v1/widgets')]
public function getWidgets(): DataResponse {
$widgets = $this->dashboardManager->getWidgets();
@ -189,4 +195,60 @@ class DashboardApiController extends OCSController {
return new DataResponse($items);
}
/**
* Get the layout
*
* @NoAdminRequired
* @return DataResponse<Http::STATUS_OK, array{layout: list<string>}, array{}>
*
* 200: Layout returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v3/layout')]
public function getLayout(): DataResponse {
return new DataResponse(['layout' => $this->service->getLayout()]);
}
/**
* Update the layout
*
* @NoAdminRequired
* @param list<string> $layout The new layout
* @return DataResponse<Http::STATUS_OK, array{layout: list<string>}, array{}>
*
* 200: Statuses updated successfully
*/
#[ApiRoute(verb: 'POST', url: '/api/v3/layout')]
public function updateLayout(array $layout): DataResponse {
$this->config->setUserValue($this->userId, 'dashboard', 'layout', implode(',', $layout));
return new DataResponse(['layout' => $layout]);
}
/**
* Get the statuses
*
* @NoAdminRequired
* @return DataResponse<Http::STATUS_OK, array{statuses: list<string>}, array{}>
*
* 200: Statuses returned
*/
#[ApiRoute(verb: 'GET', url: '/api/v3/statuses')]
public function getStatuses(): DataResponse {
return new DataResponse(['statuses' => $this->service->getStatuses()]);
}
/**
* Update the statuses
*
* @NoAdminRequired
* @param list<string> $statuses The new statuses
* @return DataResponse<Http::STATUS_OK, array{statuses: list<string>}, array{}>
*
* 200: Statuses updated successfully
*/
#[ApiRoute(verb: 'POST', url: '/api/v3/statuses')]
public function updateStatuses(array $statuses): DataResponse {
$this->config->setUserValue($this->userId, 'dashboard', 'statuses', implode(',', $statuses));
return new DataResponse(['statuses' => $statuses]);
}
}

@ -30,10 +30,11 @@ declare(strict_types=1);
*/
namespace OCA\Dashboard\Controller;
use OCA\Dashboard\Service\DashboardService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\Dashboard\IManager;
@ -54,7 +55,8 @@ class DashboardController extends Controller {
private IManager $dashboardManager,
private IConfig $config,
private IL10N $l10n,
private ?string $userId
private ?string $userId,
private DashboardService $service,
) {
parent::__construct($appName, $request);
}
@ -64,12 +66,11 @@ class DashboardController extends Controller {
* @NoAdminRequired
* @return TemplateResponse
*/
#[FrontpageRoute(verb: 'GET', url: '/')]
public function index(): TemplateResponse {
\OCP\Util::addStyle('dashboard', 'dashboard');
\OCP\Util::addScript('dashboard', 'main', 'theming');
$systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar');
$userLayout = explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault));
$widgets = array_map(function (IWidget $widget) {
return [
'id' => $widget->getId(),
@ -78,15 +79,10 @@ class DashboardController extends Controller {
'url' => $widget->getUrl()
];
}, $this->dashboardManager->getWidgets());
$configStatuses = $this->config->getUserValue($this->userId, 'dashboard', 'statuses', '');
$statuses = json_decode($configStatuses, true);
// We avoid getting an empty array as it will not produce an object in UI's JS
// It does not matter if some statuses are missing from the array, missing ones are considered enabled
$statuses = ($statuses && count($statuses) > 0) ? $statuses : ['weather' => true];
$this->initialState->provideInitialState('panels', $widgets);
$this->initialState->provideInitialState('statuses', $statuses);
$this->initialState->provideInitialState('layout', $userLayout);
$this->initialState->provideInitialState('statuses', $this->service->getStatuses());
$this->initialState->provideInitialState('layout', $this->service->getLayout());
$this->initialState->provideInitialState('appStoreEnabled', $this->config->getSystemValueBool('appstoreenabled', true));
$this->initialState->provideInitialState('firstRun', $this->config->getUserValue($this->userId, 'dashboard', 'firstRun', '1') === '1');
$this->config->setUserValue($this->userId, 'dashboard', 'firstRun', '0');
@ -104,24 +100,4 @@ class DashboardController extends Controller {
return $response;
}
/**
* @NoAdminRequired
* @param string $layout
* @return JSONResponse
*/
public function updateLayout(string $layout): JSONResponse {
$this->config->setUserValue($this->userId, 'dashboard', 'layout', $layout);
return new JSONResponse(['layout' => $layout]);
}
/**
* @NoAdminRequired
* @param string $statuses
* @return JSONResponse
*/
public function updateStatuses(string $statuses): JSONResponse {
$this->config->setUserValue($this->userId, 'dashboard', 'statuses', $statuses);
return new JSONResponse(['statuses' => $statuses]);
}
}

@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 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 OCA\Dashboard\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\OCSController;
use OCP\IConfig;
use OCP\IRequest;
class LayoutApiController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
private IConfig $config,
private ?string $userId,
) {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
*
* @param string $layout
* @return JSONResponse
*/
public function create(string $layout): JSONResponse {
$layout = htmlspecialchars($layout);
$this->config->setUserValue($this->userId, 'dashboard', 'layout', $layout);
return new JSONResponse(['layout' => $layout]);
}
}

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2024, Kate Döen <kate.doeen@nextcloud.com>
*
* @author Kate Döen <kate.doeen@nextcloud.com>
*
* @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 OCA\Dashboard\Service;
use JsonException;
use OCP\IConfig;
class DashboardService {
public function __construct(
private IConfig $config,
private String $userId,
) {
}
/**
* @return list<string>
*/
public function getLayout(): array {
$systemDefault = $this->config->getAppValue('dashboard', 'layout', 'recommendations,spreed,mail,calendar');
return array_values(array_filter(explode(',', $this->config->getUserValue($this->userId, 'dashboard', 'layout', $systemDefault)), fn (string $value) => $value !== ''));
}
/**
* @return list<string>
*/
public function getStatuses() {
$configStatuses = $this->config->getUserValue($this->userId, 'dashboard', 'statuses', '');
try {
// Parse the old format
/** @var array<string, bool> $statuses */
$statuses = json_decode($configStatuses, true, 512, JSON_THROW_ON_ERROR);
// We avoid getting an empty array as it will not produce an object in UI's JS
return array_keys(array_filter($statuses, static fn (bool $value) => $value));
} catch (JsonException $e) {
return array_values(array_filter(explode(',', $configStatuses), fn (string $value) => $value !== ''));
}
}
}

@ -172,10 +172,10 @@
}
},
"paths": {
"/ocs/v2.php/apps/dashboard/api/v1/widgets": {
"/ocs/v2.php/apps/dashboard/api/v1/widget-items": {
"get": {
"operationId": "dashboard_api-get-widgets",
"summary": "Get the widgets",
"operationId": "dashboard_api-get-widget-items",
"summary": "Get the items for the widgets",
"tags": [
"dashboard_api"
],
@ -188,6 +188,38 @@
}
],
"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,
"minimum": 1,
"maximum": 30
}
},
{
"name": "widgets[]",
"in": "query",
"description": "Limit results to specific widgets",
"schema": {
"type": "array",
"default": [],
"items": {
"type": "string"
}
}
},
{
"name": "OCS-APIRequest",
"in": "header",
@ -201,7 +233,7 @@
],
"responses": {
"200": {
"description": "Widgets returned",
"description": "Widget items returned",
"content": {
"application/json": {
"schema": {
@ -223,7 +255,10 @@
"data": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/Widget"
"type": "array",
"items": {
"$ref": "#/components/schemas/WidgetItem"
}
}
}
}
@ -236,9 +271,9 @@
}
}
},
"/ocs/v2.php/apps/dashboard/api/v1/widget-items": {
"/ocs/v2.php/apps/dashboard/api/v2/widget-items": {
"get": {
"operationId": "dashboard_api-get-widget-items",
"operationId": "dashboard_api-get-widget-items-v2",
"summary": "Get the items for the widgets",
"tags": [
"dashboard_api"
@ -263,7 +298,7 @@
{
"name": "limit",
"in": "query",
"description": "Limit number of result items per widget",
"description": "Limit number of result items per widget, not more than 30 are allowed",
"schema": {
"type": "integer",
"format": "int64",
@ -319,10 +354,7 @@
"data": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "#/components/schemas/WidgetItem"
}
"$ref": "#/components/schemas/WidgetItems"
}
}
}
@ -335,10 +367,10 @@
}
}
},
"/ocs/v2.php/apps/dashboard/api/v2/widget-items": {
"/ocs/v2.php/apps/dashboard/api/v1/widgets": {
"get": {
"operationId": "dashboard_api-get-widget-items-v2",
"summary": "Get the items for the widgets",
"operationId": "dashboard_api-get-widgets",
"summary": "Get the widgets",
"tags": [
"dashboard_api"
],
@ -352,32 +384,300 @@
],
"parameters": [
{
"name": "sinceIds",
"in": "query",
"description": "Array indexed by widget Ids, contains date/id from which we want the new items",
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "string"
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Widgets returned",
"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": {
"$ref": "#/components/schemas/Widget"
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/dashboard/api/v3/layout": {
"get": {
"operationId": "dashboard_api-get-layout",
"summary": "Get the layout",
"tags": [
"dashboard_api"
],
"security": [
{
"bearer_auth": []
},
{
"name": "limit",
"basic_auth": []
}
],
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Layout returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"layout"
],
"properties": {
"layout": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
},
"post": {
"operationId": "dashboard_api-update-layout",
"summary": "Update the layout",
"tags": [
"dashboard_api"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "layout[]",
"in": "query",
"description": "Limit number of result items per widget, not more than 30 are allowed",
"description": "The new layout",
"required": true,
"schema": {
"type": "integer",
"format": "int64",
"default": 7,
"minimum": 1,
"maximum": 30
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "widgets[]",
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Statuses updated successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"layout"
],
"properties": {
"layout": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/dashboard/api/v3/statuses": {
"get": {
"operationId": "dashboard_api-get-statuses",
"summary": "Get the statuses",
"tags": [
"dashboard_api"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Statuses returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"statuses"
],
"properties": {
"statuses": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
},
"post": {
"operationId": "dashboard_api-update-statuses",
"summary": "Update the statuses",
"tags": [
"dashboard_api"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "statuses[]",
"in": "query",
"description": "Limit results to specific widgets",
"description": "The new statuses",
"required": true,
"schema": {
"type": "array",
"default": [],
"items": {
"type": "string"
}
@ -396,7 +696,7 @@
],
"responses": {
"200": {
"description": "Widget items returned",
"description": "Statuses updated successfully",
"content": {
"application/json": {
"schema": {
@ -417,8 +717,16 @@
},
"data": {
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/WidgetItems"
"required": [
"statuses"
],
"properties": {
"statuses": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}

@ -229,7 +229,7 @@ export default {
return (panel) => this.layout.indexOf(panel.id) > -1
},
isStatusActive() {
return (status) => !(status in this.enabledStatuses) || this.enabledStatuses[status]
return (status) => this.enabledStatuses.findIndex((s) => s === status) !== -1
},
sortedAllStatuses() {
@ -349,13 +349,13 @@ export default {
}
},
saveLayout() {
axios.post(generateUrl('/apps/dashboard/layout'), {
layout: this.layout.join(','),
axios.post(generateOcsUrl('/apps/dashboard/api/v3/layout'), {
layout: this.layout,
})
},
saveStatuses() {
axios.post(generateUrl('/apps/dashboard/statuses'), {
statuses: JSON.stringify(this.enabledStatuses),
axios.post(generateOcsUrl('/apps/dashboard/api/v3/statuses'), {
statuses: this.enabledStatuses,
})
},
showModal() {
@ -395,15 +395,18 @@ export default {
}
},
enableStatus(app) {
this.enabledStatuses[app] = true
this.enabledStatuses.push(app)
this.registerStatus(app, this.allCallbacksStatus[app])
this.saveStatuses()
},
disableStatus(app) {
this.enabledStatuses[app] = false
const i = this.registeredStatus.findIndex((s) => s === app)
const i = this.enabledStatuses.findIndex((s) => s === app)
if (i !== -1) {
this.registeredStatus.splice(i, 1)
this.enabledStatuses.splice(i, 1)
}
const j = this.registeredStatus.findIndex((s) => s === app)
if (j !== -1) {
this.registeredStatus.splice(j, 1)
Vue.set(this.statuses, app, { mounted: false })
this.$nextTick(() => {
Vue.delete(this.callbacksStatus, app)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save