IntuneManagement 4.0.0-beta1

This commit is contained in:
Mikael Karlsson
2026-09-23 19:13:09 +10:00
commit 7869619510
892 changed files with 577109 additions and 0 deletions
@@ -0,0 +1,670 @@
# Details / object-view dialog (Show-IntuneManagerDetailedView + helpers).
# 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 Get-DetailsSettingsSignature
{
param($Form)
if(-not $Form) { return "" }
$scopeTagIds = @()
if($script:_scopeTagPropName -and $null -ne $script:_detailsAssignedIds) {
$scopeTagIds = @($script:_detailsAssignedIds | Sort-Object)
}
$descriptionEnabled = $false
try { $descriptionEnabled = [bool]($script:UIProvider.GetXamlProperty($Form, "txtObjectDescription", "IsEnabled")) } catch {}
$signature = [PSCustomObject]@{
Name = [string]($script:UIProvider.GetXamlProperty($Form, "txtObjectName", "Text"))
DescriptionEnabled = $descriptionEnabled
Description = if($descriptionEnabled) { [string]($script:UIProvider.GetXamlProperty($Form, "txtObjectDescription", "Text")) } else { $null }
ScopeTagProperty = [string]$script:_scopeTagPropName
ScopeTagIds = $scopeTagIds
}
return ($signature | ConvertTo-Json -Depth 10 -Compress)
}
function Update-DetailsColumnEditorBindings
{
param($Form)
if(-not $Form) { return }
foreach($controlName in @("txtObjectColumnsProperty", "txtObjectColumnsHeader", "chkObjectColumnOverride")) {
try {
$ctl = $Form.FindName($controlName)
if(-not $ctl) { continue }
$expression = $ctl.GetBindingExpression([System.Windows.Controls.TextBox]::TextProperty)
if(-not $expression -and $controlName -eq "chkObjectColumnOverride") {
$expression = $ctl.GetBindingExpression([System.Windows.Controls.Primitives.ToggleButton]::IsCheckedProperty)
}
if($expression) { $expression.UpdateSource() }
}
catch { }
}
}
function Get-DetailsColumnsSignature
{
param($Form)
if(-not $Form) { return "" }
Update-DetailsColumnEditorBindings -Form $Form
$columns = @()
foreach($col in @($script:colObjectProperties)) {
if(-not $col) { continue }
$columns += [PSCustomObject]@{
Property = [string]$col.Property
Header = [string]$col.Header
}
}
$signature = [PSCustomObject]@{
OverrideDefaultColumns = [bool]($script:UIProvider.GetXamlProperty($Form, "chkObjectColumnOverride", "IsChecked"))
Columns = $columns
}
return ($signature | ConvertTo-Json -Depth 20 -Compress)
}
function Test-DetailsViewHasUnsavedChanges
{
param($Form)
if(-not $Form) { return $false }
$settingsDirty = $false
$columnsDirty = $false
try {
if($script:_detailsInitialSettingsSignature) {
$settingsDirty = ((Get-DetailsSettingsSignature -Form $Form) -ne $script:_detailsInitialSettingsSignature)
}
}
catch { Write-LogDebug "Details settings dirty check failed: $($_.Exception.Message)" }
try {
if($script:_detailsInitialColumnsSignature) {
$columnsDirty = ((Get-DetailsColumnsSignature -Form $Form) -ne $script:_detailsInitialColumnsSignature)
}
}
catch { Write-LogDebug "Details columns dirty check failed: $($_.Exception.Message)" }
return ($settingsDirty -or $columnsDirty)
}
function Confirm-DetailsViewClose
{
if(-not (Test-DetailsViewHasUnsavedChanges -Form $script:detailsForm)) { return $true }
$result = $script:UIProvider.ShowMessageBox(
"You have unsaved changes in Settings or Columns.`n`nClose without saving?",
"Unsaved changes", "YesNo", "Warning")
return ($result -eq "Yes")
}
function Show-IntuneManagerDetailedView
{
param(
$FormTitle = "",
[switch]$NoLoadFull)
$selectedItem = $script:dgIntuneManagerObjects.SelectedItem
if(-not $selectedItem -and $script:dgIntuneManagerObjects.ItemsSource) {
$selectedItem = @($script:dgIntuneManagerObjects.ItemsSource |
Where-Object { $_.PSObject.Properties['IsSelected'] -and $_.IsSelected -eq $true } |
Select-Object -First 1)
if($selectedItem.Count -gt 0) { $selectedItem = $selectedItem[0] }
}
if(-not $selectedItem) { return }
if(-not $selectedItem.JsonObject) { return }
$script:detailsForm = $script:UIProvider.GetXamlObject(($script:AppUIRootFolder + "\Xaml\ObjectDetails.xaml"))
if(-not $script:detailsForm) { return }
$script:detailsForm.Tag = $selectedItem
$script:detailsForm | Add-Member -MemberType ScriptMethod -Name ConfirmClose -Value { Confirm-DetailsViewClose } -Force
if(-not $FormTitle) { $FormTitle = $selectedItem.PolicyName }
$objName = $selectedItem.Name
if($objName)
{
$FormTitle = "$FormTitle - $objName"
}
if($selectedItem.PolicyType.AddUIDetailsExtension) {
$selectedItem.PolicyType.AddUIDetailsExtension($script:detailsForm)
}
$script:UIProvider.SetXamlProperty($script:detailsForm, "txtValue", "Text", $selectedItem.JsonString)
if($selectedItem.PolicyType.AllowFullDetails -eq $false)
{
$script:UIProvider.SetXamlProperty($script:detailsForm, "btnFull", "Visibility", "Collapsed")
}
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnCopy", "Add_Click", {
$tmp = $script:detailsForm.FindName("txtValue")
if($tmp.Text) { $tmp.Text | Set-Clipboard }
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnFull", "Add_Click", {
$selectedItem = $script:detailsForm.Tag
if(-not $selectedItem) { return }
Write-Status "Get full object $($selectedItem.Name)"
[void]$selectedItem.Get()
if($selectedItem.IsFullObject)
{
$script:UIProvider.SetXamlProperty($script:detailsForm, "txtValue", "Text", $selectedItem.JsonString)
}
Write-Status ""
})
#Settings tab
$script:UIProvider.SetXamlProperty($script:detailsForm, "txtObjectName", "Text", $selectedItem.Name)
$script:UIProvider.SetXamlProperty($script:detailsForm, "txtObjectDescription", "Text", $selectedItem.Description)
# Snapshots for the save-confirmation dialog: it names the object and says
# exactly what changed, computed against these open-time values. The
# scope-tag pair resets here because the picker block below only runs for
# types WITH a scope-tag property - without the reset a previous dialog's
# sets would leak into this object's change summary.
$script:_detailsOriginalName = [string]$selectedItem.Name
$script:_detailsOriginalDesc = [string]$selectedItem.Description
$script:_detailsOriginalAssignedIds = $null
# ---- Scope tags (dual-list) ----------------------------------------------
# Type-level capability: _ScopeTagProperty defaults to "roleScopeTagIds" on every
# IntunePolicyType, but a few types clear it. Whether the property currently exists
# on the JSON is irrelevant — a user can assign tags to a policy that has none yet.
$script:_scopeTagPropName = $null
if($selectedItem.PolicyType.ScopeTagProperty) {
$script:_scopeTagPropName = [string]$selectedItem.PolicyType.ScopeTagProperty
}
if(-not $script:_scopeTagPropName) {
$script:UIProvider.SetXamlProperty($script:detailsForm, "pnlScopeTagsLabel", "Visibility", "Collapsed")
$script:UIProvider.SetXamlProperty($script:detailsForm, "grdScopeTags", "Visibility", "Collapsed")
}
else {
# Source the full tag list from the warm dependency cache
# (Initialize-TenantDependencyCache preloads it on auth). The cache list
# includes the synthetic Default (Id=0). Falls back to a live call if the
# cache is empty (e.g. a session that started before the preload landed).
$allScopeTags = @()
try {
$depObjects = Get-GraphDependencySourceObjects $selectedItem -DefaultPoliciesOnly
if($depObjects -and $depObjects.ContainsKey("ScopeTags")) {
$allScopeTags = @($depObjects["ScopeTags"])
}
}
catch { Write-LogDebug "Scope tag dependency cache lookup failed: $($_.Exception.Message)" }
if($allScopeTags.Count -eq 0) {
$allScopeTags = @(Get-GraphPolicies -PolicyType "ScopeTags" -TokenId $selectedItem._TokenId)
}
# Resolve currently-assigned IDs from the JSON. Stringify so the comparison
# works regardless of whether the cache entry stores Id as int (synthetic
# Default) or string (real tags from Graph). Safe when the property is
# absent — PowerShell yields $null which the empty-array cast swallows.
$assignedIdSet = [System.Collections.Generic.HashSet[string]]::new()
$currentIds = $null
if($selectedItem.Object -and
$selectedItem.Object.PSObject.Properties[$script:_scopeTagPropName]) {
$currentIds = $selectedItem.Object.$script:_scopeTagPropName
}
foreach($id in @($currentIds)) {
if($null -ne $id) { [void]$assignedIdSet.Add([string]$id) }
}
# Project to plain PSCustomObject {Id, Name} so the ListBox binding works
# uniformly. The cache mixes two shapes: the synthetic Default is a real
# PSCustomObject (renders fine), but real tags are IntunePolicyBase class
# instances with Name added as a ScriptProperty via Add-Member — WPF's
# DisplayMemberPath reflection on class instances doesn't reliably see
# ScriptProperties, so those rows render blank. Projecting normalizes both.
#
# Dedup by Id while projecting. The dependency cache can contain BOTH the
# synthetic Default and the real Default scope tag from Graph (both have
# Id "0"); without dedup, "Default" rendered twice and a single Assigned
# move could leave a duplicate stuck in the wrong list.
$script:_detailsAllScopeTags = @()
$seenIds = [System.Collections.Generic.HashSet[string]]::new()
foreach($tag in $allScopeTags) {
$idStr = [string]$tag.Id
if(-not $seenIds.Add($idStr)) { continue }
$script:_detailsAllScopeTags += [PSCustomObject]@{ Id = $idStr; Name = [string]$tag.Name }
}
$script:_detailsAllScopeTags = @($script:_detailsAllScopeTags | Sort-Object Name)
# Single source of truth for which tags are currently assigned. Both
# ObservableCollections are derived from this set + the full list, so
# mutual exclusion is structural — Available is always "everything that
# isn't in the assigned set", no chance of duplicate or drift bugs.
$script:_detailsAssignedIds = [System.Collections.Generic.HashSet[string]]::new()
foreach($id in $assignedIdSet) { [void]$script:_detailsAssignedIds.Add($id) }
$script:_detailsOriginalAssignedIds = [System.Collections.Generic.HashSet[string]]::new($script:_detailsAssignedIds)
$script:colAvailableScopeTags = [System.Collections.ObjectModel.ObservableCollection[object]]::new()
$script:colAssignedScopeTags = [System.Collections.ObjectModel.ObservableCollection[object]]::new()
$script:_syncDetailsScopeTagLists = {
$script:colAvailableScopeTags.Clear()
$script:colAssignedScopeTags.Clear()
foreach($tag in $script:_detailsAllScopeTags) {
if($script:_detailsAssignedIds.Contains($tag.Id)) {
[void]$script:colAssignedScopeTags.Add($tag)
} else {
[void]$script:colAvailableScopeTags.Add($tag)
}
}
}
& $script:_syncDetailsScopeTagLists
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstAvailableScopeTags", "ItemsSource", $script:colAvailableScopeTags)
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstAssignedScopeTags", "ItemsSource", $script:colAssignedScopeTags)
# Click handlers reference only $script:-scoped state so they keep access
# to module-private helpers (Get-XamlProperty etc.). Move = mutate the
# canonical assigned-ids set, then re-derive both lists.
$script:_moveScopeTags = {
param([string]$Action, [string[]]$Ids)
if($Ids.Count -eq 0) { return }
if($Action -eq "assign") {
foreach($id in $Ids) { [void]$script:_detailsAssignedIds.Add([string]$id) }
} else {
foreach($id in $Ids) { [void]$script:_detailsAssignedIds.Remove([string]$id) }
}
& $script:_syncDetailsScopeTagLists
}
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnScopeTagAssign", "Add_Click", {
try {
$lb = $script:detailsForm.FindName("lstAvailableScopeTags")
if(-not $lb -or $lb.SelectedItems.Count -eq 0) {
Write-Status "Select one or more scope tag(s) in the Available list first"
return
}
$ids = @($lb.SelectedItems | ForEach-Object { [string]$_.Id })
& $script:_moveScopeTags "assign" $ids
}
catch { Write-LogError "Scope-tag assign handler failed" $_.Exception }
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnScopeTagUnassign", "Add_Click", {
try {
$lb = $script:detailsForm.FindName("lstAssignedScopeTags")
if(-not $lb -or $lb.SelectedItems.Count -eq 0) {
Write-Status "Select one or more scope tag(s) in the Assigned list first"
return
}
$ids = @($lb.SelectedItems | ForEach-Object { [string]$_.Id })
& $script:_moveScopeTags "unassign" $ids
}
catch { Write-LogError "Scope-tag unassign handler failed" $_.Exception }
})
# Double-click toggles. Lets the user assign/unassign without aiming for
# the arrow buttons, which is the more common interaction pattern.
$script:UIProvider.AddXamlEvent($script:detailsForm, "lstAvailableScopeTags", "Add_MouseDoubleClick", {
try {
$ids = @(@($script:UIProvider.GetXamlProperty($script:detailsForm, "lstAvailableScopeTags", "SelectedItems")) | ForEach-Object { [string]$_.Id })
& $script:_moveScopeTags "assign" $ids
}
catch { Write-LogError "Scope-tag double-click (available) failed" $_.Exception }
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "lstAssignedScopeTags", "Add_MouseDoubleClick", {
try {
$ids = @(@($script:UIProvider.GetXamlProperty($script:detailsForm, "lstAssignedScopeTags", "SelectedItems")) | ForEach-Object { [string]$_.Id })
& $script:_moveScopeTags "unassign" $ids
}
catch { Write-LogError "Scope-tag double-click (assigned) failed" $_.Exception }
})
}
$script:_detailsInitialSettingsSignature = Get-DetailsSettingsSignature -Form $script:detailsForm
# ToDo: Disable description if property does not exist
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectSettingsSave", "Add_Click", {
$selectedItem = $script:detailsForm.Tag
if(-not $selectedItem) { return }
# Name the object and say what changed - computed against the
# open-time snapshots so the dialog is meaningful.
$originalName = if($script:_detailsOriginalName) { $script:_detailsOriginalName } else { [string]$selectedItem.Name }
$newName = [string]($script:UIProvider.GetXamlProperty($script:detailsForm, "txtObjectName", "Text"))
$newDesc = [string]($script:UIProvider.GetXamlProperty($script:detailsForm, "txtObjectDescription", "Text"))
$changes = @()
if($newName -and $newName -cne $originalName) { $changes += "New name: '$newName'" }
if($newDesc -cne [string]$script:_detailsOriginalDesc) { $changes += "Description updated" }
if($null -ne $script:_detailsAssignedIds -and $null -ne $script:_detailsOriginalAssignedIds)
{
$added = @($script:_detailsAssignedIds | Where-Object { -not $script:_detailsOriginalAssignedIds.Contains($_) }).Count
$removed = @($script:_detailsOriginalAssignedIds | Where-Object { -not $script:_detailsAssignedIds.Contains($_) }).Count
if($added -or $removed) { $changes += "Scope tags: $added added, $removed removed" }
}
$confirmMsg = "Are you sure you want to update '$originalName'?"
if($changes.Count) { $confirmMsg += "`n`n" + ($changes -join "`n") }
if(($script:UIProvider.ShowMessageBox($confirmMsg, "Update object information?", "YesNo", "Warning")) -ne "Yes")
{
return
}
Write-Status "Update object information for $($selectedItem.Name)"
$nameValue = ($script:UIProvider.GetXamlProperty($script:detailsForm, "txtObjectName", "Text"))
if(-not $nameValue)
{
$script:UIProvider.ShowMessageBox("Name property must not be empty!", "Error", "OK", "Error")
return
}
# Save settings here...
$nameProp = (?? $selectedItem.PolicyType.NameProperty "displayName")
$idProp = (?? $selectedItem.PolicyType.IDProperty "id")
$updateHT = @{}
$updateHT.Add($idProp, $selectedItem.ID)
$updateHT.Add($nameProp, $nameValue)
if(($selectedItem.Object."@odata.type"))
{
$updateHT.Add("@odata.type", ($selectedItem.JsonObject."@odata.type"))
}
if(($script:UIProvider.GetXamlProperty($script:detailsForm, "txtObjectDescription", "IsEnabled")) -eq $true)
{
$updateHT.Add("description", ($script:UIProvider.GetXamlProperty($script:detailsForm, "txtObjectDescription", "Text")))
if($null -eq $updateHT["description"] -and $selectedItem.Description)
{
# If description is null, remove it
$updateHT["description"] = ""
}
elseif($null -eq $updateHT["description"])
{
$updateHT.Remove("description")
}
}
# Scope tags: write the assigned-list IDs back under the policy's
# ScopeTagProperty (typically roleScopeTagIds). Read from the canonical
# $script:_detailsAssignedIds set (single source of truth) — the
# ObservableCollection is just a UI derivative.
if($script:_scopeTagPropName -and $null -ne $script:_detailsAssignedIds) {
$assignedIds = @($script:_detailsAssignedIds)
# Soft guard: Intune treats roleScopeTagIds as containing "0" (Default)
# by default. A non-empty list that omits Default is unusual and often
# rejected by Graph; warn before committing rather than letting the
# PATCH 400 silently. Empty list isn't flagged — that reads as a
# deliberate reset.
if($assignedIds.Count -gt 0 -and $assignedIds -notcontains "0") {
$proceed = $script:UIProvider.ShowMessageBox(
"The 'Default' scope tag (Id 0) is not in the assigned list.`n`nIntune usually requires Default to be present and the API call may fail.`n`nContinue anyway?",
"Default scope tag missing", "YesNo", "Warning")
if($proceed -ne "Yes") {
Write-Status ""
return
}
}
$updateHT.Add($script:_scopeTagPropName, $assignedIds)
}
$updateObj = [PSCustomObject]$updateHT
$api = "$($selectedItem.PolicyType.API)/$($selectedItem.ID)"
$json = $updateObj | ConvertTo-Json -Depth 20
$ret = Invoke-MSGraphAPI $api -HttpMethod "PATCH" -Content $json -FullResponseObject -TokenId $selectedItem._TokenId
Write-Status ""
if($ret.Success -eq $false)
{
Write-log "Failed to update object information updated for $($selectedItem.Name). Error: $($ret.StatusCode) - $($ret.StatusDescription)"
$script:UIProvider.ShowMessageBox("Object information could not be verified!`n`nCheck the log file", "Update warning", "OK", "Warning")
}
else {
Write-log "Object information updated for $($selectedItem.Name)"
# Refresh the in-memory object so the parent grid's "ScopeTags" column
# reflects what we just sent. Clearing _ScopeTags forces the lazy
# getter to re-resolve from the dependency cache next access; updating
# Object.<prop> keeps subsequent reads (and a re-open of this dialog)
# in sync without a round-trip. Items.Refresh() then asks WPF to
# re-read every bound cell — without it, the DataGrid keeps showing
# the pre-Save snapshot because PSCustomObject doesn't notify.
if($script:_scopeTagPropName -and $null -ne $script:_detailsAssignedIds) {
$assignedIds = @($script:_detailsAssignedIds)
try {
$selectedItem.Object.$script:_scopeTagPropName = $assignedIds
} catch { Write-LogDebug "Failed to update in-memory ScopeTags array: $($_.Exception.Message)" }
$selectedItem._ScopeTags = $null
$selectedItem._ScopeTagsString = $null
}
# Name / description may also have changed in this Save — refresh once
# at the end so every cell of the affected row re-reads, not just the
# ScopeTags column.
try { $script:dgIntuneManagerObjects.Items.Refresh() }
catch { Write-LogDebug "DataGrid refresh failed: $($_.Exception.Message)" }
$script:_detailsInitialSettingsSignature = Get-DetailsSettingsSignature -Form $script:detailsForm
}
})
#Columns tab
$skipBasicProperties = @("JsonObject", "Object", "JsonString", "TenantId", "TokenId", "IsSelected")
$arrBasicProperties = @()
foreach($prop in ($selectedItem.PSObject.Properties | Where-Object Name -notin $skipBasicProperties))
{
$arrBasicProperties += ([PSCustomObject]@{ Name=$prop.Name;Value=$prop;Source="Basic" })
}
$arrBasicProperties = $arrBasicProperties | Sort-Object -Property Name
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstBasicProperties", "ItemsSource", $arrBasicProperties)
$arrObjectProperties = @()
$skipObjectProperties = @("@odata.editLink")
foreach($prop in ($selectedItem.Object.PSObject.Properties | Where-Object { $_.Name -notin $skipObjectProperties -and $_.Name -notlike "*?@odata.*" }))
{
$arrObjectProperties += ([PSCustomObject]@{ Name=$prop.Name;Value=$prop;Source="Object" })
}
$arrObjectProperties = $arrObjectProperties | Sort-Object -Property Name
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstObjectProperties", "ItemsSource", $arrObjectProperties)
$script:colObjectProperties = [System.Collections.ObjectModel.ObservableCollection[object]]::new()
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstObjectColumns", "ItemsSource", $script:colObjectProperties)
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsReset", "Add_Click", {
<#
if(([System.Windows.MessageBox]::Show("Are you sure you want to reset columns to default?", "Reset Columns?", "YesNo", "Warning")) -ne "Yes")
{
return
}
$selectedType = Get-SelectedObjectTypeString
Remove-SettingStoreValue "IntuneManager\ObjectColumns\$selectedType" "$($script:IntuneManagerSelectedObject.Id)"
#>
$script:colObjectProperties.Clear()
Show-ObjectDefaultColumnsSettings -Reset
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "lstObjectColumns", "Add_SelectionChanged", {
$script:UIProvider.SetXamlProperty($script:detailsForm, "grdObjectColumns", "DataContext", ($script:UIProvider.GetXamlProperty($script:detailsForm, "lstObjectColumns", "SelectedItem")))
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnBasicColumnsAdd", "Add_Click", {
$selectedItem = $script:UIProvider.GetXamlProperty($script:detailsForm, "lstBasicProperties", "SelectedItem")
if($selectedItem) {
$script:colObjectProperties.Add(([ObjectColumnInfo]::new($selectedItem.Name, "")))
}
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsAdd", "Add_Click", {
$selectedItem = $script:UIProvider.GetXamlProperty($script:detailsForm, "lstObjectProperties", "SelectedItem")
if($selectedItem) {
$script:colObjectProperties.Add(([ObjectColumnInfo]::new("Object." + $selectedItem.Name, "")))
}
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsMoveUp", "Add_Click", {
$selectedIndex = $script:UIProvider.GetXamlProperty($script:detailsForm, "lstObjectColumns", "SelectedIndex")
if($selectedIndex -gt 0)
{
$tmpObj = $script:colObjectProperties[$selectedIndex]
$script:colObjectProperties.RemoveAt($selectedIndex)
$tmpObj = $script:colObjectProperties.Insert(($selectedIndex-1),$tmpObj)
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstObjectColumns", "SelectedIndex", ($selectedIndex-1))
}
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsMoveDown", "Add_Click", {
$selectedIndex = $script:UIProvider.GetXamlProperty($script:detailsForm, "lstObjectColumns", "SelectedIndex")
if($selectedIndex -ge 0 -and $selectedIndex -lt ($script:colObjectProperties.Count-1)) {
$tmpObj = $script:colObjectProperties[$selectedIndex]
$script:colObjectProperties.RemoveAt($selectedIndex)
$tmpObj = $script:colObjectProperties.Insert(($selectedIndex+1),$tmpObj)
$script:UIProvider.SetXamlProperty($script:detailsForm, "lstObjectColumns", "SelectedIndex", ($selectedIndex+1))
}
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsDelete", "Add_Click", {
$selectedIndex = $script:UIProvider.GetXamlProperty($script:detailsForm, "lstObjectColumns", "SelectedIndex")
if($selectedIndex -ge 0) {
if(($script:UIProvider.ShowMessageBox("Are you sure you want to remove selected column?", "Remove Columns?", "YesNo", "Warning")) -ne "Yes")
{
return
}
$script:colObjectProperties.RemoveAt($selectedIndex)
}
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsClear", "Add_Click", {
if(($script:UIProvider.ShowMessageBox("Are you sure you want to clear custom column settings?", "Clear Custom Columns?", "YesNo", "Warning")) -ne "Yes") {
return
}
$script:colObjectProperties.Clear()
})
$script:UIProvider.AddXamlEvent($script:detailsForm, "btnObjectColumnsSave", "Add_Click", {
$selectedType = Get-SelectedObjectTypeString
if(-not $selectedType) { return }
if(($script:UIProvider.ShowMessageBox("Are you sure you want to save custom column settings?", "Save Custom Columns?", "YesNo", "Warning")) -ne "Yes")
{
return
}
if($script:colObjectProperties.Count -gt 0)
{
$arrCols = @()
if(($script:UIProvider.GetXamlProperty($script:detailsForm, "chkObjectColumnOverride", "IsChecked")) -eq $true)
{
$arrCols += "0"
}
foreach($colProp in $script:colObjectProperties)
{
$tmp = $colProp.Property
if($colProp.Header -and $colProp.Header -cne $colProp.Property)
{
$tmp = "$($tmp)=$($colProp.Header)"
}
$arrCols += $tmp
}
$strCols = $arrCols -join ","
Save-SettingStoreValue "IntuneManager\ObjectColumns\$selectedType" "$($script:IntuneManagerSelectedObject.Id)" $strCols
}
else
{
$strCols = $null
Remove-SettingStoreValue "IntuneManager\ObjectColumns\$selectedType" "$($script:IntuneManagerSelectedObject.Id)"
}
Show-ObjectDefaultColumnsSettings
$script:_detailsInitialColumnsSignature = Get-DetailsColumnsSignature -Form $script:detailsForm
})
Show-ObjectDefaultColumnsSettings
$script:_detailsInitialColumnsSignature = Get-DetailsColumnsSignature -Form $script:detailsForm
# Show dialog
$script:UIProvider.ShowModalForm($FormTitle, $detailsForm)
}
function Show-ObjectDefaultColumnsSettings
{
param([switch]$Reset)
if($Reset -ne $true) {
$selectedType = Get-SelectedObjectTypeString
if(-not $selectedType) { return }
$strColSettings = Get-SettingStoreValue "IntuneManager\ObjectColumns\$selectedType" "$($script:IntuneManagerSelectedObject.Id)"
}
else {
$strColSettings = ""
}
$script:colObjectProperties.Clear()
$defaultColumns = $script:IntuneManagerSelectedObject.ViewProperties
if($strColSettings)
{
$arrColSettings += $strColSettings -split ",|;"
$script:UIProvider.SetXamlProperty($script:detailsForm, "chkObjectColumnOverride", "IsChecked", ($arrColSettings.Count -gt 0 -and $arrColSettings[0] -eq "0"))
$script:UIProvider.SetXamlProperty($script:detailsForm, "lblObjectColumnsConfig", "Text", $strColSettings)
$start = 0
if($arrColSettings.Count -gt 0 -and ($arrColSettings[0] -eq "0" -or $arrColSettings[0] -eq "1"))
{
$start++
}
$colArr = @()
for($i = $start;$i -lt $arrColSettings.Count;$i++)
{
$colProp,$colHeader= $arrColSettings[$i].Split("=")
if(-not $colHeader)
{
$colHeader = $colProp
}
$script:colObjectProperties.Add([ObjectColumnInfo]::new($colProp,$colHeader))
$colArr += $colProp
}
if(($arrColSettings.Count -eq 0 -or $arrColSettings[0] -ne "0"))
{
$tmpArr = $defaultColumns
$tmpArr += $colArr
$colArr = $tmpArr
}
$script:UIProvider.SetXamlProperty($script:detailsForm, "lblObjectColumnsConfig", "Text", ("$(($colArr-join ','))"))
}
else
{
$script:UIProvider.SetXamlProperty($script:detailsForm, "lblObjectColumnsConfig", "Text", "$(($defaultColumns -join ',')) (Default)")
}
}
# Match-resolution helpers (Get-IntuneImportPolicyReferenceTokens,
# Normalize-IntuneImportPolicyName, New-IntuneImportMatchResult,
# Resolve-IntuneImportUpdateTarget) moved to Internal/IntuneManager.ps1
# so the Avalonia tree can share them.