Files
Mitgliederverwaltung/.plans/done/issue-52-filelink-fileexplorer.md
T
shahondin1624 b29a268b1d Restructure .plans/ into done/ and open/ subdirectories
- Move completed plan files to .plans/done/
- Move 18 open plan files to .plans/open/
- Update .gitignore to exclude .verified_plans temp file
- Verified all 18 open plans still describe unimplemented issues
2026-04-28 20:30:55 +02:00

92 lines
5.8 KiB
Markdown

# Implementation Plan: Issue #52 - FileLinkService + FileExplorer component
## Summary
Implement a FileLinkService for managing Nextcloud file folder links per member/Lager, with configurable path patterns, auto-create toggle, and a FileExplorer Vue component that displays folder contents inline on the member detail page "Dateien" tab. This integrates with Nextcloud's `IRootFolder` for file system operations.
## Files to Create/Modify
### New Files
1. **`lib/Service/FileLinkService.php`** - Core service for member/Lager folder path computation, folder listing, and auto-creation via `IRootFolder`
2. **`lib/Controller/FileController.php`** - REST controller for file browsing and settings endpoints
3. **`src/components/FileExplorer.vue`** - Vue 3 inline file browser component for member detail "Dateien" tab
4. **`src/stores/files.js`** - Pinia store for file browsing state
5. **`tests/Unit/FileLinkServiceTest.php`** - Unit tests for path computation and validation
### Modified Files
6. **`appinfo/routes.php`** - Add file-related API routes
7. **`src/views/MemberDetail.vue`** - Wire up "Dateien" tab to use FileExplorer component instead of placeholder
## Step-by-Step Plan
### Step 1: Create FileLinkService.php
- Constructor: inject `IRootFolder`, `IConfig` (for app settings), `LoggerInterface`
- `getMemberFolderPath(Member $member): string` - compute path from configurable base + pattern (e.g., `/Verein/Mitglieder/{Nachname}_{Vorname}/`)
- `ensureMemberFolder(Member $member): bool` - create folder if auto-create enabled and folder doesn't exist; return true if folder exists after call
- `listFolderContents(string $path): array` - list files in a folder via `IRootFolder->getUserFolder()`, return array of `[name, size, mtime, mimetype, path]`
- `getMemberFiles(Member $member): array` - convenience: compute path + list contents
- `getLagerFolderPath(Lager $lager): string` - same pattern for Lager folders
- Settings helpers: `getBasePath(): string`, `getSubfolderPattern(): string`, `isAutoCreateEnabled(): bool`, `getLagerBasePath(): string`
- Settings are stored via `IConfig::setAppValue/getAppValue` with keys:
- `file_base_path` (default: `/Verein/Mitglieder/`)
- `file_subfolder_pattern` (default: `{Nachname}_{Vorname}`)
- `file_auto_create` (default: `false`)
- `file_lager_base_path` (default: `/Verein/Lager/`)
### Step 2: Create FileController.php
Following exact pattern from LagerController:
- Constructor: inject `FileLinkService`, `IRequest`, `LoggerInterface`
- `getSettings()` - GET `/api/v1/files/settings` - return current file settings
- `updateSettings()` - PUT `/api/v1/files/settings` - update file settings (admin only)
- `memberFiles(int $memberId)` - GET `/api/v1/members/{memberId}/files` - list member folder contents
- `ensureFolder(int $memberId)` - POST `/api/v1/members/{memberId}/files/ensure-folder` - create member folder if auto-create
- `lagerFiles(int $lagerId)` - GET `/api/v1/lager/{lagerId}/files/browse` - browse Lager folder
### Step 3: Add Routes to appinfo/routes.php
Add file-related routes in a new section:
```php
// -- Files integration ---
['name' => 'file#getSettings', 'url' => '/api/v1/files/settings', 'verb' => 'GET'],
['name' => 'file#updateSettings', 'url' => '/api/v1/files/settings', 'verb' => 'PUT'],
['name' => 'file#memberFiles', 'url' => '/api/v1/members/{memberId}/files', 'verb' => 'GET'],
['name' => 'file#ensureFolder', 'url' => '/api/v1/members/{memberId}/files/ensure-folder', 'verb' => 'POST'],
['name' => 'file#lagerFiles', 'url' => '/api/v1/lager/{lagerId}/files/browse', 'verb' => 'GET'],
```
### Step 4: Create Pinia store (src/stores/files.js)
- State: `files`, `loading`, `error`, `folderExists`, `settings`
- Actions: `fetchMemberFiles(memberId)`, `ensureMemberFolder(memberId)`, `fetchSettings()`, `updateSettings(settings)`
- Uses axios pattern consistent with other stores (e.g., members.js)
### Step 5: Create FileExplorer.vue component
- Props: `memberId` (Number, required)
- On mount: fetch member files via store
- Display: table with columns: Dateiname, Groesse, Geaendert
- Click file: open in NC Files (new tab via `/apps/files/?dir=...&openfile=...`)
- "In Nextcloud Files oeffnen" button: navigate to folder in NC Files
- Warning banner when folder doesn't exist + "Ordner erstellen" button (calls ensureFolder)
- Loading spinner while fetching
- Empty state when folder exists but is empty
- Uses `@nextcloud/vue` NcButton, NcLoadingIcon, NcEmptyContent
### Step 6: Wire MemberDetail.vue "Dateien" tab
- Import FileExplorer component
- In the tab content section, when `activeTab === 'files'`, render `<FileExplorer :member-id="Number(id)" />`
- Remove the placeholder "Wird in einem spaeteren Milestone implementiert" for the files tab
### Step 7: Create Unit Test
- Test `getMemberFolderPath()` with various patterns
- Test path pattern substitution
- Test settings defaults
## Acceptance Criteria Verification Checklist
1. [ ] FileLinkService.php exists with getMemberFolderPath, ensureMemberFolder, listFolderContents, getMemberFiles, getLagerFolderPath methods
2. [ ] FileController.php exists with getSettings, updateSettings, memberFiles, ensureFolder, lagerFiles endpoints
3. [ ] Routes added to appinfo/routes.php for all 5 file endpoints
4. [ ] FileExplorer.vue component renders file list with name, size, modified date columns
5. [ ] FileExplorer.vue shows warning state when folder doesn't exist
6. [ ] FileExplorer.vue has "In Nextcloud Files oeffnen" button that generates correct NC Files URL
7. [ ] MemberDetail.vue "Dateien" tab renders FileExplorer instead of placeholder
8. [ ] Settings configurable: base path, subfolder pattern, auto-create toggle, Lager base path
9. [ ] Pinia store (files.js) manages state for file browsing
10. [ ] Unit test validates path computation logic