Friday, May 9, 2008
  Daniel’s DropDowns 2.1 - WordPress Plug-In

Version 2.1 of Daniel's DropDowns has been released. This fixes a problem introduced with the 2.5-series of WordPress - the output of the WordPress tag changed, so the search-and-replace portion that added a “Select Category” entry didn't work. This has been fixed in version 2.1. I also corrected a small bug that caused the first entry in the category list to be selected if a default wasn't specified.

It can be downloaded from the WordPress Plug-In Directory. Enjoy!

(UPDATE: This plug-in is inactive, as its functionality is now part of WordPress core.)

Categorized under
Tagged , , ,

Friday, March 28, 2008
  A Handy PHP Backup Script

I found a script over on the Lunarpages Forums about using PHP to back up your site. I have taken it, modified it a little, beefed up the documentation a lot, and am now posting it here. You can copy and paste it from below to customize it for your own use.

<?php
/**
 * Generic Backup Script.
 *
 * To configure this script for your purposes, just edit the parameters below.
 * Once you have the parameters set properly, when the script executes, it will
 * create an archive file, gzip it, and e-mail it to the address specified.  It
 * can be executed through cron with the command
 *
 * php -q [name of script]
 *
 * You are free to use this, modify it, copy it, etc.  However, neither DJS
 * Consulting nor Daniel J. Summers assume any responsibility for good or bad
 * things that happen when modifications of this script are run.
 *
 * @author Daniel J. Summers <daniel@djs-consulting.com>
 */

// --- SCRIPT PARAMETERS ---

/*  -- File Name --
	This is the name of the file that you're backing up, and should contain no
	slashes.  For example, if you're backing up a database, this might look
	something like...
$sFilename = "backup-my_database_name-" . date("Y-m-d") . ".sql"; */
$sFilename = "backup-[whatever-it-is]-" . date("Y-m-d") . ".[extension]";

/*  -- E-mail Address --
	This is the e-mail address to which the message will be sent. */
$sEmailAddress = "[your e-mail address]";

/*  -- E-mail Subject --
	This is the subject that will be on the e-mail you receive. */
$sEmailSubject = "[something meaningful]";

/*  -- E-mail Message --
	This is the text of the message that will be sent. */
$sMessage = "Compressed database backup file $sFilename.gz attached.";

/*  -- Backup Command --
	This is the command that does the work.

  A note on the database commands - your setup likely requires a password
	for these commands, and they each allow you to pass a password on the
	command line.  However, this is very insecure, as anyone who runs "ps" can
	see your password!  For MySQL, you can create a ~/.my.cnf file - it is
	detailed at //dev.mysql.com/doc/refman/4.1/en/password-security.html .
	For PostgreSQL, the file is ~/.pgpass, and it is detailed at
	//www.postgresql.org/docs/8.0/interactive/libpq-pgpass.html .  Both of
	these files should be chmod-ded to 600, so that they can only be viewed by
	you, the creator.

  That being said, some common commands are...

  - Backing Up a MySQL Database
$sBackupCommand = "mysqldump -u [user_name] [db_name] > $sFilename";

  - Backing Up a PostgreSQL Database
$sBackupCommand = "pg_dump [db_name] -h localhost -U [user_name] -d -O > $sFilename";

  - Backing Up a set of files (tar and gzip)
$sBackupCommand = "tar cvf $sFilename [directory]

  Whatever command you use, this script appends .gz to the filename after the command is executed.  */
$sBackupCommand = "[a backup command]";

// --- END OF SCRIPT PARAMETERS ---
//
// Edit below at your own risk.  :)

// Do the backup.
$sResult = passthru($sBackupCommand . "; gzip $sFilename");
$sFilename .= ".gz";

// Create the message.
$sMessage = "Compressed database backup file $sFilename attached.";
$sMimeBoundary = "<<<:" . md5(time());
$sData = chunk_split(base64_encode(implode("", file($sFilename))));

$sHeaders = "From: $sEmailAddress\r\n"
		. "MIME-Version: 1.0\r\n"
		. "Content-type: multipart/mixed;\r\n"
		. " boundary=\"$sMimeBoundary\"\r\n";

$sContent = "This is a multi-part message in MIME format.\r\n\r\n"
		. "--$sMimeBoundary\r\n"
		. "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n"
		. "Content-Transfer-Encoding: 7bit\r\n\r\n"
		. $sMessage."\r\n"
		. "--$sMimeBoundary\r\n"
		. "Content-Disposition: attachment;\r\n"
		. "Content-Type: Application/Octet-Stream; name=\"$sFilename\"\r\n"
		. "Content-Transfer-Encoding: base64\r\n\r\n"
		. $sData."\r\n"
		. "--$sMimeBoundary\r\n";

// Send the message.
mail($sEmailAddress, $sEmailSubject, $sContent, $sHeaders);

// Delete the file - we don't need it any more.
unlink($sFilename);
Categorized under , ,
Tagged , , ,

Friday, March 28, 2008
  Algorithm for One-to-Many Child Table Updates

While working on the Not So Extreme Makeover: Community Edition site, I came up with an algorithm that simplifies anything else I've ever written to deal with this condition. I'll set the scenario, explain the algorithm, share how I implemented it in PHP, and provide a modification if the scenario is a bit more complicated.

Scenario - You have two parent tables, and a child table with a many-to-one relationship with both parent tables, used to map entries in the two parent tables to each other. For this example, we'll use these three tables…

create table volunteer (
    vol_id  integer  not null,
    vol_last_name  varchar(50)  not null,
    ...etc...
  primary key (vol_id)
);

create table r_volunteer_area (
    rva_id  integer  not null,
    rva_description  varchar(255)  not null,
  primary key (rva_id)
);

create table volunteer_area (
    va_volunteer_id  integer  not null,
    va_area_id  integer  not null,
  primary key (va_volunteer_id, va_area_id),
  foreign key (va_volunteer_id) references volunteer (vol_id),
  foreign key (va_area_id) references r_volunteer_area (rva_id)
);

Algorithm - The three-step algorithm is as follows…

  1. Create a comma-delimited string of IDs for the child table.
  2. Delete the IDs from the child table that are not in the list.
  3. Insert the IDs into the child table that are not there already.

Implementation - In PHP, if you have an array, it's easy to come up with comma-delimited list. To get an array of values back in a post, define your fields with “[]” after the name…

<input type="checkbox" name="area[]" id="chkArea1" value="1" />
<label for="chkArea1">Do Something</label><br />
<input type="checkbox" name="area[]" id="chkArea7" value="7" />
<label for="chkArea7">Do Something Else</label>

Here's the PHP code, using PHP Data Objects (PDO) as the database interface, behind a helper class that creates the statement, appends the parameters, and executes it. (The “quoting” escapes the statement to avoid potential SQL injection attacks - putting it in its own class would make the implementation here much cleaner.)

/**
 * STEP 1
 *    Create a comma-delimited list of IDs.
 */

// Quote will return the string as '2,3,4' - since we're using this
// as an IN clause of integers, we'll strip the quotes off.
$sAreas = $pdo->quote(join(",", $_POST["area"]));
$sAreas = substr($sAreas, 1, strlen($sAreas) - 1);

// Quote the volunteer ID.
$iVol = $pdo->quote($_POST["vol"], PDO::PARAM_INT);

/**
 * STEP 2
 *    Delete the IDs that are no longer in the list.
 */
$dbService->executeCommand(
    "DELETE FROM volunteer_area
    WHERE   va_volunteer_id = ?
        AND va_area_id NOT IN ($sAreas)",
    array($iVol);

/**
 * STEP 3
 *    Insert the IDs that are not yet in the list.
 */
$dbService->executeCommand(
    "INSERT INTO volunteer_area
        SELECT $iVol, rva_id
        FROM r_volunteer_area
        WHERE   rva_id IN ($sAreas)
            AND rva_id NOT IN
            (SELECT va_area_id
            FROM volunteer_area
            WHERE va_volunteer_id = ?)",
    array($iVol));

Modification - Suppose that now you accepted comments along with each of the checkboxes, so a simple two-integer insert/delete is no longer sufficient. You would still only need to break step 3 into two steps.

  1. Get a list of IDs to update.
  2. For each ID in the posted list
    1. If the ID exists in the update list, update it.
    2. Otherwise, insert it.

The implementation would then be able to use this list to make the decision without hitting the database every time.

// Assume this returns an associative array of IDs.
$aUpdates = $dbService->performSelect(
    "SELECT va_area_id
    FROM volunteer_area
    WHERE   va_volunteer_id = ?
        AND va_area_id IN ($sAreas)",
    array($iVol));

foreach($_POST["area"] as $iArea) {
    if (in_array($iArea, $aUpdates)) {
        // Update the table
        ...etc...
    }
    else {
        // Insert into the table
        ...etc...
    }
}

I think you'll agree that this is much better than spinning through a loop, doing a count on each ID to see if it exists, then either doing an update or an insert based on the count. And, while the implementation here is PHP, it could easily be implemented in any language that supports arrays and database access.

Categorized under , ,
Tagged , , , , ,

Friday, March 14, 2008
  On Mission

Sorry for the lack of new content (although I did download and build the latest release of xine). I've been working on a community-based volunteer effort in Albuquerque, New Mexico called Not So Extreme Makeover: Community Edition. I'm handling the public website, as well as a private side that the leaders can use to track lots of different things. I'm going to have some good stuff I've discovered out here once we're done (along with lots of praise to heap on PostgreSQL - I have a new “most favorite” open source database), but for the rest of March, I'll likely be incommunicado.

Happy Easter - see you in April!

Categorized under
Tagged , ,

Friday, March 14, 2008
  xine-lib 1.1.10.1 RPM

Below are the library and development RPMs for xine-lib version 1.1.10.1. These have been built the same way that 1.1.7 and 1.1.9.1 were - built on Ubuntu Linux, and converted to RPM using alien. Be sure to check out the About the xine RPMs post for more information.

xine-lib - The main xine library
xine-lib-dev - The development xine library (needed if you're building an interface against xine-lib)

You'll also need a user interface - as of this release, the most current release of xine-ui is 0.99.5.

(To save disk space, only the current release and two prior releases will be maintained.)

Categorized under
Tagged ,

Wednesday, January 23, 2008
  xine-lib 1.1.9.1 RPM

Below are the library and development RPMs for xine-lib version 1.1.9.1. These have been built the same way that the 1.1.7 RPMs were - built on Ubuntu Linux, and converted to RPM using alien. Be sure to check out the About the xine RPMs post for more information.

xine-lib - The main xine library
xine-lib-dev - The development xine library (needed if you're building an interface against xine-lib)

You'll also need a user interface - as of this release, the most current release of xine-ui is 0.99.5.

(To save disk space, only the current release and two prior releases will be maintained.)

Categorized under
Tagged ,