Attention: We are retiring the ASP.NET Community Blogs. Learn more >

Fear and Loathing

Gonzo blogging from the Annie Leibovitz of the software development world.

  • Creating multiple lists from templates

    In the current project we're doing, we create multiple document libraries in a site when a user selects a value from a list. The choices for the list are business templates for new Contracts. A Contract can be implemented through several document libraries. Each document library makes up part of a full document that is assembled together later when the user wants to preview the whole thing. Okay, so we're golden. When we first did this we thought this would work. Iterate through all the custom list templates and build a new document library based on it. This code does that, but doesn't work:

    public void DoesNotWork()

    {

           SPSite siteCollection = new SPSite("http://localhost/sites/sitename");

           SPWeb web = siteCollection.OpenWeb();

           SPListTemplateCollection customListTemplates = siteCollection.GetCustomListTemplates(web);

     

           SPSite testSite = new SPSite("http://localhost/sites/sitename/subsite");

           SPWeb testWeb = testSite.OpenWeb();

     

           foreach (SPListTemplate spListTemplate in customListTemplates)

           {

                  string newListTitle = spListTemplate.Name;

     

                  // this fails with internal error when adding multiple lists

                  testWeb.Lists.Add(newListTitle, "", spListTemplate);

     

                  // this doesn't fix it

                  testWeb.Update();

           }

    }

     

    The problem was that we would open the target web (testWeb in the above code) and create all the document libraries we needed to based on the templates from the parent site. These were adding new document libraries based on a template, everything was valid but it would throw an exception on the second call to testWeb.Lists.Add. Even with the testWeb.Update call it still didn't help. So we had to collapse all our documents into a single one until we figured out why.

     

    After doing a couple of spike projects we found that we had to open the web each time for the multicreate to work. So this code fixed it:

     

    public void Works()

    {

           SPSite siteCollection = new SPSite("http://localhost/sites/sitename");

           SPWeb web = siteCollection.OpenWeb();

           SPListTemplateCollection customListTemplates = siteCollection.GetCustomListTemplates(web);

     

           foreach (SPListTemplate spListTemplate in customListTemplates)

           {

                  SPSite testSite = new SPSite("http://localhost/sites/sitename/subsite");

                  SPWeb testWeb = testSite.OpenWeb();

                  string newListTitle = spListTemplate.Name;

                  testWeb.Lists.Add(newListTitle, "", spListTemplate);

           }

    }

     

    Yes, you should call Dispose on the SPSite and SPWeb objects. In any case, this code works but I'm still not sure why?

  • ITPro Calgary and TechNet Winter Tour

    No SharePoint blog today but just to let you know I'll be at the TechNet Winter Tour in Calgary on March 15 (and if all works out I'll shuffle up to Edmonton for the 17th event too as MVPs are scarce up there). I'll be there wearing two hats of sorts. One as the president of the Calgary ITPro User Group and the other manning the Ask The Expert booth mainly for SharePoint technologies but I'll field questions around the technologies used on the presentation which covers Virtual Server 2005 (Great for SharePoint development setups kids), Windows Server 2003 (the core ingredient to SharePoint, see how I tie all this in?), Network Security (SharePoint has security, hey there's a pattern forming here), and Exchange Server (SharePoint, mail, alerts, you see the connection...)

    As for the Canadian ITPro User Groups, we kicked it off late last year in Calgary (along with most of the other Canadian ones) and did start the ball rolling. Now we're getting serious with a few hundred people coming out to the Mariott Hotel event. In this event, Bruce Cowper will be looking under the hood of Windows Server System. Lots of great stuff planned for this group and activities in Calgary this year.

    The venues are all filled up now but there will be more to come so stay tuned. Bruce Cowper will be presenting from Microsoft at both events and I'll be doing various stuff like serving soft drinks and running to Future Shop to buy XBox prizes or something. Watch for more Calgary ITPro User Group stuff coming shortly. Hope to see you there!

  • New Discussion Board Template

    The WSS Discussion board template is probably one of worst implementations of a threaded forum I've ever seen. Compare it to the typical Forums we see like phpBB, ASP.NET Forums, Snitz, etc. and you'll probably agree. However, finally, someone has stepped up and put together a nice alternative.

    Thanks to Serge van den Oever who brought us the Macaw SPS Joust Menu for SharePoint 2001, he's now put together the Macaw Discussion Board, a WSS template that provides last-post first views, a discussion thread view, and SWYRO (See What You Respond On).

    Great stuff! You can find the templates here for download and documentation here on his blog. Enjoy.

  • MSDN Article: Customizing List Item Forms

    A new article on of the MSDN Office Development Centre today that goes in depth around customizing forms used with List Items. Well done and informative so check it out here.

  • Looking for SharePoint Database Explorer?

    While I may be slow on the draw some people might be looking for James Edelen's excellent SharePoint Database Explorer tool. His old blog on bloggedup.com vanished (much like my portal did this morning) so he's moved sites to new blog site here. SharePoint Database Explorer is a great tool for troubleshooting SharePoint, especially if your portal is non-functioning. With just the database files you can usually restore what you need. You can get both the binaries and source to his database tool as well as SPExport here.

  • 1 is the lonliest number

    SharePoint. Some days you love it. Some days you hate it. Today is my hate day.

    Site definitions are a pretty cool thing in that you can create a whole bunch of stuff for a user without lifting a finger. In ONET.XML you can create new lists in the <Configuration> tag. So go ahead and create a bunch of document libraries like this:

    <Configurations>
    <Configuration ID="0" Name="Default
    ">
    <Lists>
    <List Title="Document Library 1" Url="Document Library 1" Type="101"/>
    <List Title="Document Library 2" Url="Document Library 2" Type="101"/>
    <List Title="Document Library 3" Url="Document Library 3" Type="101
    "/>
    <List Title="Document Library 4" Url="Document Library 4" Type="101
    "/>
    <List Title="Document Library 5" Url="Document Library 5" Type="101
    "/>
    </Lists
    >
    </Configuration>
    </Configurations>

    Great. You now have 5 brand new unique Document Libraries, all with their own paths that you can add documents into. With ONET.XML you can also add these Document Libraries to a page automatically so we do this:

    <Modules>
    <Module Name="Default" Url="" Path
    ="">
    <File Url="default.aspx" NavBarHome="True
    ">
    <View List="101" BaseViewID="0" WebPartZoneID="Footer"/>
    </File>
    </Module
    >
    </Modules>

    Awesome. Your Document Library is now smack in the Web Part Zone named Footer on your home page. Hmmm. Which document library? You only got to specify List="101". But now you're saying that you have not 1 but 5 of these things. Therein lies the rub. Just how do you get the second one on the page? And the third? Me little brain thought I would be clever and try this:

    <Modules>
    <Module Name="Default" Url="" Path
    ="">
    <File Url="default.aspx" NavBarHome="True
    ">
    <View List="101" BaseViewID="0" WebPartZoneID="Footer" Url="Document Library 1" />
    <View List="101" BaseViewID="0" WebPartZoneID="Footer" Url="Document Library 2" />
    <View List="101" BaseViewID="0" WebPartZoneID="Footer
    " Url="Document Library 3" />
    <View List="101" BaseViewID="0" WebPartZoneID="Footer
    " Url="Document Library 4" />
    <View List="101" BaseViewID="0" WebPartZoneID="Footer
    " Url="Document Library 5" />
    </
    File>
    </Module
    >
    </Modules>

    After all, each Document Library did get created and had a unique Url. Nope. This just created 5 copies of Document Library 1 on the page.

    Update: Thanks to Frans for the suggestion to change the Type in the ListTemplates section. It seems to be the only way to create multiple unique document libraries. At least you don't have to create a whole different folder under the LISTS directory and you can reuse the DOCLIB one. So basically in your ListTemplates section of ONET.XML do this:

    <ListTemplate Name="doclib" DisplayName="Document Library 1" Type="500" BaseType="1" OnQuickLaunch="TRUE" SecurityBits="11" Image="/_layouts/images/itdl.gif" DocumentTemplate="101"/>
    <ListTemplate Name="doclib" DisplayName="Document Library 2" Type="501" BaseType="1" OnQuickLaunch="TRUE" SecurityBits="11" Image="/_layouts/images/itdl.gif" DocumentTemplate="101
    "/>
    <ListTemplate Name="doclib" DisplayName="Document Library 3" Type="502" BaseType="1" OnQuickLaunch="TRUE" SecurityBits="11" Image="/_layouts/images/itdl.gif" DocumentTemplate="101
    "/>
    <ListTemplate Name="doclib" DisplayName="Document Library 4" Type="503" BaseType="1" OnQuickLaunch="TRUE" SecurityBits="11" Image="/_layouts/images/itdl.gif" DocumentTemplate="101
    "/>
    <ListTemplate Name="doclib" DisplayName="Document Library 5" Type="504" BaseType="1" OnQuickLaunch="TRUE" SecurityBits="11" Image="/_layouts/images/itdl.gif" DocumentTemplate="101"/>

    It's the Type that has to be unique, not the Name. This will allow you to put multiple document libraries onto a single page when a new site is created from your template.

  • NAnt, NUnit, and NDoc... the hard way

    I live and die these days by the holy trinity. NAnt, NUnit, and NDoc. They're lifesavers when it comes to automating builds, doing testing, and quickly generating API docs for your code (which helps if you're passing it off to another group for support).

    However I'm screwed right now with these tools and stuck to using the command line (which is ok because I grew up out of that world). For NUnit, I was using the NUnit add-in which was great but then it "evolved" into TestDriven.NET. Great. A nice MSI package that you can install and now you can right click on test and run it. The output window shows you any tests that failed and a double-click on it will take you to the offending test. Only problem is that I run it and get yelled at that my version is old. Checking the Download page the last few weeks I've been getting nothing but an error page with an ICAP Error (whatever that is). The download page just doesn't seem to come up for me no matter where I'm trying it from.

    The other bee in my bonet is NAntRunner. NAnt is a great tool and our build script kicks (as much as a build script can). However I always have a console window open to run NAnt so work in the VS.NET IDE, switch to command window, run NAnt, lather, rinse, repeat. Okay, I could add NAnt to my Tools menu but it just opens a window and runs so it's hard to see when something goes wrong. Plus I would have to do something funky so I could specify different targets, etc. NAntRunner is a nice looking add-in that is supposed to recognize your .build file and will let you (graphically) pick a target and run it. The results will also be output to the Visual Studio IDE so I can just happily work in my environment. However I have yet to successfuly get NAntRunner working. The SourceForge project didn't a source release but the code is in CVS so I could see what's wrong and maybe fix it, but I don't want to do that. That's like renting a car and filling it with gas but it still doesn't do anything. It's not the end-consumers problem that the tool doesn't work (and others say they can't get it to work either). Obviously it somehow got working for the guy that wrote it as I can see screenshots of it in action. However, looking at the code reveals some really ugly assumptions like where cmd.exe is installed and where Visual Studio lives. I just want to use something that works!

    It would be nice if I could just press a keystroke combination, see the output of my tests, run and deploy my build with NAnt all within the Visual Studio IDE. Is that too much to ask for? Sigh.

  • Automagically updating themes

    Themes are great in that you apply them and an entire WSS site is transformed before your very eyes. However there are some drawbacks to themes, namely they live on the SharePoint server (which you may or may not have access to in a production environment) and if you change a theme, it doesn't re-apply itself to a site immediately. Actually I've always found that you have to apply a different them, then re-apply your own theme. There are other ways to apply a theme like using the Object Model but that requires code, etc. and is pretty messy just to change the look of your site.

    Here's a trick that you may want to employ for your sites. Create a new theme using the normal editing process as outlined in the SDK. However when it comes to editing your THEMES.CSS file, simply enter this for it:

    @import url("http://servername/filename.css")

    The filename above refers to a CSS file that you control. Place it on a central web server or something and update it anytime you want. Voila. All WSS sites using this theme will be instantly updated (well updated the next time a user visits the page) using the changes you apply to your CSS file.

    A couple of additional tricks you can do here. Rather than put your CSS file on a web server (where it might not be accessible due to production servers being locked down) put it into a SharePoint document library. Not only will you get security around it but you get version control for free. Another thing is you can combine the THEMES.CSS file with the stock SPS.CSS file to create a mega-CSS file that both your portal AND your WSS sites can use and create a consistent look between the two.

    Enjoy.

  • SharePoint Wrappers 0.10

    Back in the middle of last year, I had created a set of .NET classes that let me access SharePoint through Web Services. I generally was frustrated with having to always create the web service, assign credentials, etc. and then if I wanted to connect to another server/site, I had to create a different reference or re-use one and set a bunch of properties. The big problem was that almost anything anyone wanted to do had to happen on the server and frankly, I despise running client tools (even if they're utilities) on a server. Servers are meant to run well, servers. Workstations are meant to run clients. How often do you see someone running Office 2003 from a server? The other problem I saw was that SharePoint Web Services, while great and all, didn't always give me everything I needed from one service. Sometimes I had to call on 2 or 3 different services at the same time to accompish a single task.

    Anyways, this led me to create a set of wrapper classes. Classes that let me deal with SharePoint servers, libraries, lists and whatnot like real objects and simple methods. I've created a new project on SourceForge to house these and uploaded the initial revisions into CVS (here's the link to CVS through your browser). You can grab them from the site here through anonymous CVS access (instructions here). I haven't done an "official" release yet but you can compile it and create the API doucmentation with the built-in NAnt script. There are plans to do both a Gui and Web based set of sample programs (like a non-runat-server version of SharePoint Explorer). I chose SourceForge over something like GotDotNet just because it has a few more services that I like and I run a bunch of similar projects off it. If GotDotNet adopts the SharePoint platform as some discussions have been going on and there's a source control plug-in, then maybe things will move.

    These do not mimic the SharePoint object model 100% as it was a lightweight solution to providing access to remote clients. It can grow, however I probably wouldn't want to see it completely do everything the OM does. The intent however is to provide enough capabitilies so you don't need to run applications on a SharePoint server (so you can build desktop and non-SharePoint web based tools for SharePoint). What can you do with these things now? Plenty and it's pretty simple. You can easily:

    • Connect to a SharePoint Server, get its properties, and enumerate sites
    • Connect to a SharePoint Site, get its properties, and enumerate lists
    • Create lists, doclibs, and list items
    • Upload documents to a document library
    • Get all versions of a document in a library

    How do you use them? It's fairly simple:

    SharePointSite site = new SharePointSite(url);
    site.LoadWebCollection();
    foreach(SharePointSite subsite in site.webCollection)
    Console.WriteLine(subsite.Name);

    If you do have suggestions or would like to signup for the project, drop me an email. More updates on this in the coming week with some samples, additions to functionality, etc. and whatever suggestions you guys have. Here's to expanding the project to be a useful tool in your SharePoint arsenal!

  • Who the heck is SPSTaskUser?

    And more importantly why is he filling up my Application logs with these:

    Windows cannot unload your classes registry file - it is still in use by other applications or services. The file will be unloaded when it is no longer in use.

    Perplexed? I was. Until I spent a couple of days with a very fabulous Microsoft support person (Thanks Tracy!) going through a few errors we were having on a couple of our portals.

    SharePoint (both SharePoint Portal Server [SPS] and Windows SharePoint Services [WSS]) have a few combinations of installs:

    1. SharePoint Portal Server using the built-in database engine (plain old MSDE)
    2. SharePoint Portal Server using SQL Server
    3. Windows SharePoint Services using the built-in database engine (new and cool WSMSDE)
    4. Windows SharePoint Services using SQL Server

    Each has it's merits and downfalls (MSDE is limited to 2GB whereas WSMSDE doesn't have that limitation). Normally in a development setup you might decide to just install option #1, SPS with the built-in database engine. After all it's easy right? You don't have to do worry about a separate install of SQL and it's service packs and all that mumbo-jumbo. With that combo though comes, your friend and mine, SPSTaskUser. I was really perplexed as to who this user was? I certainly didn't create him. After a little Googling, others mentioned him (but usually it was "Who is this SPSTaskUser?"). However, just like why the infant universe did not simply spread out evenly after the Big Bang 14 billion years ago, the answer is here.

    Installing SPS in this configuration creates a new local machine account called SPSTaskUser. The default website Application Pool (and all portals created after that on this server) will use a predfined Network Service account to do all the SPS crawling. When you set this up and your crawler kicks off (in my case it was every 10 minutes) you'll get a pair of messages in your Application Event Log like this:

    2/18/2005 4:00:05 PM Userenv 1516 NT AUTHORITY\SYSTEM SERVERNAME Windows unloaded user S-1-5-21-2435244326-407298798-4041372769-1009_Classes registry when it received a notification that no other applications or services were using the profile.

    2/18/2005 4:00:01 PM Userenv 1524 SERVERNAME\SPSTaskUser SERVERNAME "Windows cannot unload your classes registry file - it is still in use by other applications or services. The file will be unloaded when it is no longer in use.

    It's rather annoying and was causing me some grief as I personally don't like anything but Information in my Application logs (and even then I get a little torqued about the amount of those Windows creates). Anyways, we spent some time and I was convinced that we hadn't created that user. SharePoint also creates a new scheduled task in the Windows Task Scheduler for each crawl of some content (luckily WSS only installs don't have this). That task will run as, you guessed it, SPSTaskUser. And we all know that when Scheduled Tasks run as a local account rather than a domain one it causes problems don't we kids?

    We didn't find there were performance issues with the server, but it was dang annoying. There's two ways to fix this blip. The easiest answer is to just use SQL instead of MSDE. With SQL as the backend, two things happen. The Application Pools for the websites use a named user (a domain account you create with a non-expiring password) rather than the built-in NETWORK SYSTEM account. Second, the Default Content Access account (or Application Pool account, I can't remember which) that you specify to crawl content will be the account that runs the Scheduled Tasks. Problem gone, Application logs clean. Move along. A second option (although I haven't tried this) is to keep on using MSDE (although why would you?) and manually replace the Scheduled Task account with some domain account that you have for this type of stuff. That should fix it but your mileage may vary so caveat emptor.

    One note, I don't know if WSMSDE creates this guy or not so maybe someone can confirm that and post a comment on it. I suspect it might not and the SPSTaskUser only gets created with SharePoint Portal Server and MSDE because it's the one doing the crawling of content (WSS searches content using SQLs Full Text Search engine).

    Anyways, hope that helps someone out there and have a great weekend!