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);
 

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();
 

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;
}
 

In a recent project, I’ve begun to heavily use regular expressions to capture the data from a looong string.  This post will demonstrate how I used ExplicitCapture to make it really easy to use the captured data later on using the Match object.

The data is in the following format:

[Data I do not care about...]<textarea1><![CDATA[blahhhhhhh]]>
</textarea1> <textarea2 xmlns=""><![CDATA[bada bing bada boom]]>
</textarea2>[More data I do not care about]

I need to capture all the data inside those CDATA areas, and also the number of the textarea.  Even though the content of the CDATA may be HTML and therefore not a regular language, I am still using a regular expression here as the content outside of the textareas is regular.  If you need to extract data from HTML, I’d recommend using HTML Agility Pack.

Regex pattern = new Regex("<textarea(?<textareaid>\\d+)( xmlns=\"\"|)><!\\[CDATA\\[(?<content>.*?)\\]\\]></textarea\\d+>", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.ExplicitCapture);

Here I’m storing my regular expression in to a variable called pattern.  The expression is a little more complex than expected due to me having to escape my backslashes when I do actually want them represented in the regex pattern.  The interesting parts are the RegexOptions in the second parameter of the Regex constructor.

Of course I’m using ignore case as there may be instances where the case is different.

I’m using single line as I don’t want to take in to account line breaks, nor am I using ^ or $ to indicate the start or end of lines respectively.  This option treats the string as one long unbroken line with line break characters and so enables a kind of dotall functionality.

The final option I’m using is explicit capture. This allows me to specify which capture groups I’m actually interested in and giving them a name.  In the above example, you can see I capture the textarea id number by writing (?<textareaid>\\d+).  This matches one or more digits and calls it “textareaid”. Awesome.

Now to use that later on I use the following code:

MatchCollection matches = pattern.Matches(content);
string convertedContent = String.Empty;
foreach (Match match in matches)
{
    convertedContent += string.Format("<div id=\"textarea{0}\">{1}</div>", match.Groups["textareaid"].Value, match.Groups["content"].Value);
}

The string convertedContent will now contain as many divs as there are textareas and will contain the content held within the CDATA.  Using match.Groups["whatever"] is a really easy way to get at the match values.

 

Update: an even better solution has been provided by ivowiblo in the comments below making use of DataLoadOptions! Be sure to check that out! – Original post as follows:

It might seem like a sudden change for me to blog about C# and ASP.NET MVC but it’s what I work with at my work and I’m loving it. Plus I haven’t had the inspiration to use django in months. From now on, most posts will be about C#, ASP.NET MVC, HTML, CSS, and many other acronyms…

This may be already have been obvious to you but I had been working with a slow query for a number of days before I really realised what was going on.

Here’s the scenario: You want to show a list of two or more fields from different sql tables on a single page but for some reason the longer the list gets, the page just keeps taking longer and longer to load.

Continue reading »

© 2011 athe.la blog Suffusion theme by Sayontan Sinha