Our merchant services provider, Global Payments, sent us a letter stating that we must be PCI Compliant, or face consequences such as fines, termination of our merchant account, and general woe and spiders. We already follow the prescribed standards. So, no problem? No, problem: they want us to pay $90 to fill in a self-assessment questionnaire.
HA! HA HA HA! HA HA HA HA HA HA HA! HA HA HA HA HA! HA HA! HA!
Read more...
Do you use PHP from the command line and need a password prompt? This is how you do.
Windows:
$scriptpw = new Com('ScriptPW.Password');
$password = $scriptpw->getPassword();
The Windows version requires the file scriptpw.dll. This comes with some versions of Windows, such as XP, but not others, such as 2000, Vista, or 7. If you don't have the file, simply copy scriptpw.dll from the system32 folder of another machine to the system32 folder of your machine. Then, from a Command Prompt window (running as Administrator for Vista and 7) enter the command
regsvr32 scriptpw.dll
Linux:
system('stty -echo');
$password = trim(fgets(STDIN));
system('stty echo');
echo "\n";
Those of you who run your own email servers will likely need to move from server to server from time to time. It would be nice if you could just change an MX record and have all email instantly arrive at the new server. Unfortunately, for various reasons, not everyone obeys TTL, and may cache the old MX record for an unknown period of time. Here's a list of basic instructions for how to migrate your email from one server to another, guaranteeing that no email will fall through the cracks.
Read more...
At Mango's day job, he operates a mailing list for his customers. Approximately 2,000 customers have subscribed to it. He wanted to set up test email accounts with various webmail providers to be sure his emails arrived as intended. But which webmail providers should he choose? The most popular email providers were easily found with this MySQL query:
SELECT
SUBSTRING(`email`, LOCATE('@', `email`)) AS `domain`,
COUNT(*) AS `count`
FROM `table`
GROUP BY `domain`
ORDER BY `count` DESC
For our list, the results were:
Major regional ISPs - 39%
Hotmail - 14%
Yahoo - 9%
Gmail - 8%
AOL - 1%
The remaining 29% consisted of small ISPs and insignificant webmail providers, each less than 1% of the total.
We've always wanted to be able to set up Joomla to automatically add a "Read More" link for articles in Category Blog or Section Blog Layouts so that the articles would all be the same length and they would line up just as pretty as you please. Google revealed a handful of other people attempting the same thing, but having no solution. So Mango wrote a plugin for it.
Read more...
We were trying to solve an annoying bug in our mod_rewrite code today and couldn't quite figure out why our server was acting the way it was. Fortunately we discovered a nifty tool (Thanks pkhach!) that allows you to view the HTTP request and response headers for any URL you like. You can even choose between HTTP/1.1 and HTTP/1.0, with and without the Host header.
View HTTP Headers
A reader also suggests
this Firefox extension. Thanks, Eric!
The best part is that both of these are much easier than messing about with Telnet.exe!
Here are six
"notify" sound effects that Mango made for use in an application, free for the download.
The latest techniques we've been playing with involve using a PHP script on our server in Michigan to print to a remote printer at our office in Vancouver. In the past we've done this by generating an
XHTML document with high-resolution images and simply prompting the user to print it. This worked, but if any sort of precision was required, the user had to configure their browser's page setup just so, and to make things even more difficult, different browsers required different settings.
Enter PostScript, a language understood by many laser printers. With PostScript, we can go directly from our script to the printer, (pipe the finished PostScript document to the printer on port 9100) eliminating the stop off at the browser. And, we can position things on our page with as much precision as necessary.
Read more...
When we're writing software, we always seem to use a few functions for quick conversions. We've got a series of converters bookmarked, but today we were thinking it would be even more handy to have everything all in one place. So we developed the Converters for Webmasters bookmarklet.
Read more...
Alternate title: "Help! My Quotes Appear as Question Marks or Other Strange Characters!"
The "Smart quotes" feature in Microsoft Office transforms straight quotes into curly quotes. It also transforms hyphens into em-dashes and three periods into ellipses. While one might think, "How lovely! My document looks almost as if I'm educated!" readers of said document may not. Microsoft, in its infinite wisdom, decided to assign special characters such as the ones I just mentioned to a range of codes above 128. Problem: these codes were already assigned to other characters, resulting in frustrating incompatibility with non-Microsoft systems.
Keep reading for some PHP and MySQL code to help out with this issue, as well as a Joomla! plugin.
Read more...
I wanted people to be able to leave comments to my articles without having to go through the hassle of signing up and waiting for an email confirmation. So, I looked for a CAPTCHA plugin for WordPress - one of those "enter the words you see in this picture" programs. I found reCAPTCHA, and let me tell you, reCAPTCHA has got to be one of the neatest ideas ever. According to the
reCAPTCHA website, "About 60 million CAPTCHAs are solved by humans around the world every day. In each case, roughly ten seconds of human time are being spent. Individually, that's not a lot of time, but in aggregate these little puzzles consume more than 150,000 hours of work each day."
Amazing! Apparently, a brilliant group of people from the Carnegie Mellon University thought so too. Their next thought was, "How can we harness those 150,000 hours of work?" (Why can't I ever have thoughts like that?) They got together with some people digitizing old books and newspapers. They had a problem. OCR, technology that is used to convert an image of printed text into digital format, is not perfect and occasionally can't read a word. reCAPTCHA was born. The program takes a known word, pairs it with an unknown word, and gives it to a user to enter. If the user solves the one for which the answer is already known, reCAPTCHA assumes the second is also solved correctly. Presto! Word by word, a book is eventually digitized.
Here's an example. The word on the right was difficult to read. I assume it's "their". reCAPTCHA will show this word to a few other people as well, in case I'm wrong.
I spent a lot of time trying to find a simple, lightweight, drag-and-drop example to learn from. Finally I located a
drag-and-drop demo from the fine folks at WebToolkit. The Javascript is only 50 lines which impressed even me. I've tested this to work with Firefox, IE, and Opera.
Here's how to use it. The element must have position:absolute; or position:relative;.
var dragable = dragHandler.attach(document.getElementById('some_element_id'));
/*This function will be called when dragging starts. Note that we do not write begin(); here. */
dragable.dragBegin = begin;
/*This function will be called every time the element moves.*/
dragable.drag = drag;
/*This function will be called when dragging ends.*/
dragable.dragEnd = end;
The last three lines are optional. You can write your own functions (here, imaginitively named 'begin', 'drag', and 'end') that will be run when you specify.
Here's a
Drag-and-Drop example I made. Check the source code for the rest of the Javascript.
I was thinking a few months ago how handy it would be if I could set server variables with .htaccess. In particular was the path to which I had installed my customer management software. Many programs had used its include file, both in the software's directory and outside of it, requiring me to hard-code its path. And, of course, it is good programming practice NOT to hard-code paths.
I was told then that this was impossible. I like to think that I am the first to make a great discovery, but it is likely that the person that told me that was just wrong.
SetEnv cm_path /home/mango/public_html/cm
and in PHP, you simply access the cm_path variable like this:
$_SERVER['cm_path']
Today I decided I needed to figure out a way to prevent people double-clicking Submit buttons. Now, before you get all horrified, I already did have protection server-side. But if the Submit button was double-clicked, the user would get a confusing "Duplicate transaction" message. Guess who would have to explain that one?
Disabling the Submit button was trivial, but in the scenario that the user's computer was not able to contact the server and the user clicked the Back button, the Submit button stayed disabled. The following Javascript solves both problems. Simply add
onsubmit='return submitOnce;' to your <form> tag.
This has worked successfully with Firefox, IE, and Opera. Please comment if you test it with any other browsers.
var form_submitted = false;
function submitOnce() {
if (navigator.appName == "Microsoft Internet Explorer" || navigator.appName == "Netscape" || navigator.appName == "Opera") {
if (form_submitted == false) {
form_submitted = true;
window.onunload = function() { form_submitted = false; }
return true;
} else {
return false;
}
} else {
return true;
}
}
I wrote two date/time functions in PHP that I was really pleased with so thought I'd post them.
- string friendlydate ( numeric or string $input )
Return value is a day in relation to today in the format "Today", "Yesterday", "Wednesday", "March 2", or "November 7, 2007".
- string howlong ( numeric or string $a, numeric or string $b )
Return value is the approximate length of time between two dates, $a and $b. Examples are "4 seconds ago", "1 minute from now", "1 hour ago", "yesterday", "2 months ago", "1 year ago".
Read more...
While writing a Power Search module for my accounting/customer management software, I came up with a great way to order results in MySQL.
The problem was this: some of our accounts are listed under a business name, and some are listed under a person's name. You couldn't
order by `BusinessName`,`LastName` because all the accounts that didn't have a business name listed would appear first in the list. If you reversed it, all the accounts who had only a business name would appear first in the list. The solution, fortunately, was beyond simple:
order by CONCAT(`BusinessName`,`LastName`,`FirstName`)
This is equivalent to saying "Sort by BusinessName, if it doesn't exist then sort by LastName, if it doesn't exist then sort by FirstName. Yay!
I thought I'd post my htmlspecialchars() functions for Javascript. This should mimic the PHP version of htmlspecialchars(). I also include rhtmlspecialchars() in case you need to do the reverse.
Read more...
I've had a few requests to post my AJAX Communicate Function, so here it is. As you can see, you may pass four arguments to it. The first is 'method', which may be either GET or POST (defaults to GET), 'url' and 'querystring', which are self-explanatory, and 'failure'. The 'failure' argument may contain code to be evaluated in case of failure. This can be triggered due to anything from the server giving a 404 error to the user suddenly losing their network connection. This is optional, but useful.
Read more...
I was writing a module for a webapp today and was thinking how cool it would be if I could dynamically add CSS classes using Javascript. At that particular moment, I had all the classes for all the modules grouped together in one big file, but since sometimes I didn't load all the modules, it was inefficient.
It took me a while to find some examples on doing this, so figured I would post the function I wrote in case anyone else has this same problem.
function addCSSclass(className, classRule) {
if (document.all) {
document.styleSheets[0].addRule("." + className, classRule)
} else if (document.getElementById) {
document.styleSheets[0].insertRule("." + className + " { " + classRule + " }", 0);
}
}
Enjoy!
Well, sort of.
With the rising popularity of webapps, more and more documents like receipts, invoices, form letters, and whatnot are printed using a web browser. This is great, because you have all the advantages of a webapp. But, when printed, your 72dpi logo looks just a
touch muddy. No problem! Just print it at 600dpi and that logo will be as crisp and clear as the surrounding text.
Read more...
One thing that really frustrates me is when I try to print something off a website, and my printer prints three pages of headers, graphics, and menus, and ONE page of what I actually wanted to print. Some designers have thought ahead and have a link to open their pages in a new window, unformatted, but this doesn't always work, especially if the page is, for example, dynamically generated by submitting a form, or the user has a pop-up blocker. Fortunately, there's a really cool CSS technique for making this easier. It lets you use one stylesheet for what your users see on the screen, and a completely different stylesheet for what you print! And it works like a
charm!
Read more...
While Javascript isn't quite (and likely never will be) ready for running entire websites any more than Flash is, it's quickly becoming a popular language for writing browser-based applications. The advantages are aplenty: the bulk of the data processing is done at the client, so it's fast. There's no waiting for your browser to download, render, and display a page every time you click a link. And since it runs in a web browser, deployment, upgrades, bug fixes, and configuration become non-issues.
There's something about browser-based applications that some programmers just don't like though. And that is that browser-based applications are ... well ... browser-based. Fortunately, there are a few techniques that one can use to make a webapp act just like a regular desktop application, and this article is about the technique I've developed.
Read more...