Category Archives: Software Development

The main theme of this blog: SharePoint, C#, ASP.NET, yada, yada

How to change a Page Layout Associated Content Type Id Programmatically

I developed some code that uploads a new page layout file (ASPX) from disk to the Master Page Gallery of a site collection.  I wanted to associate the uploaded publishing page with an existing content type, such as the “Welcome Page” content type.  This is equivalent to editing the properties of the page layout and setting the “Associated Content Type” property…

 

image

 

Setting the Associated Content Type property in code is not as easy as thought.  After trawling around the web for an hour, I found no good example.  I found plenty of examples to create a Page Layout declaratively in XML and assign the associated content property, but no examples in C# for an existing SPFile object.  Then it hit me…

The PublishingAssociatedContentType property expects a formatted string that contains both the display name and the content type ID.  So, armed with both an SPFile object that is the page layout file and an SPContentType object, I was able to associate the page layout with the following code:

file.Item.Properties["PublishingAssociatedContentType"] = String.Format(";#{0};#{1};#", spCT.Name, spCT.Id.ToString());

Make sure to call file.Item.Update();

Wham!

Assigning a Unique Master Page to a Page Layout in SharePoint 2010

In the old days of SharePoint 2007, the master page reference in a publishing page layout lived in the MasterPageFile attribute of the @Page reference at the top off the layout file. 

This made good when you needed to create a page layout that stood out from the common branding of the site – such as a page layout that had no chrome for popups etc… and this was exactly what I wanted to accomplish today in SharePoint 2010.

Unfortunately, Microsoft changed the way in which Page Layouts associate with their master page.  Open any of the out of the box page layouts in SP2010 and you should notice that there are no references to master page files anywhere.  This is because the master page association is handled by the containing site settings.

So how does one go about creating a “special” page layout that does not follow the same branding as the rest of the site?  One option is to isolate such pages in sub sites, which is frankly crappy.  Unfortunately, the alternative is much better – the general consensus is that the solution to this problem consists of creating a new sub class that inherits the PublishingLayoutPage class and sets the CustomMasterUrl property explicitly in the Page Init event.

Thanks to Eric for his post, which I referenced to solve this issue.

TaxonomyClientService.AddTerms Wrong Documentation

I’ve been working lately on a project that requires access to the Managed Metadata Service in SP2010.  I got to a point where I needed to add a term to the default term store under a term set.

I have some code in my project that takes in the following parameters and creates a term in the term store:

- TaxonomyClientService proxy instance

- Term name

- Term store ID

- Term Set ID

I needed to use the AddTerm method of the proxy to create a new term, and spent most of my afternoon wrestling with the format of the NewTerms parameter of the method.

The following MSDN documentation is wrong! (here) – or at least not informative.

The MSDN documentation stipulates to use NewTerm nodes to wrap new terms in the XML passed to the service.  What the documentation did not tell me was:

1. The term set must be open, otherwise the method returns an empty string.

2. The method need the exact syntax for the XML to work – looking on the web, I found no real answer to this problem, and ended up reflecting the web service code to get my answer.  Below is a sample piece of XML.

<newTerms><newTerm label="MyTerm" clientId="1" parentTermId="GUID of parent or empty GUID if none"></newTerm></newTerms>

Worth noting with the above XML…

1. Notice the lowercase use of newTerms and newTerm (not uppercase N as in the MSDN documentation)

2. clientId does very little and so you can pass the value 1

3. The parentTermId must be a real GUID, and Guid.Empty if no parent

4. New terms wrap in the newTerms node, which MSDN failed to mention.

I hope this post saves others an afternoon worth of work, which it cost me.

Programmatically Provision Term Store

I recently had to write a feature to provision the SharePoint 2010 Term Store. Numerous blog posts exist on how to populate the term store using Power Shell or how to write XML to add terms to the store, but what I wanted to do was a little different.  The requirements for my feature were as follows:

1. Works in SharePoint 2010 (duh)

2. Create a new Term Store Group in default Term Store

3. Great a series of Term Sets

4. Create a series of Terms in the Term Sets

5. Deploy the feature at the Farm scope.

One of the issues with setting up a Term Store in the SharePoint 2010 Metadata Managed Service Application is that the service can be temperamental if the proxy for the service does not have the setting checked for default term store storage location for site collections.  I wrote a previous blog post on this issue here

To save you some reading, the issue above is with obtaining the default term store instance for a given site collection when using the TaxonomySession object in the Microsoft.SharePoint.Taxonomy API.  As my previous post mentioned, the way to resolve this issue is to check the option under Properties fore the proxy and ensure that the current user had full control as an Administrator of the Managed Metadata Service Application.

Nice one Rob! But what if you want to avoid the manual step and want to configure these operations in code?  It is not as hard as it may sound, check out the code below…

        private void ProvisionMetadataService()
        {
            // We don't have the metadata service configured, so let's do that.
            var proxy = SPFarm.Local.ServiceProxies.Where(s => s.GetType().Name.Equals
("MetadataWebServiceProxy")).FirstOrDefault();
            if (null == proxy) 
throw new SPException("Failed to get instance of metadata web service proxy, is it installed?");
            foreach (var proxyApp in proxy.ApplicationProxies.Where(proxyApp => 
proxyApp.Properties.ContainsKey("IsDefaultSiteCollectionTaxonomy")))
            {
                proxyApp.Properties["IsDefaultSiteCollectionTaxonomy"] = true;
                proxyApp.Update(true);
            }
            // Give the current user access rights to the metadata service.
            var service = SPFarm.Local.Services.Where(s => s.GetType().Name.Equals
("MetadataWebService")).FirstOrDefault();
            if (null == service) 
throw new SPException("Failed to get instance of metadata web service, is it installed?");
            var serviceApp = service.Applications.OfType<SPIisWebServiceApplication>().FirstOrDefault();
            if (null == serviceApp) 
throw new SPException("Failed to get instance of metadata web service app, is it installed?");
            var security = serviceApp.GetAdministrationAccessControl();
            var cba = SPClaimProviderManager.Local;
            var claim = cba.ConvertIdentifierToClaim("DOMAIN\user", 
SPIdentifierTypes.WindowsSamAccountName);
            security.AddAccessRule(
new SPAclAccessRule<SPCentralAdministrationRights>(claim, SPCentralAdministrationRights.FullControl));
            serviceApp.SetAdministrationAccessControl(security);
            serviceApp.Uncache();
            service.Uncache();
        }

 

With the above code executed, you can then open an instance of the site collection to the Central Admin Site and then request the default site collection term store with the following code:

using (var site = new SPSite(_farmUrl))
            {
                // Do we have explicit credentials?
                if (!String.IsNullOrEmpty(_username) && !String.IsNullOrEmpty(_password))
                {
                    var user = site.RootWeb.AllUsers[_username];
                    if (null == user) 
throw new SPException(String.Format("no user in site collection {0}", _username));
                    using (var secureSite = new SPSite(site.ID, user.UserToken))
                    {
                        // Get the term store.
                        var session = new TaxonomySession(secureSite);
                        var termStore = session.DefaultSiteCollectionTermStore;
                        if (null == termStore) 
throw new SPException("Failed to get the default term store instance");
                        del(termStore);
                    }
                }
                else
                {
                    // Get the term store.
                    var session = new TaxonomySession(site);
                    var termStore = session.DefaultSiteCollectionTermStore;
                    if (null == termStore) 
throw new SPException("Failed to get the default term store instance");
                    del(termStore);
                }
            }

It’s not obvious from the code above, but the call to “del” is a delegate call that I include as a parameter to the wrapping method of the above cod.

So, lastly, how did I get the Central Admin URL in the feature receiver?  See below:

var app = SPAdministratrionWebApplication.GetInstanceLocalToFarm(SPFarm.Local);
var url = app.Sites[0].Url

Managed Metadata Service: DefaultSiteCollectionTermStore == null

I happened to configure my SP2010 farm using Powershell automated scripts and and as a result my default Metadata term store proxy was not default for any new or existing site collections.  This issue manifested itself when I was trying to access the default site collection term store via the SharePoint API as a property of the TaxonomySession class.

I came across the following blog post, which got me as far as establishing the metadata service proxy as default for site collection.  To access the DefaultSiteCollectionTermStore property I had to configure additional permissions.

By default my Metadata Service Applications allowed permitted access to the farm and application pool accounts, but my custom code was running under the context of the logged in user.  To rectify this issue I could either elevate permissions to run as the app pool user, or give the logged in user explicit permissions by clicking through as follows in Central Administration:

1. Central Administration

2. Application Management

3. Manage Service Applications

4. Metadata Service Application (not the proxy)

5. Permissions (Ribbon)

6. Add the user via the dialog and give them permissions to access the application.

Interview Questions for SharePoint Developer Position

See how you do…

Q1. Name two SharePoint API objects you should define to open a site collection and sub web.

Q2. What is the role of the Shared Services provider in Microsoft Office SharePoint Server 2007?

Q3. How man content databases can a site collection span?

Q4. Can a content database contain multiple site collections?

Q5. What SharePoint capability allows navigation access to multiple site collections in a single site collection?

Q6. What is STSADM?

Q7. What is a metadata property in the search configuration?

Q8. What SharePoint capability allows for the use of multiple domain names for the same SharePoint site application?

Q9. What is a SharePoint Feature and how are they deployed?

Q10. Name all 4 feature scope levels

Q11. Name two development approaches to creating a WSP (SharePoint Deployment Package)

Q12. Should you dispose an instance of SPWeb obtained from the ALLWEBS collection of an SPSite object?

Q13. Should you dispose the ROOTWEB object of an SPSite object?

Q14. Should you dispose the site or web objects referenced from SPCONTEXT?

Q15. Your site crashes and SharePoint reports a standard error message after installing some custom code, how would you diagnose the issue?

Q16. How would you apply a common branding to all pages on your publishing sites?

Q17. What feature do page layouts belong?

Q18. How many direct parent content types may a child content type have?

Q19. Comment on why explicit permissions given to list items is bad practice

Q20. Is “Contributors” a SharePoint Group or Permission Level?

Q21. What is the role of the term store?

Q22. Comment how SP2010 no longer uses the SSP, and the new approach to service architecture

Q23. What is the sandbox?

Q24. What is Claims-Based-Authentication?

Q25. What Microsoft Office application allows design and implementation of workflows visually?

SP2010 Features – Reference

This post is to help my my strained memory, the following post contains a list of all SharePoint 2010 feature GUIDs

SP2010 ListData.svc give 404

If you see an HTTP 404 when accessing the /_vti_bin/ListData.svc WCF service in SharePoint 2010 then be sure to install the ADO.NET Data Services 1.5 CTP2

Link

Site Collection URL in a User Control

Ever have a problem remembering something small, I do.  No matter how many projects I develop in SharePoint I cannot seem to remember how to reference the site collection URL from a user control.  So, after digging around for 5 minutes to find the answer I decided to blog it and save my aging memory:

<%$SPUrl:~SiteCollection/ %>

SP2010 Sandbox Development Tip

If you want to make sure that you aren’t using any of the restricted APIs before you deploy your solution to a sandbox environment, manually reference your project against:

[SharePoint Root]\UserCode\assemblies\Microsoft.SharePoint.dll  

If your code compiles, then you’re pretty safe!

NEVER DEPLOY code with this Microsoft.SharePoint.dll reference, instead reference the Microsoft.SharePoint.dll in

[SharePoint Root]\ISAPI folder.

From the SharePoint 2010 book I’m reviewing

Follow

Get every new post delivered to your Inbox.

Join 293 other followers

%d bloggers like this: