← Back to list

Building a Media Usage & Orphan Tracking Dashboard in Sitecore XM 10

Hello everyone

Deep sharma · 2026-01-11 15:53 · 1 claps · 2.6 min read
#sitecore-xm #sitecore-development #media-management #aspnet-development
Open on Medium ↗
Wiki topics: BIZ · Business Strategy 🎬 · Film & Television

Building a Media Usage & Orphan Tracking Dashboard in Sitecore XM 10

Hello everyone

While working on a Sitecore XM 10 implementation, I noticed a common challenge most content-heavy projects face over time: media sprawl.

Editors struggle to answer simple questions like:

  • Which images are actually used?
  • Which files are safe to delete?
  • Where is this PDF referenced?

To solve this, I built a Media Usage & Orphan Tracking dashboard inside Sitecore.

This post explains what the solution does and includes essential code snippets so you can implement it in your own Sitecore project.

Solution Overview

The dashboard provides:

  • Linked vs orphan media detection
  • Expandable usage view (one media → many pages)
  • Search, filtering, and pagination
  • Excel export for reporting
  • Support for site-specific and shared media

The implementation uses:

  • Sitecore XM 10
  • ASP.NET WebForms
  • Stored procedures for performance
  • EPPlus for Excel export

Step 1: Media Usage Model

Create a model to represent a media item and its usage.

[Serializable]
public class MediaItem
{
    public string ID { get; set; }
    public string Name { get; set; }
    public string MediaType { get; set; }
    public long Size { get; set; }
    public string Language { get; set; }
    public string IsPublished { get; set; }
    public string MediaPath { get; set; }
    public string IsLinked { get; set; }

    // Grouping & UI helpers
    public bool IsGroupHeader { get; set; }
    public bool IsExpanded { get; set; }
    public int LinkedItemsCount { get; set; }
    public List<MediaItem> LinkedItems { get; set; }
}

This model supports grouping and expand/collapse behavior in the UI.

Step 2: Fetch Media Usage Data (Performance First)

Avoid traversing the content tree at runtime. Instead, fetch media usage via a stored procedure.

DataSet ds = masterDatabase.ExecuteDataSet(
    "storeProcedure",
    masterDatabase.CreateParameter("@par1", SqlDbType.NVarChar, par1),
    masterDatabase.CreateParameter("@par2", SqlDbType.NVarChar, par2),
    masterDatabase.CreateParameter("@par3", SqlDbType.NVarChar, par3)
);

Why stored procedures?

  • Faster execution
  • Cleaner C# code
  • Easy to optimize later
  • Scales well for large media libraries

Step 3: Map Raw Data to Strongly Typed Objects

var mediaItems = ds.Tables[0].AsEnumerable()
    .Select(row => new MediaItem
    {
        ID = row["ID"].ToString(),
        Name = row["Name"].ToString(),
        MediaType = row["Extension"].ToString(),
        MediaPath = row["MediaPath"].ToString(),
        IsLinked = row["LinkedStatus"].ToString()
    })
    .ToList();

This makes filtering, grouping, and exporting far easier.

Step 4: Group Media by Usage

Multiple pages can reference the same media item. Instead of showing duplicates, group them.

private List<MediaItem> GroupMediaItems(List<MediaItem> items)
{
    return items
        .GroupBy(x => x.ID)
        .Select(group =>
        {
            var header = group.First();
            header.IsGroupHeader = true;
            header.LinkedItemsCount = group.Count();
            header.LinkedItems = group.ToList();
            return header;
        })
        .ToList();
}

This enables:

  • Expand / collapse UI
  • Clean presentation
  • Faster scanning for editors

Step 5: Filtering & Search

var filtered = mediaItems
    .Where(x => x.Name.ToLower().Contains(searchText))
    .Where(x => status == "All" || x.IsLinked == status)
    .Where(x => type == "All" || x.MediaType == type)
    .ToList();

This works seamlessly with pagination.

Step 6: Server-Side Pagination

var pagedItems = filtered
    .Skip(currentPage * pageSize)
    .Take(pageSize)
    .ToList();

Server-side pagination ensures:

  • Consistent performance
  • Minimal memory usage
  • Smooth UX even with thousands of items

Step 7: Expand / Collapse Linked Items

Track expanded rows using ViewState.

bool isExpanded = expandedIds.Contains(mediaItem.ID);
mediaItem.IsExpanded = isExpanded;

Nested repeaters display linked pages only when expanded.

Step 8: Export to Excel (EPPlus)

using (var package = new ExcelPackage())
{
    var sheet = package.Workbook.Worksheets.Add("Media Usage");
    sheet.Cells[1, 1].Value = "Media Name";
    sheet.Cells[1, 2].Value = "Type";
    sheet.Cells[1, 3].Value = "Linked Status";
    int row = 2;
    foreach (var item in mediaItems)
    {
        sheet.Cells[row, 1].Value = item.Name;
        sheet.Cells[row, 2].Value = item.MediaType;
        sheet.Cells[row, 3].Value = item.IsLinked;
        row++;
    }
    package.SaveAs(response.OutputStream);
}

Why This Approach Works

✔ Fast (DB-driven) ✔ Scalable ✔ Editor-friendly ✔ Production-safe ✔ Easy to extend (delete unused media, bulk actions, etc.)

Final Thoughts

This Media Usage dashboard has become one of the most useful internal tools in our Sitecore projects. It dramatically improves content governance and editor confidence.

If you’re working with Sitecore XM 10, I highly recommend implementing something similar.

Happy Sitecore-ing


메타데이터
post_id
cd85bdf69fe3
slug
building-a-media-usage-orphan-tracking-dashboard-in-sitecore-xm-10-cd85bdf69fe3
url
https://medium.com/@drs231999/building-a-media-usage-orphan-tracking-dashboard-in-sitecore-xm-10-cd85bdf69fe3
canonical_url
https://medium.com/@drs231999/building-a-media-usage-orphan-tracking-dashboard-in-sitecore-xm-10-cd85bdf69fe3
author_url
https://medium.com/@drs231999
status
ok
fetched_at
2026-07-17 04:32:30