Browse more articles about web development

Better Control Over Drupal Modules Running Order

If you've been a Drupal developer for a while you know that modules run according to the value of the weight field in the system table. You can update this table using an install file so your module runs exactly when you need it. This is what community tags does to run after tagadelic and the method I've used to make sure some form_alter code runs after everything else when I needed to modify event forms.

But the change in the module's weight affect all the hooks in it. What happens if you want to control the runnning order in a per hook basis? I needed to do this to run some nodeapi operations in a custom module before event_nodeapi and form_alter stuff, in the same module, after event_form_alter. Believe me, you may need this some time (unless you opt for using before and after modules).

This idea inspired me to code a bit and individually override the operations in event_nodeapi with some code in my custom module. It worked nice. Now I can run operations load and view in event_nodeapi and override insert and update with code from my module.

The concept is quite simple. I added an event_nodeapi_overriders table with two fields: module and op. My module just needs to use its install file to insert values in this table. If I want mymodule to provide overriders for insert and update operations in event_nodeapi I should add the following module/op pairs to the table: mymodule/insert and mymodule/update.

Then I added a few select boxes to show the values from event_nodeapi_overriders table in the event settings page where I can choose which module should override which operation and used some conditions with variable_get() in event_nodeapi and mymodule_nodeapi to decide which code to run.

Perhaps the concept could be extended to eventually have a matrix of all hooks in all modules or a hook_weight parameter to allow developers to decide the running order. Or maybe is too much overhead?

Anyway, this was some kind of experiment and even if after talking to Gerhard we decided to update the event module to use Form API in the submission process instead, the idea of having better control over hooks running order could deserve some more thinking.

jCalendar: A jQuery Based Calendar Goes The Drupal Way

While working on a Drupal based project where we needed a date picker for our forms, my good friend, and old drupaler, Gerhard, recommended me taking a look at Ted Serbinski's jCalendar, a jQuery based calendar.

I started hacking a quick version of jcalendar.module using some ideas of the very helpful jscalendar.module, which uses the jsCalendar javascript library, like using a special CSS class called via #attribute in the form element array to convert textfields into jcalendar enabled.

The jcalendar module uses hook_form_alter() to look for textfields with the add-jcalendar class, I had to use something different than jcalendar which is used later on the generated form elements by the jQuery code.

This is a sample of how to enable jcalendar from one of your textfields once you've installed the module:

// Define a range of years to show
$years = array('0' => 0) + drupal_map_assoc(range(2007,2020));

$plot_dates = array(
'2008-11-03',
'2007-04-01',
'2007-06-11',
); // YYYY-MM-DD

$form['firstdate'] = array(
'#type' => 'textfield',
'#title' => 'The first date',
'#default_value' => '2007-09-29',
'#weight' => -20,
'#attributes' => array('class' => 'add-jcalendar'),
'#jcalendar_year_range' => $years,
'#jcalendar_plot_dates' => $plot_dates,
'#description' => t('YYYY-MM-DD'),
);</code>

The #jcalendar_year_range custom property allows you to provide a range of years, look at the associative array $years, to show in the calendar. If the property is not passed jcalendar.module defaults to the current year plus 20.

#jcalendar_plot_dates, which is not working yet, should allow passing an array of dates to the calendar to plot. I'm not sure if this should be the job of the Drupal module or jCalendar's jQuery code.

This first version of jcalendar.module works, it converts your textfield to three select boxes, day, month, year, and a nice table based date picker, easy to theme via CSS, and once you have the right date you can do whatever you want with it. The original textfield is removed later so your form validation and processing functions should consider if jcalendar is enabled (a simple if (module_exists('jcalendar'))) to handle form elements in a different way.

So, what's the catch? Unfortunately, after I finished my tests and added a second date textfield to my form, I needed to choose a start date and an end date, then I realized jcalendar is duplicating its job for each textfield, it seems the jQuery code only supports one jcalendar per web page. I still need to review the comments in Ted's blog and talk to him to confirm this but I thought it would be a good idea posting what I've done so far in case any of you want to play with the code and improve it.

So here it is, download jcalendar.module and let us know if you get a way to work with more than one calendar per page or alternate ways to alter the original forms.

Hit the Road with Ubuntu and a System76 Pangolin

Just a few days ago I wrote about Dell, Ubuntu and Linux conquering the world in 2007. Today I'm the proud owner of a brand new System76 Pangolin.

System76 Pangolin

Her name is Amber (she joins Flenser, Woodcarver, Alpha and Manny) and she's a nice Core 2 Duo T7200 laptop with a gorgeous 15.4" wide screen and 2 Gb. of RAM running Ubuntu Feisty Fawn.

Ordering from System76 was a great experience, I was able to pay directly from my checking account, I got quick and friendly email replies from Carl and Erik to all my questions before and after buying and they even upgraded my shipping to 2nd day UPS for free.

The laptop works perfectly out of the box. I turned it on, followed Ubuntu's simple wizard to create my user account, which runs the first time you boot, and it detected my home Wi-Fi network instantly. I was browsing the Net in just a couple of minutes. I'll be telling you more about sweet Amber in a few days, she's now my main computer.

I plan on buying another laptop later this year and even if I'd like to try a Linux Dell I'm quite happy with my first System76 and could go for a Serval next.

Anyway, if you're on the market for a Linux laptop, System76 is highly recommended. Meanwhile, why don't you meet Amber on Flickr?

Launch Offer: Reseller Web Hosting Plans For Web Developers

Update: Offer no longer available. No more accouns accepted in Alpha.

Alpha, the first server in the new era of web hosting services at Ventanazul, is ready. And even if I haven't finished with the official details I can already announce our first offer.

We have reseller plans only for web developers, exactly what you need for your projects and your clients' sites.

This is the offer: The first 25 clients can get their reseller plans (details below) for just US$ 20 for the first two months, that's just US$ 10 per month, the regular price will be US$ 35 per month after that.

This unique reseller plan includes:

  • 10 Gb. of disk space.
  • 100 Gb. of monthly bandwidth.
  • Unlimited domains, subdomains, mailing lists, email accounts and databases.
  • cPanel and WHM 11.
  • MySQL 5.
  • PHP 5.2.
  • PostgreSQL 7.4.

The service is only for web developers, that way we'll be able to focus just on their needs, we won't deal with final users.

Alpha is an AMD Dual Opteron 252 with 4Gb. RAM and SCSI drives, it's colocated at LinkLINE Datacenter in Los Angeles, California, and has redundant connections to many reliable ISP's in the US.

All our clients will receive tech support via Ventanazul Helpdesk.

To get your reseller web hosting plan just contact me. You can pay with credit card, Paypal or Western Union.

I'll publish more details soon and I'm already working to be able to host Ruby on Rails and Django applications.

Remember, US$ 20 for two months, just for the 25 first clients.

OLPC Will Not Use Windows

"We are a free and open source shop. We have no one from OLPC working with Microsoft on developing a Windows platform for the XO. MS doesn't get any special treatment from OLPC."

That's what Walter Bender, president of Software and Content at OLPC, told to Ken Fisher at Ars Technica.

They really scared us, uh?

2007: The Year For Linux on Laptops

A few months ago I knew about a cool small company: System 76, embraced by many in the Linux GNU community because they were one of the few selling desktops and, specially, laptops with Linux preinstalled.

Running Linux on laptops has always been problematic because many hardware manufacturers are too reserved with their software, hence the development of open source drivers is harder or sometimes just impossible.

Even if any modern Linux distribution includes many drivers and can detect a lot of current hardware, expecting full wireless, video and multimedia support in laptops was often some kind of a gamble.

But that's started to change and System76 has been one of the best examples of companies providing open hardware and writing their own drivers for Linux. The company even has a forum hosted by Ubuntu, the distribution pre-installed on all their machines.

And there were so many great testimonials of people using System76's laptops with Ubuntu that I couldn't resist and ordered mine a few days ago, it should be arriving next week.

But that's not all, the biggest computer manufacturer in the world, Dell, started listening to their users some months ago, they asked for, actually screamed for, you guessed, Linux. After a few weeks Dell told the world the good news, they would start offering desktops and laptops with Linux pre-installed, goodbye Windows tax!

Yesterday, May 1st, 2007, we reached what I think is the tipping point of Linux:
Dell announced a deal with Ubuntu, which will be preinstalled in their Linux desktops and laptops.

There's no doubt that this move will make the number of Linux users skyrocket.

The world is changing, there's no coming back, and you know what? Even if the change started many years ago thanks to a few great minds and now we are millions of Linux geeks around the globe, Ubuntu, System76 and Dell have already earned their place as very important players.

And of course, I already have a Linux Dell laptop in my wishlist.

Newsletter Checkbox in Campaign Monitor Blog

I just got a message from David Greiner telling me he had posted about Newsletter Checkbox in Campaign Monitor's blog.

Newsletter Checkbox is a very simple module but gets the job done and has received good feedback from many users so I'm looking for the time to improve it and release a new version soon.

If you have some suggestion please add your comments.

Redirecting Users After Submitting a Form in Drupal 5

If you create web sites based on Drupal you know that controlling forms, how they are themed, validated and processed, is a highly valued skill for any serious developer. This article will discuss a few ways to redirect your users to another page after they've submitted a form, overriding Drupal's defaults.

Drupal 5 provides developers with a Forms API, a clever way to craft HTML forms using logical and extremely flexible arrays.

Drupal Forms API also allows developers to theme every little detail of a form. For simpler cases using the #prefix and #suffix properties can be enough, but if you need extreme power, and sooner or later you will, then you can use forms-exclusive theme functions and drupal_render().

I'll explain the basics of Forms API in a future article, if you can't wait I suggest taking a look at the Forms API Quickstart Guide, for now let's focus in redirecting users after form submission in Drupal.

In the beginning was the form function

Every form in Drupal starts as a function that creates an array, usually named $form. Then the function's name is passed to drupal_get_form().

As every function in a module, a form creation function should start with the name of the module, for example:

boogeeks_notify_form()

First, this assumes my module is named boogeeks, the first word in my function's name. I'm separating each word with an underscore and I suggest you do the same.

Second, I've chosen the verb notify to have a clear idea of what the form will do. I used something like this in a recent project for hacking a form where users opt-in to receive email alerts.

Third, I added form, helpful when you have many functions in your module and need a quick way to tell apart the ones creating forms.

Notice that the only requirement for naming your form function is the first one, starting with your module name, the other two are my suggestions for cleaner and easier to understand code.

Theme, validate and submit

Now that we have boogeeks_notify_form() taking charge of form creation we need a few additional functions, all of them named based on the original.

  • theme_boogeeks_notify_form(): Will take care of the looks of your form, this is the theming function.
  • boogeeks_notify_form_validate(): Will check if user entered information that validates according to rules established here.
  • boogeeks_notify_form_submit(): Will process the form and redirect the user to other page.

For this article's purposes we are interested in the third function, the one ending in _submit.

Where to go now?

Every form submission function, boogeeks_notify_form_submit() in our example, needs to return a value, this value is the url where the user will go after successfully submitting a form.

So, if you want to thank your user after opting in to receive email alerts then you can finish boogeeks_notify_form_submit() with something like:

return 'thankyou-for-subscribing';

That's the url of some page with the usual thank-you-we-love-you-dear-visitor stuff.

All Drupal provided forms have pre-defined values in their submit functions. Two well known and often used ones are user_login() and user_register(), used to create new accounts, both are created in user.module.

How to override default form redirection in Drupal

There are two ways to override default form redirection, with a destination parameter in a url and using the #redirect property of Forms API. After some testing, I know this could be obvious to hardcore Drupal developers but it's still useful for new comers, I determined the redirection processing flow is as follows:

  • The url returned from the form submit function,
  • which is overriden by #redirect,
  • which is overriden by destination passed in the url.

You'll need to modify a form using hook_form_alter() to use #redirect.

If you want to work with destination you'll have to include it in the url used to call the page showing your form.

Now you can take your users whenever you want after submitting a form, no matter if it's a Drupal provided form or one that you've coded.

Bonus: got a headache with redirection after registration?

Yeah, me too, at least until finding a small bug in user.module, maybe that's your case.

How To Find Next Result When Searching With Firebug

There's no need to add reasons why you should be using Firebug while creating your web applications, specially if you are into DOM scripting. I'd just like to add a small detail that we may miss when using Firebug to search in our code: how to find the next result?

It's simple, just press Enter; for previous results use Shift + Enter.

Many of us are used to F3 and Shift + F3 in Firefox but may get a little confused when we start to search with Firebug.

PHP Class For Authorize.net Automated Recurring Billing Payments API

John Conde was the first one coding a PHP class for the recently launched Authorize.net Automated Recurring Payments API (ARB), he was also the guy who told me this so much wanted API was coming a few weeks ago.

His code was PHP 5 only so I made a few changes a have just finished my first version, which runs in both PHP 5 and PHP 4.

Download arb2.zip and let me know how it works for you.

I'm still testing and could find some small bugs which need to some polish, I'll update this post and code if needed.

You will need to have an ARB enabled Authorize.net account, of course.

You can test the class with the following sample code:

<?php
  require_once("AuthnetARB.class.php");

$login = 'cnpdev4289'; $transkey = 'SR2P8g4jdEn7vFLQ'; $test = TRUE;

$arb = new AuthnetARB($login, $transkey, $test);

$arb->setParameter('interval_length', 1); $arb->setParameter('interval_unit', 'months'); $arb->setParameter('startDate', date("Y-m-d")); $arb->setParameter('totalOccurrences', 12); $arb->setParameter('trialOccurrences', 0); $arb->setParameter('trialAmount', 0.00);

$arb->setParameter('amount', 1.00); $arb->setParameter('refId', 15); $arb->setParameter('cardNumber', '5424000000000015'); $arb->setParameter('expirationDate', '2009-05');

$arb->setParameter('firstName', 'Joe'); $arb->setParameter('lastName', 'Doe'); $arb->setParameter('address', 'Casa 1872'); $arb->setParameter('city', 'City'); $arb->setParameter('state', 'FL'); $arb->setParameter('zip', '33619'); $arb->setParameter('country', 'us');

$arb->setParameter('subscrName', 'The Test Account'); $arb->createAccount();

echo 'isSuccessful: ' .$arb->isSuccessful() . '<br />';

if ($arb->isSuccessful()) { echo 'cool, it worked! <br />'; } else { echo 'error in payment <br />'; }

echo 'isError: ' .$arb->isError() . '<br />'; echo 'getSubscriberID: ' .$arb->getSubscriberID() . '<br />'; echo 'getResponse: ' .$arb->getResponse() . '<br />'; echo 'getResultCode:' .$arb->getResultCode() . '<br />'; echo 'getString: ' .$arb->getString() . '<br />'; echo 'getRawResponse: ' .$arb->getRawResponse() . '<br />'; ?>

Enjoy.