Daniel Banck's Thoughts

A blog about software development with focus on web applications. Flavoured with ubuntu, photography and some university notes.

pam_ssh_agent_auth with Ubuntu

You may have come across pam_ssh_agent_auth which allows you to forward the sudo authentication to your local ssh agent.

There are some great blog posts about installing / configuring it already, but I wanted to make it even easier.
I’ve created a ubuntu package, available from my server ppa.

The installation is pretty straight forward:
sudo aptitude install python-software-properties
sudo apt-add-repository ppa:dbanck/server
sudo aptitude update
sudo aptitude install pam-ssh-agent-auth

After the installation is done, you just have to edit two config files to enable it.
In /etc/pam.d/sudo replace
@include common-auth with
auth sufficient pam_ssh_agent_auth.so file=%h/.ssh/authorized_keys

And in /etc/sudoers add this line:
Defaults env_keep += SSH_AUTH_SOCK

That’s it!
Now connect to your server via ssh -A to forward the ssh-agent.
You may clear you cached sudo credentials via sudo -K and check if everything works.

Move and theme update

It’s been over a year since I wrote the last post. Time is running pretty fast…

I just moved this blog to a new server and changed the theme to a simple and cleaner one.
Maybe I’ll move away from wordpress soon. I just need the time for that.

POST Data with .net Html Agility Pack

I’m using the Html Agility Pack for getting and parsing HTML pages. It offers many possibilities, including XPath selectors.

Today I had a problem with posting data to a webpage. It’s possible to call the HtmlWeb Load function with a second “POST” parameter, but it offers no easy way to add values to be posted.
After a while of searching I came across this solution: add this two functions to the HtmlWeb class. (You can get the source in download section, too)

public HtmlDocument SubmitFormValues (NameValueCollection fv, string url)
{
	// Attach a temporary delegate to handle attaching
	// the post back data
	PreRequestHandler handler = delegate(HttpWebRequest request) {
		string payload = this.AssemblePostPayload (fv);
			byte[] buff = Encoding.ASCII.GetBytes (payload.ToCharArray ());
			request.ContentLength = buff.Length;
			request.ContentType = "application/x-www-form-urlencoded";
			System.IO.Stream reqStream = request.GetRequestStream ();
			reqStream.Write (buff, 0, buff.Length);
			return true;
	}
	this.PreRequest += handler;
	HtmlDocument doc = this.Load (url, "POST");
	this.PreRequest -= handler;
	return doc;
}
 
private string AssemblePostPayload (NameValueCollection fv)
{
	StringBuilder sb = new StringBuilder ();
	foreach (String key in fv.AllKeys) {
		sb.Append ("&" + key + "=" + fv.Get (key));
	}
	return sb.ToString ().Substring (1);
}

Source: http://htmlagilitypack.codeplex.com/Thread/View.aspx?ThreadId=14255

Now posting is easy. Example:

NameValueCollection postData = new NameValueCollection (1);
postData.Add ("name", "value");
doc = this.hw.SubmitFormValues (postData, url);

(hw is an HtmlWeb object and doc an HtmlDocument object)

If you need to use cookies, too, you need to modify the HtmlWeb a little more, because there is a little bug.
First add a new private variable:

private CookieContainer _ck = new CookieContainer();

Then find this section:

if (_useCookies) 
{
        req.CookieContainer = new CookieContainer();
}

And change it into:

if (_useCookies) 
{
        req.CookieContainer = this._ck;
}

Now you just have to activate the usage of cookies in your HtmlWeb object:

this.hw = new HtmlWeb ();
this.hw.UseCookies = true;

That’s it! Enjoy easy form posting with a full cookie jar (:

Meine Mitschriften

Eine Idee kurzerhand umgesetzt.
Auf http://uni.dbanck.de findet ihr ab heute die meisten meiner Mitschriften der Vorlesungen,
die ich an der Uni Kiel besuche.

Weitere Informationen gibt es auf der Seite. Viel Spaß!

Github, Windows and TortoiseGit – Part 1 Installing & Pulling

This is a guide for all the Windows users (not me) out there.
It will explain how to work with a github repository under windows using TortoiseGit.

Let’s get started. At this point you should be a collaborator or owner of a github repository.
Download TortoiseGit from here.
Install it like any other program. There are no special settings, just keep on clicking ‘next’.

Now download msysgit from here.
During this installation you have to set some important settings. Please be sure that you set them correctly.

Adjust the PATH environment

Adjust the PATH environment

Choose the SSH executable

Choose the SSH executable

Choose line endings

Choose line endings

After the successful installation of both programs continue with the generation of private and public SSH-key.
To do this you have to start ‘PuTTY Key Generator’ – find it in Start – Programs – TortoiseGit

Just click 'Generate' and move the mouse

Just click 'Generate' and move the mouse

After it’s done you see your freshly generated ssh public key.
You can define a passphrase at this step, but it isn’t required.

Save the public AND private key for later usage.

Save the public AND private key for later usage.

You can copy the key out of the box and add it to your github account.
Add key to github

Now your ready to create a local repository. Create an empty folder and right-click into it. Choose ‘Git Create repository here’
Create repository

Now go to the repository settings

Now go to the repository settings...

... and set your username and email that your using at github.

... and set your full name and email that your using at github.

Config remote
Here you click ‘Add New’ and paste the github ‘Your Clone URL’ into the url field.
Select the putty private key we created before, too.

Now we’re ready to pull for the first time.
Select ‘Pull’ from the TortoiseGit menu.
Pulling
For ‘remote’ select the ‘origin’ we added before.
Be sure that ‘AutoLoad Putty Key’ is checked and for ‘Remote Branch’ ‘master’ is selected (type it if you can select it).

Now click you and we’re down.
You should all the repository files now if you did everything correctly.
Part 2 – Commiting & Merging is coming soon.

Bug in cherokee <0.99.20

If you’re trying to use cherokees ‘Advanced Virtual Hosting’ and a ‘File exists’ rule it will not work.
The rule is not matching any files. Without the AVHosting the rule is working well.

I already reported this bug and it’s fixed in the latest svn version. So if you need to work with AVHosting and this rule you better checkout from svn and compile on your own instead of using any precompiled packages.

But I guess in further versions the bugfix will be included.

Umstrukturierung

Da ich nun mit der Schule fertig bin und ab 1.7 anfange zu arbeiten, tritt meine Tätigkeit als Freelancer in den Hintergrund.
Daher gibt es hier auf der Startseite nun auch direkt meinen Blog zu lesen und keine “Webentwicklung aus Kiel!” mehr.

Außerdem werde ich sicherlich regelmäßiger aktiv sein und die Artikel – je nach Laune – in Englisch oder Deutsch verfassen.
Dank der simplen Cherokee-Konfiguration wird /blog auch einfach auf / umgeleitet. So landet nichts im Nirwana.

CakePHP and Cherokee

Getting CakePHP working with cherokee is really simple:

First put your CakePHP files into any directory. For me it’s /home/daniel/Web/dcms
You can delete all the .htaccess files:
/.htaccess
/app/.htaccess
/app/webroot/.htaccess

Add a new virtual host to cherokee, set the document root to the CakePHP webroot:
/home/daniel/Web/dcms/app/webroot
Directory Indexes should be set to index.php

Add a new PHP rule – with the new wizard it’s really simple – and keep the default settings.
Add a new ‘file exists’ rule and let it match ‘Any File’ and handle it as ‘Static Content’.
Cherokee 'Match any file'
Cherokee 'Handle as static content'

How we need to modify the default rule. Change the handler to ‘Redirection’ and setup the regex like this:
Cherokee 'Default regex'
(Internal, ^.*$, index.php)

That’s it!
Just make sure that the rules are in the correct order (php, any file, default):
Cherokee 'rule order'

Just save and access CakePHP via your defined domain.
Final result

Gymnasium Altenholz online!

Endlich!
Nach einem Jahr Entwicklungsarbeit und zu guter Letzt noch ein paar Problemen beim Transfer der Domain, erstrahlt der Webauftritt des Gymnasium Altenholz’ endlich in einem neuen Licht.

Nun heißt es wohl nur noch: auf die nächsten Bugs warten, hoffen das es nicht zu viele sind und sie schnellstens beheben.

Using Jinja2 with Django

In the last days I tried to get Jinja2 working with Django.
Here I want to explain a bit about the integration progress.

First we need to install django and jinja2, but I won’t explain it in here, just use easy_install.
After installing you can check if everything works by importing django and jinja2 inside a python shell.

If it works we need to create a django project and a example application. (Go for the django tutorial if you don’t know how to do this).

Now we need to tell django to use jinja2 to render templates.
The best way to do this is creating a little lib with function to do this which one can import into the views.
So we create inside our project root a directory called libs and there an empty __init__.py file and a file for the jinja2 things.
Lets call it jin.py.

Inside this file we want to create a render_to_response function for jinja2 templates which can be imported from any view.
So here we go:

from django.http import HttpResponse
from django.conf import settings
from django.utils import translation
from django.core.urlresolvers import get_callable
from jinja2 import FileSystemLoader, Environment, PackageLoader, ChoiceLoader
from functools import wraps
 
loader_array = []
for pth in getattr(settings, 'TEMPLATE_DIRS', ()):
    loader_array.append(FileSystemLoader(pth))
 
for app in settings.INSTALLED_APPS:
    loader_array.append(PackageLoader(app))
 
default_mimetype = getattr(settings, 'DEFAULT_CONTENT_TYPE')
global_exts = getattr(settings, 'JINJA_EXTS', ())
env = Environment(extensions=global_exts, loader=ChoiceLoader(loader_array))

This creates the basic jinja2 environment. Notice that it automatically reads the TEMPLATE_DIRS from settings.py and furthermore you can declare inside the settings file which jinja2 extensions should be loaded.

if 'jinja2.ext.i18n' in global_exts:
    env.install_gettext_translations(translation)
 
global_imports = getattr(settings, 'JINJA_GLOBALS', ())
for imp in global_imports:
    method = get_callable(imp)
    method_name = getattr(method,'jinja_name',None)
    if not method_name == None:
        env.globals[method_name] = method
    else:
        env.globals[method.__name__] = method
 
global_filters = getattr(settings, 'JINJA_FILTERS', ())
for imp in global_filters:
    method = get_callable(imp)
    method_name = getattr(method,'jinja_name',None)
    if not method_name == None:
        env.filters[method_name] = method
    else:
        env.filters[method.__name__] = method
 
global_tests = getattr(settings, 'JINJA_TESTS', ())
for imp in global_tests:
    method = get_callable(imp)
    method_name = getattr(method,'jinja_name',None)
    if not method_name == None:
        env.tests[method_name] = method
    else:
        env.tests[method.__name__] = method

We can also declare the jinja2 globas, filters and tests inside the settings.py via adding:

JINJA_EXTS = (
    'jinja2.ext.i18n',
)
 
JINJA_GLOBALS = (
)
 
JINJA_FILTERS = (
)
 
JINJA_TESTS = (
)

to it.

So until now our jinja2 environment contains the i18n extension and all other things added in settings.py.
Time to create a render_to_response function.

def render_to_string(filename, context={}):
    template = env.get_template(filename)
    rendered = template.render(**context)
    return rendered
 
def render_to_response(filename, context={}, request=None, mimetype=default_mimetype):
    if request:
        context['request'] = request
        context['user'] = request.user
    rendered = render_to_string(filename, context)
    return HttpResponse(rendered,mimetype=mimetype)

This function also adds the user object to the context if a request is available.

Inside a view we can to render a template with jinja2:

from libs.jin import render_to_response
 
def index(request):
    return render_to_response('app_name/index.html')

Wow. This wasn’t hard, was it?

But you might notice that the user object still isn’t available.
To get the user object back we would need to pass the request object every time to the render_to_response function. That’s really annoying and looks ugly:

return render_to_response('app_name/index.html',{},request)

So we create a decorator with some more nice features inside our jin.py:

def jin_renderer(prefix=None):
    def renderer(func):
        @wraps(func)
        def wrapper(request, *args, **kwargs):
            global default_mimetype
            response = func(request, *args, **kwargs)
 
            if isinstance(response, HttpResponse):
                return response
            elif isinstance(response, basestring):
                template_name = response
                context_processors = {}
                mimetype = default_mimetype
            elif isinstance(response, (tuple, list)):
                len_tuple = len(response)
                if len_tuple == 2:
                    template_name, context_processors = response
                    mimetype = default_mimetype
                elif len_tuple == 3:
                    template_name, context_processors, mimetype = response
 
            if prefix:
                if isinstance(template_name, (list, tuple)):
                    template_name = map(correct_path, template_name)
                else:
                    template_name = correct_path(template_name)
 
            return render_to_response(template_name,context_processors,request,mimetype)
        return wrapper
 
    def correct_path(template_name):
        if template_name.startswith('/'):
            return template_name[1:]
        return '%s%s' % (prefix, template_name)
 
    return renderer

When using this renderer the request object is always available and we can also define a template prefix path.
Using this new renderer is very easy, too:

from libs.jin import jin_renderer
 
render_to_html = jin_renderer('app_name/')
 
@render_to_html
def index(request):
    return 'index.html',{'test':'some context test'}

Now the index.html side your templates/app_name/ will be rendered.

These sites helped me creating the code:
http://jinja.pocoo.org/2/documentation/
http://www.djangosnippets.org/snippets/1112/
http://www.djangosnippets.org/snippets/1061/
http://tinyurl.com/57mojt