20140206 (Thursday, 06 February 2014)

Release for Roger

Yesterday evening I announced a release of Renamed “Lino Faggio” to “Lino Voga” for Roger, but I had to postpone this because there were quite a lot of last-minute surprises:

  • How to use git for stepping back?
  • lino_faggio.migrate wasn’t yet ready.
  • Yet another thing which indicates that set_detail_tab is not good: there was a problem with the vat_id field of a company. Last week I had moved this field to Partner, during migration I noticed that this was a wrong decision, the field must remain in contacts.Company. It took me quite some time to find where this field was being added to the contacts.PartnerDetail. Now I use a sales.PartnerDetailMixin. This also solves the problem of ordering of the PartnerDetail tabs in Renamed “Lino Faggio” to “Lino Voga”: sales should come before ledger although logically (technically) it is the opposite.
  • Enter, Ctrl-Enter and Ctrl-S
  • ComboBox and the TAB key

How to use git for stepping back?

On a production site I have a repositories directory whith 5 git clones: atelier, djangosite, north, lino and lino_faggio. I wrote a script called “pull” which does a git pull all those 5 directories. But what if I don’t like the new version and want to step back? How should I organize my upgrade so that I can step back to the revision that were master at a certain timestamp?


$ git log
commit 3e501c9d6bd4150681abce6ad0f7f323a05113dd
Author: Luc Saffre <luc.saffre@gmail.com>
Date:   Tue Dec 10 21:06:44 2013 +0200


commit a300827274f59f5eb07964e741319889f87dd6e6
Author: Luc Saffre <luc.saffre@gmail.com>
Date:   Tue Dec 10 08:35:28 2013 +0200



commit bc0697447443c50987503c5820f82f8aca77dfae
Author: Luc Saffre <luc.saffre@gmail.com>
Date:   Tue Dec 10 21:06:56 2013 +0200


commit 823431e30e3ea477bebad473eaabdc36a23e0af0
Author: Luc Saffre <luc.saffre@gmail.com>
Date:   Tue Dec 10 14:41:20 2013 +0200



commit 3767b78ca3edc94ef7b1572c17dc44fffb316cfa
Author: Luc Saffre <luc.saffre@gmail.com>
Date:   Thu Dec 5 19:09:25 2013 +0200


Enter, Ctrl-Enter and Ctrl-S

I start to understand more about our Ctrl-Enter/Enter problem. Having ENTER bound to save() is indeed necessary within FormPanels used for insert actions. So I decided to take Joe’s approach (instead of mine) as a quick workaround before solving the problem more deeply (I’d like to add a “Insert and add new record” button to the InsertFormPanels, and also make the set of available insert submit buttons configurable per model or actor)

But with Joe’s solution I get a JavaScript error:

TypeError: e.target is undefined

After inspecting ext-all-debug.js I found that my version of ExtJS passes two arguments (key, event) to the handler function, so only the following variant of Joe’s solution works for me:

fn: function(k, e) {
    if(e.target.type === 'textarea' && !e.ctrlKey) {
        return true;

I verified that both ExtJS 3.3.1, and ExtJS 3.4 officially do it like this:

it will call the function with this signature (…): (String key, Ext.EventObject e)


ComboBox and the TAB key

Cool! I managed to fix another old problem for keyboard users:

When entering data into a FormPanel using the keyboard, then I recommend to use the TAB key for skipping to the next input field. But Lino until now had a strange behaviour: in order to select a value from a ComboBox using the keyboard you must first hit RET. Pressing TAB will restore the previous value! As if you had typed ESC!

This is related to a strange piece of code in Ext.form.ComboBox.initEvents:

this.keyNav = new Ext.KeyNav(this.el, {
    "enter" : function(e){ this.onViewClick(); },
    "esc" : function(e){ this.collapse(); },
    "tab" : function(e){
        if (this.forceSelection === true) {
        } else {
        return true;

ComboBox.forceSelection is “true to restrict the selected value to one of the values in the list, false to allow the user to set arbitrary text into the field” (quote from the docs). And this is an important feature. Lino manages this option automatically: it is usually set to false, except for Learning Comboboxes.

I don’t understand why the ExtJS authors decided to give to forceSelection option this surprising side-effect.

But then I discovered that the test is done here for the exact true value (using the triple equal sign ===), while at the place where it really matters, they just test for the boolean equivalent:

if(!rec && this.forceSelection){

I don’t know whether this was intended, but it suits me very well: I can now set forceSelection to some non-empty text and voilà: forceSelection is enabled, but :kbd`TAB` now selects the value before jumping to the next field.


  • Bei Auswahl Kursleiter steht jetzt nicht mehr die Kursleiterart in Klammern hinter dem Namen.
  • Kursserie einfügen: auch Felder Thema und Kursleiter
  • Neue Partner (Kursleiter, Schüler, Organisationen, sonstige) haben jetzt par défaut “Belgien” als Land (genauer gesagt das Land des Site-Besitzers (system.SiteConfig.site_company)


Continued on docs/tickets/93. How to issue a presence attestation (Anwesenheitsbescheinigung)?

Das ist eine der heftigsten Änderungen in der Benutzerschnittstelle, die ich vorhabe.

Bisher sagt man am Empfang “Bitte eine Bescheinigung (F6)” und TIM fragt darauf “Welche?”. In Lino würde ich das gerne andersrum machen: Um z.B. eine Bescheinung auszustellen, dass jemand anwesend war, gibt man zunächst die Anwesenheit ein (bzw. findet sie am Bildschirm) und klickt dort auf “Bescheinigung”. Manuelle Eingabe der Uhrzeit ist dann (im Normalfall) nicht mehr nötig, weil Lino die ja kennt.

Code changes:

  • lino.modlib.cal now supports overriding of the cal.Guest model.
  • lino_welfare.modlib.cal now uses this to make Guest an Attestable.
  • lino.modlib.attestations.AttestationType has a new field content_type.


  • Continue on the issue_attestation action: how to configure or customize whether the document is to be printed directly without displaying a detail window.

Release for Roger

Getting a pull request

Joe converted the ExtJS HTML generating to a system based on templates and sent a pull request. Wow! That was a great work!

I have local changes because I worked today, but this time there are no conflicts. Here is my git status:

# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#   modified:   docs/blog/2014/0206.rst
#   modified:   docs/warnings_html.txt
#   modified:   lino/locale/de/LC_MESSAGES/django.mo
#   modified:   lino/locale/de/LC_MESSAGES/django.po
#   modified:   lino/locale/django.pot
#   modified:   lino/locale/et/LC_MESSAGES/django.po
#   modified:   lino/locale/fr/LC_MESSAGES/django.po
#   modified:   lino/locale/nl/LC_MESSAGES/django.po
#   modified:   lino/locale/pt-br/LC_MESSAGES/django.po
#   modified:   lino/modlib/attestations/models.py
#   modified:   lino/modlib/cal/models_guest.py
#   modified:   lino/modlib/contacts/models.py
#   modified:   lino/modlib/extensible/__init__.py
#   modified:   lino/modlib/reception/models.py
#   modified:   lino/project_info.py
#   modified:   lino/projects/docs/settings.py
#   modified:   lino/utils/xmlgen/sepa/validate.py
no changes added to commit (use "git add" and/or "git commit -a")

Before committing my local changes I merge the pull request using the GitHub web interface.

Now I commit my local changes. Oops, this gives:

$ git commit -a -m http://docs.lino-framework.org/blog/2014/0206.html
[master ac2d661] http://docs.lino-framework.org/blog/2014/0206.html
 17 files changed, 1768 insertions(+), 1377 deletions(-)
$ git push
To git@github.com:lsaffre/lino.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:lsaffre/lino.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first merge the remote changes (e.g.,
hint: 'git pull') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Yes, in fact git is right. I must first pull Joe’s changes:

$ git pull
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (18/18), done.
remote: Total 20 (delta 7), reused 0 (delta 0)
Unpacking objects: 100% (20/20), done.
From github.com:lsaffre/lino
   ec134a1..4fc982c  master     -> origin/master
Merge made by the 'recursive' strategy.
 lino/core/web.py                          |   7 ++-
 lino/modlib/extjs/config/extjs/index.html | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lino/modlib/extjs/ext_renderer.py         | 327 +++++++++++++++---------------------------------------------------------------------------------
 3 files changed, 301 insertions(+), 278 deletions(-)
 create mode 100644 lino/modlib/extjs/config/extjs/index.html

Now I can do:

$ git push
Counting objects: 92, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (44/44), done.
Writing objects: 100% (49/49), 42.57 KiB | 0 bytes/s, done.
Total 49 (delta 32), reused 0 (delta 0)
To git@github.com:lsaffre/lino.git
   4fc982c..ae16aad  master -> master

And that was it! At least the Lino test suite passes.

But when I runserver for Renamed “Lino Faggio” to “Lino Voga” and point my browser to it, then I get an UndefinedError “‘lng’ is undefined” in /lino/modlib/extensible/__init__.py in get_js_includes, line 88. It seems that this was a typo, I just needed to replace lng by languages in the new file /lino/modlib/extensible/config/extjs/index.html.

Committed this one, too. And now I continue to work for the promised release.

Release for Roger

Basically the upgrade consisted of the following actions:

# cd /var/www/vhosts/eiche
# ./backup.sh
# ./pull.sh
# cd lino
# . env/bin/activate
# python manage.py run /var/backups/lino/eiche/20140206_184532/restore.py