mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-29 03:15:38 +02:00
IntuneManagement 4.0.0-beta1
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
# Bulk Export form.
|
||||
# Split out of IntuneManagerUIWPF.ps1 on 2026-06-03 to match the Avalonia tree's
|
||||
# per-feature layout (architecture rule R9 — keep the WPF shell from growing further).
|
||||
|
||||
function Show-GraphBulkExportForm
|
||||
{
|
||||
$script:bulkExportForm = $script:UIProvider.GetXamlObject(($script:AppUIRootFolder + "\Xaml\BulkExportForm.xaml"), $true)
|
||||
if(-not $script:bulkExportForm) { return }
|
||||
|
||||
$script:dgObjectsToExport = $script:bulkExportForm.FindName("dgObjectsToExport")
|
||||
|
||||
$exportSettings = [IntuneManagerExportSettings]::new()
|
||||
|
||||
$script:bulkExportForm.DataContext = $exportSettings
|
||||
|
||||
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "browseExportPath", "add_click", {
|
||||
$folder = Get-Folder ($script:UIProvider.GetXamlProperty($script:bulkExportForm, "txtExportPath", "Text")) "Select root folder for export"
|
||||
if($folder)
|
||||
{
|
||||
$script:bulkExportForm.DataContext.ExportFolder = $folder
|
||||
$tmp = $script:bulkExportForm.DataContext
|
||||
$script:bulkExportForm.DataContext = $null
|
||||
$script:bulkExportForm.DataContext = $tmp
|
||||
}
|
||||
})
|
||||
|
||||
# Register dynamic export-properties / UI extensions for every exportable
|
||||
# policy type up front. Done once (not per mode switch) so toggling
|
||||
# Group/API doesn't re-register and we never miss a type that only appears
|
||||
# in the other view.
|
||||
$script:bulkExportExportableGroups = @($script:IntuneGroups | Where-Object {
|
||||
$_.Title -and (-not ($_.ShowButtons -is [Object[]]) -or $_.ShowButtons -contains "Export")
|
||||
})
|
||||
foreach($intuneGroup in $script:bulkExportExportableGroups)
|
||||
{
|
||||
$intuneGroup.PolicyTypes | Add-IntuneManagerExportProperties -ExportSettings $exportSettings
|
||||
$intuneGroup.PolicyTypes | Add-IntuneManagerExportUIExtensions -Form $script:bulkExportForm
|
||||
}
|
||||
|
||||
$column = Get-GridCheckboxColumn "Selected"
|
||||
$script:dgObjectsToExport.Columns.Add($column)
|
||||
|
||||
$column.Header.IsChecked = $true # All items are checked by default
|
||||
$column.Header.add_Click({
|
||||
foreach($Item in $script:dgObjectsToExport.ItemsSource)
|
||||
{
|
||||
$Item.Selected = $this.IsChecked
|
||||
}
|
||||
$script:dgObjectsToExport.Items.Refresh()
|
||||
}
|
||||
)
|
||||
|
||||
# Add Object type column
|
||||
$binding = [System.Windows.Data.Binding]::new("Title")
|
||||
$column = [System.Windows.Controls.DataGridTextColumn]::new()
|
||||
$column.Header = "Object type"
|
||||
$column.IsReadOnly = $true
|
||||
$column.Binding = $binding
|
||||
$script:dgObjectsToExport.Columns.Add($column)
|
||||
|
||||
# Default view is "Group" (matches the IsChecked=True on the XAML radio).
|
||||
$script:bulkExportMode = "Group"
|
||||
Update-BulkExportObjectList
|
||||
|
||||
# Repopulate the grid when the user flips between Group and API views.
|
||||
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "rbBulkExportViewGroup", "add_Checked", {
|
||||
$script:bulkExportMode = "Group"
|
||||
Update-BulkExportObjectList
|
||||
})
|
||||
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "rbBulkExportViewType", "add_Checked", {
|
||||
$script:bulkExportMode = "Type"
|
||||
Update-BulkExportObjectList
|
||||
})
|
||||
|
||||
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "btnClose", "add_click", {
|
||||
$script:bulkExportForm = $null
|
||||
Show-ModalObject
|
||||
})
|
||||
|
||||
# NOTE: the "Save settings for batch job" button was removed 2026-06-12 per
|
||||
# user decision — scheduled/CI runs use the public drivers directly
|
||||
# (Start-GraphBulkExport -SettingsFile files can still be produced with
|
||||
# Save-GraphBulkExportSettings from a script).
|
||||
|
||||
# btnExport: collect the selected rows from the DataGrid and call the public
|
||||
# driver. The UI does NOT contain export logic — it's a thin caller of
|
||||
# Start-GraphBulkExport so the same operation is scriptable headlessly.
|
||||
$script:UIProvider.AddXamlEvent($script:bulkExportForm, "btnExport", "add_click", {
|
||||
$selection = Get-BulkExportSelectedIds
|
||||
$unit = if ($script:bulkExportMode -eq "Type") { "policy type" } else { "object group" }
|
||||
|
||||
if ($selection.Count -eq 0) {
|
||||
$script:UIProvider.ShowMessageBox("Select at least one $unit to export.", "Bulk Export", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
|
||||
# Read the path straight from the TextBox rather than relying on the binding —
|
||||
# the field is bound TwoWay/PropertyChanged so they should agree, but pulling
|
||||
# from the control directly is bullet-proof. Trim and unquote (users sometimes
|
||||
# paste paths with surrounding quotes).
|
||||
$rawPath = ($script:UIProvider.GetXamlProperty($script:bulkExportForm, "txtExportPath", "Text"))
|
||||
if ($rawPath) { $rawPath = ([string]$rawPath).Trim().Trim('"').Trim("'") }
|
||||
if (-not $rawPath) {
|
||||
$script:UIProvider.ShowMessageBox("Select an export folder.", "Bulk Export", "OK", "Warning") | Out-Null
|
||||
return
|
||||
}
|
||||
# Resolve to a full path so the success message shows the canonical location,
|
||||
# not whatever relative-ish text the user typed.
|
||||
try { $resolvedPath = [IO.Path]::GetFullPath($rawPath) }
|
||||
catch {
|
||||
$script:UIProvider.ShowMessageBox("Invalid export folder path: $($_.Exception.Message)", "Bulk Export", "OK", "Error") | Out-Null
|
||||
return
|
||||
}
|
||||
$script:bulkExportForm.DataContext.ExportFolder = $resolvedPath
|
||||
Write-Log "Bulk export starting. Folder: $resolvedPath"
|
||||
|
||||
$startParams = @{
|
||||
ExportSettings = $script:bulkExportForm.DataContext
|
||||
}
|
||||
if ($script:bulkExportMode -eq "Type") {
|
||||
$startParams.PolicyType = $selection
|
||||
}
|
||||
else {
|
||||
$startParams.PolicyGroup = $selection
|
||||
}
|
||||
|
||||
try {
|
||||
$summary = Start-GraphBulkExport @startParams
|
||||
Write-Status $null
|
||||
$msg = ("Exported {0} policies across {1} types ({2} failed) in {3:hh\:mm\:ss}.`n`nFolder:`n{4}" -f `
|
||||
$summary.Policies, $summary.Types, $summary.Failed, $summary.Duration, $resolvedPath)
|
||||
$unknownNote = Get-IntuneUnknownSelectorSummary $summary.UnknownSelectors
|
||||
if($unknownNote) { $msg += "`n`n$unknownNote" }
|
||||
$script:UIProvider.ShowMessageBox($msg, "Bulk Export", "OK", "Information") | Out-Null
|
||||
}
|
||||
catch {
|
||||
Write-LogError "Bulk export failed" $_.Exception
|
||||
$script:UIProvider.ShowMessageBox("Bulk export failed: $($_.Exception.Message)", "Bulk Export", "OK", "Error") | Out-Null
|
||||
}
|
||||
|
||||
$script:bulkExportForm = $null
|
||||
Show-ModalObject
|
||||
})
|
||||
|
||||
$script:UIProvider.ShowModalForm("Bulk Export", $script:bulkExportForm, $true)
|
||||
}
|
||||
|
||||
# Rebuild the bulk-export DataGrid for the current $script:bulkExportMode.
|
||||
# Group mode lists policy groups (Configuration, Compliance, ...); Type mode
|
||||
# lists individual policy types (APIs). Both sort by Title so the order is
|
||||
# stable instead of class-load order.
|
||||
function Update-BulkExportObjectList
|
||||
{
|
||||
if (-not $script:dgObjectsToExport) { return }
|
||||
|
||||
$script:exportObjects = @()
|
||||
|
||||
if ($script:bulkExportMode -eq "Type") {
|
||||
$exportableGroupIds = @($script:bulkExportExportableGroups | ForEach-Object { $_.Id })
|
||||
$sortedTypes = $script:IntuneTypes |
|
||||
Where-Object { $_.PolicyGroup -and ($exportableGroupIds -contains $_.PolicyGroup.Id) } |
|
||||
Sort-Object Title
|
||||
foreach ($intuneType in $sortedTypes) {
|
||||
$script:exportObjects += New-Object PSObject -Property @{
|
||||
Title = $intuneType.Title
|
||||
Selected = $true
|
||||
ObjectGroup = $null
|
||||
ObjectType = $intuneType
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$sortedGroups = $script:bulkExportExportableGroups | Sort-Object Title
|
||||
foreach ($intuneGroup in $sortedGroups) {
|
||||
$script:exportObjects += New-Object PSObject -Property @{
|
||||
Title = $intuneGroup.Title
|
||||
Selected = (?? $intuneGroup.BulkExport $true)
|
||||
ObjectGroup = $intuneGroup
|
||||
ObjectType = $null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$script:dgObjectsToExport.ItemsSource = $script:exportObjects
|
||||
}
|
||||
|
||||
# Pull the Id list out of the currently-checked rows. In Group mode that's
|
||||
# PolicyGroup IDs (for -PolicyGroup); in Type mode it's PolicyType IDs (for
|
||||
# -PolicyType). Returns @() when nothing is selected.
|
||||
function Get-BulkExportSelectedIds
|
||||
{
|
||||
if ($script:bulkExportMode -eq "Type") {
|
||||
return @($script:exportObjects |
|
||||
Where-Object { $_.Selected -eq $true -and $_.ObjectType -and $_.ObjectType.Id } |
|
||||
ForEach-Object { $_.ObjectType.Id })
|
||||
}
|
||||
return @($script:exportObjects |
|
||||
Where-Object { $_.Selected -eq $true -and $_.ObjectGroup -and $_.ObjectGroup.Id } |
|
||||
ForEach-Object { $_.ObjectGroup.Id })
|
||||
}
|
||||
|
||||
# ─── Bulk Scope Tags ──────────────────────────────────────────────────────────
|
||||
# Thin UI wrapper around Set-GraphBulkScopeTags. The form mirrors Bulk Export's
|
||||
# Group / API selector for picking object types, adds an action selector
|
||||
# (Add / Replace / Remove), an orphan-cleanup checkbox, and a dual-list scope
|
||||
# tag picker. All business logic lives in the public command — the UI only
|
||||
# collects inputs, calls it, and shows the summary.
|
||||
# Get-BulkScopeTagCatalog moved to Internal/IntuneScopeTags.ps1 so the
|
||||
# Avalonia tree can share it.
|
||||
|
||||
Reference in New Issue
Block a user