Quick tip: Flash like click through on HTML elements

Supposedly you have an element and you need to apply a mask over it, but you still want to be able to interact with the element you applied the mask over. You can do that via a CSS3, by editing the style of the mask and adding this:

#mask {
    pointer-events: none;
}

The bad news is that it is only implemented in the latest browser versions. The good news is that it is implemented in mobile devices browsers since a few years ago. Visit this link to read more about the implementation of pointer-events: http://caniuse.com/#feat=pointer-events

Sending e-mails from CakePHP 2.0 using Amazon’s SES SMTP server

In mid-December 2011, Amazon announced that they simplified the process of using Amazon SES by allowing users to send e-mails through a SMTP server. You can read some details about Amazon SES and an alternate implementation in CakePHP 1.3 here.

What follows is a simple setup to make CakePHP 2.0 send e-mails through Amazon SES:

Step 1: Creating your SMTP credentials

Using the Amazon SES console you will be able to create unique credentials for your app (both user and password). These are not the same as the key or secret used to access the API. To do this, go to your SES console and click on SMTP Settings. A new page will open with partial information (such as the server address and the port number you need to use). At the bottom of the page, you can find a button called “Create my SMTP Credentials”. Click on it and choose your username (this can be anything and it is used to create an encrypted string that will serve as the actual username). After clicking the “Create” button you will be served your credentials. These will be shown to you only once and you cannot come back to them or modify them. You can, however, download them.

Step 2: CakePHP configuration

We will be using Cake’s update e-mail component for our setup.

First, if you haven’t done so, you need to rename the email.php.default file under /app/Config to email.php. Once you’ve done this, open the file and go the second configuration array, called “Smtp” and the following values:

	public $smtp = array(
		'transport' => 'Smtp',
		'from' => array('no-reply@example.com' => 'Testing'), //this needs to validated with SES. More details here: http://viorel.sfetea.ro/blog/en/2011/10/31/sending-e-mails-the-efficient-way-using-amazon-ses-and-cakephp/
		'host' => 'ssl://email-smtp.us-east-1.amazonaws.com',
		'port' => 465,
		'timeout' => 10,
		'username' => 'YOUR-GENERATED-USERNAME',
		'password' => 'YOUR-GENERATED-PASSWORD',
		'client' => null,
		'log' => false
	);

Please notice the “ssl://” before the actual hostname. That is the key to open the right channel to communicate with the SMTP server

Second, we have the controller configuration. This is a basic configuration that can be modified per your needs. Details about modifying it here.

<?php
	App::uses('CakeEmail', 'Network/Email');
 
	class FrontendController extends AppController {
	    public $name = 'Frontend';
 
		function index() {
			$email = new CakeEmail();
			$email->config('smtp')
				->template('test_template', 'test_layout') //I'm assuming these were created
			    ->emailFormat('html')
			    ->to('anyone@example.com')
			    ->from(array('no-reply@example.com' => 'Example'))
			    ->subject('I\'m just testing something')
			    ->send();
		}
	}
?>

Step 3: Never worry about your e-mails again

Simple cache in PHP without extra extensions

There are cases when you can’t use APC or Memcache or whatever cache extension you prefer on using in PHP. What follows is a simple way of implementing cache for an app. All you need is a writable folder somewhere on the server.

The implementation

The mechanism consists of three functions, operating with files on the server:

  1. The writeCache function takes your data, serializes it (if necessary) and writes it to a static file.
  2. The checkCache function reads the timestamp on the file that was written, compares it to a time frame you provided and returns false if the cache needs the be rewritten.
  3. The readCache function reads the contents of the cache file, unserializes it (if necessary) and returns it.
/*the base folder of the cache file(s) 
 * (assuming there are no open_basedir restrictions)
*/
$cacheFileBase = $_SERVER['DOCUMENT_ROOT'] . '/cache/';
 
/*the cache time in minutes - how long will the results be cached*/
$cacheTime = 10;
 
/*Function to check if a certain cache file needs to be updated*/
function checkCache( $identificator ) {
	global $cacheFileBase, $cacheTime;
 
	$file = $cacheFileBase . $identificator . '.txt';
 
	/*return false if the cache file doesn't exist*/
	if( !file_exists( $file ) ) {
		return false;
	}
 
	/*Return false if the file's cache time substracted 
	 * from the current time is larger than the cache time in seconds*/
	if( (time() - filemtime($file)) > $cacheTime * 60) {
		return false;
	}
 
	return true;
}
 
function writeCache( $identificator, $data ) {
	global $cacheFileBase;
 
	/*The full path to the cache file*/
	$file = $cacheFileBase . $identificator . '.txt';
 
	/*serialize the data if we're dealing with an array or an object*/
	if( is_array($data) || is_object($data) ) {
		$data = serialize($data);
	}
 
	if( file_put_contents($file, $data) ) {
		return true;
	}
 
	return false;
}
 
function readCache( $identificator ) {
	global $cacheFileBase;
 
	/*The full path to the cache file*/
	$file = $cacheFileBase . $identificator . '.txt';
 
	$data = file_get_contents($file);
 
	/*check to see if the data was 
	 * serialized so we know what to return
	 * http://stackoverflow.com/questions/2878218/check-if-string-is-serialized-in-php/2878254#2878254
	 * */
	$checkSerialization = @unserialize($data);
	if ( $checkSerialization !== false || $data === 'b:0;' ) {
	    $data = $checkSerialization;
	}
 
	return $data;
 
}

The usage

if( !checkCache('testing') ) {
	$test_data = array('test1', 'test2', 'test3');
 
	if( !writeCache('testing', $test_data) ) {
		//log the data if cache failed
	}
 
} else {
	$test_data = readCache('testing');
}
 
print_r($test_data);

Sending e-mails the efficient way using Amazon SES and CakePHP

A few words about Amazon SES

Amazon SES is an e-mail service provided by Amazon that allows professional e-mail sending through Amazon’s servers. It handles sending and processing e-mails so you don’t have to (sending, catching bounces, verifying addresses). I am using SES to ensure the best deliverability rates for notifications e-mails from my apps.

Is that free?

Not really, but it’s worth every cent. And speaking of cents, you probably won’t be spending too many of those using SES. Prices start from 10 cents per 1000 sent e-mails. Rates are even lower if you are using their cloud solutions to host your app.

Let’s get cooking

First, let’s take care of the CakePHP implementation:

Download the PHP SDK Amazon prepared from here, rename the folder to aws-sdk and upload it to your /app/vendors/ folder in the CakePHP installation.

After copying the PHP SDK, you will need a simple component that extends Cake’s email component to use SES. You can get that from my github page (this a tweaked version of an excelent component from Lucas Ferreira). Copy that to your /app/ folder preserving the folder structure.

Assuming you don’t already own an Amazon AWS (that includes SES) account, please create one here. Since this is not a free service, you will need to provide your credit card number before finishing. It will take a few minutes to activate all the services.

Having got all your services activated, you will need to go back to the AWS PHP SDK you have in your CakePHP installation and create the configuration file. Rename the file config-sample.inc.php to config.inc.php and add your own values for AWS_KEY, AWS_SECRET_KEY, AWS_ACCOUNT_ID and AWS_CANONICAL_ID. You will find these numbers on the same page, in the Security Credentials section of your account page.

Testing

Amazon SES requires production access in order to be used freely. This happens because the object here is a very sensitive one and they need to handpick anyone that is going to use their service. This prevents spammers from taking advantage of SES and using it for illegal purposes.

However, you will have sandbox access, meaning that you will be able to use the service fully, but only with addresses you previously define (verify). You will have to verify both the addresses you send e-mail from (this will be the case in production, too) and the e-mail addresses that receive the e-mail. Once you are granted production access, you won’t need to verify the addresses you are sending e-mail to anymore. The verifying process consists of sending a request to the desired address and then clicking a link in an e-mail sent to that address.

You can verify e-mail addresses by calling a method in the EmailService component you previously downloaded from github:

class PagesController extends AppController {
 
	var $name = 'Pages';
	var $components = array('EmailService');
 
	function beforeFilter() {
 
	}
 
	function display() {
		$verify = $this->EmailService->verifyEmailAddress('me@example.com');
		var_dump($verify);
	}
 
}

In relation with the original CakePHP e-mail component, the usage is pretty much the same. In addition, you will just have to the the Email component which kind of e-mails to send. Everything else is the same, just that you have to use $this->EmailService instead of $this->Email. An example:

class PagesController extends AppController {
 
	var $name = 'Pages';
	var $components = array('EmailService');
 
	function display() {
		$this->EmailService->delivery = 'aws_ses'; //or aws_ses_raw
		$this->EmailService->to = array('me@example.com');
		$this->EmailService->subject = 'Example e-mail';
		$this->EmailService->from = 'Me <meagain@example.com>';
		$this->EmailService->template = 'template';
		$this->EmailService->sendAs = 'html';
		$this->EmailService->send();
	}
 
}

aws_ses or aws_ses_raw delivery?

This depends on how your actual e-mail is built. After doing some tests, I ended up to the conclusion that aws_ses is suitable for plain text and light-weight HTML e-mails (no images) and aws_ses_raw is best suited for complex HTML e-mails (text and images) and/or e-mails containing attachments.

…and finally. Requesting production access

Go to this simple form and fill your details in there. You will be receiving production access as soon as they review your request.

Installing and setting up Subversion on a WHM/Cpanel machine

This post is desired to be a 101 on installing and setting up Subversion on a WHM/Cpanel machine.

Notes:
1. The following was tested using WHM version 11.30.4 (build 6), installed in CentOS 5.7
2. The following reference will be used:
example = referring to the account name of a cPanel account set up in WHM
example.com = the domain for which the example account was set up for
3. You will need to get dirty with the terminal using SSH access
4. The setup is going to use Apache’s mod_dav for authentication

Installing mod_dav (if needed)

First, check to see if mod_dav isn’t already installed on your system by running the following command in the terminal:

root@machine [~]# /sbin/service httpd -M | grep dav

If mod_dav is already installed, it will return:

dav_module (static)
dav_fs_module (static)

If not, you will have to rerun EasyApache (EasyApache is a script used by WHM installations to configure Apache). You can do this by clicking on the EasyApache (Apache Update) link in the WHM menu. Select the most suitable profile to load based on what you are using the server for (if this is not a fresh server, it is better to read the EasyApache documentation before proceeding so that you don’t lose prior configurations). When you reach step 6 (Exhaustive Options List) be sure to check DavFS and Dav in the Apache Built-in Modules list. Save and build.
Note: Running EasyApache will shut down your webserver for about five minutes.

Install Subversion

In order to properly configure it, Subversion needs to be compiled from its sources. First, go to http://subversion.apache.org/download/#recommended-release and copy the link pointing to the .tar.gz archive (in my case, http://subversion.tigris.org/downloads/subversion-1.6.17.tar.gz). Now, download the source and unpack it:

root@machine [~]# cd /usr/local/src/
root@machine [/usr/local/src]# wget http://subversion.tigris.org/downloads/subversion-1.6.17.tar.gz
root@machine [/usr/local/src]# tar -xvf subversion-1.6.17.tar.gz

Before compiling Subversion, SQLite needs to be copied to the sources. Go to http://www.sqlite.org/download.html and get the link for the latest tarball. In my case, this is http://www.sqlite.org/sqlite-autoconf-3070800.tar.gz:

root@machine [/usr/local/src]# wget http://www.sqlite.org/sqlite-autoconf-3070800.tar.gz
root@machine [/usr/local/src]# tar -xvf sqlite-autoconf-3070800.tar.gz
root@machine [/usr/local/src]# cd subversion-1.6.17
root@machine [/usr/local/src/subversion-1.6.17]# mkdir sqlite-amalgamation
root@machine [/usr/local/src/subversion-1.6.17]# cp ../sqlite-autoconf-3070800/sqlite3.c ./sqlite-amalgamation/

Now, you need to configure Subversion in order to install it:

root@machine [/usr/local/src/subversion-1.6.17]# ./configure --with-apxs=/usr/local/apache/bin/apxs --with-apr=/home/cpeasyapache/src/httpd-2.2.19/srclib/apr --with-apr-util=/home/cpeasyapache/src/httpd-2.2.19/srclib/apr-util
root@machine [/usr/local/src/subversion-1.6.17]# make && make install

You may need to update your path to httpd, if you get the following error:

checking for APR... configure: error: the --with-apr parameter is incorrect. It must specify an install prefix, a build directory, or an apr-config file.
 
root@machine [/usr/local/src/subversion-1.6.17]# cd /home/cpeasyapache/src/
root@machine [/home/cpeasyapache/src]# ls -all

Look for httpd-*.*.** (where the stars are your script version) and update accordingly. cd back to the subversion directory and run the configure, make and make install commands again.

Subversion and Apache

Given you are using WHM and it uses EasyApache, you can’t just modify the Apache configuration to include the Subversion modules. Instead, you must use the Include Editor, which you can find by browsing to “Apache Configuration” in the WHM menu. There, you must use the Pre Main Include. Select the latest Apache version from the dropdown list and a textarea will appear. Add the following to it and click on “Update”:

LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so

Add Subversion users

Run the following command for every user you wish to add. You will be asked for passwords for each one:

root@machine [~]# /usr/local/apache/bin/htpasswd -cm /etc/svn-auth-conf username

Set up your first repository

I am assuming you are going to set up more than one repository on one domain, but this also applies to when you want just one repository.

First, you need to add a new virtual host for your repository:

root@machine [~]# cd /etc/httpd/conf/userdata/std/2/

If you get a “-bash: cd: /etc/httpd/conf/userdata/std/2/: No such file or directory” error, run these commands to create the folder:

root@machine [~]# mkdir /etc/httpd/conf/userdata
root@machine [~]# mkdir /etc/httpd/conf/userdata/std
root@machine [~]# mkdir /etc/httpd/conf/userdata/std/2

Now, add a folder for your domain:

root@machine [~]# mkdir /etc/httpd/conf/userdata/std/2/example
root@machine [~]# mkdir /etc/httpd/conf/userdata/std/2/example/example.com
root@machine [~]# cd /etc/httpd/conf/userdata/std/2/example/example.com

Next, you need to create the Subversion config file

root@machine [/etc/httpd/conf/userdata/std/2/example/example.com]# nano svn_custom.conf

Paste the following config and save the file. For every repository you wish to create, add another <location> block

<IfModule mod_dav_svn.c>
    #begin location
    <location /repo>
	DAV svn
	SVNPath /home/example/public_svn/repo/svn/
	AuthType Basic
	AuthName "SVN Repo"
	AuthUserFile /home/example/.svn.htpasswd
	Require valid-user
    </location>
    #end location
</IfModule>

Notice that we are using a folder called public_svn, outside the standard public_html folder. It is not recommended to add repositories in the same place as the public webserver files, because you are going to confuse Apache when you are going to try to access the repository through a local Subversion interface. Although you will be able to load the repository in your browser, you will get a “Repository moved permanently to ‘http://example.com/repo/’; please relocate” error when trying to access remotely. This happens because Apache doesn’t know how to respond to the request and gets confused on what it should return.

Now, create the actual repository files:

root@machine [~]# su example
example@machine [/root]# cd ~
example@machine [~]# mkdir public_svn
example@machine [~]# mkdir public_svn/repo
example@machine [~]# cd public_svn/repo
example@machine [~/public_svn/repo]# svnadmin create svn
example@machine [~/public_svn/repo]# chmod 775 -R svn
example@machine [~/public_svn/repo]# exit

Update the Apache configuration and you are all set up:

/scripts/ensure_vhost_includes --all-users

Go to http://example.com/repo to test your repository. It should ask for your user and password and then show you something like the following: