A captcha for Chuck Norris
October 8th, 2007My friend Juan sent me this captcha today.

I guess it’s only for Chuck
My friend Juan sent me this captcha today.

I guess it’s only for Chuck
It’s been almost a month since my last post and now it’s when I say it’s because I’ve been busy and think I meant lazy. For this time I have a proof: I’ve been making a website for a friend’s pop band, Ana Lógica It’s not completely finished but most of the work is already done.
I’ll try to describe the proccess of creating such a site so beware of a very boring post if you are not into web development.
The first thing I did was talk with my friend/client about what they wanted. We decided to take another website as a model for a start point. That website is http://www.lombardafolk.com/ and I did it too, some years ago.
As this was very vague talking I had the feeling I lack more information from them about what exactly they wanted. The model website was about an old folk band and this one was about a new pop band and some requirements are different.
I drew all the screens in paper in a fairly detailed manner (10 screens in total). Then I photocopied them and got 5 copies and scheduled a meeting with all the members of the band. We met in a bar and while drinking some beers we discussed my design. They liked it generally but gave me some new ideas and spotted minor things they didn’t like or I did fogotten. We also made a list of the multimedia and information material they needed to give to me. I was really happy with the results of that initial meeting because:
It took us less than one hour and a half (including other chats, drinking and eating) and I got everything I needed to get me started. Lesson learned: always meet the client before you make any coding and if you can, make a paper prototype, you can’t imagine how much work and frustration you will save by doing so.
Now that I knew what I had to do I needed to choose how to do it. In other words, the technology. I choosed Django over Zope or PHP for this project for the following reasons:
It was time to get my hands dirty so I created a subversion repository to put my code in and started to make a Django 0.96 application. I only worked in my (not so frequent) spare time. I started on the 5th of September and got a beta version yesterday. Acording to my commits I worked 13 days and I estimate an average of 2-3 hours each day: around 30 hours in total. Not too bad, huh?
First I designed the models and created the database. For development I used SQLite and for deployment I used MySQL. I set up some fixtures so I didn’t have to input all the data again and again. By designing almost all the models initially, rebuilding the database was something I didn’t have to do very often.
Then I started to write views for each tab page. It’s important to note that I didn’t write any CSS code yet. The pages were really ugly but at the same time were pretty clean and allowed me to focus on the functionality and structure without distractions. I made 80% of all view code before even thinking on any visual design issue.
Then my friend sent me the material. He sent me the graphics for all their home-made records and reminded me that they want the website to look similar to the last record graphical design. So I opened my vector graphics application (Inkscape) and designed the homepage of the website. I thought about doing that in a raster graphics application (GIMP) but happily I used Inkscape which let you change a lot of stuff much more easily than with GIMP.
When I finished the graphical design I sent it to my friend for validation and after some minor corrections they all agreed with it so I could continue. Then I started moving that desing into the website using CSS and some graphics. I’m pretty proud of the final result since it’s quite elastical and adapt to a wide range of screen resolutions. Of course its XHTML and CSS validates and it’s quite accesible. Long life to web standards!
Next challengue was to make a template tag for the navigation tabs so it could automatically detect which one was selected in each view. In the settings.py file I had a list of tabs:
NAVTABS = (
('/', 'Noticias'),
('/songs/', 'Canciones'),
('/pictures/', 'Fotos'),
('/concerts/', 'Conciertos'),
('/band/', 'El Grupo'),
('/contact/', 'Contacto'),
)
Each tuple having an url prefix and the label of the tab. Then my custom templatetag looks like this:
@register.inclusion_tag('navtabs.html', takes_context=True)
def navtabs(context):
request = context['request']
path = request.get_full_path()
tabs = [dict(url=url, title=title, current=path.startswith(url))
for url, title in settings.NAVTABS]
active_tabs = [t for t in tabs if t['current'] is True]
# We may have two or more current tabs if prefixes are the same
# In this case the longest matching prefix wins
if len(active_tabs) > 1:
# sort the tabs by its length
def sorter(tab1, tab2):
return cmp(len(tab1['url']), len(tab2['url']))
active_tabs.sort(sorter)
# The only current one is the longest one (the last)
for tab in active_tabs[:-1]:
tab['current'] = False
return {'navtabs': tabs}
And the template navtabs.html looks like this:
<ul id="nav-tabs">
{% for tab in navtabs %}
<li {% if tab.current %}class="current"{% endif %}><a href="{{ tab.url }}">{{
tab.title }}</a></li>
{% endfor %}
</ul>
Now, all I have to do in my master template file is add this piece of code to have automatic navigation tabs! :
{% load navtabs %}
{% navtabs %}
And if I change, add or remove a tab I just need to update the settings.py file.
Something the band wanted is that the users could download their songs. I thought about two options: a) downloading each songs individually so you can get a grasp of how the band sounds like and b) download the whole album if you liked them. This way everybody saves bandwidth
But I didn’t want to store the mp3 files twice in the storage server. Each one individually and a ZIP for each record with the mp3s inside it.
So I decided to create the zip files on the fly:
def record_download(request, record_id):
record = get_object_or_404(Record, id=record_id)
buffer = StringIO.StringIO()
zip = zipfile.ZipFile(buffer, "w")
for song in record.song_set.all():
filename = song.get_audio_filename()
zip.write(filename, os.path.basename(filename))
zip.close()
name = u'Ana Logica - %s.zip' % unicode(record.title, 'utf-8')
name = name.encode('utf-8')
response = HttpResponse(mimetype="application/zip")
response['Content-Disposition'] = 'attachment;filename="%s"' % name
response.write(buffer.getvalue())
return response
My layout is the typical header - main content with right sidebar - foot layout and I wanted to apply a soft background image tileable vertically in the sidebar. Usually you go for a liquid layout where you fix the width of the website and create a big horizontal image (something like 800×5 pixels) and repeat it all over the main content. That way, when you float right the sidebar everything looks like the height of the sidebar is the same as the height of the main content even when that is not actually the case. But I had two problems:
So how did I solved it? Using a javascript oneliner
:
$(document).ready(function () { $("#sidebar").height($("#body").height()); });
Now my sidebar is always as tall as the main content (div id=”body”) of my website and the background image is applied properly.
I had a problem with 800×600 screen resolutions: as the main content has a width of 80%, in those resolutions it was not big enough and the tabs collide with the logo. Some more javascript and problem solved:
function fix_layout() {
/* If the window is too small, no margins */
if ($(window).width() < 880) {
$("div#page-wrapper").css("margin", "0");
} else {
$("div#page-wrapper").css("margin", "0 10% 0 10%");
}
/* Make the sidebar as tall as the #body element */
$("#sidebar").animate({height: $("#body").height()}, 1000);
}
$(document).ready(fix_layout);
$(window).resize(fix_layout);
Now you can see that when the user has an old screen he/she can still view the website properly but with no margins. I love the way javascript looks after dressing it with JQuery
As the band is going to update the content and not me I wanted the design to stay as good as initially. This mean no big images popping suddenly in the middle of a news item or a gallery. This also means I can’t trust my users to resize the images because they are humans and humans usually forget those tedious things. So I stolen this small template tag to get automatic thumbnailing for free:
def thumbnail(file, size='200x200'):
# defining the size
x, y = [int(x) for x in size.split('x')]
# defining the filename and the miniature filename
basename, format = file.rsplit('.', 1)
miniature = basename + '_' + size + '.' + format
miniature_filename = os.path.join(settings.MEDIA_ROOT, miniature)
miniature_url = os.path.join(settings.MEDIA_URL, miniature)
# if the image wasn't already resized, resize it
if not os.path.exists(miniature_filename):
filename = os.path.join(settings.MEDIA_ROOT, file)
image = Image.open(filename)
image.thumbnail([x, y], Image.ANTIALIAS)
image.save(miniature_filename, image.format)
return miniature_url
register.filter(thumbnail)
This is an example of how to use it:
{% if newsItem.picture %}
<img alt="" src="{{ newsItem.picture|thumbnail:"100x100" }}" width="100" height="100"/>
{% endif %}
Something I had learned from the past is this rule: every public HTML form you put in a website is going to be used for spam sooner or later. You need to assume it and live with it: either you don’t put public forms or you fight spammers with a captcha or similar techique. I decided to go with recaptcha.net as it looks quite nice and they use the information the users write for digitalizing books. Really smart.
Adding support for recaptcha is fairly easy:
@register.simple_tag
def recaptcha():
return captcha.displayhtml(settings.RECAPTCHA_PUBLIC_KEY)
{% load recaptcha %}
<form method="post" action=".">
{{ form.as_p }}
{% recaptcha %}
<p class="center"><input type="submit" value="Enviar" /></p>
</form>
{% endblock %}
def captcha_is_valid(request):
challengue = request.POST.get('recaptcha_challenge_field', '')
response = request.POST.get('recaptcha_response_field', '')
ip = request.META.get('HTTP_X_FORWARDED_FOR', '')
captcha = submit(challengue, response, settings.RECAPTCHA_PRIVATE_KEY, ip)
return captcha.is_valid
def contact_form(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if captcha_is_valid(request) and form.is_valid():
# send the mail
...
The problem with recaptcha is that you need to type two words instead of one and the band members have complained to me saying it may be too difficult for their visitors. Sigh
I probably forget to mention some stuff but I guess this post is already way too long so I will stop now. I’ll just mention that I developed the whole thing using Firefox 2.0 and I tried the page in Internet Explorer 7.0 I was happily surprised since it looked pretty good. I know it doesn’t look as nice in Internet Explorer 6.0 and I need to do some more work to fix it but the success with IE 7.0 made me happy and I have hopes in a web development future where we can finally say, write one surf everywhere
Besides using strict standards I recommend you using ‘em’ instead of ‘px’ in your stylesheet as much as you can to get best results.
Well, that’s all for now, I hope you learn something from my experience. I certainly did with this project and get some fun with it!
I’ve been talking about how to make a web application in my last two posts but still no single html code has been written so far. What an hoax you may think. Well don’t panic, in this article you will finally get some pixels on the screen.
As usually I’ll start remembering how to write a view in Django. We’ll use the models of our last article to keep things simple. So, let’s say you want a view that renders a list of your records, each one with a list of songs. Not too hard. You start writing your view function in a file called views.py inside your app directory:
from django.shortcuts import render_to_response
from myapp.models import Record
def all_my_records(request):
records = Record.objects.all()
return render_to_response('record_list.html', dict(records=records))
Now you need to write your template file, in this case we’ve called it record_list.html and it will live inside a templates directory in the root directory of your Django project:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><title>Record list</title></head>
<body>
<h1>My records:</h1>
{% for record in records %}
<h2>{{ record.title }} - {{ record.artist }}</h2>
<ul>
{% for song in record.song_set.all() %}
<li>{{ song.title }} - {{ song.length }} seconds</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>
Now we need to do a couple of administrative tasks to make this work. First, we need to tell Django that our templates directory is that one. We said so in the settings.py file of our project:
TEMPLATE_DIRS = ('/home/lgs/DJProject/templates',)
Next, we should write a urls.py file inside the project and another one inside the application. Let’s start with the one inside the application directory:
from django.conf.urls.defaults import patterns
urlpatterns = patterns('',
(r'^my_records/$', 'DJProject.DJApp.views.all_my_records'),
)
As you can see we associate a url (/my_records/) with a view function. Now we just need to include this urls from the master urls.py file in the project directory:
from django.conf.urls.defaults import patterns, include
urlpatterns = patterns('',
(r'^/', include('DJProject.DJApp.urls')
)
And finally a line in the settings.py file:
ROOT_URLCONF = 'DJProject.urls'
Even that Django does not have a configuration language (ZCML) it needs quite a few of configuration settings. This is good indeed since it makes it a lot more powerful and flexible. Note that the settings.py changes and the urls.py of the project directory needs to be changed only onced. If we were to write another view for the DJApp only the urls.py of this directory would need to be changed.
Another good point for Django is its template system. It’s quite readable and pretty powerful. But as any other template system, simple examples like this one are no real issue for it. We will push it harder later on and we will see how good it actually is.
You can test your view by starting the web server and pointing your browser to http://127.0.0.1:8000/my_records/
You will probably get an empty list since there is no records in your database yet
Time to move to Zope3. Let’s try to write the same view but first let’s do an assumption. Our record objects will be stored in the root of the ZODB tree. This will make things simple enough to concentrate in the view issue.
As a convention we will put our HTTP view code in a subpackage called ‘browser’. So let’s create that directory inside our project package and put this code in its __init__.py file:
from myapp.interfaces import IRecord
class AllMyRecordsView(object):
def records(self):
for obj in self.context.values():
if IRecord.providedBy(obj):
yield obj
This time it’s not a function but a class. Not much harder than the Django version, huh? It just have a method that returns an iterator of the records in the context object. Here the context will be the root container. We need the extra if just in case there is something else in the root container.
Now time for the template, introducing the ZPT (Zope Page Template) language:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Strict//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><title>Record list</title></head>
<body>
<h1>My records:</h1>
<div tal:repeat="record view/records" tal:omit-tag="">
<h2 tal:content="python:'%s - %s' % (record.title, record.artist)">Sticky Fingers - Rolling Stones</h2>
<ul>
<li tal:repeat="song record/values"
tal:content="python:'%s - %d seconds' % (song.title, song.length)">Wild Horses - 220 seconds</li>
</ul>
</div>
</body>
</html>
I will tell you that the final html is the same in both versions. I think the Django one is easier to read but we have an advantage with the Zope one is that it is a valid xhtml file that can be edited and previsualized in any HTML editor so you can split your work between programmers and web designers. As you have seen in the previous example we even provided some default values (they will be replaced with the real ones in the runtime version) so we can get a grasp of how it will look like at the end.
Supposed we save the previous template in a file called record_list.pt. We will need to plug the pieces with a bit of configuration code. Put this code in a configure.zcml file inside your browser package:
<configure xmlns="http://namespaces.zope.org/browser">
<page
name="my_records"
for="zope.app.folder.interfaces.IFolder"
class=".AllMyRecordsView"
template="record_list.pt"
permission="zope.Public" />
</configure>
Now include this browser package from your main configure.zcml file in the root of your app:
<include package=".browser"/>
And you are ready to go. Start the server and go to http://127.0.0.1:8080/my_records
But having no way to add content to our web application is not really fun. We could add it from a pure Python app but we should expect something else from this great frameworks. See you in part 4 of these series where we’ll discover the black magick mistery of automatic web forms for adding and editing content!
Yesterday I wrote an (introduction) article about how to start developing webapps with Zope coming with a Django background. We got to the point where a development environment for both frameworks was ready to start coding. It’s time to get our hands dirty.
As any modern web framework, both Django and Zope are based on a more or less formal variant of the MVC pattern. Usually in this pattern you start writing your models. You do this in Django creating a models.py file inside your app and writing classes that inherit from django.db.models.Model. For example if you are modeling a multimedia library application you will probably have the following models:
from django.db.import models
class Record(models.Model):
artist = models.CharField('Artist', maxlength=80)
title = models.CharField('Title', maxlength=150)
length = models.PositiveIntField('Length', blank=True)
date = models.DateField('Date', blank=True)
class Song(models.Model):
title = models.CharField('Title', maxlength=150)
length = models.PositiveIntField('Length', blank=True)
record = models.ForeignKey(Record)
class Movie(models.Model):
title = models.CharField('Title', maxlength=150)
director = models.CharField('Director', maxlength=80, blank=True
date = models.DateField('Date', blank=True)
length = models.PositiveIntField('Length', blank=True)
Ok, that’s enough to have a semi-realistic example. Let’s see how to do the same with your Zope3 app. As pretty much everything else in Zope3 a model is a component, made up by an interface and an implementation. The interface says how the model looks like and what kind of things you allow others on your data. The implementation is an example of something that support your interface. You write your interfaces in a interfaces.py file and the implementation in another file or files. Note that the name of the file ‘interfaces.py’ is just a convention. The ‘models.py’ file in you Django app need to be called that way. Not a big deal though. Let’s write our interfaces.py file:
import zope.app.container.constraints
import zope.app.container.interfaces
import zope.schema
class IRecord(zope.app.container.interfaces.IContainer):
artist = zope.schema.TextLine(title=u'Artist', description=u'Author of the record')
title = zope.schema.TextLine(title=u'Title', description=u'Title')
length = zope.schema.Int(title=u'Length', description=u'Length in seconds', min=0, required=False)
date = zope.schema.Date(title=u'Date', description=u'Release date', required=False)
contains('.ISong')
def length_in_minutes():
"""Return a pretty string with the length in the mm:ss format"""
class ISong(zope.app.container.interfaces.IContained):
title = zope.schema.TextLine(title=u'Title', description=u'Title')
length = zope.schema.Int(title=u'Length', description=u'Length in seconds', min=0, required=False)
containers(IRecord)
def length_in_minutes():
"""Return a pretty string with the length in the mm:ss format"""
class IMovie(zope.app.container.interfaces.IContained):
title = zope.schema.TextLine(title=u'Title', description=u'Title')
director = zope.schema.TextLine(title=u'Director', description=u'Director', required=False)
date = zope.schema.TextLine(title=u'Date', description=u'Release date', required=False)
length = zope.schema.Int(title=u'Length', description=u'Length in minutes', min=0, required=False)
Now, time for some comments. Even that our Django models.py and our Zope3 interfaces.py have a lot of things in common as they are defining our models they have some important differences:
We still need to implement our interfaces in Zope3 so we’ll write a file called multimedia.py where we can put the implementations. Usually I use several files, one implementation in each one but we’ll put everything in the same file for the sake of simplicity:
import persistent
import zope.interfacefrom zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
import myapp.interfaces
class Record(BTreeContainer):
zope.interface.implements(myapp.interfaces.IRecord)
def __init__(self, artist, title, length=None, date=None):
super(Record, self).__init__()
self.artist, self.title, self.length, self.date = artist, title, length, date
class Song(Contained, persistent.Persistent):
zope.interface.implements(myapp.interfaces.ISong)
def __init__(self, title, length=None):
super(Song, self).__init__()
self.title, self.length = title, length
class Movie(Contained, persistent.Persistent):
zope.interface.implements(myapp.interfaces.IMovie)
def __init__(self, title, director=None, date=None, length=None):
super(Song, self).__init__()
self.title, self.director, self.date, self.length = title, director, date, length
Ok, that’s it. Not as scary as you thought thanks to the existence of a lot of useful base classes we can use. We use BTreeContainer and Contained to make our models aware of the relationship between Records and Songs. We also use Persistent to allow our objects to persist in the ZODB. We could use SQLAlchemy, Storm or even Django ORM here if we wanted to persist our objects in a Relational Database.
Now we must hook the models into the main app. We do this in Django with a two steps process:
In Zope3 we need to register the interfaces. In the next article, we’ll also write some security declarations to our implementations. The database is not affected until we create objects. So we open the configure.zcml file in the root of our application source directory and add this lines:
<interface interface=".interfaces.IRecord"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.ISong"
type="zope.app.content.interfaces.IContentType" />
<interface interface=".interfaces.IMovie"
type="zope.app.content.interfaces.IContentType" />
What we are doing is fill some records for the Zope3 component registry, which is a small database of components that is kept globaly in memory but can be overriden with values in the ZODB database. Remember, it’s all about the component architecture: that’s what gives you real power. But every great power comes with great responsability and here the responsability is to correctly fill the configure.zcml file. I hear you little boy crying and winning about that ugly XML file. As we will see later, having a registry of components and the relationships between them allows you to do pretty amazing things.
So I’d say this is the first really important (and probably biggest) difference between Django and Zope3: the component architecture. Love it or hate it, that’s one thing that Django lacks. Before you complaint about this let met tell you a little history: I onced had to explain Django to a PHP experienced developer. As many PHP developers he used to write the <?php > tag scattered in his html files. No templates, no models, no views. Controller? what’s that?. I tried to explain him that using the MVC pattern was a good thing that will help him to design and maintain his applications. He was not sure that this extra learn effort was worthy. I ask the Django developer the same question: is the Component Architecture twist of mind worthy for you?
The answer will depend on your project. As a rule of thumb all I can say is: the more complex your project is the more value you will get from the CA. Every time I hear Zope3 is too hard, it has a very sloppy learning curve, etc, etc. I can’t say that’s not true. That is indeed true for every software project with a component architecture: think about Mozilla XUL, Microsoft COM or OpenOffice.org UNO. Zope3 looks easy next to them
See you in the next article, where we will see how to render pixel on the screen: the exciting part!
I’m been working on a couple of Django projects for two months and I must say it’s a great platform for making web applications. Even when this is not the first time I play with Django. I tend to forget things too easily…
Having said that I must say I still prefer another Python web framework. One that’s pretty big but at the same time it’s small. One that it’s new but at the same time quite old. Yes, I’m talking about Zope. Zope3 precisely.
But I still think there is no perfect framework and each one has its strengths and its weaknesses. So, as always, you should study your requirements and your problem and decide which tool is best for you. As an overly simplified statement I’d say take Django for small 3-5 months projects that won’t change in the future and take Zope3 for everything else. Don’t use the same hammer for every nail.
Now, one of the things Django gets much better than Zope3 is documentation. It’s much easier to get started with Django not only because it doesn’t have a component architecture but because its tutorial and the rest of documentation really rocks.
I’ll try to write a series of posts to give the Zope3 newbie a little bit of knowledge so he/she doesn’t get lost in the Zope3 forrest. And more important, I’ll write them from a Djangoer perspective so you can always compare both frameworks.
Note of caution: in the zope3 world there are a lot of ways to do the same things. You may like this or not but that’s the way it is. In these articles I’ll just choose one of such ways. Probably not the best and hopefully not the worst. Just don’t think it is the canonical zope3 development process. I don’t think there is such one.
In this first article we’ll talk about installation. In Django you just need to have Python installed in your box and a database management system like sqlite, Mysql or Postgresql. That’s pretty much everything you need. Then you write this command:
django-admin.py startproject DJProject
and it creates an environment where you can start writing your code. Actually your are supposed to create applications inside of your project. I don’t like this wording since for me, a project is an application most of the time. Usually I split my project in components following best practices but they are not applications by their own. I know, Django lets you live without creating apps but things get out of the main road and you start living by your own. So let’s be good citizen and create our application:
cd DJProject
./manage.py startapp DJApp
Finally if you want to start your development server all you have to do is type this command:
./manage.py runserver
Ok, let’s jump into the zope3 side. In the old days Zope was a big fat framework and your app was just a module that you hooked into it. Now recently Zope3 has been splitted into lots of small components distributed as Python eggs. This approach has the advantage that you only use what you need keeping it simpler. The disadvantage is that usually you don’t know what you need
At least at the beginning. With some practice everything get easier.
Zope3 doesn’t currently work with Python 2.5 due to some changes in the C extensions mechanism that the 2.5 version introduced. There is quite a lot of C code in Zope3 to optimize things: some examples are the ZODB and the security system. That’s why you should stick to 2.4 until the 2.5 support is finished.
To work with Python eggs you need Easy Install which is the tool than manages them. Installing it is super simple:
wget http://peak.telecommunity.com/dist/ez_setup.py
python ez_setup.py
This will download everything needed to get started. Next step is install zopeproject. It is a great tool to start a Zope3 project. Its philosophy is that Zope3 is just a library and your app should use that library, not the other way around. It creates a WSGI application with a lot of default configuration made for you. Check out this great article to learn why WSGI is such a great thing. Ok, let’s get back to our project:
easy_install zopeproject
zopeproject Z3Project
The last command will ask you for a directory to place the zope3 eggs. This way other applications can use those eggs without duplicating them. It will also ask you for an administrator username and password. Something similar to what you get when you syncdb your Django project after adding the django.contrib.auth application.
Now you have a Z3Project directory pretty similar to your myproject Django directory. Running an http development server is quite simple:
cd Z3Project
bin/paste serve deploy.ini
But wait, I’ve got a bunch of files I don’t understand!, what are they for? And where is my code supposed to live? Well, don’t panic. I’ll try to answer these questions. The environment we just created is managed by buildout. This software handle the dependencies for your project and create/update the starting points for running, testing and debugging it among other things. These are the files you get:
And your code is supposed to live in the z3project subdirectory inside the Z3Project that zopeproject created. You will see a configure.zcml and an application.py files inside that directory, that is a Python package actually. The configure.zcml is included from the site.zcml we already saw and it loads a bunch of sane defaults for Zope3. It’s also the place where you should register your own components. The application.py defines a WSGI application factory that Paste will use to run the http server.
We’ll see how to write our models in the next article. See you then!
I guess I’m too used to the Python programming language that whenever I have to write simple Javascript functions I keep making the same mistakes. I tend to write as much functionality as possible in the server side (Python
) but in order to create rich user interfaces I have to use the client side (Javascript
) and things like this happens:
function addTopic(topic) {
var input_attrs = {
type: "checkbox",
checked: "checked",
name: "topics",
value: topic,
};
var li = Builder.node('li', [Builder.node('label', [Builder.node('input', input_attrs), topic.title])]);
$("topic_list").appendChild(li);
}
For those of you who are curious, yes I’m using Script.aculo.us and Prototype libraries here but here is the point: There is a syntax error in the previous code, one that Firefox (and thus Firebug) won’t catch but one that Internet Explorer will. The funny thing is the Internet Explorer error message:
Error: Expected identifier, string or number
So, anybody can answer me: is the trailing comma a real Javascript syntax error or just another Internet Explorer flaw?
Yesterday I spent quite a lot of time debugging an annoying CSS issue. I wanted to make an horizontal navigation bar so I started using this markup:
<ul>
<li><a href="/section1/">Section 1</a></li>
<li><a href="/section2/">Section 2</a></li>
<li><a href="/section3/">Section 3</a></li>
<li><a href="/section4/">Section 4</a></li>
</ul>
In order to make the list horizontal there are two CSS techniques you can use: 1) float everything or 2) use display: inline. I decided to use option 2) since it is less intrusive and I didn’t want to change the rest of the page layout.
So this is how the CSS looks like:
ul { display: inline; }
li { display: inline; border: 1px solid black; }
My problem was that there was some unwanted space between the list elements that made them difficult to stylish properly. After trying everything on earth to fix it I google for a while until I found the offending thing.
As you make the elements inline whitespace matters and the new line characters in the markup are the ones making the little margin between the list elements. You can fix this problem putting all the elements in a single line but that’s not very readable and it’s quite ugly. So this is how I fix it:
<ul>
<li><a href="/section1/">Section 1</a></li
><li><a href="/section2/">Section 2</a></li
><li><a href="/section3/">Section 3</a></li
><li><a href="/section4/">Section 4</a></li>
</ul>
Today I got a pretty creative email from the Zope3-Users mailing list. It seems they are hiring super heroes. I can’t expect less from a company with such a name….
The Maemo SDK runs on top of Scratchbox, which has some problems with Fedora kernels and memory managers. If you see an error like this when trying to log in in your scratchbox environment:
Inconsistency detected by ld.so: rtld.c: 1192: dl_main: Assertion `(void *) ph->p_vaddr == _rtld_local._dl_sysinfo_dso' failed!
Then you can try this:
# su -
# echo 0 > /proc/sys/vm/vdso_enabled
Note that the maemo-sdk-install_3.1.sh script will fail if you don’t disable the vdso kernel feature as above.
El otro día hice inventario del cacharreo que tengo en casa y tengo que admitir que me sorprendí al darme cuenta de que tenía más máquinas conectadas en red de las que en principio creía:
Un breve comentario sobre cada una:
Y ahora algunas curiosidades sobre estas máquinas: