Monday, November 17, 2008

Idaho SEO

It really bothers me when local businesses don't take the time to create a website and optimize it for the search engines. Nothing like searching for a local company and having a bunch of national directory websites show up in the search results instead. Have businesses in Idaho ever heard of Idaho SEO? With a few simple SEO tweaks to their websites they could boot the national sites from the list and make a lot of local customers happy.

Tuesday, November 11, 2008

See What Version of a Package Is Installed on Ubuntu

To see what version of a package is installed on Ubuntu, or a Debian version of linux:

dpkg -s

Monday, October 27, 2008

jQuery Image Gallery, jQuery Slideshow

The Galleria module for jQuery makes a really great jQuery Image Gallery. Added some tweaks to it, and now I've got a jQuery Image Gallery and Slideshow. With features such as pause on hover, and a contact sheet.

Friday, October 10, 2008

Theme Drupal Search Results

I had the most awful time trying to theme the drupal search results page and remove the silly "node type", "author", "date" and "extra" info from the search results.

Initially I tried the easy route which was to copy modules/search/search-result.tpl.php into my theme and then in the themes template.php file remove the search-info div. Because that's the info I didn't want to show up.

Didn't work. Worked only for /search/node. Failed when going to /search/node/some%20search%20term.

Finally was able to get it working by implementing hook_prepocess_search_result, and making sure the $info array didn't have anything.

* Implement hook_preprocess_search_result()
function phptemplate_preprocess_search_result(&$variables) {
$result = $variables['result'];
$variables['url'] = check_url($result['link']);
$variables['title'] = check_plain($result['title']);

$info = array();
if (!empty($result['type'])) {
$info['type'] = check_plain($result['type']);
if (!empty($result['user'])) {
$info['user'] = $result['user'];
if (!empty($result['date'])) {
$info['date'] = format_date($result['date'], 'small');
if (isset($result['extra']) && is_array($result['extra'])) {
$info = array_merge($info, $result['extra']);
// Check for existence. User search does not include snippets.
$variables['snippet'] = isset($result['snippet']) ? $result['snippet'] : '';
// Provide separated and grouped meta information..
$variables['info_split'] = $info;
$variables['info'] = implode(' - ', $info);
// Provide alternate search result template.
$variables['template_files'][] = 'search-result-'. $variables['type'];

With this gem in my hook_theme implementation

'search_result' => array(
'arguments' => array('result' => NULL, 'type' => NULL),
'template' => 'search-result',
'path' => drupal_get_path('module', 'search')

Monday, October 06, 2008

Disable the Drupal User Contact Form

The best way I've found to disable the user "contact form", is to hook_menu_alter and remove the 'contact' fieldset from the user profile form itself.

function modulename_form_alter(&$form, $form_state, $form_id) {
switch ($form_id) {
case 'user_profile_form':

Thursday, September 25, 2008

Webware for Python, and WebKit

So does anybody still use Webware for Python? And how do the developers of Webware feel about WebKit? Now there stuck with names for their products that companies with bigger pocket books have rebranded.

Ask anyone about WebKit, they won't tell you its a python application server that runs on top of Webware. No, they'll say, WebKit is an open source web browser engine with roots in KHTML, that Apple has invested heavily in.

Monday, September 08, 2008

Drupal CCK Search

So you've added a custom content type, you've added CCK fields to it. Now you want to create some pages, to provide search result lists for your custom content type.

If your using Drupal 6, make sure the CCK Module, the Views Module, and the Advanced Help module are installed.

Then within the drupal admin, go to "Views". This is where you can create your custom search result pages. The advanced help module, will help you make sense of things, and the "Views" module itself is great in that it tells you the exact SQL that will be used to generate your search results. You can use any thing related to your node to help filter your results; be it CCK, Taxonomy or many other options.

Saturday, September 06, 2008

Drupal Gripes

Today's Drupal gripes:
  • Everything in the database
  • Difficult to move between development and production servers
  • Version Control, difficult to have a running production server, add changes to a development server, and then merge the changes into a production environment
  • No Unit Testing
Why Java, .Net, Ruby, Django, Pylons, TurboGears, etc are better
  • Unit Testing
  • Easy to move between development and production code

Friday, September 05, 2008

Drupal Custom Menu

So, you've made a custom drupal menu, now you want to use it in your theme. How do you use your custom drupal menu in a theme? Here's how.

First, Drupal adds "menu-" to your custom menu. So if you named your menu "custom-menu", when referencing your custom menu you have to call it "menu-custom-menu".

The drupal api call to make is "menu_navigation_links", and then pass the result to "theme" to render the list.

print theme('links', menu_navigation_links('menu-custom-menu',0));

Look how the code above is similar to what you're used to doing for the $primary_links, and $secondary_links that are built into drupal, as shown below:

print theme('links', $primary_links, array('class' => 'links primary-links'));

Top Drupal Developer Links

Here's my Top Drupal Developer Links:

Friday, August 29, 2008

Vim Tab Preferences

Vim Tab preferences for PHP and Python Development:

set expandtab
set tabstop=4
set shiftwidth=4
set autoindent
set smartindent

Wednesday, August 13, 2008

Convert Degrees, Minutes, and Seconds to Decimal Degrees

Converting Latitude and Longitude values from Degrees, Minutes, Seconds to Decimal Degrees.

Here is the Formula:

(DEG + (MIN * 1/60) + (SEC * 1/60 * 1/60))

If it has an additional North, East, South or West, multiply by -1 for South and West.

Friday, July 18, 2008

Article on Buying a Notebook

Some self promotion here. Here's a link to Buying a Notebook Computer.

Thursday, July 10, 2008

2 Things to make Microsoft Web Expression Suck Less

Microsoft Web Expressions, the Microsoft replacement for the piece of crap software we call Microsoft Frontpage. Web Expressions doesn't suck as bad as Frontpage, but still has its quirks. These 2 tips make it suck less.

  1. Turn off hidden meta data by going to "site" » "site settings" » "general" and unchecking "Manage the web site using hidden Metadata files"
  2. Turn off stupid Byte Order Marker (BOM) "tools" » "page editor options" » "authoring" and unchecking "Add BOM when creating or renaming UTF-8 documents" towards the right of the screen.

Tuesday, June 10, 2008

jQuery AJAX Login

Wanted to use jQuery to display a login form on a site, and use AJAX to do the login. Seemed easy enough. Prerequisites are jQuery, and jqModal. Created an HTML page with a Login link, and a jqModal div that hides the username, password form.

<a class="display-form">Login</a>

<div id="login" class="jqmDialog">
<div class="jqmdMSG">
<form id="login-form"> here is the login form.
hidden in a jqModal div. The css hides the div.
The jQuery javascript handles the show/hide of the
login form.</form>

<script src="text/javascript" >
$().ready( function() {
// setup the login form to display,
// hide itself appropriately.
var t = $('#login div.jqmdMSG');
// a link of class display-form is clicked,
// the login dialog will be displayed
target: t,
overlay: 30,
onHide: function(h) {
h.o.remove(); // remove the overlay
h.w.fadeOut(888); // fade out the dialog

// setup the login form to work via ajax
url: 'url to post form to',
beforeSubmit: function(formData, jqForm, options) {
// code executed before form
// is submited could possibly
// display a "Please wait ..."
// and hide the login button on the form
return true;
success: function(responseText, statusText) {
// code executed if logged in success
error: function(responseText, statusText) {
// code executed if things failed

Monday, June 02, 2008

WebFaction Web Hosting that Doesn't Suck

Have to say I have been quite pleased with my web hosting through WebFaction. When looking for a Web Hosting Provider such as WebFaction, these things are most important to me:

  1. Uptime, the server needs to be up all the time
  2. Fast, even under heaving loads the server needs to respond quickly to user requests
  3. Support, when I contact them at anytime day or night, I expect a response within a few hours
  4. Ease of Use, configuring websites should be easy

I've found that WebFaction meets my web hosting requirements.

I've gone with other hosting providers and have never been that impressed. Other web hosting providers tend to never have competent staff on duty. Don't care about uptime. Give an ancient software interface to configure things. And whats really annoying is when there web hosting is down, they could care less about getting things back up again fast. Drives me crazy, if my site isn't up, there is no money being made. If there is no money being made, then I don't have money for food. If I don't have money for food ... well lets just say I can be a little cranky.

So my hats off to WebFaction. They deliver good service, for a decent price, with good support.

Thursday, May 15, 2008

JQuery AJAX Tabs

Really impressed with JQuery tabs. JQuery's documentation isn't half that bad. The Tab Examples were helpful. This website had a little bit more in depth example of how to use JQuery AJAX tabs.

Monday, April 21, 2008

OpenSSL Certificate Authority (CA)

# To Create the CA Private/Public Keys
openssl req -x509 -newkey rsa:1024 -keyout CA/private/cakey.pem -out
CA/cacert.pem -passout pass:capass -config openssl.cnf

# To sign a Certificate Signing Request
openssl ca -in user.csr -out user.crt -notext -passin pass:capass -config openssl.cnf

Generating a CRL

An empty CRL that is signed by the CA can be generated with the command

openssl ca -gencrl -crldays 15 -out crl.pem

If you omit the -crldays option then the default_crl_days value (30 days) specified in openssl.cnf is used.

If you prefer the CRL to be in binary DER format, then this conversion can be achieved with

openssl crl -in crl.pem -outform DER -out cert.crl

The directory /etc/ipsec.d/crls/ contains all CRLs either in binary DER or in base64 PEM format. Irrespective of the file suffix, pluto "automagically" determines the correct format.

Revoking a certificate

A specific host certificate stored in the file host.pem is revoked with the command

openssl ca -revoke host.pem

Next the CRL file must be updated

openssl ca -gencrl -crldays 15 -out crl.pem

The content of the CRL file can be listed with the command

openssl crl -in crl.pem -noout -text

in the case of a base64 CRL, or alternatively for a CRL in DER format

openssl crl -inform DER -in cert.crl -noout -text

How do I generate a certificate request for VeriSign?

Applying for a certificate signed by a recognized certificate authority like VeriSign is a complex bureaucratic process. You’ve got to perform all the requisite paperwork before creating a certificate request.

As in the recipe for creating a self-signed certificate, you’ll have to decide whether or not you want a passphrase on your private key. The recipe below assumes you don’t. You’ll end up with two files: a new private key called mykey.pem and a certificate request called myreq.pem.

openssl req \
-new -newkey rsa:1024 -nodes \
-keyout mykey.pem -out myreq.pem

If you’ve already got a key and would like to use it for generating the request, the syntax is a bit simpler.

openssl req -new -key mykey.pem -out myreq.pem

Similarly, you can also provide subject information on the command line.

openssl req \
-new -newkey rsa:1024 -nodes \
-subj '/ Dom, Inc./C=US/ST=New York/L=Portland' \
-keyout mykey.pem -out myreq.pem

When dealing with an institution like VeriSign, you need to take special care to make sure that the information you provide during the creation of the certificate request is exactly correct. I know from personal experience that even a difference as trivial as substituting “and” for “&” in the Organization Name will stall the process.

If you’d like, you can double check the signature and information provided in the certificate request.

# verify signature
openssl req -in myreq.pem -noout -verify -key mykey.pem

# check info
openssl req -in myreq.pem -noout -text

Save the key file in a secure location. You’ll need it in order to use the certificate VeriSign sends you. The certificate request will typically be pasted into VeriSign’s online application form.

Thursday, March 06, 2008

CherryPy, Mako, and Py2exe

So I wanted to make a windows service out of CherryPy. CherryPy as the webserver, Mako as my template engine, and Py2exe to turn it into a windows service.

Easy enough I thought, great reference about CherryPy and Windows Services on CherryPy's website. Unfortunately their tutorial didn't work with CherryPy 3.0. Instead I got a "cherrypy No HTTP servers have been created" error. Easy enough to fix though change the cherrypy.server.start() to be cherrypy.server.quickstart().

My next hurdle was wanting the config to be within the code and not in an external file. From Cherry py's example on windows services, cherrypy.tree.mount(HelloWorld(), '/'). There is an optional third parameter, the config object. cherrypy.tree.mount(HelloWorld(), '/', config ).

My config looks like this:
config = {
'/': {
'tools.staticdir.root': path,
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': "static",
Finally a working windows service. Then off to integrating Mako. That was the easy part.

In my controllers code:

from mako.template import Template
from mako.lookup import TemplateLookup

lookup = TemplateLookup( directories=['template'], module_directory="cache",
output_encoding="utf-8", encoding_errors="replace",


class HelloWorld(object):
def index(self):
# index.html is in the template dir
tmp = lookup.get_template("index.html")
# title used as ${title} in index.html template file
return tmp.render(**dict(title="Welcome")) = True
The last hurdle was Py2Exe. Did I mention I was using simplejson and YUI? Simplejson's egg needed to be unzipped. Simplejson made it easy to send python data to YUI as something YUI could understand; json.

mako.cache and simplejson needed to be included in my packages list, so my py2exe options looked like this:

"py2exe": {
"compressed": 1,
"optimize": 2,
"bundle_files": 2,
"packages": [
"excludes": [
My mako template directory needed to be copied to the py2exe dist directory:

("template", glob.glob("template/*.html"))

End result is a Windows HTTP Service that works quite nice.

Friday, February 15, 2008

MSI Testing

To install with debugging:

msiexec /i your.msi /l* debug.log


msiexec /i your.msi /l*v debug.log

To uninstall an msi file without going to add remove programs:

msiexec /x your.msi

Monday, January 21, 2008

Python Beats Ruby

Interesting news on the Python front,

Python beats Ruby. Python holds its ground at #7 while Ruby drops ground from #10 to #11.

Wednesday, January 09, 2008

Programmatically give Logon as Batch Right

ntrights.exe is nice and all, but sometimes you want to do things on your own. The code below will allow you to programmatically assign the Logon as Batch Right (SeBatchLogonRight) to a user. This can be applied to any other rights assignment you want to give a user on Windows. For instance:
SeCreateTokenPrivilege, SeAssignPrimaryTokenPrivilege, SeLockMemoryPrivilege, SeIncreaseQuotaPrivilege, SeUnsolicitedInputPrivilege, SeMachineAccountPrivilege, TcbPrivilege, SeSecurityPrivilege, SeTakeOwnershipPrivilege, SeLoadDriverPrivilege, SeSystemProfilePrivilege, SeSystemtimePrivilege, SeProfileSingleProcessPrivilege,
SeIncreaseBasePriorityPrivilege, SeCreatePagefilePrivilege, SeCreatePermanentPrivilege,
SeBackupPrivilege, SeRestorePrivilege, SeShutdownPrivilege, SeAuditPrivilege, SeSystemEnvironmentPrivilege, SeChangeNotifyPrivilege, or SeRemoteShutdownPrivilege. This technique, in python, can be carried over easy enough to C++ if desired.

import win32security

user = "Administrator"
system = "Some computer name" # or None for local

handle = win32security.LsaOpenPolicy(
win32security.POLICY_ALL_ACCESS )

sid, domain, tmp = win32security.LookupAccountName(system, user)

if not 'SeBatchLogonRight' in \
win32security.LsaEnumerateAccountRights(handle, sid):

('SeBatchLogonRight',) )