Tuesday, April 23, 2013

Django Admin Filter OR queries

Django admin in 1.4+ support the query parameter __in for filtering on multiple values of the same field.  Adding this to the admin took some work but is possible, I am not sure if this is the best way to do it but it does work.

Here is a snippet of my admin.py file
https://gist.github.com/justhamade/5447780

Sunday, April 07, 2013

Securing LAMP for Ubuntu 12.04 LTS

This is a summary of this post http://www.thefanclub.co.za/how-to/how-secure-ubuntu-1204-lts-server-part-1-basics

Setup non root user

ssh root@YOURSERVERIP
adduser YOUNONROOTUSER
useradd -G sudo YOUNONROOTUSER
logout
ssh YOURNONROOTUSER@YOURSERVERIP
sudo -i
Install LAMP, Security and other useful stuff
sudo apt-get install lamp-server^ ufw denyhosts fail2ban psad rkhunter chkrootkit nmap logwatch libdate-manip-perl apparmor apparmor-profiles tiger htop libxml2 libxml2-dev libxml2-utils libaprutil1 libaprutil1-dev libapache-mod-security -y
Setup UFW
sudo ufw enable
ufw status verbose
sudo ufw allow ssh
sudo ufw allow http
Secure shared mem (requires reboot)
echo 'tmpfs /dev/shm tmpfs defaults,noexec,nosuid 0 0' >> /etc/fstab
Disallow root ssh logins (can also change SSH PORT)
sed -i 's/PermitRootLogin yes/PermitRootLogin no/g' /etc/ssh/sshd_config
sed -i 's/Port 22/Port 2222/g' /etc/ssh/sshd_config
sudo /etc/init.d/ssh restart
Limit su command to users that have sudo
sudo dpkg-statoverride --update --add root sudo 4750 /bin/su
Harden network with sysctl settings.  This is what my /etc/sysctl.conf file ends up looking like with no comments
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.tcp_syncookies=1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
net.ipv4.icmp_echo_ignore_all = 1 
sudo sysctl -p
Prevent IP Spoofing
# The "order" line is only used by old versions of the C library.
order bing,hosts
nospoof on
multi on 
Secure PHP (assumes that exec, system, shell_exec, passthru are not already there)

sed -i 's/expose_php = On/expose_php = Off/g' /etc/php5/apache2/php.ini
sed '/^disable_functions/ s/$/,exec,system,shell_exec,passthru/' /etc/php5/apache2/php.ini > /etc/php5/apache2/php.ini
Secure Apache
sed -i 's/ServerTokens OS/ServerTokens Prod/g' /etc/apache2/conf.d/security
sed -i 's/ServerSignature On/ServerSignature Off/g' /etc/apache2/conf.d/security
echo 'Header unset ETag' >> /etc/apache2/conf.d/security
echo 'FileETag None' >> /etc/apache2/conf.d/security
a2enmod headers
/etc/init.d/apache2 restart
Update fail2ban
sed -s 'action = %(action_)s/action = %(action_mwl)s/g' s//etc/fail2ban/jail.conf
 


Monday, February 25, 2013

Vagrant on Cygwin

Not fully tested taken from memory and history so if anything is missing please comment.

Taken mostly from a good post here http://www.enrise.com/2012/12/git-and-vagrant-in-a-windows-environment/ Assumes you have latest cygwin and virtualbox installed.

Cygwin need svn and bash Install other deps
svn --force export http://apt-cyg.googlecode.com/svn/trunk/ /bin/
chmod +x /bin/apt-cyg
apt-cyg install make ruby curl git bsdtar ssh

Get Vagrant 1.1.0 dev from git (once this is stable then this step is not needed).
git clone https://github.com/mitchellh/vagrant.git
cd vagrant 

Update pull request for cygwin fix from here https://github.com/mitchellh/vagrant/pull/1366
curl https://github.com/mitchellh/vagrant/pull/1366.patch | git am

Install gem so deps are install then rake install to update to dev version with patches applied
gem install vagrant
rake install

Add VBOX_USER_HOME
export VBOX_USER_HOME=`pwd`/.VirtualBox/

Add symlink to VBoxManage
ln '/cygdrive/c/Program\ Files/Oracle/VirtualBox/VBoxManage.exe' /usr/local/bin/VBoxManage

Use vagrant
vagrant box add lucid32 http://files.vagrantup.com/lucid32.box
vagrant init lucid32
vagrant up

You May get an error when starting up. This is because the Virtual box guest additions need to be installed so the directory can be mounted
vagrant ssh
sudo apt-get install virtualbox-guest-addition

Tuesday, November 06, 2012

Stackato

wget http://downloads.activestate.com/stackato/vm/v2.4.3/stackato-vm-vbox-v2.4.3.zip
unzip stackato-vm-vbox-v2.4.3.zip
cd Stackato-VM
VBoxManage import Stackato-v2.4.3.ovf
VBoxManage modifyvm Stackato-v2.4.3 --nic1 bridged --bridgeadapter1 eth0
VBoxManage startvm Stackato-v2.4.3 --type headless
To figure out the ip
# scan port 22
nmap -p 22 --open -sV 192.168.X.0/24
VBoxManage showvminfo Stackato-v2.4.3 | grep MAC
# OUTPUT IS: NIC 1:           MAC: 080027656420, Attachment: Bridged Interface 'eth0', Cable connected: on, Trace: off (file: none), Type: 82540EM, Reported speed: 0 Mbps, Boot priority: 0, Promisc Policy: deny
arp -an | grep -i 08:00:27:65:64:20 
Install stackato client
wget http://downloads.activestate.com/stackato/client/v1.5/stackato-1.5-linux-glibc2.3-x86_64.zip
unzip stackato-1.5-linux-glibc2.3-x86_64.zip 
cd stackato-1.5-linux-glibc2.3-x86_64/
sudo mv stackato /usr/local/bin/.
stackato

Saturday, March 31, 2012

ISPConfig 3.0.4.3 with Django 1.4, Virtualenv and WSGI on Debian Squeeze

I recently go a killer deal on a VPS at http://chicagovps.net/ so I am moving all my stuff from my Webfaction and Linode to my own VPS. I want something to easily manage my apache configs, dns and mail still and have used ISPConfig in the past and found it pretty good for a free open source program. I actually like it better than cPanel or DirectAdmin which I find too restrictive.

Moving my Django projects from Webfaction will take a bit of work. ISPConfig has some nice options in the GUI for PHP but the Python option uses mod_python :(.

This will not be a complete guide but as usual some documentation for myself. It assumes that ISPConfig is already setup and working, there is an decent setup guide in docs/INSTALL_DEBIAN_6.0_courier_mydns.txt and some older but still useable docs here http://www.howtoforge.com/perfect-server-debian-lenny-ispconfig3.

First you need to install some additional packages
apt-get install python-setuptools libapache2-mod-wsgi python-dev -y
easy_install pip
pip install virtualenv

Then you need to setup your site in ISPConfig and copy your Django project to it or start a new one. For simplicity we will just create a new one here
cd /var/www/mysite.com/
virtualenv myproject-env
. /myproject-env/bin/activate
pip install mysql-python
pip install PIL
pip install django
django startproject myproject
This will create a wsgi.py file in myproject/myproject/wsgi.py

Then in your ISPConfig Site go to Sites > Website > mysite.com > Options and in the Apache Directive add the following. Make sure to change the path to the correct absolute path.
Alias /media/ /var/www/clients/client0/web1/myproject/myproject/media/
Alias /static/ /var/www/clients/client0/web1/myproject/myproject/static/

Alias /robots.txt /var/www/clients/client0/web1/myproject/myproject/static/robots.txt
Alias /favicon.ico /var/www/clients/client0/web1/myproject/myproject/static/images/favicon.ico
 

WSGIDaemonProcess mysite.com user=web1 group=client0 python-path=/var/www/clients/client0/web1:/var/www/clients/client0/web1/myproject-env/lib/python2.6/site-packages
WSGIProcessGroup mysite.com
WSGIScriptAlias / /var/www/clients/client0/web1/myproject/myproject/wsgi.py
<Directory /var/www/clients/client0/web1/myproject>
Order deny,allow
Allow from all
</Directory>
<Directory /var/www/clients/client0/web1/myproject>
Order deny,allow
Allow from all
</Directory>

<Directory /var/www/clients/client0/web1/myproject>
<Files wsgi.py>
Order deny,allow
Allow from all
</Files>
</Directory>
This assumes that STATIC_URL STATIC_ROOT MEDIA_URL and MEDIA_ROOT are set to /static/ and /media/ in myproject/myproject
# Django settings for myproject project.
import os

# Add this near the top
PROJECT_DIR = os.path.abspath(os.path. dirname(__file__))

# Absolute filesystem path to the directory that will hold user-uploaded files.
# Example: "/home/media/media.lawrence.com/media/"
MEDIA_ROOT = PROJECT_DIR + '/media'

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
MEDIA_URL = '/media/'

# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/home/media/media.lawrence.com/static/"
STATIC_ROOT = PROJECT_DIR + '/static'

# URL prefix for static files.
# Example: "http://media.lawrence.com/static/"
STATIC_URL = '/static/'

That should be it.  Make sure that user web1 and group client0 are the owners of the correct directories and files and have exec permission.

Monday, July 04, 2011

Quicksynergy autostart Ubuntu

Starting quicksynergy in Ubuntu is actually really easy. If you do a "ps aux" after you have quicksynergy configured and running you will see something like
/usr/bin/synergys -f --config .quicksynergy/synergy.conf

All you need to do is add this to your "Startup Applications"
System Settings -> Startup Applications -> Add
Name: Synergy
Command: /usr/bin/synergys -f --config .quicksynergy/synergy.conf
Add->Close->Log out and log back in.

Done!

Saturday, January 29, 2011

Python Uniquify a List

Using sets it is actually pretty trivial to do

import sets
my_list = [1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 7]
unique_set = sets.Set(my_list)

Sunday, December 12, 2010

What is DevOps and why is it important

With the recent purchase of Heroku by salesforce made me think about the future of the "SysAdmin".

As I was writing this I saw a recent talk given by Jacob Kaplan-Moss at Django Boston Meetup titled DevOps.

The job of a SysAdmin is changing, frameworks like Django and Ruby on Rails along with services providers like Google Apps, Heroku, VMForce, and Djangy give developers everything they need. The role of a SysAdmin is going to become very specialized, unless your talking about Windows systems ;)

Thursday, November 25, 2010

Gitorious install on Debian Squeeze with stompserver

I have modified this install tutorial from Christian Johansen found here http://cjohansen.no/en/ruby/setting_up_gitorious_on_your_own_server

Some info from here as well http://www.bluequartz.net/projects/ElectronicImaging/SCMService/html/index.html

There is also source for a debian package by Marius MÃ¥rnes Mathiesen here http://gitorious.org/gitorious-for-debian/gitorious the most recent commits are forked here http://gitorious.org/~brodock/gitorious-for-debian/brodocks-gitorious

Squeeze is needed for the new magickwand stuff


Had issues with activemq so using stompserver instead


apt-get install -y sudo git-core git-svn apg build-essential libpcre3 libpcre3-dev sendmail \
make zlib1g zlib1g-dev ssh memcached apache2 \
libonig-dev libyaml-dev geoip-bin libgeoip-dev libgeoip1 \
uuid uuid-dev openjdk-6-jre apache2-prefork-dev curl openssl libcurl4-gnutls-dev

apt-get install -y imagemagick libmagickwand-dev && \
gem install --no-ri --no-rdoc rmagick

apt-get install -y mysql-client mysql-server libmysqlclient15-dev && \
gem install --no-ri --no-rdoc mysql

wget http://rubyforge.org/frs/download.php/71099/ruby-enterprise_1.8.7-2010.02_i386_ubuntu8.04.deb
sudo dpkg -i ruby-enterprise*

echo 'export PATH=/opt/ruby-enterprise/bin:$PATH
export LD_LIBRARY_PATH="/usr/local/lib"
export LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib"' >> /etc/profile

echo '/usr/local/lib
include ld.so.conf.d/*.conf' >> /etc/ld.so.conf

ldconfig

cd /usr/src
wget http://rubyforge.org/frs/download.php/70696/rubygems-1.3.7.tgz
tar xvzf rubygems*
cd rubygems*
ruby setup.rb

cd /usr/src
wget http://www.sphinxsearch.com/downloads/sphinx-0.9.9.tar.gz
tar xvfz sphinx-*
cd sphinx-*
./configure
make && sudo make install
gem install --no-ri --no-rdoc ultrasphinx

gem install stompserver
echo '#!/bin/sh
# Start/stop the stompserver
# WATCH THE LINE WRAPPING BELOW
### BEGIN INIT INFO
# Provides: stomp
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Stomp
# Description: Stomp
### END INIT INFO
$DAEMON=/usr/local/bin/stompserver
test -f $DAEMON || exit 0
. /lib/lsb/init-functions
case $1 in
start) log_daemon_msg Starting stompserver stompserver
     start-stop-daemon --start --name stompserver --startas $DAEMON \
                                      --background --user git
     log_end_msg $?
    ;;
stop) log_daemon_msg Stopping stompserver stompserver
     start-stop-daemon --stop --name stompserver
     log_end_msg $?
     ;;
restart) log_daemon_msg Restarting stompserver stompserver
    start-stop-daemon --stop --retry 5 --name stompserver
    start-stop-daemon --start --name stompserver --startas $DAEMON \
                                       --background --user git
    log_end_msg $?
    ;;
status)
    status_of_proc $DAEMON stompserver && exit 0 || exit $?
    ;;
*) log_action_msg Usage: /etc/init.d/stomp {start|stop|restart|status}
    exit 2
    ;;
esac
exit 0' > /etc/init.d/stompserver
chmod +x /etc/init.d/stompserver
update-rc.d stompserver defaults

echo '#!/bin/sh
# Start/stop the git poller
#
### BEGIN INIT INFO
# Provides: git-poller
# Required-Start: stomp
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Gitorious poller
# Description: Gitorious poller
### END INIT INFO

GITORIOUS_ROOT=/var/www/gitorious
echo  `date` " Starting git-poller" >> "$GITORIOUS_ROOT/log/git-poller.log"
/bin/su -- git -c " cd $GITORIOUS_ROOT; RAILS_ENV=production script/poller $@"' > /etc/init.d/poller
chmod +x /etc/init.d/poller
update-rc.d poller defaults


git clone git://gitorious.org/gitorious/mainline.git gitorious
chmod -R g+sw /var/www/gitorious
ln -s /var/www/gitorious/script/gitorious /usr/local/bin/gitorious
cd gitorious/
rm public/.htaccess
mkdir -p tmp/pids
chmod ug+x script/*
chmod -R g+w config/ log/ public/ tmp/

which ruby
vi doc/templates/ubuntu/git-daemon
Gitorious ships with ready to use git-ultrasphinx and git-daemon scripts. Be aware that the git-daemon script has hardcoded paths to both Ruby Enterprise Edition and Gitorious. Pop open git-daemon and set correct paths to the Ruby you want to use (stick with REE if you installed it earlier) and your Gitorious installation. These are the lines you need to see (lines 16 and 18 at the time of writing):
GIT_DAEMON="/opt/ruby-enterprise/bin/ruby \
    /var/www/gitorious/script/git-daemon -d"
ln -s /var/www/gitorious/doc/templates/ubuntu/git-ultrasphinx /etc/init.d/.
ln -s /var/www/gitorious/doc/templates/ubuntu/git-daemon /etc/init.d/.

chmod +x /etc/init.d/git-ultrasphinx
chmod +x /etc/init.d/git-daemon
update-rc.d -f git-daemon start 99 2 3 4 5 .
update-rc.d -f git-ultrasphinx start 99 2 3 4 5 .

gem install --no-ri --no-rdoc rails mongrel mime-types textpow chronic \
    ruby-hmac daemons mime-types oniguruma textpow chronic BlueCloth \
    ruby-yadis ruby-openid geoip rspec rspec-rails RedCloth echoe
gem install --no-ri --no-rdoc --version="1.0.1" rack

adduser git
usermod -a -G gitorious git
mkdir /var/git
mkdir /var/git/repositories
mkdir /var/git/tarballs
mkdir /var/git/tarball-work
chown -R git:git /var/git

su git
mkdir ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
vi ~/.bashrc
Append the following to /home/git/.bashrc
# User specific aliases and functions
export RUBY_HOME=/opt/ruby-enterprise
export GEM_HOME=$RUBY_HOME/lib/ruby/gems/1.8/gems
export PATH=$RUBY_HOME/bin:$PATH

exit
cd /var/www/gitorious
cp config/database.sample.yml config/database.yml
cp config/gitorious.sample.yml config/gitorious.yml
cp config/broker.yml.example config/broker.yml
apg -m 64
config/gitorious.yml
See original doc for details on this config file
rake db:migrate RAILS_ENV=production
mysql -uroot -p
create database gitorious;
create database gitorious_test;
create database gitorious_dev;
grant all privileges on gitorious.* to YOURUSER@localhost \
    identified by 'YOURPASSWORD';
grant all privileges on gitorious_test.* to YOURUSER@localhost;
grant all privileges on gitorious_dev.* to YOURUSER@localhost;
sudo rake gems:install
vi config/database.yml
Make your config match your mysql db setup
rake db:migrate RAILS_ENV=production
cd /var/www/gitorious
env RAILS_ENV=production ruby script/create_admin
script/console
> user = User.first
> user.login = "yourname" # Change to your desired username
> user.activate
> user.accept_terms
> user.save
rake ultrasphinx:bootstrap RAILS_ENV=production
chown -R git:gitorious config/environment.rb script/poller log tmp
chmod -R g+w config/environment.rb script/poller log tmp
chmod ug+x script/poller
/etc/init.d/activemq start
env RAILS_ENV=production /etc/init.d/git-daemon start
su git -c \
  "cd /var/www/gitorious && \
    env RAILS_ENV=production script/poller run"

su git -c \
  "cd /var/www/gitorious && script/server -e production"

echo '* */1 * * * root cd /var/www/gitorious && rake ultrasphinx:index RAILS_ENV=production' >> /etc/crontab
cp doc/templates/ubuntu/gitorious-logrotate /etc/logrotate.d/gitorious
sudo chmod +x /etc/logrotate.d/gitorious
gem install passenger
passenger-install-apache2-module

a2enmod rewrite
a2enmod deflate
a2enmod passenger
a2enmod expires
a2enmod rewrite

vi conf/vhost.conf
Update config
ln -s /var/www/gitorious/conf/vhost.conf /etc/apache2/sites-available/gitorious
a2ensite gitorious

Wednesday, August 11, 2010

Nagios3 on Debian external commands

Enabling external commands in nagios on a Debian deb install of nagios is a bit of a pain.

First in /etc/nagios3/nagios.cfg change
check_external_commands=1

Then you will get errors about
Error: Could not stat() command file '/var/lib/nagios3/rw/nagios.cmd'!

If you
rm /var/lib/nagios3/rw/nagios.cmd
chmod a+x /var/lib/nagios3
chmod g+s /var/lib/nagios3/rw
/etc/init.d/nagios restart

That error should go away and www-data should have read access to that file. You may need to
chgrp www-data /var/lib/nagios3/rw
But that was already there for me.

Sunday, July 18, 2010

Aastra disappoints again

I have been doing VoIP work with Asterisk since pre 1.0 days, which is about 6-7 years. The very first phones we choose were the Aastra/Sayason 480i which were very feature rich and pretty good hardware. The software quality is what seemed to be lacking, it was a while ago so I can't remember specific examples. We decided, back at that time, to switch to Polycom 501s. The 501s were not much more in price, had the same features, maybe even more at that time, and just felt like a much better product.

After a few years Aastra came out with the 57i. The popular Nerd Vittles Blog run by Ward Mundy called this phone the best asterisk phone back in 2008 http://nerdvittles.com/?p=207. I was still hesitant to use it due to my poor experience that I had with Aastra phones previously.

We decided to try them again about 6 months ago, and with the new XML scripts that Aastra has made for Trixbox/Elastix/PIF these phones work great with Asterisk and I have not had one issue with the phones firmware.

Now due to my renewed faith in Aastra we decided to try the Aastralink Pro 160 bundle, which comes with a few phones. Our customer also needed wireless as well so we thought we would try the MBU400 which the ALP 160 fully supports. This is where the fun started.

First the Aastralink Pro 160. It has decent hardware, 2 FXS ports and 6 FXO ports, and it a pretty reasonable price compared to building your own system with Sangoma or Digium PCI cards. For the software that is a different story.  As an Asterisk hacker I have been spoiled by flexibility. Even with Elastix (my choice of FreePBX distro) I can always throw something in extensions_custom.conf and change the dial plan to suite any customer's needs. Now I knew the ALP 160 didn't give me root access so I figured the basic stuff would be supported by the web based GUI, I guessed wrong.
  1. You are forced to dial 9 (PSTN) or 8 (SIP) to dial out, you can't change this and my customers hate dialing prefixes
  2. Not only do you have to dial a 8 prefix for SIP but if you have multiple SIP trunks you have to dial another prefix for the trunk number, ie 80 or 81 or 82
  3. Inbound SIP calls all go to the same place, you cannot control calls based on SIP trunk or DID, you can control where the calls from the FXO ports go.
  4. Each extension must have a Aastra hardware phone and must be online. You can have any SIP device as a mirrored extension but if the phone that it's mirrored to goes offline so does it.
  5. I am guessing that timing is not sync'd between the FXO and FXS because I could not get faxing to work for the life of me.

So what other options are there for a small PBX? Xorcom makes one that is decent, this is what we switched our customer to. Building your own out of a Atom based barebones kit is a good option too. Or Ward calls his choice the Orgasmatron http://nerdvittles.com/?tag=orgasmatron but they don't have any FXS or FXO.

Second the MBU 400. Now this is a nice looking unit. It actually is identical hardware to the SNOM m3. It integrates pretty easy with the Aastralink Pro 160 (except it can't be a mirrored extension it has to be its own extension). But for some reason the unit we had would not connect back to the access point after being out of range, unless there was some sort of manual intervention. So I spent hours going back and forth with Aastra tech support. Now you figure people calling reseller tech support would be fairly technical, so the support people should be just as technical or even more. This is not the case for Aastra. The most common response I get from them for a few different issues that I had called about was "I don't know." If it is a tough problem that is fine, but at least offer a next step or some sort of escalation.

So what did we do? We switched to Polycom, again. This time to the Polycom Kirk 5040 handsets with the 600 access point, and a couple repeaters. These were a bit tough to dig through the manual and figure out how to get it setup, I did need help from our distributor on setting up the repeaters, but other than that these things have been rock solid for our customer.

The point of me writing this, other than to bash on Aastra, is to plead for them to stop trying to be a software company. I have heard of and experienced many show stopping issues in their firmware.  Every time I upgrade an Aastra device I am not sure what I am going to get. It has been like this since the 480i days 7 years ago. I don't know what their development and testing process is, but I doubt that it is good. On the flip side, the hardware they make seems to be great, the problem is it takes them years to release firmware for it that is as good as the hardware is.

Thursday, July 15, 2010

Rails on Debian

Ran into a strange issue while debugging a Rails app that I am working on. I finally found the exact problem and issue here http://wiki.github.com/radiant/radiant/undefined-method-for-enumerable

Turns out that
the error comes from an incompatibility between Ruby 1.8.7 and Rails 2.0.2. Ruby 1.8.7 has String#chars and returns an Enumerator object, but Rails 2.0.2 expects an ActiveSupport::Multibyte::Chars object.
Which is pointed out here http://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg528878.html

Putting this in your environment.rb fixes the issue
unless '1.9'.respond_to?(:force_encoding)
  String.class_eval do
    begin
      remove_method :chars
    rescue NameError
      # OK
    end
  end
end

Wednesday, July 14, 2010

Fixing Python indentations in VIM

Had this problem today where I created a file with eclipse then edited it with vim and that made the tabs inconsistent. Found this post on stackoverflow that had a couple of suggestions

http://stackoverflow.com/questions/1024435/howto-fix-python-indentation

1. Use reindent.py - this requires apt-get install python-examples on debian/ubuntu
2. :%s/\t/ /g - this is what I used and it worked great

Other vim tricks I have used lately:
- Remove empty lines :%s/^[\ \t]*\n//g
- Re-indent entire file gg=G
- Remove lines with comments g/^#/d

Tuesday, June 01, 2010

Modular Django Admin Part 3

Django Auth has a lot of stuff built in http://docs.djangoproject.com/en/dev/topics/auth/

A list of all the default templates and urls you need for login password reset and password change. Most of these templates are in django/contrib/admin/templates/registration

url(r'^accounts/login/$', 'django.contrib.auth.views.login',name='login'),
url(r'^accounts/$', 'django.contrib.auth.views.login',name='login'),
url(r'^accounts/logout/$', 'django.contrib.auth.views.logout',{'next_page': '/'},name='logout'),
url(r'^accounts/password_change/$', 'django.contrib.auth.views.password_change',name='password_change'),
url(r'^accounts/password_change_done/$', 'django.contrib.auth.views.password_change_done',name='password_change_done'),
url(r'^accounts/password_reset/$', 'django.contrib.auth.views.password_reset',name='password_reset'),
url(r'^accounts/accounts/password_reset/done/$', 'django.contrib.auth.views.password_reset_done',name='password_reset_done'),
url(r'^accounts/reset/(?P[0-9A-Za-z]+)-(?P.+)/$', 'django.contrib.auth.views.password_reset_confirm',name='password_reset_confirm'),
url(r'^accounts/reset/done/$', 'django.contrib.auth.views.password_reset_complete',name='password_reset_complete'),

template/registration/login.html (django/contrib/admin/templates/admin/login.html)
template/registration/logged_out.html
template/registration/password_change_form.html
template/registration/password_change_done.html
template/registration/password_reset_email.html
template/registration/password_reset_done.html
template/registration/password_reset_confirm.html
template/registration/password_reset_complete.html

So now we can login see generic view lists of our models, not we need to edit them.

Sunday, May 16, 2010

Django Modular Admin Part 2

I just realized that alot of the functionality that the admin login has is not part of django.contrib.auth but is only in the admin.

A couple of things I have figure out as a result with form validation errors in the template.
{{ form.non_field_errors }} is for a ValidationError that is for the whole form.

A snippet of my login.html template


<div id="welcome_login" title="Welcome To My Site"> 
  <p><b>Admin Login</b></p>
  <form action="." method="post" enctype="multipart/form-data" class="forms" name="form" >{% csrf_token %}
   {% if form.non_field_errors %}
   <div class="response-msg error ui-corner-all">
    <span>Error message</span>
    {{ form.non_field_errors }}
   </div>
   {% endif %}
     <ul> 
    <li> 
     <label for="id_username" class="desc">{% trans 'Username:' %}</label> 
     <div>
      {% if form.username.errors %}
      <div class="response-msg error ui-corner-all">
       <span>Error message</span>
       {{ form.username.errors }}
      </div>
      {% endif %}
      <input type="text" tabindex="1" maxlength="255" value="" class="field text full" name="username" id="email" />

     </div> 
    </li> 
    <li> 
     <label for="id_password" class="desc">{% trans 'Password:' %}</label>
     <div> 
      {% if form.password.errors %}
      <div class="response-msg error ui-corner-all">
       <span>Error message</span>
       {{ form.password.errors }}
      </div>
      {% endif %}
      <input type="password" tabindex="1" maxlength="255" value="" class="field text full" name="password" id="password" />
      <input type="hidden" name="this_is_the_login_form" value="1" />
     </div> 
    </li> 
   </ul> 
  </form> 
 </div>

More to come on django auth

Thursday, May 13, 2010

Modular Django Admin Part 1

The django admin is awesome, we all know that, but there is always the debate to role your own or to customize the admin. Make the admin more modular would be ideal and that is what we will be trying to do.

In part one we will just start with the basics and setup a tabular list using the object_list generic view with gives you pagination for free.

One trick to get the key/value pairs into the template of the model is using a function to return self.__dict__.iteritems()

The Model (myapp/models.py)

class MyObject(models.Model):
    name = models.CharField(max_length=75)
    slug = models.SlugField(unique=True)
    
    def __unicode__(self):
        return self.name

    def attrs(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

The View (myapp/views.py)

from django.views.generic.list_detail import object_list
def applicant_list_jobs(request):
    return object_list(request, queryset=Job.objects.all(), paginate_by=5)

The Template (templates/myapp/myobject_list.html)

{% extends "admin/change_list.html" %}
{% block content %}
 <table>
  {% for object in object_list %}
  
  <tr class="{% cycle 'row1' 'row2' %}">
   {% for key,value in object.attrs %}
   <td>{{ value }}</td>
   {% endfor %}
  </tr>
  {% endfor %}
 </table>
 <p class="paginator">
 {% if page_obj.has_previous %}
  <a href="?page={{ page_obj.previous_page_number }}"><</a>
 {% endif %}
  Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
 {% if page_obj.has_next %}
  <a href="?page={{ page_obj.next_page_number }}">></a>
 {% endif %}
</p>
{% endblock content %}

Next up will be where the templates and urls go for login and the built in authentication tools.