Archives
-
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:
- SharePoint Portal Server using the built-in database engine (plain old MSDE)
- SharePoint Portal Server using SQL Server
- Windows SharePoint Services using the built-in database engine (new and cool WSMSDE)
- 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!
-
SharePoint Pet Peeve #327
Dear Microsoft,
Please enable alerts to fire when an item is submitted to a list that requires approval.
Man, I don't know how many times I keep forgetting to go to a raft of lists that I have to approve content to see if anything new has been added. I know that the EventHandler interface for lists was omitted due to timing, but having to manually check lists where approval is required is rather annoying.
Nintex has a pretty good add-on for Document Libraries but it doesn't work for Lists. There are some Web Parts floating around that will show you what needs you approval which is good too. However I was hoping to minimize some of the extra add-ons that I feel should be part of the base system.
Okay, enough ranting for this morning.
-
Best Practices for Writing HTML in Web Parts?
Something that's as puzzling as the Cadbury secret (and we all know that us programmers figured that out long ago) is just what's the best way to write out HTML in Web Parts? So call me masochistic but I write Web Parts with code. Yes, it's ugly. Yes, it's painful. Yes, you could use something cool like SmartPart or load the controls yourself (but that brings on a host of other issues like Code Access Security so we won't go into that).
For those of us that hold true to the "old fashioned" way, what's the best way to write all that code out? Consider these two approaches that writes out a label and control in a row for a form:
private void RenderRow(HtmlTextWriter output, Label label, WebControl control)
{
output.Write("<tr><td class=\"ms-formlabel\" valign=\"top\" nowrap=\"true\">");
label.RenderControl(output);
output.Write("</td><td class=\"ms-formbody\">");
control.RenderControl(output);
output.Write("</td></tr>");
}
private void RenderRow(HtmlTextWriter output, Label label, WebControl control)
{
output.RenderBeginTag("tr");
output.RenderBeginTag("td");
output.AddAttribute("class", "ms-formlabel");
output.AddAttribute("valign", "top");
output.AddAttribute("nowrap", "true");
label.RenderControl(output);
output.RenderEndTag();
output.RenderBeginTag("td");
output.AddAttribute("class", "ms-formbody");
control.RenderControl(output);
output.RenderEndTag();
output.RenderEndTag();
}
Both output exactly the same HTML. Does it matter? The first approach is less lines but is it any more (or less) readable? Or maybe everything should be built up in a StringBuilder class and slammed out to the HtmlTextWriter? Or is it simply whatever is readable or maintainable works? Looking for your thoughts, ideas, suggestions, rants, assorted concealed lizards of any kind. -
Opening Links in new Windows
This question gets asked a lot in the newsgroups. The default behaviour of the links list (which is just a template afterall) is to open the link in the same window. I took a look at Jim Duncan's cBlog site definition recently that had a nice modification that I think should be on everyone's SharePoint box. He added a simple checkbox to set a link to open in a new window.
First you'll need a new site definition so copy STS to another one for this purpose. Then copy the Links list definition to a new one (it's in a folder called FAVORITES). Alternately, you can just work directly on the definition itself but see the note at the end of this blog about that. In the LINKS definition they'll be a SCHEMA.XML file which defines all the fields and how the list behaves.
First let's add the new checkbox field that will hold our new option:
<Field Type="Boolean" DisplayName="Open in New Window" Name="NewWindow"/>
Great. Now it'll show up on the New and Edit forms and be saved with the list. How do you get it to show up in the rendered HTML? This is done in the DisplayPattern tag for the URL field. The default Links list part that we're interested looks like this:
<Default>
<HTML><![CDATA[<A onfocus="OnLink(this)" HREF="]]></HTML>
<Column Name="URL" HTMLEncode="TRUE"/>
<HTML><![CDATA[">]]></HTML>
<Switch>
<Expr><Column2 Name="URL"/></Expr>
<Case Value=""><Column Name="URL" HTMLEncode="TRUE"/></Case>
<Default><Column2 Name="URL" HTMLEncode="TRUE"/></Default>
</Switch>
<HTML><![CDATA[</A>]]></HTML>
</Default>So we want to do a check on our NewWindow field and if it's set, add in a "target=_blank" piece of HTML to our HREF tag. So the new DisplayPattern tag should look something like this (changed highlighted in Red):
<Default>
<HTML><![CDATA[<A onfocus="OnLink(this)" HREF="]]></HTML>
<Column Name="URL" HTMLEncode="TRUE"/>
<HTML><![CDATA[">]]></HTML>
<Switch>
<Expr><Column2 Name="URL"/></Expr>
<Case Value=""><Column Name="URL" HTMLEncode="TRUE"/></Case>
<Default><Column2 Name="URL" HTMLEncode="TRUE"/></Default>
</Switch>
<Switch>
<Expr>
<Field Name="NewWindow"/>
</Expr>
<Case Value="Yes"><HTML><![CDATA[ target="_blank"]]></HTML></Case>
</Switch>
<HTML><![CDATA[</A>]]></HTML>
</Default>That's it. A simple change to one file and all your links have the capability to open in new windows across all your sites. You can preset this value if you want (using the <ROWS> tag in your sitedef) and hide the field or let your users choose. Of course, modifying your base SharePoint install isn't recommended as the next service pack or update may wipe out those changes however for this change I'm willing to do the file management myself to avoid this. Your mileage may vary.
The other trap is that all the sites that are out there, already created using the STS template might break. Adding a new field to a definition usually is ok but it's something you need to test big time if you have a lot of sites out there already created (NUnitAsp is great for this). It would be nice if it was part of the base system though wouldn't it?
-
MSOS DevCon, Word XML, and the Mvp.Xml Project
I was invited, but unfortunately due to scheduling conflicts I was unable to attend this years Microsoft Office System Developer Conference in Redmond this past week. My loss as Bill Gates gave the keynote about the importance of XML, connectivity, ease of development, lower CTO, better performance, extreme developer tools (VS2005 is just plain freakin' amazing, my words not his) and the importance of reuse and leveraging both 3rd party and MS developers to build interopabile solutions. The great thing (from my perspective) is that SharePoint is being positions at the core of most of the products which means lots more development and collaboration to come.
Mike Fitzmaurice gave a presentation about how SharePoint can be used to access backend data services (SAP, Siebel, PeopleSoft, etc.) including some discussion around BizTalk, SSO (Single Sign On), etc.
I think one of the key things is to look at storing Word, Excel, etc. files in XML when saving stuff in SharePoint rather than the traditional DOC, XLS, etc. formats. I noticed the recently released (2/4/2005) Word XML Software Developers Kit which follows the Office XML Reference Schemas that were published awhile ago. Displaying Word XML docs in a browser (via SharePoint and an XSL file) is so much better on performance than launching embedded Word and you've got so much more power like loading it up into an XmlDocument and modifying it rather than the traditional Word COM fiasco.
On the Xml front, you might also want to check out the Mvp.Xml project here which is aimed at supplementing .NET framework functionality available through the System.Xml namespace. Helps a lot when you're working with Xml Web Services coming out of SharePoint.
-
Stupid SharePoint Fact #327
Ever wonder why all those FrontPage (and subsequently now SPS and WSS) directories were called /vti? VTI was the acronym for a company called Vermeer Technologies Incorporated. Microsoft aquired the company in 1996 which had a flagship product. This product eventually became FrontPage. Hence all the vti directories all over your servers. You can check out the original press release here on it.
-
Reading vs. Updating objects in the SharePoint Object Model
A message came up on the newsgroup about someone having a problem with this code:
System.Guid listId=web.Lists.Add(title,description,web.ListTemplates["Document Library"],web.DocTemplates[0]);
web.Lists[listId].OnQuickLaunch=true;
web.Lists[listId].Update();They were creating a list (a document library in this case) and wanted to display it on the Quick Launch. The code looked okay but the list wasn't being displayed on the Quick Launch. After a little experimenting (going down a couple of turkey trails like web part vs. console app) I found this slight change worked:
System.Guid listId=web.Lists.Add(title,description,web.ListTemplates["Document Library"],web.DocTemplates[0]);
SPList list = web.Lists[listId];
list.OnQuickLaunch=true;
list.Update();I do remember passing by a document somewhere that said when accessing classes in the SharePoint namespace for updating to use the actual objects rather than an index of a collection. For whatever reason, I just naturally always get the list object myself so never came across this before. For reading purposes (like listing all the lists on a site) it's fine, but updates need to be implemented this way. I can't find that link right now but I did find a blog by Kris Syverstad from July of 2004 here on it to which Peter Provost thought maybe the WSS team implemented a property indexer when they probably should have implemented a method.
Looking through the newsgroups, this is probably one of the biggest "why doesn't my code work" message posted when it comes to updating properties. This tip will be true for any object in a collection (SPSite, SPList, SPField, etc.). Good stuff to know.
-
AlternateHeaders and WSS sites...
...doesn't work. Yup, plain and simple. The ONET.XML file describes site definitions and can contain an attributed called "AlternateHeader" which you specify as your own aspx page living in the LAYOUTS\1033 directory. By default, all portal areas (everything under the SPSxxx folders) have this set to "PortalHeader.aspx" which just brings in the various CSS files and registers a couple of tag prefixes for accessing SharePoint Web Controls. However, none of the WSS site definitions have this AlternateHeader defined. You might think you're smart (several people have thought this) and set the AlternateHeader attribute to PortalHeader.aspx and magically all your WSS sites will start looking like they actually belong to the portal (i.e. having the same Portal navigation, etc.). Nope. All it does is create chaos and anarchy because SharePoint controls don't necessarily work in WSS sites. And it's only Tuesday...