25/08
2010

In SharePoint 2010 I came across a bug where the listitems lost their content types and reverted all the way back to being a ‘folder’. This was in a document library where the content types were managed by setting “Allow management of content types” to Yes in the advanced library settings.

I needed a way of iterating through every list item in the list and programmatically setting the content type to the correct one.

First off I made a small struct to store the path of the document library and the corresponding content type name.

public struct ListContentTypeMapping
    {
        public string Folder;
        public string ContentType;
    }

Then I iterate through the list to change the content types. The actual way to change content types of list items (at least in sharepoint 2010) is to change the property called “ContentTypeId” and set it to the Id (SPContentTypeId) of the chosen SPContentType object.

public void SetContentTypesOnDocumentLibraries(IEnumerable<ListContentTypeMapping> mappings)
	{
		using (var siteRoot = new SPSite("http://sitename/"))
		{
			foreach (var mapping in mappings)
			{
				using (var web = siteRoot.RootWeb)
				{
					var list = web.GetList(mapping.Folder);

					foreach(SPListItem item in list.Items)
					{
						ChangeContentType(list, item, mapping.ContentType);
					}
				}
			}
		}
	}

private static void ChangeContentType(SPList list, SPListItem listItem, string contentTypeName)
	{
		SPContentType contentType = list.ContentTypes[contentTypeName];

		listItem["ContentTypeId"] = contentType.Id;
		listItem.Update();
	}

Here’s some sample code, it will change all the items in those folders to the specific content types.

var mappings = new List<ListContentTypeMapping>();
mappings.Add(new ListContentTypeMapping { Folder = "some/path/to/list/", ContentType = "ContentTypeOne" });
mappings.Add(new ListContentTypeMapping { Folder = "some/path/to/anotherlist/", ContentType = "ContentTypeTwo" });
SetContentTypesOnDocumentLibraries(mappings);
19/08
2010

Sharepoint has a nasty habbit of keep navigation flyouts on the screen for a good second before they hide.  This is probably to aid accessibility where a user might not have precise mouse control.  However if you’re a good designer then you should have plenty of large clickable navigation menu items and plenty of white space.

When rolling over a number of flyouts quickly, the user sees all the flyouts shown on the screen at once which looks rubbish.  To remove the delay altogether use this css in your page somewhere:

li.hover-off>ul
{
    display:none;
}

The way it works is when you hover over an item in the nav the built in sharepoint javascript adds a css class called “hover” and as soon as your mouse leaves the area it changes the class to “hover-off” for 1 second before removing it completely. This CSS will hide the unordered list directly below the list item that has the class “hover-off” thus hiding the flyout as soon as your mouse leaves the parent.

(P.s. this kind of flyout navigation can be set up by setting the ‘MaximumDynamicDisplayLevels’ attribute of the SharePoint:AspMenu control)

06/08
2010

On the SPFile object there is a method called UndoCheckOut().  This discards the checkout and returns the file or publishing page back to its original state.

To discard the checkout of a publishing page do this:

publishingPage.ListItem.File.UndoCheckOut();
04/08
2010

Just got a new laptop and noticed a few youtube videos were green and garbled as if they had been corrupted.  I went and disabled hardware acceleration in the flash settings and now everything is fine :)

15/07
2010

I’ve just recently attempted to upload an entire directory up to sharepoint.  I did initially try doing this using WebDAV but the speed was unimaginable slow (30 – 40 secs for a 1KB gif image).  I didn’t want to upload the images through the browser as you can only do 100 at a time (I had ~3,500 images plus ~4,000 documents).

So I looked at copying over the files using the sharepoint object model!  It turns out that this is speedy fast and works a treat!  There’s an MSDN article I used as my basis but I’ve made a few changes so it uploads a whole directory instead of just a single file.  Here’s how you do it:

public void UploadImages()
{
    this.CopyDirectory(@"\\some\server\images", "PublishingImages");
    this.CopyDirectory(@"C:\ImagesFolder\ImagesToUpload", "PublishingImages");
}

private void CopyDirectory(string frm, string to)
{
    var serveraddress = "http://server:80/";
    using (var web = new SPSite(serveraddress + to).OpenWeb())
    {
        if (!web.Exists)
        {
            throw new Exception("Web doesn't exist here");
        }

        var di = new DirectoryInfo(frm);
        var fileInfos = di.GetFiles("*.*", SearchOption.TopDirectoryOnly);
        foreach (var fileInfo in fileInfos)
        {
            this.UploadFile(fileInfo.FullName, web);
        }
    }
}

private void UploadFile(string srcUrl, SPWeb web)
{
    if (!File.Exists(srcUrl))
    {
        throw new ArgumentException(String.Format("{0} does not exist",
            srcUrl), "srcUrl");
    }

    byte[] contents;

    using (FileStream fStream = File.OpenRead(srcUrl))
    {
        contents = new byte[fStream.Length];
        fStream.Read(contents, 0, (int) fStream.Length);
        fStream.Close();
    }

    string filename = srcUrl.Substring(srcUrl.LastIndexOf("\\") + 1);

    if (!IsValidFileName(filename))
    {
        throw new Exception(filename + " is not a valid filename.  Please review and try again.")
    }

    var file = web.Files.Add(filename, contents)
    file.Update();
}

private static bool IsValidFileName(string filename)
{
    if (filename.Contains("..") || filename.EndsWith(".") || filename.StartsWith("."))
    {
        return false;
    }

    if (filename.IndexOfAny(new[] { '\"', '#', '%', '&', '*', ':', '<', '>', '?', '\\', '/', '{', '|', '}', '~' }) > -1)
    {
        return false;
    }

    if (filename.Length > 128)
    {
        return false;
    }

    return true;
}