Using the hook_user for user validation such as preventing an email for a username

I run a Drupal 6 site that gets a lot of registrations every day and sometimes people register with their email address as their username. The problem is that the username is displayed anytime that user posts content on the website so we try to discourage this. I finally got fed up and just wrote a bit of code that uses hook_user and would go into your custom module. It looks something like this:


function custommodule_user($type, &$edit, &$account, $category = NULL) {
  if ($type == 'validate') {
    $regexp = '/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$/';
    if (preg_match($regexp, $edit['name'])) {
      form_set_error('name', "Do not enter an email address 
        in the username field as it is publicly viewable.");
    }
  }
}

Replace custommodule with the name of your module. The rest of the code simply looks to see if the validate operation is being performed, checks the name form field and does a simple preg_match to see if it looks like an email.

Using .htaccess to fix the Facebook like IE fb_xd_fragment bug

If you notice any hits to URLs on your website with a querystring variable that look like:

http://mywebsite.com/somepage?fb_xd_fragment

Then chances are you have the Facebook like button installed and you are experiencing the notorious fb_xd_fragment bug caused by Internet Explorer. There are lots of solutions but if you use a *nix host and have access to your .htaccess file, the easiest thing to do is strip querystring variables matching fb_xd fragment.

Add the following lines and it should fix the problem. (Make sure you replace example.com with your actual URL.):


# Fix facebook bug
RewriteCond %{QUERY_STRING} ^fb_xd_fragment
RewriteRule ^(.*)$ http://example.com/$1? [R=301,L]

Make sure you test on your website before just assuming this works!

Apparently someone working on Google Analytics' API has a sense of humour

Ok, maybe sense of humour is a stretch but they are at least huge fans of Street Fighter.

I was working on integrating the Google Analytics API into a website and came across this little snippet at the very end of their common query samples. I attached a screenshot.

Ken vs Ryu

This query returns data on battles fought between Ken and Ryu, sorted by number of fireballs thrown in descending order.

dimensions=ga:fighterName1,ga:fighterName2
metrics=ga:roundsWon,ga:punches,ga:kicks
filter=ga:fighterName1==Ken,ga:fighterName2==Ken;ga:fighterName1==Ryu,ga:fighterName2==Ryu
sort=-ga:fireballs

Just kidding, this one doesn't actually work, but it would be so cool if it
did...

Minimize logged in users on your Drupal website to reduce load on your server

If you have lots of authenticated users, then you probably know that most of Drupal's caching is pretty much thrown out the window for logged in users. By default, when a user logs in, the user will stay logged in for 200000 seconds (that's just over 3 weeks!)

That equals 23 days that your Drupal website is probably working its ass off serving mostly dynamically generated pages. Instead, Drupal could simply be grabbing the page out of the page cache and handing it to an anonymous session whilst sipping margaritas in its spare time. (That's what I'd be doing with free cycles anyways.)

There's a cheap trick in Drupal 6 and 7 to reduce the number of authenticated users thereby reducing the load on your server. In the settings.php file (located in the /sites/default folder), there is a setting called session.cookie_lifetime. Change this to something a little more draconian such as 7200 (2 hours); enough time to post a comment or whatever and logout (or be forcibly logged out).

Obviously, this won't be practical for lots of sites but for some sites it can make a huge difference.

Happy Drupaling!

Quick and dirty data geocoding using Drupal 6 and Google's Geocoding API

I recently got a bunch of business data that I needed to match up with existing data for one of the sites that I look after. Unfortunately, the data was not in a super convenient format nor was it geocoded. There are some really neat APIs that have recently come out that make problems like this much easier to solve such as Factual's Crosswalk API but unfortunately this isn't available in Canada yet. Anyways, I managed to geocode the data pretty quickly and then match it up using a phone number or textual comparison.

I happened to have an older function laying around to geocode data using V2 of Google's Geocoding API. Although this doesn't support all the fancy new features of V3 such as neighbourhood data, it serves my purpose and has the added bonus of allowing me to do more than 2500 queries in a day which is the cap on V3's free version. You will need an API key, however.

I've pasted the code at the bottom of this post and attached a file as well in case there are formatting issues. The code was originally designed to work within a Drupal module specifically with nodes and can easily be adapted to do that again. That is why it makes a fake node and uses that information to do the geocoding.

The first function tools_geocode_data needs to be modified to your specific use case and match up with your data. In my case, I am querying a mySQL database and iterating thru a table called "data". You also will need to edit the $api variable in the tools_geocode_location function and enter your Google API key.

Happy coding!


function tools_geocode_data() {

$result = db_query("SELECT * FROM {data} WHERE (lat = 0 OR lat IS NULL)");

while ($data = db_fetch_object($result)) {

$node = new stdClass();

$node->field_location_street[0]['value'] = $data->address . " " . $data->city;
$node->field_location_postal[0]['value'] = $data->zipcode1 . $data->zipcode2;

$location = tools_google_geocode_location($node);

//drupal_set_message("GEOCODE: " . $result['lat'] . " - " . $result['lon']);

if ($location['lat']) {
$cnt += 1;
db_query("UPDATE {justeatimport} SET lat = %f, lon = %f WHERE id = %d", $location['lat'], $location['lon'], $data->id);
usleep(250000);
}

}

drupal_set_message("Updated $cnt businesses.");

}

function tools_geocode_location($node) {

$key = "INSERT YOUR API KEY HERE";

$query = array(
'key' => $key,
'sensor' => 'false', // Required by TOS.
'output' => 'xml',
//'ll' => 0,
//'spn' => 0,
'gl' => 'CA',
'q' => tools_address_flatten($node),
);

$url = url('http://maps.google.com/maps/geo', array(
'query' => $query,
'external' => TRUE,
));

//drupal_set_message("GOOGLE: " . $url);

$http_reply = drupal_http_request($url);

$status_code_match = array();
preg_match('/(.*)/', $http_reply->data, $status_code_match);
$status_code = $status_code_match[1];
if ($status_code != 200) {
return NULL;
}

$accuracy_code_match = array();
preg_match('/Accuracy="([0-9])"/', $http_reply->data, $accuracy_code_match);
$accuracy_code = $accuracy_code_match[1];
if ($accuracy_code return NULL;
}

$latlon_match = array();
preg_match('/(.*)/', $http_reply->data, $latlon_match);

//drupal_set_message("lat/lon: " . $latlon_match[1], 'status');

$latlon_exploded = explode(',', $latlon_match[1]);

return array('lat' => $latlon_exploded[1], 'lon' => $latlon_exploded[0]);

}

function tools_address_flatten($node) {
// Check if its a valid address
if (empty($node)) {
return '';
}

if ($node->nid) {
$term = taxonomy_node_get_terms_by_vocabulary($node, 1, 'vid');
$city = $term[1]->name;
$province = $term[1]->description;
}

$address = '';
if (!empty($node->field_location_street[0]['value'])) {
$address .= $node->field_location_street[0]['value'];
}

if (!empty($city)) {
if (!empty($node->field_location_street[0]['value'])) {
$address .= ', ';
}
$address .= $city;
}

if (!empty($province)) {
if (!empty($node->field_location_street[0]['value']) || !empty($node->field_location_city[0]['value'])) {
$address .= ', ';
}

$address .= $province;
}

if (!empty($node->field_location_postal[0]['value'])) {
if (!empty($address)) {
$address .= ' ';
}
$address .= $node->field_location_postal[0]['value'];
}

return $address;
}

?>

Parse emails and insert into a MySQL Database using PHP

I had a problem where I needed to parse emails and insert them into a MySQL database in real time as they are received. This is a relatively easy problem to figure out, but I wanted to outline the steps I took to tackle this in case anyone else wanted simple, step by step instructions on how to solve this problem.

First of all, this solution uses Linux, specifically I like to use Ubuntu. I also used Postfix, an open source mail server. Finally, the mail script is based on Eric London's mail parsing script so he did all the mail heavy lifting. I simply added some database stuff to insert it into a MySQL table using PDO. Props to Eric for a great script that simplified this task for me.

To start, you will need a linux server. I use Amazon EC2 instances in the cloud so I just fire up one of alestic's AMIs and bam, server. I make sure the security preferences allow incoming on port 25 (SMTP). (Note: if you are having issues, it could be a security / firewall issue.) For testing purposes I use the micro AMI because they are inexpensive.

Once I have SSH-ed into the box, I run a few commands to make sure everything is up to date and that I have all the tools and software I need installed.


apt-get update && apt-get upgrade -y
tasksel install lamp-server
apt-get install postfix php5-cli

You don't actually need Apache for what we are doing here but I am installing the entire lamp stack for ease and simplicity and I will be using it later.

Let's create our database called email and the mail table where we will store the incoming email:


mysql -p
create database email
CREATE TABLE `mail` (
`mid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created` int(10) unsigned NOT NULL DEFAULT '0',
`body` longtext,
`return_path` varchar(255) DEFAULT NULL,
`x_original_to` varchar(255) DEFAULT NULL,
`delivered_to` varchar(255) DEFAULT NULL,
`mailto` varchar(255) DEFAULT NULL,
`subject` varchar(255) DEFAULT NULL,
`message_id` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
`maildate` int(11) DEFAULT NULL,
`mailfrom` varchar(255) DEFAULT NULL,
`added` tinyint(3) unsigned NOT NULL,
`content_transfer_encoding` varchar(255) DEFAULT NULL,
PRIMARY KEY (`mid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='incoming mail';

Its important that the default charset is UTF8 because this can be a problem if you are in the UK or other countries where character set issues can happen.

Now we are going to make a folder in our home directory and put our mail script in there:


mkdir /home/default/scripts

I have attached the mail.php script file to this post so grab it and put it in your scripts directory that you created above. (Its named mail.php_.txt, you you will need to rename it to mail.php.)

You will need to edit the configuration variables at the top of the file to connect to the database properly and send emails to your email address if there are errors. Another thing to keep in mind is that the script uses PDO so your version of PHP needs to support that (PHP 5.1 or greater).

Now we edit our Postfix aliases file to pipe mail from a specific account to a script that we will be added later:


nano /etc/aliases

Add a line at the bottom of the file:


import: "|php -q /home/default/scripts/mail.php"

Import is the name of the account so if you want the beginning of the email address (before the @) to be something different, change this.

Finally, you are going to need to setup some DNS entries to route mail to your linux server: an A and MX record. There are lots of resources on how to do this on the Internet. Lets say the email address you were using for this was import@server.domain.com, you would need two records to make this work:

server.domain.com. IN A 50.50.50.50
server.domain.com. IN MX 10 server.domain.com.

If you don't know what any of this means, hopefully you have access to a web interface that can set this up for you.

Assuming everything went well, you should be able to send email to your linux server and it will be inserting the mail into your database table. Enjoy!

Setting up Appcelerator Titanium under Mac OSX to build mobile apps for iPhone and Android

I recently inherited an older Macbook from my wife after buying her a new fancy Macbook Pro for Christmas. After doing some research, it turns out that using a Macbook is probably the best way to develop mobile applications because the iOS (iPhone OS) SDK requires a Mac to work. Of course, the Android SDK is platform independent because its Java-based. So I might as well put the Macbook to use.

Well, first thing I discover is you need Snow Leopard (OSX 10.6) or later to install the iOS SDK. I am running 10.5.8 so I need to get a copy of Snow Leopard. Well, it turns out this was easier said than done because Lion is coming out and I could not find a copy anywhere. I end up having to buy the family license for $50 rather than the single license for $30. Oh well.

Upgrading to Snow Leopard was a snap. Installing Titanium was simple; I just needed to register an account, select the freebie version of Titanium and had it running in minutes. Now I need to install the iOS and Android SDKs.

The iOS SDK is only available to Apple developers so I had to pay $99 for the privilege. Once I was registered, installation was simple and Titanium automagically recognized the iOS SDK was installed.

The Android SDK was a little tougher. It was unclear as to which version of the SDK I should download and the one I selected was obviously wrong so I just downloaded them all. Once I added a line to my .bash_profile file pointing the $ANDROID_SDK variable to my install directory, everything worked fine.

So now I have a fully functional development environment for creating mobile applications in iOS and Android. Now what?

Bitcoin trading really, really spikes

I posted an entry some time ago about how Bitcoin trading had spiked from 6 cents to 25 or 30 cents USD. I haven't really looked at the price lately because I've been busy with other projects. I noticed that the blog entry was getting some traffic over the last couple months and checked the current price.

Imagine my surprise when I see that Bitcoin is trading at over 8 dollars! You can check out recent trading data on Bitcoin Trading Exchanges.

I think I need to backup my bitcoin wallet...

Cron, PHP and Drupal development (using the cron hook)

A while back I talked about doing development using the cron hook and how it was not very intuitive to use because if you have an error, it never gets displayed.

There is a handy module called Elysia Cron that allows you to have granular control over your cron jobs and how they are run. You can run functions in your module by clicking on them or set them to run at whatever intervals you want. All Drupal websites that have any sort of custom cron jobs need this module. Unfortunately, there is only a Drupal 6.x stable release right now.

So when you are creating a cron hook in your module, simply create a custom function that does whatever task you need to get done. You can then run this function manually in Elysia Cron to debug and make sure it produces the desired results. And then when its ready for prime time, you can call your custom function in Elysia Cron's configuration, or just add your custom function call in the cron hook function of your module and you are ready to rock.

Run persistent jobs over SSH using screen

I have a bunch of scripts that I run periodically from the command line by SSHing into a server and running the command. The problem is that sometimes these scripts can take a while and I might lose connectivity, want to reboot my machine, whatever. Yes, you can run these in the background or use nohup, but I want to run these interactively in the foreground and watch the output. Enter screen.

Screen is a windows manager for your command line sessions and is extremely powerful. I am not going to get into all that it can do but am going to simply focus on how easy it is to solve this particular task.

To start, you will need screen installed on the remote unix machine. On Ubunutu 10.04, this should already be installed. Then you simply run screen, run your job and then you can detach and re-attach to your screen session as desired:


ssh remote_machine
apt-get install screen
screen
run your_script

To detach from screen session:
screen -d

To re-attach screen session:
screen -r

If you have multiple screen sessions, you will have to specify which one when using the -r option, but this is pretty straight forward.

Now you can run your scripts without having them terminated (unless they actually blowup!)

Dealing with different character sets

I've been working on a project using MySQL, PHP and Querypath, an XML / HTML document parser where I need to store content retrieved from webpages into MySQL tables. I'm using the standard cURL to retrieve the pages, Querypath to parse them and then insert data into various tables in a MySQL database.

Everything worked great when dealing with US and Canadian websites. Then I started trying to do similar stuff on some UK websites and weird, random issues starting popping up that I couldn't really explain. One problem was that I didn't have the character set for my MySQL tables set to UTF-8 so that was an easy fix. But some pages were still parsing correctly and others weren't for no apparent reason.

After smashing my head against the wall for a few hours, it popped into my head that it could be a character set problem even though because I never had this problem with the US websites. I simply changed the options on my Querypath object as follows:

$qp = htmlqp($html, NULL, array('convert_to_encoding' => 'utf-8'));

The weird thing about this is that Querypath should be able to detect the character set because its even set in the document but maybe not. Anyways, this fix solved my problems.

Using Amazon EC2 micro instances to create development sandboxes for Drupal

I recently contracted out some work for a project I am working on in Drupal because it involved CSS, HTML and theming and quite frankly, my skills in that area suck. I used ODesk because I do some other data entry outsourcing there and it works quite well. Anyways, it was relatively easy to find a qualified person to do the work. Now the supposedly hard part, setting up a sandbox for a remote worker to access and do the work. This was a rush job that needed to get done yesterday so I needed something setup quickly.

Well luckily with Amazon EC2 and its nifty cloud, this turned out to be quite easy. I created a a micro instance in the US East (Virginia) region which costs a whopping 2 cents per hour (works out to about $15 / month). This can be decreased significantly by purchasing reserved instances. I used an EBS-backed instance so that I can start / stop it at will and not have it running if I don't need. I also assign a custom security group to it so I can add or drop custom firewall rules and it doesn't affect my other live instances. Finally, I use Ubuntu 10.04 LTS and Alestic's AMIs because its fast and simple to setup and run.

So now I have a running Ubuntu instance, now what? Well with a few simple commands, I can get a Drupal website up and running. Keep in mind this is a short term server that I need to focus on ease of use, not security so what I am doing would not be appropriate for anything else.

Copy authorized keys to root .ssh dir so I can login as root:
sudo cp ~/.ssh/authorized_keys /root/.ssh/

Now logout and SSH back in as root.

Update your Ubuntu instance:
apt-get update && apt-get -y upgrade

Install LAMP server:
tasksel install lamp-server

Edit your default site Apache configuration file with your favourite editor and point to your new htdocs dir (/home/htdocs):
pico /etc/apache2/sites-available/default

Enable mod rewrite for Drupal and reload Apache:
a2enmod rewrite
/etc/init.d/apache2 reload

Login to MySQL and create your database:
mysql -p
create database databasename

Now ssh to your live/development server and create a backup of your site with db to move to sandbox:
cd /home/htdocs
mysqldump -p databasename > db_date.sql
cd ..
tar .zcvf mywebsite.tar.gz htdocs/
rm htdocs/db_date.sql
scp mywebsite.tar.gz sandbox:/home/htdocs/

Now flip back to your sandbox and extract your tar file:

tar xfz mysqwebsite.tar.gz

Edit your Drupal settings file to point to database:

pico sites/default/settings.php

Load your database from the dump and delete the dump file:

mysql -p databasename < /home/htdocs/db_date.sql
rm /home/htdocs/db_date.sql

Now your website should be operational.

For quick access to your remote developer you can simply enable password security for your SSH daemon by changing PasswordAuthentication setting to Yes, assign a password for root and reloading SSH daemon:

pico /etc/ssh/sshd_config
passwd
/etc/init.d/sshd reload

Because most FTP clients support SFTP (FTP over SSH), your remote developer can now access your sandbox and you should be good to go.

Keep in mind that giving out root access is not ideal, nor is most of what I have talked about here good for security. In fact, it flies in the face of most best practice security measures that should be taken for any server. But if you need to get a server up and running fast for a remote worker to get stuff done quickly, it works and does the job.

Make sure you stop and/or terminate your instance as soon as you are done with it! Enjoy.

Managing linux EC2 instances (or any remote linux machines)

Its been a while since I posted anything so I thought I would talk about how I've setup my servers using Amazon's EC2 cloud services for easy administration. I have several instances running mostly Ubuntu 10.04.

Some of this stuff may be pretty simple to you depending on your level of expertise but it really simplified administration for me so I wanted to share.

I put all my keys in a folder in my home directory and then I have a file named config in my ~/.ssh folder that looks something like this:


Host server_alias
User ubuntu
Hostname public_address_to_server
IdentityFile ~/.keys/key_file.pem
Localforward 6033 localhost:3306

I have an entry like what is shown above for each server. Once you have a host setup in your config file, you can ssh to your server with one very simple command:

ssh server_alias

Wow, that was easy. There's also a bunch of other cool things you can do once you have this setup.

You'll notice I have a local forward setup on port 6033. This allows me to connect to my remote MySQL server (running on its default port of 3306) once I have established an SSH connection using the port 6033. I can use MySQL GUI tools or simply connect with the MySQL command line utility like so:

mysql -h 127.0.0.1 -P 6033 -p

I am not sure why but for whatever reason, it doesn't work if I enter localhost but if I use 127.0.0.1 it does. Weird.

File sharing over ssh is also super simple. Simply use the sshfs command line tool:

sshfs server_alias:/home/whatever_dir ~/server_alias

Booyah, now I can manipulate folders and files on my remote server locally. If you want to kill the share, simply use:

fusermount -uz server_alias

Props to @netwonk for helping me get a lot of this stuff going. Happy server administrating!

Removing querystring variables from a URL in PHP

Recently I was trying to remove some variables / keys from a URL in PHP. I wanted a simple function that I could pass a URL and an array of keys to and that would return a modified URL without the keys I specified to have removed. The resulting solution turned out not as simple as I had hoped, but here it is anyway:

function remove_querystring_vars($url, $keys) {

$url = parse_url($url);

parse_str($url['query'], $query);

foreach ($keys as $key) {
unset($query[$key]);
}

$url['query'] = http_build_query($query);

$value = glue_url($url);

return $value;

}

// $parsed is a parse_url() resulting array
function glue_url($parsed) {

if (! is_array($parsed)) return false;

if (isset($parsed['scheme'])) {
$sep = (strtolower($parsed['scheme']) == 'mailto' ? ':' : '://');
$uri = $parsed['scheme'] . $sep;
} else {
$uri = '';
}

if (isset($parsed['pass'])) {
$uri .= "$parsed[user]:$parsed[pass]@";
} elseif (isset($parsed['user'])) {
$uri .= "$parsed[user]@";
}

if (isset($parsed['host'])) $uri .= $parsed['host'];
if (isset($parsed['port'])) $uri .= ":$parsed[port]";
if (isset($parsed['path'])) $uri .= $parsed['path'];
if (isset($parsed['query'])) $uri .= "?$parsed[query]";
if (isset($parsed['fragment'])) $uri .= "#$parsed[fragment]";

return $uri;
}

?>

Basically, its two functions. The first function called remove_querystring_vars expects the url and a simple array of keys to be passed to it. The second function glue_url is required as a helper function to re-assemble the url after its dismantled. Unfortunately, there isn't an opposite function to parse_url built into the PHP library so this function is required.

Here is a sample usage:

<?php

$keys = array("foo", "foo2");
$url = "http://www.myurl.com/somepage.php?key=value&foo=bar&foo2=bar2&key2=value2";
print remove_querystring_vars($url, $keys);

?>

This should return:


http://www.myurl.com/somepage.php?key=value&key2=value2

Drupal 6: module development using the cron hook

This is interesting (at least to some people). I have been working on using the hook_cron in order to tabulate reviews on a site and assign a useful and helpful score. So I add all my code and start debugging and I see an error in the watchdog:

Cron run exceeded the time limit and was aborted.

Thats strange since cron.php ran for less than a second and I know for a fact that the max_execution_time setting in the php.ini file is set to 30 seconds.

Well, it turns out than any error in your module's hook_cron code will generate this error. So i simply tailed my apache error log and found out the error.

Perhaps a more descriptive error needs to be added to Drupal?

Bitcoin trading spikes

I discovered bitcoin this summer and was intrigued. For those who don't know what it is, its essentially a peer to peer Internet currency thats decentralized and completely untraceable.

I have been running the bitcoin client for a few months now and have managed to generate some coins from that. I also bought a bunch of bitcoins when I first started using it. I think they were trading at around 6 cents USD per bitcoin. I just checked now and the price has spiked to around 25 or 30 cents. You can check out bitcoin trading data at MT GOX, a bitcoin exchange.

If things keep going, I might have to incorporate this into my portfolio valuation! ;)

Drupal module development and block caching issue

I have spent the past couple hours trying to debug a module that creates a bunch of content blocks on a Drupal 6 website. These blocks were set to BLOCK_CACHE_PER_PAGE setting but I changed them to BLOCK_NO_CACHE because I am implementing my own caching on them.

Well, turns out that even if you enable / disable your module and update your block admin page, Drupal never changes the block's cache setting. This is apparently not by design.

I will do a future post about implement custom caching in your modules in the future because I have learned a ton while completing gutting the caching that I did a year ago (which I did a very poor job of). Until then, I guess I chalk up the hours of frustration to "experience".

Caching trick when doing web development with Chrome

So the Chromium browser really loves to cache, its one of the reasons why its so fast. In fact, not even the usual holding of the SHIFT key and pressing refresh will bypass its caching, at least not using the latest version (7.0.517.41) under Ubuntu.

One workaround is to go into your preferences, click the "Under the hood" tab, click the "Clear browsing data", select "Empty the cache" checkbox, then click the "Clear browsing data" button. Wow, thats a lot of steps and when you're developing, this is a real pain in the ass.

There is another solution though. You can pass startup options to Chrome and set the cache to zero, that way the cache is never used. This is perfect for development because really, how often have you been screwed and sent on wild goose chases because of browser cache meanwhile you were trying to figure out why your changes didn't fix the problem?

So create another shortcut to Chrome and call this one Dev Chrome, or something uniquely identifiable. Edit the properties, specifically the startup options (called Command in Ubuntu) and add a parameter at the end that says:

--disk-cache-size=0

So the entire line should look something like:

/usr/bin/chromium-browser %U --disk-cache-size=0

Now when you do web development, launch and use your dev version of Chrome instead of your regular version. Voila!

New System 76 Laptop

After hunting around for a new laptop, I finally settled on a System 76 "Pangolin Performance". I already have a desktop and so originally was going to go with something a little more compact, but ended up going with something a little bigger (and way more badassed.)

I pimped it out with pretty much the best of everything including a Core i7-740QM processor, 8 gb of ram, Intel Centrino Ultimate wifi card, 500 GB 7200rpm drive and extra battery/power adapters. All in all, a pretty good value when you figure it came to around $1800 including over $100 in shipping and the 2 year warranty. I was a little annoyed that I had to pay $200 in "duty" aka HST but hopefully I get most of that back since my business is paying for it.

Anyhows, on to the laptop. First impression is that this machine #$@#$ing screams. The quad core, dual threaded processor is fast. I like to run multiple VMs using VirtualBox so I assume they will run well with the processor and ample RAM.

It actually has a full keyboard including number keys on the side. The touchpad is unique with a kind of rough, bubbled surface that takes a little to get used to. I am not sold on the biometric scanner in between the two trackpad buttons but we will see how that works out.

The only thing I am disappointed with so far is the battery. I was expecting poor battery life in the neighborhood of 2-3 hours but so far I am seeing less than that. I am going to try using powertop and see if I can optimize that.

Overall a pretty cool laptop; hopefully I can squeeze some more life out of the battery. Good thing I got an extra battery and power adapter...

Free hosting for a year

Amazon recently announced that they are giving away a free micro instance to new accounts on their EC2 service. They call it their free usage tier.

So what is a micro instance then? Well, its their smallest computing unit in Amazon's cloud service. Basically, fire up one of these bad boys and you have your own little virtual server that exists somewhere in Amazon's vast computing nebulous.

What can you do it with it? Anything you want really. But most people will probably run linux on it and set up a LAMP (linux, apache, mysql, php) server to run web sites / applications.

What kind of experience do I need? Unfortunately, you need some skills to use Amazon's cloud services, its like running your own server with a little added complication.

I have found that micro instances are decent for most small to medium websites and testing. However, they can be a little underpowered especially with memory because they only have 630 something megabytes of RAM.

But who can complain with free? Existing customers like me I guess who the deal doesn't extend to.

More information on Amazon's EC2 free usage tier.

Drupal Theme Upgrade Woes

It seems you learn (or don't learn from past mistakes) every day. I was trying to do an upgrade tonight and just finished wrangling with the server for over an hour. It seems that Drupal 6.x really doesn't like it if you rename your theme directory and then create a new theme directory with the old name with your new files.

It caused all kinds of problems and eventually the web server starting throwing 503 service unavailable errors which will really make you scratch your head. I thought I was being smart by keeping a handy backup of the old theme directory in case something went wrong. Instead, it ended up just causing me a ton of headaches.

Once I realized the web server was fine and it was actually Drupal that was messed up, I simply started from scratch with my old Drupal installation. Then I deleted the theme folder and replaced it with the updated theme. Then I did a submit on the themes page (Site Building > Themes) and everything was fixed.

Mailer and Spam Woes

Wow, sometimes you just have to shake your head and laugh. I spent about an hour debugging a new web server trying to figure out why exim4 wasn't sending mail for the Drupal contact form. Well, it turns out it was the whole time and now I know way too much about Exim. The sender address was the same as the recipient so GMail was treating all my mail as spam. Shouldn't I have figured this out by simply looking at Exim's logs. Yeah, probably. I hate to be a super nerd but this is a classic example of PEBKAC - problem exists between keyboard and chair.

Deformed bottle of Budweiser

My cousin brought a case of Budweiser over to my place recently. He purchased it from the local liquor store on Main Street here in Vancouver.

The Vancouver Courier is littering on my property

I hate the Vancouver Courier. I just left a nasty message on their distribution voicemail because once again, they have delivered the Courier newspaper to my house even though we have requested not to have it delivered.

Actually we have called them 7 times, 5 times in the past few weeks. At what point does this become an issue of littering?

It wouldn't be so bad if the city of Vancouver wasn't on strike and they collected our recycling or if I didn't give a shit about the environment and think about how many trees are being destroyed to make this shitty newspaper.

I wish I knew what time the Courier delivery guy actually came by. I could have a little chat with him and make sure he never, ever delivered this crappy rag to my house again. I don't think calling them again is going to have an effect, it hasn't done anything thus far.

Tags: 

Poor quality keyboard mouse combo

I was recently reminded why buying the cheapest product can sometimes blow up in your face. I started work one day last week only to find my keyboard was broken. I tried to get it working for about a half an hour to no avail, chucked it in the bin and rolled down to the local computer store.

They had a huge, irresistible stack of keyboard mouse wireless combos for $40 in the centre of the store. It was like a tractor beam pulled me in and made me grab one.

Within 5 minutes of using it, I knew it was not going to work out. The keyboard is small and awkward but is usable. The mouse has to be the worst mouse ever designed. When you move it to click on something, half the time it will slide to the left for no reason whatsoever. The scroll wheel clicks loudly and turns clumsily. Definitely, the worst mouse I have used since about 1998.

Do not be sucked in by the sweet price, this is a total dud.

Tags: 

Facebook could easily be used for evil

A friend of mine recently abandoned his web site in favour of Facebook. It got me thinking about all the information on Facebook and how it could be used, specifically for evil purposes. The information about people includes the obvious such as religious and political beliefs and all kinds of other personal data. This combined with your viewing history and the information that could mined is almost infinitely vast.

Google obviously sees the value in the social networking site as they reportedly made an offer of 2 billion dollars last spring to Facebook. If any company could mine and monetize a site, Google is definitely up for the challenge. Even with their anti-evil company mantra, saying and doing are two completely different things in my experience, perhaps the temptation would be too alluring.

I took a look at Facebook's terms of use to find out what rights you were giving up on content you uploaded or added to the site. Sure enough, I was alarmed by what I discovered.

"By posting User Content to any part of the Site, you automatically grant, and you represent and warrant that you have the right to grant, to the Company an irrevocable, perpetual, non-exclusive, transferable, fully paid, worldwide license (with the right to sublicense) to use, copy, publicly perform, publicly display, reformat, translate, excerpt (in whole or in part) and distribute such User Content for any purpose on or in connection with the Site or the promotion thereof, to prepare derivative works of, or incorporate into other works, such User Content, and to grant and authorize sublicenses of the foregoing."

I guess its not really surprising that the site terms would contain a carte blanche statement that allows Facebook to do whatever they want with any content you add to the site. This could include the obvious of selling the data to marketing companies similar to what credit card companies have been doing for years. It doesn't take much imagination to come up with much more sinister uses of the data however far fetched.

I am still using Facebook even knowing this but have decided to take a more careful approach to what data I share and give up all rights and title to.

Where is Vancouver's waterfront stadium?

I was asking myself this question last week because I haven't heard much about this recently. Those who listen to the Pratt & Taylor show on Team 1040 know how its a constant topic of outright ridiculousness that could only happen in Vancouver. How could a sports facility that is 100% privately funded and overwhelmingly supported by the Vancouver populace not have been built by now?

It was originally supposed to be built in time for the soccer U20s hosted in Canada recently but that date has come and went, even though the stadium idea had four years to be planned and constructed. Yet still there is no stadium.

Well, it appears as though a third site is now being proposed for the downtown stadium east of the two originally planned sites near the Helijet terminal. Hopefully the politicians will get off their asses and deliver what this town desperately wants and needs: a world class, outdoor sports facility for soccer and other events.