Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Created December 7, 2023 18:44
Show Gist options
  • Save joerodgers/2e29a650eb0dca9d1413090b44d8c3a1 to your computer and use it in GitHub Desktop.
Save joerodgers/2e29a650eb0dca9d1413090b44d8c3a1 to your computer and use it in GitHub Desktop.
Reports web part references on modern and classic pages
#requires -modules "PnP.PowerShell"
class WebPartReference
{
[string]
$SiteUrl
[string]
$WebUrl
[string]
$FileName
[string]
$PageType
[string]
$Title
[string]
$WebPartId
[string]
$WebPartType
}
function Test-WebPartProperties
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true)]
[string[]]
$PropertiesToCheck,
[parameter(Mandatory=$true)]
[System.Collections.Generic.Dictionary[string,object]]
$WebPartProperties
)
begin
{
}
process
{
foreach( $property in $PropertiesToCheck )
{
if( -not $WebPartProperties.ContainsKey( $property) )
{
return $false
}
}
return $true
}
end
{
}
}
function Get-ModernWebPartType
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true)]
[Guid]
$WebPartId
)
begin
{
}
process
{
$mappings = @{
"c4bd7b2f-7b6e-4599-8485-16504575f590" = "Hero"
"8c88f208-6c77-4bdb-86a0-0c47b4316588" = "News"
"a5df8fdf-b508-4b66-98a6-d83bc2597f63" = "Newsreel"
"af8be689-990e-492a-81f7-ba3e4cd3ed9c" = "ImageGallery"
"c70391ea-0b10-4ee9-b2b4-006d3fcad0cd" = "QuickLinks"
"daf0b71c-6de8-4ef7-b511-faae7c388708" = "ContentRollup"
"eb95c819-ab8f-4689-bd03-0c2d65d47b1f" = "SiteActivity"
"20745d7d-8581-4a6c-bf26-68279bc123fc" = "Events"
"f6fdf4f8-4a24-437b-a127-32e66a5dd9b4" = "Twitter"
"275c0095-a77e-4f6d-a2a0-6a7626911518" = "Stream"
"cb3bfe97-a47f-47ca-bffb-bb9a5ff83d75" = "Yammer"
"7cba020c-5ccb-42e8-b6fc-75b3149aba7b" = "Sites"
"b519c4f1-5cf7-4586-a678-2f1c62cc175a" = "RecentDocuments"
"cbe7b0a9-3504-44dd-a3a3-0e5cacd07788" = "TitleRegion"
"e84a8ca2-f63c-4fb9-bc0b-d8eef5ccb22b" = "OrganizationChart"
"2f3b693c-1054-419c-af04-fee2782b414f" = "OfficeFeed"
"d0a64d22-555c-44e4-b120-aed62c263632" = "CompanyFeed"
"e377ea37-9047-43b9-8cdb-a761be2f8e09" = "BingMap"
"6410b3b6-d440-4663-8744-378976dc041e" = "LinkPreview"
"58fcd18b-e1af-4b0a-b23b-422c2c52d5a2" = "PowerBIReportEmbed"
"91a50c94-865f-4f5c-8b4e-e49659e69772" = "QuickChart"
"6676088b-e28e-4a90-b9cb-d0d0303cd2eb" = "GroupCalendar"
"f92bf067-bc19-489e-a556-7fe95f508720" = "List"
"7f718435-ee4d-431c-bdbf-9c4ff326f46e" = "People"
"71c19a43-d08c-4178-8218-4df8554c0b0e" = "CustomMessageRegion"
"2161a1c6-db61-4731-b97c-3cdb303f7cbb" = "Divider"
"b19b3b9e-8d13-4fec-a93c-401a091c0707" = "MicrosoftForms"
"8654b779-4886-46d4-8ffb-b5ed960ee986" = "Spacer"
"243166f5-4dc3-4fe2-9df2-a7971b546a0a" = "ClientWebPart"
"9d7e898c-f1bb-473a-9ace-8b415036578b" = "PowerApps"
"7b317bca-c919-4982-af2f-8399173e5a1e" = "CodeSnippet"
"cf91cf5d-ac23-4a7a-9dbc-cd9ea2a4e859" = "PageFields"
"868ac3c3-cad7-4bd6-9a1c-14dc5cc8e823" = "Weather"
"544dd15b-cf3c-441b-96da-004d5a8cea1d" = "YouTube"
"62cac389-787f-495d-beca-e11786162ef4" = "CountDown"
"a8cd4347-f996-48c1-bcfb-75373fed2a27" = "ListProperties"
"1ef5ed11-ce7b-44be-bc5e-4abd55101d16" = "MarkDown"
"39c4c1c2-63fa-41be-8cc2-f6c0b49b253d" = "Planner"
}
if( $mappings.ContainsKey( $WebPartId.ToString() ) )
{
return $mappings[$WebPartId.ToString()]
}
return $WebPartId.ToString()
}
end
{
}
}
function Get-ClassicWebPartType
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true)]
[System.Collections.Generic.Dictionary[string,object]]
$Properties
)
begin
{
}
process
{
# logic taken from https://github.com/pnp/pnpframework/blob/dev/src/lib/PnP.Framework/Modernization/Pages/BasePage.cs#L611
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "ListUrl", "ListId", "Xsl", "JSLink", "ShowTimelineIfAvailable" )
{
return "XsltListView"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "ListViewXml", "ListName", "ListId", "ViewContentTypeId", "PageType" )
{
return "ListView"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "AutoPlay", "MediaSource", "Loop", "IsPreviewImageSourceOverridenForVideoSet", "PreviewImageSource" )
{
return "Media"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "LibraryGuid", "Layout", "Speed", "ShowToolbar", "ViewGuid" )
{
return "PictureLibrarySlideshow"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "ConnectionPointEnabled", "ChartXml", "DataBindingsString", "DesignerChartTheme" )
{
return "Chart"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "NumberLimit", "DisplayType", "MembershipGroupId", "Toolbar" )
{
return "Members"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "MinRuntimeVersion", "WindowlessMode", "CustomInitParameters", "Url", "ApplicationXml" )
{
return "Silverlight"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "FeatureId", "ProductWebId", "ProductId" )
{
return "Client"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "Content" )
{
return "ScriptEditor"
}
if( Test-WebPartProperties -WebPartProperties $Properties -PropertiesToCheck "CatalogIconImageUrl", "AllowEdit", "TitleIconImageUrl", "ExportMode" )
{
return "SPUserCode"
}
return "Unknown"
}
end
{
}
}
function Get-ClassicSitePageWebPart
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[Microsoft.SharePoint.Client.ListItem]
$ListItem
)
begin
{
}
process
{
$webparts = Get-PnPWebPart -ServerRelativePageUrl $ListItem.FieldValues.FileRef -ErrorAction Stop
foreach( $webpart in $webparts )
{
$type = Get-ClassicWebPartType -Properties $webpart.WebPart.Properties.FieldValues
$wpr = [WebPartReference]::new()
$wpr.FileName = $ListItem.FieldValues.FileLeafRef
$wpr.PageType = "Classic"
$wpr.SiteUrl = ""
$wpr.Title = $webpart.WebPart.Title
$wpr.WebPartId = $webpart.Id
$wpr.WebUrl = ""
$wpr.WebPartType = $type
$wpr
}
}
end
{
}
}
function Get-ModernSitePageWebPart
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true,ValueFromPipeline=$true)]
[Microsoft.SharePoint.Client.ListItem]
$ListItem
)
begin
{
}
process
{
$webparts = Get-PnPPage -Identity $ListItem.FieldValues.FileLeafRef -ErrorAction Stop | Get-PnPPageComponent -ErrorAction Stop
foreach( $webpart in $webparts )
{
$type = Get-ModernWebPartType -WebPartId $webpart.WebPartId
$wpr = [WebPartReference]::new()
$wpr.FileName = $ListItem.FieldValues.FileLeafRef
$wpr.PageType = "Modern"
$wpr.SiteUrl = ""
$wpr.Title = $webpart.Title
$wpr.WebPartId = $webpart.WebPartId
$wpr.WebUrl = ""
$wpr.WebPartType = $type
$wpr
}
}
end
{
}
}
function Get-SitePageWebPart
{
[CmdletBinding()]
param
(
[parameter(Mandatory=$true,ParameterSetName="Site")]
[Microsoft.SharePoint.Client.Site]
$Site,
[parameter(Mandatory=$true,ParameterSetName="Web")]
[Microsoft.SharePoint.Client.Web]
$Web,
[parameter(Mandatory=$true,ParameterSetName="Item")]
[Microsoft.SharePoint.Client.ListItem]
$ListItem,
[parameter(Mandatory=$false)]
[switch]
$IncludeClassicPages
)
begin
{
}
process
{
if( $PSCmdlet.ParameterSetName -eq "Site" )
{
Write-Verbose "[$(Get-Date)] - Processing Site: $($Site.Url)"
$webs = Get-PnPSubWeb -Recurse -IncludeRootWeb
$webPartReferences = foreach( $web in $webs )
{
Get-SitePageWebPart -Web $web -IncludeClassicPages:$IncludeClassicPages.IsPresent
}
#foreach( $webPartReference in $webPartReferences )
#{
# $webPartReference.SiteUrl = $Site.Url
#}
return $webPartReferences
}
if( $PSCmdlet.ParameterSetName -eq "Web" )
{
Write-Verbose "[$(Get-Date)] - Processing Web: $($Web.Url)"
$connection = Get-PnPConnection
if( $connection.Url -ne $web.Url )
{
# connect to target web
Connect-PnPOnline `
-Url $web.Url `
-ClientId $connection.ClientId `
-Thumbprint $connection.Certificate.Thumbprint `
-Tenant $connection.Tenant `
-ErrorAction Stop
}
$items = $null
# ran into a scenario where there was "SitePages" instead of "Site Pages" so look for both
if( $sitepages = Get-PnPList -Identity "Site Pages" )
{
$items = Get-PnPListItem -List $sitepages -PageSize 5000
}
elseif( $sitepages = Get-PnPList -Identity "SitePages" )
{
$items = Get-PnPListItem -List $sitepages -PageSize 5000
}
if( $null -ne $items )
{
$webPartReferences = foreach( $item in $items )
{
Write-Verbose "Getting parts on page: $($item.FieldValues.FileLeafRef)"
Get-SitePageWebPart -ListItem $item -IncludeClassicPages:$IncludeClassicPages.IsPresent
}
#foreach( $webPartReference in $webPartReferences )
#{
# $webPartReference.WebUrl = $Web.Url
#}
return $webPartReferences
}
return $null
}
if( $PSCmdlet.ParameterSetName -eq "Item" )
{
if( $ListItem.FieldValues["ClientSideApplicationId"] -eq "b6917cb1-93a0-4b97-a84d-7cf49975d4ec" )
{
Write-Verbose "[$(Get-Date)] - Processing modern page: $($ListItem.FieldValues.FileLeafRef)"
$results = Get-ModernSitePageWebPart -ListItem $ListItem
}
elseif( $IncludeClassicPages.IsPresent )
{
Write-Verbose "[$(Get-Date)] - Processing clasic page: $($ListItem.FieldValues.FileLeafRef)"
$results = Get-ClassicSitePageWebPart -ListItem $ListItem
}
$s = Get-PnPSite
$w = Get-PnPWeb
foreach( $result in $results )
{
$result.SiteUrl = $s.Url
$result.WebUrl = $w.Url
}
return $results
}
}
end
{
}
}
# permissions required: SharePoint > Application > Sites.FullControl.All
Connect-PnPOnline `
-Url "https://$env:O365_TENANT.sharepoint.com/sites/teamsite" `
-ClientId $env:O365_CLIENTID `
-Thumbprint $env:O365_THUMBPRINT `
-Tenant $env:O365_TENANTID
# scan a web
$web = Get-PnPWeb
# $results = Get-SitePageWebPart -Web $web -IncludeClassicPages -Verbose
# scan an entire site
$site = Get-PnPSite
$results = Get-SitePageWebPart -Site $site -Verbose
# export results
$timestamp = Get-Date -Format FileDateTime
$results | Export-Csv -Path "WebPartInstances_$timestamp.csv" -NoTypeInformation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment