feat(04-07): add Phase 4 localization, shared dialogs, and example CSV resources

- Add 80+ Phase 4 EN/FR localization keys to Strings.resx and Strings.fr.resx (tabs, transfer, bulkmembers, bulksites, folderstruct, templates, bulk-shared, folderbrowser)
- Add ResourceManager property accessors for all new keys to Strings.Designer.cs
- Create ConfirmBulkOperationDialog (XAML + code-behind) with Proceed/Cancel buttons
- Create FolderBrowserDialog (XAML + code-behind) with lazy-loading TreeView of SharePoint libraries/folders
- Bundle bulk_add_members.csv, bulk_create_sites.csv, folder_structure.csv as EmbeddedResource in csproj
This commit is contained in:
Dev
2026-04-03 10:13:39 +02:00
parent fdb1108e76
commit 1a2cc13224
11 changed files with 507 additions and 0 deletions

View File

@@ -136,5 +136,96 @@ namespace SharepointToolbox.Localization {
public static string ph_dup_lib => ResourceManager.GetString("ph.dup.lib", resourceCulture) ?? string.Empty; public static string ph_dup_lib => ResourceManager.GetString("ph.dup.lib", resourceCulture) ?? string.Empty;
public static string btn_run_scan => ResourceManager.GetString("btn.run.scan", resourceCulture) ?? string.Empty; public static string btn_run_scan => ResourceManager.GetString("btn.run.scan", resourceCulture) ?? string.Empty;
public static string btn_open_results => ResourceManager.GetString("btn.open.results", resourceCulture) ?? string.Empty; public static string btn_open_results => ResourceManager.GetString("btn.open.results", resourceCulture) ?? string.Empty;
// Phase 4: Tab headers
public static string tab_transfer => ResourceManager.GetString("tab.transfer", resourceCulture) ?? string.Empty;
public static string tab_bulkMembers => ResourceManager.GetString("tab.bulkMembers", resourceCulture) ?? string.Empty;
public static string tab_bulkSites => ResourceManager.GetString("tab.bulkSites", resourceCulture) ?? string.Empty;
public static string tab_folderStructure => ResourceManager.GetString("tab.folderStructure", resourceCulture) ?? string.Empty;
// Phase 4: Transfer tab
public static string transfer_sourcesite => ResourceManager.GetString("transfer.sourcesite", resourceCulture) ?? string.Empty;
public static string transfer_destsite => ResourceManager.GetString("transfer.destsite", resourceCulture) ?? string.Empty;
public static string transfer_sourcelibrary => ResourceManager.GetString("transfer.sourcelibrary", resourceCulture) ?? string.Empty;
public static string transfer_destlibrary => ResourceManager.GetString("transfer.destlibrary", resourceCulture) ?? string.Empty;
public static string transfer_sourcefolder => ResourceManager.GetString("transfer.sourcefolder", resourceCulture) ?? string.Empty;
public static string transfer_destfolder => ResourceManager.GetString("transfer.destfolder", resourceCulture) ?? string.Empty;
public static string transfer_mode => ResourceManager.GetString("transfer.mode", resourceCulture) ?? string.Empty;
public static string transfer_mode_copy => ResourceManager.GetString("transfer.mode.copy", resourceCulture) ?? string.Empty;
public static string transfer_mode_move => ResourceManager.GetString("transfer.mode.move", resourceCulture) ?? string.Empty;
public static string transfer_conflict => ResourceManager.GetString("transfer.conflict", resourceCulture) ?? string.Empty;
public static string transfer_conflict_skip => ResourceManager.GetString("transfer.conflict.skip", resourceCulture) ?? string.Empty;
public static string transfer_conflict_overwrite => ResourceManager.GetString("transfer.conflict.overwrite", resourceCulture) ?? string.Empty;
public static string transfer_conflict_rename => ResourceManager.GetString("transfer.conflict.rename", resourceCulture) ?? string.Empty;
public static string transfer_browse => ResourceManager.GetString("transfer.browse", resourceCulture) ?? string.Empty;
public static string transfer_start => ResourceManager.GetString("transfer.start", resourceCulture) ?? string.Empty;
public static string transfer_nofiles => ResourceManager.GetString("transfer.nofiles", resourceCulture) ?? string.Empty;
// Phase 4: Bulk Members tab
public static string bulkmembers_import => ResourceManager.GetString("bulkmembers.import", resourceCulture) ?? string.Empty;
public static string bulkmembers_example => ResourceManager.GetString("bulkmembers.example", resourceCulture) ?? string.Empty;
public static string bulkmembers_execute => ResourceManager.GetString("bulkmembers.execute", resourceCulture) ?? string.Empty;
public static string bulkmembers_preview => ResourceManager.GetString("bulkmembers.preview", resourceCulture) ?? string.Empty;
public static string bulkmembers_groupname => ResourceManager.GetString("bulkmembers.groupname", resourceCulture) ?? string.Empty;
public static string bulkmembers_groupurl => ResourceManager.GetString("bulkmembers.groupurl", resourceCulture) ?? string.Empty;
public static string bulkmembers_email => ResourceManager.GetString("bulkmembers.email", resourceCulture) ?? string.Empty;
public static string bulkmembers_role => ResourceManager.GetString("bulkmembers.role", resourceCulture) ?? string.Empty;
// Phase 4: Bulk Sites tab
public static string bulksites_import => ResourceManager.GetString("bulksites.import", resourceCulture) ?? string.Empty;
public static string bulksites_example => ResourceManager.GetString("bulksites.example", resourceCulture) ?? string.Empty;
public static string bulksites_execute => ResourceManager.GetString("bulksites.execute", resourceCulture) ?? string.Empty;
public static string bulksites_preview => ResourceManager.GetString("bulksites.preview", resourceCulture) ?? string.Empty;
public static string bulksites_name => ResourceManager.GetString("bulksites.name", resourceCulture) ?? string.Empty;
public static string bulksites_alias => ResourceManager.GetString("bulksites.alias", resourceCulture) ?? string.Empty;
public static string bulksites_type => ResourceManager.GetString("bulksites.type", resourceCulture) ?? string.Empty;
public static string bulksites_owners => ResourceManager.GetString("bulksites.owners", resourceCulture) ?? string.Empty;
public static string bulksites_members => ResourceManager.GetString("bulksites.members", resourceCulture) ?? string.Empty;
// Phase 4: Folder Structure tab
public static string folderstruct_import => ResourceManager.GetString("folderstruct.import", resourceCulture) ?? string.Empty;
public static string folderstruct_example => ResourceManager.GetString("folderstruct.example", resourceCulture) ?? string.Empty;
public static string folderstruct_execute => ResourceManager.GetString("folderstruct.execute", resourceCulture) ?? string.Empty;
public static string folderstruct_preview => ResourceManager.GetString("folderstruct.preview", resourceCulture) ?? string.Empty;
public static string folderstruct_library => ResourceManager.GetString("folderstruct.library", resourceCulture) ?? string.Empty;
public static string folderstruct_siteurl => ResourceManager.GetString("folderstruct.siteurl", resourceCulture) ?? string.Empty;
// Phase 4: Templates tab
public static string templates_list => ResourceManager.GetString("templates.list", resourceCulture) ?? string.Empty;
public static string templates_capture => ResourceManager.GetString("templates.capture", resourceCulture) ?? string.Empty;
public static string templates_apply => ResourceManager.GetString("templates.apply", resourceCulture) ?? string.Empty;
public static string templates_rename => ResourceManager.GetString("templates.rename", resourceCulture) ?? string.Empty;
public static string templates_delete => ResourceManager.GetString("templates.delete", resourceCulture) ?? string.Empty;
public static string templates_siteurl => ResourceManager.GetString("templates.siteurl", resourceCulture) ?? string.Empty;
public static string templates_name => ResourceManager.GetString("templates.name", resourceCulture) ?? string.Empty;
public static string templates_newtitle => ResourceManager.GetString("templates.newtitle", resourceCulture) ?? string.Empty;
public static string templates_newalias => ResourceManager.GetString("templates.newalias", resourceCulture) ?? string.Empty;
public static string templates_options => ResourceManager.GetString("templates.options", resourceCulture) ?? string.Empty;
public static string templates_opt_libraries => ResourceManager.GetString("templates.opt.libraries", resourceCulture) ?? string.Empty;
public static string templates_opt_folders => ResourceManager.GetString("templates.opt.folders", resourceCulture) ?? string.Empty;
public static string templates_opt_permissions => ResourceManager.GetString("templates.opt.permissions", resourceCulture) ?? string.Empty;
public static string templates_opt_logo => ResourceManager.GetString("templates.opt.logo", resourceCulture) ?? string.Empty;
public static string templates_opt_settings => ResourceManager.GetString("templates.opt.settings", resourceCulture) ?? string.Empty;
public static string templates_empty => ResourceManager.GetString("templates.empty", resourceCulture) ?? string.Empty;
// Phase 4: Shared bulk operation strings
public static string bulk_confirm_title => ResourceManager.GetString("bulk.confirm.title", resourceCulture) ?? string.Empty;
public static string bulk_confirm_proceed => ResourceManager.GetString("bulk.confirm.proceed", resourceCulture) ?? string.Empty;
public static string bulk_confirm_cancel => ResourceManager.GetString("bulk.confirm.cancel", resourceCulture) ?? string.Empty;
public static string bulk_confirm_message => ResourceManager.GetString("bulk.confirm.message", resourceCulture) ?? string.Empty;
public static string bulk_result_success => ResourceManager.GetString("bulk.result.success", resourceCulture) ?? string.Empty;
public static string bulk_result_allfailed => ResourceManager.GetString("bulk.result.allfailed", resourceCulture) ?? string.Empty;
public static string bulk_result_allsuccess => ResourceManager.GetString("bulk.result.allsuccess", resourceCulture) ?? string.Empty;
public static string bulk_exportfailed => ResourceManager.GetString("bulk.exportfailed", resourceCulture) ?? string.Empty;
public static string bulk_retryfailed => ResourceManager.GetString("bulk.retryfailed", resourceCulture) ?? string.Empty;
public static string bulk_validation_invalid => ResourceManager.GetString("bulk.validation.invalid", resourceCulture) ?? string.Empty;
public static string bulk_csvimport_title => ResourceManager.GetString("bulk.csvimport.title", resourceCulture) ?? string.Empty;
public static string bulk_csvimport_filter => ResourceManager.GetString("bulk.csvimport.filter", resourceCulture) ?? string.Empty;
// Phase 4: Folder browser dialog
public static string folderbrowser_title => ResourceManager.GetString("folderbrowser.title", resourceCulture) ?? string.Empty;
public static string folderbrowser_loading => ResourceManager.GetString("folderbrowser.loading", resourceCulture) ?? string.Empty;
public static string folderbrowser_select => ResourceManager.GetString("folderbrowser.select", resourceCulture) ?? string.Empty;
public static string folderbrowser_cancel => ResourceManager.GetString("folderbrowser.cancel", resourceCulture) ?? string.Empty;
} }
} }

View File

@@ -221,4 +221,87 @@
<data name="ph.dup.lib" xml:space="preserve"><value>Tous (laisser vide)</value></data> <data name="ph.dup.lib" xml:space="preserve"><value>Tous (laisser vide)</value></data>
<data name="btn.run.scan" xml:space="preserve"><value>Lancer l'analyse</value></data> <data name="btn.run.scan" xml:space="preserve"><value>Lancer l'analyse</value></data>
<data name="btn.open.results" xml:space="preserve"><value>Ouvrir les résultats</value></data> <data name="btn.open.results" xml:space="preserve"><value>Ouvrir les résultats</value></data>
<!-- Phase 4: Tab headers -->
<data name="tab.transfer" xml:space="preserve"><value>Transfert</value></data>
<data name="tab.bulkMembers" xml:space="preserve"><value>Ajout en masse</value></data>
<data name="tab.bulkSites" xml:space="preserve"><value>Sites en masse</value></data>
<data name="tab.folderStructure" xml:space="preserve"><value>Structure de dossiers</value></data>
<!-- Phase 4: Transfer tab -->
<data name="transfer.sourcesite" xml:space="preserve"><value>Site source</value></data>
<data name="transfer.destsite" xml:space="preserve"><value>Site destination</value></data>
<data name="transfer.sourcelibrary" xml:space="preserve"><value>Bibliotheque source</value></data>
<data name="transfer.destlibrary" xml:space="preserve"><value>Bibliotheque destination</value></data>
<data name="transfer.sourcefolder" xml:space="preserve"><value>Dossier source</value></data>
<data name="transfer.destfolder" xml:space="preserve"><value>Dossier destination</value></data>
<data name="transfer.mode" xml:space="preserve"><value>Mode de transfert</value></data>
<data name="transfer.mode.copy" xml:space="preserve"><value>Copier</value></data>
<data name="transfer.mode.move" xml:space="preserve"><value>Deplacer</value></data>
<data name="transfer.conflict" xml:space="preserve"><value>Politique de conflit</value></data>
<data name="transfer.conflict.skip" xml:space="preserve"><value>Ignorer</value></data>
<data name="transfer.conflict.overwrite" xml:space="preserve"><value>Ecraser</value></data>
<data name="transfer.conflict.rename" xml:space="preserve"><value>Renommer (ajouter suffixe)</value></data>
<data name="transfer.browse" xml:space="preserve"><value>Parcourir...</value></data>
<data name="transfer.start" xml:space="preserve"><value>Demarrer le transfert</value></data>
<data name="transfer.nofiles" xml:space="preserve"><value>Aucun fichier a transferer.</value></data>
<!-- Phase 4: Bulk Members tab -->
<data name="bulkmembers.import" xml:space="preserve"><value>Importer CSV</value></data>
<data name="bulkmembers.example" xml:space="preserve"><value>Charger l'exemple</value></data>
<data name="bulkmembers.execute" xml:space="preserve"><value>Ajouter les membres</value></data>
<data name="bulkmembers.preview" xml:space="preserve"><value>Apercu ({0} lignes, {1} valides, {2} invalides)</value></data>
<data name="bulkmembers.groupname" xml:space="preserve"><value>Nom du groupe</value></data>
<data name="bulkmembers.groupurl" xml:space="preserve"><value>URL du groupe</value></data>
<data name="bulkmembers.email" xml:space="preserve"><value>Courriel</value></data>
<data name="bulkmembers.role" xml:space="preserve"><value>Role</value></data>
<!-- Phase 4: Bulk Sites tab -->
<data name="bulksites.import" xml:space="preserve"><value>Importer CSV</value></data>
<data name="bulksites.example" xml:space="preserve"><value>Charger l'exemple</value></data>
<data name="bulksites.execute" xml:space="preserve"><value>Creer les sites</value></data>
<data name="bulksites.preview" xml:space="preserve"><value>Apercu ({0} lignes, {1} valides, {2} invalides)</value></data>
<data name="bulksites.name" xml:space="preserve"><value>Nom</value></data>
<data name="bulksites.alias" xml:space="preserve"><value>Alias</value></data>
<data name="bulksites.type" xml:space="preserve"><value>Type</value></data>
<data name="bulksites.owners" xml:space="preserve"><value>Proprietaires</value></data>
<data name="bulksites.members" xml:space="preserve"><value>Membres</value></data>
<!-- Phase 4: Folder Structure tab -->
<data name="folderstruct.import" xml:space="preserve"><value>Importer CSV</value></data>
<data name="folderstruct.example" xml:space="preserve"><value>Charger l'exemple</value></data>
<data name="folderstruct.execute" xml:space="preserve"><value>Creer les dossiers</value></data>
<data name="folderstruct.preview" xml:space="preserve"><value>Apercu ({0} dossiers a creer)</value></data>
<data name="folderstruct.library" xml:space="preserve"><value>Bibliotheque cible</value></data>
<data name="folderstruct.siteurl" xml:space="preserve"><value>URL du site</value></data>
<!-- Phase 4: Templates tab -->
<data name="templates.list" xml:space="preserve"><value>Modeles enregistres</value></data>
<data name="templates.capture" xml:space="preserve"><value>Capturer un modele</value></data>
<data name="templates.apply" xml:space="preserve"><value>Appliquer le modele</value></data>
<data name="templates.rename" xml:space="preserve"><value>Renommer</value></data>
<data name="templates.delete" xml:space="preserve"><value>Supprimer</value></data>
<data name="templates.siteurl" xml:space="preserve"><value>URL du site source</value></data>
<data name="templates.name" xml:space="preserve"><value>Nom du modele</value></data>
<data name="templates.newtitle" xml:space="preserve"><value>Titre du nouveau site</value></data>
<data name="templates.newalias" xml:space="preserve"><value>Alias du nouveau site</value></data>
<data name="templates.options" xml:space="preserve"><value>Options de capture</value></data>
<data name="templates.opt.libraries" xml:space="preserve"><value>Bibliotheques</value></data>
<data name="templates.opt.folders" xml:space="preserve"><value>Dossiers</value></data>
<data name="templates.opt.permissions" xml:space="preserve"><value>Groupes de permissions</value></data>
<data name="templates.opt.logo" xml:space="preserve"><value>Logo du site</value></data>
<data name="templates.opt.settings" xml:space="preserve"><value>Parametres du site</value></data>
<data name="templates.empty" xml:space="preserve"><value>Aucun modele enregistre.</value></data>
<!-- Phase 4: Shared bulk operation strings -->
<data name="bulk.confirm.title" xml:space="preserve"><value>Confirmer l'operation</value></data>
<data name="bulk.confirm.proceed" xml:space="preserve"><value>Continuer</value></data>
<data name="bulk.confirm.cancel" xml:space="preserve"><value>Annuler</value></data>
<data name="bulk.confirm.message" xml:space="preserve"><value>{0} — Continuer ?</value></data>
<data name="bulk.result.success" xml:space="preserve"><value>Termine : {0} reussis, {1} echoues</value></data>
<data name="bulk.result.allfailed" xml:space="preserve"><value>Les {0} elements ont echoue.</value></data>
<data name="bulk.result.allsuccess" xml:space="preserve"><value>Les {0} elements ont ete traites avec succes.</value></data>
<data name="bulk.exportfailed" xml:space="preserve"><value>Exporter les elements echoues</value></data>
<data name="bulk.retryfailed" xml:space="preserve"><value>Reessayer les echecs</value></data>
<data name="bulk.validation.invalid" xml:space="preserve"><value>{0} lignes contiennent des erreurs. Corrigez et reimportez.</value></data>
<data name="bulk.csvimport.title" xml:space="preserve"><value>Selectionner un fichier CSV</value></data>
<data name="bulk.csvimport.filter" xml:space="preserve"><value>Fichiers CSV (*.csv)|*.csv</value></data>
<!-- Phase 4: Folder browser dialog -->
<data name="folderbrowser.title" xml:space="preserve"><value>Selectionner un dossier</value></data>
<data name="folderbrowser.loading" xml:space="preserve"><value>Chargement de l'arborescence...</value></data>
<data name="folderbrowser.select" xml:space="preserve"><value>Selectionner</value></data>
<data name="folderbrowser.cancel" xml:space="preserve"><value>Annuler</value></data>
</root> </root>

View File

@@ -221,4 +221,87 @@
<data name="ph.dup.lib" xml:space="preserve"><value>All (leave empty)</value></data> <data name="ph.dup.lib" xml:space="preserve"><value>All (leave empty)</value></data>
<data name="btn.run.scan" xml:space="preserve"><value>Run Scan</value></data> <data name="btn.run.scan" xml:space="preserve"><value>Run Scan</value></data>
<data name="btn.open.results" xml:space="preserve"><value>Open Results</value></data> <data name="btn.open.results" xml:space="preserve"><value>Open Results</value></data>
<!-- Phase 4: Tab headers -->
<data name="tab.transfer" xml:space="preserve"><value>Transfer</value></data>
<data name="tab.bulkMembers" xml:space="preserve"><value>Bulk Members</value></data>
<data name="tab.bulkSites" xml:space="preserve"><value>Bulk Sites</value></data>
<data name="tab.folderStructure" xml:space="preserve"><value>Folder Structure</value></data>
<!-- Phase 4: Transfer tab -->
<data name="transfer.sourcesite" xml:space="preserve"><value>Source Site</value></data>
<data name="transfer.destsite" xml:space="preserve"><value>Destination Site</value></data>
<data name="transfer.sourcelibrary" xml:space="preserve"><value>Source Library</value></data>
<data name="transfer.destlibrary" xml:space="preserve"><value>Destination Library</value></data>
<data name="transfer.sourcefolder" xml:space="preserve"><value>Source Folder</value></data>
<data name="transfer.destfolder" xml:space="preserve"><value>Destination Folder</value></data>
<data name="transfer.mode" xml:space="preserve"><value>Transfer Mode</value></data>
<data name="transfer.mode.copy" xml:space="preserve"><value>Copy</value></data>
<data name="transfer.mode.move" xml:space="preserve"><value>Move</value></data>
<data name="transfer.conflict" xml:space="preserve"><value>Conflict Policy</value></data>
<data name="transfer.conflict.skip" xml:space="preserve"><value>Skip</value></data>
<data name="transfer.conflict.overwrite" xml:space="preserve"><value>Overwrite</value></data>
<data name="transfer.conflict.rename" xml:space="preserve"><value>Rename (append suffix)</value></data>
<data name="transfer.browse" xml:space="preserve"><value>Browse...</value></data>
<data name="transfer.start" xml:space="preserve"><value>Start Transfer</value></data>
<data name="transfer.nofiles" xml:space="preserve"><value>No files found to transfer.</value></data>
<!-- Phase 4: Bulk Members tab -->
<data name="bulkmembers.import" xml:space="preserve"><value>Import CSV</value></data>
<data name="bulkmembers.example" xml:space="preserve"><value>Load Example</value></data>
<data name="bulkmembers.execute" xml:space="preserve"><value>Add Members</value></data>
<data name="bulkmembers.preview" xml:space="preserve"><value>Preview ({0} rows, {1} valid, {2} invalid)</value></data>
<data name="bulkmembers.groupname" xml:space="preserve"><value>Group Name</value></data>
<data name="bulkmembers.groupurl" xml:space="preserve"><value>Group URL</value></data>
<data name="bulkmembers.email" xml:space="preserve"><value>Email</value></data>
<data name="bulkmembers.role" xml:space="preserve"><value>Role</value></data>
<!-- Phase 4: Bulk Sites tab -->
<data name="bulksites.import" xml:space="preserve"><value>Import CSV</value></data>
<data name="bulksites.example" xml:space="preserve"><value>Load Example</value></data>
<data name="bulksites.execute" xml:space="preserve"><value>Create Sites</value></data>
<data name="bulksites.preview" xml:space="preserve"><value>Preview ({0} rows, {1} valid, {2} invalid)</value></data>
<data name="bulksites.name" xml:space="preserve"><value>Name</value></data>
<data name="bulksites.alias" xml:space="preserve"><value>Alias</value></data>
<data name="bulksites.type" xml:space="preserve"><value>Type</value></data>
<data name="bulksites.owners" xml:space="preserve"><value>Owners</value></data>
<data name="bulksites.members" xml:space="preserve"><value>Members</value></data>
<!-- Phase 4: Folder Structure tab -->
<data name="folderstruct.import" xml:space="preserve"><value>Import CSV</value></data>
<data name="folderstruct.example" xml:space="preserve"><value>Load Example</value></data>
<data name="folderstruct.execute" xml:space="preserve"><value>Create Folders</value></data>
<data name="folderstruct.preview" xml:space="preserve"><value>Preview ({0} folders to create)</value></data>
<data name="folderstruct.library" xml:space="preserve"><value>Target Library</value></data>
<data name="folderstruct.siteurl" xml:space="preserve"><value>Site URL</value></data>
<!-- Phase 4: Templates tab -->
<data name="templates.list" xml:space="preserve"><value>Saved Templates</value></data>
<data name="templates.capture" xml:space="preserve"><value>Capture Template</value></data>
<data name="templates.apply" xml:space="preserve"><value>Apply Template</value></data>
<data name="templates.rename" xml:space="preserve"><value>Rename</value></data>
<data name="templates.delete" xml:space="preserve"><value>Delete</value></data>
<data name="templates.siteurl" xml:space="preserve"><value>Source Site URL</value></data>
<data name="templates.name" xml:space="preserve"><value>Template Name</value></data>
<data name="templates.newtitle" xml:space="preserve"><value>New Site Title</value></data>
<data name="templates.newalias" xml:space="preserve"><value>New Site Alias</value></data>
<data name="templates.options" xml:space="preserve"><value>Capture Options</value></data>
<data name="templates.opt.libraries" xml:space="preserve"><value>Libraries</value></data>
<data name="templates.opt.folders" xml:space="preserve"><value>Folders</value></data>
<data name="templates.opt.permissions" xml:space="preserve"><value>Permission Groups</value></data>
<data name="templates.opt.logo" xml:space="preserve"><value>Site Logo</value></data>
<data name="templates.opt.settings" xml:space="preserve"><value>Site Settings</value></data>
<data name="templates.empty" xml:space="preserve"><value>No templates saved yet.</value></data>
<!-- Phase 4: Shared bulk operation strings -->
<data name="bulk.confirm.title" xml:space="preserve"><value>Confirm Operation</value></data>
<data name="bulk.confirm.proceed" xml:space="preserve"><value>Proceed</value></data>
<data name="bulk.confirm.cancel" xml:space="preserve"><value>Cancel</value></data>
<data name="bulk.confirm.message" xml:space="preserve"><value>{0} — Proceed?</value></data>
<data name="bulk.result.success" xml:space="preserve"><value>Completed: {0} succeeded, {1} failed</value></data>
<data name="bulk.result.allfailed" xml:space="preserve"><value>All {0} items failed.</value></data>
<data name="bulk.result.allsuccess" xml:space="preserve"><value>All {0} items completed successfully.</value></data>
<data name="bulk.exportfailed" xml:space="preserve"><value>Export Failed Items</value></data>
<data name="bulk.retryfailed" xml:space="preserve"><value>Retry Failed</value></data>
<data name="bulk.validation.invalid" xml:space="preserve"><value>{0} rows have validation errors. Fix and re-import.</value></data>
<data name="bulk.csvimport.title" xml:space="preserve"><value>Select CSV File</value></data>
<data name="bulk.csvimport.filter" xml:space="preserve"><value>CSV Files (*.csv)|*.csv</value></data>
<!-- Phase 4: Folder browser dialog -->
<data name="folderbrowser.title" xml:space="preserve"><value>Select Folder</value></data>
<data name="folderbrowser.loading" xml:space="preserve"><value>Loading folder tree...</value></data>
<data name="folderbrowser.select" xml:space="preserve"><value>Select</value></data>
<data name="folderbrowser.cancel" xml:space="preserve"><value>Cancel</value></data>
</root> </root>

View File

@@ -0,0 +1,8 @@
GroupName,GroupUrl,Email,Role
Marketing Team,https://contoso.sharepoint.com/sites/Marketing,user1@contoso.com,Member
Marketing Team,https://contoso.sharepoint.com/sites/Marketing,manager@contoso.com,Owner
HR Team,https://contoso.sharepoint.com/sites/HR,hr-admin@contoso.com,Owner
HR Team,https://contoso.sharepoint.com/sites/HR,recruiter@contoso.com,Member
HR Team,https://contoso.sharepoint.com/sites/HR,analyst@contoso.com,Member
IT Support,https://contoso.sharepoint.com/sites/IT,sysadmin@contoso.com,Owner
IT Support,https://contoso.sharepoint.com/sites/IT,helpdesk@contoso.com,Member
1 GroupName GroupUrl Email Role
2 Marketing Team https://contoso.sharepoint.com/sites/Marketing user1@contoso.com Member
3 Marketing Team https://contoso.sharepoint.com/sites/Marketing manager@contoso.com Owner
4 HR Team https://contoso.sharepoint.com/sites/HR hr-admin@contoso.com Owner
5 HR Team https://contoso.sharepoint.com/sites/HR recruiter@contoso.com Member
6 HR Team https://contoso.sharepoint.com/sites/HR analyst@contoso.com Member
7 IT Support https://contoso.sharepoint.com/sites/IT sysadmin@contoso.com Owner
8 IT Support https://contoso.sharepoint.com/sites/IT helpdesk@contoso.com Member

View File

@@ -0,0 +1,6 @@
Name;Alias;Type;Template;Owners;Members
Projet Alpha;projet-alpha;Team;;admin@contoso.com;user1@contoso.com, user2@contoso.com
Projet Beta;projet-beta;Team;;admin@contoso.com;user3@contoso.com, user4@contoso.com
Communication RH;comm-rh;Communication;;rh-admin@contoso.com;manager1@contoso.com, manager2@contoso.com
Equipe Marketing;equipe-marketing;Team;;marketing-lead@contoso.com;designer@contoso.com, redacteur@contoso.com
Portail Intranet;portail-intranet;Communication;;it-admin@contoso.com;
1 Name Alias Type Template Owners Members
2 Projet Alpha projet-alpha Team admin@contoso.com user1@contoso.com, user2@contoso.com
3 Projet Beta projet-beta Team admin@contoso.com user3@contoso.com, user4@contoso.com
4 Communication RH comm-rh Communication rh-admin@contoso.com manager1@contoso.com, manager2@contoso.com
5 Equipe Marketing equipe-marketing Team marketing-lead@contoso.com designer@contoso.com, redacteur@contoso.com
6 Portail Intranet portail-intranet Communication it-admin@contoso.com

View File

@@ -0,0 +1,20 @@
Level1;Level2;Level3;Level4
Administration;;;
Administration;Comptabilite;;
Administration;Comptabilite;Factures;
Administration;Comptabilite;Bilans;
Administration;Ressources Humaines;;
Administration;Ressources Humaines;Contrats;
Administration;Ressources Humaines;Fiches de paie;
Projets;;;
Projets;Projet Alpha;;
Projets;Projet Alpha;Documents;
Projets;Projet Alpha;Livrables;
Projets;Projet Beta;;
Projets;Projet Beta;Documents;
Communication;;;
Communication;Interne;;
Communication;Interne;Notes de service;
Communication;Externe;;
Communication;Externe;Communiques de presse;
Communication;Externe;Newsletter;
1 Level1 Level2 Level3 Level4
2 Administration
3 Administration Comptabilite
4 Administration Comptabilite Factures
5 Administration Comptabilite Bilans
6 Administration Ressources Humaines
7 Administration Ressources Humaines Contrats
8 Administration Ressources Humaines Fiches de paie
9 Projets
10 Projets Projet Alpha
11 Projets Projet Alpha Documents
12 Projets Projet Alpha Livrables
13 Projets Projet Beta
14 Projets Projet Beta Documents
15 Communication
16 Communication Interne
17 Communication Interne Notes de service
18 Communication Externe
19 Communication Externe Communiques de presse
20 Communication Externe Newsletter

View File

@@ -25,6 +25,12 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\bulk_add_members.csv" />
<EmbeddedResource Include="Resources\bulk_create_sites.csv" />
<EmbeddedResource Include="Resources\folder_structure.csv" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="CsvHelper" Version="33.1.0" /> <PackageReference Include="CsvHelper" Version="33.1.0" />

View File

@@ -0,0 +1,29 @@
<Window x:Class="SharepointToolbox.Views.Dialogs.ConfirmBulkOperationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:SharepointToolbox.Localization"
Title="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[bulk.confirm.title]}"
Width="450" Height="220" WindowStartupLocation="CenterOwner"
ResizeMode="NoResize">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="MessageText" Grid.Row="0"
TextWrapping="Wrap" FontSize="14"
VerticalAlignment="Center" />
<StackPanel Grid.Row="1" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="0,20,0,0">
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[bulk.confirm.cancel]}"
Width="100" Margin="0,0,10,0" IsCancel="True"
Click="Cancel_Click" />
<Button x:Name="ProceedButton"
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[bulk.confirm.proceed]}"
Width="100" IsDefault="True"
Click="Proceed_Click" />
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,28 @@
using System.Windows;
namespace SharepointToolbox.Views.Dialogs;
public partial class ConfirmBulkOperationDialog : Window
{
public bool IsConfirmed { get; private set; }
public ConfirmBulkOperationDialog(string message)
{
InitializeComponent();
MessageText.Text = message;
}
private void Proceed_Click(object sender, RoutedEventArgs e)
{
IsConfirmed = true;
DialogResult = true;
Close();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
IsConfirmed = false;
DialogResult = false;
Close();
}
}

View File

@@ -0,0 +1,28 @@
<Window x:Class="SharepointToolbox.Views.Dialogs.FolderBrowserDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:SharepointToolbox.Localization"
Title="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderbrowser.title]}"
Width="400" Height="500" WindowStartupLocation="CenterOwner"
ResizeMode="CanResizeWithGrip">
<DockPanel Margin="10">
<!-- Status -->
<TextBlock x:Name="StatusText" DockPanel.Dock="Top" Margin="0,0,0,10"
Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderbrowser.loading]}" />
<!-- Buttons -->
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderbrowser.cancel]}"
Width="80" Margin="0,0,10,0" IsCancel="True"
Click="Cancel_Click" />
<Button x:Name="SelectButton"
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderbrowser.select]}"
Width="80" IsDefault="True" IsEnabled="False"
Click="Select_Click" />
</StackPanel>
<!-- Tree -->
<TreeView x:Name="FolderTree" SelectedItemChanged="FolderTree_SelectedItemChanged" />
</DockPanel>
</Window>

View File

@@ -0,0 +1,125 @@
using System.Windows;
using System.Windows.Controls;
using Microsoft.SharePoint.Client;
using SharepointToolbox.Core.Helpers;
namespace SharepointToolbox.Views.Dialogs;
public partial class FolderBrowserDialog : Window
{
private readonly ClientContext _ctx;
public string SelectedLibrary { get; private set; } = string.Empty;
public string SelectedFolderPath { get; private set; } = string.Empty;
public FolderBrowserDialog(ClientContext ctx)
{
InitializeComponent();
_ctx = ctx;
Loaded += OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
try
{
// Load libraries
var web = _ctx.Web;
var lists = _ctx.LoadQuery(web.Lists
.Include(l => l.Title, l => l.Hidden, l => l.BaseType, l => l.RootFolder)
.Where(l => !l.Hidden && l.BaseType == BaseType.DocumentLibrary));
var progress = new Progress<Core.Models.OperationProgress>();
await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(_ctx, progress, CancellationToken.None);
foreach (var list in lists)
{
var libNode = new TreeViewItem
{
Header = list.Title,
Tag = new FolderNodeInfo(list.Title, string.Empty),
};
// Add dummy child for expand arrow
libNode.Items.Add(new TreeViewItem { Header = "Loading..." });
libNode.Expanded += LibNode_Expanded;
FolderTree.Items.Add(libNode);
}
StatusText.Text = $"{FolderTree.Items.Count} libraries loaded.";
}
catch (Exception ex)
{
StatusText.Text = $"Error: {ex.Message}";
}
}
private async void LibNode_Expanded(object sender, RoutedEventArgs e)
{
if (sender is not TreeViewItem node || node.Tag is not FolderNodeInfo info)
return;
// Only load children once
if (node.Items.Count == 1 && node.Items[0] is TreeViewItem dummy && dummy.Header?.ToString() == "Loading...")
{
node.Items.Clear();
try
{
var folderUrl = string.IsNullOrEmpty(info.FolderPath)
? GetLibraryRootUrl(info.LibraryTitle)
: info.FolderPath;
var folder = _ctx.Web.GetFolderByServerRelativeUrl(folderUrl);
_ctx.Load(folder, f => f.Folders.Include(sf => sf.Name, sf => sf.ServerRelativeUrl));
var progress = new Progress<Core.Models.OperationProgress>();
await ExecuteQueryRetryHelper.ExecuteQueryRetryAsync(_ctx, progress, CancellationToken.None);
foreach (var subFolder in folder.Folders)
{
if (subFolder.Name.StartsWith("_") || subFolder.Name == "Forms")
continue;
var childNode = new TreeViewItem
{
Header = subFolder.Name,
Tag = new FolderNodeInfo(info.LibraryTitle, subFolder.ServerRelativeUrl),
};
childNode.Items.Add(new TreeViewItem { Header = "Loading..." });
childNode.Expanded += LibNode_Expanded;
node.Items.Add(childNode);
}
}
catch (Exception ex)
{
node.Items.Add(new TreeViewItem { Header = $"Error: {ex.Message}" });
}
}
}
private string GetLibraryRootUrl(string libraryTitle)
{
var uri = new Uri(_ctx.Url);
return $"{uri.AbsolutePath.TrimEnd('/')}/{libraryTitle}";
}
private void FolderTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (e.NewValue is TreeViewItem node && node.Tag is FolderNodeInfo info)
{
SelectedLibrary = info.LibraryTitle;
SelectedFolderPath = info.FolderPath;
SelectButton.IsEnabled = true;
}
}
private void Select_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private record FolderNodeInfo(string LibraryTitle, string FolderPath);
}