Long time ago, when using SharePoint 2010, I had to set the default Content types on a bunch of SharePoint Lists and Libraries. Of course I had the ease of doing this in C#. In a recent project I had to do the same thing, however being a SharePoint 2013 online project, I could only do this in JavaScript. It is amazing how something you know so well in C# that would take less than an hour to do, can take hours in JavaScript if you aren’t a JavaScript expert like myself.
How you use this code is down to you, my code originally was part of a huge JavaScript configuration file, using namespaces and callbacks. For simplicity sake, I have written this blog just with straight JavaScript code to perform setting the default content type, and show you how to set up your environment to test this. This isn’t production ready code, just a way to show you how you can do it.
Environment Setup
- Create yourself a simple team site.
- Create some Site Content Types, I’ve based mine on Document.
I have created a HR library which consists of CF Holiday content type, and an Accounts library which has CF Claim Form, CF Invoice and CF Purchase Order added to it.
Below HR Library
Below Accounts Library
This is the environment set up. Currently the Content Types I wish to use have already been added to the libraries, and Document is the default content type.
Code and Demo
As this is purely a demo, I’m going to use SharePoint Designer to create a basic page, that when you click a button, it updates the Default Content Types for lists and libraries. It’s not ideal using SPD as there is no intellisense, but you can quickly create a page and upload to SharePoint, which is great for testing like this.
- Connect SharePoint Designer to your site.
- Open the All Files section, then right click and add an aspx page. I called mine test.aspx
- Open your aspx page, you will get a warning, Click Yes to open the page in advanced mode.
- Delete everything in the page and paste the following. This will give you a basic page, for a Standard SharePoint Master Page.
<%@ Assembly Name="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Import Namespace="Microsoft.SharePoint.WebPartPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" MasterPageFile="~masterurl/default.master" Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=15.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server" >
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:ProjectProperty Property="Title" runat="server"/>
</asp:Content>
<asp:Content ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<SharePoint:ProjectProperty Property="Title" runat="server"/>
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
</asp:Content>
- If you save this page and then navigate to your site, you should get just a blank page, no errors. So your site URL would be similar to http://cf/sites/teams/test.aspx
First thing is to ensure I have all my references I require for the code to work. I’m using underscore.js. I have found underscore to be very useful for looping through arrays. I’ve downloaded underscore.js and using SharePoint designer I have created a folder called Assets/Scripts and placed the underscore.js file in there.
- Add a reference inside your test.aspx. Put the reference inside your PlaceHolderAdditionalPageHead. I’ve also put stubs in place ready for our JavaScript in a later step.
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server" >
<script type="text/javascript" src="Assets/Scripts/underscore.js"></script>
<script type="text/javascript">
/*Our javascript code will go here */
</script>
</asp:Content>
- In PlaceHolderMain content placeholder, add a button that will call our JavaScript function to set Default Content Types.
<div>
<button type="button" onclick="setDefaultContentType();return false">Update Default Content Types</button>
</div>
- Within the PlaceHolderAdditionalPageHead after the underscore.js reference, add the following script.
//Collection of Lists and Default Content Types for given list.
var defaultContentTypes = [
{ ListTitle: 'HR', ContentTypeId: '0x0101003293FA81A1DB8B47AC008BE39BCE695C0057F88502D28F9042A061813A2DA79AE9', ContentTypeName: 'CF Holiday' },
{ ListTitle: 'Accounts', ContentTypeId: '0x0101005C18A4FC55B2C84FB8DD5E5A71D1DA02000529316EC50BA14CB28D9E8D69BA43B4', ContentTypeName: 'CF Claim Form' }
];
var context;
function setDefaultContentType(){
context = SP.ClientContext.get_current();
_.each(defaultContentTypes, function(current){
var rootFolder = context.get_web().get_lists().getByTitle(current.ListTitle).get_rootFolder();
var contentTypes = context.get_web().get_lists().getByTitle(current.ListTitle).get_contentTypes();
context.load(rootFolder, 'ContentTypeOrder', 'UniqueContentTypeOrder');
context.load(contentTypes);
});
context.executeQueryAsync(loadContentTypesSuccess, onScriptFailure);
};
function loadContentTypesSuccess(){
_.each(defaultContentTypes, function (current) {
var originalCTO;
var rootFolder = context.get_web().get_lists().getByTitle(current.ListTitle).get_rootFolder();
var contentTypes = context.get_web().get_lists().getByTitle(current.ListTitle).get_contentTypes();
originalCTO = rootFolder.get_contentTypeOrder();
var newCTO = new Array();
var contentTypeEnum = contentTypes.getEnumerator();
while (contentTypeEnum.moveNext()) {
var currentCT = contentTypeEnum.get_current();
if (currentCT.get_name().toLowerCase() == 'folder')
continue;
if (currentCT.get_name().toLowerCase() == current.ContentTypeName.toLowerCase()) {
newCTO.splice(0, 0, currentCT.get_id());
continue;
}
for (i = 0; i < originalCTO.length; i++) {
if (originalCTO[i].toString() == currentCT.get_id().toString()) {
newCTO.push(currentCT.get_id());
break;
}
}
}
rootFolder.set_uniqueContentTypeOrder(newCTO);
rootFolder.update();
});
context.executeQueryAsync(onUpdateContentTypes, onScriptFailure);
};
function onUpdateContentTypes(){
alert('Updated Default ContentTypes');
};
function onScriptFailure(sender, args){
alert(args.get_message() + '\n' + args.get_stackTrace());
};
Let me explain each section to you. Right at the top of the script we have a global variable called defaultContentTypes. Here we have the List Name, the Content Type ID and Name which will be set as the default content type. If you have more than 2 lists to set the content type, just add another line to this collection for each list. You can add as many, or as little as you want, the code is written to be flexible here.
The first function setDefaultContentType (which is what the button will call when pressed), gets the SharePoint context, then using the underscore.js _.each function, loops through each list in our defaultContentTypes variable. Before we can update anything we need to grab the information from SharePoint. This is what this looping function does. It gets the List rootFolder, and current content types for that list.
The line below ensures that rootFolder contains the values for the properties ContentTypeOrder, and UniqueContentTypeOrder. The ContentTypeOrder is required to get the current order of the Content Types, and the UniqueContentTypeOrder is required because when we set the new order back, this is the property to use. Leaving them out of the following line would throw an error stating “The property or field ‘ContentTypeOrder’ has not been initialized”.
context.load(rootFolder, 'ContentTypeOrder', 'UniqueContentTypeOrder');
Once we have loaded all the lists and content types (loadContentTypesSuccess) we then loop through all in defaultContentTypes again. However because we have already loaded the rootFolder in the context, this time when we call it, we will have values. We grab the original order of the content types, and loop through them. Within the while statement there is 3 checks.
- If content type == “folder” then don’t add it.
- If content type == our default content type, then add first in the array.
- If it is still a content type to display, add it to the end of the content type order array.
The reason why the 3rd check is written as below, is if one of the Content types is not visible on the New button, it ensures that it continues to behave this way.
for (i = 0; i < originalCTO.length; i++) {
if (originalCTO[i].toString() == currentCT.get_id().toString()) {
newCTO.push(currentCT.get_id());
break;
}
}
Lastly, once we have obtained the correct Content Type Order for the list, setting the default Content type at number one, we need to write back to SharePoint and save this data. On the root folder, we set_uniqueContentTypeOrder passing in the new array order. Then call executeQueryAsync, which will either alert success or failure.
If we see this in action, by loading up the test.aspx page we will just have a simple button, saying “Update Default Content Type”. We click this button and hopefully receive a successful alert.
If we look at our Accounts library, we can see the CF Claims form is now the default content type.
And if we look at our HR library, we can see the CF Holiday is now the default content type.
If in your code, you didn’t want to make the SharePoint Document content type visible, in your code, just don’t add it to the newCTO array, like what we do with Folder.
if (currentCT.get_name().toLowerCase() == 'folder' || currentCT.get_name().toLowerCase() == 'document')
continue;