Friday, October 31, 2008

Image rotating in Drupal

Here's the poor man's image rotating approach for a Drupal site (this simple technique would work for other sites as well, provided you can generate a random number betwen X and Y). It is a very simple CSS-based with some random number generation for creating a different CSS class name for the header image.

NOTE: I'm displaying images as a background of a #header DIV.

Place some images inside your Drupal's current theme's images subfolder (that should be /[drupal root]/sites/all/themes/[your-theme-name]/images/headers/). I've placed mine in the subfolder headers where I keep all header images.

Its probably a good idea to name them similarly - header1.jpg, header2.jpg, etc.

Edit your stylesheet for the site and add one line per every image you have in the images/headers/ subfolder:
{ background:url(images/headers/header1.jpg) no-repeat;}
{ background:url(images/headers/header2.jpg) no-repeat; }
// ... add more with the same pattern
Edit your page.tpl.php and generate a dynamic class name for the #header DIV (which displays the header image), using a random number generator with a new CSS class name. The resulting code would be:

<div id="wrapper">
srand ((double) microtime( )*1000000);
$random_number = rand(1,10);
<!-- start #header -->
<div id="header" class="header-<?php echo $random_number; ?>">

NOTE: I'm using rand(1,10) to create random numbers between 1 and 10. Adjust as appropriate for your setup.

That's it. Once you open the page you'll get the #header DIV to have class="header-1" ... until class="header-10" in, hopefully, random manner.

Tuesday, October 21, 2008

ASP.NET 2.0 useful formatting tips

This post applies to ASP.NET 2.0, using VB.NET. It contains some useful tips on dealing with data binding syntax and formatting of data.

Conditional boolean field formatting in a GridView
<asp:Label ID="Label3"
Text='<%# IIf(CBool(DataBinder.Eval(Container.DataItem,"hasPassedFlag")) = true, "Pass","Fail" )%>'>
Parsing decimal numbers before SqlDataSource fires update/insert

Sometimes, the UI shows a number in ###.## format but the underlying culture info has ###,## for the decimal format. If left untouched, entering a ###.## would get interpreted as a much larger number in the context of the culture info that expects ###,## decimals.

To avoid problems, intercept the inserting/updating event on the form view and manipulate the decimal value there, setting the appropriate value into the SqlDataSource parameters.

Sample definition of the SqlDataSource with its parameters:
<asp:SqlDataSource runat="server" ... >
FormField="fldAmount" Name="fldAmount" />
... // similar for InsertParameters
FormView uses the above SqlDataSource as the DataSourceID and contains a TextBox bound to the fldAmount:
<asp:TextBox Text='<%# Bind("fldAmount") %>' ID="fldAmount" runat="server"></asp:TextBox>
In the code behind, handle the ItemInserting/ItemUpdating events of the FormView. First, deal with ItemInserting:
Protected Sub form1_ItemInserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.FormViewInsertEventArgs) Handles form1.ItemInserting
Dim tmp As Decimal
tmp = Decimal.Parse(e.Values.Item("fldAmount"), System.Globalization.CultureInfo.InvariantCulture)
e.Values.Item("fldAmount") = tmp
End Sub
Similarly, ItemUpdating (notice the e.NewValues)
Protected Sub form1_ItemUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.FormViewUpdateEventArgs) Handles form1.ItemUpdating
Dim tmp As Decimal
tmp = Decimal.Parse(e.NewValues.Item("fldAmount"), System.Globalization.CultureInfo.InvariantCulture)
e.NewValues.Item("fldAmount") = tmp
End Sub
That should do the trick. The form value for fldAmount would get appropriately parsed and then via data-binding that's already in place would get properly updated in the database.

Thursday, October 2, 2008

Recipe 49 gotcha (from Advanced Rails Recepies - 84 New Ways...)

Advanced Rails Recepies suggests in recipe #49 to avoid specifying IDs for fixtures when you're trying to express has_and_belongs_to_many relationships and instead just refer to the name of the instance in one of the fixtures. Looks wonderful.

I had some number of fixtures and just wanted to introduce this by adding a completely new fixture file and specifying the relationship as suggested. The old files that I had I modified by adding the relationship reference.

For example, I had a simple users.yml which had:
id: 1
name: jack

I has_and_belongs_to_many :roles for user. My roles.yml fixture was:
id: 1
name: role1
So I tried adding a reference to the role1 in my user1 like so:
id: 1
name: jack
roles: role1
The test failed when I tried verifying that user1 contains role1. It appeared that the connection was never made. I did notice in the database that a record in tables roles, users and roles_users was added but (!) the ID for roles_users.role_id was this weird big number that was obviously the reason why it appeared that the user did not get the role required.

To fix this, just remove the ID from those instances inside your fixture files that you'll be "connecting" through that nicer named-reference approach.

undefined method url_for in rails functional test

When attempting to use url_for(options) method (which works fine in controllers and views) inside a functional test (Rails 2.1.1) I'd get 'undefined method url_for...' exception. This was a standard functional test so I thought these things would be there.

To fix it, inside your functional test class definition do something like this:
class GroupControllerTest < Test::Unit::TestCase
# Note: to make sure url_for works in a functional test, include the two files below!
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TagHelper
I wonder if there's a nicer way to do this?

Wednesday, October 1, 2008

Issues with purging Client variables data store in ColdFusion

In short: CFMX seems not to do much regarding database purging of client variables. You'd probably need to do manual purge of data from these tables to avoid funny application errors (if your application uses client variables).

Here's the long story:

We have been using client variables on our CFMX7 project for quite some time. They are stored in a database (MSSQL). CF admin is configured to purge these at 48 hours intervals. Since it worked fine for many months I never bothered to check whether the purge actually happens until we've encountered some rather weird problems recently.

The basic issue was that emails that CF would generate would no longer be sent. A day before they were running just fine. Looking at exception.log file I'd notice that there were lots of Smtp-related authentication problems of some sort. Mail sending process reads client-scoped variables for username/password to be used for email sending (they are read from the application database and then stored in client variables while this process runs). I've verified that the mail server and the application settings are all good - they use proper username/passwords and the rest of it.

The log file of the application nicely says that the emails are generated for sending and they are queued for sending. On the mail server side I see it complains that the attempt to send the mail is not authenticated and we need to reconfigure our client sending application.

Finally, when I looked at the [CFMX ROOT]/Mail/Undelivr folder where unsent mails are stored, I spotted (at the top of the mail file) the username+password+mail server string that typically looks like: username:password@servername:port. In the case of undelivered mails they were showing some really old email settings + password (I later verified that the password changed for this other email account which would explain why sending stuff via that email account would not be properly authenticated).

The only place where these values come from are the client variables and it dawned on me that we could be using some old client variables once I saw that the database tables where they are stored had around 90,000 rows in them. Looked like no one was purging the database and I'm guessing the unique identifier (CFTOKEN) must have been duplicated at a later time and ended-up reading values from way before.

After deleting all the records from the client database store things started working again.