diff --git a/lib/Controller/AdminController.php b/lib/Controller/AdminController.php index 676a507c65..ca15311c95 100644 --- a/lib/Controller/AdminController.php +++ b/lib/Controller/AdminController.php @@ -17,6 +17,7 @@ use OCA\Libresign\ResponseDefinitions; use OCA\Libresign\Service\Certificate\ValidateService; use OCA\Libresign\Service\CertificatePolicyService; +use OCA\Libresign\Service\FooterService; use OCA\Libresign\Service\Install\ConfigureCheckService; use OCA\Libresign\Service\Install\InstallService; use OCA\Libresign\Service\ReminderService; @@ -27,6 +28,7 @@ use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\ContentSecurityPolicy; +use OCP\AppFramework\Http\DataDownloadResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\Files\SimpleFS\InMemoryFile; @@ -61,6 +63,7 @@ public function __construct( private CertificatePolicyService $certificatePolicyService, private ValidateService $validateService, private ReminderService $reminderService, + private FooterService $footerService, ) { parent::__construct(Application::APP_ID, $request); $this->eventSource = $this->eventSourceFactory->create(); @@ -782,4 +785,74 @@ public function deleteTsaConfig(): DataResponse { return new DataResponse(['status' => 'success']); } + + /** + * Get footer template + * + * Returns the current footer template if set, otherwise returns the default template. + * + * @return DataResponse + * + * 200: OK + */ + #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/admin/footer-template', requirements: ['apiVersion' => '(v1)'])] + public function getFooterTemplate(): DataResponse { + return new DataResponse([ + 'template' => $this->footerService->getTemplate(), + 'isDefault' => $this->footerService->isDefaultTemplate(), + ]); + } + + /** + * Save footer template and render preview + * + * Saves the footer template and returns the rendered PDF preview. + * + * @param string $template The Twig template to save + * @param int $width Width of preview in points (default: 595 - A4 width) + * @param int $height Height of preview in points (default: 50) + * @return DataDownloadResponse|DataResponse + * + * 200: OK + * 400: Bad request + */ + #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/admin/footer-template', requirements: ['apiVersion' => '(v1)'])] + public function saveFooterTemplate(string $template, int $width = 595, int $height = 50) { + try { + $this->footerService->saveTemplate($template); + $pdf = $this->footerService->renderPreviewPdf(null, $width, $height); + + return new DataDownloadResponse($pdf, 'footer-preview.pdf', 'application/pdf'); + } catch (\Exception $e) { + return new DataResponse([ + 'error' => $e->getMessage(), + ], Http::STATUS_BAD_REQUEST); + } + } + + /** + * Preview footer template as PDF + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $template Template to preview + * @param int $width Width of preview in points (default: 595 - A4 width) + * @param int $height Height of preview in points (default: 50) + * @return DataDownloadResponse|DataResponse + * + * 200: OK + * 400: Bad request + */ + #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/admin/footer-template/preview-pdf', requirements: ['apiVersion' => '(v1)'])] + public function footerTemplatePreviewPdf(string $template = '', int $width = 595, int $height = 50) { + try { + $pdf = $this->footerService->renderPreviewPdf($template ?: null, $width, $height); + return new DataDownloadResponse($pdf, 'footer-preview.pdf', 'application/pdf'); + } catch (\Exception $e) { + return new DataResponse([ + 'error' => $e->getMessage(), + ], Http::STATUS_BAD_REQUEST); + } + } } diff --git a/lib/Service/FooterService.php b/lib/Service/FooterService.php new file mode 100644 index 0000000000..cb546a1756 --- /dev/null +++ b/lib/Service/FooterService.php @@ -0,0 +1,50 @@ +appConfig->getValueString(Application::APP_ID, 'footer_template', ''); + return empty($customTemplate); + } + + public function getTemplate(): string { + return $this->appConfig->getValueString(Application::APP_ID, 'footer_template', ''); + } + + public function saveTemplate(string $template): void { + $this->appConfig->setValueString(Application::APP_ID, 'footer_template', $template); + } + + public function renderPreviewPdf(?string $template = null, int $width = 595, int $height = 50): string { + if ($template !== null) { + $this->saveTemplate($template); + } + + return $this->footerHandler + ->setTemplateVar('uuid', 'preview-' . bin2hex(random_bytes(8))) + ->setTemplateVar('signers', [ + [ + 'displayName' => 'Preview Signer', + 'signed' => date('c'), + ], + ]) + ->getFooter([['w' => $width, 'h' => $height]]); + } +} diff --git a/openapi-administration.json b/openapi-administration.json index e4abf77b55..496dad32ff 100644 --- a/openapi-administration.json +++ b/openapi-administration.json @@ -2599,6 +2599,214 @@ } } }, + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template": { + "get": { + "operationId": "admin-get-footer-template", + "summary": "Get footer template", + "description": "Returns the current footer template if set, otherwise returns the default template.\nThis endpoint requires admin access", + "tags": [ + "admin" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "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": "OK", + "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": [ + "template", + "isDefault" + ], + "properties": { + "template": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + } + } + } + } + } + } + } + } + } + } + } + }, + "post": { + "operationId": "admin-save-footer-template", + "summary": "Save footer template and render preview", + "description": "Saves the footer template and returns the rendered PDF preview.\nThis endpoint requires admin access", + "tags": [ + "admin" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "template" + ], + "properties": { + "template": { + "type": "string", + "description": "The Twig template to save" + }, + "width": { + "type": "integer", + "format": "int64", + "default": 595, + "description": "Width of preview in points (default: 595 - A4 width)" + }, + "height": { + "type": "integer", + "format": "int64", + "default": 50, + "description": "Height of preview in points (default: 50)" + } + } + } + } + } + }, + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "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": "OK", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request", + "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": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/libresign/api/{apiVersion}/crl/list": { "get": { "operationId": "crl_api-list", diff --git a/openapi-full.json b/openapi-full.json index 0676ab3480..3c80fdfd74 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -3275,6 +3275,127 @@ } } }, + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template/preview-pdf": { + "post": { + "operationId": "admin-footer-template-preview-pdf", + "summary": "Preview footer template as PDF", + "tags": [ + "admin" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "template": { + "type": "string", + "default": "", + "description": "Template to preview" + }, + "width": { + "type": "integer", + "format": "int64", + "default": 595, + "description": "Width of preview in points (default: 595 - A4 width)" + }, + "height": { + "type": "integer", + "format": "int64", + "default": 50, + "description": "Height of preview in points (default: 50)" + } + } + } + } + } + }, + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "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": "OK", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request", + "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": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/libresign/api/{apiVersion}/file/validate/uuid/{uuid}": { "get": { "operationId": "file-validate-uuid", @@ -10955,6 +11076,214 @@ } } }, + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template": { + "get": { + "operationId": "admin-get-footer-template", + "summary": "Get footer template", + "description": "Returns the current footer template if set, otherwise returns the default template.\nThis endpoint requires admin access", + "tags": [ + "admin" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "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": "OK", + "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": [ + "template", + "isDefault" + ], + "properties": { + "template": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + } + } + } + } + } + } + } + } + } + } + } + }, + "post": { + "operationId": "admin-save-footer-template", + "summary": "Save footer template and render preview", + "description": "Saves the footer template and returns the rendered PDF preview.\nThis endpoint requires admin access", + "tags": [ + "admin" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "template" + ], + "properties": { + "template": { + "type": "string", + "description": "The Twig template to save" + }, + "width": { + "type": "integer", + "format": "int64", + "default": 595, + "description": "Width of preview in points (default: 595 - A4 width)" + }, + "height": { + "type": "integer", + "format": "int64", + "default": 50, + "description": "Height of preview in points (default: 50)" + } + } + } + } + } + }, + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "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": "OK", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request", + "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": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/libresign/api/{apiVersion}/crl/list": { "get": { "operationId": "crl_api-list", diff --git a/openapi.json b/openapi.json index e2a7fed0fc..d72927278f 100644 --- a/openapi.json +++ b/openapi.json @@ -3125,6 +3125,127 @@ } } }, + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template/preview-pdf": { + "post": { + "operationId": "admin-footer-template-preview-pdf", + "summary": "Preview footer template as PDF", + "tags": [ + "admin" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "template": { + "type": "string", + "default": "", + "description": "Template to preview" + }, + "width": { + "type": "integer", + "format": "int64", + "default": 595, + "description": "Width of preview in points (default: 595 - A4 width)" + }, + "height": { + "type": "integer", + "format": "int64", + "default": 50, + "description": "Height of preview in points (default: 50)" + } + } + } + } + } + }, + "parameters": [ + { + "name": "apiVersion", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "v1" + ], + "default": "v1" + } + }, + { + "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": "OK", + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "Bad request", + "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": [ + "error" + ], + "properties": { + "error": { + "type": "string" + } + } + } + } + } + } + } + } + } + } + } + } + }, "/ocs/v2.php/apps/libresign/api/{apiVersion}/file/validate/uuid/{uuid}": { "get": { "operationId": "file-validate-uuid", diff --git a/src/types/openapi/openapi-administration.ts b/src/types/openapi/openapi-administration.ts index 2ff2daf5b9..a3f24ed06e 100644 --- a/src/types/openapi/openapi-administration.ts +++ b/src/types/openapi/openapi-administration.ts @@ -297,6 +297,32 @@ export type paths = { patch?: never; trace?: never; }; + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get footer template + * @description Returns the current footer template if set, otherwise returns the default template. + * This endpoint requires admin access + */ + get: operations["admin-get-footer-template"]; + put?: never; + /** + * Save footer template and render preview + * @description Saves the footer template and returns the rendered PDF preview. + * This endpoint requires admin access + */ + post: operations["admin-save-footer-template"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/ocs/v2.php/apps/libresign/api/{apiVersion}/crl/list": { parameters: { query?: never; @@ -1375,6 +1401,99 @@ export interface operations { }; }; }; + "admin-get-footer-template": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + template: string; + isDefault: boolean; + }; + }; + }; + }; + }; + }; + }; + "admin-save-footer-template": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description The Twig template to save */ + template: string; + /** + * Format: int64 + * @description Width of preview in points (default: 595 - A4 width) + * @default 595 + */ + width?: number; + /** + * Format: int64 + * @description Height of preview in points (default: 50) + * @default 50 + */ + height?: number; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/pdf": string; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error: string; + }; + }; + }; + }; + }; + }; + }; "crl_api-list": { parameters: { query?: { diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index 9517398526..d1e7014c87 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -510,6 +510,23 @@ export type paths = { patch?: never; trace?: never; }; + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template/preview-pdf": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Preview footer template as PDF */ + post: operations["admin-footer-template-preview-pdf"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/ocs/v2.php/apps/libresign/api/{apiVersion}/file/validate/uuid/{uuid}": { parameters: { query?: never; @@ -1291,6 +1308,32 @@ export type paths = { patch?: never; trace?: never; }; + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get footer template + * @description Returns the current footer template if set, otherwise returns the default template. + * This endpoint requires admin access + */ + get: operations["admin-get-footer-template"]; + put?: never; + /** + * Save footer template and render preview + * @description Saves the footer template and returns the rendered PDF preview. + * This endpoint requires admin access + */ + post: operations["admin-save-footer-template"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/ocs/v2.php/apps/libresign/api/{apiVersion}/crl/list": { parameters: { query?: never; @@ -2683,6 +2726,69 @@ export interface operations { }; }; }; + "admin-footer-template-preview-pdf": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description Template to preview + * @default + */ + template?: string; + /** + * Format: int64 + * @description Width of preview in points (default: 595 - A4 width) + * @default 595 + */ + width?: number; + /** + * Format: int64 + * @description Height of preview in points (default: 50) + * @default 50 + */ + height?: number; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/pdf": string; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error: string; + }; + }; + }; + }; + }; + }; + }; "file-validate-uuid": { parameters: { query?: never; @@ -5691,6 +5797,99 @@ export interface operations { }; }; }; + "admin-get-footer-template": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + template: string; + isDefault: boolean; + }; + }; + }; + }; + }; + }; + }; + "admin-save-footer-template": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @description The Twig template to save */ + template: string; + /** + * Format: int64 + * @description Width of preview in points (default: 595 - A4 width) + * @default 595 + */ + width?: number; + /** + * Format: int64 + * @description Height of preview in points (default: 50) + * @default 50 + */ + height?: number; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/pdf": string; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error: string; + }; + }; + }; + }; + }; + }; + }; "crl_api-list": { parameters: { query?: { diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index 9bbf611bb0..24e0185921 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -510,6 +510,23 @@ export type paths = { patch?: never; trace?: never; }; + "/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/footer-template/preview-pdf": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Preview footer template as PDF */ + post: operations["admin-footer-template-preview-pdf"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/ocs/v2.php/apps/libresign/api/{apiVersion}/file/validate/uuid/{uuid}": { parameters: { query?: never; @@ -2292,6 +2309,69 @@ export interface operations { }; }; }; + "admin-footer-template-preview-pdf": { + parameters: { + query?: never; + header: { + /** @description Required to be true for the API request to pass */ + "OCS-APIRequest": boolean; + }; + path: { + apiVersion: "v1"; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description Template to preview + * @default + */ + template?: string; + /** + * Format: int64 + * @description Width of preview in points (default: 595 - A4 width) + * @default 595 + */ + width?: number; + /** + * Format: int64 + * @description Height of preview in points (default: 50) + * @default 50 + */ + height?: number; + }; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/pdf": string; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ocs: { + meta: components["schemas"]["OCSMeta"]; + data: { + error: string; + }; + }; + }; + }; + }; + }; + }; "file-validate-uuid": { parameters: { query?: never; diff --git a/tests/php/Unit/Service/FooterServiceTest.php b/tests/php/Unit/Service/FooterServiceTest.php new file mode 100644 index 0000000000..1c51eae89f --- /dev/null +++ b/tests/php/Unit/Service/FooterServiceTest.php @@ -0,0 +1,117 @@ +appConfig = $this->getMockAppConfig(); + $this->footerHandler = $this->createMock(FooterHandler::class); + $this->service = new FooterService($this->appConfig, $this->footerHandler); + } + + #[DataProvider('provideIsDefaultTemplateScenarios')] + public function testIsDefaultTemplate(string $configValue, bool $expected): void { + $this->appConfig->setValueString(Application::APP_ID, 'footer_template', $configValue); + + $this->assertSame($expected, $this->service->isDefaultTemplate()); + } + + public static function provideIsDefaultTemplateScenarios(): array { + return [ + 'empty string is default' => ['', true], + 'custom template is not default' => ['
Custom
', false], + 'whitespace only is not default' => [' ', false], + ]; + } + + public function testSaveTemplate(): void { + $template = '
My custom template
'; + + $this->service->saveTemplate($template); + + $this->assertSame( + $template, + $this->appConfig->getValueString(Application::APP_ID, 'footer_template') + ); + } + + public function testGetTemplate(): void { + $template = '
Custom template
'; + $this->appConfig->setValueString(Application::APP_ID, 'footer_template', $template); + + $this->assertSame($template, $this->service->getTemplate()); + } + + public function testGetTemplateReturnsEmptyWhenNotSet(): void { + $this->appConfig->deleteKey(Application::APP_ID, 'footer_template'); + + $this->assertSame('', $this->service->getTemplate()); + } + + #[DataProvider('provideRenderPreviewPdfScenarios')] + public function testRenderPreviewPdf(?string $template, int $width, int $height, bool $shouldSaveTemplate): void { + $this->footerHandler + ->expects($this->exactly(2)) + ->method('setTemplateVar') + ->willReturnCallback(function ($key, $value) { + if ($key === 'uuid') { + $this->assertMatchesRegularExpression('/^preview-[a-f0-9]{16}$/', $value); + } elseif ($key === 'signers') { + $this->assertIsArray($value); + $this->assertCount(1, $value); + $this->assertSame('Preview Signer', $value[0]['displayName']); + $this->assertArrayHasKey('signed', $value[0]); + } + return $this->footerHandler; + }); + + $this->footerHandler + ->expects($this->once()) + ->method('getFooter') + ->with([['w' => $width, 'h' => $height]]) + ->willReturn('PDF binary content'); + + $result = $this->service->renderPreviewPdf($template, $width, $height); + + $this->assertSame('PDF binary content', $result); + + if ($shouldSaveTemplate) { + $this->assertSame( + $template, + $this->appConfig->getValueString(Application::APP_ID, 'footer_template') + ); + } + } + + public static function provideRenderPreviewPdfScenarios(): array { + return [ + 'with custom template and default dimensions' => ['
Custom
', 595, 50, true], + 'without template uses default' => [null, 595, 50, false], + 'with custom dimensions' => ['
Test
', 800, 100, true], + 'A4 width with custom height' => [null, 595, 75, false], + 'empty string template' => ['', 595, 50, true], + 'template with unicode characters' => ['
签名 подпись توقيع
', 595, 50, true], + 'minimum dimensions' => ['
Min
', 1, 1, true], + 'large dimensions' => [null, 2000, 500, false], + ]; + } +}