diff --git a/modules/websearch/bin/webcoll.in b/modules/websearch/bin/webcoll.in
index 650b5b717..2b7e23a0d 100644
--- a/modules/websearch/bin/webcoll.in
+++ b/modules/websearch/bin/webcoll.in
@@ -1,1094 +1,1094 @@
 #!@PYTHON@
 ## -*- mode: python; coding: utf-8; -*-
 
 ## $Id$
 
 ## This file is part of CDS Invenio.
 ## Copyright (C) 2002, 2003, 2004, 2005, 2006 CERN.
 ##
 ## CDS Invenio is free software; you can redistribute it and/or
 ## modify it under the terms of the GNU General Public License as
 ## published by the Free Software Foundation; either version 2 of the
 ## License, or (at your option) any later version.
 ##
 ## CDS Invenio is distributed in the hope that it will be useful, but
 ## WITHOUT ANY WARRANTY; without even the implied warranty of
 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 ## General Public License for more details.
 ##
 ## You should have received a copy of the GNU General Public License
 ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
 """Creates CDS Invenio collection specific pages, using WML and MySQL configuration tables."""
 
 __version__ = "$Id$"
 
 ## import modules:
 try:
     import calendar
     import copy
     import getopt
     import getpass
     import marshal
     import signal
     import sys
     import cgi
     import sre
     import os
     import math
     import string
     import urllib
     import zlib
     import MySQLdb
     import Numeric
     import time
     import traceback
 except ImportError, e:
     print "Error: %s" % e
     import sys
     sys.exit(1)
 
 try:
     from invenio.config import *
     from invenio.messages import gettext_set_language, language_list_long
     from invenio.search_engine import HitSet, search_pattern, get_creation_date, get_field_i18nname
     from invenio.search_engine_config import cfg_author_et_al_threshold, cfg_instant_browse, cfg_max_recID, cfg_narrow_search_show_grandsons
     from invenio.dbquery import run_sql
     from invenio.access_control_engine import acc_authorize_action
     from invenio.bibrank_record_sorter import get_bibrank_methods
     import invenio.template
     websearch_templates = invenio.template.load('websearch')
 except ImportError, e:
     print "Error: %s" % e
     import sys
     sys.exit(1)
 
 ## global vars
 collection_house = {} # will hold collections we treat in this run of the program; a dict of {collname2, collobject1}, ...
 options = {} # will hold task options
 
 # cfg_cache_last_updated_timestamp_tolerance -- cache timestamp
 # tolerance (in seconds), to account for the fact that an admin might
 # accidentally happen to edit the collection definitions at exactly
 # the same second when some webcoll process was about to be started.
 # In order to be safe, let's put an exaggerated timestamp tolerance
 # value such as 20 seconds:
 cfg_cache_last_updated_timestamp_tolerance = 20
 
 # cfg_cache_last_updated_timestamp_file -- location of the cache
 # timestamp file:
 cfg_cache_last_updated_timestamp_file = "%s/collections/last_updated" % cachedir
 
 def get_collection(colname):
     """Return collection object from the collection house for given colname.
        If does not exist, then create it."""
     if not collection_house.has_key(colname):
         colobject = Collection(colname)
         collection_house[colname] = colobject
     return collection_house[colname]
 
 ## auxiliary functions:
 def mymkdir(newdir, mode=0777):
     """works the way a good mkdir should :)
         - already exists, silently complete
         - regular file in the way, raise an exception
         - parent directory(ies) does not exist, make them as well
     """
     if os.path.isdir(newdir):
         pass
     elif os.path.isfile(newdir):
         raise OSError("a file with the same name as the desired " \
                       "dir, '%s', already exists." % newdir)
     else:
         head, tail = os.path.split(newdir)
         if head and not os.path.isdir(head):
             mymkdir(head, mode)
         if tail:
 	    os.umask(022)
             os.mkdir(newdir, mode)
 
 def escape_string(s):
     "Escapes special chars in string.  For MySQL queries."
     s = MySQLdb.escape_string(s)
     return s
 
 def is_selected(var, fld):
     "Checks if the two are equal, and if yes, returns ' selected'.  Useful for select boxes."
     if var == fld:
         return " selected"
     else:
         return ""
 
 def write_message(msg, stream=sys.stdout):
     """Write message and flush output stream (may be sys.stdout or sys.stderr).  Useful for debugging stuff."""
     if stream == sys.stdout or stream == sys.stderr:
         stream.write(time.strftime("%Y-%m-%d %H:%M:%S --> ", time.localtime()))
         stream.write("%s\n" % msg)
         stream.flush()
     else:
         sys.stderr.write("Unknown stream %s.  [must be sys.stdout or sys.stderr]\n" % stream)
     return
 
 def get_field(recID, tag):
     "Gets list of field 'tag' for the record with 'recID' system number."
 
     out = []
     digit = tag[0:2]
 
     bx = "bib%sx" % digit
     bibx = "bibrec_bib%sx" % digit
     query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag='%s'" \
             % (bx, bibx, recID, tag)
     res = run_sql(query)
     for row in res:
         out.append(row[0])
     return out
 
 def print_record(recID, format='hb', ln=cdslang):
     "Prints record 'recID' formatted accoding to 'format'."
 
     out = ""
     # HTML brief format by default
     query = "SELECT value FROM bibfmt WHERE id_bibrec='%s' AND format='%s'" % (recID, format)
     res = run_sql(query, None, 1)
     if res:
         # record 'recID' is formatted in 'format', so print it
         out += "%s" % zlib.decompress(res[0][0])
     else:
         # record 'recID' does not exist in format 'format', so print some default format:
         # firstly, title:
         titles = get_field(recID, "245__a")
         # secondly, authors:
         authors = get_field(recID, "100__a") + get_field(recID, "700__a")
         # thirdly, date of creation:
         dates = get_field(recID, "260__c")
         # thirdly bis, report numbers:
         rns = get_field(recID, "037__a") + get_field(recID, "088__a")
         # fourthly, beginning of abstract:
         abstracts = get_field(recID, "520__a")
         # fifthly, fulltext link:
         urls_z = get_field(recID, "8564_z")
         urls_u = get_field(recID, "8564_u")
         out += websearch_templates.tmpl_record_body(
                      weburl = weburl,
                      titles = titles,
                      authors = authors,
                      dates = dates,
                      rns = rns,
                      abstracts = abstracts,
                      urls_u = urls_u,
                      urls_z = urls_z,
                      ln=ln)
 
     # at the end of HTML mode, print "Detailed record" and "Mark record" functions:
     out += websearch_templates.tmpl_record_links(
                                     weburl = weburl,
                                     recid = recID,
                                     ln = ln
                                    )
     return out
 
 class Collection:
     "Holds the information on collections (id,name,dbquery)."
 
     def __init__(self, name=""):
         "Creates collection instance by querying the MySQL configuration database about 'name'."
         self.calculate_reclist_run_already = 0 # to speed things up wihtout much refactoring
         self.update_reclist_run_already = 0 # to speed things up wihtout much refactoring
         self.reclist_with_nonpublic_subcolls = HitSet()
         if not name:
             self.name = cdsname # by default we are working on the home page
             self.id = 1
             self.dbquery = None
             self.nbrecs = None
             self.reclist = HitSet()
         else:
             self.name = name
             query = "SELECT id,name,dbquery,nbrecs,reclist FROM collection WHERE name='%s'" % escape_string(name)
             try:
                 res = run_sql(query, None, 1)
                 if res:
                     self.id = res[0][0]
                     self.name = res[0][1]
                     self.dbquery = res[0][2]
                     self.nbrecs = res[0][3]
                     try:
                         self.reclist = HitSet(Numeric.loads(zlib.decompress(res[0][5])))
                     except:
                         self.reclist = HitSet()
                 else: # collection does not exist!
                     self.id = None
                     self.dbquery = None
                     self.nbrecs = None
                     self.reclist = HitSet()
             except MySQLdb.Error, e:
                 print "Error %d: %s" % (e.args[0], e.args[1])
                 sys.exit(1)
 
     def get_name(self, ln=cdslang, name_type="ln", prolog="", epilog="", prolog_suffix=" ", epilog_suffix=""):
         """Return nicely formatted collection name for language LN.
         The NAME_TYPE may be 'ln' (=long name), 'sn' (=short name), etc."""
         out = prolog
         i18name = ""
         res = run_sql("SELECT value FROM collectionname WHERE id_collection=%s AND ln=%s AND type=%s", (self.id, ln, name_type))
         try:
             i18name += res[0][0]
         except IndexError:
             pass
         if i18name:
             out += i18name
         else:
             out += self.name
         out += epilog
         return out
 
     def get_ancestors(self):
         "Returns list of ancestors of the current collection."
         ancestors = []
         id_son = self.id
         while 1:
             query = "SELECT cc.id_dad,c.name FROM collection_collection AS cc, collection AS c "\
                     "WHERE cc.id_son=%d AND c.id=cc.id_dad" % int(id_son)
             res = run_sql(query, None, 1)
             if res:
                 col_ancestor = get_collection(res[0][1])
                 ancestors.append(col_ancestor)
                 id_son = res[0][0]
             else:
                 break
         ancestors.reverse()
         return ancestors
 
     def restricted_p(self):
         """Predicate to test if the collection is restricted or not.  Return the contect of the
          `restrited' column of the collection table (typically Apache group).  Otherwise return
          None if the collection is public."""
         out = None
         query = "SELECT restricted FROM collection WHERE id=%d" % self.id
         res = run_sql(query, None, 1)
         try:
             out = res[0][0]
         except:
             pass
         return out
 
     def get_sons(self, type='r'):
         "Returns list of direct sons of type 'type' for the current collection."
         sons = []
         id_dad = self.id
         query = "SELECT cc.id_son,c.name FROM collection_collection AS cc, collection AS c "\
                 "WHERE cc.id_dad=%d AND cc.type='%s' AND c.id=cc.id_son ORDER BY score DESC, c.name ASC" % (int(id_dad), type)
         res = run_sql(query)
         for row in res:
             sons.append(get_collection(row[1]))
         return sons
 
     def get_descendants(self, type='r'):
         "Returns list of all descendants of type 'type' for the current collection."
         descendants = []
         id_dad = self.id
         query = "SELECT cc.id_son,c.name FROM collection_collection AS cc, collection AS c "\
                 "WHERE cc.id_dad=%d AND cc.type='%s' AND c.id=cc.id_son ORDER BY score DESC" % (int(id_dad), type)
         res = run_sql(query)
         for row in res:
             col_desc = get_collection(row[1])
             descendants.append(col_desc)
             descendants += col_desc.get_descendants()
         return descendants
 
     def write_cache_file(self, filename='', filebody=''):
         "Write a file inside collection cache."
         # open file:
         dirname = "%s/collections/%d" % (cachedir, self.id)
         mymkdir(dirname)
         fullfilename = dirname + "/%s.html" % filename
         try:
 	    os.umask(022)
             f = open(fullfilename, "w")
         except IOError, v:
             try:
                 (code, message) = v
             except:
                 code = 0
                 message = v
             print "I/O Error: " + str(message) + " (" + str(code) + ")"
             sys.exit(1)
         # print user info:
         if options["verbose"] >= 6:
             write_message("... creating %s" % fullfilename)
         sys.stdout.flush()
         # print page body:
         f.write(filebody)
         # close file:
         f.close()
 
     def update_webpage_cache(self):
         """Create collection page header, navtrail, body (including left and right stripes) and footer, and
            call write_cache_file() afterwards to update the collection webpage cache."""
         ## do this for each language:
         for lang, lang_fullname in language_list_long():
 
             # load the right message language
             _ = gettext_set_language(lang)
 
             ## first, update navtrail:
             for as in range(0,2):
                 self.write_cache_file("navtrail-as=%s-ln=%s" % (as, lang), self.create_navtrail_links(as, lang))
             ## second, update page body:
             for as in range(0,2): # do both simple search and advanced search pages:
                 body = websearch_templates.tmpl_webcoll_body(
-                         weburl = weburl,
+                         ln=lang,
                          te_portalbox = self.create_portalbox(lang, 'te'),
                          searchfor = self.create_searchfor(as, lang),
                          np_portalbox = self.create_portalbox(lang, 'np'),
                          narrowsearch = self.create_narrowsearch(as, lang, 'r'),
                          focuson = self.create_narrowsearch(as, lang, "v"),
                          instantbrowse = self.create_instant_browse(as=as, ln=lang),
                          ne_portalbox = self.create_portalbox(lang, 'ne')
                        )
                 self.write_cache_file("body-as=%s-ln=%s" % (as, lang), body)
             ## third, write portalboxes:
             self.write_cache_file("portalbox-tp-ln=%s" % lang, self.create_portalbox(lang, "tp"))
             self.write_cache_file("portalbox-te-ln=%s" % lang, self.create_portalbox(lang, "te"))
             self.write_cache_file("portalbox-lt-ln=%s" % lang, self.create_portalbox(lang, "lt"))
             self.write_cache_file("portalbox-rt-ln=%s" % lang, self.create_portalbox(lang, "rt"))
             ## fourth, write 'last updated' information:
             self.write_cache_file("last-updated-ln=%s" % lang, time.strftime("%d %b %Y %H:%M:%S %Z", time.localtime()))
         return
 
     def create_navtrail_links(self, as=0, ln=cdslang):
         """Creates navigation trail links, i.e. links to collection
         ancestors (except Home collection).  If as==1, then links to
         Advanced Search interfaces; otherwise Simple Search.
         """
         
         dads = []
         for dad in self.get_ancestors():
             if dad.name != cdsname: # exclude Home collection
                 dads.append((dad.name, dad.get_name(ln)))
 
         return websearch_templates.tmpl_navtrail_links(
             as=as, ln=ln, dads=dads)
 
 
     def create_portalbox(self, lang=cdslang, position="rt"):
         """Creates portalboxes of language CDSLANG of the position POSITION by consulting MySQL configuration database.
            The position may be: 'lt'='left top', 'rt'='right top', etc."""
         out = ""
         query = "SELECT p.title,p.body FROM portalbox AS p, collection_portalbox AS cp "\
                 " WHERE cp.id_collection=%d AND p.id=cp.id_portalbox AND cp.ln='%s' AND cp.position='%s' "\
                 " ORDER BY cp.score DESC" % (self.id, lang, position)
         res = run_sql(query)
         for row in res:
             title, body = row[0], row[1]
             if title:
                 out += websearch_templates.tmpl_portalbox(title = title,
                                              body = body)
             else:
                 # no title specified, so print body ``as is'' only:
                 out += body
         return out
 
     def create_narrowsearch(self, as=0, ln=cdslang, type="r"):
         """Creates list of collection descendants of type 'type' under title 'title'.
         If as==1, then links to Advanced Search interfaces; otherwise Simple Search.
         Suitable for 'Narrow search' and 'Focus on' boxes."""
 
         # get list of sons and analyse it
         sons = self.get_sons(type)
 
         if not sons:
             return ''
         
         # get descendents
         descendants = self.get_descendants(type)
 
         grandsons = []
         if cfg_narrow_search_show_grandsons:
             # load grandsons for each son
             for son in sons:
                 grandsons.append(son.get_sons())
 
         # return ""
         return websearch_templates.tmpl_narrowsearch(
                  as = as,
                  ln = ln,
                  type = type,
                  father = self,
                  has_grandchildren = len(descendants)>len(sons),
                  sons = sons,
                  display_grandsons = cfg_narrow_search_show_grandsons,
                  grandsons = grandsons
                )
 
     def create_instant_browse(self, rg=cfg_instant_browse, as=0, ln=cdslang):
         "Searches database and produces list of last 'rg' records."
         
         if self.restricted_p():
             return websearch_templates.tmpl_box_restricted_content(ln = ln)
         
         else:
             if self.nbrecs and self.reclist:
                 # firstly, get last 'rg' records:
                 recIDs = Numeric.nonzero(self.reclist._set)
                 passIDs = []
 
                 total = len(recIDs)
                 to_display = min(rg, total)
                 
                 for idx in range(total-1, total-to_display-1, -1):
                     recid = recIDs[idx]
 
                     passIDs.append({'id': recid,
                                     'body': print_record(recid, ln=ln),
                                     'date': get_creation_date(recid, fmt="%Y-%m-%d<br>%H:%i")})
                     
                 if self.nbrecs > rg:
                     url = websearch_templates.build_search_url(
                         cc=self.name, jrec=rg+1, ln=ln, as=as)
                 else:
                     url = ""
                     
                 return websearch_templates.tmpl_instant_browse(
                                  as=as, ln=ln, recids=passIDs, more_link=url)
 
         return websearch_templates.tmpl_box_no_records(ln=ln)
 
     def create_searchoptions(self):
         "Produces 'Search options' portal box."
         box=""
         query = """SELECT DISTINCT(cff.id_field),f.code,f.name FROM collection_field_fieldvalue AS cff, field AS f
                    WHERE cff.id_collection=%d AND cff.id_fieldvalue IS NOT NULL AND cff.id_field=f.id
                    ORDER BY cff.score DESC""" % self.id
         res = run_sql(query)
         if res:
             for row in res:
                 field_id = row[0]
                 field_code = row[1]
                 field_name = row[2]
                 query_bis = """SELECT fv.value,fv.name FROM fieldvalue AS fv, collection_field_fieldvalue AS cff
                                WHERE cff.id_collection=%d AND cff.type='seo' AND cff.id_field=%d AND fv.id=cff.id_fieldvalue
                                ORDER BY cff.score_fieldvalue DESC, cff.score DESC, fv.name ASC""" % (self.id, field_id)
                 res_bis = run_sql(query_bis)
                 if res_bis:
                     values = [{'value' : '', 'text' : 'any' + field_name}] # @todo internationalisation of "any"
                     for row_bis in res_bis:
                         values.append({'value' : cgi.escape(row_bis[0], 1), 'text' : row_bis[1]})
 
                     box += websearch_templates.tmpl_select(
                                  fieldname = field_code,
                                  values = values
                                 )
         return box
 
     def create_sortoptions(self, ln=cdslang):
         "Produces 'Sort options' portal box."
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         box=""
         query = """SELECT f.code,f.name FROM field AS f, collection_field_fieldvalue AS cff
                    WHERE id_collection=%d AND cff.type='soo' AND cff.id_field=f.id
                    ORDER BY cff.score DESC, f.name ASC""" % self.id
         values = [{'value' : '', 'text': "- %s -" % _("latest first")}]
         res = run_sql(query)
         if res:
             for row in res:
                 values.append({'value' : row[0], 'text': row[1]})
         else:
             for tmp in ('title', 'author', 'report number', 'year'):
                 values.append({'value' : tmp.replace(' ', ''), 'text' : get_field_i18nname(tmp, ln)})
 
         box = websearch_templates.tmpl_select(
                    fieldname = 'sf',
                    css_class = 'address',
                    values = values
                   )
         box += websearch_templates.tmpl_select(
                     fieldname = 'so',
                     css_class = 'address',
                     values = [
                               {'value' : 'a' , 'text' : _("asc.")},
                               {'value' : 'd' , 'text' : _("desc.")}
                              ]
                    )
         return box
 
     def create_rankoptions(self, ln=cdslang):
         "Produces 'Rank options' portal box."
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         values = [{'value' : '', 'text': "- %s %s -" % (string.lower(_("OR")), _("rank by"))}]
         for (code,name) in get_bibrank_methods(self.id, ln):
             values.append({'value' : code, 'text': name})
         box = websearch_templates.tmpl_select(
                    fieldname = 'sf',
                    css_class = 'address',
                    values = values
                   )
         return box
 
     def create_displayoptions(self, ln=cdslang):
         "Produces 'Display options' portal box."
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         values = []
         for i in ['10', '25', '50', '100', '250', '500']:
             values.append({'value' : i, 'text' : i + ' ' + _("results")})
 
         box = websearch_templates.tmpl_select(
                    fieldname = 'rg',
                    css_class = 'address',
                    values = values
                   )
 
         if self.get_sons():
             box += websearch_templates.tmpl_select(
                         fieldname = 'sc',
                         css_class = 'address',
                         values = [
                                   {'value' : '1' , 'text' : _("split by collection")},
                                   {'value' : '0' , 'text' : _("single list")}
                                  ]
                        )
         return box
 
     def create_formatoptions(self, ln=cdslang):
         "Produces 'Output format options' portal box."
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         box = ""
         values = []
         query = """SELECT f.code,f.name FROM format AS f, collection_format AS cf
                    WHERE cf.id_collection=%d AND cf.id_format=f.id ORDER BY cf.score DESC, f.name ASC"""  % self.id
         res = run_sql(query)
         if res:
             for row in res:
                 values.append({'value' : row[0], 'text': row[1]})
         else:
             values.append({'value' : 'hb', 'text' : "HTML %s" % _("brief")})
         box = websearch_templates.tmpl_select(
                    fieldname = 'of',
                    css_class = 'address',
                    values = values
                   )
         return box
 
     def create_searchwithin_selection_box(self, fieldname='f', value='', ln='en'):
         "Produces 'search within' selection box for the current collection."
 
         # get values
         query = """SELECT f.code,f.name FROM field AS f, collection_field_fieldvalue AS cff
                    WHERE cff.type='sew' AND cff.id_collection=%d AND cff.id_field=f.id
                    ORDER BY cff.score DESC, f.name ASC"""  % self.id
         res = run_sql(query)
         values = [{'value' : '', 'text' : get_field_i18nname("any field", ln)}]
         if res:
             for row in res:
                 values.append({'value' : row[0], 'text' : row[1]})
         else:
             if cfg_cern_site:
                 for tmp in ['title', 'author', 'abstract', 'report number', 'year']:
                     values.append({'value' : tmp.replace(' ', ''), 'text' : get_field_i18nname(tmp, ln)})
             else:
                 for tmp in ['title', 'author', 'abstract', 'keyword', 'report number', 'year', 'fulltext', 'reference']:
                     values.append({'value' : tmp.replace(' ', ''), 'text' : get_field_i18nname(tmp, ln)})
 
         return websearch_templates.tmpl_searchwithin_select(
                                                 fieldname = fieldname,
                                                 ln = ln,
                                                 selected = value,
                                                 values = values
                                               )
     def create_searchexample(self):
         "Produces search example(s) for the current collection."
         out = "$collSearchExamples = getSearchExample(%d, $se);" % self.id
         return out
 
     def create_searchfor(self, as=0, ln=cdslang):
         "Produces either Simple or Advanced 'Search for' box for the current collection."
         if as == 1:
             return self.create_searchfor_advanced(ln)
         else:
             return self.create_searchfor_simple(ln)
 
     def create_searchfor_simple(self, ln=cdslang):
         "Produces simple 'Search for' box for the current collection."
 
         return websearch_templates.tmpl_searchfor_simple(
           ln=ln,
           collection_id = self.name,
           collection_name=self.get_name(ln=ln),
           record_count=self.nbrecs,
           middle_option = self.create_searchwithin_selection_box(ln=ln),
         )
 
     def create_searchfor_advanced(self, ln=cdslang):
         "Produces advanced 'Search for' box for the current collection."
 
         return websearch_templates.tmpl_searchfor_advanced(
           ln = ln,
           collection_id = self.name,
           collection_name=self.get_name(ln=ln),
           record_count=self.nbrecs,
 
           middle_option_1 = self.create_searchwithin_selection_box('f1', ln=ln),
           middle_option_2 = self.create_searchwithin_selection_box('f2', ln=ln),
           middle_option_3 = self.create_searchwithin_selection_box('f3', ln=ln),
 
           searchoptions = self.create_searchoptions(),
           sortoptions = self.create_sortoptions(ln),
           rankoptions = self.create_rankoptions(ln),
           displayoptions = self.create_displayoptions(ln),
           formatoptions = self.create_formatoptions(ln)
         )
 
     def calculate_reclist(self):
         """Calculate, set and return the (reclist, reclist_with_nonpublic_subcolls) tuple for given collection."""
         if self.calculate_reclist_run_already:
             # do we have to recalculate?
             return (self.reclist, self.reclist_with_nonpublic_subcolls)
         if options["verbose"] >= 6:
             write_message("... calculating reclist of %s" % self.name)
         reclist = HitSet() # will hold results for public sons only; good for storing into DB
         reclist_with_nonpublic_subcolls = HitSet() # will hold results for both public and nonpublic sons; good for deducing total
                                                    # number of documents
         if not self.dbquery:
             # A - collection does not have dbquery, so query recursively all its sons
             #     that are either non-restricted or that have the same restriction rules
             for coll in self.get_sons():
                 coll_reclist, coll_reclist_with_nonpublic_subcolls = coll.calculate_reclist()
                 if ((coll.restricted_p() is None) or
                     (coll.restricted_p() == self.restricted_p())):
                     # add this reclist ``for real'' only if it is public
                     reclist.union(coll_reclist)
                 reclist_with_nonpublic_subcolls.union(coll_reclist_with_nonpublic_subcolls)
         else:
             # B - collection does have dbquery, so compute it:
             reclist = search_pattern(None,self.dbquery)
             reclist_with_nonpublic_subcolls = copy.deepcopy(reclist)
         # deduce the number of records:
         reclist.calculate_nbhits()
         reclist_with_nonpublic_subcolls.calculate_nbhits()
         # store the results:
         self.nbrecs = reclist_with_nonpublic_subcolls._nbhits
         self.reclist = reclist
         self.reclist_with_nonpublic_subcolls = reclist_with_nonpublic_subcolls
         # last but not least, update the speed-up flag:
         self.calculate_reclist_run_already = 1
         # return the two sets:
         return (self.reclist, self.reclist_with_nonpublic_subcolls)
 
     def update_reclist(self):
         "Update the record universe for given collection; nbrecs, reclist of the collection table."
         if self.update_reclist_run_already:
             # do we have to reupdate?
             return 0
         if options["verbose"] >= 6:
             write_message("... updating reclist of %s (%s recs)" % (self.name, self.nbrecs))
         sys.stdout.flush()
         try:
             query = "UPDATE collection SET nbrecs=%d, reclist='%s' WHERE id=%d" % \
                     (self.nbrecs, escape_string(zlib.compress(Numeric.dumps(self.reclist._set))), self.id)
             res = run_sql(query)
             self.reclist_updated_since_start = 1
         except MySQLdb.Error, e:
             print "Database Query Error %d: %s." % (e.args[0], e.args[1])
             sys.exit(1)
         # last but not least, update the speed-up flag:
         self.update_reclist_run_already = 1
         return 0
 
 def get_datetime(var, format_string="%Y-%m-%d %H:%M:%S"):
     """Returns a date string according to the format string.
        It can handle normal date strings and shifts with respect
        to now."""
     date = time.time()
     shift_re=sre.compile("([-\+]{0,1})([\d]+)([dhms])")
     factors = {"d":24*3600, "h":3600, "m":60, "s":1}
     m = shift_re.match(var)
     if m:
         sign = m.groups()[0] == "-" and -1 or 1
         factor = factors[m.groups()[2]]
         value = float(m.groups()[1])
         date = time.localtime(date + sign * factor * value)
         date = time.strftime(format_string, date)
     else:
         date = time.strptime(var, format_string)
         date = time.strftime(format_string, date)
     return date
 
 def get_current_time_timestamp():
     """Return timestamp corresponding to the current time."""
     return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
     
 def compare_timestamps_with_tolerance(timestamp1,
                                       timestamp2,
                                       tolerance=0):
     """Compare two timestamps TIMESTAMP1 and TIMESTAMP2, of the form
        '2005-03-31 17:37:26'. Optionally receives a TOLERANCE argument
        (in seconds).  Return -1 if TIMESTAMP1 is less than TIMESTAMP2
        minus TOLERANCE, 0 if they are equal within TOLERANCE limit,
        and 1 if TIMESTAMP1 is greater than TIMESTAMP2 plus TOLERANCE.
     """
     # remove any trailing .00 in timestamps:
     timestamp1 = sre.sub(r'\.[0-9]+$', '', timestamp1)
     timestamp2 = sre.sub(r'\.[0-9]+$', '', timestamp2)
     # first convert timestamps to Unix epoch seconds:
     timestamp1_seconds = calendar.timegm(time.strptime(timestamp1, "%Y-%m-%d %H:%M:%S"))
     timestamp2_seconds = calendar.timegm(time.strptime(timestamp2, "%Y-%m-%d %H:%M:%S"))
     # now compare them:
     if timestamp1_seconds < timestamp2_seconds - tolerance:
         return -1
     elif timestamp1_seconds > timestamp2_seconds + tolerance:
         return 1
     else:
         return 0
 
 def get_database_last_updated_timestamp():
     """Return last updated timestamp for collection-related and
        record-related database tables.
     """
     database_tables_timestamps = []
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'bibrec'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'bibfmt'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'idxWORD%%'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'collection%%'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'portalbox'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'field%%'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'format%%'")))
     database_tables_timestamps.extend(map(lambda x: str(x[11]), run_sql("SHOW TABLE STATUS LIKE 'rnkMETHODNAME'")))
     return max(database_tables_timestamps)
 
 def get_cache_last_updated_timestamp():
     """Return last updated cache timestamp."""
     try:
         f = open(cfg_cache_last_updated_timestamp_file, "r")
     except:
         return "1970-01-01 00:00:00"
     timestamp = f.read()
     f.close()
     return timestamp
 
 def set_cache_last_updated_timestamp(timestamp):
     """Set last updated cache timestamp to TIMESTAMP."""
     try:
         f = open(cfg_cache_last_updated_timestamp_file, "w")
     except:
         pass
     f.write(timestamp)
     f.close()
     return timestamp
 
 def write_message(msg, stream=sys.stdout):
     """Prints message and flush output stream (may be sys.stdout or sys.stderr)."""
     if stream == sys.stdout or stream == sys.stderr:
         stream.write(time.strftime("%Y-%m-%d %H:%M:%S --> ", time.localtime()))
         stream.write("%s\n" % msg)
         stream.flush()
     else:
         sys.stderr.write("Unknown stream %s.  [must be sys.stdout or sys.stderr]\n" % stream)
 
 def task_sig_sleep(sig, frame):
     """Signal handler for the 'sleep' signal sent by BibSched."""
     if options["verbose"] >= 9:
         write_message("got signal %d" % sig)
     write_message("sleeping...")
     task_update_status("SLEEPING")
     signal.pause() # wait for wake-up signal
 
 def task_sig_wakeup(sig, frame):
     """Signal handler for the 'wakeup' signal sent by BibSched."""
     if options["verbose"] >= 9:
         write_message("got signal %d" % sig)
     write_message("continuing...")
     task_update_status("CONTINUING")
 
 def task_sig_stop(sig, frame):
     """Signal handler for the 'stop' signal sent by BibSched."""
     if options["verbose"] >= 9:
         write_message("got signal %d" % sig)
     write_message("stopping...")
     task_update_status("STOPPING")
     pass # FIXME: is there anything to be done?
     task_update_status("STOPPED")
     sys.exit(0)
 
 def task_sig_suicide(sig, frame):
     """Signal handler for the 'suicide' signal sent by BibSched."""
     if options["verbose"] >= 9:
         write_message("got signal %d" % sig)
     write_message("suiciding myself now...")
     task_update_status("SUICIDING")
     write_message("suicided")
     task_update_status("SUICIDED")
     sys.exit(0)
 
 def task_sig_unknown(sig, frame):
     """Signal handler for the other unknown signals sent by shell or user."""
     write_message("unknown signal %d ignored" % sig) # do nothing for other signals
 
 def authenticate(user, header="WebColl Task Submission", action="runwebcoll"):
     """Authenticate the user against the user database.
        Check for its password, if it exists.
        Check for action access rights.
        Return user name upon authorization success,
        do system exit upon authorization failure.
        """
     print header
     print "=" * len(header)
     if user == "":
         print >> sys.stdout, "\rUsername: ",
         user = string.strip(string.lower(sys.stdin.readline()))
     else:
         print >> sys.stdout, "\rUsername: ", user
     ## first check user pw:
     res = run_sql("select id,password from user where email=%s", (user,), 1)
     if not res:
         print "Sorry, %s does not exist." % user
         sys.exit(1)
     else:
         (uid_db, password_db) = res[0]
         if password_db:
             password_entered = getpass.getpass()
             if password_db == password_entered:
                 pass
             else:
                 print "Sorry, wrong credentials for %s." % user
                 sys.exit(1)
         ## secondly check authorization for the action:
         (auth_code, auth_message) = acc_authorize_action(uid_db, action)
         if auth_code != 0:
             print auth_message
             sys.exit(1)
     return user
 
 def task_submit(options):
     """Submits task to the BibSched task queue.  This is what people will be invoking via command line."""
     ## sanity check: remove eventual "task" option:
     if options.has_key("task"):
         del options["task"]
     ## authenticate user:
     user = authenticate(options.get("user", ""))
     ## submit task:
     if options["verbose"] >= 9:
         print ""
         write_message("storing task options %s\n" % options)
     task_id = run_sql("""INSERT INTO schTASK (id,proc,user,runtime,sleeptime,status,arguments)
                          VALUES (NULL,'webcoll',%s,%s,%s,'WAITING',%s)""",
                       (user, options["runtime"], options["sleeptime"], marshal.dumps(options)))
     ## update task number:
     options["task"] = task_id
     run_sql("""UPDATE schTASK SET arguments=%s WHERE id=%s""", (marshal.dumps(options),task_id))
     write_message("Task #%d submitted." % task_id)
     return task_id
 
 def task_update_progress(msg):
     """Updates progress information in the BibSched task table."""
     global task_id
     return run_sql("UPDATE schTASK SET progress=%s where id=%s", (msg, task_id))
 
 def task_update_status(val):
     """Updates status information in the BibSched task table."""
     global task_id
     return run_sql("UPDATE schTASK SET status=%s where id=%s", (val, task_id))
 
 def task_read_status(task_id):
     """Read status information in the BibSched task table."""
     res = run_sql("SELECT status FROM schTASK where id=%s", (task_id,), 1)
     try:
         out = res[0][0]
     except:
         out = 'UNKNOWN'
     return out
 
 def task_get_options(id):
     """Returns options for the task 'id' read from the BibSched task queue table."""
     out = {}
     res = run_sql("SELECT arguments FROM schTASK WHERE id=%s AND proc='webcoll'", (id,))
     try:
         out = marshal.loads(res[0][0])
     except:
         write_message("Error: WebColl task %d does not seem to exist." % id)
         sys.exit(1)
     return out
 
 def task_run():
     """Run the WebColl task by fetching arguments from the BibSched task queue.
        This is what BibSched will be invoking via daemon call.
        The task will update collection reclist cache and collection web pages for
        given collection. (default is all).
        Arguments described in usage() function.
        Return 1 in case of success and 0 in case of failure."""
     global task_id, options
     task_run_start_timestamp = get_current_time_timestamp()
     options = task_get_options(task_id) # get options from BibSched task table
     ## check task id:
     if not options.has_key("task"):
         write_message("Error: The task #%d does not seem to be a WebColl task." % task_id)
         return 0
     ## check task status:
     task_status = task_read_status(task_id)
     if task_status != "WAITING":
         write_message("Error: The task #%d is %s.  I expected WAITING." % (task_id, task_status))
         return 0
     ## we can run the task now:
     if options["verbose"]:
         write_message("Task #%d started." % task_id)
     task_update_status("RUNNING")
     ## initialize signal handler:
     signal.signal(signal.SIGUSR1, task_sig_sleep)
     signal.signal(signal.SIGTERM, task_sig_stop)
     signal.signal(signal.SIGABRT, task_sig_suicide)
     signal.signal(signal.SIGCONT, task_sig_wakeup)
     signal.signal(signal.SIGINT, task_sig_unknown)
     colls = []
     # decide whether we need to run or not, by comparing last updated timestamps:
     if options["verbose"] >= 3:
         write_message("Database timestamp is %s." % get_database_last_updated_timestamp())
         write_message("Collection cache timestamp is %s." % get_cache_last_updated_timestamp())
     if options.has_key("force") or \
        compare_timestamps_with_tolerance(get_database_last_updated_timestamp(),
                                          get_cache_last_updated_timestamp(),
                                          cfg_cache_last_updated_timestamp_tolerance) >= 0:
         ## either forced update was requested or cache is not up to date, so recreate it:
         # firstly, decide which collections to do:
         if options.has_key("collection"):
             coll = get_collection(options["collection"])
             if coll.id == None:
                 usage(1, 'Collection %s does not exist' % coll.name)
             colls.append(coll)
         else:
             res = run_sql("SELECT name FROM collection ORDER BY id")
             for row in res:
                 colls.append(get_collection(row[0]))
         # secondly, update collection reclist cache:
         i = 0
         for coll in colls:
             i += 1
             if options["verbose"]:
                 write_message("%s / reclist cache update" % coll.name)
             coll.calculate_reclist()
             coll.update_reclist()
             task_update_progress("Part 1/2: done %d/%d" % (i,len(colls)))
         # thirdly, update collection webpage cache:
         i = 0
         for coll in colls:
             i += 1
             if options["verbose"]:
                 write_message("%s / web cache update" % coll.name)
             coll.update_webpage_cache()
             task_update_progress("Part 2/2: done %d/%d" % (i,len(colls)))
 
         # finally update the cache last updated timestamp:
         # (but only when all collections were updated, not when only
         # some of them were forced-updated as per admin's demand)
         if not options.has_key("collection"):
             set_cache_last_updated_timestamp(task_run_start_timestamp)
             if options["verbose"] >= 3:
                 write_message("Collection cache timestamp is set to %s." % get_cache_last_updated_timestamp())
     else:
         ## cache up to date, we don't have to run
         if options["verbose"]:
             write_message("Collection cache is up to date, no need to run.")        
         pass 
     ## we are done:
     task_update_progress("Done.")
     task_update_status("DONE")
     if options["verbose"]:
         write_message("Task #%d finished." % task_id)
     return 1
 
 def usage(exitcode=1, msg=""):
     """Prints usage info."""
     if msg:
         sys.stderr.write("Error: %s.\n" % msg)
     sys.stderr.write("Usage: %s [options]\n" % sys.argv[0])
     sys.stderr.write("Command options:\n")
     sys.stderr.write("  -c, --collection\t Update cache for the given collection only. [all]\n")
     sys.stderr.write("  -f, --force\t Force update even if cache is up to date. [no]\n")
     sys.stderr.write("Scheduling options:\n")
     sys.stderr.write("  -u, --user=USER \t User name to submit the task as, password needed.\n")
     sys.stderr.write("  -t, --runtime=TIME \t Time to execute the task (now), e.g.: +15s, 5m, 3h, 2002-10-27 13:57:26\n")
     sys.stderr.write("  -s, --sleeptime=SLEEP \t Sleeping frequency after which to repeat task (no), e.g.: 30m, 2h, 1d\n")
     sys.stderr.write("General options:\n")
     sys.stderr.write("  -h, --help      \t\t Print this help.\n")
     sys.stderr.write("  -V, --version   \t\t Print version information.\n")
     sys.stderr.write("  -v, --verbose=LEVEL   \t Verbose level (from 0 to 9, default 1).\n")
     sys.stderr.write("""Description: %s updates the collection cache
     (record universe for a given collection plus web page elements)
     based on WML and MySQL configuration parameters.
     If the collection name is passed as the second argument, it'll update
     this collection only.  If the collection name is immediately followed
     by a plus sign, it will also update all its desdendants.  The
     top-level collection name may be entered as the void string.\n""" % sys.argv[0])
     sys.exit(exitcode)
 
 def main():
     """Main function that analyzes command line input and calls whatever is appropriate.
        Useful for learning on how to write BibSched tasks."""
     global task_id
 
     ## parse command line:
     if len(sys.argv) == 2 and sys.argv[1].isdigit():
         ## A - run the task
         task_id = int(sys.argv[1])
         try:
             if not task_run():
                 write_message("Error occurred.  Exiting.", sys.stderr)
         except StandardError, e:
             write_message("Unexpected error occurred: %s." % e, sys.stderr)
             write_message("Traceback is:", sys.stderr)
             traceback.print_tb(sys.exc_info()[2])
             write_message("Exiting.", sys.stderr)
             task_update_status("ERROR")
     else:
         ## B - submit the task
         # set default values:
         options["runtime"] = time.strftime("%Y-%m-%d %H:%M:%S")
         options["verbose"] = 1
         options["sleeptime"] = ""
         # set user-defined options:
         try:
             opts, args = getopt.getopt(sys.argv[1:], "hVv:u:s:t:c:f",
                                        ["help", "version", "verbose=","user=","sleep=","time=","collection=","force"])
         except getopt.GetoptError, err:
             usage(1, err)
         try:
             for opt in opts:
                 if opt[0] in ["-h", "--help"]:
                     usage(0)
                 elif opt[0] in ["-V", "--version"]:
                     print __version__
                     sys.exit(0)
                 elif opt[0] in [ "-u", "--user"]:
                     options["user"] = opt[1]
                 elif opt[0] in ["-v", "--verbose"]:
                     options["verbose"] = int(opt[1])
                 elif opt[0] in [ "-s", "--sleeptime" ]:
                     get_datetime(opt[1]) # see if it is a valid shift
                     options["sleeptime"] = opt[1]
                 elif opt[0] in [ "-t", "--runtime" ]:
                     options["runtime"] = get_datetime(opt[1])
                 elif opt[0] in [ "-c", "--collection"]:
                     options["collection"] = opt[1]
                 elif opt[0] in [ "-f", "--force"]:
                     options["force"] = 1
                 else:
                     usage(1)
         except StandardError, e:
             usage(e)
         task_submit(options)
     return
 
 ### okay, here we go:
 if __name__ == '__main__':
     main()
diff --git a/modules/websearch/lib/websearch_regression_tests.py b/modules/websearch/lib/websearch_regression_tests.py
index b6992f1f8..b04f8c710 100644
--- a/modules/websearch/lib/websearch_regression_tests.py
+++ b/modules/websearch/lib/websearch_regression_tests.py
@@ -1,415 +1,416 @@
 ## $Id$
 ##
 ## This file is part of CDS Invenio.
 ## Copyright (C) 2002, 2003, 2004, 2005, 2006 CERN.
 ##
 ## CDS Invenio is free software; you can redistribute it and/or
 ## modify it under the terms of the GNU General Public License as
 ## published by the Free Software Foundation; either version 2 of the
 ## License, or (at your option) any later version.
 ##
 ## CDS Invenio is distributed in the hope that it will be useful, but
 ## WITHOUT ANY WARRANTY; without even the implied warranty of
 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 ## General Public License for more details.  
 ##
 ## You should have received a copy of the GNU General Public License
 ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
 import unittest
 import re
 import urlparse, cgi
 from sets import Set
 
 from mechanize import Browser, LinkNotFoundError
 
 from invenio.testutils import make_suite, make_url, warn_user_about_tests_and_run
 from invenio.config import weburl, cdsname, cdslang
 from invenio.urlutils import same_urls_p
 
 def parse_url(url):
     parts = urlparse.urlparse(url)
     query = cgi.parse_qs(parts[4], True)
 
     return parts[2].split('/')[1:], query
 
 class WebsearchTestLegacyURLs(unittest.TestCase):
 
     """ Check that the application still responds to legacy URLs for
     navigating, searching and browsing."""
 
     def test_legacy_collections(self):
         """ websearch - collections handle legacy urls """
 
         b = Browser()
 
         def check(legacy, new):
             b.open(legacy)
             got = b.geturl()
             
             self.failUnless(same_urls_p(got, new), got)
 
         # Use the root URL unless we need more
         check(make_url('/', c=cdsname),
               make_url('/'))
 
         # Other collections are redirected in the /collection area
         check(make_url('/', c='Poetry'),
               make_url('/collection/Poetry'))
 
         # Drop unnecessary arguments, like ln and as (when they are
         # the default value)
         check(make_url('/', c='Poetry', as=0, ln=cdslang),
               make_url('/collection/Poetry'))
 
         # Otherwise, keep them
         check(make_url('/', c='Poetry', as=1, ln=cdslang),
               make_url('/collection/Poetry', as=1))
 
         # Support the /index.py addressing too
         check(make_url('/index.py', c='Poetry'),
               make_url('/collection/Poetry'))
         
 
     def test_legacy_search(self):
         """ websearch - search queries handle legacy urls """
 
         b = Browser()
 
         def check(legacy, new):
             b.open(legacy)
             got = b.geturl()
             
             self.failUnless(same_urls_p(got, new), got)
 
         # /search.py is redirected on /search
         check(make_url('/search.py', p='nuclear', as=1),
               make_url('/search', p='nuclear', as=1))
 
         # direct recid searches are redirected to /record
         check(make_url('/search.py', recid=1, ln='es'),
               make_url('/record/1', ln='es'))
 
 
 
 class WebsearchTestRecord(unittest.TestCase):
     """ Check the interface of the /record results """
 
     def test_format_links(self):
         """ websearch - check format links for records """
 
         b = Browser()
 
         # We open the record in all known HTML formats
         for hformat in ('hd', 'hx', 'hm'):
             b.open(make_url('/record/1', of=hformat))
 
             # all except the selected links should be present in the
             # page.
             for oformat in ('hd', 'hx', 'hm', 'xm', 'xd'):
                 target = make_url('/record/1', of=oformat)
                 
                 if oformat == hformat:
                     try:
                         b.find_link(url=target)
                     except LinkNotFoundError:
                         continue
 
                     self.fail('link %r should not be in page' % target)
                 else:
                     try:
                         b.find_link(url=target)
                     except LinkNotFoundError:
                         self.fail('link %r should be in page' % target)
                     
         return
 
 
 class WebsearchTestCollections(unittest.TestCase):
     
     def test_traversal_links(self):
         """ websearch - traverse all the publications of a collection """
 
         # Ensure that it is possible to traverse a collection as
         # /collection/My_Collection?jrec=...
 
         b = Browser()
 
         try:
             for as in (0, 1):
                 b.open(make_url('/collection/Preprints', as=as))
 
                 for jrec in (11, 21, 11, 23):
                     args = {'jrec': jrec, 'cc': 'Preprints'}
                     if as:
                         args['as'] = as
 
                     url = make_url('/search', **args)
                     b.follow_link(url=url)
 
         except LinkNotFoundError:
             self.fail('no link %r in %r' % (url, b.geturl()))
             
     def test_collections_links(self):
         """ websearch - enter in collections and subcollections """
 
         b = Browser()
 
         def tryfollow(url):
             cur = b.geturl()
             body = b.response().read()
             try:
                 b.follow_link(url=url)
             except LinkNotFoundError:
                 print body
                 self.fail("in %r: could not find %r" % (
                     cur, url))
             return
 
         for as in (0, 1):
             if as: kargs = {'as': 1}
             else:  kargs = {}
                 
             # We navigate from immediate son to immediate son...
             b.open(make_url('/', **kargs))
             tryfollow(make_url('/collection/Articles%20%26%20Preprints', **kargs))
             tryfollow(make_url('/collection/Articles', **kargs))
 
             # But we can also jump to a grandson immediately
             b.back()
             b.back()
             tryfollow(make_url('/collection/ALEPH', **kargs))
 
         return
 
     def test_records_links(self):
         """ websearch - check the links toward records in leaf collections """
         
         b = Browser()
         b.open(make_url('/collection/Preprints'))
 
         def harvest():
 
             """ Parse all the links in the page, and check that for
             each link to a detailed record, we also have the
             corresponding link to the similar records."""
             
             records = Set()
             similar = Set()
             
             for link in b.links():
                 path, q = parse_url(link.url)
 
                 if not path:
                     continue
                 
                 if path[0] == 'record':
                     records.add(int(path[1]))
                     continue
 
                 if path[0] == 'search':
                     if not q.get('rm') == ['wrd']:
                         continue
 
                     recid = q['p'][0].split(':')[1]
                     similar.add(int(recid))
             
             self.failUnlessEqual(records, similar)
             
             return records
         
         # We must have 10 links to the corresponding /records
         found = harvest()
         self.failUnlessEqual(len(found), 10)
 
         # When clicking on the "Search" button, we must also have
         # these 10 links on the records.
         b.select_form(name="search")
         b.submit()
 
         found = harvest()
         self.failUnlessEqual(len(found), 10)
         return
 
 
 class WebsearchTestBrowse(unittest.TestCase):
 
     def test_browse_field(self):
         """ websearch - check that browsing works """
 
         b = Browser()
         b.open(make_url('/'))
 
         b.select_form(name='search')
         b['f'] = ['title']
         b.submit(name='action_browse')
 
         def collect():
             # We'll get a few links to search for the actual hits, plus a
             # link to the following results.
             res = []
             for link in b.links(url_regex=re.compile(weburl + r'/search\?')):
                 if link.text == 'Advanced Search':
                     continue
             
                 path, q = parse_url(link.url)
                 res.append((link, q))
 
             return res
         
         # if we follow the last link, we should get another
         # batch. There is an overlap of one item.
         batch_1 = collect()
 
         b.follow_link(link=batch_1[-1][0])
 
         batch_2 = collect()
 
         # FIXME: we cannot compare the whole query, as the collection
         # set is not equal
         self.failUnlessEqual(batch_1[-2][1]['p'], batch_2[0][1]['p'])
         
 
 class WebsearchTestSearch(unittest.TestCase):
 
     def test_hits_in_other_collection(self):
         """ websearch - check extension of a query to the home collection """
 
         b = Browser()
         
         # We do a precise search in an isolated collection
         b.open(make_url('/collection/ISOLDE', ln='en'))
         
         b.select_form(name='search')
         b['f'] = ['author']
         b['p'] = 'matsubara'
         b.submit()
 
         path, current_q = parse_url(b.geturl())
         
         link = b.find_link(text_regex=re.compile('.*hit', re.I))
         path, target_q = parse_url(link.url)
 
         # the target query should be the current query without any c
         # or cc specified.
         for f in ('cc', 'c', 'action_search', 'ln'):
             if f in current_q:
                 del current_q[f]
 
         self.failUnlessEqual(current_q, target_q)
 
     def test_nearest_terms(self):
         """ websearch - provide a list of nearest terms """
         
         b = Browser()
         b.open(make_url(''))
 
         # Search something weird
         b.select_form(name='search')
         b['p'] = 'gronf'
         b.submit()
 
         path, original = parse_url(b.geturl())
         
         for to_drop in ('cc', 'action_search', 'f'):
             if to_drop in original:
                 del original[to_drop]
         
         # we should get a few searches back, which are identical
         # except for the p field being substituted (and the cc field
         # being dropped).
         if 'cc' in original:
             del original['cc']
         
         for link in b.links(url_regex=re.compile(weburl + r'/search\?')):
             if link.text == 'Advanced Search':
                 continue
             
             path, target = parse_url(link.url)
 
             original['p'] = [link.text]
             self.failUnlessEqual(original, target)
 
         return
 
     def test_switch_to_simple_search(self):
         """ websearch - switch to simple search """
         
         b = Browser()
         b.open(make_url('/collection/ISOLDE', as=1))
 
         b.select_form(name='search')
         b['p1'] = 'tandem'
         b['f1'] = ['title']
         b.submit()
 
         b.follow_link(text='Simple Search')
 
         path, q = parse_url(b.geturl())
 
         self.failUnlessEqual(q, {'cc': ['ISOLDE'], 'p': ['tandem'], 'f': ['title']})
 
         
     def test_switch_to_advanced_search(self):
         """ websearch - switch to advanced search """
         
         b = Browser()
         b.open(make_url('/collection/ISOLDE'))
 
         b.select_form(name='search')
         b['p'] = 'tandem'
         b['f'] = ['title']
         b.submit()
 
         b.follow_link(text='Advanced Search')
 
         path, q = parse_url(b.geturl())
 
         self.failUnlessEqual(q, {'cc': ['ISOLDE'], 'p1': ['tandem'], 'f1': ['title'], 'as': ['1']})
         
     def test_no_boolean_hits(self):
         """ websearch - check the 'no boolean hits' proposed links """
         
         b = Browser()
         b.open(make_url(''))
 
         b.select_form(name='search')
         b['p'] = 'quasinormal muon'
         b.submit()
 
         path, q = parse_url(b.geturl())
 
         for to_drop in ('cc', 'action_search', 'f'):
-            del q[to_drop]
+            if to_drop in q:
+                del q[to_drop]
         
         for bsu in ('quasinormal', 'muon'):
             l = b.find_link(text=bsu)
             q['p'] = bsu
 
             if not same_urls_p(l.url, make_url('/search', **q)):
                 self.fail(repr((l.url, make_url('/search', **q))))
 
     def test_similar_authors(self):
         """ websearch - test similar authors box """
 
         b = Browser()
         b.open(make_url(''))
 
         b.select_form(name='search')
         b['p'] = 'Ellis, R K'
         b['f'] = ['author']
         b.submit()
 
         l = b.find_link(text="Ellis, R S")
         self.failUnless(same_urls_p(l.url, make_url('/search', p="Ellis, R S", f='author')))
     
 test_suite = make_suite(WebsearchTestSearch,
                         WebsearchTestBrowse,
                         WebsearchTestCollections,
                         WebsearchTestRecord,
                         WebsearchTestLegacyURLs)
 
 if __name__ == "__main__":
     warn_user_about_tests_and_run(test_suite)
     
diff --git a/modules/websearch/lib/websearch_templates.py b/modules/websearch/lib/websearch_templates.py
index e3cfc7f51..f9758ddfe 100644
--- a/modules/websearch/lib/websearch_templates.py
+++ b/modules/websearch/lib/websearch_templates.py
@@ -1,2492 +1,2465 @@
 # -*- coding: utf-8 -*-
 ## $Id$
 
 ## This file is part of CDS Invenio.
 ## Copyright (C) 2002, 2003, 2004, 2005, 2006 CERN.
 ##
 ## CDS Invenio is free software; you can redistribute it and/or
 ## modify it under the terms of the GNU General Public License as
 ## published by the Free Software Foundation; either version 2 of the
 ## License, or (at your option) any later version.
 ##
 ## CDS Invenio is distributed in the hope that it will be useful, but
 ## WITHOUT ANY WARRANTY; without even the implied warranty of
 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 ## General Public License for more details.  
 ##
 ## You should have received a copy of the GNU General Public License
 ## along with CDS Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
 import urllib
 import time
 import cgi
 import gettext
 import string
 import locale
 import sre
 from sets import ImmutableSet
 
 from invenio.config import *
 from invenio.dbquery import run_sql
 from invenio.messages import gettext_set_language
 from invenio.search_engine_config import *
 from invenio.urlutils import make_canonical_urlargd, drop_default_urlargd, a_href
 
 
 def get_fieldvalues(recID, tag):
     """Return list of field values for field TAG inside record RECID.
        FIXME: should be imported commonly for search_engine too."""
     out = []
     if tag == "001___":
         # we have asked for recID that is not stored in bibXXx tables
         out.append(str(recID))
     else:
         # we are going to look inside bibXXx tables
         digit = tag[0:2]
         bx = "bib%sx" % digit
         bibx = "bibrec_bib%sx" % digit
         query = "SELECT bx.value FROM %s AS bx, %s AS bibx WHERE bibx.id_bibrec='%s' AND bx.id=bibx.id_bibxxx AND bx.tag LIKE '%s'" \
                 "ORDER BY bibx.field_number, bx.tag ASC" % (bx, bibx, recID, tag)
         res = run_sql(query)
         for row in res:
             out.append(row[0])
     return out
 
 class Template:
 
     # This dictionary maps CDS Invenio language code to locale codes (ISO 639)
     tmpl_localemap = {
         'ca': 'ca_ES',
         'de': 'de_DE',
         'el': 'el_GR',
         'en': 'en_US',
         'es': 'es_ES',
         'pt': 'pt_BR',
         'fr': 'fr_FR',
         'it': 'it_IT',
         'ru': 'ru_RU',
         'sk': 'sk_SK',
         'cs': 'cs_CZ',
         'no': 'no_NO',
         'sv': 'sv_SE',
         'uk': 'uk_UA',
         'ja': 'ja_JA',
         'pl': 'pl_PL'
         }
     tmpl_default_locale = "en_US" # which locale to use by default, useful in case of failure
 
 
     # Type of the allowed parameters for the web interface for search results
     search_results_default_urlargd = {
         'cc': (str, cdsname), 'c': (list, []),
         'p': (str, ""), 'f': (str, ""),
         'rg': (int, 10),
         'sf': (str, ""),
         'so': (str, "d"),
         'sp': (str, ""),
         'rm': (str, ""),
         'of': (str, "hb"),
         'ot': (list, []),
         'as': (int, 0),
         'p1': (str, ""), 'f1': (str, ""), 'm1': (str, ""), 'op1':(str, ""),
         'p2': (str, ""), 'f2': (str, ""), 'm2': (str, ""), 'op2':(str, ""),
         'p3': (str, ""), 'f3': (str, ""), 'm3': (str, ""),
         'sc': (int, 0),
         'jrec': (int, 0),
         'recid': (int, -1), 'recidb': (int, -1), 'sysno': (str, ""),
         'id': (int, -1), 'idb': (int, -1), 'sysnb': (str, ""),
         'action': (str, "search"),
         'action_search': (str, ""),
         'action_browse': (str, ""),
         'd1y': (int, 0), 'd1m': (int, 0), 'd1d': (int, 0),
         'd2y': (int, 0), 'd2m': (int, 0), 'd2d': (int, 0),
         'ap': (int, 1),
         'verbose': (int, 0),
         }
 
     # ...and for search interfaces
     search_interface_default_urlargd = {
         'as': (int, 0),
         'verbose': (int, 0)}
 
     def build_search_url(self, known_parameters={}, **kargs):
         """ Helper for generating a canonical search
         url. 'known_parameters' is the list of query parameters you
         inherit from your current query. You can then pass keyword
         arguments to modify this query.
         
            build_search_url(known_parameters, of="xm")
 
         The generated URL is absolute.
         """
 
         parameters = {}
         parameters.update(known_parameters)
         parameters.update(kargs)
 
         # Now, we only have the arguments which have _not_ their default value
         parameters = drop_default_urlargd(parameters, self.search_results_default_urlargd)
 
         # Asking for a recid? Return a /record/<recid> URL
         if 'recid' in parameters:
             target = "%s/record/%d" % (weburl, parameters['recid'])
             del parameters['recid']
             target += make_canonical_urlargd(parameters, self.search_results_default_urlargd)
             return target
 
         return "%s/search%s" % (weburl, make_canonical_urlargd(parameters, self.search_results_default_urlargd))
 
     def build_search_interface_url(self, known_parameters={}, **kargs):
         """ Helper for generating a canonical search interface URL."""
 
         parameters = {}
         parameters.update(known_parameters)
         parameters.update(kargs)
 
         c = parameters['c']
         del parameters['c']
         
         # Now, we only have the arguments which have _not_ their default value
         if c and c != cdsname:
             base = weburl + '/collection/' + urllib.quote(c)
         else:
             base = weburl
         
         return base + make_canonical_urlargd(parameters, self.search_results_default_urlargd)
         
 
     def tmpl_navtrail_links(self, as, ln, dads):
         """
         Creates the navigation bar at top of each search page (*Home > Root collection > subcollection > ...*)
 
         Parameters:
 
           - 'as' *bool* - Should we display an advanced search box?
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'separator' *string* - The separator between two consecutive collections
 
           - 'dads' *list* - A list of parent links, eachone being a dictionary of ('name', 'longname')
         """
         out = []
         for url, name in dads:
             out.append(a_href(name, href=self.build_search_interface_url(c=url, as=as, ln=ln), _class='navtrail'))
             
         return ' &gt; '.join(out)
 
-    def tmpl_webcoll_body(self, weburl, te_portalbox, searchfor,
+    def tmpl_webcoll_body(self, ln, te_portalbox, searchfor,
                           np_portalbox, narrowsearch, focuson,
                           instantbrowse, ne_portalbox):
 
         """ Creates the body of the main search page.
 
         Parameters:
 
           - 'weburl' *string* - The base URL for the site
 
           - 'te_portalbox' *string* - The HTML code for the portalbox on top of search
 
           - 'searchfor' *string* - The HTML code for the search options
 
           - 'np_portalbox' *string* - The HTML code for the portalbox on bottom of search
 
           - 'searchfor' *string* - The HTML code for the search categories (left bottom of page)
 
           - 'focuson' *string* - The HTML code for the "focuson" categories (right bottom of page)
 
           - 'ne_portalbox' *string* - The HTML code for the bottom of the page
         """
 
         if not narrowsearch:
             narrowsearch = instantbrowse
             
         body = '''
                 <form name="search" action="%(weburl)s/search" method="get">
                 %(searchfor)s
                 %(np_portalbox)s
                 <table cellspacing="0" cellpadding="0" border="0">
                   <tr>
                     <td valign="top">%(narrowsearch)s</td>
                ''' % {
                  'weburl' : weburl,
                  'searchfor' : searchfor,
                  'np_portalbox' : np_portalbox,
                  'narrowsearch' : narrowsearch
                }
         if focuson:
             body += """<td valign="top">""" + focuson + """</td>"""
         body += """</tr></table>
             %(ne_portalbox)s
                </form>""" % {'ne_portalbox' : ne_portalbox}
         return body
 
     def tmpl_portalbox(self, title, body):
         """Creates portalboxes based on the parameters
         Parameters:
 
           - 'title' *string* - The title of the box
 
           - 'body' *string* - The HTML code for the body of the box
 
         """
         out = """<div class="portalbox">
                     <div class="portalboxheader">%(title)s</div>
                     <div class="portalboxbody">%(body)s</div>
                  </div>""" % {'title' : title, 'body' : body}
 
         return out
 
     def tmpl_searchfor_simple(self, ln, collection_id, collection_name, record_count, middle_option):
         """Produces simple *Search for* box for the current collection.
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'header' *string* - header of search form
 
           - 'middle_option' *string* - HTML code for the options (any field, specific fields ...)
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = '''
         <!--create_searchfor_simple()-->
         '''
 
         argd = drop_default_urlargd({'ln': ln, 'cc': collection_id},
                                     self.search_results_default_urlargd)
 
         # Only add non-default hidden values
         for field, value in argd.items():
             out += self.tmpl_input_hidden(field, value)
 
 
         header = _("Search %s records for:") % \
                  self.tmpl_nbrecs_info(record_count, "","")
 
         asearchurl = self.build_search_interface_url(c=collection_id, as=1, ln=ln)
         
         # print commentary start:
         out += '''
         <table class="searchbox">
          <thead>
           <tr align="left">
            <th colspan="3" class="searchboxheader">%(header)s</th>
           </tr>
          </thead>
          <tbody>
           <tr valign="baseline">
            <td class="searchboxbody" align="left"><input type="text" name="p" size="40" value=""></td>
            <td class="searchboxbody" align="left">%(middle_option)s</td>
            <td class="searchboxbody" align="left">
              <input class="formbutton" type="submit" name="action_search" value="%(msg_search)s">
              <input class="formbutton" type="submit" name="action_browse" value="%(msg_browse)s"></td>
           </tr>
           <tr valign="baseline">
            <td class="searchboxbody" colspan="3" align="right">
              <small>
                <a href="%(weburl)s/help/search/tips.%(ln)s.html">%(msg_search_tips)s</a> ::
                %(asearch)s
              </small>
            </td>
           </tr>
          </tbody>
         </table>
         <!--/create_searchfor_simple()-->
         ''' % {'ln' : ln,
                'weburl' : weburl,
                'asearch' : a_href(_('Advanced Search'), href=asearchurl),
                'header' : header,
                'middle_option' : middle_option,
                'msg_search' : _('Search'),
                'msg_browse' : _('Browse'),
                'msg_search_tips' : _('Search Tips')}
         
         return out
 
     def tmpl_searchfor_advanced(self,
                                 ln,                  # current language
                                 collection_id,
                                 collection_name,
                                 record_count,
                                 middle_option_1, middle_option_2, middle_option_3,
                                 searchoptions,
                                 sortoptions,
                                 rankoptions,
                                 displayoptions,
                                 formatoptions
                                 ):
         """
           Produces advanced *Search for* box for the current collection.
 
           Parameters:
 
             - 'ln' *string* - The language to display
 
             - 'weburl' *string* - The base URL for the site
 
             - 'ssearchurl' *string* - The URL to simple search form
 
             - 'header' *string* - header of search form
 
             - 'middle_option_1' *string* - HTML code for the first row of options (any field, specific fields ...)
 
             - 'middle_option_2' *string* - HTML code for the second row of options (any field, specific fields ...)
 
             - 'middle_option_3' *string* - HTML code for the third row of options (any field, specific fields ...)
 
             - 'searchoptions' *string* - HTML code for the search options
 
             - 'sortoptions' *string* - HTML code for the sort options
 
             - 'rankoptions' *string* - HTML code for the rank options
 
             - 'displayoptions' *string* - HTML code for the display options
 
             - 'formatoptions' *string* - HTML code for the format options
 
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = '''
         <!--create_searchfor_advanced()-->
         '''
         
         argd = drop_default_urlargd({'ln': ln, 'as': 1, 'cc': collection_id},
                                     self.search_results_default_urlargd)
 
         # Only add non-default hidden values
         for field, value in argd.items():
             out += self.tmpl_input_hidden(field, value)
 
 
         header = _("Search %s records for:") % \
                  self.tmpl_nbrecs_info(record_count, "","")
 
         ssearchurl = self.build_search_interface_url(c=collection_id, as=0, ln=ln)
 
         out += '''
         <table class="searchbox">
          <thead>
           <tr>
            <th class="searchboxheader" colspan="3">%(header)s</th>
           </tr>
          </thead>
          <tbody>
           <tr valign="bottom">
             <td class="searchboxbody" nowrap>%(matchbox_m1)s<input type="text" name="p1" size="40" value=""></td>
             <td class="searchboxbody">%(middle_option_1)s</td>
             <td class="searchboxbody">%(andornot_op1)s</td>
           </tr>
           <tr valign="bottom">
             <td class="searchboxbody" nowrap>%(matchbox_m2)s<input type="text" name="p2" size="40" value=""></td>
             <td class="searchboxbody">%(middle_option_2)s</td>
             <td class="searchboxbody">%(andornot_op2)s</td>
           </tr>
           <tr valign="bottom">
             <td class="searchboxbody" nowrap>%(matchbox_m3)s<input type="text" name="p3" size="40" value=""></td>
             <td class="searchboxbody">%(middle_option_3)s</td>
             <td class="searchboxbody" nowrap>
               <input class="formbutton" type="submit" name="action_search" value="%(msg_search)s">
               <input class="formbutton" type="submit" name="action_browse" value="%(msg_browse)s"></td>
           </tr>
           <tr valign="bottom">
             <td colspan="3" class="searchboxbody" align="right">
               <small>
                 <a href="%(weburl)s/help/search/tips.%(ln)s.html">%(msg_search_tips)s</a> ::
                 %(ssearch)s
               </small>
             </td>
           </tr>
          </tbody>
         </table>
         <!-- @todo - more imports -->
         ''' % {'ln' : ln,
                'weburl' : weburl,
                'ssearch' : a_href(_("Simple Search"), href=ssearchurl),
                'header' : header,
 
                'matchbox_m1' : self.tmpl_matchtype_box('m1', ln=ln),
                'middle_option_1' : middle_option_1,
                'andornot_op1' : self.tmpl_andornot_box('op1', ln=ln),
 
                'matchbox_m2' : self.tmpl_matchtype_box('m2', ln=ln),
                'middle_option_2' : middle_option_2,
                'andornot_op2' : self.tmpl_andornot_box('op2', ln=ln),
 
                'matchbox_m3' : self.tmpl_matchtype_box('m3', ln=ln),
                'middle_option_3' : middle_option_3,
 
                'msg_search' : _("Search"),
                'msg_browse' : _("Browse"),
                'msg_search_tips' : _("Search Tips")}
 
         if (searchoptions):
             out += """<table class="searchbox">
                       <thead>
                        <tr>
                          <th class="searchboxheader">
                            %(searchheader)s
                          </th>
                        </tr>
                       </thead>
                       <tbody>
                        <tr valign="bottom">
                         <td class="searchboxbody">%(searchoptions)s</td>
                        </tr>
                       <tbody>
                      </table>""" % {
                        'searchheader' : _("Search options:"),
                        'searchoptions' : searchoptions
                      }
 
         out += """<table class="searchbox">
                    <thead>
                     <tr>
                       <th class="searchboxheader">
                         %(added)s
                       </th>
                       <th class="searchboxheader">
                         %(until)s
                       </th>
                     </tr>
                    </thead>
                    <tbody>
                     <tr valign="bottom">
                       <td class="searchboxbody">%(date_added)s</td>
                       <td class="searchboxbody">%(date_until)s</td>
                     </tr>
                    </tbody>
                   </table>
                   <table class="searchbox">
                    <thead>
                     <tr>
                       <th class="searchboxheader">
                         %(msg_sort)s
                       </th>
                       <th class="searchboxheader">
                         %(msg_display)s
                       </th>
                       <th class="searchboxheader">
                         %(msg_format)s
                       </th>
                     </tr>
                    </thead>
                    <tbody>
                     <tr valign="bottom">
                       <td class="searchboxbody">%(sortoptions)s %(rankoptions)s</td>
                       <td class="searchboxbody">%(displayoptions)s</td>
                       <td class="searchboxbody">%(formatoptions)s</td>
                     </tr>
                    </tbody>
                   </table>
                   <!--/create_searchfor_advanced()-->
               """ % {
 
                     'added' : _("Added since:"),
                     'until' : _("until:"),
                     'date_added' : self.tmpl_inputdate("d1", ln=ln),
                     'date_until' : self.tmpl_inputdate("d2", ln=ln),
 
                     'msg_sort' : _("Sort by:"),
                     'msg_display' : _("Display results:"),
                     'msg_format' : _("Output format:"),
                     'sortoptions' : sortoptions,
                     'rankoptions' : rankoptions,
                     'displayoptions' : displayoptions,
                     'formatoptions' : formatoptions
                   }
         return out
 
     def tmpl_matchtype_box(self, name='m', value='', ln='en'):
         """Returns HTML code for the 'match type' selection box.
 
           Parameters:
 
             - 'name' *string* - The name of the produced select
 
             - 'value' *string* - The selected value (if any value is already selected)
 
             - 'ln' *string* - the language to display
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = """
         <select name="%(name)s">
         <option value="a"%(sela)s>%(opta)s
         <option value="o"%(selo)s>%(opto)s
         <option value="e"%(sele)s>%(opte)s
         <option value="p"%(selp)s>%(optp)s
         <option value="r"%(selr)s>%(optr)s
         </select>
         """ % {'name' : name,
                'sela' : self.tmpl_is_selected('a', value),
                                                            'opta' : _("All of the words:"),
                'selo' : self.tmpl_is_selected('o', value),
                                                            'opto' : _("Any of the words:"),
                'sele' : self.tmpl_is_selected('e', value),
                                                            'opte' : _("Exact phrase:"),
                'selp' : self.tmpl_is_selected('p', value),
                                                            'optp' : _("Partial phrase:"),
                'selr' : self.tmpl_is_selected('r', value),
                                                            'optr' : _("Regular expression:")
               }
         return out
 
     def tmpl_is_selected(self, var, fld):
         """
           Checks if *var* and *fld* are equal, and if yes, returns ' selected'.  Useful for select boxes.
 
           Parameters:
 
           - 'var' *string* - First value to compare
 
           - 'fld' *string* - Second value to compare
         """
         if var == fld:
             return " selected"
         else:
             return ""
 
     def tmpl_andornot_box(self, name='op', value='', ln='en'):
         """
           Returns HTML code for the AND/OR/NOT selection box.
 
           Parameters:
 
             - 'name' *string* - The name of the produced select
 
             - 'value' *string* - The selected value (if any value is already selected)
 
             - 'ln' *string* - the language to display
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = """
         <select name="%(name)s">
         <option value="a"%(sela)s>%(opta)s
         <option value="o"%(selo)s>%(opto)s
         <option value="n"%(seln)s>%(optn)s
         </select>
         """ % {'name' : name,
                'sela' : self.tmpl_is_selected('a', value), 'opta' : _("AND"),
                'selo' : self.tmpl_is_selected('o', value), 'opto' : _("OR"),
                'seln' : self.tmpl_is_selected('n', value), 'optn' : _("AND NOT")
               }
         return out
 
     def tmpl_inputdate(self, name, ln, sy = 0, sm = 0, sd = 0):
         """
           Produces *From Date*, *Until Date* kind of selection box. Suitable for search options.
 
           Parameters:
 
             - 'name' *string* - The base name of the produced selects
 
             - 'ln' *string* - the language to display
         """
         # load the right message language
         _ = gettext_set_language(ln)
 
         box = """
                <select name="%(name)sd">
                  <option value=""%(sel)s>%(any)s
               """ % {
                 'name' : name,
                 'any' : _("any day"),
                 'sel' : self.tmpl_is_selected(sd, 0)
               }
         for day in range(1,32):
             box += """<option value="%02d"%s>%02d""" % (day, self.tmpl_is_selected(sd, day), day)
         box += """</select>"""
         # month
         box += """
                 <select name="%(name)sm">
                   <option value=""%(sel)s>%(any)s
                """ % {
                  'name' : name,
                  'any' : _("any month"),
                  'sel' : self.tmpl_is_selected(sm, 0)
                }
         for mm, month in [(1,_("January")), (2,_("February")), (3,_("March")), (4,_("April")), \
                           (5,_("May")), (6,_("June")), (7,_("July")), (8,_("August")), \
                           (9,_("September")), (10,_("October")), (11,_("November")), (12,_("December"))]:
             box += """<option value="%02d"%s>%s""" % (mm, self.tmpl_is_selected(sm, mm), month)
         box += """</select>"""
         # year
         box += """
                 <select name="%(name)sy">
                   <option value=""%(sel)s>%(any)s
                """ % {
                  'name' : name,
                  'any' : _("any year"),
                  'sel' : self.tmpl_is_selected(sy, 0)
                }
         this_year = int(time.strftime("%Y", time.localtime()))
         for year in range(this_year-20, this_year+1):
             box += """<option value="%d"%s>%d""" % (year, self.tmpl_is_selected(sy, year), year)
         box += """</select>"""
         return box
 
     def tmpl_narrowsearch(self, as, ln, type, father,
                           has_grandchildren, sons, display_grandsons,
                           grandsons):
 
         """
         Creates list of collection descendants of type *type* under title *title*.
         If as==1, then links to Advanced Search interfaces; otherwise Simple Search.
         Suitable for 'Narrow search' and 'Focus on' boxes.
 
         Parameters:
 
           - 'as' *bool* - Should we display an advanced search box?
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'title' *string* - The title of the produced box
 
           - 'type' *string* - The type of the produced box (virtual collections or normal collections)
 
           - 'father' *collection* - The current collection
 
           - 'has_grandchildren' *bool* - If the current collection has grand children
 
           - 'sons' *list* - The list of the sub-collections (first level)
 
           - 'display_grandsons' *bool* - If the grand children collections should be displayed (2 level deep display)
 
           - 'grandsons' *list* - The list of sub-collections (second level)
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         title = {'r': _("Narrow by collection:"),
                  'v': _("Focus on:")}[type]
         
 
         if has_grandchildren:
             style_prolog = "<strong>"
             style_epilog = "</strong>"
         else:
             style_prolog = ""
             style_epilog = ""
 
-        out = ''
-        if type == 'r':
-            out += """<input type="hidden" name="cc" value="%(name)s">""" % {
-                     'name' : cgi.escape(father.name, 1),
-                   }
-
-        if len(sons):
-            out += """<table class="narrowsearchbox">
-                       <thead>
-                        <tr>
-                         <th colspan="2" align="left" class="narrowsearchboxheader">
-                           %(title)s
-                         </th>
-                        </tr>
-                       </thead>
-                       <tbody>""" % {'title' : title}
-            # iterate through sons:
-            i = 0
-            for son in sons:
-                out += """<tr><td class="narrowsearchboxbody" valign="top">"""
-                if type=='r':
-                    if son.restricted_p() and son.restricted_p() != father.restricted_p():
-                        out += """<input type=checkbox name="c" value="%(name)s">&nbsp;</td>""" % {'name' : son.name }
-                    else:
-                        out += """<input type=checkbox name="c" value="%(name)s" checked>&nbsp;</td>""" % {'name' : son.name }
-                out += """<td valign="top">%(link)s%(recs)s """ % {
-                    'link': a_href(style_prolog + son.get_name(ln) + style_epilog,
-                                   href=self.build_search_interface_url(c=son.name, ln=ln, as=as)),
-                    'recs' : self.tmpl_nbrecs_info(son.nbrecs, ln=ln)}
-                
-                if son.restricted_p():
-                    out += """ <small class="warning">[%(msg)s]</small>""" % { 'msg' : _("restricted") }
-                if display_grandsons and len(grandsons[i]):
-                    # iterate trough grandsons:
-                    out += """<br>"""
-                    for grandson in grandsons[i]:
-                        out += """ %(link)s%(nbrec)s """ % {
-                            'link': a_href(grandson.get_name(ln),
-                                           href=self.build_search_interface_url(c=grandson.name, ln=ln, as=as)),
-                            'nbrec' : self.tmpl_nbrecs_info(grandson.nbrecs, ln=ln)}
-                        
-                out += """</td></tr>"""
-                i += 1
-            out += "</tbody></table>"
-        else:
-            if type == 'r':
-                # no sons, and type 'r', so print info on collection content:
-                out += """<table class="narrowsearchbox">
-                           <thead>
-                            <tr>
-                             <th class="narrowsearchboxheader">
-                               %(header)s
-                             </th>
-                            </tr>
-                           </thead>
-                           <tbody>
-                            <tr>
-                             <td class="narrowsearchboxbody">%(body)s</td>
-                            </tr>
-                           <tbody>
-                          </table>""" % {
-                           'header' : _("Latest additions:"),
-                           'body' : instant_browse
-                       }
+        out = """<table class="narrowsearchbox">
+                   <thead>
+                    <tr>
+                     <th colspan="2" align="left" class="narrowsearchboxheader">
+                       %(title)s
+                     </th>
+                    </tr>
+                   </thead>
+                   <tbody>""" % {'title' : title}
+        # iterate through sons:
+        i = 0
+        for son in sons:
+            out += """<tr><td class="narrowsearchboxbody" valign="top">"""
+            if type=='r':
+                if son.restricted_p() and son.restricted_p() != father.restricted_p():
+                    out += """<input type=checkbox name="c" value="%(name)s">&nbsp;</td>""" % {'name' : son.name }
+                else:
+                    out += """<input type=checkbox name="c" value="%(name)s" checked>&nbsp;</td>""" % {'name' : son.name }
+            out += """<td valign="top">%(link)s%(recs)s """ % {
+                'link': a_href(style_prolog + son.get_name(ln) + style_epilog,
+                               href=self.build_search_interface_url(c=son.name, ln=ln, as=as)),
+                'recs' : self.tmpl_nbrecs_info(son.nbrecs, ln=ln)}
+
+            if son.restricted_p():
+                out += """ <small class="warning">[%(msg)s]</small>""" % { 'msg' : _("restricted") }
+            if display_grandsons and len(grandsons[i]):
+                # iterate trough grandsons:
+                out += """<br>"""
+                for grandson in grandsons[i]:
+                    out += """ %(link)s%(nbrec)s """ % {
+                        'link': a_href(grandson.get_name(ln),
+                                       href=self.build_search_interface_url(c=grandson.name, ln=ln, as=as)),
+                        'nbrec' : self.tmpl_nbrecs_info(grandson.nbrecs, ln=ln)}
+
+            out += """</td></tr>"""
+            i += 1
+        out += "</tbody></table>"
 
         return out
 
     def tmpl_nice_number(self, num, ln=cdslang):
         "Returns nicely printed number NUM in language LN using thousands separator char defined in the I18N messages file."
 
         if num is None: return None
 
         _ = gettext_set_language(ln)
 
         separator = _(",")
 
         chars_in = list(str(num))
         num = len(chars_in)
         chars_out = []
         for i in range(0,num):
             if i % 3 == 0 and i != 0:
                 chars_out.append(separator)
             chars_out.append(chars_in[num-i-1])
         chars_out.reverse()
         return ''.join(chars_out)
 
     def tmpl_nbrecs_info(self, number, prolog=None, epilog=None, ln=cdslang):
         """
         Return information on the number of records.
 
         Parameters:
 
         - 'number' *string* - The number of records
 
         - 'prolog' *string* (optional) - An HTML code to prefix the number (if **None**, will be
         '<small class="nbdoccoll">(')
 
         - 'epilog' *string* (optional) - An HTML code to append to the number (if **None**, will be
         ')</small>')
         """
 
         if number is None: return ''
 
         if prolog is None:
             prolog = '''&nbsp;<small class="nbdoccoll">('''
         if epilog is None:
             epilog = ''')</small>'''
 
         return prolog + self.tmpl_nice_number(number, ln) + epilog
 
     def tmpl_box_restricted_content(self, ln):
         """
           Displays a box containing a *restricted content* message
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         return _("The contents of this collection is restricted.")
 
     def tmpl_box_no_records(self, ln):
         """
           Displays a box containing a *no content* message
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         return _("This collection does not contain any document yet.")
 
 
     def tmpl_instant_browse(self, as, ln, recids, more_link = None):
         """
           Formats a list of records (given in the recids list) from the database.
 
         Parameters:
 
           - 'as' *int* - Advanced Search interface or not (0 or 1)
 
           - 'ln' *string* - The language to display
 
           - 'recids' *list* - the list of records from the database
 
           - 'more_link' *string* - the "More..." link for the record. If not given, will not be displayed
 
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         body = '''<table class="latestadditionsbox">'''
         for recid in recids:
             body += '''
             <tr>
               <td class="latestadditionsboxtimebody">%(date)s</td>
               <td class="latestadditionsboxrecordbody">%(body)s</td>
             </tr>''' % {'date': recid['date'],
                         'body': recid['body']
                       }
         body += "</table>"
         if more_link:
             body += '<div align="right"><small>' + \
                     a_href('[&gt;&gt; %s]' % _("more"), href=more_link) + \
                     '</small></div>'
 
         return '''
         <table class="narrowsearchbox">
           <thead>
             <tr>
               <th class="narrowsearchboxheader">%(header)s</th>
             </tr>
           </thead>
           <tbody>
             <tr>
             <td class="narrowsearchboxbody">%(body)s</td>
             </tr>
           <tbody>
         </table>''' % {'header' : _("Latest additions:"),
                        'body' : body,
                        }
 
 
     def tmpl_searchwithin_select(self, ln, fieldname, selected, values):
         """
           Produces 'search within' selection box for the current collection.
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'fieldname' *string* - the name of the select box produced
 
           - 'selected' *string* - which of the values is selected
 
           - 'values' *list* - the list of values in the select
         """
         
         out = '<select name="%(fieldname)s">' % {'fieldname': fieldname}
 
         if values:
             for pair in values:
                 out += """<option value="%(value)s"%(selected)s>%(text)s""" % {
                          'value'    : pair['value'],
                          'selected' : self.tmpl_is_selected(pair['value'], selected),
                          'text'     : pair['text']
                        }
         out += """</select>"""
         return out
 
     def tmpl_select(self, fieldname, values, selected=None, css_class=''):
         """
           Produces a generic select box
 
         Parameters:
 
           - 'css_class' *string* - optional, a css class to display this select with
 
           - 'fieldname' *list* - the name of the select box produced
 
           - 'selected' *string* - which of the values is selected
 
           - 'values' *list* - the list of values in the select
         """
         if css_class != '':
             class_field = ' class="%s"' % css_class
         else:
             class_field = ''
         out = '<select name="%(fieldname)s"%(class)s>' % {
             'fieldname' : fieldname,
             'class' : class_field
             }
 
         for pair in values:
             if pair.get('selected', False) or pair['value'] == selected:
                 selected = ' selected'
             else:
                 selected = ''
             
             out += '<option value="%(value)s"%(selected)s>%(text)s' % {
                      'value'    : pair['value'],
                      'selected' : selected,
                      'text'     : pair['text']
                    }
             
         out += """</select>"""
         return out
 
     def tmpl_record_links(self, weburl, recid, ln):
         """
           Displays the *More info* and *Find similar* links for a record
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'recid' *string* - the id of the displayed record
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = '''<br><span class="moreinfo">%(detailed)s - %(similar)s</span>''' % {
             'detailed': a_href(_("Detailed record"), _class="moreinfo",
                                href=self.build_search_url(recid=recid, ln=ln)),
             'similar': a_href(_("Similar records"), _class="moreinfo",
                               href=self.build_search_url(p="recid:%d" % recid, rm='wrd', ln=ln))}
                  
         if cfg_experimental_features:
             out += '''<span class="moreinfo"> - %s </span>''' % \
                    a_href(_("Cited by"), _class="moreinfo",
                           href=self.build_search_url(p='recid:%d' % recid, rm='cit', ln=ln))
                  
         return out
 
     def tmpl_record_body(self, weburl, titles, authors, dates, rns, abstracts, urls_u, urls_z, ln):
         """
           Displays the "HTML basic" format of a record
 
         Parameters:
 
           - 'weburl' *string* - The base URL for the site
 
           - 'authors' *list* - the authors (as strings)
 
           - 'dates' *list* - the dates of publication
 
           - 'rns' *list* - the quicknotes for the record
 
           - 'abstracts' *list* - the abstracts for the record
 
           - 'urls_u' *list* - URLs to the original versions of the notice
 
           - 'urls_z' *list* - Not used
         """
         out = ""
         for title in titles:
             out += "<strong>%(title)s</strong> " % {
                      'title' : cgi.escape(title)
                    }
         if authors:
             out += " / "
             for author in authors[:cfg_author_et_al_threshold]:
                 out += '%s; ' % \
                        a_href(cgi.escape(author), 
                               href=self.build_search_url(p=author, f='author', ln=ln))
                     
             if len(authors) > cfg_author_et_al_threshold:
                 out += "<em>et al</em>"
         for date in dates:
             out += " %s." % cgi.escape(date)
         for rn in rns:
             out += """ <small class="quicknote">[%(rn)s]</small>""" % {'rn' : cgi.escape(rn)}
         for abstract in abstracts:
             out += "<br><small>%(abstract)s [...]</small>" % {'abstract' : cgi.escape(abstract[:1+string.find(abstract, '.')]) }
         for idx in range(0,len(urls_u)):
             out += """<br><small class="note"><a class="note" href="%(url)s">%(name)s</a></small>""" % {
                      'url' : urls_u[idx],
                      'name' : urls_u[idx]
                    }
         return out
 
     def tmpl_search_in_bibwords(self, p, f, ln, nearest_box):
         """
           Displays the *Words like current ones* links for a search
 
         Parameters:
 
           - 'p' *string* - Current search words
 
           - 'f' *string* - the fields in which the search was done
 
           - 'nearest_box' *string* - the HTML code for the "nearest_terms" box - most probably from a create_nearest_terms_box call
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = "<p>%(words)s <em>%(p)s</em> " % {
                  'words' : _("Words nearest to"),
                      'p' : p,
               }
         if f:
             out += "%(inside)s <em>%(f)s</em> " %{
                  'inside' : _("inside"),
                      'f' : f,
               }
         out += _("in any collection are:") + "<br>"
         out += nearest_box
         return out
 
     def tmpl_nearest_term_box(self, p, ln, f, terminfo, intro):
         """
           Displays the *Nearest search terms* box
 
         Parameters:
 
           - 'p' *string* - Current search words
 
           - 'f' *string* - a collection description (if the search has been completed in a collection)
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'terminfo': tuple (term, hits, argd) for each near term
 
           - 'intro' *string* - the intro HTML to prefix the box with
         """
 
         out = '''<table class="nearesttermsbox" cellpadding="0" cellspacing="0" border="0">'''
 
         for term, hits, argd in terminfo:
 
             if hits:
                 hitsinfo = str(hits)
             else:
                 hitsinfo = '-'
             
             term = cgi.escape(term)
 
             if term == p: # print search word for orientation:
                 if hits > 0:
                     term = a_href(term, href=self.build_search_url(argd),
                                   _class="nearesttermsselected")
             else:
                 term = a_href(term, href=self.build_search_url(argd),
                               _class="nearestterms")
 
             out += '''\
             <tr>
               <td class="nearesttermsboxbodyselected" align="right">%(hits)s</td>
               <td class="nearesttermsboxbodyselected" width="15">&nbsp;</td>
               <td class="nearesttermsboxbodyselected" align="left">%(term)s</td>
             </tr>  
             ''' % {'hits': hitsinfo,
                    'term': term}
 
         out += "</table>"
         return intro + "<blockquote>" + out + "</blockquote>"
 
     def tmpl_browse_pattern(self, f, ln, browsed_phrases_in_colls, colls):
         """
           Displays the *Nearest search terms* box
 
         Parameters:
 
           - 'f' *string* - a field name (i18nized)
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'browsed_phrases_in_colls' *array* - the phrases to display
 
           - 'urlargs_colls' *string* - the url parameters for the search
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = """<table class="searchresultsbox">
               <thead>
                <tr>
                 <th class="searchresultsboxheader" align="left">
                   %(hits)s
                 </th>
                 <th class="searchresultsboxheader" width="15">
                   &nbsp;
                 </th>
                 <th class="searchresultsboxheader" align="left">
                   %(f)s
                 </th>
                </tr>
               </thead>
               <tbody>""" % {
                 'hits' : _("Hits"),
                 'f' : f
               }
 
         if len(browsed_phrases_in_colls) == 1:
             # one hit only found:
             phrase, nbhits = browsed_phrases_in_colls[0][0], browsed_phrases_in_colls[0][1]
 
             query = {'c': colls,
                      'ln': ln,
                      'p': '"%s"' % phrase,
                      'f': f}
 
             out += """<tr>
                        <td class="searchresultsboxbody" align="right">
                         %(nbhits)s
                        </td>
                        <td class="searchresultsboxbody" width="15">
                         &nbsp;
                        </td>
                        <td class="searchresultsboxbody" align="left">
                         %(link)s
                        </td>
                       </tr>""" % {'nbhits': nbhits,
                                   'link': a_href(phrase, href=self.build_search_url(query))}
                         
         elif len(browsed_phrases_in_colls) > 1:
             # first display what was found but the last one:
             for phrase, nbhits in browsed_phrases_in_colls[:-1]:
                 query = {'c': colls,
                          'ln': ln,
                          'p': '"%s"' % phrase,
                          'f': f}
                 
                 out += """<tr>
                            <td class="searchresultsboxbody" align="right">
                             %(nbhits)s
                            </td>
                            <td class="searchresultsboxbody" width="15">
                             &nbsp;
                            </td>
                            <td class="searchresultsboxbody" align="left">
                             %(link)s
                            </td>
                           </tr>""" % {'nbhits' : nbhits,
                                       'link': a_href(phrase, href=self.build_search_url(query))}
                             
             # now display last hit as "next term":
             phrase, nbhits = browsed_phrases_in_colls[-1]
             query = {'c': colls,
                      'ln': ln,
                      'p': phrase,
                      'f': f}
             
             out += """<tr><td colspan="2" class="normal">
                             &nbsp;
                           </td>
                           <td class="normal">
                             <img src="%(weburl)s/img/sn.gif" alt="" border="0">
                             %(link)s
                           </td>
                       </tr>""" % {'link': a_href(_("next"),
                                                  href=self.build_search_url(query, action='browse')),
                                   'weburl' : weburl}
         out += """</tbody>
             </table>"""
         return out
 
     def tmpl_search_box(self, ln, as, cc, cc_intl, ot, sp,
                         action, fieldslist, f1, f2, f3, m1, m2, m3,
                         p1, p2, p3, op1, op2, rm, p, f, coll_selects,
                         d1y, d2y, d1m, d2m, d1d, d2d, sort_formats,
                         sf, so, ranks, sc, rg, formats, of, pl, jrec):
         
         """
           Displays the *Nearest search terms* box
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'as' *bool* - Should we display an advanced search box?
 
           - 'cc_intl' *string* - the i18nized current collection name
 
           - 'cc' *string* - the internal current collection name
 
           - 'ot', 'sp' *string* - hidden values
 
           - 'action' *string* - the action demanded by the user
 
           - 'fieldlist' *list* - the list of all fields available in CDSWare, for use in select within boxes in advanced search
 
           - 'p, f, f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2, op3, rm' *strings* - the search parameters
 
           - 'coll_selects' *array* - a list of lists, each containing the collections selects to display
 
           - 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
 
           - 'sort_formats' *array* - the select information for the sorting format
 
           - 'sf' *string* - the currently selected sort format
 
           - 'so' *string* - the currently selected sort order ("a" or "d")
 
           - 'ranks' *array* - ranking methods
 
           - 'rm' *string* - selected ranking method
 
           - 'sc' *string* - split by collection or not
 
           - 'rg' *string* - selected results/page
 
           - 'formats' *array* - available output formats
 
           - 'of' *string* - the selected output format
 
           - 'pl' *string* - `limit to' search pattern
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
 
         # These are hidden fields the user does not manipulate
         # directly
         argd = drop_default_urlargd({
             'ln': ln, 'as': as,
             'cc': cc, 'ot': ot, 'sp': sp,
             }, self.search_results_default_urlargd)
         
 
         out = '''
         <h1 class="headline">%(ccname)s</h1>
         <form name="search" action="%(weburl)s/search" method="get">
         ''' % {'ccname' : cc_intl,
                'weburl' : weburl}
         
         # Only add non-default hidden values
         for field, value in argd.items():
             out += self.tmpl_input_hidden(field, value)
         
         leadingtext = _("Search")
 
         if action == 'browse':
             leadingtext = _("Browse")
 
         if as == 1:
             # print Advanced Search form:
             google = ''
             if cfg_google_box and (p1 or p2 or p3):
                 google = '<small> :: <a href="#googlebox">%(search_smwhere)s</a></small>' % {
                      'search_smwhere' : _("Try your search on...")
                    }
 
             # define search box elements:
             out += '''
             <table class="searchbox">
              <thead>
               <tr>
                <th colspan="3" class="searchboxheader">
                 %(leading)s:
                </th>
               </tr>
              </thead>
              <tbody>
               <tr valign="top">
                 <td class="searchboxbody">%(matchbox1)s
                   <input type="text" name="p1" size="%(sizepattern)d" value="%(p1)s">
                 </td>
                 <td class="searchboxbody">%(searchwithin1)s</td>
                 <td class="searchboxbody">%(andornot1)s</td>
               </tr>
               <tr valign="top">
                 <td class="searchboxbody">%(matchbox2)s
                   <input type="text" name="p2" size="%(sizepattern)d" value="%(p2)s">
                 </td>
                 <td class="searchboxbody">%(searchwithin2)s</td>
                 <td class="searchboxbody">%(andornot2)s</td>
               </tr>
               <tr valign="top">
                 <td class="searchboxbody">%(matchbox3)s
                   <input type="text" name="p3" size="%(sizepattern)d" value="%(p3)s">
                 </td>
                 <td class="searchboxbody">%(searchwithin3)s</td>
                 <td class="searchboxbody">
                   <input class="formbutton" type="submit" name="action_search" value="%(search)s">
                   <input class="formbutton" type="submit" name="action_browse" value="%(browse)s">&nbsp;
                 </td>
               </tr>
               <tr valign="bottom">
                 <td colspan="3" align="right" class="searchboxbody">
                   <small>
                     <a href="%(weburl)s/help/search/tips.%(ln)s.html">%(search_tips)s</a> ::
                     %(simple_search)s
                   </small>
                   %(google)s
                 </td>
               </tr>
              </tbody>
             </table>
             ''' % {
                 'simple_search': a_href(_("Simple Search"),
                                         href=self.build_search_url(p=p1, f=f1, rm=rm, cc=cc, ln=ln, jrec=jrec, rg=rg)),
                 
                 'leading' : leadingtext,
                 'sizepattern' : cfg_advancedsearch_pattern_box_width,
                 'matchbox1' : self.tmpl_matchtype_box('m1', m1, ln=ln),
                 'p1' : cgi.escape(p1,1),
                 'searchwithin1' : self.tmpl_searchwithin_select(
                                   ln = ln,
                                   fieldname = 'f1',
                                   selected = f1,
                                   values = self._add_mark_to_field(value = f1, fields = fieldslist, ln = ln)
                                 ),
               'andornot1' : self.tmpl_andornot_box(
                                   name = 'op1',
                                   value = op1,
                                   ln = ln
                                 ),
               'matchbox2' : self.tmpl_matchtype_box('m2', m2, ln=ln),
               'p2' : cgi.escape(p2,1),
               'searchwithin2' : self.tmpl_searchwithin_select(
                                   ln = ln,
                                   fieldname = 'f2',
                                   selected = f2,
                                   values = self._add_mark_to_field(value = f2, fields = fieldslist, ln = ln)
                                 ),
               'andornot2' : self.tmpl_andornot_box(
                                   name = 'op2',
                                   value = op2,
                                   ln = ln
                                 ),
               'matchbox3' : self.tmpl_matchtype_box('m3', m3, ln=ln),
               'p3' : cgi.escape(p3,1),
               'searchwithin3' : self.tmpl_searchwithin_select(
                                   ln = ln,
                                   fieldname = 'f3',
                                   selected = f3,
                                   values = self._add_mark_to_field(value = f3, fields = fieldslist, ln = ln)
                                 ),
               'search' : _("Search"),
               'browse' : _("Browse"),
               'weburl' : weburl,
               'ln' : ln,
               'search_tips': _("Search Tips"),
               'google' : google,
             }
         else:
             # print Simple Search form:
             google = ''
             if cfg_google_box and (p1 or p2 or p3):
                 google = '''<small> :: <a href="#googlebox">%(search_smwhere)s</a></small>''' % {
                      'search_smwhere' : _("Try your search on...")
                    }
 
             out += '''
             <table class="searchbox">
              <thead>
               <tr>
                <th colspan="3" class="searchboxheader">
                 %(leading)s:
                </th>
               </tr>
              </thead>
              <tbody>
               <tr valign="top">
                 <td class="searchboxbody"><input type="text" name="p" size="%(sizepattern)d" value="%(p)s"></td>
                 <td class="searchboxbody">%(searchwithin)s</td>
                 <td class="searchboxbody">
                   <input class="formbutton" type="submit" name="action_search" value="%(search)s">
                   <input class="formbutton" type="submit" name="action_browse" value="%(browse)s">&nbsp;
                 </td>
               </tr>
               <tr valign="bottom">
                 <td colspan="3" align="right" class="searchboxbody">
                   <small>
                     <a href="%(weburl)s/help/search/tips.%(ln)s.html">%(search_tips)s</a> ::
                     %(advanced_search)s
                   </small>
                   %(google)s
                 </td>
               </tr>
              </tbody>
             </table>
             ''' % {
               'advanced_search': a_href(_("Advanced Search"),
                                         href=self.build_search_url(p1=p, f1=f, rm=rm, as=1, cc=cc, jrec=jrec, ln=ln, rg=rg)),
               'leading' : leadingtext,
               'sizepattern' : cfg_advancedsearch_pattern_box_width,
               'p' : cgi.escape(p, 1),
               'searchwithin' : self.tmpl_searchwithin_select(
                                   ln = ln,
                                   fieldname = 'f',
                                   selected = f,
                                   values = self._add_mark_to_field(value=f, fields=fieldslist, ln=ln)
                                 ),
               'search' : _("Search"),
               'browse' : _("Browse"),
               'weburl' : weburl,
               'ln' : ln,
               'search_tips': _("Search Tips"),
               'google' : google,
             }
             
         ## secondly, print Collection(s) box:
         selects = ''
         for sel in coll_selects:
             selects += self.tmpl_select(fieldname='c', values=sel)
 
         out += """
             <table class="searchbox">
              <thead>
               <tr>
                <th colspan="3" class="searchboxheader">
                 %(leading)s %(msg_coll)s:
                </th>
               </tr>
              </thead>
              <tbody>
               <tr valign="bottom">
                <td valign="top" class="searchboxbody">
                  %(colls)s
                </td>
               </tr>
              </tbody>
             </table>
              """ % {
                'leading' : leadingtext,
                'msg_coll' : _("collections"),
                'colls' : selects,
              }
 
         ## thirdly, print search limits, if applicable:
         if action != _("Browse") and pl:
             out += """<table class="searchbox">
                        <thead>
                         <tr>
                           <th class="searchboxheader">
                             %(limitto)s:
                           </th>
                         </tr>
                        </thead>
                        <tbody>
                         <tr valign="bottom">
                           <td class="searchboxbody">
                            <input type="text" name="pl" size="%(sizepattern)d" value="%(pl)s">
                           </td>
                         </tr>
                        </tbody>
                       </table>""" % {
                         'limitto' : _("Limit to"),
                         'sizepattern' : cfg_advancedsearch_pattern_box_width,
                         'pl' : cgi.escape(pl, 1),
                       }
 
         ## fourthly, print from/until date boxen, if applicable:
         if action == _("Browse") or (d1y==0 and d1m==0 and d1d==0 and d2y==0 and d2m==0 and d2d==0):
             pass # do not need it
         else:
             cell_6_a = self.tmpl_inputdate("d1", ln, d1y, d1m, d1d)
             cell_6_b = self.tmpl_inputdate("d2", ln, d2y, d2m, d2d)
             out += """<table class="searchbox">
                        <thead>
                         <tr>
                           <th class="searchboxheader">
                             %(added)s
                           </th>
                           <th class="searchboxheader">
                             %(until)s
                           </th>
                         </tr>
                        </thead>
                        <tbody>
                         <tr valign="bottom">
                           <td class="searchboxbody">%(date1)s</td>
                           <td class="searchboxbody">%(date2)s</td>
                         </tr>
                        </tbody>
                       </table>""" % {
                         'added' : _("Added since:"),
                         'until' : _("until:"),
                         'date1' : self.tmpl_inputdate("d1", ln, d1y, d1m, d1d),
                         'date2' : self.tmpl_inputdate("d2", ln, d2y, d2m, d2d),
                       }
 
         ## fifthly, print Display results box, including sort/rank, formats, etc:
         if action != _("Browse"):
 
             rgs = []
             for i in [10, 25, 50, 100, 250, 500]:
                 rgs.append({ 'value' : i, 'text' : "%d %s" % (i, _("results"))})
 
             # sort by:
             out += """<table class="searchbox">
                  <thead>
                   <tr>
                    <th class="searchboxheader">
                     %(sort_by)s
                    </th>
                    <th class="searchboxheader">
                     %(display_res)s
                    </th>
                    <th class="searchboxheader">
                     %(out_format)s
                    </th>
                   </tr>
                  </thead>
                  <tbody>
                   <tr valign="bottom">
                    <td valign="top" class="searchboxbody">
                      %(select_sf)s %(select_so)s %(select_rm)s
                    </td>
                    <td valign="top" class="searchboxbody">
                      %(select_rg)s %(select_sc)s
                    </td>
                    <td valign="top" class="searchboxbody">%(select_of)s</td>
                   </tr>
                  </tbody>
                 </table>""" % {
                   'sort_by' : _("Sort by:"),
                   'display_res' : _("Display results:"),
                   'out_format' : _("Output format:"),
                   'select_sf' : self.tmpl_select(fieldname = 'sf', values = sort_formats, selected = sf, css_class = 'address'),
                   'select_so' : self.tmpl_select(fieldname = 'so', values = [{
                                     'value' : 'a',
                                     'text' : _("asc.")
                                   }, {
                                     'value' : 'd',
                                     'text' : _("desc.")
                                   }], selected = so, css_class = 'address'),
                   'select_rm' : self.tmpl_select(fieldname = 'rm', values = ranks, selected = rm, css_class = 'address'),
                   'select_rg' : self.tmpl_select(fieldname = 'rg', values = rgs, selected = rg, css_class = 'address'),
                   'select_sc' : self.tmpl_select(fieldname = 'sc', values = [{
                                     'value' : '0',
                                     'text' : _("single list")
                                   }, {
                                     'value' : '1',
                                     'text' : _("split by collection")
                                   }], selected = so, css_class = 'address'),
                   'select_of' : self.tmpl_searchwithin_select(
                                   ln = ln,
                                   fieldname = 'of',
                                   selected = of,
                                   values = self._add_mark_to_field(value = of, fields = formats, chars = 3, ln = ln)
                                 ),
                 }
 
         ## last but not least, print end of search box:
         out += """</form>"""
         return out
 
     def tmpl_input_hidden(self, name, value):
         "Produces the HTML code for a hidden field "
         return """<input type="hidden" name="%(name)s" value="%(value)s">""" % {
                  'name' : cgi.escape(str(name), 1),
                  'value' : cgi.escape(str(value), 1),
                }
 
     def _add_mark_to_field(self, value, fields, ln, chars = 1):
         """Adds the current value as a MARC tag in the fields array
         Useful for advanced search"""
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = fields
         if value and str(value[0:chars]).isdigit():
             out.append({'value' : value,
                         'text' : str(value) + " " + _("MARC tag")
                         })
         return out
 
     def tmpl_google_box(self, ln, cc, p, f, prolog_start, prolog_end, column_separator, link_separator, epilog):
         """Creates the box that proposes links to other useful search engines like Google.
 
         Parameters:
 
           - 'ln' *string* - The language to display in
 
           - 'cc' *string* - the internal current collection name
 
           - 'p' *string* - the search query
 
           - 'f' *string* - the current field
 
           - 'prolog_start, prolog_end, column_separator, link_separator, epilog' *strings* - default HTML code for the specified position in the box
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out_links = []
         p_quoted = urllib.quote(p)
         # Amazon
         if cfg_google_box_servers.get('Amazon', 0):
             if string.find(cc, "Book") >= 0:
                 if f == "author":
                     out_links.append("""<a class="google" href="http://www.amazon.com/exec/obidos/external-search/?field-author=%s&tag=cern">%s %s Amazon</a>""" % (p_quoted, p, _('in')))
                 else:
                     out_links.append("""<a class="google" href="http://www.amazon.com/exec/obidos/external-search/?keyword=%s&tag=cern">%s %s Amazon</a>""" % (p_quoted, p, _('in')))
         # CERN Intranet:
         if cfg_google_box_servers.get('CERN Intranet', 0):
             out_links.append("""<a class="google" href="http://search.cern.ch/query.html?qt=%s">%s %s CERN&nbsp;Intranet</a>""" % (urllib.quote(string.replace(p, ' ', ' +')), p, _('in')))
         # CERN Agenda:
         if cfg_google_box_servers.get('CERN Agenda', 0):
             if f == "author":
                 out_links.append("""<a class="google" href="http://agenda.cern.ch/search.php?field=speaker&keywords=%s&search=Search">%s %s CERN&nbsp;Agenda</a>""" % (p_quoted, p, _('in')))
             elif f == "title":
                 out_links.append("""<a class="google" href="http://agenda.cern.ch/search.php?field=title&keywords=%s&search=Search">%s %s CERN&nbsp;Agenda</a>""" % (p_quoted, p, _('in')))
         # CERN EDMS:
         if cfg_google_box_servers.get('CERN Agenda', 0):
             # FIXME: reusing CERN Agenda config variable until we can enter CERN EDMS into config.wml
             if f == "author":
                 out_links.append("""<a class="google" href="https://edms.cern.ch/cedar/plsql/fullsearch.doc_search?p_search_type=ADVANCED&p_author=%s">%s %s CERN&nbsp;EDMS</a>""" % (p_quoted, p, _("in")))
             elif f == "title" or f == "abstract" or f == "keyword":
                 out_links.append("""<a class="google" href="https://edms.cern.ch/cedar/plsql/fullsearch.doc_search?p_search_type=ADVANCED&p_title=%s">%s %s CERN&nbsp;EDMS</a>""" % (p_quoted, p, _("in")))
             elif f == "reportnumber":
                 out_links.append("""<a class="google" href="https://edms.cern.ch/cedar/plsql/fullsearch.doc_search?p_search_type=ADVANCED&p_document_id=%s">%s %s CERN&nbsp;EDMS</a>""" % (p_quoted, p, _("in")))
             else:
                 out_links.append("""<a class="google" href="https://edms.cern.ch/cedar/plsql/fullsearch.doc_search?p_search_type=BASE&p_free_text=%s">%s %s CERN&nbsp;EDMS</a>""" % (p_quoted, p, _("in")))
         # CiteSeer:
         if cfg_google_box_servers.get('CiteSeer', 0):
             out_links.append("""<a class="google" href="http://citeseer.ist.psu.edu/cs?q=%s">%s %s CiteSeer</a>""" % (p_quoted, p, _('in')))
         # Google Print:
         if cfg_google_box_servers.get('Google Scholar', 0):
             # FIXME: reusing Google Scholar config variable until we can enter Google Print into config.wml
             if string.find(cc, "Book") >= 0:
                 out_links.append("""<a class="google" href="http://print.google.com/print?q=%s">%s %s Google Print</a>""" % (p_quoted, p, _("in")))
         # Google Scholar:
         if cfg_google_box_servers.get('Google Scholar', 0):
             if f == "author":
                 out_links.append("""<a class="google" href="http://scholar.google.com/scholar?q=author%%3A%s">%s %s Google Scholar</a>""" % (p_quoted, p, _('in')))
             else:
                 out_links.append("""<a class="google" href="http://scholar.google.com/scholar?q=%s">%s %s Google Scholar</a>""" % (p_quoted, p, _('in')))
         # Google Web:
         if cfg_google_box_servers.get('Google Web', 0):
             if f == "author":
                 p_google = p
                 if string.find(p, ",") >= 0 and (not p.startswith('"')) and (not p.endswith('"')):
                     p_lastname, p_firstnames = string.split(p, ",", 1)
                     p_google = '"%s %s" OR "%s %s"' % (p_lastname, p_firstnames, p_firstnames, p_lastname)
                 out_links.append("""<a class="google" href="http://google.com/search?q=%s">%s %s Google Web</a>""" % (urllib.quote(p_google), p_google, _('in')))
             else:
                 out_links.append("""<a class="google" href="http://google.com/search?q=%s">%s %s Google Web</a>""" % (p_quoted, p, _('in')))
         # IEC
         if cfg_google_box_servers.get('IEC', 0):
             if string.find(cc, "Standard") >= 0:
                 out_links.append("""<a class="google" href="http://www.iec.ch/cgi-bin/procgi.pl/www/iecwww.p?wwwlang=E&wwwprog=sea22.p&search=text&searchfor=%s">%s %s IEC</a>""" % (p_quoted, p, _('in')))
         # IHS
         if cfg_google_box_servers.get('IHS', 0):
             if string.find(cc, "Standard") >= 0:
                 out_links.append("""<a class="google" href="http://global.ihs.com/search_res.cfm?&input_doc_title=%s">%s %s IHS</a>""" % (p_quoted, p, _('in')))
         # INSPEC
         if cfg_google_box_servers.get('INSPEC', 0):
             if f == "author":
                 p_inspec = sre.sub(r'(, )| ', '-', p)
                 p_inspec = sre.sub(r'(-\w)\w+$', '\\1', p_inspec)
                 out_links.append("""<a class="google" href="http://www.datastarweb.com/cern/?dblabel=inzz&query=%s.au.">%s %s INSPEC</a>""" % (urllib.quote(p_inspec), p_inspec, _('in')))
             elif f == "title":
                 out_links.append("""<a class="google" href="http://www.datastarweb.com/cern/?dblabel=inzz&query=%s.ti.">%s %s INSPEC</a>""" % (p_quoted, p, _('in')))
             elif f == "abstract":
                 out_links.append("""<a class="google" href="http://www.datastarweb.com/cern/?dblabel=inzz&query=%s.ab.">%s %s INSPEC</a>""" % (p_quoted, p, _('in')))
             elif f == "year":
                 out_links.append("""<a class="google" href="http://www.datastarweb.com/cern/?dblabel=inzz&query=%s.yr.">%s %s INSPEC</a>""" % (p_quoted, p, _('in')))
         # ISO
         if cfg_google_box_servers.get('ISO', 0):
             if string.find(cc, "Standard") >= 0:
                 out_links.append("""<a class="google" href="http://www.iso.org/iso/en/StandardsQueryFormHandler.StandardsQueryFormHandler?languageCode=en&keyword=%s&lastSearch=false&title=true&isoNumber=&isoPartNumber=&isoDocType=ALL&isoDocElem=ALL&ICS=&stageCode=&stagescope=Current&repost=1&stagedatepredefined=&stageDate=&committee=ALL&subcommittee=&scopecatalogue=CATALOGUE&scopeprogramme=PROGRAMME&scopewithdrawn=WITHDRAWN&scopedeleted=DELETED&sortOrder=ISO">%s %s ISO</a>""" % (p_quoted, p, _('in')))
         # KEK
         if cfg_google_box_servers.get('KEK', 0):
             kek_search_title = "KEK KISS Preprints"
             kek_search_baseurl = "http://www-lib.kek.jp/cgi-bin/kiss_prepri?"
             if string.find(cc, "Book") >= 0:
                 kek_search_title = "KEK Library Books"
                 kek_search_baseurl = "http://www-lib.kek.jp/cgi-bin/kiss_book?DSP=1&"
             elif string.find(cc, "Periodical") >= 0:
                 kek_search_title = "KEK Library Journals"
                 kek_search_baseurl = "http://www-lib.kek.jp/cgi-bin/kiss_book?DSP=2&"
             if f == "author":
                 out_links.append("""<a class="google" href="%sAU=%s">%s %s %s</a>""" % \
                                  (kek_search_baseurl, p_quoted, p, _('in'), kek_search_title))
             elif f == "title":
                 out_links.append("""<a class="google" href="%sTI=%s">%s %s %s</a>""" % \
                                  (kek_search_baseurl, p_quoted, p, _('in'), kek_search_title))
             elif f == "reportnumber":
                 out_links.append("""<a class="google" href="%sRP=%s">%s %s %s</a>""" % \
                                  (kek_search_baseurl, p_quoted, p, _('in'), kek_search_title))
         # NEBIS
         if cfg_google_box_servers.get('NEBIS', 0):
             if string.find(cc, "Book") >= 0:
                 if f == "author":
                     out_links.append("""<a class="google" href="http://opac.nebis.ch/F/?func=find-b&REQUEST=%s&find_code=WAU">%s %s NEBIS</a>""" % (p_quoted, p, _('in')))
                 elif f == "title":
                     out_links.append("""<a class="google" href="http://opac.nebis.ch/F/?func=find-b&REQUEST=%s&find_code=WTI">%s %s NEBIS</a>""" % (p_quoted, p, _('in')))
                 else:
                     out_links.append("""<a class="google" href="http://opac.nebis.ch/F/?func=find-b&REQUEST=%s&find_code=WRD">%s %s NEBIS</a>""" % (p_quoted, p, _('in')))
         # Scirus:
         if cfg_google_box_servers.get('Google Scholar', 0):
             # FIXME: reusing Google Scholar config variable until we can enter Scirus into config.wml
             if f == "author":
                 out_links.append("""<a class="google" href="http://www.scirus.com/srsapp/search?q=author%%3A%s">%s %s Scirus</a>""" % (p_quoted, p, _("in")))
             elif f == "title":
                 out_links.append("""<a class="google" href="http://www.scirus.com/srsapp/search?q=title%%3A%s">%s %s Scirus</a>""" % (p_quoted, p, _("in")))
             elif f == "keyword":
                 out_links.append("""<a class="google" href="http://www.scirus.com/srsapp/search?q=keywords%%3A%s">%s %s Scirus</a>""" % (p_quoted, p, _("in")))
             else:
                 out_links.append("""<a class="google" href="http://www.scirus.com/srsapp/search?q=%s">%s %s Scirus</a>""" % (p_quoted, p, _("in")))
         # SPIRES
         if cfg_google_box_servers.get('SPIRES', 0):
             spires_search_title = "SLAC SPIRES HEP"
             spires_search_baseurl = "http://www.slac.stanford.edu/spires/find/hep/"
             if string.find(cc, "Book") >= 0:
                 spires_search_title = "SLAC Library Books"
                 spires_search_baseurl = "http://www.slac.stanford.edu/spires/find/books/"
             elif string.find(cc, "Periodical") >= 0:
                 spires_search_title = "SLAC Library Journals"
                 spires_search_baseurl = "http://www.slac.stanford.edu/spires/find/tserials/"
             if f == "author":
                 out_links.append("""<a class="google" href="%swww?AUTHOR=%s">%s %s %s</a>""" % \
                        (spires_search_baseurl, p_quoted, p, _('in'), spires_search_title))
             elif f == "title":
                 out_links.append("""<a class="google" href="%swww?TITLE=%s">%s %s %s</a>""" % \
                        (spires_search_baseurl, p_quoted, p, _('in'), spires_search_title))
             elif f == "reportnumber":
                 out_links.append("""<a class="google" href="%swww?REPORT-NUM=%s">%s %s %s</a>""" % \
                        (spires_search_baseurl, p_quoted, p, _('in'), spires_search_title))
             elif f == "keyword":
                 out_links.append("""<a class="google" href="%swww?k=%s">%s %s %s</a>""" % \
                        (spires_search_baseurl, p_quoted, p, _('in'), spires_search_title))
             else: # invent a poor man's any field search since SPIRES doesn't support one
                 out_links.append("""<a class="google" href="%swww?rawcmd=find+t+%s+or+a+%s+or+k+%s+or+s+%s+or+r+%s">%s %s %s</a>""" % \
                 (spires_search_baseurl, p_quoted, p_quoted, p_quoted, p_quoted, p_quoted, p, _('in'), spires_search_title))
         # okay, so print the box now:
         out = ""
         if out_links:
             out += """<a name="googlebox"></a>"""
             out += prolog_start + _("Haven't found what you were looking for? Try your search on other servers:") + prolog_end
             nb_out_links_in_one_column = len(out_links)/2
             out += string.join(out_links[:nb_out_links_in_one_column], link_separator)
             out += column_separator
             out += string.join(out_links[nb_out_links_in_one_column:], link_separator)
             out += epilog
         return out
 
     def tmpl_search_pagestart(self, ln) :
         "page start for search page. Will display after the page header"
         return """<div class="pagebody"><div class="pagebodystripemiddle">"""
 
     def tmpl_search_pageend(self, ln) :
         "page end for search page. Will display just before the page footer"
         return """</div></div>"""
 
     def tmpl_print_warning(self, msg, type, prologue, epilogue):
         """Prints warning message and flushes output.
 
         Parameters:
 
           - 'msg' *string* - The message string
 
           - 'type' *string* - the warning type
 
           - 'prologue' *string* - HTML code to display before the warning
 
           - 'epilogue' *string* - HTML code to display after the warning
         """
 
         out = '\n%s<span class="quicknote">' % (prologue)
         if type:
             out += '%s: ' % type
         out += '%s</span>%s' % (msg, epilogue)
         return out
 
     def tmpl_print_search_info(self, ln, weburl, middle_only,
                                collection, collection_name, as, sf,
                                so, rm, rg, nb_found, of, ot, p, f, f1,
                                f2, f3, m1, m2, m3, op1, op2, p1, p2,
                                p3, d1y, d1m, d1d, d2y, d2m, d2d,
                                all_fieldcodes, cpu_time, pl_in_url,
                                jrec, sc, sp):
         
         """Prints stripe with the information on 'collection' and 'nb_found' results and CPU time.
            Also, prints navigation links (beg/next/prev/end) inside the results set.
            If middle_only is set to 1, it will only print the middle box information (beg/netx/prev/end/etc) links.
            This is suitable for displaying navigation links at the bottom of the search results page.
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'middle_only' *bool* - Only display parts of the interface
 
           - 'collection' *string* - the collection name
 
           - 'collection_name' *string* - the i18nized current collection name
 
           - 'as' *bool* - if we display the advanced search interface
 
           - 'sf' *string* - the currently selected sort format
 
           - 'so' *string* - the currently selected sort order ("a" or "d")
 
           - 'rm' *string* - selected ranking method
 
           - 'rg' *int* - selected results/page
 
           - 'nb_found' *int* - number of results found
 
           - 'of' *string* - the selected output format
 
           - 'ot' *string* - hidden values
 
           - 'p' *string* - Current search words
 
           - 'f' *string* - the fields in which the search was done
 
           - 'f1, f2, f3, m1, m2, m3, p1, p2, p3, op1, op2' *strings* - the search parameters
 
           - 'jrec' *int* - number of first record on this page
 
           - 'd1y, d2y, d1m, d2m, d1d, d2d' *int* - the search between dates
 
           - 'all_fieldcodes' *array* - all the available fields
 
           - 'cpu_time' *float* - the time of the query in seconds
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = ""
         # left table cells: print collection name
         if not middle_only:
             out += '''
                   %(collection_name)s
                   <form action="%(weburl)s/search" method="get">
                   <table class="searchresultsbox"><tr><td class="searchresultsboxheader" align="left">
                   <strong><big>%(collection_link)s</big></strong></td>
                   ''' % {
                     'collection_name': a_href('', name=collection),
                     'weburl' : weburl,
                     'collection_link': a_href(collection_name,
                                               href=self.build_search_url(cc=collection, as=as, ln=ln)),
                   }
         else:
             out += """
                   <form action="%(weburl)s/search" method="get"><div align="center">
                   """ % { 'weburl' : weburl }
 
         # middle table cell: print beg/next/prev/end arrows:
         if not middle_only:
             out += """<td class="searchresultsboxheader" align="center">
                       %(recs_found)s &nbsp;""" % {
                      'recs_found' : _("<strong>%s</strong> records found") % self.tmpl_nice_number(nb_found, ln)
                    }
         else:
             out += "<small>"
             if nb_found > rg:
                 out += "" + collection_name + " : " + _("<strong>%s</strong> records found") % self.tmpl_nice_number(nb_found, ln) + " &nbsp; "
 
         if nb_found > rg: # navig.arrows are needed, since we have many hits
             if (pl_in_url):
               scbis = 1
             else:
               scbis = 0
 
             query = {'p': p, 'f': f,
                      'cc': collection,
                      'sf': sf, 'so': so,
                      'sp': sp, 'rm': rm,
                      'of': of, 'ot': ot,
                      'as': as, 'ln': ln,
                      'p1': p1, 'p2': p2, 'p3': p3,
                      'f1': f1, 'f2': f2, 'f3': f3,
                      'm1': m1, 'm2': m2, 'm3': m3,
                      'op1': op1, 'op2': op2,
                      'sc': scbis,
                      'd1y': d1y, 'd1m': d1m, 'd1d': d1d,
                      'd2y': d2y, 'd2m': d2m, 'd2d': d2d,
                 }
             
             # @todo here
             def img(gif, txt):
                 return '<img src="%(weburl)s/img/%(gif)s.gif" alt="%(txt)s" border="0">' % {
                     'txt': txt, 'gif': gif, 'weburl': weburl}
 
             if jrec-rg > 1:
                 out += a_href(img('sb', _("begin")), _class='img',
                               href=self.build_search_url(query, jrec=1, rg=rg))
                 
             if jrec > 1:
                 out += a_href(img('sp', _("previous")), _class='img',
                               href=self.build_search_url(query, jrec=max(jrec-rg, 1), rg=rg))
                 
             if jrec+rg-1 < nb_found:
                 out += "%d - %d" % (jrec, jrec+rg-1)
             else:
                 out += "%d - %d" % (jrec, nb_found)
 
             if nb_found >= jrec+rg:
                 out += a_href(img('sn', _("next")), _class='img',
                               href=self.build_search_url(query, jrec=jrec+rg, rg=rg))
 
             if nb_found >= jrec+rg+rg:
                 out += a_href(img('se', _("end")), _class='img',
                               href=self.build_search_url(query, jrec=nb_found-rg+1, rg=rg))
                 
 
             # still in the navigation part
             cc = collection
             sc = 0
             for var in ['p', 'cc', 'f', 'sf', 'so', 'of', 'rg', 'as', 'ln', 'p1', 'p2', 'p3', 'f1', 'f2', 'f3', 'm1', 'm2', 'm3', 'op1', 'op2', 'sc', 'd1y', 'd1m', 'd1d', 'd2y', 'd2m', 'd2d']:
                 out += self.tmpl_input_hidden(name = var, value = vars()[var])
             for var in ['ot', 'sp', 'rm']:
                 if vars()[var]:
                     out += self.tmpl_input_hidden(name = var, value = vars()[var])
             if pl_in_url:
                 fieldargs = cgi.parse_qs(pl_in_url)
                 for fieldcode in all_fieldcodes:
                     # get_fieldcodes():
                     if fieldargs.has_key(fieldcode):
                         for val in fieldargs[fieldcode]:
                             out += self.tmpl_input_hidden(name = fieldcode, value = val)
             out += """&nbsp; %(jump)s <input type="text" name="jrec" size="4" value="%(jrec)d">""" % {
                      'jump' : _("jump to record:"),
                      'jrec' : jrec,
                    }
 
         if not middle_only:
             out += "</td>"
         else:
             out += "</small>"
 
         # right table cell: cpu time info
         if not middle_only:
             if cpu_time > -1:
                 out += """<td class="searchresultsboxheader" align="right"><small>%(time)s</small>&nbsp;</td>""" % {
                          'time' : _("Search took %.2f seconds.") % cpu_time,
                        }
             out += "</tr></table>"
         else:
             out += "</div>"
         out += "</form>"
         return out
 
     def tmpl_nice_number(self, number, ln):
         "Returns nicely printed number NUM in language LN using the locale."
         if number is None:
             return None
         # Temporarily switch the numeric locale to the requeted one, and format the number
         # In case the system has no locale definition, use the vanilla form
         ol = locale.getlocale(locale.LC_NUMERIC)
         try:
             locale.setlocale(locale.LC_NUMERIC, self.tmpl_localemap.get(ln, self.tmpl_default_locale))
         except locale.Error:
             return str(number)
         number = locale.format('%d', number, True)
         locale.setlocale(locale.LC_NUMERIC, ol)
         return number
 
     def tmpl_records_format_htmlbrief(self, ln, weburl, rows, relevances_prologue, relevances_epilogue):
         """Returns the htmlbrief format of the records
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'rows' *array* - Parts of the interface to display, in the format:
 
           - 'rows[number]' *int* - The order number
 
           - 'rows[recid]' *int* - The recID
 
           - 'rows[relevance]' *string* - The relevance of the record
 
           - 'rows[record]' *string* - The formatted record
 
           - 'relevances_prologue' *string* - HTML code to prepend the relevance indicator
 
           - 'relevances_epilogue' *string* - HTML code to append to the relevance indicator (used mostly for formatting)
 
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = """
               <form action="%(weburl)s/yourbaskets.py/add" method="post">
               <table>
               """ % {
                 'weburl' : weburl,
               }
 
         for row in rows:
             out += """
                     <tr><td valign="top" align="right" nowrap><input name="recid" type="checkbox" value="%(recid)s">
                     %(number)s.
                    """ % row
             if row['relevance']:
                 out += """<br><div class="rankscoreinfo"><a title="rank score">%(prologue)s%(relevance)s%(epilogue)s</a></div>""" % {
                          'prologue' : relevances_prologue,
                          'epilogue' : relevances_epilogue,
                          'relevance' : row['relevance']
                        }
             out += """</td><td valign="top">%s</td></tr>""" % row['record']
         out += """</table>
                <br><input class="formbutton" type="submit" name="action" value="%(basket)s">
                </form>""" % {
                  'basket' : _("ADD TO BASKET")
                }
         return out
 
     def tmpl_records_format_other(self, ln, weburl, rows, format, url_argd):
         """Returns other formats of the records
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'rows' *array* - Parts of the interface to display, in the format:
 
           - 'rows[record]' *string* - The formatted record
 
           - 'rows[number]' *int* - The order number
 
           - 'rows[recid]' *int* - The recID
 
           - 'rows[relevance]' *string* - The relevance of the record
 
           - 'format' *string* - The current format
 
           - 'url_argd' *string* - Parameters of the search query
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = """ <p><div align="right"><small>%(format)s: """ % {
                 'format' : _("Format")
               }
 
         result = []
         
         for abbrev, name in (('hd', 'HTML'),
                              ('hx', 'BibTeX'),
                              ('xd', 'DC'),
                              ('hm', 'MARC'),
                              ('xm', 'MARCXML')):
             if format == abbrev:
                 result.append(name)
             else:
                 result.append(a_href(name, href=self.build_search_url(url_argd, of=abbrev)))
 
         out += " | ".join(result)
         
         out += "</small></div>"
 
         for row in rows:
             out += row ['record']
 
             if format.startswith("hd"):
                 # do not print further information but for HTML detailed formats
             
                 if row ['creationdate']:
                     out += '''<div class="recordlastmodifiedbox">%(dates)s</div>
                               <p><span class="moreinfo">%(similar)s</span>
                               <form action="%(weburl)s/yourbaskets/add" method="post">
                                 <input name="recid" type="hidden" value="%(recid)s">
                                 <input name="ln" type="hidden" value="%(ln)s">
                                 <br>
                                 <input class="formbutton" type="submit" name="action" value="%(basket)s">
                               </form>
                            ''' % {
                         'dates': _("Record created %s, last modified %s") % (row['creationdate'],
                                                                               row['modifydate']),
                         'weburl': weburl,
                         'recid': row['recid'],
                         'ln': ln,
                         'similar': a_href(_("Similar records"), _class="moreinfo",
                                           href=self.build_search_url(p='recid:%d' % row['recid'], rm='wrd', ln=ln)),
                         'basket' : _("ADD TO BASKET")
                         }
 
                 out += '<table>'
 
                 if row.has_key ('citinglist'):
                     cs = row ['citinglist']
 
                     similar = self.tmpl_print_record_list_for_similarity_boxen(
                         _("Cited by: %s records") % len (cs), cs, ln)
 
                     out += '''
                     <tr><td>
                       %(similar)s&nbsp;%(more)s
                       <br><br>
                     </td></tr>''' % {
                         'more': a_href(_("more"),
                             href=self.build_search_url(p='recid:%d' % row['recid'], rm='cit', ln=ln)),
                         'similar': similar}
 
                 if row.has_key ('cociting'):
                     cs = row ['cociting']
 
                     similar = self.tmpl_print_record_list_for_similarity_boxen (
                         _("Co-cited with: %s records") % len (cs), cs, ln)
 
                     out += '''
                     <tr><td>
                       %(similar)s&nbsp;%(more)s
                       <br>
                     </td></tr>''' % { 'more': a_href(_("more"),
                                             href=self.build_search_url(p='cocitedwith:%d' % row['recid'], ln=ln)),
                                       'similar': similar}
 
                 if row.has_key ('citationhistory'):
                     out += '<tr><td>%s</td></tr>' % row ['citationhistory']
 
                 if row.has_key ('downloadsimilarity'):
                     cs = row ['downloadsimilarity']
 
                     similar = self.tmpl_print_record_list_for_similarity_boxen (
                         _("People who downloaded this document also downloaded:"), cs, ln)
 
                     out += '''
                     <tr><td>%(graph)s</td></tr>
                     <tr><td>%(similar)s</td></tr
                     >''' % { 'weburl': weburl,   'recid': row ['recid'], 'ln': ln,
                              'similar': similar, 'more': _("more"),
                              'graph': row ['downloadhistory']
                              }
 
                 out += '</table>'
 
                 if row.has_key ('viewsimilarity'):
                     out += '<p>&nbsp'
                     out += self.tmpl_print_record_list_for_similarity_boxen (
                         _("People who viewed this page also viewed:"), row ['viewsimilarity'], ln)
 
                 if row.has_key ('reviews'):
                     out += '<p>&nbsp'
                     out += row['reviews']
 
                 if row.has_key ('comments'):
                     out += row['comments']
 
             out += "<p>&nbsp;"
         return out
 
     def tmpl_print_results_overview(self, ln, weburl, results_final_nb_total, cpu_time, results_final_nb, colls, url_args):
         """Prints results overview box with links to particular collections below.
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'results_final_nb_total' *int* - The total number of hits for the query
 
           - 'colls' *array* - The collections with hits, in the format:
 
           - 'coll[code]' *string* - The code of the collection (canonical name)
 
           - 'coll[name]' *string* - The display name of the collection
 
           - 'results_final_nb' *array* - The number of hits, indexed by the collection codes:
 
           - 'cpu_time' *string* - The time the query took
 
           - 'url_args' *string* - The rest of the search query
         """
 
         if len(colls) == 1:
             # if one collection only, print nothing:
             return ""
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         # first find total number of hits:
         out = """<p><table class="searchresultsbox">
                 <thead><tr><th class="searchresultsboxheader">%(founds)s</th></tr></thead>
                 <tbody><tr><td class="searchresultsboxbody"> """ % {
                 'founds' : _("<strong>Results overview:</strong> Found <strong>%s</strong> records in %.2f seconds.") % (self.tmpl_nice_number(results_final_nb_total, ln), cpu_time)
               }
         # then print hits per collection:
         for coll in colls:
             if results_final_nb.has_key(coll['code']) and results_final_nb[coll['code']] > 0:
                 out += '''<strong><a href="#%(coll)s">%(coll_name)s</a></strong>,
                       <a href="#%(coll)s">%(number)s</a><br>''' % {
                         'coll' : urllib.quote(coll['code']),
                         'coll_name' : coll['name'],
                         'number' : _("<strong>%s</strong> records found") % self.tmpl_nice_number(results_final_nb[coll['code']], ln)
                       }
         out += "</td></tr></tbody></table>"
         return out
 
     def tmpl_search_no_boolean_hits(self, ln, nearestterms):
         """No hits found, proposes alternative boolean queries
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'nearestterms' *array* - Parts of the interface to display, in the format:
 
           - 'nearestterms[nbhits]' *int* - The resulting number of hits
 
           - 'nearestterms[url_args]' *string* - The search parameters
 
           - 'nearestterms[p]' *string* - The search terms
 
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = _("Boolean query returned no hits. Please combine your search terms differently.")
 
         out += '''<blockquote><table class="nearesttermsbox" cellpadding="0" cellspacing="0" border="0">'''
         for term, hits, argd in nearestterms:
             out += '''\
             <tr>
               <td class="nearesttermsboxbody" align="right">%(hits)s</td>
               <td class="nearesttermsboxbody" width="15">&nbsp;</td>
               <td class="nearesttermsboxbody" align="left">
                 %(link)s
               </td>
             </tr>''' % {'hits' : hits,
                         'link': a_href(cgi.escape(term), _class="nearestterms",
                                        href=self.build_search_url(argd))}
         out += """</table></blockquote>"""
         return out
 
     def tmpl_similar_author_names(self, authors, ln):
         """No hits found, proposes alternative boolean queries
 
         Parameters:
 
           - 'authors': a list of (name, hits) tuples
           - 'ln' *string* - The language to display
         """
 
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = '''<a name="googlebox"></a>
                  <table class="googlebox"><tr><th colspan="2" class="googleboxheader">%(similar)s</th></tr>''' % {
                 'similar' : _("See also: similar author names")
               }
         for author, hits in authors:
             out += '''\
             <tr>
               <td class="googleboxbody">%(nb)d</td>
               <td class="googleboxbody">%(link)s</td>
             </tr>''' % {'link': a_href(cgi.escape(author), _class="google",
                                        href=self.build_search_url(p=author, f='author', ln=ln)),
                         'nb' : hits}
             
         out += """</table>"""
 
         return out
 
     def tmpl_print_record_detailed(self, recID, ln, weburl):
         """Displays a detailed on-the-fly record
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'recID' *int* - The record id
         """
         # okay, need to construct a simple "Detailed record" format of our own:
         out = "<p>&nbsp;"
         # secondly, title:
         titles = get_fieldvalues(recID, "245__a")
         for title in titles:
             out += "<p><p><center><big><strong>%s</strong></big></center>" % title
         # thirdly, authors:
         authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
         if authors:
             out += "<p><p><center>"
             for author in authors:
                 out += '%s; ' % a_href(cgi.escape(author),
                                        href=self.build_search_url(ln=ln, p=author, f='author'))
             out += "</center>"
         # fourthly, date of creation:
         dates = get_fieldvalues(recID, "260__c")
         for date in dates:
             out += "<p><center><small>%s</small></center>" % date
         # fifthly, abstract:
         abstracts = get_fieldvalues(recID, "520__a")
         for abstract in abstracts:
             out += """<p style="margin-left: 15%%; width: 70%%">
                      <small><strong>Abstract:</strong> %s</small></p>""" % abstract
         # fifthly bis, keywords:
         keywords = get_fieldvalues(recID, "6531_a")
         if len(keywords):
             out += """<p style="margin-left: 15%%; width: 70%%">
                      <small><strong>Keyword(s):</strong>"""
             for keyword in keywords:
                 out += '%s; ' % a_href(cgi.escape(keyword),
                                        href=self.build_search_url(ln=ln, p=keyword, f='keyword'))
                 
             out += '</small>'
         # fifthly bis bis, published in:
         prs_p = get_fieldvalues(recID, "909C4p")
         prs_v = get_fieldvalues(recID, "909C4v")
         prs_y = get_fieldvalues(recID, "909C4y")
         prs_n = get_fieldvalues(recID, "909C4n")
         prs_c = get_fieldvalues(recID, "909C4c")
         for idx in range(0,len(prs_p)):
             out += """<p style="margin-left: 15%%; width: 70%%">
                      <small><strong>Publ. in:</strong> %s"""  % prs_p[idx]
             if prs_v and prs_v[idx]:
                 out += """<strong>%s</strong>""" % prs_v[idx]
             if prs_y and prs_y[idx]:
                 out += """(%s)""" % prs_y[idx]
             if prs_n and prs_n[idx]:
                 out += """, no.%s""" % prs_n[idx]
             if prs_c and prs_c[idx]:
                 out += """, p.%s""" % prs_c[idx]
             out += """.</small>"""
         # sixthly, fulltext link:
         urls_z = get_fieldvalues(recID, "8564_z")
         urls_u = get_fieldvalues(recID, "8564_u")
         for idx in range(0,len(urls_u)):
             link_text = "URL"
             try:
                 if urls_z[idx]:
                     link_text = urls_z[idx]
             except IndexError:
                 pass
             out += """<p style="margin-left: 15%%; width: 70%%">
             <small><strong>%s:</strong> <a href="%s">%s</a></small>""" % (link_text, urls_u[idx], urls_u[idx])
         # print some white space at the end:
         out += "<p><p>"
         return out
 
     def tmpl_print_record_list_for_similarity_boxen(self, title, score_list, ln=cdslang):
         """Print list of records in the "hs" (HTML Similarity) format for similarity boxes.
            FIXME: bad symbol names again, e.g. SCORE_LIST is *not* a list of scores.  Humph.
         """
 
         from invenio.search_engine import print_record
 
         out = '''
         <table><tr><td>
           <table><tr><td class="blocknote">%(title)s</td></tr></table>
         </td>
         <tr><td><table>
         ''' % { 'title': title }
 
         for recid, score in score_list [:5]:
             out += '''
             <tr><td><font class="rankscoreinfo"><a>(%(score)s)&nbsp;</a></font><small>&nbsp;%(info)s</small></td></tr>''' % {
                 'score': score,
                 'info' : print_record(recid, format="hs", ln=ln),
                 }
 
         out += """</table></small></td></tr></table> """
         return out
                               
 
     def tmpl_print_record_brief(self, ln, recID, weburl):
         """Displays a brief record on-the-fly
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'recID' *int* - The record id
         """
         out = ""
 
         # record 'recID' does not exist in format 'format', so print some default format:
         # firstly, title:
         titles = get_fieldvalues(recID, "245__a")
         # secondly, authors:
         authors = get_fieldvalues(recID, "100__a") + get_fieldvalues(recID, "700__a")
         # thirdly, date of creation:
         dates = get_fieldvalues(recID, "260__c")
         # thirdly bis, report numbers:
         rns = get_fieldvalues(recID, "037__a")
         rns = get_fieldvalues(recID, "088__a")
         # fourthly, beginning of abstract:
         abstracts = get_fieldvalues(recID, "520__a")
         # fifthly, fulltext link:
         urls_z = get_fieldvalues(recID, "8564_z")
         urls_u = get_fieldvalues(recID, "8564_u")
 
         return self.tmpl_record_body(
                  weburl = weburl,
                  titles = titles,
                  authors = authors,
                  dates = dates,
                  rns = rns,
                  abstracts = abstracts,
                  urls_u = urls_u,
                  urls_z = urls_z,
                  ln=ln)
 
     def tmpl_print_record_brief_links(self, ln, recID, weburl):
         """Displays links for brief record on-the-fly
 
         Parameters:
 
           - 'ln' *string* - The language to display
 
           - 'weburl' *string* - The base URL for the site
 
           - 'recID' *int* - The record id
         """
         # load the right message language
         _ = gettext_set_language(ln)
 
         out = ""
         if cfg_use_aleph_sysnos:
             alephsysnos = get_fieldvalues(recID, "970__a")
             if len(alephsysnos)>0:
                 alephsysno = alephsysnos[0]
                 out += '<br><span class="moreinfo">%s</span>' % \
                     a_href(_("Detailed record"), _class="moreinfo",
                            href=self.build_search_url(sysno=alephsysno, ln=ln))
             else:
                 out += '<br><span class="moreinfo">%s</span>' % \
                     a_href(_("Detailed record"), _class="moreinfo",
                            href=self.build_search_url(recid=recID, ln=ln))
         else:
             out += '<br><span class="moreinfo">%s</span>' % \
                    a_href(_("Detailed record"), _class="moreinfo",
                           href=self.build_search_url(recid=recID, ln=ln))
 
             out += '<span class="moreinfo"> - %s</span>' % \
                    a_href(_("Similar records"), _class="moreinfo",
                           href=self.build_search_url(p="recid:%d" % recID, rm="wrd", ln=ln))
 
         if cfg_experimental_features:
             out += '<span class="moreinfo"> - %s</span>' % \
                    a_href(_("Cited by"), _class="moreinfo",
                           href=self.build_search_url(p="recid:%d" % recID, rm="cit", ln=ln))
                  
         return out