Files
Mitgliederverwaltung/.plans/done/issue-52-filelink-fileexplorer.md
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

5.8 KiB

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

  1. appinfo/routes.php - Add file-related API routes
  2. 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:

// -- 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