Karrigell/Home page

Home page
For the moment we'll ignore the CDs and begin writing the home page

Create a new folder mycds in your Karrigell distribution, under the www folder. With your favorite text editor, save this text in a file called index.ks

def index: print "&lt;h1&gt;My CD collection&lt;/h1&gt;"

In your browser ask localhost/mycds/index.ks/index

The extension "ks" (Karrigell Service) means that the script is a Python script where functions match urls : here, index.ks/index means that the function index will send the data back to the browser

In fact
 * if you don't specify a function name, the index function in the script will be run, so you could have asked localhost/mycds/index.ks
 * and if you don't even specify a script name in a folder, the script called <tt>index</tt> will be run. So all you have to type really is <tt>localhost/mycds</tt>

Notice the <tt>print</tt> statement in the <tt>index</tt> function. In Karrigell scripts, the <tt>print</tt> statement sends the content to the browser ; by default, this content is supposed to be HTML code

Adding a page counter
The page counter will print the number of times that the page has been seen. We'll use a file, <tt>counter.txt</tt>, to store the number of visits and increment it for each visit

def index: print "&lt;h1&gt;My CD collection&lt;/h1&gt;" try: visits = int(open('counter.txt').read) except IOError: # first visit : the file does not exist visits = 0 visits += 1 out = open('counter.txt','w') out.write(str(visits)) out.close print "%s visits" %visits

Each time the page is reloaded, you will see the number of visits incremented by 1

Notice that the file is opened by the usual <tt>open</tt> function ; relative paths are converted to absolute paths relatively to the script directory

A better page counter
A page counter should not increment if the same user repeatedly reloads the same page. This requires a way to identify the browser which is sending the request. For this task, web programming provides session management, and Karrigell has a very simple implementation of this concept

Use the built-in <tt style="color:blue">Session</tt> function

Karrigell scripts are run in a namespace prepared by the framework : among the names available in scripts, you have this <tt style="color:blue">Session</tt> function, which returns an object specific to each individual client (it uses cookies). This object is an ordinary Python object, to which you can set any attribute you like

Here, for a new client, we will create an attribute user to the session object. If a client requests the home page and his session object already has this attribute, the counter will not be incremented

def index: print "&lt;h1&gt;My CD collection&lt;/h1&gt;" try: visits = int(open('counter.txt').read) except IOError: # first visit : the file does not exist visits = 0 if not hasattr(Session,"user"): visits += 1 out = open('counter.txt','w') out.write(str(visits)) out.close Session.user = None   # create attribute user print "%s visits" %visits

Including scripts
This page counter could be used in other parts of the application, so it's better to put it in a separate file : save this part of the script in <tt>counter.py</tt>

try: visits = int(open('counter.txt').read) except IOError: # first visit : the file does not exist visits = 0 if not hasattr(Session,"user"): visits += 1 out = open('counter.txt','w') out.write(str(visits)) out.close Session.user = None  # create attribute user print "%s visits" %visits

To include this script in the home page, change the script <tt>index.ks</tt> to this :

def index: print "&lt;h1&gt;My CD collection&gt;/h1&gt;" Include('counter.py')

<tt style="color:blue">Include</tt> is another Karrigell built-in function : it takes a script url as argument, an inserts the result of this script's execution in the page

To make <tt>counter.py</tt> even more modular, the file name could be passed as argument to <tt style="color:blue">Include</tt> :

def index: print "&lt;h1&gt;My CD collection&gt;/h1&gt;" Include('counter.py',counter_file='counter.txt')

The keyword argument to <tt style="color:blue">Include</tt> will be in the execution namespace when <tt>counter.py</tt> is run. Rewrite <tt>counter.py</tt> with this parameter :

try: visits = int(open(counter_file).read) except IOError: # first visit : the file does not exist visits = 0 if not hasattr(Session,"user"): visits += 1 out = open(counter_file,'w') out.write(str(visits)) out.close Session.user = None  # create attribute user print "%s visits" %visits

Logging in/out
Let's add the link for users to log in to the application

def index: print "&lt;h1&gt;My CD collection&lt;/h1&gt;" print '&lt;a href="login"&gt;Login&lt;/a&gt;&lt;br&gt;' Include('../counter.py',counter_file='counter.txt')

The link leads to the reference <tt>"login"</tt>. It is a relative url to the base url, <tt>host/records/index.ks/index</tt>, so it resolves to <tt>host/records/index.ks/login</tt>, meaning that the link will invoke the execution of the function <tt>login</tt> in the script <tt>index.ks</tt>

This function will generate an HTML form asking the user's login and password, and submitting these values to an authentication test

def login: print '&lt;h1&gt;Login&lt;/h1&gt;' print '&lt;form action="check_login" method="post"&gt;' print 'Login &lt;input name="login"&gt;&lt;br&gt;' print 'Password &lt;input type="password" name="passwd"&gt;&lt;br&gt;' print '&lt;input type="submit" value="Ok"&gt;' print '&lt;/form&gt;'

The attribute <tt>"action"</tt> for the form is <tt>check_login</tt>. Again, this means that the function <tt>check_login</tt> in <tt>index.ks</tt> will be run

Unlike <tt>index</tt> and <tt>login</tt>, this function will take arguments : the submitted login and password. The arguments must be called like the names used in the input fields of the form : <tt>login</tt> and <tt>passwd</tt>. For the moment we'll use a very simple test to see if the user is allowed to log in

def check_login(login,passwd): if login=="john" and passwd=="doe": Session.user = login print "logged in" else: print "try again"

Inside this function, if the test succeeds, the attribute <tt>user</tt> of the session object is set to the user's login

For the moment, all we have is the result of the test. After the test, we should be automatically sent back to the home page. The HTTP protocol provides a redirection feature, and Karrigell implementation is once again very straightforward : a built-in exception called <tt style="color:blue">HTTP_REDIRECTION</tt>

Change the function <tt>check_login</tt> like this :

def check_login(login,passwd): if login=="john" and passwd=="doe": Session.user = login raise HTTP_REDIRECTION,"index"

This means that after the function is run, the browser redirects to the specified url, here the one that matches the function <tt>index</tt> in the script

We can now change the home page to welcome the authenticated user with his name, and allow him to logout :

def index: print "&lt;h1&gt;My CD collection&lt;/h1&gt;" <b>logged = hasattr(Session,"user") and Session.user is not None if logged: print 'Logged in as %s&lt;br&gt;' %Session.user print '&lt;a href="logout"&gt;Logout&lt;/a&gt;&lt;br&gt;' else:</b> print '&lt;a href="login"&gt;Login&lt;/a&gt;&lt;br&gt;' Include('../counter.py',counter_file='counter.txt')

The logout function will only set <tt>Session.user</tt> to <tt>None</tt> and redirect to the home page :

def logout: del Session.user raise HTTP_REDIRECTION,"index"

Summary
The programming style used by Karrigell is based on namespace : in the scripts, instead of importing modules or using environment variables, a number of built-in names are available for the most common tasks. So far we have covered session management with the built-in name <tt style="color:blue">Session</tt>, inclusion of scripts with <tt style="color:blue">Include</tt> and HTTP redirection with the built-in exception <tt style="color:blue">HTTP_REDIRECTION</tt>

This makes programming with Karrigell extremely straightforward, thus easy to code and to maintain

While other formats are available (see the documentation for pure-Python scripts, CGI, Python Inside HTML, HTML Inside Python) I suggest to use "Karrigell Services" to group most of the application logic in one script ; peripheric modules can be imported or included

In the next section we will write the code to manage the CD database