Chomper Stomping jQuery/JavaScript/CSS 3/HTML 5, Java/PHP/Python/ActionScript, Git, Chrome/Firefox Extensions, Wordpress/Game/iPhone App Development and other random techie tidbits I've collected

7Oct/088

HTML E-Mail with PHP: 5 Pointers to Get ‘er DONE!

Sending e-mail can be pretty useful with PHP. It's also really easy... until you want to send HTML that is.

I used these two tutorials to learn how to send HTML e-mails in PHP:

sitepoint.com advanced e-mail tutorial
webcheatsheets.com sending e-mail tutorial

For something that requires very strict accurate guidelines to be followed, you would think they wouldn't just gloss over them or ignore them entirely. But they did.

So, here are five pointers to get you off on the right foot:

1. Use Content-Type: multipart/alternative; with a random hash

Don't just send the e-mail as HTML. Many e-mail providers don't allow for HTML e-mails, so you need alternative content. You will need a boundary string, but you will also want it to be random hash. Why? Well, I'm not really sure. I can easily imagine one scenario, which would be that if you are sending mail on behalf of someone, and they figure out your static boundary string, they could do some sort of injection attack and send some nasty thing through with your e-mail. Not really sure if this would work, or if this is the reason, but everyone else uses a random hash, so you should too!

<?php
//create a boundary string. It must be unique 
//so we use the MD5 algorithm to generate a random hash
$random_hash = md5(date('r', time())); 
//define the headers we want passed. Note that they are separated with \r\n
$headers = "From: webmaster@example.com\r\nReply-To: webmaster@example.com";
//add boundary string and mime type specification
$headers .= "\r\nContent-Type: multipart/alternative; boundary=\"PHP-alt-$random_hash\"";
?>

2. Don't use HEREDOC syntax

At first I was trying to just do this:

$msg <<<HERE
--PHP-alt-$random_hash
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

Plain text stuff

--PHP-alt-$random_hash
Content-Type: text/html; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

<html>
<body>
<p>
Blah blah blah $var blah!
</p>
</body>
</html>
--PHP-alt-$random_hash--
HERE;

But it just wouldn't work! I figured out you have to do it like this:

$message = "--PHP-alt-$random_hash\n\r" .  
"Content-Type: text/plain; charset=\"iso-8859-1\"" .  
"Content-Transfer-Encoding: 7bit\n\r" .  

"Hello World!!! \n\r" .  
"This is simple text email message. \n\r" .  

"--PHP-alt-$random_hash\n" .  
"Content-Type: text/html; charset=\"iso-8859-1\"" .  
"Content-Transfer-Encoding: 7bit\n\r" .  

"<h2>Hello World!</h2>\n\r" .  
"<p>This is something with <b>HTML</b> formatting.</p> \n\r" .  

"--PHP-alt-$random_hash--";

Pay special attention to where you are putting your \ns and \rs!!!

3. Get the from right!

When building your headers, don't forget to include a from. This will make your e-mail more recognizable and professional. Don't just include the from though, include a human readable value as well!

$headers = "From: An actual name <email@email.com>\r\n";

4. Don't forget those trailing "--" after the final bondary!

Don't forget those last two dashes on your final boundary:

--==Multipart_Boundary_{$semi_rand}x--

See them there at the end? You don't put those on the others, so they are easy to forget on the final one. Also, it's boundary with an "a", not "boundry"... Yeah, I misspelled it the first time...

5. If things aren't working, experiment

It took me a good hour and a half to get this working right. It all came down to those little \r and \n things in the end. I had to carefully add and remove them from different places until I got everything just right. The webcheatsheet code conveniently uses some buffer trickery to avoid this, but my web host does not support that, so I had to figure it all out myself. Here is a version of their code that actually works on my web hose (Dreamhost):

<?php
//define the receiver of the email
$to = 'email@gmail.com';
//define the subject of the email
$subject = 'Test HTML email'; 
//create a boundary string. It must be unique 
//so we use the MD5 algorithm to generate a random hash
$random_hash = md5(date('r', time())); 
//define the headers we want passed. Note that they are separated with \r\n
$headers = "From: webmaster@example.com\r\nReply-To: webmaster@example.com";
//add boundary string and mime type specification
$headers .= "\r\nContent-Type: multipart/alternative; boundary=\"PHP-alt-$random_hash\""; 
//define the body of the message.
$message = "--PHP-alt-$random_hash\n\r" .  
"Content-Type: text/plain; charset=\"iso-8859-1\"" .  
"Content-Transfer-Encoding: 7bit\n\r" .  

"Hello World!!! \n\r" .  
"This is simple text email message. \n\r" .  

"--PHP-alt-$random_hash\n" .  
"Content-Type: text/html; charset=\"iso-8859-1\"" .  
"Content-Transfer-Encoding: 7bit\n\r" .  

"<h2>Hello World!</h2>\n\r" .  
"<p>This is something with <b>HTML</b> formatting.</p> \n\r" .  

"--PHP-alt-$random_hash--";

//send the email
$mail_sent = mail( $to, $subject, $message, $headers );
//if the message is sent successfully print "Mail sent". Otherwise print "Mail failed" 
echo $mail_sent ? "Mail sent" : "Mail failed";
echo $message;
?>

Have fun! Feel free to comment if you have any other tips/questions.

Tagged as: , , , 8 Comments
4Oct/0817

Auto Upload File on Save in Aptana

This is an explanation on how to automatically upload a file via FTP when you click "save" in the Aptana IDE (2.x does not work in 3.x). Let's jump right in...

1. If you don't have a project, create one. 

2. If you don't have a "scripts" folder in your project, create one (I put mine in the root directory of my project. No clue if it will work from anywhere else). 

3. Create a new file inside your scripts folder with a .js extension (I named mine "upload_current_file_on_save.js", not sure if it will work with any other name). 

4. Place this code inside the file: 

 

Code:
/* 
 * Menu: gMan > Upload On Save 
 * Kudos: Ingo Muschenetz 
 * License: EPL 1.0 
 * Listener: commandService().addExecutionListener(this); 
 * DOM: http://localhost/com.aptana.ide.syncing 
 * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript 
 */  

// Add  * Listener: commandService().addExecutionListener(this); to the top of this file to enable it 

/** 
 * Returns a reference to the workspace command service 
 */ 
function commandService() 

   var commandServiceClass = Packages.org.eclipse.ui.commands.ICommandService; 
    
   // same as doing ICommandService.class 
    var commandService = Packages.org.eclipse.ui.PlatformUI.getWorkbench().getAdapter(commandServiceClass); 
    return commandService; 

/** 
 * Called before any/every command is executed, so we must filter on command ID 
 */ 
function preExecute(commandId, event) {} 

/* Add in all methods required by the interface, even if they are unused */ 
function postExecuteSuccess(commandId, returnValue) 
 { 
    
   // if we see a save command 
   if (commandId == "org.eclipse.ui.file.save") 
   { 
      sync.uploadCurrentEditor(); 
       
      /* Replace above line if you'd like to limit it to just certain projects 
      var fileName = editors.activeEditor.uri; 
      if(fileName.match(/projectName/ig)) 
      { 
         sync.uploadCurrentEditor();    
      } 
      */ 
    } 

function notHandled(commandId, exception) {} 

function postExecuteFailure(commandId, exception) {}

 

5. Save the file. 

A few notes on what needed changed in this file from the sample file I took this from: 

1. I added this line of code to the comment block at the top: 

 

Code:
 * Listener: commandService().addExecutionListener(this);

 

I know there is a comment in this file that tells you to "add this to the top of this file", but since I'm new to the whole scripts thing I didn't get that when it said "top of the file" it meant "the middle of the comment block at the top of this file". I thought I was literally supposed to make that line the very first line in the file... Which didn't work at all... 

2. I changed the "menu" line to: 

 

Code:
 * Menu: gMan > Upload On Save 


from: 

 

Code:
 * Menu: Synchronize > Upload Current File On Save Enabled

 

For some reason it didn't work until I changed this line. Since my project is code named "gMan" (It's a user management system, you'll make the connection...) I just named the menu item "gMan" after my project. Then the command is to upload on save, so that's what I named it. As soon as I made this change is when the whole thing started working. Could be coincidence, or could be that you have to do this for the script to work (rename the menu command something other than what the sample is named). 

In case you haven't figured it out, you run the script by going to the "Scripts" menu in the toolbar at the top of Apatan (and selecting the script, whatever you named it in the previous step). 

Want to read more about website creation in Aptana from this author? Check out his book:
HTML, XHTML, and CSS All-in-One Desk Reference For Dummies (For Dummies (Computer/Tech)). Support the Author of this post and buy the book from Amazon by clicking on the link on this page. Thanks for the help!

16Sep/083

phpbb3 & zen cart integration

I've been trying to get the phpbb3 & zen cart integration to work.

I've had some errors in the default SQl zen cart comes with. First I was getting an error about an invalid column name in the phpbb_user_groups table (specifically the group_description column not existing). Once I fixed that, I got a very cryptic "1062 Duplicate entry '' for key 2" error every time I tried to create a new user.

Here's how to fix BOTH of these problems:

Problem 1 - "column group_description does not exist":

Edit line 196 in store>includes>classes>class.phpbb.php. Change "group_description" to "group_desc"

Problem 2 - 1062 Duplicate entry '' for key 2:

Edit line 187 (in the same file as in Problem 1). Change it from:

(user_id, username, user_password, user_email, user_regdate)

to:

(user_id, username, user_password, username_clean, user_email, user_regdate)

Edit line 189. Change it from:

('" . (int)$user_id . "', '" . $nick . "', '" . md5($password) . "', '" . $email_address . "', '" . time() ."')";

to:

('" . (int)$user_id . "', '" . $nick . "', '" . md5($password) . "', '" . $nick . "', '" . $email_address . "', '" . time() ."')";

phpbb requires that each user have a unique "username_clean". This is the second "key" in the table requirements. The first creates an index on user_id, the second requires username_clean be UNIQUE, the third creates an INDEX on user_birthday, and so on. In fact, at NO POINT do they EVER REQUIRE THE USERNAME TO BE UNIQUE! WHAT????? yep. It's username_clean that must be unique. zen cart doesn't even try and insert anything into username_clean, it just tries to make the username (this makes no sense at all!!!).

Feel free to comment if you have any insights/questions!

15Sep/0812

SPE (Python IDE) for Windows


I've been playing with Python this weekend. Currently I'm rebuilding "Clink" in Python instead of finishing it in Flash (It really needs AS 3.0 to do properly, and I only have access to 2.0).

The book I am reading (Game Programming: The L Line, The Express Line to Learning) recommeds the SPE (Stani's Python Editor) IDE for creating Python programs. However, the book doesn't really say how to get the program. Stani (the IDE's author) doesn't appear to have a web page with a "download" link and instructions; so here's a download link with instructions from me instead (this is for installing on Windows):

 

Step 1: Download & Install wxPython (NOT wxWidgets!!!!)

The book states that you should download and install wxWidgets. It turns out, you need wxPython instead, which you can get here:

http://www.wxpython.org/download.php

Scroll down and get the version that matches your version of Python. Just double click the downloaded file, and it basically installs itself. I won't give you any advice on what boxes to check at the end of the install, because I don't remember what they said, and to be honest, I arbitrarily made my choices having no clue as to why I was choosing what I chose (I unchecked the top one, and left the bottom two checked).

Step 2: Download and Install SPE

Go to http://developer.berlios.de/project/showfiles.php?group_id=4161

Get this file: SPE-0.8.4.c-wx2.6.1.0-no_setup.zip

Unzip it to somewhere you will remember (I put mine in Program Files) but don't rename the folder anything other than "_spe".

Go inside the _spe directory you just created and double click SPE.py

At this point your shiny new Python IDE should start up. If not, sorry... I don't know why it didn't work. Make a shortcut to the editor on your desktop for easy opening (or start menu, or quicklaunch toolbar).

Have fun!

13Sep/0822

Installing Python (mod_python) on XAMPP (on Windows)

So you want to get Python working with XAMPP eh? Me too! So, apparently, do a lot of people; and pretty much no one who knows how is saying how from what I've found online in my cursory 5 minute search. Let's just jump right in, shall we?

Resources:

Step 0: Install XAMPP & Python

You should have installed XAMPP already, but if you are a brand new "b" to the Server Side Dev ring, and you have stumbled upon this post looking for the catch all solution to getting your feet wet with Server Side Programming and have picked Python as your poison, go get XAMPP! It basically installs and configures itself, and there is an excellent tutorial on the apachefriends website to get you through this process:

http://www.apachefriends.org/en/xampp-windows.html#641

You might not have installed Python already (although, if not, why did you choose Python over PHP or Ruby?). If you haven't installed it already, go download and install it now:

http://www.python.org/download/

Step 1: Make a Backup

If you are like me, and have a few years worth of stuff in your XAMPP directory that you don't want to 'splode when you screw up the first time you try to get Python working with XAMPP, go ahead and just zip c:\xampp to x:\xampp-backup.zip now...

Step 2: Download mod_python

Download mod_python now...

http://httpd.apache.org/modules/python-download.cgi

Wait, which one do I download?

Good question! Check your version of Apache and Python to determine which one you need

  1. Enter the text (leave out the space between phpinfo and ()):
    <? phpinfo (); ?>
    into a new text document in notepad.
  2. Save the document as "test.php" (or something) in the htdocs directory of your xampp install.
  3. Start Apache (open the xampp control panel, c:\xampp\xampp-control.exe, and click "Start" next to Apache).
    NOTE: If you have IIS running, stop it. (Control Panel, Administrative Tools, Services, IIS Admin, Stop (say yes to stopping those other services too). If you scroll up to "Apache" at this point and click on it, you will see which version you are running and can skip to the next step in the mod_python install).
  4. Browse to the document you saved in step 2 through your web browser (localhost\test.php). You can NOT just double click the file and expect it to be parsed by apache and run...
  5. Scroll down and view the output in the "apache2handler" section. The first box in the table should be "Apache Version" and should tell you which version of Apache you are running (mine is 2.2.4, so I downloaded the very last ".exe" file so that I would have Python 2.5 w/ Apache 2.2)

Step 3: install mod_python

  1. Double click the exe file
  2. Click "Next"
  3. It should find your Python install. If you have more than one version, select the one you want to use. Click "Next"
  4. Click "Next"
  5. There should be a pop-up asking where Apache is installed. If you installed xampp to c:\, then this will be c:\xampp\apache

Step 4: Configure Apache

  1. Open c:\xampp\apache\conf\httpd.conf
  2. Scroll down to the section with all the "LoadModule" lines (about line #67) and add this line:
    LoadModule python_module modules/mod_python.so

    Note: If you are unable to start Apache after this, go back and type the line in the conf file by hand instead of copying/pasting.

  3. Scroll down to the section with the <Directory "/xampp/htdocs"> tag ends (about line #232) and add these lines following the closing </Directory> tag:
    <Directory "/xampp/htdocs/test">
            AddHandler mod_python .py
            PythonHandler mptest
            PythonDebug On
    </Directory>
  4. Save and close the file.
  5. Restart Apache
  6. Create a new file (and folder) c:\xampp\htdocs\test\mptest.py
  7. Place the following text in the file, and save it (watch your formatting, Python is whitespace sensitive!):
    from mod_python import apache
    
    def handler(req):
    	req.content_type = 'text/plain'
    	req.write("Hello World!")
    	return apache.OK

Step 5: Testing Py!

Point your browser to http://localhost/test/mptest.py

You should see "Hello World!". If you don't, check this page out (and this one and this one). Also, make sure you got the path correct in the <Directory /> tag, and saved your mptest.py file to the right place.

What's next? Why not get a Python book? This one is really good:
Game Programming: The L Line, The Express Line to Learning (The L Line: The Express Line To Learning). That's right! Game programming in Python.

Or, why not check out some tutorials, starting with this one! (and check out this presentation on django)

Edit:

Some user comments:

<Directory /test> ... </Directory> <-- This is wrong!

But, the following works:

<Directory "C:/xampp/htdocs/test"> ... </Directory>

instead on adding the directory block, which turns on mod-python for a single folder, add the following at the end of the file (assumes you want to use the Publisher handler):

<IfModule python_module>
AddHandler mod_python .py
PythonHandler mod_python.publisher
PythonDebug On
</IfModule>

If you have any other comments that require code. Just send me an e-mail (cmcculloh-at-gmail-dot-com) and I'll post the code (so you don't have to worry about it getting mangled).