Fear and Loathing
Gonzo blogging from the Annie Leibovitz of the software development world.
-
Local SharePoint on Server 2008 Development
Sometimes I'm such an idiot I wonder how I get enough neurons firing in the morning to get out of bed. I've built a million virtual setups for SharePoint development (and a million really isn't an exaggeration, at least that's how I feel). They're all the same. Install this, configure that, lather, rinse, repeat.
Setting up a new VM today and for the life of me I couldn't get logged into the site no matter what account I used. You know that challenge/respose dialog you get and you just keep entering your domain\userid hoping to get in? When I setup a new VM I generally create a bunch of host names (trying to remember port numbers is for the birds) so either using the HOSTS file or using A records in DNS (if you're running it on a domain controller, which is what I was doing). These are simple names to remember like "sharepoint", "portal", "sandbox" and more specialized ones (for example I have a new one called "techdays2009" which is a web app just for doing my TechDays demos).
Like I said, I've done this untold numbers of times before and it's routine. However something always seems to trip me up. Trying to access any of the new sites didn't work. At. All. Of course there are no error messages, event logs are empty, and SharePoint basically says everything is normal. Thanks. That's a lot of help. Luckily I tried to create a new site using the server name and port number (think it was a DNS problem) and I could access the site. So I tried pinging the machine name vs. the host names and would get results (although the host name returned IPv6 results even though I have it disabled while host header names would return IPv4 results).
All of this was a wild goose chase.
Here's the problem. If you do this on Server 2003 it works fine. On Server 2008 Microsoft introduced a nice new "feature" to protect local loopback attacks. However this "feature" also creates a problem and doesn't let you use host headers in IIS web sites on local machines. There's a detailed description of the issue here but I only found that by searching for "sharepoint" and "disableloopbackcheck".
The problem also isn't very intuitive to find because the KB article is entitled "401.1 Error .. on IIS 5.1 or 6". In my case, I'm running on IIS 7 so this KB would probably never show up when looking for this problem. Oh yeah, and there was no 401.1 error (or any error for that matter).
In any case, the simple fix is to disable the check. The other option listed in the KB article is to specify host names. This works but is a PITA. So everytime you want to add a new site (say http://mysite) you have to do a registry edit? No thanks. Microsoft also doesn't *recommend* that you do this on production, but this is another piece of mis-information. You would never need this on production because we're only trying to access the site via host headers from the machine itself.
So bottom line is that if you have Server 2008 and want to use host headers with local development, do this:
-
Click Start, click Run, type regedit, and then click OK.
-
In Registry Editor, locate and then click the following registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa -
Right-click Lsa, point to New, and then click DWORD Value.
-
Type DisableLoopbackCheck, and then press ENTER.
-
Right-click DisableLoopbackCheck, and then click Modify.
-
In the Value data box, type 1, and then click OK.
-
Quit Registry Editor, and then restart your computer.
Then you'll be able to access your sites on the server using host names.
-
-
Edmonton Code Camp Time Again...
I'll be packing up my troubles and heading up to Edmonton for the day with John "The Pimp" Bristowe for Edmonton Code Camp. The Code Camp takes place on September 19th and runs all day at Grant MacEwan's downtown campus. I'm bringing my SharePoint rig with me and will be doing a little SharePoint dance called Stupid SharePoint Tricks. Here's the description of the session:
What can you do with SharePoint and what shouldn’t you do with it? Let’s take a walk through some of the lesser known features of SharePoint and things you can do out-of-the-box that you might not know you could do. We’ll also look at the dark side of SharePoint and what you shouldn’t do with it. Finally we’ll look at some of the better tools out there, how to maximize your development time with SharePoint and lots of code, code, code!
So grab your favorite nerd and head on down for a day of general debauchery and early afternoon drinking (oh yeah, and code!). Registration is open up on the website now with a list of all the speakers and sessions.
See you there!
-
No-code SharePoint Menus
A few years ago I wrote up an article about creating custom SharePoint-style menu for your own Web Parts. Today on the call with the SharePoint Experts (a fun time!) someone had created a series of Data View Web Parts and wanted some way to navigate between them.
I suggested to build a custom menu, similar to the Views pull down you get in the stock List View page. You know the one I'm talking about?
So here's the snippet to do this. It's actually based on some old stuff I did in the 2003 version, but works perfectly fine in the 2007 verison. Just modify the <menu> section with your own entries. I've added the stock list from a typical list view to get you started.
1: <table>
2: <tr>
3: <td class="ms-listheaderlabel">View:</td>
4: <td class="ms-viewselector">
5:
6: <div class="ms-HoverCellInActive"
7: onmouseover="this.className='ms-HoverCellActive'
8: onmouseout="ths.className='ms-HoverCellInActive'>
9:
10: <a id="MSO_MyMenuLink" title="All Documents" style="CURSOR:hand"
11: onclick="MSOWebPartPage_OpenMenu(MSO_MyMenu, this);" tabindex="0">
12: All Documents
13: <img alt="All Documents" src="/_layouts/images/menudark.gif" align="absBottom">
14: </a>
15:
16: <div>
17:
18: </td>
19: </tr>
20: </table>
21:
22: <menu id="MSO_MyMenu" class="ms-SrvMenuUI">
23:
24: <ie:menuitem id="MSO_MyMenu_Link1" title="All Documents"
25: onMenuClick="javascript:window.location.href='AllItems.aspx';">
26: All Documents
27: </ie:menuitem>
28:
29: <ie:menuitem id="MSO_MyMenu_Link2" title="Explorer View"
30: onMenuClick="javascript:window.location.href='WebFldr.aspx';">
31: Explorer View
32: </ie:menuitem>
33:
34: <ie:menuitem id="MSO_MyMenu_Link1" title="Modify this View"
35: iconSrc="/_layouts/images/edititem.gif"
36: onMenuClick="javascript:window.location.href='ViewEdit.aspx';">
37: Modify this View
38: </ie:menuitem>
39:
40: <ie:menuitem id="MSO_MyMenu_Link1" title="Create View"
41: iconSrc="/_layouts/images/newitem.gif"
42: onMenuClick="javascript:window.location.href='ViewType.aspx';">
43: Create View
44: </ie:menuitem>
45:
46: </menu>
And here's what it looks like:
Like I said, just modify all the <ie:menuitem> elements with your own text, replacing the onMenuClick locations with whatever you want. Then drop this snippet into a CEWP, slap it on a Web Part Page, add your Data View Web Part, lather, rinse, repeat for each page.
You can also use this just about anywhere you want to provide SharePoint looking navigation to your users.
-
Spreading some SharePoint Love to EditorPart
EditorPart. Such a lonely guy in SharePoint land. Back in the 2003 era (remember that?) we had this thing called ToolParts. You would create a class to handle your custom property editors and then hook it back up into your Web Part. Well, times are a changing and in 2007 land we use these things called EditorParts now.
By default when you add a property and expose it for editing by the user, a set of default EditorParts built into SharePoint kick in. These will handle basic types (text, boolean, enums). Here are the properties in code:
1: [Personalizable(PersonalizationScope.Shared)]
2: [WebBrowsable(true)]
3: public string MyProperty { get; set; }
4:
5: [Personalizable(PersonalizationScope.Shared)]
6: [WebBrowsable(true)]
7: public bool MyOtherProperty { get; set; }
and you get something like this in SharePoint for editing properties:
You can name the group (default is "Miscellaneous") and all is well in the world. However what if you want to use a Color Picker? Or a Rich Text Editor? Or something even more customized than that? That's where the EditorPart comes in.
Briefly you add the IWebEditable[link] interface to your WebPart[link], implement the CreateEditorParts method and WebBrowsableObject property and then create a class to do your property editing for you. This new class will inherit from EditorPart and basically handle a) creating all the controls for editing propert(ies) and sync the data back to your Web Part.
No big deal right? Here's the CreateChildControls for our custom EditorPart:
1: protected override void CreateChildControls()
2: {
3: Controls.Add(new Label { Text = "MyProperty"});
4: Controls.Add(new TextBox());
5: Controls.Add(new CheckBox());
6: Controls.Add(new Label { Text = "MyOtherProperty" });
7: }
and the resulting UI is something like this (I've added a couple of breaks):
Functional yes, but the form is somewhat lacking. The labels are not the right colours and there's no dotted separator between items. It's not bad but still not the *seamless* effect we might want and frankly, trying to match SharePoint styles is like trying to birth a baby without a uterus (sorry, I'm just all out of good analogies today).
I thought it would be nice to put together some simple controls that mimicked the SharePoint 2007 UI a little closer. This was for a bigger project (which the blog is in the works for shortly) but it's a fairly simple process to format your controls so you can have something like this instead:
Here's how you do it. First add the IWebEditable interface to your Web Part. Now you can override/implement the one method and property to get this:
1: #region IWebEditable Members
2:
3: EditorPartCollection IWebEditable.CreateEditorParts()
4: {
5: var editors = new List<EditorPart>
6: {
7: new SPCalendarExWebPartEditorPart(ID)
8: };
9: return new EditorPartCollection(editors);
10: }
11:
12: object IWebEditable.WebBrowsableObject
13: {
14: get { return this; }
15: }
16:
17: #endregion
The naming isn't really hot so come up with something more appropriate. In this case we've got an EditorPart that's going to allow us to set properties on a Calendar (this is for the second post that has yet to come). The WebBrowsableObject just returns the currrent Web Part so nothing special there.
The EditorPart requires and ID to work so we pass it in from the parent into the constructor and give it a prefix. This allows you to have multiple instances of your Web Part on the same page. We also set the Title property which will appear in the Editor Pane.
1: public SPCalendarExWebPartEditorPart(string id)
2: {
3: ID = "SPCalendarExWebPartEditorPart" + id;
4: Title = "Calendar Properties";
5: }
Finally we override the EditorPart's CreateChildControls method. Here's where the fun begins. The method starts off with a table but then a series of new controls are added:
1: protected override void CreateChildControls()
2: {
3: Controls.Add(new LiteralControl("<TABLE border=\"0\" cellSpacing=\"0\" width=\"100%\">"));
4:
5: Controls.Add(new LiteralControl("<TBODY>"));
6:
7: Controls.Add(new SPTextBoxEditorPartControl("AccessKey", "Keyboard shortcut used by the control."));
8: Controls.Add(new SPDropDownListEditorPartControl("BorderStyle", "Style of the border around the control.", typeof(BorderStyle)));
9: Controls.Add(new SPCheckBoxEditorPartControl("Enabled", "Enabled state of the control."));
10:
11: Controls.Add(new LiteralControl("</TBODY>"));
12:
13: Controls.Add(new LiteralControl("</TABLE>"));
14: }
What the heck is a SPTextBoxEditorPartControl you ask?
It's a custom control that I created to encapsulate the look and feel and bits and pieces that SharePoint uses to display it's own controls in the same style. I hunted around to see if there was a way to do this through the API and just leveage what SharePoint gives you but it's a typical story. The code in there is overly complicated and there are so many dependencies it's hard to tell what to call to get the same look and feel or effect. (if anyone has figured this out and thus making all this junk redundant I would love to hear it!).
In any case here's the text box control wrapped up in a table row and surrounded with a few DIV tags from the UserXXX classess you can find in core.css. This matches the HTML output that the SharePoint default EditorPart controls emit (or at least come pretty close) so you can get a seamless UI.
1: public class SPTextBoxEditorPartControl : Control, INamingContainer
2: {
3: public string Label { get; set; }
4: public string ToolTip { get; set; }
5:
6: public SPTextBoxEditorPartControl(string label, string toolTip)
7: {
8: Label = label;
9: ToolTip = toolTip;
10: }
11:
12: protected override void CreateChildControls()
13: {
14: Controls.Add(new LiteralControl("<TR>"));
15: Controls.Add(new LiteralControl("<TD>"));
16: Controls.Add(new LiteralControl("<DIV class=\"UserSectionHead\">"));
17: Controls.Add(new Label { Text = Label, ToolTip = ToolTip });
18: Controls.Add(new LiteralControl("</DIV>"));
19:
20: Controls.Add(new LiteralControl("<DIV class=\"UserSectionBody\">"));
21: Controls.Add(new LiteralControl("<DIV class=\"UserControlGroup\">"));
22: Controls.Add(new LiteralControl("<NOBR>"));
23: Controls.Add(new TextBox { CssClass = "UserInput", Width = 176 });
24: Controls.Add(new LiteralControl("</NOBR>"));
25: Controls.Add(new LiteralControl("</DIV>"));
26: Controls.Add(new LiteralControl("</DIV>"));
27:
28: Controls.Add(new LiteralControl("<DIV class=\"UserDottedLine\" width=\"100%\"/>"));
29: Controls.Add(new LiteralControl("</TD>"));
30: Controls.Add(new LiteralControl("</TR>"));
31: }
32: }
I've wrapped these up into a simple control that takes in what it needs via the constructor. It's a couple dozen lines of code, but something you can put away for a rainy day and makes it quick to just grab when you want to blend in with the SharePoint look and feel. As for branding, since everything is using SharePoint styles, just modify those and everything will (should) [might?] fall into place.
There are a couple of other control wrappers here, one for a CheckBox and one for a DropDownList. All of these follow the same pattern as the TextBox (inherit from Control and INamingContainer, override CreateChildControls with SharePoint-y look and feel magic).
The CheckBox is pretty much the same as the TextBox. The DropDownList is kind of fun as you pass in the enumerated type you want to pick from and it adds the items to the control. For example, here's the call to create a dropdown control for the DayNameFormat of a Calendar control:
1: Controls.Add(new SPDropDownListEditorPartControl("DayNameFormat", "Format for day header text.", typeof(DayNameFormat)));
Here's the drop down wrapper (Choices is an autopropty and set from the third parameter in the constructor):
1: public SPDropDownListEditorPartControl(string label, string toolTip, Type choices)
2: {
3: Label = label;
4: ToolTip = toolTip;
5: Choices = choices;
6: }
7:
8: protected override void CreateChildControls()
9: {
10: Controls.Add(new LiteralControl("<TR>"));
11: Controls.Add(new LiteralControl("<TD>"));
12: Controls.Add(new LiteralControl("<DIV class=\"UserSectionHead\">"));
13: Controls.Add(new Label { Text = Label, ToolTip = ToolTip });
14: Controls.Add(new LiteralControl("</DIV>"));
15:
16: Controls.Add(new LiteralControl("<DIV class=\"UserSectionBody\">"));
17: Controls.Add(new LiteralControl("<DIV class=\"UserControlGroup\">"));
18: Controls.Add(new LiteralControl("<NOBR>"));
19:
20: var list = new DropDownList();
21: foreach (var choice in Enum.GetNames(Choices))
22: {
23: list.Items.Add(choice);
24: }
25: Controls.Add(list);
26:
27: Controls.Add(new LiteralControl("</NOBR>"));
28: Controls.Add(new LiteralControl("</DIV>"));
29: Controls.Add(new LiteralControl("</DIV>"));
30:
31: Controls.Add(new LiteralControl("<DIV class=\"UserDottedLine\" width=\"100%\"/>"));
32: Controls.Add(new LiteralControl("</TD>"));
33: Controls.Add(new LiteralControl("</TR>"));
34: }
And the result that provides a dropdown of all the values of the enum provided:
Features of this code over the OOTB properties? It's fairly simple to use (just add the controls, specifying the name, tooltip, etc. in the constructor). Seamless look and feel integration with the regular SharePoint properties. Tooltips for labels. This is just a sampling but any control could be used here for more sophisticated property editing.
That's it. I'll post the full source along with something special coming later that utilizes this, but there's enough here to get you going should you want to roll your own. Feel free to leave comments, suggestions, and "What the hell are you doing Bil?" notes with me.
Here's a bunch of great links on writing your own custom EditorParts:
-
Speaking at TechDays 2009, Me
TechDays, the all-Canadian all-knowing all-seeing Microsoft conference is kicking off in a couple of weeks in Vancouver. To the disappointment of the street walkers of Gastown, I won’t be there. I will however be in Calgary in November. Here are the two talks I’m giving:
Developing and Consuming Services for SharePoint
The world gets more service-oriented every day, and with that comes the demand to integrate all kinds of services, including those from SharePoint. This session introduces SharePoint as a developer platform and provides an overview of how you can build and deploy custom services with it. The focus will be on developing ASP.NET and Windows Communication Foundation services for SharePoint as well as building a Silverlight client to consume them.
Versioning and Upgrade of SharePoint-based Solutions
Now that you've deployed your first SharePoint application, you will probably get a long list of enhancement requests and maybe some bugs to fix. However it is not at all obvious how to update various site elements. For example, adding a field to a content type, requires a completely different process than updating Web Part pages. In this you will learn how to update these and other elements including workflows, Web Parts, and list item event handlers based upon best practices . Thorny problems abound when it comes to making a change. Learn what strategies to apply and what behaviours to expect for different types of artifacts in SharePoint. In this session you'll see what the Patterns & Practices team put into their guidance for SharePoint-- and why.
TechDays is a great conference for us Canadians. It’s two days of fun-filled talks on all sorts of Microsoft Evil Doings. Make sure you get the early bird special as it’s $299 (that’s *Canadian* dollarinos, none of the this American crap) for a limited time (then you get to pay through the nose and shell out $599, and really, is it worth listening to me speak for that amount of money?).
Have you got a TechDays badger yet? If not, drop by here to see them then slap that cap, err… badger onto your website somewhere inconspicuous (like right at the top over your blog logo). If you’re looking for the individual badger rather than the full logo sheet, then go here (I was confused at first until I actually read the page). I went for the hanging badger just because the maple leaf seemed *too* Canadian and the stickman, well, let me tell you about stickmen!
If I had some time tonight, I would make my very own customized Justice Gray stickman badger, but alas I have work to do. I’m sure D’Arcy or someone will come up with something creative.
Should be a blast so hopefully you’ll drop by and see how much Sesame Street content a guy can stuff into two presentations on SharePoint.
This blog post was brought you by the letters “S”, “P”, and the number “2010” which won’t be making an appearance at TechDays 2009 but we still like Microsoft anyways.
P.S. The website lists 200+ level sessions. I’ve lost track of what level I try to present at so let’s just kick this puppy up a bit and crank the SharePoint sessions up to 1100, k?
-
Happy Birthday CodePlex!
CodePlex just recently turned 3 years old and celebrated a pretty good success (hitting the 10,000 project mark). I'm very happy that my SharePoint Forums project was one of the first public non-Microsoft projects hosted on CodePlex (before it was called CodePlex).
Here are some impressive stats from the 3 year old:
-
10,000 projects
-
Consistent growth over the 3 years in visits, page views, projects and source code
-
3 million visitors per month
-
Almost 10 million page views per month
-
160,000 registered users
-
160 million lines of code stored across 10 team foundation servers
-
16,000 source code check-in per month
Congrats guys! Great work. Check out more stats here on the CodePlex blog and some other info here on Port 25.
-
-
Boo! SharePoint. Not really.
I stumbled onto Peter Campbell's well written article about why SharePoint scares him. His points on what bothers him about SharePoint bother me as he tries to compare some parts of SharePoint to say open source CMS systems (he's not specific as to "what" open source portal he compares SharePoint too but one can assume Drupal, Joomla, DotNetNuke maybe).
It's not clear if Peter has actually ever used SharePoint (either as a developer, admin, or even an end-user). He says that he's been "evaluating" the product for the past four years but that could be just reading white papers and looking at slide decks for all I know. There are some valid points in his article but I think it paints SharePoint in a dim light without being honest to the points he raises. I don't see Peter bashing SharePoint (too much) but rather his take on it for his purpose. His summary is that it requires a lot of consulting, hardware, and budget but organizational needs can better be served by simpler alternative installations. I agree but there are some important points to be aware of if making this comparison and decision.
So here's yet another point/counter-point blog entry that will probably label me as a Microsoft flag-waver but so be it.
Integration with Legacy Systems
Mainly Peter points out that SharePoint is based on SOAP vs. the more simpler REST approach from an integration perspective. This is true and if you're not familar with SOAP and know what the limitations of the web services SharePoint provides you can get yourself into a pickle. I think integration with *any* legacy system is complicated no matter what the interface is. You're dealing with legacy data which doesn't conform to any current standard. SharePoint isn't the cause for concern here, what you're trying to get SharePoint to do is. If you're trying to migrate a legacy application into SharePoint to be your new Time Tracker, Inventory System, or Help Desk (complete with all the legacy features you enjoyed in your legacy system) then you're making the wrong choice. SharePoint might be great as a presentation layer for this and you can expose all the data through the BDC for example, but trying to rebuild SAP or something in SharePoint? Bad choice IMHO as a SharePoint app and more suited as a .NET application where you can use REST (e.g. via ASP.NET MVC) and expose that content to SharePoint via presentation. SharePoint is not the dumping ground for migrating legacy applications to.
Two Applications
He states that SharePoint is "two major, separately developed applications". Not really. Windows SharePoint Services is essentially a platform (and free). The templates Microsoft provides out-of-the-box are just that. Templates. You can rip all of them out, hide them, and otherwise completely ignore them and build your own set of site templates on top of WSS. That's why I consider it a platform that can be extended. Want to build a DotNetNuke or Drupal clone on top of WSS? It would probably take a lot of work to create the templates, styles, and master pages but can be done. Microsoft Office SharePoint Server is the separate application that a) relies on WSS b) provides additional templates and c) provides additional services (Excel, Records Managment, etc.). I don't agree they were "hastily merged into one app". WSS hasn't changed much since the 2.0 days fundamentally (other than the flip in the ASP.NET architecture) and MOSS was developed over the course of 4 years or so of development and testing. How does that classifiy as "hastily"?
Misguided Site Structure
Peter doesn't bash SharePoint here (at least I don't think he does). He simply states what we all have said in the past. With great power comes great responsibliity. If you let your users create whatever taxonomy of mischief they can imagine, don't be too proud of this technological terror that gets created. I posted a blog entry to stop blaming SharePoint for your misfortunes here. I think this point just restates that intent.
The Revenge of the Blob
Peter points out that the database stores documents as blobs (rather than linking to files on the disk) which in turn might lead to corruption and has some performance hit. He cites he doesn't want to put his organization's critical work and put it on a box that could easily break. I'm not sure the failure rate of SQL and data content but frankly I think this is as fragile as files on a file system. I don't know how many times I've had cross-linked files or ACL issues or whatever on the file system, compared to SQL corruption. I'll come clean and say that I've experienced SQL corruption first-hand (a log file overran the disk and corrupted the data file which was not backed up as the SQL agent was disabled). However this is the exception to the rule. Frankly, the thought of having a set of files on the file system and the metadata in a database scares me more. The risk of getting this out of sync is far greater than SQL database corruption and a SQL database doesn't retain the file corruption that would happen if the file system went south. I simply restore it to a new SQL instance. File level corruption would be replicated if I did file backups so I would be SOL with my "critical work product". Also I'm not sure how you handle file versioning if files are stored on the file system. A folder for each file? Some naming strategy for files? If you look at modern source control systems (e.g. Subversion, Git, etc.) and how they version files on the file system they all use blob storage for the files with delta values. No different than what SharePoint is doing, except the storage medium is a file system instead of a database.
Licensing Complications
I'm no licensing expert and frankly licensing for *any* Microsoft product is over complicated. Ask five licensing guys at Microsoft about a product and you'll get seven different answers. What I do know is that licensing for external is pretty basic. If you're exposing your site externally you need the external connector license. This is not per-user, but just a single fee for exposing your site on the internet. I believe there is an additional license if you're exposing more functionality than what WSS has to offer (e.g. MOSS features like InfoPath forms or Excel Services) but again I'm not an expert. Peter is correct that pricing is somewhat prohibitive and I agree it's complicated. It's getting simpler (the licensing for 2003 was far more complicated than it is for 2007) and hopefully it'll get better for 2010.
Hardware Requirements
Peter compares the hardware requirements of SharePoint to "most open source portals". I'll agree here. Drupal doesn't seem to mention it's requirements (that I can find) however they recently took all their donated money are poured it into infrastructure amounting to 4 total servers and spending about $10,000. This is for the public facing Drupal site. Not bad for a site but again, I can't tell how many users are hitting the site or what kind of stats that hardware is needed to serve up. Similiarly, Joomla only lists software requirments and both basically say they need a LAMP stack (or WAMP in some cases) but no mention of hardware is provided. So to do a comparison you can try to look at hardware requirments for MySQL, Apache and PHP and try to fit them into similar offerings for SharePoint (SQL, IIS, ASP.NET) but it's difficult.
SharePoint hardware requirements are pretty straight forward and I've lived through all configurations. If you have 1,000 users who use it on a normal basis (more on this in a minute) you can run the entire rig on a modern machine (high end workstation) without any issue. SQL is a pig and your IIS worker process can easily gobble up a few hundred megs of memory just starting up. So a) run 64 bit, it's the only way b) through at least 4GB of RAM on the box, but 8 would be better. After 1,000 users you'll need to scale up to a small farm. This can be 2 web-front ends, the app, and SQL box. Now we're talking about most middle size farms serving up 10,000 users. When I say "normal basis" I mean mostly readers of documents, people authoring documents on a regular basis (say 1 worker uploading a new document a couple of times a day with a maximum of a few hundred workers). The numbers here are not scientific and I encourage you to get the SharePoint Capacity Planning Tool which will give you a much better set of numbers to work from. In any case, a LAMP stack is more easier on hardware but then I've got various WAMP stacks running on the Internet where PHP can easily bring IIS (and Apache) down to it's knees if you don't have a good CPU and lots of RAM behind it. Same as SharePoint. I'll admit the requirements for SharePoint are higher as there's more going on behind the scenes but this is typical of what SharePoint does vs. what you can do with an open source portal.
I grow tired of the "commercial vs. open source" discussion every day. It's like comparing apples to gasoline. Each does what it needs to do differently, there are different costs involved, different outputs, different inputs. And trying to nitpick over the value of one over another is futile and value-diminishing. When two products from each space can meet all requirements in a balanced fashion then you can make the comparison and put more value on one over another but I have yet to find those products to compare.
Overall
It's just my opinion (as is Peters) but I think Peter compares too many fundamental architectural problems and blames SharePoint as the cause. Also there are vague references to "open source" being better and more fluent. The primary issue I have here is that most any other package doesn't offer as many features as SharePoint has (mainly on the integration with the Office client).
I'll single out Drupal, Joomla, and Wordpress here as I use them all the time. I know Wordpress is considered "just a blogging tool" but it has a lot of functionality that goes beyond that. The third party market for these products is huge and there's some quality stuff out there. If I want to plug a feature into Drupal, it doesn't take long to find it. Building WordPress plugins isn't too complicated and the APIs are flexible and simple to use. Are these tools as powerful as SharePoint? No. Is SharePoint better at doing say Wikis or Blogs like MediaWiki or WordPress. No. However you have to choose the right tool for the right job and in the corporate environment where 99% of your information workers live and breathe in Word, PowerPoint, Excel, and Outlook there's nothing that does things better and easier than SharePoint. Today it's not Web 2.0 and it's painful to do what might be the simplest of things in an open source equivalent.
Sure, Drupal is easier to setup and Joomla can be more "Web 2.0" out-of-the-box but from a feature perspective and long-term sustainability view, Enterprises have to rely on something that's going to be around in 7+ years and not drastically change. Open source products, while quicker to setup, don't scale to this level; don't offer this kind of integration; and haven't been around long enough without drastic re-work to yield this kind of longevity.
Far be it for me to say SharePoint is some kind of holy grail in content managment or intranet portals. It's not. It's a good product that does some things very well and other things not so well. Its growing and evolving. With the latest sneak peek at the 2010 version, like the transition from 2003 -> 2007, things are about to get a whole lot better again.
-
The undocumented “deleteconfigurationobject” parameter
SharePoint is sometimes a riddle, wrapped in a mystery, surrounded by an enigma. And even more so when you get stuck trying to do something, hoping the system will tell you how, only to stumble onto an undocumented command line switch that does what you need.
Enter the super-secret-that-everyone-knows-about “deleteconfigurationobject” parameter in stsadm.exe.
Go ahead. Enter this in your console.
1: stsadm -help deleteconfigurationobject
And rather than getting the usage screen (meaning you entered a parameter that the program didn’t understand) you get this:
1: stsadm.exe -o deleteconfigurationobject
2: -id <objectId>
Woah. That wasn’t in the usage screen when you enter stsadm.exe by itself. Nor is it in the documentation for stsadm.exe on TechNet here.
This is what is known in the software world as an “undocumented feature”. Meaning something you can use, invoke, etc. but it’s not documented so it’s not guaranteed to be there tomorrow.
Okay, armed with the knowledge this bad boy exists, what would I do with it?
In my situation, I’m working on custom timer jobs. Andrew Connell recently wrote some great blog entries and MSDN white papers on custom timer jobs and Gary Lapointe recently had a posting about deploying files not handled by the WSP schema for you.
Basically if you want to run something on a regular basis you can create a custom timer job, register it, and have the OWSTIMER.EXE service take care of it for you. This beats the pants off of having to write a console application then setting it up on the Task Scheduler because you have immediate access to the SharePoint site structure to do updates or what have you. For example, rather than calling out to a weather web service for every user or having to deal with caching information, I created a timer job that did it for me and let my web part simply read from a sanitized SharePoint list on the site for the local weather. This required some hourly updates to the list which I did via a custom timer job. Again, check out AC’s article(s) on the subject and you can’t go wrong.
However, there are times that things go wrong. Very, very wrong. Like tonight. I had created some timer jobs but foolishly ripped out the default constructor (which is required for serialization/de-serialization). Bad Bil. This caused me no end of grief when I tried to remove the jobs since de-serialization failed, the jobs threw exceptions during feature de-activation and never got deleted.
What I ended up with was this:
The names of the custom jobs are obfuscated here, but the two job names at the bottom of the image (pixelated out) are the same job name but with different instance IDs.
In the Central Admin screen, you can view the job definition and even disable jobs but there’s no where to delete a job definition. There’s even a CodePlex project here for doing custom job manipulation (very handy if you want to quickly reschedule a job) but alas, there’s still no delete feature.
Update: I did find a project here on CodePlex that’s specifically made for manipulating timer jobs and does (based on the screenshots) allow deletion. Grab it and install it if you don’t want to do the manual steps below!
I thought I was going to have write a cruddy console app to find the job and delete it via the API. That’s until I found this forum post on the interweb. It hinted at a command line operation for stsadm.exe called deleteconfigurationobject. Like the forum post says, go into Central Admin to find your jobs and hover over the job name. Then select the shortcut and paste it into Notepad or something. You’ll get something like this:
1: http://myserver:25435/_admin/JobEdit.aspx?JobId=bf166029%2D7c99%2D49cc%2D9ade%2D45586487c20c
Note the Id after the JobId parameter. The dashes are encoded and show as “%2D”. Simple change them to a real dash and strip away the rest to get this:
1: bf166029-7c99-49cc-9ade-45586487c20c
This is the GUID for the job timer that we want to delete.
Now go to a command prompt and enter the secret deleteconfigurationobject operation using the GUID for your job as the id:
1: stsadm -o deleteconfigurationobject -id bf166029-7c99-49cc-9ade-45586487c20c
2:
3: Operation completed successfully.
Voila! Rogue job timer gone away! Poof.
Daniel Bugday, you made my day! I’m sure there are other configuration objects you might need to delete that have no user interface anywhere in SharePoint that might be used with this command.
Like I said, caveat emptor. This is an undocumented feature and maybe a simple oversight or potentially not something meant for end-users. Thus Microsoft may choose to remove it in the next version or change it’s behavior or turn it into a newt. Don’t bet the farm on this command (but it’s nice to know it’s there isn’t it?).
-
Extending SharePoint: Checking if a List exists
A common problem when working with the SharePoint Object Model is getting a handle to a list. Very often we find ourselves writing this:
1: try
2: {
3: using (var web = site.OpenWeb())
4: {
5: if (web != null)
6: {
7: try
8: {
9: var list = web.Lists[ListTitle];
10: }
11: catch (ArgumentException)
12: {
13: web.Lists.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
14: }
15: }
16: else
17: {
18: Console.WriteLine("Unable to open web site.");
19: }
20: }
21: }
22: catch (Exception ex)
23: {
24: Console.WriteLine(ex.Message);
25: }
This adds a list if it doesn't exist on the site. What a pain to have to write this over and over again. Okay, so step 1 might be to write a method on a helper class (usually a static class and static method) that would do it for you. I'm not a huge fan of helper classes as you eventually end up with the MonolithicHelperClass or MotherOfAllHelperClasses that pretty much does everything. That's not cool in my books.
Enter extension methods for SharePoint.
If you're building your web part or solution on .NET 3.0 or higher, you can make use of a feature that was introduced called extension methods. I won't get into the details of extension methods here but check out this post by Scott Guthrie on them and do some Googling if you want to know more. In a nutshell, extension methods let us extend existing classes not by subclassing them but by adding new methods to them. In the SharePoint world, extension methods are really useful due to a lot of the OM "workarounds" we face every day.
To create an extension method we need to do two things. First, create a static class to hold them. This can be named anything you want. Then include a static method. The first parameter to the static method will be the class you're extending (in our case SPWeb) prefixed with the keyword "this". "this" tells the compiler that we're extending the type specified so whenver we encounter a method with this signature of the type, please run our code for us. Here's our code moved into an extension method:
1: public static class SPWebExtensions
2: {
3: public static bool ListExists(this SPWeb web, string listName)
4: {
5: try
6: {
7: var list = web.Lists[listName];
8: return true;
9: }
10: catch (ArgumentException)
11: {
12: return false;
13: }
14: }
15: }
Not bad. So now we can use it like this:
1: try
2: {
3: using (var web = site.OpenWeb())
4: {
5: if (web != null)
6: {
7: if(!web.ListExists(ListTitle))
8: {
9: web.Lists.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
10: }
11: }
12: else
13: {
14: Console.WriteLine("Unable to open web site.");
15: }
16: }
17: }
18: catch (Exception ex)
19: {
20: Console.WriteLine(ex.Message);
21: }
However I'm still not happy with the try/catch statement in our extension method. Sure it works but it's expensive always throwing an exception when a list doesn't exist. Inside our extension method, we have access to all public methods and properties of the instance of the object we're extending so why not iterate through the list, checking to see if the name matches up and returning our boolean value instead?
1: public static class SPWebExtensions
2: {
3: public static bool ListExists(this SPWeb web, string listName)
4: {
5: var lists = web.Lists;
6: foreach (SPList list in lists)
7: {
8: if(list.Title.Equals(listName))
9: return true;
10: }
11: return false;
12: }
13: }
This is cleaner and doesn't throw an exception if the list isn't found. Some would argue that iterating through the entire list is expensive but really people, if you have *that* many lists on your site you might want to consider a bit of a content/architectural review first before blaming the code techniques presented here.
FWIW, there is a nice library of SharePoint extensions available on CodePlex here. They have an extension method that will check for a list however I'm presenting this solution here because you may not want to commit to an entire library for a single need like we have here. Their ListExists method extends the SPWeb object, but also makes use of their own extension to the SPListCollection and some LINQ magic. Feel free to use that solultion as there are a lot of great extensions available there.
Unfortunately there's not much hope in the future for fixes to the Object Model so we continue to add our own extensions as we see fit and the need arises. Eventually something will exist in the base system that you can retire your own RYO code but for now this is a pretty good compromise IMHO. YMMV.
-
Fill out a survey, win a chance for a SharePoint Conference 2009 pass
Yeah, title says all. Mindsharp, Nintex, and Combined Knowledge are sponsoring a Global SharePoint Survey. This is an independent survey that you can fill out to let them know about your experience with adoption and usage of SharePoint from your perspective. The survey is quick (only 15 questions) and most questions are multiple choice. You could probably let your cat or two year old fill it out (I did) but also consider taking a few minutes to put some thought behind it (unlike what I did).
In the end, you get a chance to win a conference pass to the Microsoft SharePoint Conference 2009 (October 19-22) in Las Vegas. This pass is worth $1,119 USD if purchased with real money. A winner will be drawn randomly after the closing of the survey July 17th then notified by email and/or phone. Note the contest is *only* for the conference pass. You still need to provide a way to get there and pay for your hotel, mini-bar, and pub crawl expenses (and trust me, when you hang with SharePoint dudes, the bar bill can get pretty hefty). Still, it’s a short slice out of your life for a chance to make it big in the city that never sleep.
You can fill out the survey here. Enjoy and good luck!