From f72e80bbf4e2858e1e732ed8696f1a0c93f3faf2 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:25:43 -0300
Subject: [PATCH 1/9] fix(settings): guard isAdmin access when getCurrentUser
returns null
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/components/Settings/Settings.vue | 2 +-
.../components/Settings/Settings.spec.ts | 48 +++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/src/components/Settings/Settings.vue b/src/components/Settings/Settings.vue
index 01f6f672f1..fb49a7b879 100644
--- a/src/components/Settings/Settings.vue
+++ b/src/components/Settings/Settings.vue
@@ -56,7 +56,7 @@ export default {
},
data() {
return {
- isAdmin: getCurrentUser().isAdmin,
+ isAdmin: getCurrentUser()?.isAdmin ?? false,
}
},
methods: {
diff --git a/src/tests/components/Settings/Settings.spec.ts b/src/tests/components/Settings/Settings.spec.ts
index e8ea29b8c1..4b8b4e7ca2 100644
--- a/src/tests/components/Settings/Settings.spec.ts
+++ b/src/tests/components/Settings/Settings.spec.ts
@@ -258,6 +258,54 @@ describe('Settings', () => {
})
})
+ describe('RULE: unauthenticated users (signing via email link) do not crash the component', () => {
+ const createUnauthenticatedWrapper = () => {
+ getCurrentUserMock.mockReturnValue(null)
+
+ return mount(Settings, {
+ global: {
+ stubs: {
+ NcAppNavigationItem: {
+ name: 'NcAppNavigationItem',
+ props: ['name', 'to', 'href', 'icon'],
+ template: '
{{ name }}',
+ },
+ AccountIcon: { template: '' },
+ StarIcon: { template: '' },
+ TuneIcon: { template: '' },
+ },
+ mocks: { t },
+ },
+ })
+ }
+
+ it('mounts without throwing when getCurrentUser returns null', () => {
+ expect(() => createUnauthenticatedWrapper()).not.toThrow()
+ })
+
+ it('isAdmin is false when getCurrentUser returns null', () => {
+ wrapper = createUnauthenticatedWrapper()
+
+ expect(getWrapper().vm.isAdmin).toBe(false)
+ })
+
+ it('hides the Administration link when user is unauthenticated', () => {
+ wrapper = createUnauthenticatedWrapper()
+ const items = getItems()
+ const adminItem = findItemByName(items, 'Administration')
+
+ expect(adminItem).toBeUndefined()
+ })
+
+ it('shows 2 navigation items for unauthenticated user', () => {
+ wrapper = createUnauthenticatedWrapper()
+ const items = getItems()
+
+ // Account + Rate = 2
+ expect(items.length).toBe(2)
+ })
+ })
+
describe('RULE: navigation items count depends on admin status', () => {
it('shows 2 items for non-admin', () => {
wrapper = createWrapper(false)
From 1ec0d5ae1c4b67552f1244fa96f380670d010151 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:25:43 -0300
Subject: [PATCH 2/9] feat(external): add dedicated ExternalApp without
NcContent and reset server.css layout
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/ExternalApp.vue | 40 ++++++++++++++++++++++++++++
src/assets/styles/external-page.scss | 15 +++++++++++
src/external.ts | 3 ++-
3 files changed, 57 insertions(+), 1 deletion(-)
create mode 100644 src/ExternalApp.vue
create mode 100644 src/assets/styles/external-page.scss
diff --git a/src/ExternalApp.vue b/src/ExternalApp.vue
new file mode 100644
index 0000000000..07fd53c6fa
--- /dev/null
+++ b/src/ExternalApp.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/styles/external-page.scss b/src/assets/styles/external-page.scss
new file mode 100644
index 0000000000..d5ed4db0ef
--- /dev/null
+++ b/src/assets/styles/external-page.scss
@@ -0,0 +1,15 @@
+/**
+ * SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+// Override server.css layout rules that assume authenticated header layout.
+// `html body #content` beats the specificity of server.css selectors.
+html body #content {
+ position: fixed;
+ inset: 0;
+ margin: 0;
+ width: 100vw;
+ height: 100vh;
+ border-radius: 0;
+}
diff --git a/src/external.ts b/src/external.ts
index 78d6490d66..a5c2c87158 100644
--- a/src/external.ts
+++ b/src/external.ts
@@ -7,8 +7,9 @@ import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { translate as t, translatePlural as n } from '@nextcloud/l10n'
-import App from './App.vue'
+import App from './ExternalApp.vue'
import router from './router/router'
+import './assets/styles/external-page.scss'
if (window.OCA && !window.OCA.LibreSign) {
Object.assign(window.OCA, { LibreSign: {} })
From 1615798231b6076a85b4d00866b32b4827318b2a Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:25:43 -0300
Subject: [PATCH 3/9] fix(sidebar): add SignPDFExternal to sidebarRoutes to
prevent premature close on route change
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/store/sidebar.js | 2 +-
src/tests/store/sidebar.spec.ts | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/store/sidebar.js b/src/store/sidebar.js
index 5c5b056970..ef261fbe5d 100644
--- a/src/store/sidebar.js
+++ b/src/store/sidebar.js
@@ -9,7 +9,7 @@ export const useSidebarStore = defineStore('sidebar', {
state: () => ({
show: false,
activeTab: '',
- sidebarRoutes: ['fileslist', 'SignPDF', 'ValidationFile', 'IdDocsApprove'],
+ sidebarRoutes: ['fileslist', 'SignPDF', 'SignPDFExternal', 'ValidationFile', 'IdDocsApprove'],
}),
getters: {
diff --git a/src/tests/store/sidebar.spec.ts b/src/tests/store/sidebar.spec.ts
index 7d16da7246..d870076ab8 100644
--- a/src/tests/store/sidebar.spec.ts
+++ b/src/tests/store/sidebar.spec.ts
@@ -213,6 +213,16 @@ describe('sidebar store - visibility rules', () => {
expect(store.show).toBe(true)
})
+ it('keeps sidebar visible for SignPDFExternal route', () => {
+ const store = useSidebarStore()
+ store.show = true
+ store.activeTab = 'sign-tab'
+
+ store.handleRouteChange('SignPDFExternal')
+
+ expect(store.show).toBe(true)
+ })
+
it('hides sidebar for non-allowed routes', () => {
const store = useSidebarStore()
store.show = true
From f7e832a4fb3e00f1397d0632f2b8f02ce7516eeb Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:25:43 -0300
Subject: [PATCH 4/9] fix(sidebar): keep NcAppSidebar mounted via open prop to
avoid null ref during transition
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/components/RightSidebar/RightSidebar.vue | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/components/RightSidebar/RightSidebar.vue b/src/components/RightSidebar/RightSidebar.vue
index cd0f3f1fc9..b0e0b20d02 100644
--- a/src/components/RightSidebar/RightSidebar.vue
+++ b/src/components/RightSidebar/RightSidebar.vue
@@ -3,8 +3,9 @@
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
-
Date: Fri, 27 Feb 2026 14:25:43 -0300
Subject: [PATCH 5/9] fix(sign): keep sidebar open on mobile for external
signing route
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/views/SignPDF/SignPDF.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/views/SignPDF/SignPDF.vue b/src/views/SignPDF/SignPDF.vue
index 1bea844e73..ffff7f45b8 100644
--- a/src/views/SignPDF/SignPDF.vue
+++ b/src/views/SignPDF/SignPDF.vue
@@ -103,7 +103,7 @@ export default {
await this.initIdDocsApprove()
}
- if (this.isMobile){
+ if (this.isMobile && this.$route?.name !== 'SignPDFExternal') {
this.toggleSidebar();
}
From 9d76f6b0f84e471b95e1741560d2d1911197050d Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:33:41 -0300
Subject: [PATCH 6/9] fix: year
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/ExternalApp.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ExternalApp.vue b/src/ExternalApp.vue
index 07fd53c6fa..a18517b56b 100644
--- a/src/ExternalApp.vue
+++ b/src/ExternalApp.vue
@@ -1,5 +1,5 @@
From aeed64fe09f1d051b130086b1d5a49e10012835a Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:46:09 -0300
Subject: [PATCH 7/9] refactor(external): consolidate global CSS overrides into
ExternalApp component
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/ExternalApp.vue | 38 ++++++++++++++++++++++++++------------
1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/src/ExternalApp.vue b/src/ExternalApp.vue
index a18517b56b..69195a27e8 100644
--- a/src/ExternalApp.vue
+++ b/src/ExternalApp.vue
@@ -18,23 +18,37 @@ defineOptions({ name: 'LibreSignExternal' })
import RightSidebar from './components/RightSidebar/RightSidebar.vue'
+
+
From 777516f2644169a71ccccc5224e97056e2df5fb6 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:46:09 -0300
Subject: [PATCH 8/9] refactor(external): remove external-page.scss, styles
moved into ExternalApp component
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/assets/styles/external-page.scss | 15 ---------------
1 file changed, 15 deletions(-)
delete mode 100644 src/assets/styles/external-page.scss
diff --git a/src/assets/styles/external-page.scss b/src/assets/styles/external-page.scss
deleted file mode 100644
index d5ed4db0ef..0000000000
--- a/src/assets/styles/external-page.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2026 LibreCode coop and LibreCode contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-// Override server.css layout rules that assume authenticated header layout.
-// `html body #content` beats the specificity of server.css selectors.
-html body #content {
- position: fixed;
- inset: 0;
- margin: 0;
- width: 100vw;
- height: 100vh;
- border-radius: 0;
-}
From f991658b57b2193cb683bb6bca0402878ca82a48 Mon Sep 17 00:00:00 2001
From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Date: Fri, 27 Feb 2026 14:46:09 -0300
Subject: [PATCH 9/9] refactor(external): remove external-page.scss import
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
---
src/external.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/external.ts b/src/external.ts
index a5c2c87158..d3cd5510dd 100644
--- a/src/external.ts
+++ b/src/external.ts
@@ -9,7 +9,6 @@ import { translate as t, translatePlural as n } from '@nextcloud/l10n'
import App from './ExternalApp.vue'
import router from './router/router'
-import './assets/styles/external-page.scss'
if (window.OCA && !window.OCA.LibreSign) {
Object.assign(window.OCA, { LibreSign: {} })