Put Your 404 Page to Work with URL Splitting

If you launch a new version of a site and the URL structure changes - you can certainly handle most of that with an htaccess file and a 301 Redirect command. But sometimes, as I've found at least, this isn't always the best solution because if some of the URLs have similar parts, your redirect rules can get confused and not behave as expected. Or perhaps your crappy web host doesn't allow such things.

So instead of pulling your hair out, you can shift some of the burden off onto your 404 File Not Found page. You can make this a php page, and put something like this in your .htaccess file (assumes Apache 2 - I think Apache 1 is different):

ErrorDocument 404 /missing.php

Now, all our missing files and pages will be handled by missing.php - cool. So we can work some PHP magic to help people get where they need to go.

I use this following PHP code quite a bit. It takes the url and splits it into an array, w/o the domain:

$url = explode("/", $_SERVER['REQUEST_URI']);
if ($url[count($url)-1] == '') {
  array_pop($url);
}
if (substr($url[count($url)-1], 0, 9) == 'index.php') {
  array_pop($url);
}
 
array_shift($url);

Now we have our URL in an array called $url. So a URL like this:

http://www.something.com/products/category/item

Will create an array like this:

array
  0 => 'products'
  1 => 'category'
  2 =>'item'

So now, let's say we changed the above url structure so that category part is no longer there, rather it's this:

http://www.something.com/products/item

You can now do something like this in missing.php when someone hits the old, no longer valid url:

if ($url[1] == 'category') {
  header( "HTTP/1.1 301 Moved Permanently" );
  header( "Location: /$url[0]/$url[3]" );
}

What you're doing here is sending the same kind of 301 redirect that .htaccess can, which lets search engines know this is the permanent new home for this page/file - and then shoots the engine/person over to that page. If the pattern doesn't match, you should just have some html at the bottom of the page giving the visitor some sort of friendly message, and perhaps providing a link to the home page if you're really courteous.

Cheers!

Posted in Apache, PHP, SEO | Leave a comment

How to Fix Minify Error: !FAIL: environment : PHP/server does not auto-HTTP-encode content

So you've setup PHP Minify and think you're a good citizen for making your site zippier. You run the tests in min_unit_tests and all pass, except for the last one:

!FAIL: environment : PHP/server does not auto-HTTP-encode content (1 of 72 tests run so far have failed)

Well isn't that a pickle? What this means is that your server, likely of the linux variety and likely using mod_deflate, is already compressing everything and it's getting in the way of how Minify likes to compress the files itself. So you certainly do not want to turn off all that regular compressed goodness across the site, you just want to turn it off in a couple directories and let Minify take over compression duties.

So here's what you do. In the "min" directory there's already an .htaccess file. Add this to the end of it:

SetEnvIfNoCase Request_URI .* no-gzip dont-vary

Then you also need to create an .htaccess file within the min_unit_tests directory, and add the above line to that file too, otherwise your unit tests will continue to fail ;)

Still having trouble? Please post or search the Minify Google group, as I'm not sure if I can help much more on this one!

Posted in Apache, PHP | Leave a comment

Fix: Drupal Site Redirects to p3p0.com

I recently helped a client fix a particularly sneaky site hack. While this pertains to Drupal - similar attacks have been reported in Joomla, or any other PHP based site. The fix here may help still - you'll just need to look harder. Here's the rundown:

The symptom:
When you type your full website domain name into a browser, all seems well. However, when you click a search engine (or any other link) to your site, it goes to some variation of a p3p0.com site - a dirty, affiliate-based site that hawks anything from mortgages to meeting singles.

The Cause:
The following core Drupal files have been hacked:

  1. xmlrpc.php
  2. update.php
  3. install.php
  4. cron.php
  5. 500.php
  6. index.php

Any other files in your root directory may have been hacked too - check the timestamps.

The Fix:
Look for something that resembles this code - and either comment it out (if you are unsure) or delete it:

eval(base64_decode("ZXJyb3...etc..."));

The actual line of code will be quite long - I've shortened it here. It's generally at the very top of the page.

And lastly - change your FTP login! That's how they got in and did this - it isn't a Drupal security issue, it's that somebody hacked your FTP server - it's the only way they could've done this. Or, if your web host has some sort of file editing feature within the control panel, then change your login for your webhost too.

Why did they do this?
They did it to make money by redirecting actual traffic to your site to their crappy, scum-bag affiliate site. Basically, they identify websites that seem to get a fair amount of traffic or have a heavy user base with lots of content for search engines to crawl. The code they use is sneaky in that it only kicks in when it detects someone is clicking through from another site, like a search engine. So if you don't Google (or Yahoo, Bing, whatever) your site much, then it'll take you awhile to even realize what's happening.

What a pain -  hope this helps someone out.

Posted in Drupal, PHP | Leave a comment

Pear DB and “DB Error: already exists” On Insert

Note that this post relates to using a MySQL database in conjunction with the PEAR DB module, or any other PEAR module that depends on DB - like DB_TABLE (which I've been enjoying).

You might run into this error if you switch over from using PHP's built-in MySQL functions, over to a database abstraction layer, which is what I did on a recent project. After much banging-the-head-on-the-desk, I finally figured out that PEAR DB doesn't use the same auto-increment sequence that the actual MySQL engine does.

For example, you may have a database using a CMS that doesn't use PEAR. You do your various inserts, and inevitably, your deletes. So, your auto-increment column might start out in a perfect sequence like 1 - 2 - 3 - 4 - 5, etc. But then say the first two get deleted, so now your sequence id's are 3 - 4 - 5. But then you insert again, and your CMS, using the DBMS directly, knows to use 6 for the next auto-increment value.

But then say you switch over to PEAR DB, and why wouldn't you? It's great - and readily bundled into most PHP installations. But then you notice, taking the above example, that your next insert starts at 1. Then the next insert produces an id of 2. And then you get to 3 and WHOA what happens, "DB Error: already exists" that's wtf happens. PEAR DB ignores MySQL's auto-increment sequence and makes its own (jump down to the notes/warnings in red).

You may also notice that you have table names with "_seq" at the end.

Why is this? I have no idea - but it's stooooopid. Is there some reason for this? Seriously, please enlighten me! I mean, what's an example of where you'd like to use more than 1 sequence?

seqSo what to do? You need to alter those "_seq" tables, that's what. So if you're having trouble inserting into a table named "products" - get MySQL's "Next Autoindex" value, which you can easily do in phpMyAdmin on a table's "structure" tab (see the screenshot, where it's 20).

Now, go into the products_seq table, and change the id to that number (20 in this case). Now you shouldn't have any problems.

So the moral of the story is, if you start out using a PEAR database abstraction layer, you'll never have this problem. But if you're unlucky enough to not have done that in the first place and switch over to it, you may have this issue and this is a quick fix.

Posted in mysql, PHP | Leave a comment

Fancybox: Reload Parent Page When Closed

This caused me a bit of a headache. I knew you had to use the onClosed option in your Fancybox call, but wasn't sure how to use it. For example, if you have a page full of images, and use Fancybox to add/edit those images, then you want that parent page to refresh when you close the Fancybox to show any changes you made.

In this example, I attach a Fancybox to any link with the class "editImage." When it's closed, the parent will refresh:

$("a.editImage").fancybox({
  'width': 800,
  'height': 450,
  'onClosed': function() {
    parent.location.reload(true);
  }
});

If you're not sure what this means or how to use Fancybox, you should hop over here.

Update August 10, 2011:

Note that if you have something like this in your file:

$(".iframe").fancybox({
  'width': 800,
  'height': 400   
});

That it should go before anything else, otherwise if your fancy box link has both of these classes, the iframe class will cancel out anything you declare before it. So in this example, if my link was like <a class="iframe editImage" href="whatever"> then in my javascript, I'd want to declare the iframe before the editImage, like so:

$(".iframe").fancybox({
  'width': 800,
  'height': 400   
});
 
$("a.editImage").fancybox({
  'width': 800,
  'height': 450,
  'onClosed': function() {
    parent.location.reload(true);
  }
});
Posted in jquery | 17 Comments

TextExpander Stopped Working? Try this.

Suddenly I noticed that my beloved TextExpander just stops working/expanding for no apparent reason. Restarting the app didn't work, but what does work is restarting Firefox.

I guess there's some conflict between Firefox version 3.6.x and the TextExpander engine. So the quick fix seems to be to just restart Firefox when this happens.

Evidently, this isn't fixed in TE3 - so no need to upgrade with hope of this getting fixed (at least not of this writing).

Cheers!

UPDATE January 4, 2011:
It has been pointed out in the comments that closing Chrome can also fix the problem - thanks folks!

Posted in gtd, OS X | 9 Comments