KSSing your website with kss.django application
msaelices
I will use the coolwiki project code as an example of KSSing a web site.
Register kss.django application in your project Register kss.django application in your settings.py:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'coolwiki.wiki',
'kss.django', # KSS django application
)
Setup your KSS plugins If you want to use scriptaculous effects, or other plugins, you can put on KSS_EXTRA_PLUGINS settings parameter, like that:
KSS_EXTRA_PLUGINS = ['scriptaculous-effects']
URL configuration One of the main goals for kss.django application was the advocacy of using exactly the same django views for Javascript version and classic version of web sites. First of all, in your root urls.py, you have to put this:
urlpatterns = patterns('',
(r'^kss/', include('kss.django.urls')),
...
)
This line is needed for automatically loading of all the KSS Javascripts.
Later, in your application, you must change your URLs. For example I put the URLs like below for my coolwiki demosite:
from wiki import views
urlpatterns = patterns('',
(r'^$', views.index),
(r'^view$', views.view),
(r'^edit$', views.edit),
(r'^save$', views.save),
(r'^history$', views.history),
(r'^ajax/view$', views.view, {'is_kss': True}),
(r'^ajax/edit$', views.edit, {'is_kss': True}),
(r'^ajax/save$', views.save, {'is_kss': True}),
(r'^ajax/history$', views.history, {'is_kss': True}),
)
As you can see, It is using the same view for the AJAX version and the normal version in every URL. All the URLs begin with wiki/... are standard, but the ajax/... the URLs go to the same view in KSS version (with is_kss parameter enabled).
What decides the URL is that the user goes to when he clicks in a wiki word? The answer is: KSS. In your template, you put some HTML code like this:
This is a <span class="page-link"><a href="/wiki/view?title=WikiPage">WikiPage</a></span>.
And you put this in your KSS file:
.page-link a:click{
evt-click-preventdefault: True;
action-server: ajax/view;
ajax/view-title: nodeContent();
}
If you have Javascript disabled, the link you click goes to /wiki/view?title=WikiPage?, that pass to wiki.views.view with is_kss=False. But if you have enabled Javascript, KSS will do an Ajax request to /ajax/view with a title HTTP parameter with WikiPage? value. The views will change the wiki content but without returning a normal HttpResponse.
Warning: KSS does not yet support for dynamic URL actions, that is needed for to get RESTful URLs in your application. For example, in the wiki demo site, I have to change the /wiki/WikiWord/view URL format to a /wiki/view?title=WikiWord? format, to get working both Javascript and non Javascript version with exactly the same code in the django views.
KSSing django views The low level way to use a django view with KSS is this:
def view(request, is_kss=False):
page_name = request.REQUEST['title'] # it take the wiki word
page = Page.objects.get(name=page_name)
if is_kss:
commands = KSSCommands()
commands.core.replaceInnerHTML(css('div.content'), page.cooked_content)
return HttpResponse(commands.render(), mimetype='text/xml')
else:
return render_to_response('wiki/view.html', {'page': page})
It is valid and clean way to get AJAX rendering and also works with Javascript disabled.
For the django templates, kss.django has several templatetags that helps your development. One of those is include_kssjs. It looks for the plugins you have installed and puts all of your <script type="text/javascript" src="..." /> tags in automatically.
The wiki/view.html may look like this:
<html>
<head>
{% load ksslib %}
{% include_kssjs %}
</head>
<div class="content">
</div>
</html>
But often you have a more complex template with a lot of rendering thing and you don't want to do the rendering by hand. For example, if you have this template:
<html>
<head>
{% load ksslib %}
{% include_kssjs %}
</head>
<div class="content">
<h1>{{ page.name }}</h1>
<div>
{{ page.cooked_content }}
<table>
<tr><td>Version:</td><td>{{ page.version }}</td></tr>
</table>
</div>
</div>
</html>
If you want to change all div.content fragment, you must to do ugly things like this:
def view(request, is_kss=False):
...
tpl = Template('''<h1>{{ page.name }}</h1>
<div>
{{ page.cooked_content }}
<table>
<tr><td>Version:</td><td>{{ page.version }}</td></tr>
</table>
</div>''')
ctx = Context({'page': page})
if is_kss:
commands.core.replaceInnerHTML(css('div.content'), tpl.render(ctx))
...
To avoid this ugly example and to keep with the DRY principle, kss.django has a ksswidget templatetag, and several generic views like render_widget. The template becomes:
<html>
<head>
{% load ksslib %}
{% include_kssjs %}
</head>
<div class="content">
{% ksswidget main %}
<h1>{{ page.name }}</h1>
<div>
{{ page.cooked_content }}
<table>
<tr><td>Version:</td><td>{{ page.version }}</td></tr>
</table>
</div>
{% endksswidget %}
</div>
{% ksswidget footer %}
You are on {{ page.name }}
{% endksswidget %}
</html>
And the views becomes:
def view(request, is_kss=False):
page_name = request.REQUEST['title'] # it take the wiki word
page = Page.objects.get(name=page_name)
if is_kss:
commands = KSSCommands()
commands.django.replace_widgets('wiki/view.html',
['main', 'footer'], {'page': page})
return HttpResponse(commands.render(), mimetype='text/xml')
else:
return render_to_response('wiki/view.html', {'page': page})
commands.django is a command set that is registered in KSS when django.kss initializes. replace_widgets is a KSS command that renders the ksswidget nodes and returns the HTML rendered as a KSS action. The effect is that both the main content and footer are updated in the page, but without refreshing the page.
There is a shortcut for avoid the return return HttpResponse(commands.render(), mimetype='text/xml'). Is the kss_response method. The code becomes:
from kss.django.render import kss_response
def view(request, is_kss=False):
page_name = request.REQUEST['title'] # it take the wiki word
page = Page.objects.get(name=page_name)
if is_kss:
commands = KSSCommands()
commands.django.replace_widgets('wiki/view.html',
['main', 'footer'], {'page': page})
return kss_response(commands)
else:
return render_to_response('wiki/view.html', {'page': page})
There is also a shortcut for this, the render_kss_response generic view. By using this the view code can be reduced to:
from kss.django.render import render_kss_response
def view(request, is_kss=False):
page_name = request.REQUEST['title'] # it take the wiki word
page = Page.objects.get(name=page_name)
commands = KSSCommands()
return render_kss_response('wiki/view.html', {'page': page},
is_kss, ksswidgets=['main', 'footer'],
commands=commands)
The render_kss_response will do the if is_kss: ... else: ... stuff for us. Remember that this small amount of code works with Javascript and without.
If you want to add some extra effects to the ksswidget rendering, you can also create KSSCommands and pass it to the render_kss_response, like this:
from kss.django.render import render_kss_response
def view(request, is_kss=False):
page_name = request.REQUEST['title'] # it take the wiki word
page = Page.objects.get(name=page_name)
commands = KSSCommands()
if is_kss:
commands.scriptaculous.effect(css('div.message'), 'blinddown')
commands.scriptaculous.effect(css('div.message'), 'blindup', delay=2)
return render_kss_response('wiki/view.html', {'page': page},
is_kss, ksswidgets=['main', 'footer'], commands=commands))