feat: Admin frontend of custom signature template

Signed-off-by: Vitor Mattos <vitor@php.rio>
This commit is contained in:
Vitor Mattos 2025-03-26 17:53:23 -03:00
parent e2c6debad2
commit dab0ea499f
No known key found for this signature in database
GPG key ID: B7AB4B76A7CA7318
15 changed files with 1011 additions and 120 deletions

View file

@ -2,13 +2,13 @@
"require": {
"endroid/qr-code": "^5.0",
"jsignpdf/jsignpdf-php": "^1.2",
"league/plates": "^3.5",
"libresign/whatosami": "^0.0.2",
"mikehaertl/php-pdftk": "^0.13.0",
"mpdf/mpdf": "^8.2",
"pagerfanta/pagerfanta": "^4.5",
"phpseclib/phpseclib": "^3.0",
"smalot/pdfparser": "^2.4",
"twig/twig": "^3.20",
"wobeto/email-blur": "^1.0"
},
"require-dev": {

224
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "fa70120a807fdc1e2baca77ca52aebf5",
"content-hash": "a7c0ec19a821254f3138366739329b9f",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -235,70 +235,6 @@
},
"time": "2024-12-12T16:47:19+00:00"
},
{
"name": "league/plates",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/plates.git",
"reference": "12ee65166adbc6fb5916fb80b0c0758e49a2d996"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/plates/zipball/12ee65166adbc6fb5916fb80b0c0758e49a2d996",
"reference": "12ee65166adbc6fb5916fb80b0c0758e49a2d996",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"mikey179/vfsstream": "^1.6",
"phpunit/phpunit": "^11.4",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"League\\Plates\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Reinink",
"email": "jonathan@reinink.ca",
"role": "Developer"
},
{
"name": "RJ Garcia",
"email": "ragboyjr@icloud.com",
"role": "Developer"
}
],
"description": "Plates, the native PHP template system that's fast, easy to use and easy to extend.",
"homepage": "https://platesphp.com",
"keywords": [
"league",
"package",
"templates",
"templating",
"views"
],
"support": {
"issues": "https://github.com/thephpleague/plates/issues",
"source": "https://github.com/thephpleague/plates/tree/v3.6.0"
},
"time": "2024-11-02T15:03:35+00:00"
},
{
"name": "libresign/whatosami",
"version": "0.0.2",
@ -1345,6 +1281,85 @@
],
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.31.0",
@ -1425,6 +1440,85 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "twig/twig",
"version": "v3.20.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "3468920399451a384bef53cf7996965f7cd40183"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183",
"reference": "3468920399451a384bef53cf7996965f7cd40183",
"shasum": ""
},
"require": {
"php": ">=8.1.0",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
"type": "library",
"autoload": {
"files": [
"src/Resources/core.php",
"src/Resources/debug.php",
"src/Resources/escaper.php",
"src/Resources/string_loader.php"
],
"psr-4": {
"Twig\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "https://twig.symfony.com",
"keywords": [
"templating"
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.20.0"
},
"funding": [
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
"type": "tidelift"
}
],
"time": "2025-02-13T08:34:43+00:00"
},
{
"name": "wobeto/email-blur",
"version": "1.0.0",

View file

@ -17,6 +17,7 @@ use OCA\Libresign\ResponseDefinitions;
use OCA\Libresign\Service\Install\ConfigureCheckService;
use OCA\Libresign\Service\Install\InstallService;
use OCA\Libresign\Service\SignatureBackgroundService;
use OCA\Libresign\Service\SignatureTextService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
@ -45,6 +46,7 @@ class AdminController extends AEnvironmentAwareController {
private InstallService $installService,
private CertificateEngineHandler $certificateEngineHandler,
private IEventSourceFactory $eventSourceFactory,
private SignatureTextService $signatureTextService,
private IL10N $l10n,
protected ISession $session,
private SignatureBackgroundService $signatureBackgroundService,
@ -380,4 +382,44 @@ class AdminController extends AEnvironmentAwareController {
]
);
}
/**
* Save signature text service
*
* @param string $template Template to signature text
* @return DataResponse<Http::STATUS_OK, array{parsed: string}, array{}>
*
* 200: OK
*/
#[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/admin/signature-text', requirements: ['apiVersion' => '(v1)'])]
public function signatureTextSave(string $template): DataResponse {
$parsed = $this->signatureTextService->save($template);
return new DataResponse(
[
'parsed' => $parsed,
],
Http::STATUS_OK
);
}
/**
* Get parsed signature text service
*
* @param string $template Template to signature text
* @param string $context Context for parsing the template
* @return DataResponse<Http::STATUS_OK, array{parsed: string}, array{}>
*
* 200: OK
*/
#[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/admin/signature-text', requirements: ['apiVersion' => '(v1)'])]
public function signatureTextGet(string $template = '', string $context = ''): DataResponse {
$context = json_decode($context, true) ?? [];
$parsed = $this->signatureTextService->parse($template, $context);
return new DataResponse(
[
'parsed' => $parsed
],
Http::STATUS_OK
);
}
}

View file

@ -16,7 +16,6 @@ use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Writer\PngWriter;
use League\Plates\Engine;
use Mpdf\Mpdf;
use OCA\Libresign\AppInfo\Application;
use OCA\Libresign\Db\File as FileEntity;
@ -28,6 +27,9 @@ use OCP\IL10N;
use OCP\ITempManager;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
use Twig\Environment;
use Twig\Error\SyntaxError;
use Twig\Loader\FilesystemLoader;
class FooterHandler {
private QrCode $qrCode;
@ -100,10 +102,16 @@ class FooterHandler {
}
private function getRenderedHtmlFooter(): string {
$templateFile = $this->getTemplateFile();
$pathInfo = pathinfo($templateFile);
$templates = new Engine($pathInfo['dirname']);
return $templates->render($pathInfo['filename'], $this->getTemplateVars());
try {
$twigEnvironment = new Environment(
new FilesystemLoader(),
);
return $twigEnvironment
->createTemplate($this->getTemplate())
->render($this->getTemplateVars());
} catch (SyntaxError $e) {
throw new LibresignException($e->getMessage());
}
}
public function setTemplateVar(string $name, mixed $value): self {
@ -145,17 +153,12 @@ class FooterHandler {
return $this->templateVars;
}
private function getTemplateFile(): string {
private function getTemplate(): string {
$footerTemplate = $this->appConfig->getValueString(Application::APP_ID, 'footer_template', '');
if ($footerTemplate) {
$tempFile = $this->tempManager->getTemporaryFile('footerTemplate.php');
if (!$tempFile) {
throw new LibresignException('Failure to create temporary file footerTemplate.php');
}
file_put_contents($tempFile, $footerTemplate);
return $tempFile;
return $footerTemplate;
}
return __DIR__ . '/Templates/footer.php';
return (string) file_get_contents(__DIR__ . '/Templates/footer.teig');
}
private function getQrCodeImageBase64(string $text): string {

View file

@ -1,23 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2024 LibreCode coop and contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
?>
<table style="width:100%;border:0;<?php if (empty($qrcode)) { ?>padding-left:15px;<?php } ?>font-size:8px;">
<tr>
<?php if (!empty($qrcode)) { ?>
<td width="<?= $qrcodeSize; ?>px">
<img src="data:image/png;base64,<?= $qrcode; ?>" style="width:<?= $qrcodeSize; ?>px"/>
</td>
<?php } ?>
<td style="vertical-align: bottom;padding: 0px 0px 15px 0px;line-height:1.5em;">
<a href="<?= $linkToSite; ?>" style="text-decoration: none;color:unset;"><?= $signedBy; ?></a>
<?php if ($validateIn) { ?>
<br>
<a href="<?=$validationSite; ?>" style="text-decoration: none;color:unset;"><?= str_replace('%s', $validationSite, $validateIn); ?></a>
<?php } ?>
</td>
</tr>
</table>

View file

@ -0,0 +1,23 @@
{#
SPDX-FileCopyrightText: 2024 LibreCode coop and contributors
SPDX-License-Identifier: AGPL-3.0-or-later
#}
<table style="width:100%; border:0; {% if not qrcode %}padding-left:15px;{% endif %} font-size:8px;">
<tr>
{% if qrcode %}
<td width="{{ qrcodeSize }}px">
<img src="data:image/png;base64,{{ qrcode }}" style="width:{{ qrcodeSize }}px" />
</td>
{% endif %}
<td style="vertical-align: bottom; padding: 0px 0px 15px 0px; line-height:1.5em;">
<a href="{{ linkToSite }}" style="text-decoration: none; color: unset;">{{ signedBy }}</a>
{% if validateIn %}
<br>
<a href="{{ validationSite }}"
style="text-decoration: none; color: unset;">
{{ validateIn|replace({'%s': validationSite}) }}
</a>
{% endif %}
</td>
</tr>
</table>

View file

@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 LibreCode coop and contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Libresign\Service;
use DateTimeInterface;
use OCP\AppFramework\Services\IAppConfig;
use Sabre\DAV\UUIDUtil;
use Twig\Environment;
use Twig\Error\SyntaxError;
use Twig\Loader\FilesystemLoader;
class SignatureTextService {
public function __construct(
private IAppConfig $appConfig,
) {
}
public function save(string $template): string {
$this->appConfig->setAppValueString('signature_text_template', $template);
return $this->parse($template);
}
public function parse(string $template, array $context = []): string {
if (empty($template)) {
$template = $this->appConfig->getAppValueString('signature_text_template');
}
if (empty($context)) {
$context = [
'SignerName' => 'John Doe',
'DocumentUUID' => UUIDUtil::getUUID(),
'CommonName' => 'Acme Cooperative',
'SignatureDate' => (new \DateTime())->format(DateTimeInterface::ATOM)
];
}
try {
$twigEnvironment = new Environment(
new FilesystemLoader(),
);
return $twigEnvironment
->createTemplate($template)
->render($context);
} catch (SyntaxError $e) {
return (string) preg_replace('/in "[^"]+" at line \d+/', '', $e->getMessage());
}
}
}

View file

@ -1058,6 +1058,205 @@
}
}
},
"/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/signature-text": {
"post": {
"operationId": "admin-signature-text-save",
"summary": "Save signature text service",
"description": "This 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": "Template to signature text"
}
}
}
}
}
},
"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": [
"parsed"
],
"properties": {
"parsed": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"get": {
"operationId": "admin-signature-text-get",
"summary": "Get parsed signature text service",
"description": "This 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": "template",
"in": "query",
"description": "Template to signature text",
"schema": {
"type": "string",
"default": ""
}
},
{
"name": "context",
"in": "query",
"description": "Context for parsing the template",
"schema": {
"type": "string",
"default": ""
}
},
{
"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": [
"parsed"
],
"properties": {
"parsed": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/libresign/api/{apiVersion}/setting/has-root-cert": {
"get": {
"operationId": "setting-has-root-cert",

View file

@ -8795,6 +8795,205 @@
}
}
},
"/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/signature-text": {
"post": {
"operationId": "admin-signature-text-save",
"summary": "Save signature text service",
"description": "This 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": "Template to signature text"
}
}
}
}
}
},
"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": [
"parsed"
],
"properties": {
"parsed": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"get": {
"operationId": "admin-signature-text-get",
"summary": "Get parsed signature text service",
"description": "This 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": "template",
"in": "query",
"description": "Template to signature text",
"schema": {
"type": "string",
"default": ""
}
},
{
"name": "context",
"in": "query",
"description": "Context for parsing the template",
"schema": {
"type": "string",
"default": ""
}
},
{
"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": [
"parsed"
],
"properties": {
"parsed": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/libresign/api/{apiVersion}/setting/has-root-cert": {
"get": {
"operationId": "setting-has-root-cert",

View file

@ -139,6 +139,30 @@ export type paths = {
patch: operations["admin-signature-background-reset"];
trace?: never;
};
"/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/signature-text": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Get parsed signature text service
* @description This endpoint requires admin access
*/
get: operations["admin-signature-text-get"];
put?: never;
/**
* Save signature text service
* @description This endpoint requires admin access
*/
post: operations["admin-signature-text-save"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/ocs/v2.php/apps/libresign/api/{apiVersion}/setting/has-root-cert": {
parameters: {
query?: never;
@ -574,6 +598,82 @@ export interface operations {
};
};
};
"admin-signature-text-get": {
parameters: {
query?: {
/** @description Template to signature text */
template?: string;
/** @description Context for parsing the template */
context?: string;
};
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: {
parsed: string;
};
};
};
};
};
};
};
"admin-signature-text-save": {
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 signature text */
template: string;
};
};
};
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: {
parsed: string;
};
};
};
};
};
};
};
"setting-has-root-cert": {
parameters: {
query?: never;

View file

@ -1049,6 +1049,30 @@ export type paths = {
patch: operations["admin-signature-background-reset"];
trace?: never;
};
"/ocs/v2.php/apps/libresign/api/{apiVersion}/admin/signature-text": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Get parsed signature text service
* @description This endpoint requires admin access
*/
get: operations["admin-signature-text-get"];
put?: never;
/**
* Save signature text service
* @description This endpoint requires admin access
*/
post: operations["admin-signature-text-save"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/ocs/v2.php/apps/libresign/api/{apiVersion}/setting/has-root-cert": {
parameters: {
query?: never;
@ -4573,6 +4597,82 @@ export interface operations {
};
};
};
"admin-signature-text-get": {
parameters: {
query?: {
/** @description Template to signature text */
template?: string;
/** @description Context for parsing the template */
context?: string;
};
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: {
parsed: string;
};
};
};
};
};
};
};
"admin-signature-text-save": {
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 signature text */
template: string;
};
};
};
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": {
ocs: {
meta: components["schemas"]["OCSMeta"];
data: {
parsed: string;
};
};
};
};
};
};
};
"setting-has-root-cert": {
parameters: {
query?: never;

View file

@ -20,6 +20,7 @@
<DefaultUserFolder />
<SignatureHashAlgorithm />
<SignatureBackground />
<SignatureTextTemplate />
</NcSettingsSection>
</template>
@ -40,6 +41,7 @@ import RootCertificateCfssl from './RootCertificateCfssl.vue'
import RootCertificateOpenSsl from './RootCertificateOpenSsl.vue'
import SignatureBackground from './SignatureBackground.vue'
import SignatureHashAlgorithm from './SignatureHashAlgorithm.vue'
import SignatureTextTemplate from './SignatureTextTemplate.vue'
import Validation from './Validation.vue'
export default {
@ -61,6 +63,7 @@ export default {
DefaultUserFolder,
SignatureHashAlgorithm,
SignatureBackground,
SignatureTextTemplate,
},
data() {
return {

View file

@ -0,0 +1,95 @@
<!--
- SPDX-FileCopyrightText: 2024 LibreCode coop and LibreCode contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcSettingsSection :name="name" :description="description">
<div class="content">
<NcTextArea :value.sync="inputValue"
:placeholder="t('libresign', 'Signature text template')"
:success="showSuccess"
resize="vertical"
@keydown.enter="save"
@blur="save" />
<div class="text-pre-line">
{{ parsed }}
</div>
</div>
</NcSettingsSection>
</template>
<script>
import debounce from 'debounce'
import axios from '@nextcloud/axios'
import { translate as t } from '@nextcloud/l10n'
import { generateOcsUrl } from '@nextcloud/router'
import NcSettingsSection from '@nextcloud/vue/components/NcSettingsSection'
import NcTextArea from '@nextcloud/vue/components/NcTextArea'
export default {
name: 'SignatureTextTemplate',
components: {
NcSettingsSection,
NcTextArea,
},
data() {
return {
name: t('libresign', 'Signature text template'),
description: t('libresign', 'This template will be mixed to signature.'),
signatureTextTemplate: '',
showSuccess: false,
parsed: '',
}
},
computed: {
inputValue: {
get() {
return this.signatureTextTemplate
},
set(value) {
this.signatureTextTemplate = value.trim()
this.debouncePropertyChange()
},
},
debouncePropertyChange() {
return debounce(async function() {
await this.save()
}, 1000)
},
},
created() {
this.getData()
},
methods: {
async getData() {
await axios.get(generateOcsUrl('/apps/provisioning_api/api/v1/config/apps/libresign/signature_text_template'))
.then(({ data }) => {
this.signatureTextTemplate = data.ocs.data.data
return axios.get(generateOcsUrl('/apps/libresign/api/v1/admin/signature-text'))
})
.then(({ data }) => {
this.parsed = data.ocs.data.parsed
})
},
async save() {
this.showSuccess = false
await axios.post(generateOcsUrl('/apps/libresign/api/v1/admin/signature-text'), { template: this.signatureTextTemplate })
.then(({ data }) => {
this.parsed = data.ocs.data.parsed
this.showSuccess = true
setTimeout(() => { this.showSuccess = false }, 2000)
})
},
},
}
</script>
<style scoped>
.content{
display: flex;
flex-direction: column;
}
.text-pre-line {
white-space: pre-line;
}
</style>

View file

@ -123,11 +123,11 @@ final class FooterHandlerTest extends \OCA\Libresign\Tests\Unit\TestCase {
'footer_signed_by' => 'Digital signed by LibreSign.',
'footer_validate_in' => 'Validate in %s.',
'footer_template' => <<<'HTML'
<div style="font-size:8px;" dir="<?= $direction ?>">
qrcodeSize:<?= $qrcodeSize ?><br />
signedBy:<?= $signedBy ?><br />
validateIn:<?= $validateIn ?><br />
qrcode:<?= $qrcode ?>
<div style="font-size:8px;" dir="{{ direction }}">
qrcodeSize:{{ qrcodeSize }}<br />
signedBy:{{ signedBy|raw }}<br />
validateIn:{{ validateIn }}<br />
qrcode:{{ qrcode }}
</div>
HTML,
],
@ -148,9 +148,9 @@ final class FooterHandlerTest extends \OCA\Libresign\Tests\Unit\TestCase {
'footer_signed_by' => 'Digital signed by LibreSign.',
'footer_validate_in' => 'Validate in %s.',
'footer_template' => <<<'HTML'
<div style="font-size:8px;" dir="<?= $direction ?>">
signedBy:<?= $signedBy ?><br />
validateIn:<?= $validateIn ?><br />
<div style="font-size:8px;" dir="{{ direction }}">
signedBy:{{ signedBy|raw }}<br />
validateIn:{{ validateIn }}<br />
</div>
HTML,
],
@ -169,9 +169,9 @@ final class FooterHandlerTest extends \OCA\Libresign\Tests\Unit\TestCase {
'footer_signed_by' => 'Signé numériquement avec LibreSign.',
'footer_validate_in' => 'Validate in %s',
'footer_template' => <<<'HTML'
<div style="font-size:8px;" dir="<?= $direction ?>">
signedBy:<?= $signedBy ?><br />
validateIn:<?= $validateIn ?><br />
<div style="font-size:8px;" dir="{{ direction }}">
signedBy:{{ signedBy|raw }}<br />
validateIn:{{ validateIn }}<br />
</div>
HTML,
],
@ -190,9 +190,9 @@ final class FooterHandlerTest extends \OCA\Libresign\Tests\Unit\TestCase {
'footer_signed_by' => 'Το αρχείο υπάρχει',
'footer_validate_in' => 'Validate in %s.',
'footer_template' => <<<'HTML'
<div style="font-size:8px;" dir="<?= $direction ?>">
signedBy:<?= $signedBy ?><br />
validateIn:<?= $validateIn ?><br />
<div style="font-size:8px;" dir="{{ direction }}">
signedBy:{{ signedBy|raw }}<br />
validateIn:{{ validateIn }}<br />
</div>
HTML,
],
@ -211,9 +211,9 @@ final class FooterHandlerTest extends \OCA\Libresign\Tests\Unit\TestCase {
'footer_signed_by' => 'אין המלצות. נא להתחיל להקליד.',
'footer_validate_in' => 'אמת ב- %s.',
'footer_template' => <<<'HTML'
<div style="font-size:8px;" dir="<?= $direction ?>">
signedBy:<?= $signedBy ?><br />
validateIn:<?= $validateIn ?><br />
<div style="font-size:8px;" dir="{{ direction }}">
signedBy:{{ signedBy|raw }}<br />
validateIn:{{ validateIn }}<br />
</div>
HTML,
],

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.26.1@d747f6500b38ac4f7dfc5edbcae6e4b637d7add0">
<files psalm-version="6.5.0@38fc8444edf0cebc9205296ee6e30e906ade783b">
<file src="lib/AppInfo/Application.php">
<InvalidArgument>
<code><![CDATA[LoadAdditionalListener::class]]></code>
@ -177,6 +177,11 @@
<code><![CDATA[UUIDUtil]]></code>
</UndefinedClass>
</file>
<file src="lib/Service/SignatureTextService.php">
<UndefinedClass>
<code><![CDATA[UUIDUtil]]></code>
</UndefinedClass>
</file>
<file src="lib/Service/TFile.php">
<UndefinedDocblockClass>
<code><![CDATA[PdfTypeException]]></code>