From 5caec0ef33cb993241678a6672834ed1e05249dd Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Mon, 22 Jul 2024 11:42:17 +0200 Subject: [PATCH] add fake form to download tables export Signed-off-by: grnd-alt --- lib/Controller/Api1Controller.php | 31 ++- lib/Controller/ApiTablesController.php | 2 - lib/Controller/TableScheme.php | 7 +- lib/Service/TableService.php | 2 +- openapi.json | 99 +++++++ .../partials/NavigationTableItem.vue | 26 +- src/types/openapi/openapi.ts | 243 ++++++++++++++++++ 7 files changed, 387 insertions(+), 23 deletions(-) diff --git a/lib/Controller/Api1Controller.php b/lib/Controller/Api1Controller.php index 642886411..7a2f33666 100644 --- a/lib/Controller/Api1Controller.php +++ b/lib/Controller/Api1Controller.php @@ -134,14 +134,35 @@ public function createTable(string $title, ?string $emoji, string $template = 'c /** * returns table scheme - * @param int $tableId - * @return \OCP\AppFramework\Http\DataResponse + * + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * + * @param int $tableId Table ID + * @return DataResponse|DataResponse + * + * 200: Table returned + * 403: No permissions + * 404: Not found */ public function showScheme(int $tableId): DataResponse { - return $this->handleError(function () use ($tableId) { + try { $scheme = $this->tableService->getScheme($tableId); - return new DataResponse($scheme->jsonSerialize(), http::STATUS_OK, ["Content-Disposition" => "attachment", "filename" => $scheme->getTitle() . ".json", "Content-Type" => "application/octet-stream"]); - }); + return new DataResponse($scheme->jsonSerialize(), http::STATUS_OK, ['Content-Disposition' => 'attachment; filename="'.$scheme->getTitle() . '.json"', 'Content-Type' => 'application/octet-stream']); + } catch (PermissionError $e) { + $this->logger->warning('A permission error occurred: ' . $e->getMessage()); + $message = ['message' => $e->getMessage()]; + return new DataResponse($message, Http::STATUS_FORBIDDEN); + } catch (NotFoundError $e) { + $this->logger->warning('A not found error occurred: ' . $e->getMessage()); + $message = ['message' => $e->getMessage()]; + return new DataResponse($message, Http::STATUS_NOT_FOUND); + } catch (InternalError|Exception $e) { + $this->logger->warning('An internal error or exception occurred: '.$e->getMessage()); + $message = ['message' => $e->getMessage()]; + return new DataResponse($message, Http::STATUS_INTERNAL_SERVER_ERROR); + } } /** diff --git a/lib/Controller/ApiTablesController.php b/lib/Controller/ApiTablesController.php index 541933ff4..d246636d5 100644 --- a/lib/Controller/ApiTablesController.php +++ b/lib/Controller/ApiTablesController.php @@ -3,8 +3,6 @@ namespace OCA\Tables\Controller; use Exception; -use OCA\Tables\Db\Column; -use OCA\Tables\Db\View; use OCA\Tables\Errors\InternalError; use OCA\Tables\Errors\NotFoundError; use OCA\Tables\Errors\PermissionError; diff --git a/lib/Controller/TableScheme.php b/lib/Controller/TableScheme.php index d5d093268..81e993b31 100644 --- a/lib/Controller/TableScheme.php +++ b/lib/Controller/TableScheme.php @@ -1,11 +1,10 @@ tablesVersion = $appManager->getAppVersion("tables"); + public function __construct(string $title, string $emoji, array $columns, array $view, string $description, string $tablesVersion){ + $this->tablesVersion = $tablesVersion; $this->title = $title; $this->emoji = $emoji; $this->columns = $columns; diff --git a/lib/Service/TableService.php b/lib/Service/TableService.php index 505a07e1f..97e73a7d9 100644 --- a/lib/Service/TableService.php +++ b/lib/Service/TableService.php @@ -551,7 +551,7 @@ public function search(string $term, int $limit = 100, int $offset = 0, ?string public function getScheme(int $id): TableScheme { $columns = $this->columnService->findAllByTable($id); $table = $this->find($id); - return new TableScheme($table->getTitle(), $table->getEmoji(), $columns, $table->getViews(), $table->getDescription(), $this->appManager); + return new TableScheme($table->getTitle(), $table->getEmoji(), $columns, $table->getViews(), $table->getDescription(), $this->appManager->getAppVersion("tables")); } // PRIVATE FUNCTIONS --------------------------------------------------------------- diff --git a/openapi.json b/openapi.json index 41d22ae9b..652d6d5cd 100644 --- a/openapi.json +++ b/openapi.json @@ -1143,6 +1143,105 @@ } } }, + "/index.php/apps/tables/api/1/tables/{tableId}/scheme": { + "get": { + "operationId": "api1-show-scheme", + "summary": "returns table scheme", + "tags": [ + "api1" + ], + "security": [ + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "tableId", + "in": "path", + "description": "Table ID", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "Table returned", + "headers": { + "Content-Disposition": { + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Table" + } + } + } + }, + "403": { + "description": "No permissions", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } + } + } + }, + "500": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + } + } + } + } + } + } + }, "/index.php/apps/tables/api/1/tables/{tableId}/views": { "get": { "operationId": "api1-index-views", diff --git a/src/modules/navigation/partials/NavigationTableItem.vue b/src/modules/navigation/partials/NavigationTableItem.vue index 116d01d57..5998f798b 100644 --- a/src/modules/navigation/partials/NavigationTableItem.vue +++ b/src/modules/navigation/partials/NavigationTableItem.vue @@ -150,14 +150,13 @@ import StarOutline from 'vue-material-design-icons/StarOutline.vue' import ArchiveArrowDown from 'vue-material-design-icons/ArchiveArrowDown.vue' import ArchiveArrowUpOutline from 'vue-material-design-icons/ArchiveArrowUpOutline.vue' import permissionsMixin from '../../../shared/components/ncTable/mixins/permissionsMixin.js' -import { getCurrentUser } from '@nextcloud/auth' +import { getCurrentUser, getRequestToken } from '@nextcloud/auth' import Connection from 'vue-material-design-icons/Connection.vue' import Import from 'vue-material-design-icons/Import.vue' import NavigationViewItem from './NavigationViewItem.vue' import PlaylistPlus from 'vue-material-design-icons/PlaylistPlus.vue' import IconRename from 'vue-material-design-icons/Rename.vue' -import axios from '@nextcloud/axios' -import { generateOcsUrl } from '@nextcloud/router' +import { generateUrl } from '@nextcloud/router' export default { @@ -246,14 +245,19 @@ export default { }, exportFile() { - axios.get(generateOcsUrl(`/apps/tables/api/2/tables/scheme/${this.table.id}`)).then(res => { - const blob = new Blob([JSON.stringify(res.data.ocs.data)], { type: 'application/json' }) - const link = document.createElement('a') - link.href = URL.createObjectURL(blob) - link.download = this.table.title - link.click() - URL.revokeObjectURL(link.href) - }) + const form = document.createElement('form') + form.method = 'GET' + form.action = generateUrl(`/apps/tables/api/1/tables/${this.table.id}/scheme`) + + const token = document.createElement('input') + token.type = 'hidden' + token.name = 'requesttoken' + token.value = getRequestToken() + + form.appendChild(token) + + document.body.appendChild(form) + form.submit() }, async actionShowShare() { diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index bbf117dbf..b64696958 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -41,6 +41,23 @@ export type paths = { readonly patch?: never; readonly trace?: never; }; + readonly "/index.php/apps/tables/api/1/tables/{tableId}/scheme": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** returns table scheme */ + readonly get: operations["api1-show-scheme"]; + readonly put?: never; + readonly post?: never; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; readonly "/index.php/apps/tables/api/1/tables/{tableId}/views": { readonly parameters: { readonly query?: never; @@ -417,6 +434,40 @@ export type paths = { readonly patch?: never; readonly trace?: never; }; + readonly "/ocs/v2.php/apps/tables/api/2/tables/scheme/{id}": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + /** [api v2] Get a table Scheme */ + readonly get: operations["api_tables-show-scheme"]; + readonly put?: never; + readonly post?: never; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; + readonly "/ocs/v2.php/apps/tables/api/2/tables/scheme": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path?: never; + readonly cookie?: never; + }; + readonly get?: never; + readonly put?: never; + /** creates table from scheme */ + readonly post: operations["api_tables-create-from-scheme"]; + readonly delete?: never; + readonly options?: never; + readonly head?: never; + readonly patch?: never; + readonly trace?: never; + }; readonly "/ocs/v2.php/apps/tables/api/2/tables/{id}/transfer": { readonly parameters: { readonly query?: never; @@ -1118,6 +1169,62 @@ export interface operations { }; }; }; + readonly "api1-show-scheme": { + readonly parameters: { + readonly query?: never; + readonly header?: never; + readonly path: { + /** @description Table ID */ + readonly tableId: number; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Table returned */ + readonly 200: { + headers: { + readonly "Content-Disposition"?: string; + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": components["schemas"]["Table"]; + }; + }; + /** @description No permissions */ + readonly 403: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly message: string; + }; + }; + }; + /** @description Not found */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly message: string; + }; + }; + }; + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly message: string; + }; + }; + }; + }; + }; readonly "api1-index-views": { readonly parameters: { readonly query?: never; @@ -3581,6 +3688,142 @@ export interface operations { }; }; }; + readonly "api_tables-show-scheme": { + readonly parameters: { + readonly query?: never; + readonly header: { + /** @description Required to be true for the API request to pass */ + readonly "OCS-APIRequest": boolean; + }; + readonly path: { + /** @description Table ID */ + readonly id: number; + }; + readonly cookie?: never; + }; + readonly requestBody?: never; + readonly responses: { + /** @description Scheme returned */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly ocs: { + readonly meta: components["schemas"]["OCSMeta"]; + readonly data: components["schemas"]["Table"]; + }; + }; + }; + }; + /** @description No permissions */ + readonly 403: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly ocs: { + readonly meta: components["schemas"]["OCSMeta"]; + readonly data: { + readonly message: string; + }; + }; + }; + }; + }; + /** @description Not found */ + readonly 404: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly ocs: { + readonly meta: components["schemas"]["OCSMeta"]; + readonly data: { + readonly message: string; + }; + }; + }; + }; + }; + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly ocs: { + readonly meta: components["schemas"]["OCSMeta"]; + readonly data: { + readonly message: string; + }; + }; + }; + }; + }; + }; + }; + readonly "api_tables-create-from-scheme": { + readonly parameters: { + readonly query?: never; + readonly header: { + /** @description Required to be true for the API request to pass */ + readonly "OCS-APIRequest": boolean; + }; + readonly path?: never; + readonly cookie?: never; + }; + readonly requestBody: { + readonly content: { + readonly "application/json": { + /** @description title of new table */ + readonly title: string; + /** @description emoji */ + readonly emoji: string; + /** @description description */ + readonly description: string; + /** @description columns */ + readonly columns: readonly components["schemas"]["Column"][]; + /** @description views */ + readonly views: readonly components["schemas"]["View"][]; + }; + }; + }; + readonly responses: { + /** @description Tables returned */ + readonly 200: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly ocs: { + readonly meta: components["schemas"]["OCSMeta"]; + readonly data: components["schemas"]["Table"]; + }; + }; + }; + }; + readonly 500: { + headers: { + readonly [name: string]: unknown; + }; + content: { + readonly "application/json": { + readonly ocs: { + readonly meta: components["schemas"]["OCSMeta"]; + readonly data: { + readonly message: string; + }; + }; + }; + }; + }; + }; + }; readonly "api_tables-transfer": { readonly parameters: { readonly query?: never;