How hard can it be to sell bait

Last week I took my family to the lake to go fishing. We stopped by the hut at the dock to see what kind of boats they had to rent and to buy some worms for bait.

There was no one else there but me and the guy manning the place. I asked him about the boats and he rattled off a couple kinds they had without so much as looking me in the eye. He looked like I was bothering him.

So I left. None of the boats where going to work for us and I wasn’t about to buy bait from this guy. There is just no excuse for not making someone feel like they just made your day by talking to you. I don’t care if you are selling bait, boats, or yachts.

My wife asked what happened and I told her the guy didn’t look me in the eye. (She didn’t like it that I left but knew I wasn’t going back.) Luckily the park visitor center had some bait and nice young lady that looked happy to see me.

I keep telling my wife that if this happens again at a drive-up window I’m going to say “Sorry, I didn’t mean to bother you” and drive off and go to another place across the street.

By the way we didn’t catch any fish that day. Not even a bite.

Must see presentation for entreprenuers

I saw this presentation from David Heinemeier Hansson last year at the 2008 Startup School. Out of all the big names that presented there his was the one that hit home with so many, including me.

The idea was simple. Run your company like a business.
1. Make a product
2. Set a price
3. Make a profit

There are a dozen other gems in this presentation and I would recommend it to anyone interested in starting a WebApp business (or any business for that matter.)

WARNING: Unfortunately David drops the F-bomb several times during the presentation.

The secret to making money online

<div><a href='http://www.omnisio.com'>Share and annotate your videos</a> with Omnisio!</div>


Activity Module

One of the most important features of a social network is that page that lets you see what people you know are doing. On Facebook, the first page you see when you login is the News Feed – a list of everything that your friends have done recently.

Activity is a Drupal module for creating a page like that. It lets you see when people sign up, make comments, post content, join groups (with Organic Groups), or become friends (with User Relationships).

Once you download the module (along with the required Tokens module, which lets you change the activity messages), enable it and its submodules (comment activity, node activity, etc). You’ll probably want to visit the Drupal permissions page and give authenticated users permission to view activity and comment on it as well.

In order to try it out, logout as admin (Activity doesn’t log any actions by the admin user), login as a regular user, and make some posts and comments. Then you can visit the /activity page to see your activity.

If you don’t want that many activities showing up, you can visit the Activity Settings page (/admin/settings/activity) and disable some types of messages. You can also change their wording to fit with your site better.

Adding User Pictures to the Messages

Adding user pictures to messages is simple. If you haven’t enabled user pictures on the site yet, go to User Settings (/admin/user/settings) and turn them on. Then go to Activity Settings (/admin/settings/activity) for Nodes or Comments to edit the messages. You can now use [author-picture] in the message text to insert the user picture.

Adding the Node Teaser to the Message

For MyFishingFacts, we wanted users to be able to see new content right inside of the activity stream. Since Activity doesn’t provide a way to do this, we re-themed the message in our code, replacing it with the node teaser.

You can modify the messages in your theme’s template.php file by overriding theme_activity. In this example, we’ll check for a user creating a page, load the teaser, and insert it into the message.

<?php
function phptemplate_activity($message, $item) {  

 
// Check if this is a page node being created.
 
if ($item['type''page' && $item['operation''insert') {
   
   
// Load the node
   
$node = node_load(array('nid' => $item['data']['node-id']));
       
node_view($node, true, false, false);

        
// Add the teaser text to the activity message
   
$message .= '<em>'. $node->teaser .'</em>';
    }
   
    return
$message;
}
?>

Put that in your template.php file, clear the Drupal cache, and node teasers should start showing up in the activity stream when you create a new page.

You can also use this method for things like inserting user pictures from the Content Profile module, or changing the layout of the activities table (using theme_activity_table).

Menu and Cobalt

Developing a Drupal site involves jumping back and forth to alot of different pages, and with the default Garland theme, it can get a little old. That’s why the first thing we do when starting a new site is to install the Admin Menu module.

Admin Menu


The Admin Menu module adds a translucent dropdown menu to the top of the page, allowing you to access any admin link with one click. Its only visible to admin users, and even offers some extra links to flush caches and run updates, under the drupal icon.

Cobalt – Quicksilver for Drupal

Another module that helps with getting around your Drupal site is Cobalt. It’s like Launchy or Quicksilver for Drupal. You just press Ctrl+Space, and start typing the name of the page you want to go to. Cobalt brings up the closest match, and you just hit enter to go to the page.

(Cobalt requires either Firefox 3 or Safari, and Google Gears)

These modules really speed things up when developing or administering a Drupal site. You can download them from Drupal.org at these links:

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.