SharePoint Technology Specialist SharePoint 2010 MCITP & MCTS Certifications.

Post on 31-Dec-2015

217 views 0 download

Tags:

transcript

Deep Dive on Managing Enterprise Content Types At ScaleChris Bortlik, MicrosoftScott Jamison, Jornata

SPC070

SharePoint Technology Specialist

Meet Chris Bortlik

SharePoint Insider

Former Customer

SharePoint 2010 MCITP & MCTS Certifications

Key Topics

Enterprise Content Type Strategy

Customer Scenario

Working With Site Columns

Managing Content Types

Enterprise Content Type Strategy

Content Types

Sample Content Types

Sales Proposals

Contracts Specifications

Procedures Invoices Training Videos

Sample: Sales ProposalSite ColumnsPotential ClientAmountDue DateSalesperson

TemplateWord Document with consistent branding, formatting, etc.

WorkflowApproval process

PolicyRetention period of 3 years for all proposals

Sales Proposals

Why Content Types?

Consistency.

For both attributes and behavior.

Enterprise Content Type Strategy

Plan Deploy Manage

Planning Identify core metadata values for universal filtering and searching

Identify retention and disposition policies

Identify other foundational elements for content

Determine whether you really need content types at all

Distinguish between enterprise and site-level content type needs

Define policies for the ability to override or self-define content types

Deployment Define site columns, templates, and workflows

Distinguish between enterprise content types, site-level content types, and ad-hoc tags

If needed, define a dedicated site collection for a Content Type Hub for enterprise content types

Assign ownership and governance plan for content types

Management Deploy in TEST environment to ensure behavior is as-intended

Be aware of cross-farm issues with Content Type GUIDs

Train users to understand how/when to use content types

Construct a plan for management over the lifecycle of the content type

Working with Columns

How Columns WorkGUID is the key – not column name

Copy of columns often made

UI pushes all visible properties

OM pushes only edited properties

Before Push Down Ask

What columns are used?

How are they configured?

What columns exist in which sites, lists, or content types?

Are they local or copies of something from a parent site?

Column Usage Report

Managing Content Types

Content Types Associated to site or list

References to columns

Inherited from a parent

Copies can be changed - sometimes

Content Type ReportWhich content types are used on which lists?

How do they relate to the parent content types?

How many list items use a specific content type?

Content Type Report

Creating Content Types

Options

Content Type Syndication

PowerShell 3rd Party Tools

(e.g. AvePoint, Axceler, Metalogix, Quest)

Visual Studio

Site or Site Collection

Site or Site Collection Level

Site or Site Collection LevelProsOut of the box

End user management

Cons

Lack of change management

Scoped to a single site collection

Content Type Hub Introduced in SharePoint 2010

Allows content types to be shared across site collections & farms

Create content types at hub & publish to consumers

Sharing Content Types via Syndication

Publishing content typesContent Types are ‟published” from a

‟normal” Site Content Type GalleryMaximum of 1 Hub per Metadata Shared Application ServiceIt is not a requirement that a

Metadata Service syndicate content

types

It is not a requirement that a service

connection consume content types

from the serviceSetting a site collection to be the hub

enables necessary components on

hub

What gets published?Content Type with all the corresponding columns

Including Document Set Content TypePolicies

And workflow associations (not the

workflows)

Managing Published Content Types

From the hub

Publish

Unpublish

Republish

Roll-up errors from consuming site collections

On the consumer side

Extend a published content type **Derive from a published content type

View import errorsRefresh all content types consumed

from the Hub

** Be careful – read only by default

Content Type Syndication – Publishing from Hub

Farm 1

Web App 1aMetadata Service

Web App 1b

Site Collections 1a/b

Farm 2

Web App 2a

Web App 2b

Connect

ion P

roxy

Site Collections 1cSite Collections 2b

Site Collections 2a

How Content Type Syndication Works

Metadata Service

Connect

ion P

roxy

Subscriber Timer Job

Publish

Each subscriber now has a copy of the original Content Type:• CTs are marked as Read Only• Policies are carried along• Workflow association are established• Updates may be pushed down to list and

libraries

Deleting a Content Type at the Hub

Metadata Service

Connect

ion P

roxy

Subscriber Timer Job

Delete

D

Note: the content Types at the subscriber location are not deleted – they are now Read/Write !

Hub Timer Jo

b

Content Type Syndication Error Logs

Metadata Service

Connect

ion P

roxy

Subscriber Timer Job

! !!

!Hub Timer J

ob

!

!!!!

Content Types Sync has 2 Timer Jobs

• Content Type Hub : deleting and log aggregation• Content Type Subscriber : Cab Pull and Content Type pushdown

Syndication Across EnvironmentsMetadata Service

Connect

ion P

roxy

Subscriber Timer Job

Subs

crib

er T

imer

Job

Publish

Publish

Dev/Test

Production HubProd Auth. Sites

Content Type Publishing

Content Type SyndicationProsOut of the box

End user management

Single source for content types across organization

Cons

Lack of change management

Coordination via dedicated site collection

Does not work for hybrids

ComparisonSite Collection Content Types• Content types used

only by site collection & sub sites

• Can be extended• Changes made on

child sites preserved

Content Type Syndication• Content types can be

used across site collections & farms

• Content types read only by default on consumer sites

• Changes on consumer sites overwritten by publishing hub

PowerShell

PowerShellProsQuick, simple

Repeatable

Change management

ConsRequires IT support

Need to build own management for updates

Visual Studio

Feature Console Application

Visual StudioProsCan deploy with features or as standalone application

Repeatable

Change management

ConsRequires developer support

Need to build own management for updates

3rd Party ToolsProsNo code to support

Most handle hybrid

ConsAdditional licensing cost

Customer Scenario

Global pharmaceutical

3,000 employees worldwide

Regulated content

Customer Scenario

Solution

Customer Scenario

Goals Design Challenges

Goals Desire to have consistent values for content across team sites

Ability for business users to extend items with unique columns

Enforce retention policies

Design Site columns

Content types

Syndicated content types through a content type hub

Retention policies based on content type

Challenges Client needed to both:Push down mandatory columns on all documentsProvide business users the ability to add their own columns

Not possible to do both

Content types were too “heavy” in many scenarios

Many sites, along with many site-level updates, created a situation whereby the use of content types was unknown (how many, how are they used, etc.)

Solution Adjusted usage of enterprise content types (contracts)

Provided better training to end-users

Created PowerShell scripts for column and content type creation as-needed

Created content type reports for better monitoring

Summary

Recap

Develop Your Enterprise Content Type Strategy

Real-World Tips

Work With Site Columns

Manage Content Types

Strategy

Plan

Deploy

Manage

Site ColumnsGUID is the key – not column name

Copy of columns often made

UI pushes all visible properties

OM pushes only edited properties

Content Types

Content Types are either associated to the Site or the List

Contain references to columns but not the column definition

Content Type Hub deploys READ-ONLY copies to subscribing site collections

Real-World TipsRight-size your usage of enterprise content types

Provide training to end-users

Use PowerShell for column and content type creation as-needed

Use content type reports for better monitoring

Wrap-up

Resources The (Hidden) Power of SharePoint Content Types

http://tinyurl.com/spcontenttypes

Creating content typeshttp://tinyurl.com/CreateContentTypehttp://tinyurl.com/ContentTypeConsole

Essential SharePoint 2010http://tinyurl.com/essentialsp2010

Getting started with SharePoint 2013

http://tinyurl.com/sp2013start

Related Sessions

SPC262 What’s new with ECM in SharePoint 2013

SPC020 Best Practices for Records Management with SharePoint

Evaluate this session now on MySPC using your laptop or mobile device: http://myspc.sharepointconference.com

MySPC

Questions?Chris BortlikEmail: chris.bortlik@microsoft.com Blog: blogs.technet.com/cbortlik Twitter: @cbortlik

Scott JamisonEmail: scott.jamison@jornata.com Blog: www.scottjamison.com Twitter: @sjam

© 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.

Appendix

Column Usage Report

Code: Column Usage Reportclass Program { static void Main(string[] args) { Log("Opening web...");

using (SPSite site = new SPSite("http://intranet.contoso.com/")) { Log("Building report..."); Report = new Report(); report.BuildRowsAndFields(site.RootWeb);

Log("Writing output..."); using (StreamWriter streamWriter = new StreamWriter("ColumnUsageReport.csv")) { report.WriteOutput(streamWriter); } } Log("Done."); }

static void Log(string message) { Console.WriteLine(message); Debug.WriteLine(message); }}

foreach (SPList list in web.Lists .Cast<SPList>() .OrderBy(l => l.ID) .OrderBy(l => l.Title)) { foreach (SPField field in list.Fields) this.GetFieldLabel(field);

ReportRow listRow = new ReportRow(this, list); listRow.Parent = webRow; }

foreach (SPWeb childWeb in web.Webs .OrderBy(w => w.ServerRelativeUrl) .OrderBy(w => w.Title)) { ReportRow childWebRow = this.ScanWeb(childWeb); // recurse

childWebRow.Parent = webRow; } return webRow; } ...}

Code: Column Usage Report

class Report { ...

private ReportRow rootRow = null;

public void BuildRowsAndFields(SPWeb rootWeb) { this.rootRow = ScanWeb(rootWeb); }

private ReportRow ScanWeb(SPWeb web) { ReportRow webRow = new ReportRow(this, web);

foreach (SPField field in web.Fields) this.GetFieldLabel(field);Path URL Associated Content Type Attachments Authenticated Cache Profile Author Auto Update Automatic Update

Team Site / Associated Content Type:1 Attachments:1 Author:1 Auto Update:1 Automatic Update:1Documents /DocumentsEmployees /Lists/Employees Attachments:2Form Templates /FormServerTemplatesImages /PublishingImages Author:2Links /Lists/Links Attachments:2List Template Gallery /_catalogs/ltLong Running Operation Status /Long Running Operation Status Attachments:2Master Page Gallery /_catalogs/masterpage Associated Content Type:2 Authenticated Cache Profile:1Notification List /Notification Pages Attachments:2Pages /PagesProjects /Lists/Projects Attachments:2Quick Deploy Items /Quick Deploy Items Attachments:2Content Management /Content

Announcements /Content/Lists/Announcements Attachments:2Calendar /Content/Lists/Calendar Attachments:2Links /Content/Lists/Links Attachments:2Master Page Gallery:1 /Content/_catalogs/masterpageShared Documents /Content/Shared DocumentsTasks /Content/Lists/Tasks Attachments:2Team Discussion /Content/Lists/Team Discussion Attachments:2Wiki Pages /Content/Wiki Pages

Customer Relationship Management /CRMAnnouncements /CRM/Lists/Announcements Attachments:2Calendar /CRM/Lists/Calendar Attachments:2Links /CRM/Lists/Links Attachments:2Master Page Gallery:1 /CRM/_catalogs/masterpageShared Documents /CRM/Shared DocumentsTasks /CRM/Lists/Tasks Attachments:2Team Discussion /CRM/Lists/Team Discussion Attachments:2Wiki Pages /CRM/Wiki Pages

FAST Search Center /searchDocuments:1 /search/DocumentsImages:1 /search/PublishingImages Author:4Master Page Gallery:1 /search/_catalogs/masterpagePages /search/PagesTabs in Search Pages /search/SearchCenter Attachments:2Tabs in Search Results /search/SearchResults Attachments:2Workflow Tasks /search/WorkflowTasks Attachments:2

Code: Column Usage Reportclass Report { public Dictionary<Guid, ReportField> ReportFieldsById = new Dictionary<Guid, ReportField>(); ...

public string GetFieldLabel(SPField field) { ReportField reportField; if (!this.ReportFieldsById.TryGetValue(field.Id, out reportField)) { reportField = new ReportField(field); this.ReportFieldsById.Add(field.Id, reportField); } return reportField.GetFieldLabelForReport(field); } ...}

class ReportField { private readonly List<string> fieldIdentities = new List<string>();

public readonly string ColumnName; public readonly Guid Id;

public bool EverNotHidden = false;

public ReportField(SPField originalField) { this.ColumnName = originalField.Title; this.Id = originalField.Id; } ...}

Code: Column Usage Reportclass ReportField {

private readonly List<string> fieldIdentities = new List<string>();

...

public string GetFieldLabelForReport(SPField field) { Debug.Assert(field.Id == this.Id); if (!field.Hidden) this.EverNotHidden = true;

int identityNumber = this.GetFieldIdentityNumber(field); string fieldName = field.Title + ":" + identityNumber.ToString();

return field.Hidden ? "(" + fieldName + ")" : fieldName; }

private int GetFieldIdentityNumber(SPField field) {

string fieldIdentity = GetFieldIdentity(field);

if (!fieldIdentities.Contains(fieldIdentity)) fieldIdentities.Add(fieldIdentity);

return fieldIdentities.IndexOf(fieldIdentity) + 1; }

static string GetFieldIdentity(SPField field) { XElement element = XElement.Load( new StringReader(field.SchemaXml));

XElement normalizedElement = GetNormalizedXml(element); normalizedElement.SetAttributeValue("Version", null);

return normalizedElement.ToString().ToLowerInvariant(); }

...}

Code: Column Usage Reportstatic XElement GetNormalizedXml(XElement element) { XElement normalizedElement = new XElement(element.Name);

foreach (XAttribute attribute in element.Attributes() .OrderBy(a => a.Value) .OrderBy(a => a.Name.ToString())) {

if (!string.IsNullOrEmpty(attribute.Value)) normalizedElement.Add(attribute); } foreach (XElement childElement in element.Elements() .OrderBy(e => e.Value) .OrderBy(e => e.Name.ToString())) { normalizedElement.Add( GetNormalizedXml(childElement)); // recurse

if (!string.IsNullOrEmpty(childElement.Value)) normalizedElement.SetValue(childElement.Value.Trim()); }

return normalizedElement;}

<Field ID="{C53A03F3-F930-4ef2-B166-E0F2210C13C0}" Type="Computed" Group="$Resources:core,Document_Columns;" Name="FileType" DisplayName="$Resources:core,File_Type;" ShowInNewForm="FALSE" ShowInFileDlg="FALSE" ShowInEditForm="FALSE" Filterable="TRUE" Sortable="TRUE" AuthoringInfo="$Resources:core,file_extension;" SourceID="http://schemas.microsoft.com/sharepoint/v3/fields" StaticName="FileType"> <FieldRefs> <FieldRef ID="{39360F11-34CF-4356-9945-25C44E68DADE}" Name="File_x0020_Type" /> </FieldRefs> <DisplayPattern> <Column Name="File_x0020_Type" HTMLEncode="TRUE" /> </DisplayPattern></Field>

Code: Column Usage Reportclass ReportRow { public readonly Report Report; private ReportRow parentRow = null;

public readonly string Id;

readonly SPWeb web = null; readonly SPList list = null; public readonly List<ReportRow> ChildRows = new List<ReportRow>();

public string PathName;

public SPList List { get { return this.list; } } public SPWeb Web { get { return this.web; } }

public string Url { get { return this.List != null ? this.List.RootFolder.ServerRelativeUrl : this.Web.ServerRelativeUrl; } }

public SPFieldCollection Fields { get { if (this.List != null) return this.List.Fields; return this.Web.Fields; } }

public ReportRow ParentRow { get { return this.parentRow; } set { if (this.parentRow != null) throw new Exception("ParentRow has already been set"); this.parentRow = value; if (this.parentRow != null) this.parentRow.ChildRows.Add(this); } } ...}

public void WriteOutput(StreamWriter streamWriter, int depth) { Dictionary<string, string> rowValues = new Dictionary<string, string>(1000);

string kind = this.List != null ? "List" : "Web"; rowValues["Kind"] = kind; rowValues["URL"] = this.Url;

string rowIdentity = ""; foreach (SPField field in this.Fields) { string label = this.Report.GetFieldLabel(field); rowValues[field.Id.ToString()] = label; rowIdentity += label; }

int rowIdentityNumber = this.Report.GetRowIdentityNumber( this.PathName, rowIdentity);

rowValues["Path" + depth] = rowIdentityNumber == 0 ? this.PathName : this.PathName + ":" + rowIdentityNumber;

this.Report.WriteRowValues(streamWriter, rowValues, this);

foreach (ReportRow childRow in this.ChildRows) childRow.WriteOutput(streamWriter, depth + 1); }}

Code: Column Usage Report

class ReportRow {

...

public ReportRow(Report ownerReport, SPWeb web) { this.Report = ownerReport; this.web = web; this.Id = web.ID.ToString(); this.PathName = web.Title; }

public int GetMaxDepth() { int maxChildDepth = 0; foreach (ReportRow childRow in this.ChildRows) { maxChildDepth = Math.Max(maxChildDepth, childRow.GetMaxDepth()); } return maxChildDepth + 1; }

...

Path URL Associated Content Type Attachments Authenticated Cache Profile Author Auto Update Automatic UpdateTeam Site / Associated Content Type:1 Attachments:1 Author:1 Auto Update:1 Automatic Update:1

Documents /DocumentsEmployees /Lists/Employees Attachments:2Form Templates /FormServerTemplatesImages /PublishingImages Author:2Links /Lists/Links Attachments:2List Template Gallery /_catalogs/ltLong Running Operation Status /Long Running Operation Status Attachments:2Master Page Gallery /_catalogs/masterpage Associated Content Type:2 Authenticated Cache Profile:1Notification List /Notification Pages Attachments:2Pages /PagesProjects /Lists/Projects Attachments:2Quick Deploy Items /Quick Deploy Items Attachments:2Content Management /Content

Announcements /Content/Lists/Announcements Attachments:2Calendar /Content/Lists/Calendar Attachments:2Links /Content/Lists/Links Attachments:2Master Page Gallery:1 /Content/_catalogs/masterpageShared Documents /Content/Shared DocumentsTasks /Content/Lists/Tasks Attachments:2Team Discussion /Content/Lists/Team Discussion Attachments:2Wiki Pages /Content/Wiki Pages

Customer Relationship Management /CRMAnnouncements /CRM/Lists/Announcements Attachments:2Calendar /CRM/Lists/Calendar Attachments:2Links /CRM/Lists/Links Attachments:2Master Page Gallery:1 /CRM/_catalogs/masterpageShared Documents /CRM/Shared DocumentsTasks /CRM/Lists/Tasks Attachments:2Team Discussion /CRM/Lists/Team Discussion Attachments:2Wiki Pages /CRM/Wiki Pages

FAST Search Center /searchDocuments:1 /search/DocumentsImages:1 /search/PublishingImages Author:4Master Page Gallery:1 /search/_catalogs/masterpagePages /search/PagesTabs in Search Pages /search/SearchCenter Attachments:2Tabs in Search Results /search/SearchResults Attachments:2Workflow Tasks /search/WorkflowTasks Attachments:2

Path URL Associated Content Type Attachments Authenticated Cache Profile Author Auto Update Automatic UpdateTeam Site / Associated Content Type:1 Attachments:1 Author:1 Auto Update:1 Automatic Update:1

Documents /DocumentsEmployees /Lists/Employees Attachments:2Form Templates /FormServerTemplatesImages /PublishingImages Author:2Links /Lists/Links Attachments:2List Template Gallery /_catalogs/ltLong Running Operation Status /Long Running Operation Status Attachments:2Master Page Gallery /_catalogs/masterpage Associated Content Type:2 Authenticated Cache Profile:1Notification List /Notification Pages Attachments:2Pages /PagesProjects /Lists/Projects Attachments:2Quick Deploy Items /Quick Deploy Items Attachments:2Content Management /Content

Announcements /Content/Lists/Announcements Attachments:2Calendar /Content/Lists/Calendar Attachments:2Links /Content/Lists/Links Attachments:2Master Page Gallery:1 /Content/_catalogs/masterpageShared Documents /Content/Shared DocumentsTasks /Content/Lists/Tasks Attachments:2Team Discussion /Content/Lists/Team Discussion Attachments:2Wiki Pages /Content/Wiki Pages

Customer Relationship Management /CRMAnnouncements /CRM/Lists/Announcements Attachments:2Calendar /CRM/Lists/Calendar Attachments:2Links /CRM/Lists/Links Attachments:2Master Page Gallery:1 /CRM/_catalogs/masterpageShared Documents /CRM/Shared DocumentsTasks /CRM/Lists/Tasks Attachments:2Team Discussion /CRM/Lists/Team Discussion Attachments:2Wiki Pages /CRM/Wiki Pages

FAST Search Center /searchDocuments:1 /search/DocumentsImages:1 /search/PublishingImages Author:4Master Page Gallery:1 /search/_catalogs/masterpagePages /search/PagesTabs in Search Pages /search/SearchCenter Attachments:2Tabs in Search Results /search/SearchResults Attachments:2Workflow Tasks /search/WorkflowTasks Attachments:2

Path URL Associated Content Type Attachments Authenticated Cache Profile Author Auto Update Automatic UpdateTeam Site / Associated Content Type:1 Attachments:1 Author:1 Auto Update:1 Automatic Update:1

Documents /DocumentsEmployees /Lists/Employees Attachments:2Form Templates /FormServerTemplatesImages /PublishingImages Author:2Links /Lists/Links Attachments:2List Template Gallery /_catalogs/ltLong Running Operation Status /Long Running Operation Status Attachments:2Master Page Gallery /_catalogs/masterpage Associated Content Type:2 Authenticated Cache Profile:1Notification List /Notification Pages Attachments:2Pages /PagesProjects /Lists/Projects Attachments:2Quick Deploy Items /Quick Deploy Items Attachments:2Content Management /Content

Announcements /Content/Lists/Announcements Attachments:2Calendar /Content/Lists/Calendar Attachments:2Links /Content/Lists/Links Attachments:2Master Page Gallery:1 /Content/_catalogs/masterpageShared Documents /Content/Shared DocumentsTasks /Content/Lists/Tasks Attachments:2Team Discussion /Content/Lists/Team Discussion Attachments:2Wiki Pages /Content/Wiki Pages

Customer Relationship Management /CRMAnnouncements /CRM/Lists/Announcements Attachments:2Calendar /CRM/Lists/Calendar Attachments:2Links /CRM/Lists/Links Attachments:2Master Page Gallery:1 /CRM/_catalogs/masterpageShared Documents /CRM/Shared DocumentsTasks /CRM/Lists/Tasks Attachments:2Team Discussion /CRM/Lists/Team Discussion Attachments:2Wiki Pages /CRM/Wiki Pages

FAST Search Center /searchDocuments:1 /search/DocumentsImages:1 /search/PublishingImages Author:4Master Page Gallery:1 /search/_catalogs/masterpagePages /search/PagesTabs in Search Pages /search/SearchCenter Attachments:2Tabs in Search Results /search/SearchResults Attachments:2Workflow Tasks /search/WorkflowTasks Attachments:2

Content type usage report

Code: Content Type Reportclass Report { Dictionary<string, List<string>> rowIdentitiesByName = new Dictionary<string, List<string>>();

Dictionary<Guid, ReportField> ReportFieldsById = new Dictionary<Guid, ReportField>();

List<KeyValuePair<string, string>> ReportColumnKeysAndTitles = new List<KeyValuePair<string, string>>();

ReportRow rootRow = null; int MaxDepth = 0;

Dictionary<SPContentTypeId, ReportRow> rowsByContentTypeId = new Dictionary<SPContentTypeId, ReportRow>();

...

public void BuildRowsAndFields(SPWeb rootWeb) { this.rootRow = ScanWeb(rootWeb);

this.CollectContentTypes(rootWeb);

ReportRow contentTypeRoot = this.rootRow.ChildRows .First(row => row.IsContentType);

this.PostProcessContentTypes(contentTypeRoot); }

private void CollectContentTypes(SPWeb web) { this.CreateContentTypeRows(web.ContentTypes);

foreach (SPList list in web.Lists) { this.CreateContentTypeRows(list.ContentTypes);

this.MeasureUsage(list); }

foreach (SPWeb childWeb in web.Webs) this.CollectContentTypes(childWeb); // recurse }}

Code: Content Type Reportclass Report { ... private void CreateContentTypeRows( SPContentTypeCollection contentTypes) {

foreach (SPContentType contentType in contentTypes) { ReportRow parentRow = null;

if (!this.rowsByContentTypeId.TryGetValue( contentType.Id.Parent, out parentRow)) {

if (!contentType.Id.Parent.Equals(contentType.Id)) throw new Exception("Cannot find parentRow");

parentRow = this.rootRow; }

ReportRow contentTypeRow = new ReportRow(this, contentType);

contentTypeRow.ParentRow = parentRow;

this.rowsByContentTypeId.Add( contentType.Id, contentTypeRow); } }

private void MeasureUsage(SPList list) { Debug.WriteLine("Querying usage for list: " + list.RootFolder.Url); SPQuery query = new SPQuery(); query.Query = @"<View Scope='RecursiveAll'><ViewFields> <FieldRef Name='FileDirRef'/><FieldRef Name='ContentTypeId'/> </ViewFields></View>";

ContentIterator contentIterator = new ContentIterator(); contentIterator.ProcessListItems(list, delegate(SPListItem listItem) { ReportRow row; if (this.rowsByContentTypeId.TryGetValue( listItem.ContentTypeId, out row)) { ++row.UsageCount; } else { Debug.WriteLine("Error for item " + listItem.Url + ": Content Type Not Found"); } }, delegate(SPListItem listItem, Exception e) { Debug.WriteLine("Error for item " + listItem.Url + ": " + e.Message); return true; } ); }}

Code: Content Type Reportclass Report {

...

private void PostProcessContentTypes(ReportRow contentTypeRow) { List<ReportRow> childRows = contentTypeRow.ChildRows;

// Sort the child rows var sortedRows = childRows .OrderBy(row => row.Url) // if names are the same, sort by URL .OrderBy(row => row.ContentType.Name) .OrderBy(row => row.List == null) // list CT's before web CT's .ToList();

childRows.Clear(); childRows.AddRange(sortedRows);

contentTypeRow.PathName = contentTypeRow.ContentType.Name;

foreach (ReportRow childRow in childRows) this.PostProcessContentTypes(childRow); // recurse }

...

}

Path URL Usages Contributor Copy Source Copyright Country/Region Coverage Created Created Created ByTeam Discussion /Strategy/Lists/Team Discussion (Copy Source:2) Created:2 (Created:2) Created By:2Wiki Pages /Strategy/Wiki Pages Copy Source:3 Created:2 (Created:3) Created By:2

Wiki /wikiDocuments:1 /wiki/Documents Copy Source:3 Created:2 (Created:3) Created By:2Images:3 /wiki/PublishingImages Copy Source:3 Copyright:2 Created:2 (Created:3) Created By:2Master Page Gallery:1 /wiki/_catalogs/masterpage Copy Source:3 Created:2 (Created:3) Created By:2Pages:1 /wiki/Pages Copy Source:3 Created:2 (Created:3) Created By:2Workflow Tasks /wiki/WorkflowTasks (Copy Source:2) Created:2 (Created:2) Created By:2

CONTENT TYPES:System /

Converted Forms /IWConvertedForms Copy Source:3 Created:2 (Created:2) Created By:2List Template Gallery /_catalogs/lt Copy Source:3 Created:2 (Created:3) Created By:2Solution Gallery /_catalogs/solutions Copy Source:3 Created:2 (Created:3) Created By:2Tabs in Search Pages:1 /FastSearch/SearchCenter 4 (Copy Source:2) Created:2 (Created:2) Created By:2Tabs in Search Pages /search/SearchCenter 4 (Copy Source:2) Created:2 (Created:2) Created By:2Tabs in Search Results:1 /FastSearch/SearchResults 5 (Copy Source:2) Created:2 (Created:2) Created By:2Tabs in Search Results /search/SearchResults 3 (Copy Source:2) Created:2 (Created:2) Created By:2Theme Gallery /_catalogs/theme 20 Copy Source:3 Created:2 (Created:3) Created By:2Web Part Gallery /_catalogs/wp Copy Source:3 Created:2 (Created:3) Created By:2Common Indicator Columns /

Excel based Status Indicator /Fixed Value based Status Indicator /SharePoint List based Status Indicator /SQL Server Analysis Services based Status Indicator/

Item /Item:1 /Lists/ContentTypeSyncLogItem:2 /Lists/Customer Contacts 257Item:3 /Lists/Customers 41Item:4 /Lists/DemoProjects 103Item:5 /Lists/Employees 17Item:6 /Lists/Projects 4Item:7 /Lists/Reporting Metadata 11Item:8 /Lists/TaxonomyHiddenList 4Item:9 /Long Running Operation Status 7

Code: Content Type Reportclass ReportField { private readonly List<string> fieldIdentities = new List<string>(); ...

public string GetFieldLabelForReport(SPField field, SPContentType contentType) { Debug.Assert(field.Id == this.Id); if (!field.Hidden) this.EverNotHidden = true;

int identityNumber = this.GetFieldIdentityNumber(field); string fieldName = field.Title + ":" + identityNumber.ToString();

if (contentType != null) { SPFieldCollection sourceFields = contentType.ParentList != null ? contentType.ParentList.Fields : contentType.ParentWeb.Fields; SPField sourceField = sourceFields[field.Id]; int sourceIdentityNumber = this.GetFieldIdentityNumber(sourceField); if (sourceIdentityNumber != identityNumber) fieldName += "/" + sourceIdentityNumber; }

return field.Hidden ? "(" + fieldName + ")" : fieldName; } ...}

Item:13Item:14 Flag Control Display Name:1Announcement

Announcement:1Announcement:1Announcement:1Announcement:1

CirculationCommentContact First Name:1 (First Name Phonetic:2/1)Document

Document:1Document:1Document:1Document:2

Path URL Usages Contributor Copy Source Copyright Country/Region Coverage Created Created Created ByTeam Discussion /Strategy/Lists/Team Discussion (Copy Source:2) Created:2 (Created:2) Created By:2Wiki Pages /Strategy/Wiki Pages Copy Source:3 Created:2 (Created:3) Created By:2

Wiki /wikiDocuments:1 /wiki/Documents Copy Source:3 Created:2 (Created:3) Created By:2Images:3 /wiki/PublishingImages Copy Source:3 Copyright:2 Created:2 (Created:3) Created By:2Master Page Gallery:1 /wiki/_catalogs/masterpage Copy Source:3 Created:2 (Created:3) Created By:2Pages:1 /wiki/Pages Copy Source:3 Created:2 (Created:3) Created By:2Workflow Tasks /wiki/WorkflowTasks (Copy Source:2) Created:2 (Created:2) Created By:2

CONTENT TYPES:System /

Converted Forms /IWConvertedForms Copy Source:3 Created:2 (Created:2) Created By:2List Template Gallery /_catalogs/lt Copy Source:3 Created:2 (Created:3) Created By:2Solution Gallery /_catalogs/solutions Copy Source:3 Created:2 (Created:3) Created By:2Tabs in Search Pages:1 /FastSearch/SearchCenter 4 (Copy Source:2) Created:2 (Created:2) Created By:2Tabs in Search Pages /search/SearchCenter 4 (Copy Source:2) Created:2 (Created:2) Created By:2Tabs in Search Results:1 /FastSearch/SearchResults 5 (Copy Source:2) Created:2 (Created:2) Created By:2Tabs in Search Results /search/SearchResults 3 (Copy Source:2) Created:2 (Created:2) Created By:2Theme Gallery /_catalogs/theme 20 Copy Source:3 Created:2 (Created:3) Created By:2Web Part Gallery /_catalogs/wp Copy Source:3 Created:2 (Created:3) Created By:2Common Indicator Columns /

Excel based Status Indicator /Fixed Value based Status Indicator /SharePoint List based Status Indicator /SQL Server Analysis Services based Status Indicator/

Item /Item:1 /Lists/ContentTypeSyncLogItem:2 /Lists/Customer Contacts 257Item:3 /Lists/Customers 41Item:4 /Lists/DemoProjects 103Item:5 /Lists/Employees 17Item:6 /Lists/Projects 4Item:7 /Lists/Reporting Metadata 11Item:8 /Lists/TaxonomyHiddenList 4Item:9 /Long Running Operation Status 7