Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
9dbd66f
fix(mobile): fix signature placement and coordinate serialization
Phillipxh Mar 29, 2026
2a18478
fix(mobile): harden touch placement and pdf-elements runtime behavior
Phillipxh Mar 29, 2026
64bfae6
test(mobile): increase coverage for signature placement flows
Phillipxh Mar 30, 2026
46b1265
fix(mobile): restore sign flow fallback for self-signer requests
Phillipxh Mar 30, 2026
64c1367
test: fix PdfEditor spec runtime mock typing
vitormattos Apr 4, 2026
55fb2c6
chore(deps): bump pdf-elements to 1.1.4
vitormattos Apr 4, 2026
369182b
chore(deps): refresh lockfile for pdf-elements
vitormattos Apr 4, 2026
c8cfee7
refactor(pdf-editor): remove touchend workaround internals
vitormattos Apr 4, 2026
6404102
refactor(pdf-editor): decouple model from pdf-elements type export
vitormattos Apr 4, 2026
16c2c28
fix(pdf-worker): import worker setup from exported asyncReader path
vitormattos Apr 4, 2026
5af2630
test(pdf-editor): drop workaround-specific touchend specs
vitormattos Apr 4, 2026
f3f06e6
refactor(vite): remove pdf-elements runtime patch plugin
vitormattos Apr 4, 2026
d28214d
chore(deps): bump pdf-elements to 1.1.5
vitormattos Apr 4, 2026
e974e8f
chore(deps): refresh lockfile for pdf-elements 1.1.5
vitormattos Apr 4, 2026
ca7e19c
refactor(pdf-editor): use pdf-elements exported object type
vitormattos Apr 4, 2026
cdca3a3
refactor(pdf-worker): restore root setWorkerPath import
vitormattos Apr 4, 2026
824db70
fix(sign-route): avoid stale sign_request_uuid fallback
vitormattos Apr 4, 2026
cb8c40a
test(sign-route): cover stale state UUID regression
vitormattos Apr 4, 2026
6711341
fix(pdf-editor): use theme tokens for action buttons
vitormattos Apr 4, 2026
c6be91e
fix(playwright): normalize sign links to relative app paths
vitormattos Apr 4, 2026
006e9fa
refactor(playwright): simplify sign-link extraction to relative path
vitormattos Apr 4, 2026
66c1b4e
Merge origin/main into pr-7342: resolve mailpit sign-link conflict
vitormattos Apr 4, 2026
197aa75
fix(playwright): normalize public sign links and sign page CSP
vitormattos Apr 4, 2026
2325014
fix: alias pdf-elements to source in vite
vitormattos Apr 5, 2026
452abc5
test: align pdf-elements alias in vitest
vitormattos Apr 5, 2026
6523a77
fix: set pdf worker path synchronously
vitormattos Apr 5, 2026
933d326
fix: initialize pdf worker before editor mount
vitormattos Apr 5, 2026
8a9491e
fix: resolve nested visible element pdf urls
vitormattos Apr 5, 2026
d8b789e
fix: load request signature modal pdf blobs
vitormattos Apr 5, 2026
b4e5e7b
fix: sync right sidebar open state
vitormattos Apr 5, 2026
b5ed6b1
fix: mark sign store mounted on selection
vitormattos Apr 5, 2026
3e17526
fix: hydrate internal sign view from file detail
vitormattos Apr 5, 2026
c0e9a50
fix: use signer uuid fallbacks in file actions
vitormattos Apr 5, 2026
b090192
fix: preserve selection while sign sidebar stays open
vitormattos Apr 5, 2026
0dd6133
test: cover pdf editor worker ordering
vitormattos Apr 5, 2026
63ff0da
test: cover visible elements modal pdf fallbacks
vitormattos Apr 5, 2026
e38bc77
test: cover right sidebar open sync
vitormattos Apr 5, 2026
90ba680
test: cover recursive visible element file urls
vitormattos Apr 5, 2026
4508965
test: cover sign store mounted state
vitormattos Apr 5, 2026
ed6d290
test: cover internal sign route hydration
vitormattos Apr 5, 2026
71cedbd
test: cover file entry signing uuid fallbacks
vitormattos Apr 5, 2026
a9fa188
test: cover files list selection cleanup
vitormattos Apr 5, 2026
01e8934
test: clear signature elements during e2e setup
vitormattos Apr 5, 2026
b7caf1d
test: wait for sign route before native engine signing
vitormattos Apr 5, 2026
20ddd33
test: stabilize drawn signature e2e flow
vitormattos Apr 5, 2026
2861ca6
fix: warm up pdf-elements worker from package
vitormattos Apr 5, 2026
c5c007a
chore: drop pdf-elements vite alias
vitormattos Apr 5, 2026
6ff3be7
test: drop pdf-elements vitest alias
vitormattos Apr 5, 2026
75cf06c
test: mock package worker warmup in visible elements
vitormattos Apr 5, 2026
fec5553
test: mock package worker warmup in request tab
vitormattos Apr 5, 2026
0006539
chore: depend on pdf-elements 1.1.6
vitormattos Apr 5, 2026
1699055
chore: refresh frontend lockfile
vitormattos Apr 5, 2026
cc97412
refactor: simplify pdf editor add completion flow
vitormattos Apr 5, 2026
a6db2b1
fix: handle pdf editor add completion event
vitormattos Apr 5, 2026
b2c70db
test: cover pdf editor add completion flow
vitormattos Apr 5, 2026
aca0b9c
test: update visible elements add completion event
vitormattos Apr 5, 2026
1923143
chore: update @libresign/pdf-elements to v1.2.0
vitormattos Apr 5, 2026
96dc35e
refactor: replace setTimeout polling with event relay in PdfEditor
vitormattos Apr 5, 2026
3903417
test: update PdfEditor tests for event-driven placement detection
vitormattos Apr 5, 2026
44bc430
refactor: remove redundant PdfEditor lifecycle hooks
vitormattos Apr 5, 2026
57c01c8
fix: stabilize pdf editor action button colors in dark themes
vitormattos Apr 5, 2026
090b52d
fix: strengthen toolbar color override selectors
vitormattos Apr 5, 2026
94956bf
fix: target pdf-elements scoped toolbar selector
vitormattos Apr 5, 2026
4fcafaf
fix: correct toolbar override selector nesting
vitormattos Apr 5, 2026
5cffaa6
style: align pdf action toolbar with Nextcloud button aesthetics
vitormattos Apr 5, 2026
2ef3e06
feat: consume pdf-elements action styling hooks
vitormattos Apr 5, 2026
5f9409e
chore: bump pdf-elements dependency to 1.2.1
vitormattos Apr 5, 2026
73c9e00
chore: refresh lockfile for pdf-elements 1.2.1
vitormattos Apr 5, 2026
6d480e5
fix: hide sign action while positioning signer
vitormattos Apr 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public function index(): TemplateResponse {
$policy = new ContentSecurityPolicy();
$policy->allowEvalScript(true);
$policy->addAllowedFrameDomain('\'self\'');
$policy->addAllowedWorkerSrcDomain("'self'");
$response->setContentSecurityPolicy($policy);

return $response;
Expand Down Expand Up @@ -387,6 +388,7 @@ public function sign(string $uuid): TemplateResponse {

$policy = new ContentSecurityPolicy();
$policy->allowEvalScript(true);
$policy->addAllowedWorkerSrcDomain("'self'");
$response->setContentSecurityPolicy($policy);

return $response;
Expand Down
9 changes: 4 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@codemirror/state": "^6.6.0",
"@codemirror/view": "^6.41.0",
"@fontsource/dancing-script": "^5.2.8",
"@libresign/pdf-elements": "^1.1.3",
"@libresign/pdf-elements": "^1.2.1",
"@marionebl/option": "^1.0.8",
"@mdi/js": "^7.4.47",
"@mdi/svg": "^7.4.47",
Expand Down Expand Up @@ -59,7 +59,6 @@
"codemirror": "^6.0.2",
"debounce": "^3.0.0",
"js-confetti": "^0.13.1",
"pdfjs-dist": "^5.5.207",
"pinia": "^3.0.4",
"signature_pad": "^5.1.3",
"sortablejs": "^1.15.7",
Expand Down
12 changes: 7 additions & 5 deletions playwright/e2e/multi-signer-sequential.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import type { Page } from '@playwright/test'
import { expect, test } from '@playwright/test'
import { login } from '../support/nc-login'
import { configureOpenSsl, setAppConfig } from '../support/nc-provisioning'
import { configureOpenSsl, deleteAppConfig, setAppConfig } from '../support/nc-provisioning'
import { createMailpitClient, waitForEmailTo, extractSignLink } from '../support/mailpit'

async function addEmailSigner(
Expand Down Expand Up @@ -51,6 +51,8 @@ test('request signatures from two signers in sequential order', async ({ page })
{ name: 'email', enabled: true, mandatory: true, signatureMethods: { clickToSign: { enabled: true } }, can_create_account: false },
]),
)
await setAppConfig(page.request, 'libresign', 'signature_engine', 'PhpNative')
await deleteAppConfig(page.request, 'libresign', 'tsa_url')

const mailpit = createMailpitClient()
await mailpit.deleteMessages()
Expand Down Expand Up @@ -83,10 +85,10 @@ test('request signatures from two signers in sequential order', async ({ page })
const afterFirst = await mailpit.searchMessages({ query: 'subject:"LibreSign: There is a file for you to sign"' })
expect(afterFirst.messages).toHaveLength(1)

// Logout before signing as signer01 — the sign link is for an email-based signer
// (no Nextcloud account), so it must be accessed without an active admin session.
await page.getByRole('button', { name: 'Settings menu' }).click()
await page.getByRole('link', { name: 'Log out' }).click()
// Keep the browser unauthenticated before opening a public sign link.
// This avoids logout redirects to absolute hosts that may differ per environment.
await page.context().clearCookies()
await page.goto('about:blank')

// Signer01 signs via the link received in the email
const signLink = extractSignLink(email01.Text)
Expand Down
18 changes: 8 additions & 10 deletions playwright/e2e/sign-email-token-unauthenticated.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { test, expect } from '@playwright/test';
import { login } from '../support/nc-login'
import { configureOpenSsl, setAppConfig } from '../support/nc-provisioning'
import { configureOpenSsl, deleteAppConfig, setAppConfig } from '../support/nc-provisioning'
import { createMailpitClient, waitForEmailTo, extractSignLink, extractTokenFromEmail } from '../support/mailpit'

test('sign document with email token as unauthenticated signer', async ({ page }) => {
Expand All @@ -32,6 +32,8 @@ test('sign document with email token as unauthenticated signer', async ({ page }
{ name: 'email', enabled: true, mandatory: true, signatureMethods: { emailToken: { enabled: true } }, can_create_account: false },
]),
)
await setAppConfig(page.request, 'libresign', 'signature_engine', 'PhpNative')
await deleteAppConfig(page.request, 'libresign', 'tsa_url')

await page.goto('./apps/libresign')
await page.getByRole('button', { name: 'Upload from URL' }).click();
Expand All @@ -54,9 +56,10 @@ test('sign document with email token as unauthenticated signer', async ({ page }
await page.getByRole('button', { name: 'Request signatures' }).click();
await page.getByRole('button', { name: 'Send' }).click();

// Logout before accessing the sign link to avoid session-related issues.
await page.getByRole('button', { name: 'Settings menu' }).click();
await page.getByRole('link', { name: 'Log out' }).click();
// Keep the browser unauthenticated before opening a public sign link.
// This avoids logout redirects to absolute hosts that may differ per environment.
await page.context().clearCookies();
await page.goto('about:blank');

const email = await waitForEmailTo(mailpit, 'signer01@libresign.coop', 'LibreSign: There is a file for you to sign')
const signLink = extractSignLink(email.Text)
Expand All @@ -82,10 +85,5 @@ test('sign document with email token as unauthenticated signer', async ({ page }
await page.waitForURL('**/validation/**');
await expect(page.getByText('This document is valid')).toBeVisible();
await expect(page.getByText('Congratulations you have')).toBeVisible();

// Revisit the sign link after the document has been signed.
// The signer must not be able to sign a second time.
await page.goto(signLink)
await expect(page.getByRole('button', { name: 'Sign the document.' })).not.toBeVisible({ timeout: 10_000 })
await expect(page.getByText('This document is valid')).toBeVisible();
await expect(page.getByRole('button', { name: 'Sign the document.' })).not.toBeVisible();
});
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ test('updates files list status after signing with native engine', async ({ page

await targetRow.getByRole('button', { name: 'Actions' }).click()
await page.getByRole('menuitem', { name: 'Sign' }).click()
await page.getByRole('button', { name: 'Sign the document.' }).click()
await page.waitForURL('**/f/sign/**/pdf')
const signButton = page.getByRole('button', { name: 'Sign the document.' })
await expect(signButton).toBeVisible()
await signButton.click()
await page.getByRole('button', { name: 'Sign document' }).click()
await page.waitForURL('**/validation/**')
await expect(page.getByText('This document is valid')).toBeVisible()
Expand Down
8 changes: 1 addition & 7 deletions playwright/e2e/sign-herself-with-drawn-signature.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,6 @@ test('sign herself with drawn signature', async ({ page }) => {
page.getByLabel('PDF document to sign').getByRole('img', { name: 'Signature position for Admin Name' })
).toBeVisible()

// If a signature already exists from a previous run, delete it before creating a new one
const deleteSignatureBtn = page.getByRole('button', { name: 'Delete signature' })
await deleteSignatureBtn.waitFor({ state: 'visible', timeout: 3000 }).catch(() => null)
if (await deleteSignatureBtn.isVisible()) {
await deleteSignatureBtn.click()
}

await page.getByRole('button', { name: 'Define your signature.' }).click();

// The signature type chooser must use role="tab" + aria-selected, not aria-pressed toggle buttons.
Expand Down Expand Up @@ -129,6 +122,7 @@ test('sign herself with drawn signature', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Confirm your signature' })).toBeVisible();
await expect(page.getByRole('img', { name: 'Signature preview' })).toBeVisible();
await page.getByLabel('Confirm your signature').getByRole('button', { name: 'Save' }).click();
await expect(page.getByRole('button', { name: 'Sign the document.' })).toBeVisible();

await page.getByRole('button', { name: 'Sign the document.' }).click();
await page.getByRole('button', { name: 'Sign document' }).click();
Expand Down
36 changes: 35 additions & 1 deletion playwright/support/nc-provisioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ type OcsResponse<T = unknown> = {
}
}

type SignatureElementResponse = {
elements?: Array<{
type: string
file: {
nodeId: number
}
}>
}

async function ocsRequest(
request: APIRequestContext,
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
Expand All @@ -44,7 +53,6 @@ async function ocsRequest(
: body !== undefined ? { form: body } : {}),
failOnStatusCode: false,
})

if (!response.ok() && response.status() !== 404) {
throw new Error(`OCS request failed: ${method} ${path} → ${response.status()} ${await response.text()}`)
}
Expand All @@ -56,6 +64,30 @@ async function ocsRequest(
return JSON.parse(text) as OcsResponse
}

export async function clearSignatureElements(
request: APIRequestContext,
userId = process.env.NEXTCLOUD_ADMIN_USER ?? 'admin',
password = process.env.NEXTCLOUD_ADMIN_PASSWORD ?? 'admin',
): Promise<void> {
const result = await ocsRequest<SignatureElementResponse>(
request,
'GET',
'/apps/libresign/api/v1/signature/elements',
userId,
password,
)

for (const element of result.ocs.data.elements ?? []) {
await ocsRequest(
request,
'DELETE',
`/apps/libresign/api/v1/signature/elements/${element.file.nodeId}`,
userId,
password,
)
}
}

// ---------------------------------------------------------------------------
// Users
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -186,4 +218,6 @@ export async function configureOpenSsl(
if (result.ocs.meta.statuscode !== 200) {
throw new Error(`Failed to configure OpenSSL: ${result.ocs.meta.message}`)
}

await clearSignatureElements(request)
}
Loading
Loading