Sitecore Does NOT Like maintainScrollPositionOnPostback

We're working on a new feature for a client web site that uses the asp:Wizard control wrapped in an UpdatePanel. Since ASP provides this nice Page.MaintainScrollPositionOnPostback method that automatically sets up JavaScript that will keep your page at approximately the same position after postbacks, I enabled it in Web.config and didn't think about it for a few days ... until we noticed that most of the dialog boxes in Sitecore had quit working. I could see via Firebug that the postbacks were happening when buttons were clicked that SHOULD have resulted in something happening, but the responses were empty, even though they were accompanied with a HTTP 200 response code.

After about 24 hours of head-scratching, and attempting to roll back other code changes, I realized I'd made that seemingly minor change to Web.config. I was tempted to dismiss it entirely since there wasn't any logical reason I could think of that the change would have affected Sitecore, but I went ahead and removed it and tested ... and whaddya know, things started working again. I'm at a loss to explain why it makes a difference since I'm not intimately familiar with the internals of Sitecore, but I'm tempted to start taking a look at things using ILSpy and see if I can figure out what the deal is.

Changing Workflow State in Sitecore

A few weeks ago, I needed to change the workflow state of items in Sitecore; this particular change was to happen to a few particular items while Sitecore was handling the item:saved event. I tried several solutions I located via Google, most of which were some variation of getting the Workflow of an item, and then calling Workflow.Execute() and passing it the command I wanted to execute, the item, and some other assorted bits of data.

None of them worked; in fact, I got consistent complaints that WorkflowProvider.GetWorkflow(item) was returning null.

After beating my head against the wall for long enough to add a second window to my workspace, I recalled that Sitecore stored the workflow and workflow state in each item as a part of the Workflow section that the Standard Template inherited. The "__Workflow state" field is just a reference type field that contains the Sitecore ID of the workflow state the item is in. So, changing the state is as simple as getting the ID of the desired Workflow state and doing something like this:

item.Editing.BeginEdit();
item.Fields[Sitecore.FieldIDs.WorkflowState].Value = DESIRED_WORKFLOW_STATE_ID;
item.Editing.EndEdit(); 

In that bit of code, Sitecore.FieldIDs.WorkflowState is the same thing as "__Workflow state", I just think it's a little cleaner to use the built-in Sitecore value instead.

You can also change the actual workflow like so:

item.Fields[Sitecore.FieldIDs.Workflow].Value = DESIRED_WORKFLOW_ID;

Now ... you know the drill. With great power, comes great responsibility. If you change the workflow or workflow state this way, you've completely bypassed the Sitecore way of doing things. But, if you need a quick way to change the state and you can't get things to work via the API, this just might be the ticket for you.

Google Chrome II: The Revenge of Background-Position

I spent a good portion of this afternoon trying to track down a bizarre issue with lists and Google Chrome (Safari also has a guest apperance in this little adventure). The design in question was using background-image and its bretheren to implement custom bullets. In Chrome and Safari, the image appeared twice in each list item. I tried all sorts of tricks to remove the interloper to no avail, including breaking up the background attribute into its component background- attributes at the list item level, and adding background-image: none to the unordered list level. Then I noticed this bit of code:

background-position: 0px, 10px;

Notice that comma? I didn't the first forty or so times I looked at the code. Removing that comma made the second instance of the image go away. If you're wondering ... yes, the more commas you add, the more times the image will appear. This, for instance, will make the image appear at 0px, 10px, 20px, 30px and 40px from the left edge of the list item:

background-position: 0px, 10px, 20px, 30px, 40px

Google Chrome, SSL Certificates and Doctypes

Yesterday I received a message from a friend; he told me that people were having trouble with the SSL certificate on a site that I host for him. I fired up Firefox (on my Windows 7 box), loaded his site, and then checked the certificate details. Firefox was quite happy with the certificate, which didn't expire for another three months or so. I then proceeded to check out the site on Firefox Mac, Safari Mac & Win, IE8 Win and Chrome Mac & Win. All of the browsers reported no problems ... except for Chrome, which indicated that there was a mix of secure and unsecure content on the page.

"Well, that's easy enough to fix," I thought to myself and checked the source of the page, only to discover that all of the images, CSS & JS on the page were being loaded via HTTPS. I verified this with Chrome's Developer Tools as well. At that point I started scratching my head ... and had several friends verify they were seeing the same results. I spent some time with Google trying to find some hints, but found nothing helpful, except that Chrome had a problem with incorrectly reporting that sites had SSL issues ... but that was from late 2009, and it appeared that the bug had been fixed since then.

After some time, I noted the following few lines at the top of the page in question:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

I'd never had to worry about changing the doctype before ... and besides, IE, Firefox and Safari were perfectly happy with that setup. I went ahead and changed the "http" to "https" ... and sure enough, Chrome was happy.

So ... this is my attempt to get something into the Google results. If you've got problems with Chrome saying you have insecure elements on a secure page ... check that doctype!

Addendum: While this does seem to work (the page gets rendered properly, while removing the doctype altogether or switching to the skinny doctype caused the page to not render properly), you can't actually fetch the DTD via the https URL, so perhaps this isn't the best solution if you can, for instance, use the skinny doctype without causing problems.

Addendum the Second: Removing the reference to the DTD and truncating the doctype to <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"> also seems to work without affecting rendering.

Final Addendum (Maybe): It also appears that using a protocol-relative URL in the doctype works. I just learned about this nifty trick via this blog post ("The protocol-relative URL") by Paul Irish. (HT: Joe Devon) As it stands now, the doctype on the site in question looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Getting the Sitecore ID of the Current Layout

Recently, I found myself in a situation where I had a Sitecore sublayout that was shared by a number of layouts. For one particular layout I needed to hide an asp:Panel in the sublayout. Turns out this is pretty easy to do:

Item item = Sitecore.Context.Item;
String currentLayoutId = item.Visualization.GetLayoutID(Sitecore.Context.Device).ToString();
if (currentLayoutId == "{your-layout-id}")
{
    // do something
}
else
{
    // do something else
}

Extensionless URLs with Sitecore using the Helicon Tech ISAPI_Rewrite Module

One of Sitecore's recommended methods for making extensionless (sans .aspx) links work is to turn the addAspxExtension attribute of the /configuration/sitecore/providers/add element in Web.config to false, and the configure IIS to use /default.aspx as the 404 handler (see sections 2.1.1 and 2.2.1 of the Sitecore Dynamic Links document), which has the effect of forcing ASP.NET (and thus Sitecore) to handle all URL requests. The down side of this method is that it has a negative effect on the performance of your site.

Sitecore hints in section 2.1.2 that you can use an ISAPI filter to rewrite your URLs to achieve the same effect and a footnote directs you to the "Friendlier Marketing URLs" document on the Sitecore Developer Network web site. However, I couldn't get the instructions there to work at all.

The previously-mentioned document recommends the use of Helicon Tech's ISAPI_Rewrite module (http://www.isapirewrite.com/); I found the free Lite version of the module to be just fine in the instances where I implemented it. The main limitation of the Lite version is that you can only create global rewrite rules. Since I only have one site running on the server I implemented this solution on, that was not a problem.

If you're familiar with the syntax of the Apache mod_rewrite module, you'll be right at home configuring ISAPI_Rewrite; the syntax is, as far as I can tell, identical ... and that's after five years of configuring mod_rewrite on Apache. If you're not familiar with mod_rewrite, I think you'll find the ISAPI_Rewrite documentation (http://www.isapirewrite.com/docs/) quite helpful; it's fairly exhaustive.

All you need to do to get the ball rolling is to install the version of ISAPI_Rewrite that is appropriate for your machine, and then edit your httpd.ini file (if you're using the Lite version of the module, this will be in the installation directory, which can be found in your Program Files folder) and replace its contents with this:

 

[ISAPI_Rewrite]
RewriteEngine on
RewriteBase /
RewriteCond URL (?!/sitecore).*
RewriteRule ^([^\.\?]+)/?(\?.*)?$ /$1.aspx$2 [L]

 

The first two lines enable the module and get it set up to rewrite all requests to the web root of your site. The RewriteCond line tells ISAPI_Rewrite to not bother with any URLs that start with Sitecore, so it won't interfere with the sitecore admin functionality. The RewriteCond directive tells the module to transparently append .aspx to any URL that doesn't have an extension.

That's it! Good luck!

Addendum - Wednesday, September 22, 2010:

If your Sitecore project features integration with Coveo, you'll also want to prevent ISAPI_Rewrite from rewriting URLs in the /Coveo directory like so:

 

[ISAPI_Rewrite]
RewriteEngine on
RewriteBase /
RewriteCond URL (?!/sitecore).*
RewriteCond URL (?!/Coveo).*
RewriteRule ^([^\.\?]+)/?(\?.*)?$ /$1.aspx$2 [L]

 

Installing MongoDB as a Service on Windows 7

Over the last few months there's been an increasing amount of interest in NoSQL database options among the members of the PHP Developers' Group that I help organize in Chattanooga ... and frankly, I've been itching to dig in myself. The other organizers and I decided that what we would do is each choose a NoSQL database engine, and over the next few months we'd cover those engines by devoting the main presentation of a meeting to them. Our presentations will cover the basics of using the engine in question, discuss the strengths and weaknesses of the engine, and the process of building a simple a simple application in PHP with it.

I chose MongoDB. Well, as fate would have it, I was in the process of learning ASP.NET/C# at work, and as an exercise, I ended up setting up MongoDB on Windows and using it to build a simple web using ASP.NET ... so my first foray into MongoDB was with C# rather than PHP. Go figure.

MongoDB works quite well on Windows. Unfortunately, the downloadable .ZIP package you get from mongodb.org doesn't give you any indication of how to run it as a Windows service, and I really, really wanted to do that instead of having to manually start it up every time I wanted to play with it. I went out searching for information on how to do so, and ended up finding conflicting instructions that sometimes didn't work. I couldn't find any start-to-finish tutorial that explained all the steps of how to get things working. Hence this post ...

In this tutorial, I'm using what is currently the most recent production-ready version of Mongo, 1.4.1. You should be vaguely comfortable with working with the command prompt, editing registry entires with regedit, and starting/stopping services before going any further. You may break your computer, anger your mailman or trigger an IRS audit if you're not careful. You've been warned. Finally ... I have no idea if these instructions work on any other version of Windows; I haven't tried them on anything but Windows 7.

  1. Download and extract the MongoDB archive appropriate for your system. http://www.mongodb.org/display/DOCS/Downloads
  2. Copy the extracted folder to C:\ and rename it mongo.
  3. Create a directory called 'data' inside C:\mongo
  4. Click on the Start Menu and enter "cmd" in the search box. Right-click on cmd.exe and select "Run as administrator." This is VERY important because you won't be able to register MongoDB as a service if you run cmd.exe with normal privileges.
  5. Type "C:\mongo\bin\mongod --install" to register MongoDB as a Windows service. This will probably output what may look like an error message, but don't worry ... as long as you really did run cmd.exe with administrator privileges, everything is fine.
  6. Click on the start menu and enter "regedit" in the search box. Click on regedit.exe, and then browse to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services. There should be child folder there named MongoDB that has several keys inside it. If not, go back to step 4 and try again.
  7. The ImagePath key should read "c:\mongo\bin\mongod --service" right now; change it to  "c:\mongo\bin\mongod --service --dbpath c:\mongo\data". This will allow MongoDB to find its data files.
  8. Click on the Start Menu and enter "services" in the search box. Click on the "Component Services" option.
  9. In the pane on the far left, double-click on "Services (Local)." Locate MongoDB in the list in the center pane, and double click on it to bring up the properties inspector window.
  10. Make sure "Startup type" is set to automatic. Then click on the "Start" button.
  11. Browse to http://localhost:28017/ to verify that MongoDB is really running.

That's it!