Feeds:
Posts
Comments

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!

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.

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.

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

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.

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?

2010 in review

The stats helper monkeys at WordPress.com mulled over how this blog did in 2010, and here’s a high level summary of its overall blog health:

Healthy blog!

The Blog-Health-o-Meter™ reads Fresher than ever.

Crunchy numbers

Featured image

The average container ship can carry about 4,500 containers. This blog was viewed about 24,000 times in 2010. If each view were a shipping container, your blog would have filled about 5 fully loaded ships.

In 2010, there were 4 new posts, growing the total archive of this blog to 314 posts. There were 4 pictures uploaded, taking up a total of 126kb.

The busiest day of the year was June 15th with 128 views. The most popular post that day was Efficient way to add a new item to a SharePoint list.

Where did they come from?

The top referring sites in 2010 were codeproject.com, stackoverflow.com, dotnetslackers.com, forums.asp.net, and dotnetmafia.com.

Some visitors came searching, mostly for .net wrapper for com elevation, robert garrett, rob garrett, spperformancemonitor, and the site collection could not be restored. if this problem persists please make.

Attractions in 2010

These are the posts and pages that got the most views in 2010.

1

Efficient way to add a new item to a SharePoint list February 2009
11 comments

2

Prolific PL-2303 Driver – Vista x64 February 2008
18 comments

3

SharePoint Development Best Practices (Summary) February 2009
7 comments

4

Reinstalling COM+ on Windows XP April 2005
48 comments

5

Configuring RBS for SP2010 January 2010
3 comments

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

Following on from my previous post about list scaling and performance.  The following posts details configuration of Remote Blob Storage for SharePoint 2010 and SQL Server 2008 R2.

First download the RBS provider for SQL Server 2008 (don’t install it yet):

http://go.microsoft.com/fwlink/?LinkId=177388

Configure file stream for the SQL Server Service using the Configuration Manager:

image

Execute the following SQL queries:

EXEC sp_configure filestream_access_level, 2

RECONFIGURE

Execute the following SQL to set up a master encryption key and blob store file group:

use WSS_Content

if not exists (select * from sys.symmetric_keys where name = N'##MS_DatabaseMasterKey##')
create master key encryption by password = N'Admin Key Password !2#4'

if not exists (select groupname from sysfilegroups where 
groupname=N'RBSFilestreamProvider')alter database WSS_Content
 add filegroup RBSFilestreamProvider contains filestream

alter database [WSS_Content] add file (name = RBSFilestreamFile, filename = 'c:\Blobstore') 
to filegroup RBSFilestreamProvider

Install the RBS provider with the following command (change DBINSTANCE to your SQL server instance):

msiexec /qn /lvx* rbs_install_log.txt /i RBS_X64.msi TRUSTSERVERCERTIFICATE=true FILEGROUP=PRIMARY DBNAME="WSS_Content" DBINSTANCE="SP2010" FILESTREAMFILEGROUP=RBSFilestreamProvider FILESTREAMSTORENAME=FilestreamProvider_1

If installing RBD on production servers, be sure to run on all WFE’s with the following command (again, change the DBINSTANCE):

msiexec /qn /lvx* rbs_install_log.txt /i RBS_X64.msi DBNAME="WSS_Content" DBINSTANCE="SP2010" ADDLOCAL=”Client,Docs,Maintainer,ServerScript,FilestreamClient,FilestreamServer”

Run the following Power Shell script from the SP2010 Management Console:

$cdb = Get-SPContentDatabase –WebApplication http://sp2010

$rbss = $cdb.RemoteBlobStorageSettings

$rbss.Installed()

$rbss.Enable()

$rbss.SetActiveProviderName($rbss.GetProviderNames()[0])

$rbss

Now create a document library in SharePoint and upload an image to it.  Next visit the c:\blobstore directory and look for the GUID sub folder with recent date.  Keep drilling down until you find a file.  You should see a file with GUID name.  Drop this into IE and you should see that it is the same file you uploaded to your document library.

 

From the SharePoint 2010 book I’m reviewing

It it a well known fact that MOSS 2007 caused some rising opinions on the subject of list scalability and performance.  Many developers operated under the misconception that SharePoint lists only allowed 2000 list items before croaking out with bad performance.  Nothing could be further from the truth.

The following article talks about this issue of large lists in great depth and highlights the point that SharePoint can actually handle many more than 2000 list items in any one list.  However, “query” this data is affected by the item count and SharePoint architects should design their data access and presentation of data accordingly.

http://technet.microsoft.com/en-us/library/cc262813.aspx

Microsoft has added a number of new enhancements to lists in SharePoint 2010 to handle larger capacity and the query of this data, and the following is a short summary of the enhancements:

List Column Indexing

SP2010 now allows list designers to create up to 20 indices (some of multiple columns) on any one list.  These indices allow for faster query of data when the list size exceeds that of typical.

image 

Under list settings and in the columns section; users now see a link to Indexed Columns.

The following is a list of column types usable as indexed columns:

· Single line of text

· Choice field, but not multi choice

· Number

· Currency

· Date/Time

· Look up, but not a multi value look up

· Person or group, but not multi value

· Title, except in a document library

 

List Throttling

SharePoint administrators now have the capability to better control list queries so that developers (or general users) may issue list queries on large lists that may potentially bring down the server.  Specifically:

Administrators may define some limits at the web application level:

- Configure the number of items fetched for queries

- Administrators may receive warnings when thresholds exceeded

- Ability to configure time periods for expensive queries to operate

- Limit the size of list items (default to 8k)

- Limit the number of columns in a join (default to 6)

The following code will display the list throttling limits for the site collection:

using (SPSite site = new SPSite(siteUrl))
            {
                Console.WriteLine("MaxItemsPerThrottledOperation:{0}",
                    site.WebApplication.MaxItemsPerThrottledOperation);
                Console.WriteLine("MaxItemsPerThrottledOperationOverride:{0}",
                    site.WebApplication.MaxItemsPerThrottledOperationOverride);
                Console.WriteLine("MaxItemsPerThrottledOperationWarningLevel:{0}",
                    site.WebApplication.MaxItemsPerThrottledOperationWarningLevel);
            }

To enable list throttling on any list be sure to toggle the setting with the following:

SPList.EnableThrottling = true

MaxItemsPerThrottledOperationWarningLevel – If a list exceeds the number of items specified in this threshold then a warning is displayed on the list settings page.

What MaxItemsPerThrottledOperation – This indicates the number of list items returned to non-administrators.  Administrators can query up to the threshold in What MaxItemsPerThrottledOperationOverride but will receive a warning on the list settings page.

If administrators wish for users to execute expensive operations in specific window of time they can do so by using the following method on the WebApplication object: SetDailyUnthrottledPrivilegedOperationWindow

 

RBS Storage (Remote Blob Storage)

In some cases the use of document libraries to store large files is no longer scalable and causes content databases to become unmanageable.  An example situations where a public web site, hosted in SharePoint, provides users with rich media content – web files and large images – is once such example of the large blob storage issue. 

In MOSS, hosting content in the database provided certain benefits, such as single storage location, versioning, and access of files via the object model.  Whereas file based storage provided better scalability at the cost of orphaned content from the object model.  SP2010 solves this issue with RBS.  Site Architects can now store large files (blobs) in alternate locations to that of the SharePoint content database without relinquishing access via the OM.  From an and developer standpoint, the data is accessed as if it were in the content database, but the content is actually in a remote location.

To enable RBS you’re farm will need to use at least SQL Server 2008 R2.

Marking blobs as external at the content database level enables SharePoint to store the meta-data associated with blobs in the database while storing the actual blob content outside the content database.  Because RBS is handled at the database level, SharePoint is unaware that data is not stored in the content database but in another location.

in a future time, vendors will bring RBS providers for SP2010 to the table, but in the meantime Microsoft has provided RBS for SQL server as an extra download:

http://go.microsoft.com/fwlink/?LinkId=177388

See my next blog post on configuring RBS.

 

From the SharePoint 2010 book I’m reviewing

Older Posts »

Follow

Get every new post delivered to your Inbox.