Skip to main content

Click Mice, Unclick Mice

Go Search
Home
Wikin
Windows Live ID Authentication
  

Keith Bunge's dealings with SharePoint, Virtual Server and anything else he comes across.
SharePoint Designer videos
I worked on a series of video topics for SharePoint Designer.  The videos cover a pretty wide range of SPD functionality.  Most of the videos are around 5 minutes long so if you are looking for a quick intro in to how to do something in SPD it may be worth a look.  There are about 20 topics from data sources to workflow.  You can find them here:
 
 
 
Microsoft Office 2007  Development Environments pt. 2
A new version of the whitepaper has been released.  The major updates are around Visual Studio 2008.  There has been a section added to include more detailed information around setting up Visual Studio 2008.  There are also new project samples including a sample on a web part, list definition and a document level addin (for VS 2008).
CKS: Windows Live Authentication
I know it has been a little while since I have posted something.  I have been working on the Community Kit for SharePoint in the last few weeks.  I have been working on the Enhanced Blog Edition, but probably the bigger contribution was incorporating my Windows Live Authentication implementation in to the CKS.  I took what I had from my 1.0 release and rolled it in to the CKS solution.  I am a member of the Puget Sound SharePoint User Group and they decided to use the new CKS: WLA module which showed something was missing, so I added profile syncronization to my initial release.  What this really means is that going forward I will be doing my work in the CKS project on CodePlex so feel free to get involved as well!
 
Lawrence Liu has some screenshots and additional information posted on the SharePoint Team Blog here.
Windows Live ID Authentication for SharePoint Released
Sorry to those people I had promised an install to last week.  I got caught up in a few other projects, but I have completed the testing and have a SharePoint solution prepared for download.  I have created a site for any issues or feedback on the authentication provider, so feel free to go grab a copy and take a look.  You can find it on the Windows Live ID Authentication blog.  The solution has been tested on  a few different systems and hopefully the installation and instructions are easy for everyone to follow.  Big thanks to Todd Klindt for helping me nail down the some installation usability issues and for his feedback on the documentation.
 
Later this week I will start a series of posts about the implementation to give everyone an idea of how it works.
SharePoint List Performance
Anyone who has dealt with development in SharePoint has likely had to deal with SPList, SPListItem and SPListItemCollection.  Recently I have been working on some projects that were experiencing some performance issues.  Looking at the code, I started to realize that while I had heard a lot of things from Microsoft (specifically the SharePoint product team) about how much the SharePoint list performance had been increased, I had not actually seen a lot of publications around where, how and some good ways to take advantage of them.  Certainly there are articles about Indexed columns, and many about views only up to 1000-2000 items.  I decided to dig a little deeper, and I think you might be as surprised as I was about what I found.
 
Test Data / New Item Performance
For part of the test, I decided I should combine the testing with creating the test data.  One of the performance items I wanted to test was how SharePoint handled new items.  Specifically, I wanted to know how quickly new items could be added, and I wanted a list in the end with 100,000 list items.
 
What I found was that SharePoint could really tear in when it came to creating a new item.  Now I had a very simple list, it consisted of:
  • SingleText - A single text column that I used to put GUID's in to
  • Number - A number column I used to put a counter in to
  • Choice - A choice column I used to put a text choice in to
  • Bool - A boolean column I put either true or false in too (what choice did I have right?)

On my test hardware I could push about 50 items per second.  I was actually pretty happy with that performance.  It means I could populate 10,000 items in around 3.5 minutes.  So how did I come by these numbers?  I setup a list as above; I just used the Custom List template, and I used the following code:

  DateTime start1 = DateTime.Now;
  for (int i = 0; i < numItems; i++)
  {
    cn = rand.Next(0,3);
    SPListItem item = list.Items.Add();
    item["SingleText"] = Guid.NewGuid().ToString();
    item["Number"] = itemCount + i;
    item["Choice"] = choice[cn];
    item["Bool"] = (cn <= 1);
    item.Update();
  }
  DateTime end1 = DateTime.Now;
  TimeSpan timer1 = new TimeSpan(end1.Ticks - start1.Ticks);

As you can see this code sets a time before and after the for loop which we can use to figure out how many seconds total were spent in the loop.  I tested this by adding 1, 10, 100, 1,000, 10,000 and finally around 85,000 items to the list, and in general the timing of around 50 list items per second seemed to hold true for my hardware. 

Updating Items Performance
There are really two ways to update items in a SharePoint list.  Either you use the SPListItem.Update() method or you use the Lists web service UpdateListItems() web method.  I decided to try them both, and what I found really surprised me.  As I looked in to it more, I found some things I really hadn't expected. 
 
SPListItem.Update()
The first thing I stumbled on to was SPListItemCollection's Count property.  Apparently, using it forces some enumeration of the SPListItemCollection.  In my initial code to update items, I was using the Count property in a for loop.  The performance of that trial was just awful.  I looked in to it more and I found that by moving the SPListItemCollection Count property access outside of the for loop in to an integer variable, I was able to increase performance dramatically.  When I was testing this I had around 5,000 items in my list, and every call to the Count property was costing me around 2.5 seconds.  So an important note to anyone that uses the Count property of a SPListItemCollection - Make sure you only call it once and then put it in to a variable you control so you can refer to that instead of calling it again. 
 
After I tracked down the Count snafu I was well on to my way to getting numbers for the Update performance.  What I found is that I was only able to update 1 list item every 2.8 seconds.  Surprised?  I was.  Now as I put that together with what I found with the Count property, I had to wonder if some list item collection isn't being enumerated.  It seems quicker to delete an item and create  a new copy.  Here is the code I used to test this update:
 
  DateTime start2 = DateTime.Now;
  for (int i = 0; i < numItems; i++)
  {
    cn = rand.Next(0,3);
    list.Items[i]["SingleText"] = Guid.NewGuid().ToString();
    list.Items[i]["Number"] = itemCount+ i;
    list.Items[i]["Choice"] = choice[cn];
    list.Items[i]["Bool"] = (cn <= 1);
    list.Items[i].Update();
  }
  DateTime end2 = DateTime.Now;
  TimeSpan timer2 = new TimeSpan(end2.Ticks - start2.Ticks);
 
I am doing the same thing here with the time being set before and after the for loop.  I am afraid I only had the patience to test this with 1, 10, 100 and 1,000 items.  It was just taking too long.
 
Web Service UpdateListItems() Performance
I came to this part of the testing a little biased.  I had just witnessed the poor performance of the SPListItem.Update() and, as I stated earlier, I fully expected the web service to perform even worse.  But I was pretty much as wrong as a person can be.  UpdateListItems() allows you to submit items in batch using a XML item update definition.  This allows you to submit multiple items at once if you have them all to submit (I did find that 400 or so is the max you want to submit at once).  I hit my test button fully expecting that I would have to go make a sandwich while I waited, but as I was starting to stand up it came back as completed.  I blinked a little, thinking it was broken, but it had updated the 100 items I told it to.  As surprising as it sounds, the web service was pushing updates at the comparitively blazing speed of 50 items per second.  It was matching the creation speed of new items.  If you are as puzzled as I am about why well I am thinking of starting a support group.  As a performance tip in your code: 
Do - use the web service to update list items if you need to update them yourself. 
Don't - use SPListItem.Update() in your code if you have to update more than one item at a time. 
 
Here is the code I used for testing:
 
  DateTime start1 = DateTime.Now; 
  Lists listService = new Lists();
  listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
  listService.Url = "
http://server/site/_vti_bin/Lists.asmx";
  System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
  System.Xml.XmlElement batchElement = doc.CreateElement("Batch");
  batchElement.SetAttribute("OnError", "Continue");
  batchElement.SetAttribute("ListVersion", "1");
  batchElement.SetAttribute("ViewName", "{F15B3020-EF3F-4458-B8A2-FD7F10725F82}");
  for (int i = 1; i < numItems + 1; i++)
  {
    cn = rand.Next(0, 3);
    xml.AppendFormat("<Method ID='{0}' Cmd='Update'><Field Name='ID'>{0}</Field><Field Name='SingleText'>{1}</Field><Field Name='Number'>{2}</Field><Field Name='Choice'>{3}</Field><Field Name='Bool'>{4}</Field></Method>",i, Guid.NewGuid(), itemCount+i, choice[cn], (cn <= 1));
  }
  batchElement.InnerXml = xml.ToString();
  listService.UpdateListItems("TestDB", batchElement);
  DateTime end1 = DateTime.Now;
  TimeSpan timer1 = new TimeSpan(end1.Ticks - start1.Ticks);
 
I have so much more code quoted here because I was timing the additional cost of having to setup the web service connection, and it still was over 100 times faster than using SPListItem.Update().
 
Reading / Querying Item Performance
Once I had my 100,000 list items, I was not expecting spectacular performance on my list queries.  I was pleasantly surprised by what I found.  While the queries could take some time, there were certainly ways to get around those issues.  I was able to query the list with various different CAML queries with an average speed of around 2,000 items per second.  This speed held true when querying through the Lists web service GetItems() method as well.  This meant when I queried my 100,000 item list and I returned 89,000 items in the result set, it took around 44 seconds to completed the query.  I found that presorting the items in the query using <OrderBy> didn't affect the performance noticably at all.  I did these two queries:
 
  <Where><Contains><FieldRef='SingleText'/><Value Type='Text'>8</Value></Contains></Where>
  <OrderBy><FieldRef Name='Number'Ascending='FALSE'/></OrderBy><Where><Contains><FieldRef Name='SingleText'/><Value Type='Text'>8</Value></Contains></Where>
 

There was less than a tenth of a second difference in the average time to process the query.  So a performance hint:

Do - use the RowLimit property in the SPQuery class if you don't need to return all the results, and make the limit as small as you can while using <OrderBy> to make sure you get the most relevant results.

 
Queries I tested:
  <Where><Contains><FieldRef='SingleText'/><Value Type='Text'>8</Value></Contains></Where> (search a GUID for a single character of text)
  <OrderBy><FieldRef Name='Number'Ascending='FALSE'/></OrderBy><Where><Contains><FieldRef Name='SingleText'/><Value Type='Text'>8</Value></Contains></Where> (search a GUID for a single character of text and return a sorted list)
  <Where><And><Contains><FieldRef Name='SingleText'/><Value Type='Text'>8</Value></Contains><Contains><FieldRef Name='SingleText'/><Value Type='Text'>a</Value></Contains></And></Where> (search a GUID for a the character '8' and the character 'a')
  <Where><And><Lt><FieldRef Name='Number'/><Value Type='Text'>95000</Value></Lt><Gt><FieldRef Name='Number'/><Value Type='Text'>10000</Gt></Contains></And></Where> (search for all values in the Number field between 10,000 and 95,000)
 
Manipulating Data
As part of this performance data, I decided it was probably worth the time to check how well different methods of using the data were performing.  One of the things I see a lot is the results of a query being loaded in to a DataSet or DataTable to be combined with content from a backend system or SQL table.  I had found a new method available in the SPListItemCollection that I wanted to test.  GetDataTable() is the method and it does exactly what I thought it did.  It returns a data table populated with all the data in the SPListItemCollection.  Once you have a DataTable there are several time saving tricks to make SharePoint lists more attractive as a data source.  I found that, in general, it took more time to use the GetDataTable() method than it took to do a foreach loop and copy the data in to a new DataTable.  This seemed really odd to me, but I thought perhaps I was running in to the same performance issue I was seeing in the SPListItemCollection Count property.  It was able to process 1,400 items per second.  So my results that were around 85,000 items took about one minute to generate with GetDataTable(), and about 48 seconds with the foreach loop (putting the performance there at about 1800 items per second).
 
Wrap Up
I will summarize what I have found.
 
Action
Performance
Add New Items
50 items/sec
SPListItem.Update()
2.8 secs/item
Web Service UpdateListItems()
50 items/sec
SPQuery
2,000 items/sec
SPListItemCollection.GetDataTable()
1,400 items/sec
Manually Create DataTable

1,800 items/sec

 
Keep in mind that this is of course on my hardware, which is a 64-bit dual core machine with 4GB RAM.  Your mileage may vary. 
 
Another important discovery worthy of summarizing is the realization that you should limit calls to the SPListItemCollection.Count property. 
Windows Live ID Authentication in SharePoint
If you have been keeping track of Windows Live ID (formerly known as Passport) at all, you should have seen the announcements about the Windows Live ID Web Authentication SDK being released.  I was pretty happy to see this myself.  I was a member of their beta program because I was interested in how this authentication would work with SharePoint.  So I gave myself a weekend project.  I decided I would write a SharePoint Windows Live ID Membership Provider.  While I was in the middle of developing the Membership Provider, I found I needed to also create a Role Provider to ensure that I met all of my requirements.
 
I am in the process of creating a SharePoint solution to allow others to make use of the providers, however as I finish that up you might be wondering how it all works.  Well, the easy answer is to click on the Sign In link in the upper right hand corner of this page.  It will step you through the login process.
 
Click On The Sign In Link
The Membership and Role providers are linked in to the SharePoint Forms Based Authentication (FBA) architecture.  This means that the Sign in and Sign Out mechanisms are integrated right in to SharePoint automagically.
 
Get Redirected To Live's Login Site
When you click the Sign In link you will be redirected to the Windows Live ID login site.  This site validates the users credentials, either by requiring them to login with their Live ID credentials or by validating that they have an active Windows Live session.
 
Get Redirected Back to SharePoint
Once the Windows Live session is validated, the Live Login system will redirect you back to SharePoint.  The Windows Live system requires an Application Administrator to register a single URL per application ID to return users to once they have been validated.  The Windows Live request returns a signed authentication token that the SharePoint server verifies, and then uses to identify the user by their unique user token.  This token is unique to each Application that is registered to the Windows Live system.  What this means is if I register an application for blog.solanite.com and one for moreblogs.solanite.com I will get a different ID's for the same Windows Live ID on each system.  These unique user tokens only provide the SharePoint server with validation that Windows Live has authenticated this user.  It does not provide access to the Windows Live associated email, or really any information about the user.
 
User Token Is Processed
When the Windows Live system responds to SharePoint, the user token is checked against the profile store.  In the case of what I have written, I decided to test Microsoft's statement that with the performance tweaking abilities added to SharePoint, you can technically use a SharePoint list instead of using a SQL table for simple applications.  I just did a few web searches, and can't find any published versions of this statement, but it is something I and others I know have heard several times.  I have set the user token and email columns of the profile list as indexed to help with searching against them, and I am hopeful that I'll see good results as the number of profiles increases. 
If this user token is new, the user is prompted to enter an email identifier.  This is done to help with adding users to SharePoint sites.  Remembering a long user token is difficult to do, however typing in an email address is much easier.  The profile store itself is not accessible, unless you have specific access to the list so the data is protected.
 
Returned To The Sign In Page
Once either the new user has submitted an email address or the pre-existing user check is completed, you are redirected back in to the SharePoint page you initially signed in from. 
 
Roles
As I was working with the Membership provider I realized I was going to want to be able to add the equivalent of NT_AUTHORITY\Authenticated Users to a SharePoint site.  The answer was to create a Role provider that implemented one role, Authenticated Live Users.  This solved my primary issue, and allows someone to invite all Live users to a site.
 
Drawbacks
This is not a perfect system, however it does accomplish what it is should.  Some of the major issues come from the Windows Live ID system itself.  It does not share the email address of the Windows Live ID with the application, so in order to get a human readable identifier for the user they must be asked to provide an email (which is less than ideal). 
Another issue is how the Membership provider works.  When using WSS (not MOSS) users added from a Membership provider get a display name that is the same as their UserName in the provider.  There isn't a way to acquire a Display Name automatically, which leads to the next problem.  Users can't change their profile information in SharePoint unless they have contributor access to the site they are currently in (by default at least).   I understand the reason behind it, but it is still annoying in this particular case; however, the solution is pretty simple by adding the Edit Personal User Information  permission in to the Read permission level.  Since we have the Authenticated Live Users role then all Live users can be added with Read access.
 
Wrap Up
My intention is to finish up the solution sometime yet today or tomorrow, and let a few people I know test it out to make sure it installs smoothly.  Once it seems to be working well on their systems, I'll put it up for download assuming there is interest.  If you are interested and want to help out, feel free to give the Live integration a shake down on this site.  Please leave any comments and suggestions here.  I am happy to hear about the things you liked and the things you didn't. 
What is Missing in the SharePoint Blog Template (pt.2)
Here is Part 2 in our ongoing process to make some magic happen around the SharePoint Blog template.  If I had the announcer voice for it I would treat you all to a, "Last time on Click Mice, Unclick Mice...".  However, I think we will all have to settle for a quick recap in text. 
 
In the last post we went through the process of using SharePoint Designer to codelessly set up a web part that would give us Archive links grouped by month and year.  We had set ourselves a few requirements as goals:
  1. Navigation component that shows the Month and Year with the number of posts for that timeframe
  2. Let's make the navigation component expandable so it can show links to the posts if people choose to expand it
  3. Should appear on every page in the same position
  4. Should adhere to the theme that is on the page at the time (i.e. if I change themes it should update to reflect that)
  5. If I click on the Month Year heading it should display a filtered list of the posts for that month

We covered them all in the last post except the last one.  We did setup the Archive links web part to point to a view page, but we did not create the page...yet.  So now we have part two where we will complete requirement #5.

Series Links

What is Missing in the SharePoint Blog Template (pt. 1)

Filter View By Month and Year

This functionality doesn't exist on a normal date time field out of the box, but we have the power of XSL backing us so I don't think there will be a large problem.  Solving this particular problem seems like it will be much simpler than the Archive links web part, especially since we only have one requirement to fill (technically two since we want this to occur codelessly as well).

This post isn't as long as the last, but for those of you who are impatient you can find the finished product here.  You will need to create your own By Month.aspx view page in your Posts list, but then you can just drag this web part on to it.

Making It Happen

Since we are keeping the mantra of codelessly created in mind, we will go to my friend SharePoint Designer again, and we are going to follow the same precepts as the first post.  We will take a List View and modify it until it resembles what we want as closely as possible, then we will convert it to a Data View.

Section 1 - Configure And Convert A List View

Taking a step back, it should be fairly apparent that all we really need to do is take the Posts web part from the Home page (default.aspx) and add some custom filtering to it.  We will also need to create a List View Page to hold our new view.  We should be able to create our new view page with a few clicks inside SharePoint Designer.

  1. Right-click on the Posts web part and select Copy (Fig 1.1)
  2. Expand the Lists and Posts folders in the Folder List pane (Fig 1.2)
  3. Right-click on the Posts folder and select New -> List View Page
  4. Name it By Month and click Ok
  5. Right-click the Posts web part on the new view page and select Paste (Fig 1.3)
  6. Right-click the Posts web part and select Convert to XSLT Data View (Fig 1.4)

Figure 1.1
Figure 1.2
Figure 1.1
Figure 1.2

Figure 1.3
Figure 1.4
Figure 1.3
Figure 1.4

We actually want to keep the exact same view as the home page, we just want to add some filtering so we have gone as far as we are going with the List View web part.

Section 2 - Setup Data View Parameters

Now that we have our Data View page showing us the data that we would like, we need to prepare the Data View for filtering. 

  1. Once converted by default you should see the Common Tasks menu, click the Parameters... link (Fig 2.1)
  2. Click the New Parameter button
  3. Give the new parameter a Name of Month
  4. Make sure the Month parameter is highlighted, change the Parameter Source drop down to Query String and the Query String Variable to Month (Fig 2.2)
  5. Click the Ok button

Figure 2.1
Figure 2.1

Figure 2.2
Figure 2.2

Now we have all the raw data we need for our Data View.  Adding this parameter has given us the ability to pass a value in to the web part from the Query String, and in part one we have our Archive headings passing in the group heading as the Month query string parameter.

Section 3 - Change The DataSet

Since we have our raw data, it is time to shape it in to the finished product that we need.  We will need to go in to the Code View in order to accomplish what we want in our filtering. I can imagine what you might be thinking, there is a perfectly good filter option in the Common Data View Tasks menu.  My answer is yes, there is a Filter option in the Common tasks menu, however it does not allow us to accomplish our end goal.  The Filter option is better suited to matching exactly, or at least a portion of, the content in a column.  We need to match against a set of column data that requires some transformation (our date string needs to be transformed to month year format).

  1. Click in to the Code View and type Ctrl-F to start a Find
  2. Search for /dsQueryResponse/Rows/Row (Fig 3.1)
  3. Change /dsQueryResponse/Rows/Row to /dsQueryResponse/Rows/Row[ddwrt:FormatDateTime(string(@PublishedDate), number($Language), 'MMMM yyy')=$Month] (Fig 3.2)
  4. You will see a message that there are no posts, and below that you will see the original Posts web part that was on the page
  5. Right-click the web part and select Cut
  6. Press Ctrl-S to save the changes

Figure 3.1
Figure 3.1

Figure 3.2
Figure 3.2

Section 4 - Extra Credit

In the first post of the series, I handed out extra credit to make the Data View web part reusable outside of an STP file.  I will extend it here as well.  The instructions for making a Data View portable are here.

Wrap Up

So we started out in part one with 5 requirements, and we now have a system of two web parts that fulfill our requirements.  SharePoint does give the flexibility to build almost anything from its raw materials.  Looking forward to future posts in the series I am not sure what I will try to tackle next.  I may see what I can do for Most Active posts, or maybe how to codelessly add in links to social networking websites (like http://del.icio.us/ and http://digg.com/).

What is Missing in the SharePoint Blog Template (pt.1)
I had created the Wikin site template to overcome some of the perceived shortcomings of the out of the box SharePoint Wiki site template.  I made it in to an STP because there were a couple of people who wanted something easy to deploy on their SharePoint farms.  I have decided that I have a much more personal investment in looking at the out of the box Blog template.
 
Recently, I have been looking around and checking what the general feel for the SharePoint Wiki and Blog functionality is.  I have to say, in general the feedback wasn't positive, at least what I read.  As I read more and thought about it, the core issue was that SharePoint is really a development platform.  Granted, Microsoft will have some feature functionality that completes all your hopes and dreams, but what it doesn't give you on a platter you can create from the platform.
 
So we get to the point, since I am blogging I want what other blog software has.  I mean, who doesn't want what the other guy has, right?  I am willing to invest a little time to get what I want.  So this is part one in an open ended number of posts that will work to create new blog functionality from the building blocks of the platform without custom code.
 
Series Links
 
Proper Archive Links
The frst thing I notice that just seems missing is a proper set of Archive links.  I mean, don't get me wrong, there is a link out of the box, but where is the elegance in a link to the "All Items" view?  So the first problem is how to make this happen.  It shouldn't be too big of a deal, lets break it down to what I need:
  1. Navigation component that shows the Month and Year with the number of posts for that timeframe
  2. Let's make the navigation component expandable so it can show links to the posts if people choose to expand it
  3. Should appear on every page in the same position
  4. Should adhere to the theme that is on the page at the time (i.e. if I change themes it should update to reflect that)
  5. If I click on the Month Year heading it should display a filtered list of the posts for that month

This is a really long post, so for those of you that are impatient you can find the finished product here.

Making it Happen

Since we are doing this with the mantra of no custom code, that gives me two choices for how to make this happen: the List View web part and Data View web part.  List View is too restrictive so I am going to punt on that and we'll do this as a Data View.

Section 1 - Configure And Convert A List View

The first step is to load up the site in SharePoint Designer.  It happens to be my favorite editor for this kind of thing, and so that is what I'll be using.  Now once Designer is open, I am going to add a Posts list view web part.  I can feel you scratching your head, wondering if I remember that about a paragraph ago I was going to use a Data View.  I am, but the SharePoint Designer team has done a lot for me already in terms the #4 requirement on the theme.  If I convert the List View web part to a Data View, it will keep the CSS settings of the List View.  On the other hand, if I start from a fresh Data View I have to go through and setup the proper table and CSS structure to get everything to look consistent.  So the next step is to get the List View showing what I want.  In this case, it is the Title column grouped by the Posting Date.  To do this I will:

  1. Bring up the context menu and select the Fields option (Fig 1.1). 
  2. Setup the columns to the Title field I would like (Fig 1.2).  
  3. Bring up the context menu and select the Change Layout option (Fig 1.3). 
  4. Setup the List View options I would like (Fig 1.4)
  5. Bring up the context menu and select the Sort and Group option (Fig 1.5). 
  6. Setup the columns to the Sort order and Group by properties I would like (Fig 1.6)
  7. Right-click the List View and convert to a Data View (Fig 1.7).

That is as close as I am going to get with the List View. 

Figure 1.1
Figure 1.2
Figure 1.1
Figure 1.2

Figure 1.3
Figure 1.4
Figure 1.3

Figure 1.4

Figure 1.5
Figure 1.6
Figure 1.5
Figure 1.6

Figure 1.7
Figure 1.7

Section 2 - Modify The Data View Look And Feel

Now we have ourselves a Data View that is close, but still pretty far away from what we want.  We have our view set to group by posting date, but not by month and year yet.  So it is time to get our hands dirty with the Data View. 

  1. The easiest way to work is through the design surface so we start by right clicking next to the Title  column header and selecting Delete -> Delete Rows (which is not needed since we are going to be doing a lot of grouping and things really should already be sorted by published date) (Fig 2.1)
  2. We need to change the Group Heading for our published dates.  We can't have it saying Published: MM/DD/YYYY that just won't do.  Click on the first Published xsl:value-of element and just press delete 6 times (this is going to delete the Published column title text and the colon leaving us with just the date).
  3. We are close to where we want to be now; all that we are missing is a count of the grouped items.  Luckily, this one is easy.  We will just click on the date in the design surface, which will highlight a portion of the Code View.  Then we'll paste in (<xsl:value-of select="count($nodeset/@Title)" />) which will give us a count of the number of items passed in to the template (Fig 2.2)
  4. The last change that is required will give us the setup for requirement #5.  We will need to setup a custom view for this, but knowing ahead of time what we need allows us to plan for it.  Once again, we are going to click on the first date to get our place in the Code View.  Now we will paste in <a href="{concat(substring-before(@FileRef, '/Posts/'), '/Posts/By%20Month.aspx?Month=', $fieldvalue)}"> which will be our link to the custom view we will need to make.  Essentially, it is a view that will be in the Posts list and it will have a query string with the value of our Group Heading.  Now we close off the <a> tag at the end of the Group Heading (Fig 2.3).

 That completes our Look and Feel changes.

Figure 2.1
Figure 2.1
Figure 2.2
Figure 2.2
Figure 2.3
Figure 2.3

Section 3 - Change The DataSet

So now we have updated how the XSL template for each group heading is handled.  Next we need to get the data to actually be grouped properly.  By default our list view has automatically grouped our data by posting date.  So we just need to modify what it has done, to group by posting month and year instead.  There are three main areas we need to change. 

The first is the NewGroup_0 variable which we will track down by doing a Find for NewGroup_0.  The Code View should show the xsl:variable tag, and inside there is a xsl:when that contains a xsl:value-of tag.  This is our first taget.  This will start our group creation so we will need to change the select statment.  By default you will see it checking if the Published date has changed.  We'll alter the XSL to this ddwrt:NameChanged(ddwrt:FormatDateTime(string(@PublishedDate), 1033, 'MMMM yyy'), 0) which checks to see if a formatted date string of the published date has changed (Fig 3.1)

The next target is the groupheader0 xsl:variable which is located just a few lines below the NewGroup_0 variable.  This one has a xsl:otherwise with a xsl:value-of tag.  This will set our group header text so this select statement will be changes to ddwrt:FormatDateTime(string(@PublishedDate), 1033, 'MMMM yyy') which will set it to the formatted string we used when we changed the previous select statement (Fig 3.2)

Our last target is in the xsl:call-template for dvt_1.groupheader0 which is just below the groupheader0 variable.  There is a xsl:with-param with a name of nodeset that we need to change the select statement for.  We will change it to msxsl:node-set($dvt_Rows)/root//Row[((ddwrt:FormatDateTime(string(@PublishedDate), 1033, 'MMMM yyyy'))=$groupheader0 or ((not(ddwrt:FormatDateTime(string(@PublishedDate), 1033, 'MMMM yyyy')) or ddwrt:FormatDateTime(string(@PublishedDate), 1033, 'MMMM yyyy')='') and $groupheader0=' '))] which will select the data based on the formatted date string of its published date matching our group header (which is in Month Year format) (Fig 3.3).

Figure 3.1
Figure 3.1
Figure 3.2
Figure 3.2
Figure 3.3
Figure 3.3

Section 4 - Putting It Into Play

Well we actually have our Data View all setup now.  We can save it and it will have everything we were hoping for except #3, but getting it in to the navigation is the easy part.  Right?  One would think so; however, the Blog template has some special characteristics around how its pages work.  There is a new web part zone added to a few pages called the BlogNavigator so you have to actually export the web part and add it to these pages in their BlogNavigator web part zone:

  • default.aspx
  • Lists/Posts/Post.aspx
  • Lists/Categories/Category.aspx

After you have done this, you will need to open up the master page and add it there as well for all of the pages that are not listed above (assuming you want to).

Section 5 - Extra Credit

With any Data View web part there comes a problem: how can I make it reusable?  I am only aware of two ways.  Either make the Data View a part of an STP file, and it should automatically find its new list when the new site is provisioned, or make changes to bind to a list name instead of a GUID.  This will also find the new list in a provisioned site and has the benefit of being able to be exported and used on anyone's system without them having to edit it.  The instructions for how to make those changes are here.

Wrap Up

So while SharePoint might not have the capabilities of some Blog software out of the box, you can see that it is possible to use the raw materials to fashion what you need, and in a lot of cases I think you will find that you can do it without having to write and deploy code.  There are many ways to do things codelessly inside SharePoint.  We will take care of requirement #5 in What is Missing in the SharePoint Blog Template (pt. 2).

Debugging obtuse SharePoint errors
  • An unknown error has occurred.
  • An unexpected error has occurred.
If you have ever done any custom web parts or other custom code in a SharePoint installation you have probably seen this message.  Lately I have also found this set of steps useful for tracking down other odd things like the GroupBoard Workspace issue.  There are lots of reasons this can happen, but I think in all the debugging I have done the most prevalent reason for it has been someone closing an SPSite or an SPWeb that they pulled from the SPContext (GetContextWeb/Site in v2).  So if you see one of these messages, or any terribly vague SharePoint message, I would ask yourself if you are closing an SPSite or SPWeb and if so double check if you should.  If you have done that, well read on and hopefully this helps.
 
Changing the web.config
Well if you have double checked your work and are still seeing the error then there is only one choice.  Part the fog of SharePoint's error and take a look at the stack trace for the error.  There are two things you will need to do to make this happen. 
  1. Turn off Custom Errors: This tag basically controls what error pages your web site will show when an error occurs.  In most cases you would not want to show a full stack trace to the world when an error occurs so I would opt for setting this to On for most of the time and RemoteOnly when you want to debug something.  If it is a dev only system though knock yourself out and set it to Off.  You can set this tag in the <system.web> tag though it should already exist for you to modify.  More info on the tag here.
    • On: This will always show a simple error has occured page and will not show details.
    • RemoteOnly: This will show a simple error has occured page to remote clients and a full stack trace to a browser from the local server.
    • Off: This shows a full stack trace to everyone when an error occurs.
    • Ex: <customErrors mode="RemoteOnly" />
  2. Set the SafeMode CallStack: This is my favorite setting for SharePoint, but it comes with a price.  You can set the CallStack to true, and it will do wonders for tracking down many errors that otherwise result in the "An unknown error" type messages, however it has a dark side.  It can also hide other errors.  I have used this method to trace issues in code in SharePoint v2 and found that a fully reproducable case was "fixed" by setting the CallStack to true.  Hardly what I was looking for when I did it, but worth noting that this is not a silver bullet for all unknown error messages.  Setting this attribute can mask some poor coding standards by making SharePoint slightly more lenient in its memory cleanup and allocation.  You can find the <SafeMode> tag in the <SharePoint> tag in the web.config.  CallStack in an attribute inside that tag.  By default it is set to false.  To enable printing of the stack trace just set it to true.
    • Ex: <SafeMode MaxControls="50" CallStack="true"/>
Puget Sound SharePoint Users Group
Well the User Group has had its first two meetings and shown a little of what the future holds.  There are two formats to the meetings at the moment. 
 
The first type generally will have speakers on various SharePoint related topics.  The speakers so far have all been great.  Granted we've only had a few, but the upcoming speakers that have been listed seem good as well so I don't think that will be changing. 
 
The second type of meeting is a little more hands on.  Basically asking the members to bring their laptops and go through some training sessions on different areas of SharePoint.  I think it could be a pretty good way to come up to speed on some of the features in WSS/MOSS that people might not use all the time.
 
On top of that you really can't beat the prize giveaways and free pizza.  All in all it is a decent way to spend a couple of Thursday nights a month, if you use or want to learn about SharePoint that is, otherwise your eyes will probably glaze over about 5 minutes in.
1 - 10 Next

 ‭(Hidden)‬ Admin Links