feat(nextcloud-files): Teilen in Modul-Changelog, Anleitungen und Changelog

- Modul-Changelog: unveroeffentlichter Eintrag 1.0.0 um drei Teilen-Punkte erweitert (Version bleibt 1.0.0)
- CHANGELOG.md: Dateien, Teilen und Dateien, Uebersicht der Freigaben
- Anleitungen Anwender, Administration, Betrieb, Entwicklung beschreiben das Teilen
- Live-Test e2e-shares.sh: Abschnitt version
- Teilen-Dialog: Linkformular scrollt beim Oeffnen in den sichtbaren Bereich
- Mit mir geteilt: offene Freigaben zeigen kein irrefuehrendes Eigene Berechtigung

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-09 11:46:18 +02:00
parent c3adf851b8
commit 0585fd17c4
11 changed files with 129 additions and 5 deletions
@@ -207,4 +207,17 @@ describe('Seeds und Changelogs gehören zusammen', () => {
expect(changelog![0].date).toBe('2026-10-08');
}
});
it('nextcloud-files: das Teilen steht im unveröffentlichten Eintrag 1.0.0 (sieben Neu-Punkte, keine 1.1.0)', () => {
const changelog = MODULE_CHANGELOGS.get('nextcloud-files') ?? [];
expect(changelog).toHaveLength(1);
const changes = changelog[0].changes;
expect(changes).toHaveLength(7);
expect(changes.every((c) => c.kind === 'new')).toBe(true);
expect(changes.slice(4).map((c) => c.de)).toEqual([
expect.stringContaining('Kolleginnen, Kollegen und Gruppen'),
expect.stringContaining('Öffentliche Links'),
expect.stringContaining('„Von mir geteilt“ und „Mit mir geteilt“'),
]);
});
});
@@ -30,6 +30,21 @@ export const NEXTCLOUD_FILES_CHANGELOG: ModuleChangelog = [
de: 'Dateien und ganze Ordner per Ziehen und Ablegen hochladen, große Dateien in Stücken; die Übertragungsleiste zeigt den Fortschritt und lässt Abbrechen und Wiederholen zu. Tessera überschreibt nie stillschweigend, und Ordner und Mehrfachauswahl laden Sie als ZIP-Datei herunter.',
en: 'Upload files and whole folders by drag and drop, large files in chunks; the transfer bar shows progress and allows cancelling and retrying. Tessera never overwrites silently, and you download folders and multiple selections as a ZIP file.',
},
{
kind: 'new',
de: 'Dateien und Ordner mit Kolleginnen, Kollegen und Gruppen aus Ihrer Nextcloud teilen, wahlweise zum Ansehen oder Bearbeiten; Berechtigungen ändern und Freigaben wieder entfernen.',
en: 'Share files and folders with colleagues and groups from your Nextcloud, either to view or to edit; change permissions and remove shares again.',
},
{
kind: 'new',
de: 'Öffentliche Links erstellen und kopieren, für Ordner auch zum reinen Hochladen; Passwort und Ablaufdatum folgen den Regeln Ihrer Nextcloud.',
en: 'Create and copy public links, for folders also upload-only; password and expiry date follow the rules of your Nextcloud.',
},
{
kind: 'new',
de: 'Die Ansichten „Von mir geteilt“ und „Mit mir geteilt“ zeigen alle Freigaben, und geteilte Einträge sind in der Dateiliste markiert.',
en: 'The views “Shared by me” and “Shared with me” list all shares, and shared items are marked in the file list.',
},
],
},
];
@@ -1,7 +1,7 @@
'use client';
import { useLocale, useTranslations } from 'next-intl';
import { useId, useRef, useState } from 'react';
import { useEffect, useId, useRef, useState } from 'react';
import { copyText } from '@/components/nextcloud-files/clipboard';
import {
accessOptions,
@@ -75,6 +75,7 @@ export function LinkShareForm({
const [showPassword, setShowPassword] = useState(false);
const [passwordCopied, setPasswordCopied] = useState(false);
const passwordInput = useRef<HTMLInputElement>(null);
const formRef = useRef<HTMLFormElement>(null);
const passwordFieldVisible = editing ? editPassword === 'change' : createPasswordOn;
// --- Ablauf ---
@@ -127,11 +128,18 @@ export function LinkShareForm({
setPasswordCopied(ok);
};
// Das Formular steht unter den Personen im scrollbaren Dialog: beim Öffnen ganz in den
// sichtbaren Bereich holen, damit „Link erstellen“ und „Abbrechen“ nicht verborgen bleiben.
useEffect(() => {
formRef.current?.scrollIntoView?.({ block: 'nearest' });
}, []);
const passwordLabel = required ? t('passwordRequiredLabel') : t('passwordLabel');
const expiryLabel = rule.required ? t('expiryRequiredLabel') : t('expiryLabel');
return (
<form
ref={formRef}
onSubmit={submit}
aria-label={editing ? t('titleEdit') : t('titleCreate')}
className="mt-3 space-y-4 rounded border border-border bg-well/40 p-3"
@@ -283,6 +283,8 @@ describe('SharesView — Mit mir geteilt', () => {
name: 'Ben-Briefkasten',
target: '/Ben-Briefkasten',
pending: true,
permissions: 0,
access: 'custom',
});
mockReceived.mockResolvedValue({
shares: [incoming()],
@@ -292,6 +294,8 @@ describe('SharesView — Mit mir geteilt', () => {
});
mount('withMe');
expect(await screen.findByText('Noch nicht angenommen')).toBeTruthy();
// die Nextcloud meldet offene Freigaben ohne Berechtigung: kein irreführendes "Eigene Berechtigung"
expect(screen.queryByText(/Eigene Berechtigung/)).toBeNull();
// keine "Öffnen"-Aktion fuer eine offene Freigabe
expect(screen.queryByRole('button', { name: '„Ben-Briefkasten“ öffnen' })).toBeNull();
@@ -200,7 +200,9 @@ export function SharesView({
<p className="truncate text-xs text-muted-foreground">
{[
share.ownerName ? t('from', { name: share.ownerName }) : null,
accessText(share),
// Eine noch offene Freigabe meldet die Nextcloud ohne Berechtigung (gemessen: 0);
// „Eigene Berechtigung“ wäre dort irreführend, also steht nichts.
isPending ? null : accessText(share),
share.expiration
? tShare('people.expires', { date: dateText(share.expiration, locale) })
: null,