Commit Graph

213 Commits

Author SHA1 Message Date
Dev
df5f79d1cb feat(03-04): implement DuplicatesService composite key grouping for files and folders
- File mode: Search API KQL pagination matching SearchService pattern
- Folder mode: CAML FSObjType=1 via SharePointPaginationHelper.GetAllItemsAsync
- MakeKey composite key (name+size+dates+counts) matches DuplicatesServiceTests scaffold
- Groups only items with count >= 2, ordered by group size then name
- ExtractLibraryFromPath derives library name from path relative to site URL
- SelectProperties added per-item (StringCollection has no AddRange)
2026-04-02 15:31:57 +02:00
Dev
938de30437 feat(03-06): add Phase 3 EN/FR localization keys for Storage, Search, and Duplicates tabs
- Added 14 Storage tab keys (chk.per.lib, chk.subsites, stor.note, btn.gen.storage, btn.open.storage, stor.col.*, stor.rad.*)
- Added 26 File Search tab keys (grp.search.filters, lbl.extensions, ph.extensions, lbl.regex, ph.regex, date filters, lbl/ph.library, lbl.max.results, lbl.site.url, btn.run.search, btn.open.search, srch.col.*, srch.rad.*)
- Added 14 Duplicates tab keys (grp.dup.type, rad.dup.*, grp.dup.criteria, lbl.dup.note, chk.dup.*, chk.include.subsites, ph.dup.lib, btn.run.scan, btn.open.results)
- Matching FR translations in Strings.fr.resx with proper French text
- 54 new static properties in Strings.Designer.cs (dot-to-underscore naming convention)
2026-04-02 15:31:25 +02:00
Dev
9e3d5016e6 feat(03-04): implement SearchService KQL pagination with 500-row batches and 50,000 hard cap
- KQL builder for extension, date, creator, editor, library filters
- Pagination via StartRow += 500, stops at MaxStartRow or MaxResults
- Filters _vti_history/ version history paths from results
- Client-side Regex filter on file name and title
- ValidateKqlLength enforces 4096-char SharePoint limit
- SelectProperties added one-by-one (StringCollection has no AddRange)
2026-04-02 15:30:44 +02:00
Dev
eafaa15459 feat(03-03): implement StorageHtmlExportService
- Replace string.Empty stub with full BuildHtml implementation
- Self-contained HTML with inline CSS and JS — no external dependencies
- toggle(i) JS function with collapsible subfolder rows (sf-{i} IDs)
- _togIdx counter reset at start of each BuildHtml call (per PS pattern)
- RenderNode/RenderChildNode for recursive tree rendering
- FormatSize helper: B/KB/MB/GB adaptive display
- HtmlEncode via System.Net.WebUtility
- Add explicit System.IO using (required in WPF project)
2026-04-02 15:30:34 +02:00
Dev
94ff181035 feat(03-03): implement StorageCsvExportService
- Replace string.Empty stub with full BuildCsv implementation
- UTF-8 BOM header row: Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified
- RFC 4180 CSV quoting via Csv() helper
- FormatMb() converts bytes to MB with 2 decimal places
- Add explicit System.IO using (required in WPF project)
2026-04-02 15:29:45 +02:00
Dev
3730b54527 docs(03-02): complete StorageService plan — CSOM scan engine implemented
- Create 03-02-SUMMARY.md with full deviation and test documentation
- Update STATE.md: position = 03-02 complete, Wave 2 next (03-03/04/06)
- Update ROADMAP.md: Phase 3 at 2/8 plans complete
- Note auto-fixed Rule 3 blocker: created missing 03-01 export stubs and test scaffolds
2026-04-02 15:28:08 +02:00
Dev
556fad1377 docs(03-01): complete Wave 0 plan — models, interfaces, export stubs, test scaffolds
- 7 pure-logic tests pass (VersionSizeBytes + MakeKey composite key)
- 0 build errors, 15 export tests fail as expected (stubs)
- 12 requirements marked complete (STOR-01/05, SRCH-01/04, DUPL-01/03)
2026-04-02 15:27:35 +02:00
Dev
b5df0641b0 feat(03-02): implement StorageService CSOM StorageMetrics scan engine
- Add StorageService implementing IStorageService
- Load Folder.StorageMetrics, TimeLastModified, Name, ServerRelativeUrl in one CSOM round-trip per folder
- CollectStorageAsync returns one StorageNode per document library at IndentLevel=0
- With FolderDepth>0, CollectSubfoldersAsync recurses into child folders
- All CSOM calls use ExecuteQueryRetryHelper.ExecuteQueryRetryAsync (3 call sites)
- System/hidden lists skipped (Hidden=true or BaseType != DocumentLibrary)
- Forms/ and _-prefixed system folders skipped during subfolder recursion
- ct.ThrowIfCancellationRequested() called at top of every recursive step
2026-04-02 15:26:16 +02:00
Dev
08e4d2ee7d feat(03-01): create Phase 3 export stubs and test scaffolds
- Add StorageCsvExportService, StorageHtmlExportService stub (Plan 03-03)
- Add SearchCsvExportService, SearchHtmlExportService stub (Plan 03-05)
- Add DuplicatesHtmlExportService stub (Plan 03-05)
- Add StorageServiceTests, SearchServiceTests, DuplicatesServiceTests scaffolds
- Add export test scaffolds for all 4 Phase 3 export services
- 7 pure-logic tests pass (VersionSizeBytes + MakeKey); 4 CSOM stubs skip
2026-04-02 15:25:20 +02:00
Dev
b52f60f8eb feat(03-01): create 7 core models and 3 service interfaces for Phase 3
- StorageNode, StorageScanOptions models
- SearchResult, SearchOptions models
- DuplicateItem, DuplicateGroup, DuplicateScanOptions models
- IStorageService, ISearchService, IDuplicatesService interfaces
2026-04-02 15:23:04 +02:00
Dev
d09db015f2 docs(phase-03): research storage, search, and duplicate detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:41:39 +02:00
Dev
20780318a3 docs(phase-02): complete phase execution — 7/7 verified, advancing to phase 03
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:29:22 +02:00
Dev
80a3873a15 fix(02-07): bind export buttons to localization keys (rad.csv.perms, rad.html.perms)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:29:03 +02:00
Dev
6e9a0033f2 docs(02-07): complete Permissions integration plan — Phase 2 done
- Created 02-07-SUMMARY.md: PermissionsView XAML wired into MainWindow, all Phase 2 DI registered, human-verified
- Updated STATE.md: Phase 2 complete, 16/22 plans done, new decisions recorded
- Updated ROADMAP.md: Phase 2 all 7 plans checked, status Complete 2026-04-02

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:21:18 +02:00
Dev
afe69bd37f feat(02-07): create PermissionsView XAML + code-behind and register DI
- Created PermissionsView.xaml with left scan-config panel and right results DataGrid
- Created PermissionsView.xaml.cs wiring ViewModel via IServiceProvider, factory for SitePickerDialog
- Updated App.xaml.cs: registered IPermissionsService, ISiteListService, CsvExportService,
  HtmlExportService, PermissionsViewModel, PermissionsView, SitePickerDialog, and
  Func<TenantProfile, SitePickerDialog> factory; also registered ISessionManager -> SessionManager
- Updated MainWindow.xaml: replaced FeatureTabBase stub with named PermissionsTabItem
- Updated MainWindow.xaml.cs: wires PermissionsTabItem.Content from DI-resolved PermissionsView
- Added CurrentProfile public accessor, SitesSelectedLabel computed property, and
  IsMaxDepth toggle property to PermissionsViewModel
- Build: 0 errors, 0 warnings. Tests: 60 passed, 3 skipped (live/interactive)
2026-04-02 14:13:45 +02:00
Dev
e74cffbe31 docs(02-06): complete PermissionsViewModel and SitePickerDialog plan
- Add 02-06-SUMMARY.md with TDD results and deviation documentation
- Update STATE.md: progress bar 87%, record metrics, ISessionManager decision
- Update ROADMAP.md: phase 02-permissions now 6/7 summaries (In Progress)
2026-04-02 14:09:06 +02:00
Dev
f98ca60990 feat(02-06): implement PermissionsViewModel with multi-site scan and SitePickerDialog
- PermissionsViewModel extends FeatureViewModelBase, implements RunOperationAsync
- Multi-site mode: loops SelectedSites; single-site mode: uses SiteUrl
- ExportCsvCommand and ExportHtmlCommand enabled only when Results.Count > 0
- OpenSitePickerCommand uses dialog factory pattern (Func<Window>?)
- OnTenantSwitched clears Results, SiteUrl, SelectedSites
- Flat ObservableProperty booleans (IncludeInherited, ScanFolders, etc.) build ScanOptions record
- SitePickerDialog XAML: filterable list with CheckBox column, Title, URL columns
- SitePickerDialog code-behind: loads sites on Window.Loaded, exposes SelectedUrls
- ISessionManager interface extracted for testability (SessionManager implements it)
- StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl test passes (60/60 + 3 skip)
2026-04-02 14:06:39 +02:00
Dev
c462a0b310 test(02-06): add failing test for PermissionsViewModel multi-site scan
- Write StartScanAsync_WithMultipleSiteUrls_CallsServiceOncePerUrl test (RED)
- Create ISessionManager interface for testability
- Implement ISessionManager on SessionManager
- Add PermissionsViewModel stub (NotImplementedException) to satisfy compile
2026-04-02 14:04:22 +02:00
Dev
48ccf5891b docs(02-04): add self-check result to SUMMARY.md 2026-04-02 14:01:24 +02:00
Dev
7805e0b015 docs(02-04): complete export services plan — CsvExportService and HtmlExportService
- SUMMARY.md created for plan 02-04
- STATE.md: progress updated to 87%, session recorded, decision added
- ROADMAP.md: phase 02 progress updated (5/7 plans complete)
2026-04-02 14:01:12 +02:00
Dev
e3ab31937a feat(02-04): implement HtmlExportService with self-contained interactive HTML report
- Stats cards: Total Entries, Unique Permission Sets, Distinct Users/Groups
- Type badges: site-coll (blue), site (green), list (amber), folder (gray)
- Unique/Inherited badges based on HasUniquePermissions flag
- User pills with external-user CSS class for #EXT# logins
- Inline JS filterTable() function for client-side row filtering
- WriteAsync uses UTF-8 without BOM for HTML
- All 3 HtmlExportServiceTests pass
2026-04-02 13:59:46 +02:00
Dev
44913f8075 feat(02-04): implement CsvExportService with Merge-PermissionRows port
- GroupBy (Users, PermissionLevels, GrantedThrough) to merge duplicate entries
- Pipe-joins URLs and Titles for merged rows
- RFC 4180 CSV escaping: all fields double-quoted, internal quotes doubled
- WriteAsync uses UTF-8 with BOM for Excel compatibility
- All 3 CsvExportServiceTests pass
2026-04-02 13:58:39 +02:00
Dev
ac86bbc302 docs(02-02): complete PermissionsService plan — models, interface, scan engine
- Created 02-02-SUMMARY.md with full execution record
- Updated STATE.md with decisions (CSOM type constraints) and progress
- Updated ROADMAP.md (phase 02: 4/7 summaries, In Progress)
- Marked PERM-07 complete in REQUIREMENTS.md
2026-04-02 13:56:53 +02:00
Dev
0480f97059 docs(02-01): complete Wave 0 test scaffold plan
- 02-01-SUMMARY.md: classification helper + 5 test scaffolds across PERM-01..06
- STATE.md: progress 73%, decisions logged, session updated
- ROADMAP.md: phase 02 progress 4/7 summaries
2026-04-02 13:56:02 +02:00
Dev
9f2e2f9899 fix(02-01): add export service stubs and fix PermissionsService compile errors
[Rule 3 - Blocking] CsvExportService/HtmlExportService stubs added so export test
files compile. [Rule 1 - Bug] PermissionsService: removed Principal.Email (not on
Principal, only on User) and changed folder param from Folder to ListItem (SecurableObject).
2026-04-02 13:53:45 +02:00
Dev
d17689cc46 docs(02-03): complete SiteListService plan
- 02-03-SUMMARY.md created
- STATE.md: progress updated 67%, decisions added, session recorded
- ROADMAP.md: phase 2 progress updated (2/7 summaries)
2026-04-02 13:52:17 +02:00
Dev
c04d88882d docs(02-05): complete Phase 2 localization keys plan
- Added 02-05-SUMMARY.md with 15 EN+FR localization keys plan results
- Updated STATE.md progress (67%), session, and metrics
- Updated ROADMAP.md phase 02 progress (2/7 summaries)
- Marked PERM-01, PERM-02, PERM-04, PERM-05, PERM-06 requirements complete
2026-04-02 13:52:03 +02:00
Dev
83464a009c test(02-01): scaffold export service test stubs for PERM-05 and PERM-06
- CsvExportServiceTests.cs: 3 real [Fact] tests (header row, empty list, merge rows) — PERM-05
- HtmlExportServiceTests.cs: 3 real [Fact] tests (user names, empty HTML, external user marker) — PERM-06
- Both files reference CsvExportService/HtmlExportService from Plan 03 — compile errors expected until Plan 03 creates the services
2026-04-02 13:51:54 +02:00
Dev
4a6594d9e8 feat(02-02): define PermissionEntry, ScanOptions, and IPermissionsService
- PermissionEntry record with 9 fields matching PS Generate-PnPSitePermissionRpt
- ScanOptions record with defaults: IncludeInherited=false, ScanFolders=true, FolderDepth=1, IncludeSubsites=false
- IPermissionsService interface with ScanSiteAsync method enabling ViewModel mocking
2026-04-02 13:51:15 +02:00
Dev
57c258015b feat(02-05): add 15 Phase 2 localization keys to EN/FR resx and Designer
- Added 15 keys to Strings.resx with English values (grp.scan.opts, chk.scan.folders, chk.recursive, lbl.folder.depth, chk.max.depth, chk.inherited.perms, grp.export.fmt, rad.csv.perms, rad.html.perms, btn.gen.perms, btn.open.perms, btn.view.sites, perm.site.url, perm.or.select, perm.sites.selected)
- Added same 15 keys to Strings.fr.resx with genuine French translations (no English fallback)
- Added 15 static properties to Strings.Designer.cs following dot-to-underscore naming pattern
2026-04-02 13:50:43 +02:00
Dev
a9f6bde686 test(02-01): scaffold PermissionsService, ViewModel, and classification test stubs
- PermissionEntryHelper.cs: pure static IsExternalUser, FilterPermissionLevels, IsSharingLinksGroup
- PermissionEntryClassificationTests.cs: 7 real [Fact] tests — all passing immediately
- PermissionsServiceTests.cs: 2 stubs (PERM-01, PERM-04) skipped until Plan 02 CSOM impl
- PermissionsViewModelTests.cs: 1 stub (PERM-02) skipped until Plan 02 ViewModel impl
2026-04-02 13:50:41 +02:00
Dev
78b3d4f759 feat(02-03): implement ISiteListService and SiteListService with admin URL derivation
- SiteInfo record added to Core/Models
- ISiteListService interface with GetSitesAsync signature
- SiteListService derives admin URL via Regex, connects via SessionManager
- Filters to Active sites only, excludes OneDrive personal (-my.sharepoint.com)
- Access denied ServerException wrapped as InvalidOperationException with actionable message
- DeriveAdminUrl marked internal static for unit testability
- InternalsVisibleTo added to AssemblyInfo.cs to expose internal to test project
- 2 DeriveAdminUrl tests pass; full suite: 53 pass, 4 skip, 0 fail
2026-04-02 13:50:35 +02:00
Dev
5c10840581 test(02-03): add failing tests for SiteListService.DeriveAdminUrl
- Two tests for DeriveAdminUrl: standard URL and trailing-slash URL
- Tests fail (RED) — SiteListService not yet implemented
2026-04-02 13:49:16 +02:00
Dev
097d7b3326 docs(phase-02): add research, validation strategy, and 7 plans for Permissions phase
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:40:33 +02:00
Dev
55819bd059 docs(02-permissions): create phase 2 plan — 7 plans across 4 waves
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:38:09 +02:00
Dev
031a7dbc0f docs(phase-02): research permissions phase domain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:25:38 +02:00
Dev
27d654d86a docs(phase-01): complete phase execution — 11/11 verified, advancing to phase 02 2026-04-02 13:02:50 +02:00
Dev
62a7deb6e9 docs(01-08): complete plan — human visual checkpoint approved, Phase 1 Foundation done
- Task 2 (human-verify checkpoint) approved: all 7 visual checks passed
- Updated SUMMARY to document 3 runtime fix commits (DI registration, FR translations)
- STATE.md: plan complete, session updated
- ROADMAP.md: Phase 1 confirmed Complete 8/8

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 12:55:22 +02:00
Dev
0b8a86a58a fix(01-08): add real French translations (stubs were identical to English) 2026-04-02 12:52:16 +02:00
Dev
6211f65a5e fix(01-08): provide file paths to ProfileRepository and SettingsRepository via factory registration 2026-04-02 12:47:11 +02:00
Dev
c66efdadfa fix(01-08): register ProfileRepository and SettingsRepository in DI container 2026-04-02 12:45:59 +02:00
Dev
991c92e83a docs(01-08): complete phase 1 final verification plan — awaiting human checkpoint
- 44/44 non-interactive tests pass, 1 MSAL interactive skip (expected)
- Build: 0 errors, 0 warnings with -warnaserror
- ROADMAP.md: Phase 1 marked Complete (8/8 summaries)
- STATE.md: progress 100%, decisions recorded, checkpoint pause noted
- SUMMARY.md created for 01-08

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 12:43:28 +02:00
Dev
334a5f10ad chore(01-08): run full test suite — 44 passed, 1 skipped, 0 failed
- dotnet build with -warnaserror: 0 warnings, 0 errors
- 44 unit/integration tests pass
- 1 interactive MSAL test skipped (expected)
- Build clean on SharepointToolbox.slnx
2026-04-02 12:41:55 +02:00
Dev
405a013375 docs(01-07): complete ProfileManagementDialog + SettingsView plan
- Create 01-07-SUMMARY.md with full documentation
- Update STATE.md: progress 88%, decisions added, session recorded
- Update ROADMAP.md: Phase 1 at 7/8 plans
2026-04-02 12:40:36 +02:00
Dev
0665152e0d feat(01-07): add SettingsView and wire into MainWindow Settings tab
- Create Views/Tabs/SettingsView.xaml (UserControl with language ComboBox en/fr, DataFolder TextBox and Browse button using TranslationSource)
- Create Views/Tabs/SettingsView.xaml.cs (DI constructor injection of SettingsViewModel, LoadAsync on Loaded)
- Update MainWindow.xaml to add xmlns:views namespace and clear placeholder TextBlock from SettingsTabItem
- Register SettingsView as Transient in DI; resolve and set as SettingsTabItem.Content from MainWindow constructor
- All 42 unit tests pass, 0 build errors
2026-04-02 12:38:38 +02:00
Dev
cb7cf93c52 feat(01-07): add ProfileManagementDialog with DI factory wiring
- Create Views/Dialogs/ProfileManagementDialog.xaml (modal Window with Name/TenantUrl/ClientId fields and TranslationSource bindings)
- Create Views/Dialogs/ProfileManagementDialog.xaml.cs (DI constructor injection, LoadAsync on Loaded)
- Add OpenProfileManagementDialog factory delegate to MainWindowViewModel
- Wire ManageProfilesCommand to open dialog via factory, reload profiles after close
- Register ProfileManagementDialog as Transient in DI (App.xaml.cs)
- Inject IServiceProvider into MainWindow constructor for DI-resolved dialog factory
2026-04-02 12:38:31 +02:00
Dev
b41599d95a docs(01-06): complete WPF shell plan — SUMMARY, STATE, ROADMAP updated
- 2 tasks completed, 12 files modified
- 6 FeatureViewModelBase unit tests added and passing
- Full WPF shell with FeatureTabBase, MainWindowViewModel, LogPanelSink wiring
2026-04-02 12:34:58 +02:00
Dev
5920d42614 feat(01-06): build WPF shell — MainWindow XAML, ViewModels, LogPanelSink wiring
- Add FeatureTabBase UserControl with ProgressBar/TextBlock/CancelButton strip
  (Visibility bound to IsRunning, shown only during operations)
- Add MainWindowViewModel with TenantProfiles ObservableCollection, ConnectCommand,
  ClearSessionCommand, ManageProfilesCommand, ProgressUpdatedMessage subscription
- Add ProfileManagementViewModel wrapping ProfileService CRUD with input validation
- Add SettingsViewModel (extends FeatureViewModelBase) with language/folder settings
- Update MainWindow.xaml: DockPanel shell with Toolbar, TabControl (8 tabs), 150px
  RichTextBox LogPanel, StatusBar (tenant name | ProgressStatus | ProgressPercentage)
- MainWindow.xaml.cs: DI constructor, DataContext=viewModel, LoadProfilesAsync on Loaded
- App.xaml.cs: register all services, wire LogPanelSink after MainWindow resolved,
  register DispatcherUnhandledException and UnobservedTaskException global handlers
- App.xaml: add BoolToVisibilityConverter resource
2026-04-02 12:32:41 +02:00
Dev
3c09155648 feat(01-06): implement FeatureViewModelBase with async/cancel/progress pattern
- Add ProgressUpdatedMessage ValueChangedMessage for StatusBar live updates
- Add FeatureViewModelBase with CancellationTokenSource lifecycle, IsRunning,
  IProgress<OperationProgress>, OperationCanceledException handling
- Add 6 unit tests covering lifecycle, progress, cancellation, error handling
  and CanExecute guard
2026-04-02 12:29:38 +02:00
Dev
fcae8f0e49 docs(01-04): complete auth layer plan
- 01-04-SUMMARY.md: MsalClientFactory + SessionManager auth layer
- STATE.md: progress updated to 63%, decisions added, session recorded
- ROADMAP.md: phase 1 progress updated (5/8 summaries)
- REQUIREMENTS.md: FOUND-03 and FOUND-04 marked complete
2026-04-02 12:26:55 +02:00