Skip Ribbon Commands
Skip to main content






© 2007-2011
Becky Bertram

Home
May 08
Are You Content?

Today I'm going to deviate somewhat from my typical technical topics and talk instead about your job and your place in life.

But godliness with contentment is of great gain. -1 Timothy 6:6

My husband and I teach a class at our church, using the curriculum from an organization called Crown. This class is ostensibly about dealing with money God's way, but the class is more fundamental than that; the Bible is clear that our attitude toward money and how we handle it is a deeper reflection of what we believe, and a reflection of our attitude toward God Himself.

I ran across this simple sentence this morning as I was going through this week's lesson:

Biblical contentment is an inner peace that accepts what God has chosen for our present vocation and financial situation.

This stood out to me today because a professor of Information Technology at Calvin College (my alma mater) recently asked me to present a keynote address at a seminar he does for his graduating students, called Dynamic Link. The topic is going to be, "Vocation, calling, and getting a job." Then this morning I saw that word again... vocation.

According to Merriam Webster, the word vocation derivates from the Latin word vocare, which means "to call". So, literally, "vocation" is the "calling" on our life. For some of us, we have found a way to follow this "calling" in our professional life. For others of us, we simply "have a job" but are still looking for that thing that we can do with our lives that satisfies that deep gravitational pull we feel towards something.

I think there are some times where we all can feel a little "vocation envy" for others. I know I do. I remember running a 5K race about 10 years ago. I kept seeing all these people pass me by and I felt like I must be a real turtle. The competitive part of me didn't want to be left behind so I ran a faster pace than I should have, and I ended up burning out just a couple miles into the race. I had to walk the last mile or so. After I crossed the finish line, I discovered there were many, many people who finished after me, still trickling across the finish line a good 10, even 20 minutes after I did. I hadn't realized that I was just seeing the few people who passed me and didn't know that I was already so far ahead of so many people.

I think professionally I can do the same thing. I won't lie... I get jealous of those folks whose whole job is just to travel around and speak at conferences and "network" with friends. I enjoy doing all those things, but fundamentally I'm a consultant and I only get paid when I'm doing billable work for clients. I speak at SharePoint events because I enjoy it, but it's not the activity that puts bread on my particular table. But I sometimes have to stop and realize that contentment means realizing all the good things God has already done for me, that I didn't always have. My daughter just turned one; do I really want to be travelling to a different conference each month for a week at a time? I don't think so! Has God blessed me tremendously with clients that are local to where I live? Yes! More fundamentally, are there people whom God has placed in my life, in my workplace, on whom I should strive to have a positive impact? God has placed me in my particular station in life for a particular reason, and the quicker I can embrace that, instead of daydreaming about how good others must have it, the more contented I will be.

I recently saw a quote by Senator Dan Coates that I liked:

Character cannot be summoned at the moment of crisis if it has been squandered by years of compromise and rationalization. The only testing ground for the heroic is the mundane. The only preparation for that one profound decision which can change a life, or even a nation, is those hundreds of half-conscious, self-defining, seemingly insignificant decisions made in private. Habit is the daily battleground of character.

Jesus said it another way:

Whoever can be trusted with very little can also be trusted with much, and whoever is dishonest with very little will also be dishonest with much.

If we want God to propel us to the next level of our vocation, we need to make an effort to be the kind of people now who God can find trustworthy later.

May 03
TechEd 2012: I'll be there! Will you?

This year will be my first year to attend TechEd. For those other lucky folks who are attending as well, I'll be spending quite a bit of time proctoring the labs, so please stop by and say "Hi"!

 
April 24
Creating a Web Part that Executes a CAML Query Based on Managed Metadata Values Chosen By Users

Sometimes you might need a way to be able to retrieve pages that have been tagged with a particular Managed Metadata (MMD) value. In this scenario, let's say that we have a MMD field called Category. I want to be able to let my users use the Taxonomy "picker" control to select which category value they want to filter their results by.

To do this, I creat a Web Part Tool Pane EditorPart that I add to my Web Part that does the filtering. The EditorPart doesn't do the actual persisting of the data, so I need to create public properties on the parent Web Part that store the selected value. My web part, "MyWebPart" has the following three public properties:

int[] CategoryWssIds
string FilterTerm
Guid CategoryTermId
 

I want the picker control to be bound to the same node that my Category site column is already bound to, in the MMD tree.

Here's the code for the EditorPart class:

class Editor : EditorPart
{
  private TaxonomyWebTaggingControl termPicker;

  /// <summary>
  /// Adds the picker control and binds it to the same term set that the
  /// Category site column is bound to.
  /// </summary>
  protected override void CreateChildControls()
  {
    try
    {
      TaxonomyField category = SPContext.Current.Site.RootWeb.Fields["Category"] as TaxonomyField;

      if (categories != null)
      {
        TaxonomyWebTaggingControl termPicker = new TaxonomyWebTaggingControl();
        termPicker.SspId.Add(categories .SspId);
        termPicker.TermSetId.Add(category.TermSetId);
        termPicker.AllowFillIn = false;
        termPicker.IsMulti = false;
        termPicker.Width = 170;

        this.Controls.Add(termPicker);
      }
    }
    catch (Exception e)
    {
      //Display error message    
    }
  }

  /// <summary>
  /// Takes the term that was selected by the user in the picker control, and
  /// saves its properties to the parent web part's properties.
  /// </summary>
  /// <returns>A flag indicating whether the changes could be saved or not.</returns>
  public override bool ApplyChanges()
  {
    try
    {
      termPicker = (TaxonomyWebTaggingControl)this.Controls[0];

      //Evaluate if the user has picked a term
      string selectedTaxValue = termPicker.Text;
      int separatorIndex = selectedTaxValue.IndexOf("|");
      if (selectedTaxValue != string.Empty && separatorIndex != -1)
      {
        //If the user has selected a term, save that term and its lookup list id (or ids, if there are child terms)
        //to the parent web part. (The lookup ids are the ids of the term in the TaxonomyHiddenList in the current
        //site collection.)
        TaxonomyField category = SPContext.Current.Site.RootWeb.Fields["Category"] as TaxonomyField;
        Guid termSetId = category.TermSetId;
        TaxonomySession session = new TaxonomySession(SPContext.Current.Site);
        Guid termId = new Guid(selectedTaxValue.Substring(separatorIndex + 1));
        string filterTerm = selectedTaxValue.Substring(0, separatorIndex);
        int[] wssIds = TaxonomyField.GetWssIdsOfTerm(SPContext.Current.Site, session.TermStores[0].Id, termSetId, termId, true, 100);
        if (wssIds.Length > 0)
        {
          MyWebPart myWebPart = this.WebPartToEdit as MyWebPart;
          myWebPart.CateogryWssIds = wssIds;
          myWebPart.FilterTerm = filterTerm;
          myWebPart.CategoryTermId = termId;
          myWebPart.SaveChanges();
        }
        else
        {
          return false;
        }
      }
    }
    catch (Exception e)
    {
      return false;
    }

    return true;
  }

  /// <summary>
  ///Retrieves the properties stored in the web part and sets the value of the
  /// metadata picker control based on them.
  /// </summary>
  public override void SyncChanges()
  {
    EnsureChildControls();

    termPicker = (TaxonomyWebTaggingControl)this.Controls[0];
    MyWebPart myWebPart = this.WebPartToEdit as MyWebPart;

    if (myWebPart.CateogryWssIds != null &&
      myWebPart.CateogryWssIds.Length != 0 &&
      termPicker.Text == string.Empty)
    {
      termPicker.Text = String.Format("{0}|{1}", myWebPart.FilterTerm,
        myWebPart.CategoryTermId.ToString());
    }
  }
}

Now that I have an EditorPart, I need to add it to MyWebPart. I can do it like this:

public override EditorPartCollection CreateEditorParts()
{
  Editor editor = new Editor();
  editor.ID = this.ID + "_Editor";
  editor.Title = "Filter Term";
  List editors = new List();
  editors.Add(editor);
  return new EditorPartCollection(editors);
}

I need to add the following method to make sure the changes made by the Editor part get persisted to the web part:

public void SaveChanges()
{
  this.SetPersonalizationDirty();
}

When it's time to build my CAML query string, I do it like this:

StringBuilder values = new StringBuilder();
for (int i = 0; i < categoryWssIds.Length; i++)
{   
  values.Append("<Value type='Integer'>");
  values.Append(categoryWssIds[i].ToString());
  values.Append("</Value>");
}

string query = String.Format(@"<Where>
    <In>
      <FieldRef name="Category" LookupId="TRUE"/><Values>{0}</Values>
    </In>
  </Where>", values.ToString());

One thing to keep in mind is that this is querying the hidden list in the current site collection that stores all the MMD values that have been used. So, if you haven't used a particular term yet anywhere in your site, the GetWssIdsOfTerm method won't give you anything back.
March 23
SharePoint Saturday St. Louis 2012 Announced

SharePoint Saturday St. Louis

I am happy to announce that SharePoint Saturday St. Louis is back by popular demand, and will be taking place on June 2, 2012, at the Danforth Campus of Washington University.

We are currently looking for speakers and sponsors for this event. To learn more about presenting, or sponsoring the event, please visit the web site at http://www.sharepointsaturday.org/stlouis.

March 05
MVP Summit 2012

This year's MVP Summit was the best one by far of the 3 I've been to. We had great interactions with the product team and as always, it was fun to meet old friends from all over the world as well as make new friends.

I decided to go and play paintball this year. I was the only woman who showed up (surprise, surprise) but at least I can say I've done it now (and have the bruises to prove it!) 

We had a rare day of sunshine. The painball course was on the side of a mountain, from which we had a fantastic view of the Cascade mountains. I enjoyed the opportunity to have some longer conversations with MVPs from Iran and Portugal. (I was one of only 3 Americans playing paintball. It always amazes me just how big the world is, and how many places use Microsoft SharePoint!)


February 23
Wrox SharePoint Server 2010 Enterprise Content Management

SharePoint-Server-2010-Enterprise-Content-Management.jpgA friend of mine from St. Louis, Todd Kitta, helped author a recent SharePoint 2010 book titled SharePoint Server 2010 Enterprise Content Management, published by the same publisher as my book, Wrox. He wrote the book with some of his fellow co-workers at KnowledgeLake, a St. Louis based company that is one of the leaders in document imaging with SharePoint integration. (Please note, this book is not a KnowledgeLake production, but just happens to be written by several colleagues who happen to know a lot about document management.) I was especially interested in reading this book because, frankly, I have a lot of questions about enterprise content management in SharePoint, but specifically how to handle some of the specific issues involved when working with sites with large volumes of documents. (Because my focus is typically on building Publishing sites, the sites I help build tend to contain a lot of textual and visual content, and are generally more "web content"-centric than document-centric, so this topic is one I don't deal with that often and I need to learn more about.)

I think the first thing that I appreciated about this book is the level of detail to which it goes concerning certain topics. There's a good market out there for beginner-level books (and in fact the book I helped author was one of these), and even sometimes the advanced level books, while good, are trying to cover advanced topics covering such a broad range that even the in-depth topics aren't that in-depth. I feel like, much like the BCS book I read, this ECM book did a great job of shining a laser beam on the exact topic that the book was intended to cover. In addition, Microsoft's documentation tries to outline the "how" of the product, but it's not going to tell you when to do what so much, because Microsoft is leaving the application of the product up to you. This book gives some very concrete examples and reasoning for how and why you would do certain things in SharePoint if your SharePoint solution was acting as an ECM repository.

The first section gives a nice overview of the components that make up an ECM solution, and an overview of SharePoint 2010 in general. The second section of the book makes up the "meat" of the book, discussing the "pillars" of an ECM environment, such as document management, workflow, search, infrastructure, etc. The last section of the book talks about several somewhat unrelated, but helpful topics including: how to work with various file types; what third party companies are out there with products to help you out; and how to migrate your existing ECM solution to SharePoint 2010.

I just wanted to highlighted some of the topics that I found the most helpful in the book.

  • Document Management
    I though this chapter would be a snoozer. I mean, who doesn't know that SharePoint stores documents? This chapter surprised me with detailed information I wasn't expected, such as examples of how to work with the Managed Metadata Service and Document IDs programmatically. It also explained about how the Managed Metadata navigation works and mentioned the Content Organizer. These are concepts that aren't always implemented in "run of the mill" SharePoint sites.
  • Workflows
    While this chapter covered many of the common workflow topics such as using out of the box workflows, as well as using SharePoint Designer, Visio, and Visual Studio to create workflows, this chapter covered a little-mentioned topic, and that is "pluggable workflow services." These are workflows that can interact with an outside system. For example, building one of these can allow your HR system to initiate a workflow in SharePoint.
  • Collaboration
    I got a little thrown by this chapter title because "collaboration" used to be Microsoft's catch-all term for what SharePoint 2007 did. However, I quickly realized that this chapter covered what is commonly described as the "social media" functionality of SharePoint. (It makes sense, because "social" means people are interacting with one another, which is what happens during collaboration as well.) Like the other chapters, what I appreciated about this chapter was that it didn't just walk through the out of the box functionality, but it outlined how to programmatically interact with things like the "tagging" mechanism, or "notes" (i.e. user comments on items). This is actually going to come in handy on my current project. (I love it when books are practical!)
  • Search
    Obviously, search is a topic that easily warrants its own book. The author of this chapter did a great job of summing up details in a single chapter. This was one of my favorite chapters because it talked not just about search features (i.e. how search works) but it gave great guidance as to how to scale your search infrastructure. This is an incredibly complex topic (that has changed quite a bit from SharePoint 2007) and the author did as good a job as I think is possible to explain the relationships between the search indexers, databases, query engines, etc. I especially appreciated the sections that outlined how to balance out database size and query performance.
  • Web Content Management
    OK, so this is my bread and butter, and I wrote a chapter on WCM for the Six-in-One book. It turns out we mentioned many of the same things, so at least I know I wasn't completely out to lunch when writing my own chapter! :)
  • Records Management
    This is a topic I've recently learned a lot about, based on an application I wrote for a records-retention company. I appreciated how the chapter started out talking about the people involved with records management (i.e. records managers, compliance officers, etc.) and didn't just jump right into functionality. Along those lines, the chapter did a good job of talking about how the legal rules you need to be adhering to is the true determiner of how you construct your technical solution. Like the other chapters, this chapter gave programmatic examples of interacting with records, routing rules, etc.
  • Digital Asset Management
    Although this can seem tangential to some degree to typical ECM solutions, it's actually more frequently discussed and implemented in ECM solutions that I think most would believe. The major thing to keep in mind when dealing with DAM solutions in SharePoint is that SharePoint stores everything in a database. By nature, images usually are really big documents, so it's important to decide what sort of performance and cost considerations are involved when companies decide to store large digital assets in SharePoint. This chapter covers topics such as Remote Blob Storage, which are integral to this type of discussion. (Remember that KnowledgeLake's bread and butter is helping people store scanned images in SharePoint document libraries, so I feel good knowing the author of this chapter knows what they're talking about!)
  • Document Imaging
    Again, I don't typically think of document imaging as being a core part of an ECM solution, but I would say that probably 90% of my clients have mentioned at some point in their conversation with me that in parallel to me building an Intranet for them, they're in discussions with third party vendors regarding how to scan documents and store them in SharePoint.  This chapter is a little different from the other chapters, in that it walks you through the steps of building a sample application. It's very detailed, but gives you a true "soup to nuts" type of overview of the application elements you'd need to make this work.
  • Electronic Forms with InfoPath
    InfoPath forms: you love 'em or you hate 'em. If you're in the former camp, then you're probably using InfoPath in your ECM solution. This chapter gives an overview of how InfoPath integrates with SharePoint, and gives you a brief sample walk-through that you can follow to set up your own sample InfoPath form in SharePoint. This chapter is rather short, but is a nice overview of the basic InfoPath features. (Like some other topics covered in the book, if you're interested in more, you might seek out a book that covers just InfoPath.)
  • Scalable ECM Architecture
    This was by far my favorite chapter, mainly because I am a developer and not an IT person and know almost nothing about architecture! This chapter goes in detail about how to set up your SharePoint farm to handle large volume.

Although this book was published later than most others, I think most of its concepts will probably hold true even through the next version of SharePoint (although that is purely conjecture on my part! Please don't read anything into that statement. :) ) I highly recommend this book for those who want to know the details of how to build a robust ECM environment with SharePoint 2010.

February 09
Visual Studio Project Organization

When first building SharePoint projects in Visual Studio, it can be confusing to know how to organize your various SharePoint assets. Like many things with SharePoint in particular and software development in general, there's no "right" solution. However, having built numerous Visual Studio solutions for various clients, I've come up with general methodology that I thought I'd share, since virtually every client asks me how they should organize their projects. (I was prompted to write this because I saw a similar question posted in the SharePoint Forums.)

Naming Conventions

First off, I always name the following assets using an identical "namespace-like" name:

  • Visual Studio Solution
  • Visual Studio Project
  • Assembly name
  • Namespace
  • Solution Package Name
  • Feature Folder name

For example, say my prefix is "BB.SP2010" (for Becky Bertram, SharePoint 2010). I would have a Visual Studio project called BB.SP2010. Inside that solution I might have a Visual Studio project called BB.SP2010.Lists and another project called BB.SP2010.WebParts. In this way, it's obvious that the two projects are related, because they share the same prefix. Typically, Visual Studio projects pick up their namespace and their assembly name from the name of the project. So, my BB.SP2010.Lists project now also has an assembly name of BB.SP2010.Lists.dll and a namespace of BB.SP2010.Lists. Furthermore, I'll go ahead and make sure my solution package has a name of BB.SP2010.Lists. Finally, I'll make sure my Feature folder has the same name. (Keep in mind the name of the Feature is what shows up in the browser, and that can be a little more "user friendly", such as "Becky Bertram's Custom Lists". Also keep in mind that SharePoint automatically tries to append the Visual Studio project name to the front of the Feature folder name. However, this isn't necessary if we're using this common naming system, so make sure you customize the folder name to remove that Project name prefix.)

As you can imagine, comparing apples to apples to apples to apples simplifies life. If I see an assembly called BB.SP2010.Lists in my GAC, it's not hard for me to figure out it probably got there when the solution package called BB.SP2010.Lists was deployed. Along the same lines, if I see a Feature folder on the file system in the "FEATURES" folder, called BB.SP2010.Lists, it's not hard for me to figure out that the code for the feature is stored in the assembly by the same name.

Another, more minor helpful thing about naming projects in this way, is the fact that if you have mulitple assets (assemblies, Feature folders, solution packages, etc.) deployed to the same location, it's easier to spot all your custom assets at once because they'll be listed next to each other alphabetically.

Project Organization

Once your naming conventions are determined, knowing how to organize your content is a different matter. Say you have an event receiver, a custom list definition, a master page, a web part, and a workflow that you want to deploy. How do you structure your Visual Studio solution, projects, and more specifically solution packages and features?

As I said before, there's no right answer. I always tell my customers, even if you pick an approach that's not perfect, just be consistent! I've generally chosen one of two approaches: 

  • Creating Visual Studio projects (and therefore solution packages) based on the type of asset I'm creating. In this scenario, I might have one Visual Studio solution with two projects: BB.SP2010.Intranet.Lists, and BB.SP2010.Intranet.WebParts. I often go this route if I'm building a new application from scratch, such as an Intranet site. This way it's easy to know that if I'm building an event receiver on a list, it goes in the Lists project, and if I'm building a Visual Web Part, it goes in the WebParts project. If I have dependencies in these two I make sure my Features have dependencies specified. (For instance, I shouldn't be able to add a web part to my site that displays data from a custom list until the Lists feature has been activated so that the custom list is available.)

    The important thing to keep in mind with this approach is what happens when a solution package upgrade happens. Files on the file system are replaced with new files from the new solution package. Why is this important?

    1. People don't like the idea of upgrading the code-behind of 10 web parts if they're only making a small change to one of them. However, if when the solution package is deployed, the code-behind for the other 9 web parts is replaced with replaced with the same code they had before, who cares? There really are no negative repercussions. (provided you don't give your assembly a new version number.)

    2. Sometimes the thing you're updating only gets updated when a Feature is activated. For instance, the new version of your solution package might execute code in a Feature event receiver when the Feature is activated. What this means is that even though the new code is on the file system, it's irrelevent until it gets executed by deactivating and reactivating the Feature. This can be good or bad, depending on how you want your code to work. It's just something you need to keep in mind. However, the key is that just because you put new assets out there by upgrading your solution package, it doesn't necessarily mean those changes will immediately "take".

    The reason I point these things out is that people sometimes are scared to put a whole bunch of items in a single solution because they don't want to update a bunch of extraneous stuff just to make one change. I'm just pointing out that "updating" the other items isn't always a bad thing if you're just overwriting the assets with identical versions.

  • Create Visual Studio projects according to application functionality.  This approach works well for discrete pieces of functionality that really can be encapsulated. For instance, I might have a particular application that I want to deploy. That application has a custom list. When users add an item to that list, an event receiver needs to fire that will lock down the list item to allow only that person to view the item and edit it. I need to have three web parts: one to render the contents of the list, one to allow users to edit items in the list, and one to allow users to view an item from that list. I need just one Feature that, when activated, will add the list definition, list instance, three web parts, and event receiver in the site collection. In this kind of scenario, I would create a new project called BB.SP2010.MyCustomApp. In this scneario, I might have multiple Visual Studio projects in the same Visual Studio solution, each "bundled" by application logic. (all having the same prefix, of course.)

    The benefit to this approach is that if you have multiple developers, you can tell one developer, "You work on Application A" and tell the other developer "You work on Application B", and you know they won't be tripping over one another.

I personally tend to go with the first option. The reason is that I often build Publishing sites for clients, and I'm often the only developer writing code. I might create a site column that's used in both lists and page layouts. I also probably have web parts that use styles that were defined in a style sheet that got deployed with my publishing features. Because I typically have so many dependencies, it doesn't work so well for me to use the application-centric model. I would end up having some pretty bizarre dependencies ("Why is my Accounts Payable feature dependent on my Health and Fitness Tracking feature?") So I don't have strange dependencies where maybe a workflow in one Feature is dependent on a site column having been provisioned in another Feature, it might makes sense to come up with sort of "base" solution packages that deploy common assets such as site columns, content types, style sheets, etc., that other projects/solution packages/features/assets might use. However, in my mind, once I've done that, I figure I'm already down the path of createing "Workflow", "Columns", "Publishing" projects, etc., which sounds an awful lot like the first approach I mentioned. For me, that's why I go with approach number 1.

January 16
Dynamically Updating the CQWP ItemXslLink Property to Point to the Local Site Collection

The Content Query Web Part (CQWP) is the workhorse of SharePoint Publishing sites. The power behind the CQWP is that it allows you to configure a query using the web part tool pane, but then style the resulting XML by using your own custom XSL style sheets. Unless you want to modify the existing XSL file in SharePoint Designer, however, you need to modify a property of the web part (called ItemXslLink) to point to your new, custom XSL file. You can't update this property within the web part tool pane; the only way you can do it is to utiize a version of the web part that already has this value set. You can do this by either uploading a version of the .webpart file with this property set, or you can add a version of this .webpart file to your Web Part Gallery.

Often times, when developing SharePoint solutions using Visual Studio, the optimal things to do is to add this modified .webpart file to a solution package, and include it in a Feature, so that when the Feature is activated, this modified .webpart file gets uploaded to the Web Part Gallery.

There is a problem, though. The XslItemLink property requires an absolute URL. It does not use a site-collection-relative URL. That is to say, if you give your ItemXslLink property a value of "/XSL Style Sheets/MyStyleSheet.xsl", it will always look for the file in the site collection with a base URL of "/", so SharePoint will look for a file with a URL of something like http://mysite.com/XSL Style Sheets/MyStyleSheet.xsl. It doesn't matter if your site collection is actually located at http://myurl.com/sites/accounting/. If you try to use the original URL in a different site collection, SharePoint displays an error message when you try to use the web part, which says, essentially, you can't use that XSL file because it's in a different site collection. If you wanted the style sheet to work in the latter site collection, you'd have to use a different version of the .webpart file that has an ItemXslLink property set to that current site collection's URL, such as http://mysite.com/sites/accounting/XSL Style Sheets/MyStyleSheet.xsl.

Why is this a problem? Because if I deploy a Feature scoped to the Site Collection level, my assumption is that the Feature should work in any site collection where the Feature is activated, not just in the single site collection where that ItemXslLink property path points to. (It completely defeats the purpose of reusability in Features.)

Although I'm not typically a fan of customization, I don't see a way around it when dealing with this issue. The code you see below is code you can put in a Feature event receiver that will dynamically look for the CQWPs in your Web Part Gallery (which presumably were just added there when the Feature was activated), search the .webpart XML for the ItemXslLink node, and populate it with a site collection-specific URL.

Please note that the query looks for any web part file in the Web Part Gallery that begins with "CQWP_". I have purposely given my CQWPs this prefix, so my web parts have names like CQWP_NewsRollup.webpart, etc. You can choose to do a different CAML query if it suits you. Also, please substitute the path to the XSL style sheet with the path to your own style sheet. Finally, this code would obviously work, with a little modification, if you wanted to modify other properties in the CQWP web part file, too.

SPQuery query = new SPQuery();
query.Query = "<Where><BeginsWith><FieldRef Name=\"FileLeafRef\"/><Value Type=\"Text\">CQWP_</Value></BeginsWith></Where>";
SPListItemCollection items = site.RootWeb.GetCatalog(SPListTemplateType.WebPartCatalog).GetItems(query);
if (items.Count > 0)
{
    XmlDocument xmlDoc = null;
    XmlReader xmlReader = null;
    foreach (SPListItem item in items)
    {
        Stream fileStream = item.File.OpenBinaryStream();
        StreamReader fileReader = new StreamReader(fileStream);
        StringReader stringReader = new StringReader(fileReader.ReadToEnd());
        xmlReader = XmlReader.Create(stringReader);
        xmlDoc = new XmlDocument();
        xmlDoc.Load(xmlReader);
        XmlNamespaceManager nsm = new XmlNamespaceManager(xmlDoc.NameTable);
        nsm.AddNamespace("wp", "http://schemas.microsoft.com/WebPart/v3");
        XmlNode propertiesNode = xmlDoc.DocumentElement.ChildNodes[0].ChildNodes[1].ChildNodes[0];
        XPathNavigator navigator = propertiesNode.CreateNavigator();
        XPathNavigator selectedNode = navigator.SelectSingleNode("wp:property[@name='ItemXslLink']", nsm);
        if (selectedNode != null)
        {
            selectedNode.InnerXml = site.ServerRelativeUrl + "/Style Library/XSL Style Sheets/CustomItemStyle.xsl";
            if (item.File.CheckOutType == SPFile.SPCheckOutType.None)
            {
                item.File.CheckOut();
                item.ParentList.RootFolder.Files.Add(item.File.Url, Encoding.UTF8.GetBytes(xmlDoc.InnerXml), true);
                item.File.CheckIn("Modified by feature event receiver.");
            }
        }
    }
}

December 30
Connection String Manager

Although Business Connectivity Services provide a robus way of connecting to back-end data sources, it's sometimes easier to use the ADO.NET object model. However, there's a catch with this approach: because SharePoint web front end servers can be load balanced, it's not usually a good idea to modify the web.config files on your servers directly in order to add your connection string information. Instead, you're supposed to use the SPWebConfigModification class. This presents its own problem, though; if you have to embed the connection string in code, it defeats the reason why you wanted to put the connection string in the web.config file in the first place... to provide an uncompiled way to change the connection string for each new environment (such as Dev, QA, ad Prod)!

A way around this is to store connection string information in the property bag of an SPWeb object, or in the property bag of your web application (SPWebApplication) object. The nice thing about this approach is it allows you to modify the connection string in different farms. The downside is that there's no native ability to modify property bag data in the SharePoint user interface.

I decide to go ahead and build my own quick-and-dirty Connection String Manager. (The link to the Visual Studio project is at the bottom of this article.) It allows you to add, remove, and edit connection strings for a particular web application.

First, you'll deploy the solution package (BB.ConnectionStringManager.wsp). After that, you'll see a new farm-scoped feature called Connection String Manager. Once you activate the feature, you'll see a new link show up in Central Administration on the Application Management page, under the Web Applications heading. It will say Connection String Manager. 

Link to Becky Bertram's Connection String Manager

If you click on this link, it will take you to the Connection String Manager page. From here, you can select the web application you want to add connection strings to.

Becky Bertram's Connection String Manager

All this interface does is add a property to the underlying SPWebAppilication.Properties property bag, with the name of the connection string (prefixed by the word ConnectionString). If you want to access the connection string, you can simply do it in your code by accessing the current site's web application object and retrieving the value directly, like this:

SPContext.Current.Site.WebApplication.Properties["ConnectionStringDatabaseA"].ToString();

Otherwise, you can use a public static class that's part of the BB.ConnectionStringManager assembly, like this:

string connectionString = BB.ConnectionStringManager.ConnectionString.GetConnectionString("DatabaseA");

You can download the zipped up Visual Studio project here:

BB.ConnectionStringManager.zip

December 15
Creating Retention Rules that Work

Thanks to everyone who came to hear Marty Hansen and I present our lecture "Step 1: Create Retention Rules that Work" lecture, whether you heard us in Kansas City this weekend at SharePoint Saturday, or whether you heard us at the St. Louis User Group on Tuesday.

For those who are interested, you can download the slide deck here:

Developing Retention Rules that Work

(To the left is a pictue of me and my little elf at SharePoint Saturday Kansas City.)

1 - 10Next