Drupal Modules

I recently completed a project for a customer that afforded me the opportunity to dive into Drupal on better terms. On my first introduction to the software several years back (and Joomla for that matter) I determined I would be able to use my cross-browser theming skills more effectively if I chose WordPress as a base CMS.

This time around I was able to spend a considerable chunk of time reading the documentation (especially around the Theming Guide) and examples on other’s sites. Some topics online seemed incomplete so I did end up reviewing some chapters in a few books on Drupal 7 in particular. In my mind it seems like the developers of WordPress and Drupal are migrating toward each other in feature sets, but delivering solutions from different perspectives. WordPress appears to be geared more toward out of the box social publishing, with an easy installer, and an easy updater. It can be pushed in almost any direction, but doesn’t seem to be the goal of the software creators. Drupal on the other hand feels more like a collection of building blocks for a web database. While Drupal has fields, blocks, regions, views, and fine grained permissions with roles… URL aliases require a module to automate their selection. There’s even a module to hook up Filemaker Pro and Drupal.

Back to the modules:

I utilized the following:

For spam free contact forms (almost):

For easy page editing:

For dynamic content:

For development and style:

Post via script

I just whipped up some PHP and threw it in the root of a brand new WordPress 2.9.2 install:

<?php
include_once('wp-config.php');
include_once('wp-load.php');
include_once('wp-includes/wp-db.php');
 
$my_post = array();
$my_post['post_title'] = 'My post';
$my_post['post_content'] = 'This is my post.';
$my_post['post_status'] = 'publish';
$my_post['post_author'] = 1;
 
wp_insert_post( $my_post );
?>

Voila. The post appeared when navigating to the .php file.

See: http://codex.wordpress.org/Function_Reference/wp_insert_post.

For more creativity, get into the database: http://codex.wordpress.org/Function_Reference/wpdb_Class

iUI and the iPhone

My employer recently purchased a set of iPhones from AT&T for those in the workforce that have a need for such a thing. One of our first priorities was getting our rolodex accessible (read only) from the road. The main problem is that the data is in a restrictive, old school database. Until we can find time to move it into a more open platform I did the following:

  • Set up a routine for dumping the rolodex into a flat xml file monthly
  • Coded the searching of the the data using PHP and SimpleXML
  • Coded the cookie management (sessions) and most important, a “remember me” feature (because Safari doesn’t remember passwords on the iPhone)
  • Found this lovely library for creating iPhone compatible user intarfaces via the web (read, looks like a native app): iUI (User Interface Library for Safari development on iPhone)

Checkout the screenshots:

Login  Search  Results  Info

It’s pretty cool, because if the contact in the rolodex has a telephone number, address, email address, or webpage associated with them, the data will be displayed and linked to iPhone friendly functions… For example, the telephone number, when clicked on will provide a call button. An address clicked on will link to the built in maps app. Email links open up directly in the built in mail app. Finally, of course, webpages openup in Safari.

So here’s for getting the world more connected… one app at at time.

Goldfish and forged spam

Recently I had the goldfish autoresponder (vacation responses / email auto reply) setup for a user for about one week. Checking their inbox about half way through this time period revealed it having over 47,000 unread messages. What was happening is that spammers were sending email with forged headers. Have you ever received a spam message that apparently has been sent from yourself?

I suppose it can be accomplished via a variety of ways, however I was able to reproduce this particular issue via generating one of these tasty spam mails via the following (all internally of course… I am not a spammer):

telnet smtp.example.com 25

and then issuing these commands:

ehlo workstation.example.com
mail from:
rcpt to:
data
From: 
To: 
Subject: testing a loop
Message text
.
quit

Notice the mail is being sent to the user… and from the user…

  1. The server would receive the email addressed to emailuser@example.com
  2. goldfish would parse the message and notice it needed to respond to… emailuser@example.com
  3. The mail was delivered
  4. Rinse, wash, repeat

The source of the message looked something like this:

root@server:/var/local/vmail/example.com/emailuser# cat new/1220557062.P6882Q0M652544.server,S=1082
Return-Path: 
Delivered-To: emailuser@example.com
Received: from localhost (localhost [127.0.0.1]) by server.example.com (Postfix) with ESMTP id 923E9185F7 for ; Thu, 4 Sep 2008 15:37:42 -0400 (EDT)
X-Virus-Scanned: Debian amavisd-new at server.example.com
Received: from server.example.com ([127.0.0.1]) by localhost (server.example.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id q3tScOaitRoS for ; Thu, 4 Sep 2008 15:37:42 -0400 (EDT)
Received: from workstation.example.com (workstation.example.com [xx.xxx.xxx.xx]) by server.example.com (Postfix) with ESMTP id B89E1185EB for ; Thu, 4 Sep 2008 15:37:25 -0400 (EDT)
From: 
To: 
Subject: testing a loop
Message-Id: 
Date: Thu, 4 Sep 2008 15:37:25 -0400 (EDT)
Message text

and to prevent goldfish from causing havoc on the particular inbox (or mitigating what was already done… and preventing future issues), I added the following PHP:

//strip the line break from $address for checks
$addressCheck = substr($address,0,strlen($address)-1);
if ($addressCheck==$email)
{
	$log-&gt;addLine("Email address from autoresponder table  
	is the same as the intended recipient! Not sending the 
	mail!");
	break;
}

just before letting the mail being sent…

mail($address, $subject, $message, $headers);

I love goldfish. It’s like a do it yourself autoresponder! Keep in mind, this code works on Version 002, patch level 1, but could very well not work on any future releases (there have been indications that a newer version is in the works). Maybe I will post a few more tweaks in upcoming posts if the latest stable stays around for much longer.

Autoresponders – a loop

This past week I encountered an interesting situation between two web applications I work with regularly. The first is the goldfish autoresponder for postfix. The second is RT: Request Tracker.

The situation went something like this:

  • I had a ticket in RT that I wanted to resolve.
  • I set the form to cc the comment to a group of users who were interested in the details of the resolution.
  • I applied the ticket in RT.
  • RT cc’ed the users about the resolution.
  • One of the users had an autoresponder setup using goldfish, so it emailed back to RT about the user’s absence.
  • RT opened a ticket with the content of the email from the autoresponder, faithfully attaching the subject explaining the user’s absence.
  • RT then responded to the user’s email account about the new ticket’s creation.
  • Again goldfish autoresponds to a message from RT, creating a loop.

Every five minutes a new ticket was being created in RT because of goldfish’s configuration and cron job being set to run that often. I caught the cycle happening when the incoming queue of tickets had three or four created with their subjects typical of those created by an autoresponder.

reCAPTCHA plugin error messages

By default, on two of my installs of reCAPTCHA Plugin for WordPress – v2.7, I receive no error message if the captcha is entered incorrectly… the user of the weblog is to assume that they made a mistake I guess…

That is until I wrote this script to check for error messages being passed around on the URL by the plugin:

if (window.location.search != "") {
	var searchArray = window.location.search.split("&amp;");
	for (var i=0; i &lt; searchArray.length; i++) {
		var searchSubArray = searchArray[i].split(&quot;=&quot;);
		if (searchSubArray[0] == &quot;rerror&quot; &amp;&amp; 
		searchSubArray[1] == &quot;incorrect-captcha-sol&quot;) {
			document.write(&quot;<p>The two words in the picture were 
			typed incorrectly.</p>");
		}				
	}
}

Just drop that snippet into your “comments.php” template. When the captcha is filled in incorrectly, the plugin adds “rerror=incorrect-captcha-sol” to the url. This checks for that string, and if found, writes an error message in red.

reCAPTCHA plugin styling

Installing a CAPTCHA implementation on WordPress is quite easy when using the reCAPTCHA plugin, however the styling is hard coded into the main php file included.

Goto the installation directory, for example, “wp-content/plugins/recaptcha-wordpress-2.7″ and edit recaptcha.php. Look for the line that embeds the theme type and change it.

    var RecaptchaOptions = { theme : "red", tabindex : 5 };

I used the “clean” theme. There are other options and ideas for editing the code with css changes.

Motion Capture Sorting

When using Motion for a security camera setup, it can generate a lot of files… One installation I have setup has “Encode movies in real-time” enabled, so not only are there many .jpgs in the capture directory, but also .avis.

This can become quite tedious… copying and pasting them from a GUI every day. Consequently, I would let the files simply pile up for a few days before moving them around. Then it became a big job.

Scripting to the rescue. The following below is a PHP-CLI script, that when run from the motion capture directory will sort the various files into a nice little structure that allows for dropping the .avis into a play list for viewing, and keeps the .jpgs around for backup.

#!/usr/bin/php
<?php
 
$fileExtOne = ".jpg";
$fileExtTwo = ".avi";
$fileNameArray = array();
 
//dump filenames (ending in .jpg or .avi)
//in current directory into array
if ($handle = opendir(".")) {
    while (false !== ($file = readdir($handle))) {
        if (substr($file, -4) == $fileExtOne ||
            substr($file, -4) == $fileExtTwo) {
            array_push($fileNameArray, $file);
        }
    }
    closedir($handle);
}
 
//make multi-dimensional array with substrs of filename
for ($i=0; $i<count($fileNameArray); $i++) {
    $fileNameSplit = split("-",$fileNameArray[$i]);
    $dirArray[$i]["group"] = $fileNameSplit[0];
    $dirArray[$i]["year"] = substr($fileNameSplit[1], 0, 4);
    $dirArray[$i]["month"] = substr($fileNameSplit[1], 4, 2);
    $dirArray[$i]["day"] = substr($fileNameSplit[1], 6, 2);
    $dirArray[$i]["name"] = $fileNameArray[$i];
}
 
//create unique array with new directory names and mkdir them
for ($i=0; $i<count($dirArray); $i++) {
    $dirNameArray[$i] = $dirArray[$i]["year"]
            ."-". $dirArray[$i]["month"]
            ."-". $dirArray[$i]["day"]
            ."/". $dirArray[$i]["group"];
}
$uniqueNewDirNames = array_unique($dirNameArray);
foreach ($uniqueNewDirNames as $value) {
    $splitUniqueDirNames = split("/", $value);
    mkdir($splitUniqueDirNames[0], 0755);
    mkdir($splitUniqueDirNames[0]
        ."/". $splitUniqueDirNames[1], 0755);
}
 
//move files into the appropriate directories
for ($i=0; $i<count($dirArray); $i++) {
    if (substr($dirArray[$i]["name"], -4) == $fileExtOne) {
        rename($dirArray[$i]["name"], $dirArray[$i]["year"]
            ."-". $dirArray[$i]["month"]
            ."-". $dirArray[$i]["day"]
            ."/". $dirArray[$i]["group"]
            ."/". $dirArray[$i]["name"]);
    }
    if (substr($dirArray[$i]["name"], -4) == $fileExtTwo) {
        rename($dirArray[$i]["name"], $dirArray[$i]["year"]
            ."-". $dirArray[$i]["month"]
            ."-". $dirArray[$i]["day"]
            ."/". $dirArray[$i]["name"]);
    }
}
 
?>

Here’s what find returns after running the script on the capture directory full of sample .avis and .jpgs.

username@workstation:~/captures> find .
.
./2008-01-06
./2008-01-06/01
./2008-01-06/01/01-20080106145755-02.jpg
./2008-01-06/01/01-20080106145755-04.jpg
./2008-01-06/01/01-20080106145756-00.jpg
./2008-01-06/01/01-20080106145757-00.jpg
./2008-01-06/01/01-20080106145757-01.jpg
./2008-01-06/01/01-20080106145758-00.jpg
./2008-01-06/01/01-20080106145758-01.jpg
./2008-01-06/01/01-20080106145758-02.jpg
./2008-01-06/01/01-20080106145758-04.jpg
./2008-01-06/01/01-20080106145758-05.jpg
./2008-01-06/01/01-20080106145758-06.jpg
./2008-01-06/01/01-20080106145759-00.jpg
./2008-01-06/01/01-20080106145808-01.jpg
./2008-01-06/01-20080106145755.avi