Wednesday, May 27, 2009

Apache: "Redirecting" Missing Images with a Default Image

We all know how horrible sites with broken image links are. If you maintain a lot of sites, you can't give your attention to each and every image detail on every site. So what about displaying a default image instead when an image is missing? Well, that will work perfectly fine as long as the images are stored on your server and are not external image links; and this is real simple to do with Apache's mod_rewrite!

Try this code in your vhost entry:

RewriteEngine On
RewriteCond "%{DOCUMENT_ROOT}%{REQUEST_URI}" !-f
RewriteRule \.(gif|jpe?g|png|bmp) /path/to/your/404.gif [NC,L]


Cheers!

Note: This idea was found on the web. At the time of this writing, I cannot find the original source. But if I do, I will properly credit them!

Update A forum link was found as a reference:http://www.webmasterworld.com/apache/3274493.htm

Wednesday, May 20, 2009

Getting Unique Entries From Two Files

Okay, so you have data in one file and then you have updated data in another file. Let's say you have a list of directories that have been searched and they all have a filename, "0.txt". Another file lists all of the directories that have this "0.txt" file but they are actually empty. If you diff the files that will only give you changes being made and won't necessarily give you the entries NOT in the updated file. But, if you get all of the directory entries that are NOT in the new list, then you have a complete list of directories that have a valid file!

*(Well, that was just my example, my real life scenario involved MySQL data.)

Anyhow, with this PHP code you can basically go through both files and utilize in_array to see if certain entries exist in one file or not.

**Note: I wanted to use $argv to handle the arguments, but for some reason file didn't like it. Curious...

$f1 = "/tmp/original.txt";
$f2 = "/tmp/modified.txt";

$a = file($f1);
$b = file($f2);

foreach ($a as $k => $v) {
if (!in_array($v, $b)) {
print "$v";
}
}


Cheers!

Friday, May 8, 2009

WordPress: Modifying Post Content Automatically

Have you ever had the need to have specific content on every single blog post? The way that sounds, I'm sure you'll probably say, "no". But, think about it now, the content does include the HTML. OIC...

So here at work, I've gotten a request to have all links be passed through a link tracker that we built in-house. "Why?" Well, we want to do some sort of testing so we want to keep track of all clicks on links. So that basically means, we need to change all URIs to have the link tracker URI appended by the actual URI that the user put.

Not bad, we can use the WordPress filter, "the_content", and that will get the job done!

Now let's look at the trackURL function:

preg_match_all("/href=\"(.*)\"/",$content, $urls, PREG_PATTERN_ORDER);
$uniqueurls = array_unique($urls[1]);
foreach ($uniqueurls as $url) {
if (!preg_match('/trackerUri.com/', $url)) {
$content = str_replace($url, "http://trackerUri.com/?ab=c/hij&ku=" . $url, $content);
}


It is straightforward; it goes through the content that it is given and finds any "href" references. After those are found, it does a search & replace to insert the new tracker URI before the user-inputed URI.

The rest utilizes WordPress's built-in functions to update the post_content. Easy!

Cheers!


function trackURL($content) {
global $post;
preg_match_all("/href=\"(.*)\"/",$content, $urls, PREG_PATTERN_ORDER);
$uniqueurls = array_unique($urls[1]);
foreach ($uniqueurls as $url) {
if (!preg_match('/trackerUri.com/', $url)) {
$content = str_replace($url, "http://trackerUri.com/?ab=c/hij&ku=" . $url, $content);
}
}
return $content;
}

function trackContent($content) {
global $post;
$pcontent = array();
$pcontent['ID'] = $post->ID;
$pcontent['post_content'] = clicktrackURL($content);
wp_update_post($pcontent);
return $content;
}
add_filter('the_content', 'trackContent');

Wednesday, May 6, 2009

WordPress: update_option ping_sites via PHP CLI

I really hate relying on other site's servers for anything. For obvious reasons, if their site is slow, well that means whatever functionality you're relying on will be slow as well.

In this latest case, technorati.com's blog update service was timing out 'causing our WordPress posts to take up to 2 minutes. Not fun! So, sorry technorati, we're replacing you with google's update services until you get your act together. :)

Now, why am I doing this via CLI? Well, I manage 300+ WordPress blogs, so with a bash wrapper, I can give this script the site and then it will connect and run an update_option and we're good to go!

Yay!


include '/mnt/www/wordpress/wp-config.php';
global $wpdb;
$pingsites = get_option('ping_sites');
$newpingsites = str_replace('http://rpc.technorati.com/rpc/ping','http://blogsearch.google.com/ping/RPC2',$pingsites);
$newpingsites = str_replace('http://rpc.icerocket.com:10080/','',$newpingsites);
echo $newpingsites . "\n";

echo "updating $argv[1]'s ping_sites\n";

update_option('ping_sites', $newpingsites);

}

Friday, April 10, 2009

JavaScript: Get the Current URI's Domain Name

We have consultants that do some work for us (who doesn't?). I have a beta site setup for them to test with. But for some reason when I check the beta site, the URI's for this one part are going directly to the production site. "Hmmm...why...oh huh?! why!?" So it turns out they were hard-coding a domain root as part of the URI in one of their javascript runs. Now really, what kind of portability does that give me!?

Their code looked similar to this:var uri = 'http://drincruz.blogspot.com/' + uriPath;

So, I figured I'll fix this little nuisance by writing a quick function that will get the domain root of a URI. Now, I'm sure there's some sort of built-in function that'll get me what I need, but I couldn't find it. So if there is, someone please let me know!


function getDomainRoot()
{
var uri = location.href;
var startIndex;
if (null != uri.match("file:///") )
startIndex = 8;
if (null != uri.match("http://") )
startIndex = 7;
if (null != uri.match("https://") )
startIndex = 8;
var endIndex = uri.indexOf("/", startIndex);
var domainRoot = uri.substring(0, endIndex);
return domainRoot;
}


Now, I can put this function into their javascript and it can now be used like this:
var uri = getDomainRoot + '/' + uriPath;

/* I should really add a trailing slash check. Ah well. Next time! */

Cheers!

Tuesday, April 7, 2009

Moving a MySQL Cluster to New Hardware

Well, it came to that point in any server's life, it could no longer add any additional disks for space, the cost to upgrade the memory and/or hard disks don't outweigh the benefits of upgrading to new hardware; it's time to put these babies to sleep.

So one of our active/passive MySQL clusters had a three hour maintenance window last night (technically this morning: 12am-3am). The servers came in a few weeks earlier so I had them prepped beforehand. Though, how do you minimize downtime while moving moving the databases over? Well, obviously don't take down the server unless absolutely necessary and of course automate as much of the process as possible!

12am: I'll kick off my script that backs up all DBs in our cluster and saves them in a mount point on a SAN that the new servers can read as well.
## I really could have run this process before 12am as it didn't cause an outage. Ah well, next time.

~12:45am: Now that we have the data, we can now take down the MySQL cluster. I basically rebooted the cluster servers with new IP addresses and hostnames. I now set the IP addresses of the new cluster boxes to have the original IP addresses of the old cluster. See what I did there? Basically, we have a lot of web apps pointing to the DNS of the MySQL cluster, so we need that DNS. (Yes, yes, we could've just updated DNS as well; it's a long story, but basically my department doesn't handle DNS, so going this route is more seamless.)

I already set up replication in MySQL, but as the hostnames have now changed, well replication broke. This was the only snag in the maintenance really. Basically, what happened was MySQL was looking for the old relay logs with the old hostname. Enter, MySQL's nice documentation. I just needed to do this really:

shell< cat new_host_name-relay-bin.index >> old_host_name-relay-bin.index

shell< mv old_host_name-relay-bin.index new_host_name-relay-bin.index


1am: Now that replication is working, all I needed to do was load the data, which is one last script to run.

Here's the basic rundown:
- find the db dump
- create the db
- gunzip the db dump to a tmp dir
- load the dump to the db
- run GRANTs for whatever user


#!/usr/bin/perl -w

# mysqldumpload.pl

use strict;
use DBI;
use File::Find;

my $dbuser = qw(dbuser);
my $dbpass = qw(dbpass);
my $backupdir = "/mnt/backup/mysql";
my $dumpdir = "/usr/local/tmp/dump";

my $dbh = DBI->connect("DBI:mysql:mysql",$dbuser,$dbpass)
or die "Couldn't connect to db: $!";

my @files;

find(\&findFile, $backupdir);


sub findFile {
my $file = $File::Find::name;
# if it's a db, another_db, or another_db2 process it!
if ($_ =~ /db/ or $_ =~ /another_db/ or $_ =~ /another_db2/) {
print "Processing $_...\n";
my $dump = $_;
$dump =~ s/\.gz//g;
my $mysqldb = $dump;
$mysqldb =~ s/\.sql//g;
my $sql = qq!CREATE DATABASE $mysqldb!;
$sth = $dbh->prepare($sql);
print "$sql\n";
$sth->execute();
my $dumpfile = "$dumpdir/$dump";
my $gunzip = qq!gunzip -c $file > $dumpfile!;
print "$gunzip\n";
system($gunzip);
my $mysqlload = qq!mysql --user=$dbuser --password=$dbpass $mysqldb < $dumpfile!;
print "$mysqlload\n";
system($mysqlload);
if ($_ =~ m/db/) {
$sql = qq!GRANT ALL PRIVILEGES on `$mysqldb`.* TO 'dbuser'\@'%'!;
$sth = $dbh->prepare($sql);
print "$sql\n";
$sth->execute();
}
if ($_ =~ m/another_db/) {
$sql = qq!GRANT ALL PRIVILEGES on `$mysqldb`.* TO 'another_db'\@'%'!;
$sth = $dbh->prepare($sql);
print "$sql\n";
$sth->execute();
}
}
}


~1:45am: The script finally finished with the last DB around 1:45am. Not too bad!

Now, as you can see, we have a fairly simple setup. We only wanted to move over certain DBs and only needed a few db users to grant privileges to. With minor modifications to this script, you can have it fit your needs.

*NOTE: A lot of the prep work (i.e. adding mysql users) was done earlier, but can probably be done in one shot as well.

1400+ databases successfully moved to a new cluster, yay! Cheers!

Friday, April 3, 2009

WordPress Plugin: Change Directory Location of Smilies

We recently moved our WordPress smilies to our caching server (for obvious reasons). Well, we then needed to have WordPress use the new URI. We always want to leave core WordPress code intact unless absolutely necessary, so I wrote this quick plugin to search for the old/original URI and use the new one.

Plain. Simple.

Cheers!


function custom_smilies($text) {
$output = str_replace(get_settings('siteurl') . "/wp-includes/images/smilies/", "http://new.location.com/img/wp/", $text);
return $output;
}

add_filter('comment_text', 'custom_smilies', 30);


Note: This works for WordPress 2.0.1. I'll try and test with 2.7 and any other installations I have installed already later. :)