7

When looking at the broken links report or broken links in the bumper, sometimes it will reference a broken link in the Final Renderings. But no info is available to know which rendering is doing it. Placeholder settings, when missing show a different icon and if the rendering is completely missing from Sitecore, a bid red X is showing. But being able to identify a rendering with a bad datasource would be helpful.

Has anyone found a quick way to identify bad renderings?

enter image description here

  • Have you tried the broken links report with SPE? – Michael West Apr 21 at 18:54
  • No. But will that tell me the specific rendering name/id? – Chris Auer Apr 21 at 19:24
6

I had to make a modification to the existing Broken Links report included with SPE to show the broken renderings.

    <#
    .SYNOPSIS
        Lists the items with broken links searching all or the latest version in the current language.

    .NOTES
        Adam Najmanowicz, Michael West, Daniel Scherrer
        Adapted from the Advanced System Reporter module & Daniel Scherrer's external links checker: 
        https://gist.github.com/daniiiol/143db3e2004afe9a55c1dd3e33048940
#>

$database = "master"
$root = Get-Item -Path (@{$true="$($database):\content\home"; $false="$($database):\content"}[(Test-Path -Path "$($database):\content\home")])
$linksToCheck =  @("internal")
$linkTypes = [ordered]@{"Internal Links"="internal";"External Links (General Link only)"="external"};

$versionOptions = [ordered]@{
    "Latest"="1"
}

$props = @{
    Parameters = @(
        @{Name="root"; Title="Choose the report root"; Tooltip="Only items in this branch will be returned."; Columns=9},
        @{Name="searchVersion"; Value="1"; Title="Version"; Options=$versionOptions; Tooltip="Choose a version."; Columns="3"; Placeholder="All"},
        @{Name="linksToCheck"; Title="Link types to check"; Options=$linkTypes; Tooltip="which link types you want to check"; Editor="checklist"} 
    )
    Title = "Broken Links Report"
    Description = "Choose the criteria for the report."
    Width = 550
    Height = 300
    ShowHints = $true
    Icon = [regex]::Replace($PSScript.Appearance.Icon, "Office", "OfficeWhite", [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
}

$result = Read-Variable @props

if($result -eq "cancel"){
    exit
}

filter HasBrokenLink {
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [Sitecore.Data.Items.Item]$Item,

        [Parameter()]
        [bool]$IncludeAllVersions
    )

    if(!$Item) { return }
    if($linksToCheck.Contains("internal")) {
        $brokenLinks = $item.Links.GetBrokenLinks($IncludeAllVersions)
        if($brokenlinks -ne $null -and $brokenlinks.Length -gt 0) {
            $finalRenderings = Get-Rendering -Item $Item -FinalLayout
            $sharedRenderings = Get-Rendering -Item $Item
            $uniqueIdLookup = New-Object System.Collections.Generic.HashSet[string]
            foreach($brokenLink in $brokenLinks) {
                if([ID]::IsNullOrEmpty($brokenLink.SourceFieldID)) { continue }
                $fieldItem = Get-Item -Path "$($Item.Database.Name):\" -ID $brokenLink.SourceFieldID
                if(!$fieldItem) { continue }
                $renderings = & {
                    switch($fieldItem.ID) {
                        "{04BF00DB-F5FB-41F7-8AB7-22408372A981}" {
                            $finalRenderings
                        }
                        default {
                            $sharedRenderings
                        }
                    }
                }
                $brokenRendering = $renderings | Where-Object { $_.Datasource -eq $brokenLink.TargetPath -and !$uniqueIdLookup.Contains($_.UniqueId) } | Select-Object -First 1
                if(!$brokenRendering) { continue }
                $uniqueIdLookup.Add($brokenRendering.UniqueId) > $null
                $brokenRenderingItem = Get-Item -Path "master:" -ID $brokenRendering.ItemId
                $brokenItem = [pscustomobject]@{
                    "ID"=$Item.ID
                    "Icon"=$Item.__Icon
                    "DisplayName"=$Item.DisplayName
                    "ItemPath"=$Item.ItemPath
                    "Version"=$Item.Version
                    "Language"=$Item.Language
                    "__Updated"=$Item.__Updated
                    "__Updated by"=$Item."__Updated by"
                    "Link Field"=$fieldItem.Name
                    "Rendering" = $brokenRenderingItem.Name
                    "Target Path"=$brokenLink.TargetPath
                    "Status Code"="Missing Target Item"
                    "BrokenLink"=$brokenLink
                    "Link Type" = "Internal"
                }

                $brokenItem

            }
        }
    }

    if($linksToCheck.Contains("external")){
        if($IncludeAllVersions){
            $allItems = Get-Item "$($item.Database.Name):" -Version * -Language * -Id $item.Id
        } else {
            $allItems = @(Get-Item "$($item.Database.Name):" -Language * -Id $item.Id)
        }
        foreach($checkedItem in $allItems){
            foreach($field in $checkedItem.Fields) {
                if ($field.Type -eq 'General Link' -and $field.Value -like '*linktype="external"*') { 
                    $found = $field.Value -match '.*url="(.*?)".*'
                    if($found) {
                        $url = $matches[1]
                    }

                    try{ 
                        $response = Invoke-WebRequest -Uri $url -UseBasicParsing -Method head
                    } 
                    catch {
                        $statuscode = $_.Exception.Response.StatusCode.Value__

                        if(!$statuscode) {
                            $statuscode = "Not reachable"
                        }

                        $brokenItem = [pscustomobject]@{
                            "ID"=$checkedItem.ID
                            "Icon"=$checkedItem.__Icon
                            "DisplayName"=$checkedItem.DisplayName
                            "ItemPath"=$checkedItem.ItemPath
                            "Version"=$checkedItem.Version
                            "Language"=$checkedItem.Language
                            "__Updated"=$checkedItem.__Updated
                            "__Updated by"=$checkedItem."__Updated by"
                            "Link Field"=$field.Name
                            "Target Path"=$url
                            "Status Code"=$statuscode
                            "Link Type"="External"
                        }

                        $brokenItem
                    }
                }
            }
        }
    }
}

$items = @($root) + @(($root.Axes.GetDescendants() | Initialize-Item)) | HasBrokenLink -IncludeAllVersions (!$searchVersion)

if($items.Count -eq 0){
    Show-Alert "There are no items found which have broken links in the current language."
} else {
    $props = @{
        Title = "Broken Links Report"
        InfoTitle = "$($items.Count) items with broken links found!"
        InfoDescription = "The report checked for $($linksToCheck -join ' & ') links in $(@('all versions','latest versions')[[byte]($searchVersion='1')]) of items."
        MissingDataMessage = "There are no items found which have broken links in the current language."
        PageSize = 25
        ViewName = "BrokenLinks"
        Property = @(
            "Icon","Status Code", 
            @{Label="Name"; Expression={$_.DisplayName} }, 
            @{Label="Item Path"; Expression={$_.ItemPath} },"Link Field", "Rendering","Target Path",
            "Link Type",
            "Version",
            "Language",
            @{Label="Updated"; Expression={$_.__Updated} },
            @{Label="Updated by"; Expression={$_."__Updated by"} }
        )
    }

    $items | Show-ListView @props
}
Close-Window

This will make a nice addition to 5.1 (whenever that comes out). You can see below that the rendering in question is now included. You could take the script further and show which placeholder holds the rendering.

Example Report

  • Awesome! Nice work Mike. What I will try to do is add it as a right click on an item that will scan just that item. Then we can easily find broken renderings on the site. Thanks again. – Chris Auer Apr 22 at 17:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.