Django Messaging for AJAX Calls Using Jquery
The messaging contrib app for Django has always been been tied to a user, which has prevented me from using it in any of my apps. Now that the Django 1.2 Alpha has been released, I’ve been able to play with it, and feel good about using it. My sites usually have a mix of AJAX and traditional HTTP requests. So figuring out a good solution to handle messages for the AJAX requests, while maintaining consistency in interacting with the API was important to me. Here’s my solution for handling messages in AJAX requests.
First we need a middleware that will detect if the request is an AJAX request. I always use JSON, but if you mix the data types, you might have to add some additional logic here. The middleware adds to the JSON any messages that we might have added in the view.
import simplejson as json
from django.contrib import messages
class AjaxMessaging(object):
def process_response(self, request, response):
if request.is_ajax():
if response['Content-Type'] in ["application/javascript", "application/json"]:
try:
content = json.loads(response.content)
except ValueError:
return response
django_messages = []
for message in messages.get_messages(request):
django_messages.append({
"level": message.level,
"message": message.message,
"extra_tags": message.tags,
})
content['django_messages'] = django_messages
response.content = json.dumps(content)
return response
Make sure to add the new middleware to settings.py.
'apps.main.middleware.AjaxMessaging'
So if we have a view that responds to an AJAX request, we can add a message the same way we would for a traditional HTTP response:
if success:
messages.success(request, "The object has been modified.")
else:
messages.error(request, "The object was not modified.")
We need a place to put the messages in our template:
<ul id="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
We’ll be using a global ajaxComplete handler, so we’ll have to use a JSON parser. Simply include it with the rest of our javascript. jquery.json.js
And finally the handler, which simply loops through all the Django messages, and fades them out after three seconds.
function addMessage(text, extra_tags) {
var message = $('<li class="'+extra_tags+'">'+text+'</li>').hide();
$("#messages").append(message);
message.fadeIn(500);
setTimeout(function() {
message.fadeOut(500, function() {
message.remove();
});
}, 3000);
}
$(document).ready(function() {
$('#messages').ajaxComplete(function(e, xhr, settings) {
var contentType = xhr.getResponseHeader("Content-Type");
if (contentType == "application/javascript" || contentType == "application/json") {
var json = $.evalJSON(xhr.responseText);
$.each(json.django_messages, function (i, item) {
addMessage(item.message, item.extra_tags);
});
}
}).ajaxError(function(e, xhr, settings, exception) {
addMessage("There was an error processing your request, please try again.", "error");
});
How to Handle HTTP Redirects with Jquery and Django
Jquery has a funny way of handling 302 redirects during AJAX calls. These frequently come up when I need to redirect a user to a login page. Here’s how I’m handling it with Django.
The underlying problem we experience with redirects in Jquery is that the redirects are followed. So instead of getting a status code of HTTP/1.1 302 Found, we get HTTP/1.1 200 OK. So one way to get around this is to return a made up status code. This is easily accomplished with a middleware in django.
from django.http import HttpResponseRedirect
class AjaxRedirect(object):
def process_response(self, request, response):
if request.is_ajax():
if type(response) == HttpResponseRedirect:
response.status_code = 278
return response
Make sure to add the new middleware to settings.py.
'apps.main.middleware.AjaxRedirect'
And the final piece of the puzzle is to add a global AJAX complete handler, that checks for the new status code, redirects the page to the Location header in the HTTP response, and replace the next query variable. The last part is important because the login_required decorator automatically sets the next query variable to the HTTP Referrer, which in this case would be the URL of the AJAX call. So we want to replace it with the page we’re currently on.
$(document).ready(function() {
$('body').ajaxComplete(function(e, xhr, settings) {
if (xhr.status == 278) {
window.location.href = xhr.getResponseHeader("Location").replace(/\?.*$/, "?next="+window.location.pathname);
}
});
});
Setting Up Apache-PHP-Python-MySQL on Mac OS X Snow Leopard 10.6
- Download and install MySQL Package file [MySQL 5.1 for 10.5 (x86_64)]
- Install MySQL Startup Item [Howto]
- Turn on Web Sharing in System Preferences -> Sharing

- Copy /etc/php.ini.default to /etc/php.ini
sudo cp /etc/php.ini.default /etc/php.ini
- Add timezone information to php.ini (PHP will error without this)
date.timezone = 'America/New_York'
- Install php-mcrypt
- Download libmcrypt 2.5.8
- Build and install .
./configure --disable-posix-threads --enable-dynamic-loading make sudo make install
- Download PHP 5.3.1
- Navigate to php-5.3.1/ext/mcrypt/
- Build and install .
phpize ./configure make sudo make install
- Add extension to php.ini
extension=mcrypt.so
- Install APC
- Download PCRE 8.0
- Build and install .
./configure make sudo make install
- Download APC 3.1.3p1
- Navigate to APC-3.1.3p1/APC-3.1.3p1/
- Build and install .
phpize ./configure make sudo make install
- Add extension to php.ini
extension=apc.so
- Download and install MySQL-python 1.2.3c1
sudo python setup.py install
- Download and install libjpeg.v7
./configure --enable-shared make sudo make install
- Download and install Python Imaging Library 1.1.6
sudo python setup.py install
MySQL Startup Item for Mac OS 10.6 Snow Leopard
Save the following as /System/Library/LaunchDaemons/org.mysql.mysqld.plist
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>RunAtLoad</key> <true/> <key>Umask</key> <integer>7</integer> <key>UserName</key> <string>_mysql</string> <key>Disabled</key> <false/> <key>WorkingDirectory</key> <string>/usr/local/mysql</string> <key>GroupName</key> <string>_mysql</string> <key>KeepAlive</key> <true/> <key>Program</key> <string>/usr/local/mysql/bin/mysqld</string> <key>Label</key> <string>org.mysql.mysqld</string> <key>ProgramArguments</key> <array> <string>--user=_mysql</string> </array> </dict> </plist>
And in a bash session, run the following:
sudo chown root:wheel /System/Library/LaunchDaemons/org.mysql.mysqld.plist sudo launchctl load /System/Library/LaunchDaemons/org.mysql.mysqld.plist sudo launchctl start org.mysql.mysqld
Nginx Startup Script for Mac OS 10.6 Snow Leopard
If you don’t use MacPorts, and have a hard time finding a startup plist file for Nginx, and had trouble getting Nginx to startup with your custom plist file, this might be your solution:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>nginx</string>
<key>Program</key>
<string>/usr/local/nginx/sbin/nginx</string>
<key>KeepAlive</key>
<true/>
<key>NetworkState</key>
<true/>
<key>LaunchOnlyOnce</key>
<true/>
</dict>
</plist>
Just save this as /System/Library/LaunchDaemons/org.nginx.nginx.plist and perform the following in a bash session:
sudo chown root:wheel /System/Library/LaunchDaemons/org.nginx.nginx.plist sudo launchctl load /System/Library/LaunchDaemons/org.nginx.nginx.plist sudo launchctl start org.nginx.nginx
Growl Notifications for Apple Mail on Mac OS X Snow Leopard (10.6)
UPDATE: You can grab a beta version of 64-bit Growl 1.2 from the Growl beta site.
GrowlMail is broken on Mac OS X Snow Leopard (10.6). The dev team for Growl has a fix in the works, but I use Prowl and like to have notifications go to my iPhone when I’m not at my computer. So in the meantime, here is my solution.

I was originally inspired by James Higgs.
You first want to create a new AppleScript using AppleScript Editor and save it somewhere logical. I used /Library/Scripts/Mail Scripts/Rule Actions/Growl.scpt Below is the code:
-- Growl Alerts in Mail
-- Hunter Ford [http://www.cupcakewithsprinkles.com]
-- This script arises from the lack of any Growl Support in Mac OS X Snow Leopard (10.6)
-- Code inspired by and adapted from James Higgs [http://blog.jameshiggs.com/2009/08/28/growlmail-on-snow-leopard-a-temporary-fix/] as well as those mentioned.
tell application "GrowlHelperApp"
-- Make a list of all the notification types
-- that this script will ever send:
set the allNotificationsList to {"New Email"}
-- Make a list of the notifications
-- that will be enabled by default.
-- Those not enabled by default can be enabled later
-- in the 'Applications' tab of the growl prefpane.
set the enabledNotificationsList to {"New Email"}
-- Register our script with growl.
-- You can optionally (as here) set a default icon
-- for this script's notifications.
register as application "Mail" all notifications allNotificationsList default notifications enabledNotificationsList icon of application "Mail"
end tell
-- Mail Rule Trigger
--
-- Source: Benjamin S. Waldie [http://www.mactech.com/articles/mactech/Vol.21/21.09/ScriptingMail/index.html]
using terms from application "Mail"
on perform mail action with messages theSelectedMessages for rule theRule
repeat with thisMessage in theSelectedMessages
-- Process the current message
-- Grab the subject and sender of the message
set growlSubject to subject of thisMessage
set growlSender to my ExtractName(sender of thisMessage)
-- Use the first 100 characters of a message
set growlMessage to (content of thisMessage)
set growlLength to (length of growlMessage)
if growlLength > 100 then
set growlMessage to (characters 1 through 100 of growlMessage) & "…"
end if
set growlMessage to growlSubject & "
" & growlMessage
-- Send a Notification
tell application "GrowlHelperApp"
notify with name "New Email" title growlSender description growlMessage application name "Mail"
end tell
end repeat
end perform mail action with messages
end using terms from
-- *ExtractName*
--
-- gathers the name portion from the "From: " line
--
-- Source: robJ [http://forums.macosxhints.com/archive/index.php/t-19954.html]
to ExtractName(sender_)
if sender_ begins with "<" then
return text 2 thru -2 of sender_
else
set oldTIDs to text item delimiters
try
set text item delimiters to "<"
set name_ to first text item of sender_
set text item delimiters to oldTIDs
on error
set text item delimiters to oldTIDs
end try
return name_
end if
end ExtractName
Run this first in the AppleScript Editor. As this is what will “register” the notification with Growl. Click the green “play” button that says “Run”.

Then you want to create a new rule in mail that will run this script on every message that comes in.

Make sure to select “Don’t Apply” that way you don’t flood your screen with tons of Growl notifications.

Digitally Signed E-Mails with Thawte Personal E-Mail Certificates
UPDATE: Thawte is doing away with Free Personal E-Mail Certificates. You can read their FAQ for more information.
Digitally signed and encrypted e-mails have been around for a long time. Unfortunately, it’s never really taken off. Thawte hopes to change all that with its Web of Trust. They offer free certificates for personal use.
The biggest problem to date in my experience has been e-mail clients ability to handle digitally signed and encrypted e-mails. I use a Apple Mail to handle all of communications since its support for IMAP is top-notch.
To give you a little background, digital signatures are apart of the S/MIME specification. Here’s how Wikipedia defines it.
S/MIME provides the following cryptographic security services for electronic messaging applications: authentication, message integrity and non-repudiation of origin (using digital signatures) and privacy and data security (using encryption). S/MIME specifies the application/pkcs7-mime (smime-type “enveloped-data”) type for data enveloping (encrypting): the whole (prepared) MIME entity to be enveloped is encrypted and packed into an object which subsequently is inserted into an application/pkcs7-mime MIME entity.
If you want to get started using digital signature in your e-mail, you need to first sign-up with Thawte. At the bottom of the page, click the link that says “Click here”. You’ll then have to proceed through the wizard, verify your email, and request a certificate. After your certificate has been generated, you’ll download it, and it should get added to your keychain.
After it’s been added to your keychain, you need to make one adjustment so that you can use it in Mail. You’ll want to double-click the certificate you just added so that you get a new pane. Then, click “Trust” to expand it. Then you need to tell it to “Always Trust” “When using this certificate”.

The next time you open a composition window in Mail, you’ll see a few new buttons whenever you compose an email from the email address you got the certificate for.

The following is from Mail’s help:
A Signed icon (containing a checkmark) in the lower-right side of the message header indicates the message will be signed when you send it.
An Encrypt (closed lock) icon appears next to the Signed icon if you have a personal certificate for a recipient in your keychain; the icon indicates the message will be encrypted when you send it.
You can add several emails to your Thawte account, but each one will need its own certificate.
Now you’re halfway there. You need to become “Trusted” so that your name can be put in the certificate. Upon doing this, and downloading new certificates, your recipients won’t be presented with the message: “Unable to verify message signature”. So in order to become trusted, your identity must be verified by a Web of Trust notary. Go to the website and login. You’ll have to locate a few notaries in your area that will verify your information. Each notary can assign a certain number of “trust points”. You need 50 trust points in order to be trusted. Each notary can give out between 10-35 trust points. You’ll have to meet this person face-to-face and present them with a few documents (driver’s license, passport, company photo ID) and copies of those documents for them to keep. Before you’re meeting you’ll have to share your details with them through the website.
Half-Off Depot ‘Notify When Back In Stock’
Half-Off Depot is great! You can find a lot of restaurants or services and get gift cards or certificates for half off the actual price. No gimmicks. One problem is that everything sells out so fast! And I never know when things come back in stock. There’s a “feature” where you can be notified when things come back in stock, but it just doesn’t work. So… to solve the problem I came up with a little script that will scrape the site for the different gift cards I’m looking out for at specified intervals and send me an email. It’s not perfect, but it does the trick for now.
#!/bin/bash
NUM_AVAILABLE=`curl -s http://halfoffdepot.com/atlanta/categories/restaurants/all-restaurants/5-seasons-brewing-company.html | grep '</h1></span>' | sed -e 's/^.*;">//' -e 's/<\/.*$//'`
if [ $NUM_AVAILABLE -ne 0 ]
then
echo "http://halfoffdepot.com/atlanta/categories/restaurants/all-restaurants/5-seasons-brewing-company.html" | mail -s "There are $NUM_AVAILABLE gift cards available for 5 Seasons." "Your Name <user@domain.com>"
else
echo "No gift cards available for 5 Seasons." && echo
fi
You can just replace the link with the one you’re trying to target, and replace your email address. I have this running as a cron job daily at 8am,12pm, and 6pm. It’s not perfect because the script doesn’t check for a duplicate email, but things tend to sell-out fast, so I only usually get 2-3 emails
0 8,12,18 * * * /scripts/halfoff > /dev/null 2>&1
Better Weather Script for Indigo
I find the weather script that comes with Indigo a bit lacking, and at times a little slow. Also, I prefer using the weather feed from Yahoo since it matches up to the weather widget in my dashboard.
You’ll first need to get the right parameter for your local weather. Go to Yahoo Weather and type in your city/zip code. Just look at the URL and you should have something like: http://weather.yahoo.com/forecast/USGA0028.html
Grab what comes after ‘forecast/’ and before ‘.html’ and replace ‘USGA0028′ in the script below.
property fetchTimeout : 15 -- don't wait for query response for more than 15 seconds...
try
with timeout of fetchTimeout seconds
do shell script "curl -s 'http://xml.weather.yahoo.com/forecastrss?p=USGA0028' | grep -E '(<yweather:condition|/\\>)' | sed -e 's/^.*temp=\"//' -e 's/\".*$//'"
set temperature to result
do shell script "curl -s 'http://xml.weather.yahoo.com/forecastrss?p=USGA0028' | grep -E '(<yweather:condition|/\\>)' | sed -e 's/^.*text=\"//' -e 's/\".*$//'"
set conditions to result
do shell script "curl -s 'http://xml.weather.yahoo.com/forecastrss?p=USGA0028' | grep -E '(<yweather:forecast day=\"|/\\>)' | sed -e 's/^.*high=\"//' -e 's/\".*$//' -e 'q'"
set high to result
do shell script "curl -s 'http://xml.weather.yahoo.com/forecastrss?p=USGA0028' | grep -E '(<yweather:forecast day=\"|/\\>)' | sed -e 's/^.*low=\"//' -e 's/\".*$//' -e 'q'"
set low to result
tell application "IndigoServer"
if not (variable "weatherTemperature" exists) then
make new variable with properties {name:"weatherTemperature", value:temperature}
else
if value of variable "weatherTemperature" is not temperature then
set value of variable "weatherTemperature" to temperature
end if
end if
if not (variable "weatherConditions" exists) then
make new variable with properties {name:"weatherConditions", value:conditions}
else
if value of variable "weatherConditions" is not conditions then
set value of variable "weatherConditions" to conditions
end if
end if
if not (variable "weatherTemperatureHigh" exists) then
make new variable with properties {name:"weatherTemperatureHigh", value:high}
else
if value of variable "weatherTemperatureHigh" is not high then
set value of variable "weatherTemperatureHigh" to high
end if
end if
if not (variable "weatherTemperatureLow" exists) then
make new variable with properties {name:"weatherTemperatureLow", value:low}
else
if value of variable "weatherTemperatureLow" is not low then
set value of variable "weatherTemperatureLow" to low
end if
end if
end tell
end timeout
end try
Adjusting the Climate upon Leaving and Restoring Previous Settings upon Return
When you’re not at home, you generally don’t need to have the A/C running at full blast. One simple solution for this is turn down your A/C when you leave, and turn it back up when you return. Using Indigo and a Insteon compatible thermostat, like the one from Venstar, make this a breeze. My original solution to this worked, but it didn’t take into account my overrides of the thermostat. I’ve added a few variables to Indigo to save the thermostats current state when I leave, and restore it back to that state once I’ve returned.
For this to work, we’re going to need to two triggers (which you may already have setup), and three action groups.
First, we need a trigger to set off the action group to save the settings (you could very well embed the action script into your trigger, but I usually modularize my scripts so that I can perform actions from a remote or from multiple triggers). I have a trigger that is called when I hit the “All Off” button on a KeypadLinc by my front door. I have another trigger that will restore my settings when I return; it is set off by a motion detector by my front door.
You’ll need to create a few variables first: climateHeatSetpointPrevious, climateCoolSetpointPrevious, climateHvacModePrevious, climateFanModePrevious, climateHeatSetpointAway, climateCoolSetpointAway, climateHvacModeAway, climateFanModeAway
Now for the action groups:
1. “A/C – Save Current Settings”
if heat setpoint of device "Thermostat" is not value of variable "climateHeatSetpointAway" or cool setpoint of device "Thermostat" is not value of variable "climateCoolSetpointAway" or hvac mode of device "Thermostat" is not value of variable "climateHvacModeAway" or fan mode of device "Thermostat" is not value of variable "climateFanModeAway" then
set value of variable "climateHeatSetpointPrevious" to heat setpoint of device "Thermostat"
set value of variable "climateCoolSetpointPrevious" to cool setpoint of device "Thermostat"
set value of variable "climateHvacModePrevious" to hvac mode of device "Thermostat"
set value of variable "climateFanModePrevious" to fan mode of device "Thermostat"
end if
*Edit: I added the if statement so that if I hit the ‘All Off’ button twice by mistake, it wouldn’t save my “away” settings as the “previous” settings.
2. “A/C – Set Away Settings”
set heat setpoint of device "Thermostat" to value of variable "climateHeatSetpointAway" set cool setpoint of device "Thermostat" to value of variable "climateCoolSetpointAway" set hvac mode of device "Thermostat" to value of variable "climateHvacModeAway" set fan mode of device "Thermostat" to value of variable "climateFanModeAway"
3. “A/C – Restore Previous Settings”
set heat setpoint of device "Thermostat" to value of variable "climateHeatSetpointPrevious" set cool setpoint of device "Thermostat" to value of variable "climateCoolSetpointPrevious" set hvac mode of device "Thermostat" to value of variable "climateHvacModePrevious" set fan mode of device "Thermostat" to value of variable "climateFanModePrevious"
*Edit: You may have to add some 1 second delays (delay 1) to ensure the signal is received by the thermostat. The Venstar adapter is wireless and therefore more prone to getting error messages like: “send failed (no acknowledgment)”.