Small Business Success Expo

Last week we were at the Small Business Success Expo in Dayton, OH. It was a great time and we met a lot of great folks.

We presented a workshop called "Web Marketing Strategies". We got some great feed-back and wanted to pass the outline on to anyone else that may find it useful as well.

RefTagger Drupal Module

UPDATE: This module is now available on Drupal.org. http://drupal.org/project/reftagger

Earlier this year, Logos, the Bible software folks came out with a fantastic javascript-based tool called RefTagger that will transform every Bible reference on your site into a link complete with a tooltip showing the full verse referenced. The RefTagger homepage contains great instructions for including their javascript in your site and a dynamic form to configure the tool to your specifications.

Adding RefTagger to a Drupal site is easy enough, but what is easier than dropping in a module and turning it on! That's what Drupal's all about, so I spent a few hours developing a Drupal module for RefTagger that allows all the configuration to be done in the Drupal admin. No need to mess with templates or blocks or any code. Just drop in the module, turn it on, and configure as needed.

Drupal 5.x and 6.x version are provided below. If there is enough interest in the module, we can look at adding this as an official project on Drupal.org. Your comments are welcome, and feel free to let us know that you are using it.

Disabling the node page

Note: this post contains code for Drupal 5 - if you're using Drupal 6, you'll have to adapt it to get it to work (this page might be a good place to start).

The default homepage for a new Drupal site is the /node page that lists all content types that are marked to be promoted to the homepage. It's easy enough to change the homepage to a certain node (like a simple page) or a more customized homepage.

But something that has always bugged me is that the /node page is still just sitting out there. At any point an informed user can type in the /node path on any of my Drupal sites and see a listing of much of the content on the site. Of course, we haven't taken any time to style such a page to render nicely and it just seems unprofessional to have page sitting out there that will never be used.

Overriding Core Paths

Recently, we decided to start doing something about these default pages that weren't being used. For all of our sizable Drupal projects we create a module specific to the project that is used for gluing our overall project together. One of frequently used hooks used in a Drupal module is the hook_menu() function. It is used to tie callback functions to URL paths.

Usually hook_menu() is used to create new paths, but it can also be used to override the action tied to paths defined in Drupal core modules. The code below shows how we use this function to override the /node path to return a 404 not found page instead the default node list.

[NOTE]This code is Drupal 5

<?php
function sitename_menu($may_cache) {

 
$items = array ();
  if (
$may_cache) {
   
$items[] = array (
     
'path' => 'home',
     
'title' => t('Homepage'),
     
'callback' => 'sitename_home',
     
'access' => TRUE,
     
'type' => MENU_CALLBACK,
    );
  }
  else {
   
$items[] = array (
     
'path' => 'node',
     
'title' => t('Not Found'),
     
'callback' => 'drupal_not_found',
     
'type' => MENU_CALLBACK,
    );
  }
  return
$items;
}
?>

You'll notice 2 menu paths returned here. The first is a path for our homepage (/home) tied to the function sitename_home(). The second is an override of the /node path that is tied to the drupal_not_found() function. You will this override path is called when $may_cache is false. The hook_menu() function is called twice, first with $may_cache set to TRUE early in the bootstrap process and second with $may_cache set to FALSE later in the bootstrap process. Placing our override in the latter ensures that cached menu items will be overridden successfully. If you are overriding non-cached menu items you may need to got the extra step of making sure your module menu items are invoked later in the bootstrap process by changing the weight of your module in the system table of your database.

Organizing Drupal Modules

When setting up a new Drupal project it’s important to organize your code, especially when working with teams. In Drupal, most or your core code will be in the form of modules. We typically organize our modules into 3 folders:

/sites/all/modules/contrib/
/sites/all/modules/custom/
/sites/all/modules/dev/

Modules go in the sites folder

First, it’s important to put any Drupal modules that are not in core into the sites/ directory. This will make any Drupal core upgrades much easier and since you shouldn’t be modifying any Drupal core files, all your coding will be in the sites/all/ folder.

Separate contributed, custom and development modules

You’ll also notice that we separate contributed modules (those found on Drupal.org), custom modules (written specifically for this project), and development modules (contrib modules not used in a production enviornment).

It is highly unlikely that you will ever modify a contributed module, so keeping custom modules in their own folder makes searching for code to edit much easier. If you do make changes to a contributed module, I would suggest putting it in the custom/ folder so you know not to upgrade that module without extracting your patches.

Why a separate folder for development modules

Development modules like devel, simpletest, trace and update_status should not be used on production or staging servers. These modules are typically only used in each developers individual working copy. This being the case, we do not want to include them in our SVN repository. In SVN you can mark folder or files to be ignored by the SVN client. I used to put all development modules in the contrib/ folder and set an ignore property on each individually, then realized if I grouped them all in a single folder, I could simply ignore that one folder making SVN management much easier.

jQuery Enter to Tab

An intranet web application we've been working on involves a lot of data entry. One feature that helps when typing in a large number of small numbers is being able to use the enter key like the tab key. Instead of tabbing over to the next field on the list, you simply press enter. It's a small difference, but it's easier to reach, people are used to it from Excel, and over enough time, it makes a big difference.

The application used jQuery, and there didn't seem to be any canned solutions from that camp. We knew it had to be possible, so we did a little searching, and sure enough, found a script on "internet.com." It's rather messy, and isn't the most elegant solution, but it tells us that we simply need to capture the enter key press event, find the text box, and give it focus.

So let's go through the process of converting that basic flow to jQuery. We could have made this a jQuery extention, and probably will at some point, but in the interest of time, it's just straight jQuery code.

We start out by creating a function to run when the script is loaded, and finding all <input> boxes that have the .data-entry class (the class gave us better control over which text boxes this functionality should work with).

textboxes = $("input.data-entry");

Next, we capture the enter key (and all other keys) with the "keyDown" event. Since this is jQuery, event will be captured on all the textboxes we just found, without need a loop.

$(textboxes).keydown (checkForEnter);

Now we need to check and see if it was the enter key that was pressed, and if so, perform our tabbing functionality. We define the function "checkForEnter", accepting the "event" argument. That argument gives us the keyCode for the key that was pressed - the enter key is #13.

function checkForEnter (event) {
  if (event.keyCode == 13) {
 
  } 
}

If that statement returns true, we know the enter key was pressed, and that we need to give focus to the next textbox. We need to get at our array of textboxes from the initialization function, so we'll define the "textboxes" variable at the very top of our page, outside of any functions. Now that we have the array, let's find out where the current textbox (sent to us in "this") is in it using the "index" function.

currentBoxNumber = textboxes.index(this);

So we can find out what the next box is by simply adding one to that number. Let's make sure we're not at the end of the array first.

if (textboxes[currentBoxNumber + 1] != null) {

If that works, now we simply change the focus, and select the text inside it.

nextBox = textboxes[currentBoxNumber + 1]
nextBox.focus();
nextBox.select();

Our functionality is in place. Before we're finished with the function, however, we need to stop the enter key from doing what it normally does, which is submitting the form. Normally, this is a bit of a pain to do reliably, but jQuery gives us the "preventDefault" function. Between that and returning false with our function, the form won't submit automatically.

event.preventDefault();
return false;

Note that when the user gets to the end of all the textboxes, the above code doesn't run, so the default functionality does work and the form is submitted automatically when they reach the end.

Trying it out, we found out that it only worked in Safari. Not good. We looked back to the original script and noticed browser detection in use. Apparently that _was_ important - no worry, however, jQuery has browser checking build in, and we can simply examine the $.browser property to switch between the slightly different events.

if ($.browser.mozilla) {
$(textboxes).keypress (checkForEnter);
} else {
$(textboxes).keydown (checkForEnter);
}

And that's it - now we can move from box to box using the enter key.

XAMPP Apache Service Conflict With Skype

I have XAMPP installed on my PC Laptop for the occasional development I do when on the road. A while back I was trying to startup Apache through the XAMPP Control Panel, and Apache would attempt to start, but would always fail.

I finally realized that the culprit was Skype (must be a port collision). I just closed Skype and Apache started right up. As soon as Apache is running, I can open Skype back.

Hope this helps for any of you googling this issue online.

Thunderbird Quickmove Extension

I recently installed Stuffit Expander on my Windows machine to open a .sit file from a client, and during the installation it wiped out all of my application settings including all my email settings for Thunderbird (thanks Stuffit, I didn't really have anything else to do that day anyway).

I only use a handful of extensions for Thunderbird but had a heck of a time finding one that I realized I just couldn't do without. The extension is called TB Quickmove and is used for quickly moving emails from your inbox into other folders. I am persevering to implement David Allen's GTD productivity methods and this extension make that effortless.

I simply setup 4 shortcuts for moving my emails to 4 folders.

  1. Archive - for reference only
  2. Todo - actionable items to be done today or tomorrow
  3. Someday - actionable items to be done when I get the chance or it requires someone elses action first
  4. Receipts - I've got to keep all my email receipts handy in the case the KGB... I mean IRS... ever comes knocking on my door.

The idea is to always keep you inbox empty and sort your actionable items from the rest so you can stay sane.

TB Quickmove can be found on Mozilla's Addons page, but only an old version that is not compatible with Thunderbird 2.0.

I finally found TB Quickmove 0.4.3 on an obscure Spanish site, but to help anyone else who might be interested, I'm posting it here for download.

Download TB Quickmove 0.4.3

PHP Meetup - Drupal Basics

Last night I gave a presentation on the basics of Drupal at the PHP Meetup group in Columbus Ohio. I am attaching my very simple Powerpoint outline to this page and will list here links to some of the modules we discussed during the presentation.

The CCK module (Content Construction Kit) is one of the most popular Drupal modules and allows you to easily create complex content types without the need to do any programming.

The Views module allows you to create various views for your content without any programming. Other modules can expose their fields to the Views module for filtering or sorting purposes. For example, a popular module is the VotingAPI module which allows users to rate content. You could create a page that will list only the Blog posts that have been rated and sort them from the highest rated to the lowest rated. The Views module can be very confusing at first, but is very powerful once you get the hang of it.

The Excerpt module is a very simple module that allows you to explicitly create teasers for your content. Out-of-the-box Drupal creates teasers automatically by truncating your Body text. With the Excerpt module you can paste in exactly what you want to appear in your teaser, even if it is not a part of your body text.

The PathAuto module extends the usefulness of URL aliases (AKA the Path module which is a part of Drupal core). Instead of manually creating your URL alias for each content page you create, the PathAuto module allows you to create logical rules to create these aliases automatically. You can even have PathAuto update the URL aliases for content already on your site with just a single click.

The Zen Theme was created to provide a highly flexible starting point for creating your own custom theme design using CSS. It adds all kinds of helpful CSS classes to your HTML template so that you can style the front page or various node types differently by just modifying your stylesheets.

The Drupal Dojo provides a venue for Drupal developers to share their expertise by doing live video presentations on a variety of topics. Most of these presentations are archived as screencasts so you can browse and view topics of interest to you. Most people are visual learners to watching some of these screencasts could really boost your Drupal skills in a short amount of time.

Using Launchy to Launch Web Projects

I’ve been using Launchy, a free application launcher, for almost a year now, and cannot rave enough about it’s ability to increase my productivity as a developer. Launchy is a tiny app that runs in the background, ready to launch not only your application, but also documents, media, batch files, help files, websites, file directories and more. You simply type Alt+Space (or Ctl+Space) and up pops a tiny window that allows you to type in the program, file or directory that you want to open. Long gone are the days of using my mouse to browse through the Program directory in the Start menu, or cramming 20 of my most frequently used application in the Quick Launch area on the taskbar.

I thought I would share some of the specific ways that I use Launchy on a daily basis to launch projects in my development work.

Launching Projects

When I work on a web project, I typically need 3 applications: SciTE (my text editor), Firefox, and one or more File Explorer windows. Not only can I load SciTE and Firefox, but I can open them with all the frequently used files and web pages for that project already opened. Here’s how it works:

Create A Simple Batch file

Launchy can be used to launch any executable file, including batch files. If you are not familiar with how batch files work, check out these tutorials. In my case, I have a directory that contains all my batch files for launching projects. I’ll create a new one called dev-rambeck.bat. I prepend the word “dev” to all my projects batch files to distinguish them from other files that might have the project name in them. The below batch code will launch our applications with command-line parameters to open the needed files or web pages for our project.

:: Open Session for the Rambeck Group Project
::
@ECHO ON

:: Launch Firefox with the following tabs: Basecamp project, local version, remote version and the Drupal API
"C:\Program Files\Mozilla Firefox\firefox.exe" -new-window "http://rambeck.grouphub.com/projects/885139/project/
log|http://rg.ubuntu/|http://www.rambeck.com/|
http://api.drupal.org/"

:: Launch our project directories in Windows Explorer
explorer.exe /n,Z:\rg\
explorer.exe /n,F:\Production\rambeck.com

:: Open SciTE with our saved project session loaded
START "The Datevine" /MAX "C:\Program Files\SciTE\SciTE.exe" "-loadsession:F:/Sessions/rg.ses"

Customize Settings in Launchy

Now we will setup Launchy to index our directory of batch files in our chosen directory.

Right click the Launchy tab and click Directories.

Add the directory that contains our batch files and add the .bat file extension.

Refresh our index.

Now when you need to launch your project simply type Alt+Space then the first few characters of our batch file name.