Beispiel #1
0
class Users(Specification):
    title = _("Uživatelé")
    help = _(
        "Správa uživatelských účtů, které je poté možno zařazovat do rolí.")
    table = 'cms_users'

    def fields(self):
        return (
            Field('uid',
                  _("UID"),
                  width=5,
                  default=nextval('cms_users_uid_seq')),
            Field('login', _("Přihlašovací jméno"), width=16),
            Field('fullname', _("Celé jméno"), width=40),
            Field('passwd',
                  _("Heslo"),
                  type=pd.Password(not_null=True, minlen=4, md5=True)),
        )

    layout = ('login', 'fullname', 'passwd')
    columns = ('uid', 'login', 'fullname')
    cb = CodebookSpec(display='fullname')
    sorting = (('login', ASC), )

    def bindings(self):
        return (
            Binding('roles', _("Uživatelské role"),
                    self._spec_name('UserRoles'), 'uid'),
            Binding('sessions', _("Historie přihlášení"),
                    self._spec_name('UserSessionLog'), 'uid'),
        )
Beispiel #2
0
class Roles(Specification):
    title = _("Uživatelské role")
    help = _(
        "Správa dostupných uživatelských rolí, které je možné přiřazovat uživatelům."
    )
    table = 'cms_roles'

    def fields(self):
        return (
            Field('role_id', default=nextval('cms_roles_role_id_seq')),
            Field('name', _("Title"), width=16),
            Field('system_role',
                  _("Systémová role"),
                  width=16,
                  type=pd.String(not_null=True)),
            Field('description', _("Description"), width=64),
        )

    layout = ('name', 'description')
    columns = ('name', 'description')
    condition = pd.EQ('system_role', pd.Value(pd.String(), None))

    def bindings(self):
        return (Binding('users', _("Uživatelé zařazení do této role"),
                        self._spec_name('RoleUsers'), 'role_id'), )

    cb = CodebookSpec(display='name')
Beispiel #3
0
class InsuranceFee(Specification):
    """Function table codebook."""
    public = True
    title = _("Fees")
    table = dbdefs.InsuranceFees
    cb = CodebookSpec(display='risk')

    def _customize_fields(self, fields):
        fields.modify_except([], fixed=True)
Beispiel #4
0
class HelpParents(Specification):
    # Codebook of parent items for Help (to prevent recursion).
    public = True
    title = _("Parent item")
    table = 'ev_pytis_help'
    def fields(self): return (
        Field('page_id'),
        Field('position'),
        Field('title', _("Title")),
        )
    sorting = ('position', pd.ASCENDENT),
    columns = ('title',)
    cb = CodebookSpec(display='title', prefer_display=True)
Beispiel #5
0
class Actions(Specification):
    title = _("Dostupné akce")
    help = _("Výčet podporovaných akcí pro jednotlivé moduly.")
    table = 'cms_actions'
    access_rights = pd.AccessRights((None, ('cms_admin', pd.Permission.ALL)),
                                    (None, ('cms_user', pd.Permission.VIEW)))

    def fields(self):
        return (Field('action_id',
                      default=nextval('cms_actions_action_id_seq')),
                Field('mod_id',
                      _("Modul"),
                      not_null=True,
                      codebook=self._spec_name('Modules', False)),
                Field('name', _("Title"), width=16),
                Field('description', _("Description"), width=64))

    sorting = (('action_id', ASC), )
    layout = ('name', 'description')
    columns = ('name', 'description')
    cb = CodebookSpec(display='name')
Beispiel #6
0
class MenuParents(Specification):
    # Codebook of parent items for Menu (to prevent recursion).
    title = _("Men")
    table = 'cms_menu'

    def fields(self):
        return (
            Field('menu_id'),
            Field('menu_item_id'),
            Field('tree_order'),
            Field('lang'),
            Field('title_or_identifier', _("Title"), width=32,
                  type=_TreeOrder),
            Field('identifier', _("Identifier"), width=20),
            Field('modname', _("Modul")),
            #Field('description', _("Description"), width=64),
        )

    sorting = ('tree_order', ASC),
    columns = ('title_or_identifier', 'identifier', 'modname')
    cb = CodebookSpec(display='title_or_identifier', prefer_display=True)
Beispiel #7
0
class Themes(Specification):
    class _Field(Field):
        def __init__(self, id, label, descr=None):
            Field.__init__(self,
                           id,
                           label,
                           descr=descr,
                           type=pd.Color(),
                           dbcolumn=id.replace('-', '_'))

    title = _("Barevné Motivy")
    help = _("Správa barevných motivů.")
    table = 'cms_themes'
    fields = (
        Field('theme_id'),
        Field('name', _("Title"), nocopy=True),
        _Field('foreground',
               _("Text"),
               descr=("Barva popředí hlavních obsahových ploch.")),
        _Field(
            'background',
            _("Pozadí"),
            descr=
            ("Barva pozadí hlavních obsahových ploch (vlastní text stránky, panely)."
             )),
        _Field(
            'highlight-bg',
            _("Zvýrazněné pozadí"),
            descr=
            _("Zvýraznění pomocí barvy pozadí může být použito na různých místech, "
              "např. pro odlišení aktuální položky menu, aktuálního jazyka, nalezeného "
              "záznamu ve formuláři apod.")),
        _Field('link',
               _("Odkaz"),
               descr=_("Barva textu nenavštíveného odkazu.")),
        _Field(
            'link-visited',
            _("Navštívený odkaz"),
            descr=_(
                "Barva textu navštíveného odkazu.  Pokud ponecháte prázdné, bude "
                "použita stejná barva jako u nenavštíveného odkazu.")),
        _Field(
            'link-hover',
            _("Aktivní odkaz"),
            descr=_(
                "Obarvení odkazu při při pohybu myší nad ním.  Pokud ponecháte "
                "prázdné, nebude odkaz na pohyb myši reagovat.")),
        _Field('border', _("Orámování")),
        _Field(
            'heading-fg',
            _("Text"),
            descr=
            _("Barvy nadpisů jsou používány pro zvýrazenění názvů kapitol, panelů a "
              "jiných elementů majících povahu nadpisu či záhlaví.  V závislosti na "
              "použitých stylech mohou některé typy nadpisů používat odlišnou barvu "
              "pozadí, jiné mohou být pouze podtrženy (viz dále).  Barva textu je "
              "však společná pro oba typy nadpisů.")),
        _Field(
            'heading-bg',
            _("Pozadí"),
            descr=
            _("Barva pozadí pro nadpisy, které používají zvýraznění barvou pozadí."
              "Typicky jsou to záhlaví částí stránky (menu, panely apod.) a nadpisy "
              "vyšších úrovní.")),
        _Field(
            'heading-line',
            _("Podtržení"),
            descr=_(
                "Barva podtržení pro nadpisy, které používají zvýraznění podtržením."
                "Typicky jsou to nadpisy nižších úrovní.")),
        _Field(
            'frame-fg',
            _("Text"),
            descr=
            _("Rámy jsou obecně využívány pro odlišení samostatných prvků stránky, "
              "jako jsou například formuláře, nástrojové lišty, rejstříky apod. "
              "Rámy v tomto smyslu nemají nic splečného s HTML rámy (frame).  Pokud "
              "barvu textu nevyplníte, bude použita standardní barva textu (často u "
              "rámů budete chtít nastavit pouze barvu pozadí a orámování).")),
        _Field(
            'frame-bg',
            _("Pozadí"),
            descr=_(
                "Barva pozadí rámů pro jejich vizuální odlišení od ostatního obsahu "
                "stránky.")),
        _Field(
            'frame-border',
            _("Orámování"),
            descr=_(
                "Barva orámování okrajů plochy rámu na přechodu mezi barvou pozadí rámu "
                "a jeho podkladem.")),
        _Field(
            'top-fg',
            _("Text"),
            descr=_(
                "Na podkladových plochách se většinou text přímo nevyskytuje, ale "
                "některé styly to mohou předepisovat.")),
        _Field(
            'top-bg',
            _("Pozadí"),
            descr=
            _("Podkladové plochy tvoří okolí hlavního obsahu stránky a panelů."
              )),
        _Field(
            'top-border',
            _("Orámování"),
            descr=_(
                "Určuje barvu rámečku na přechodu mezi podkladovou plochou a obsahovými "
                "částmi stránky")),
        _Field('error-fg', _("Text")),
        _Field('error-bg', _("Pozadí")),
        _Field('error-border', _("Orámování")),
        _Field('message-fg', _("Text")),
        _Field('message-bg', _("Pozadí")),
        _Field('message-border', _("Orámování")),
        _Field('meta-fg', _("Text")),
        _Field(
            'meta-bg',
            _("Pozadí"),
            descr=_(
                "Odlišení informačního řádku v detailních výpisech, jako např. "
                "informace o datu a autorovi v seznamu článků")),
        _Field('table-cell', _("Standardní pozadí buňky")),
        _Field('table-cell2', _("Stínované pozadí buňky")),
        _Field('help', _("Nápověda formuláře")),
        _Field('inactive-folder', _("Neaktivní záložka")),
    )
    layout = pp.HGroup(
        pp.VGroup(
            'name',
            pp.LVGroup(_("Standardní barvy stránky"),
                       ('foreground', 'background', 'highlight-bg', 'link',
                        'link-visited', 'link-hover', 'border')),
            pp.LVGroup(_("Tabulky"), ('table-cell', 'table-cell2')),
            pp.LVGroup(_("Různé"), ('help', 'inactive-folder')),
        ),
        pp.VGroup(
            pp.LVGroup(_("Barvy nadpisů"),
                       ('heading-fg', 'heading-bg', 'heading-line')),
            pp.LVGroup(_("Rámy"), ('frame-fg', 'frame-bg', 'frame-border')),
            pp.LVGroup(_("Meta data záznam"), ('meta-fg', 'meta-bg')),
            pp.LVGroup(_("Barvy podkladových ploch"),
                       ('top-bg', 'top-border')),
        ),
        pp.VGroup(
            pp.LVGroup(_("Chybové zprávy"),
                       ('error-fg', 'error-bg', 'error-border')),
            pp.LVGroup(_("Informační zprávy"),
                       ('message-fg', 'message-bg', 'message-border')),
        ))
    columns = ('name', )
    cb = CodebookSpec(display='name', prefer_display=True)
Beispiel #8
0
class Modules(Specification):
    title = _("Moduly")
    help = _("Správa rozšiřujících modulů použitelných ve stránkách.")
    table = 'cms_modules'
    access_rights = pd.AccessRights((None, ('cms_admin', pd.Permission.ALL)),
                                    (None, ('cms_user', pd.Permission.VIEW)))

    def fields(self):
        return (
            Field('mod_id', default=nextval('cms_modules_mod_id_seq')),
            Field('modname', _("Title"), width=32),
            Field('descr',
                  _("Description"),
                  width=64,
                  virtual=True,
                  editable=NEVER,
                  computer=computer(self._descr)),
        )

    def _module(self, modname):
        if modname:
            for python_module_name in self._SEARCH_MODULES:
                python_module = __import__(python_module_name)
                for component in python_module_name.split('.')[1:]:
                    python_module = getattr(python_module, component)
                if hasattr(python_module, modname):
                    module = getattr(python_module, modname)
                    import wiking
                    if type(module) == type(wiking.Module) and issubclass(
                            module, wiking.Module):
                        return module
        return None

    def _descr(self, rrecord, modname):
        module = self._module(modname)
        if module:
            return module.descr() or module.title()
        else:
            return None

    sorting = ('modname', ASC),
    cb = CodebookSpec(display='modname', prefer_display=True)
    layout = ('modname', 'descr')
    columns = ('modname', 'descr')

    def bindings(self):
        return (Binding('actions', _("Dostupné akce tohoto modul"),
                        self._spec_name('Actions'), 'mod_id'), )

    def actions(self):
        return (Action('reload', _("Přenačíst dostupné akce"),
                       self._reload_actions), )

    def on_delete_record(self, record):
        import pytis.form
        data = pytis.form.create_data_object(self._spec_name('Menu'))
        count = data.select(condition=pd.EQ('mod_id', record['mod_id']))
        data.close()
        if count:
            return _(
                "Modul je používán v existujících stránkách.\n"
                "Pokud jej chcete vymazat, zrušte nejprve všechny navázané stránky."
            )
        else:
            return True

    #def on_new_record(self, prefill, transaction=None):
    #    import pytis.form
    #    record = pytis.form.new_record(self._spec_name('Modules'), prefill=prefill,
    #                                   block_on_new_record=True, transaction=transaction)
    #    if record:
    #
    #    return record
    def _reload_actions(self, record):
        import wiking

        def action_descr(module, action):
            if issubclass(module, wiking.PytisModule):
                for a in module.Spec.actions:
                    if a.name() == action:
                        return a.descr() or a.title()
            try:
                return dict(self._DEFAULT_ACTIONS)[action]
            except KeyError:
                method = getattr(module, 'action_' + action)
                docstring = method.__doc__
                return docstring and docstring.splitlines()[0] or _(
                    "Neuvedeno")

        module = self._module(record['modname'].value())
        if module:
            from pytis.form import run_dialog, CheckListDialog, create_data_object
            data = create_data_object(self._spec_name('Actions'))
            data.select(condition=pd.EQ('mod_id', record['mod_id']))
            existing_actions = {}
            while True:
                row = data.fetchone()
                if row is None:
                    break
                else:
                    existing_actions[row['name'].value()] = row['action_id']
            data.close()
            actions = [
                attr[7:] for attr in dir(module) if attr.startswith('action_')
                and isinstance(getattr(module, attr), collections.Callable)
            ]
            default_actions = [a[0] for a in self._DEFAULT_ACTIONS]
            # Order default actions first and in the order of self._DEFAULT_ACTIONS.
            order = lambda a: a in default_actions and (default_actions.index(
                a) + 1) or a
            actions.sort(lambda a, b: cmp(order(a), order(b)))
            descriptions = [action_descr(module, action) for action in actions]
            result = run_dialog(
                CheckListDialog,
                title=_("Nalezené akce"),
                message=_("Zaškrtněte akce, které chcete zpřístupnit webovým "
                          "uživatelům:"),
                items=zip([a in existing_actions for a in actions], actions,
                          descriptions),
                columns=(_("Action"), _("Description")))
            if result is not None:
                # TODO: Use a transaction.  Respect existing actions.
                for i, action in enumerate(actions):
                    if result[i]:
                        description_value = pd.Value(pd.String(),
                                                     descriptions[i] or None)
                        try:
                            key = existing_actions[action]
                        except KeyError:
                            rowdata = [('mod_id', record['mod_id']),
                                       ('name', pd.Value(pd.String(), action)),
                                       ('description', description_value)]
                            data.insert(pd.Row(rowdata))
                        else:
                            data.update(
                                (key, ),
                                pd.Row((('description', description_value), )))

    _DEFAULT_ACTIONS = (
        ('view', _("Zobrazení záznam")),
        ('list', _("Výpis všech záznamů")),
        ('export', _("Export tabulky do CSV formát")),
        ('rss', _("Zobrazení RSS kanálů")),
        ('insert', _("Vložení nového záznam")),
        ('update', _("Editace stávajícího záznam")),
        ('delete', _("Smazání záznam")),
        ('print_field', _("Export tisknutelných políček do PDF")),
    )
    _SEARCH_MODULES = ()
    """Defines list of names python modules which should be searched for available Wiking modules.
Beispiel #9
0
 def cb(self):
     return CodebookSpec(display=lambda lang: self._language_name(lang),
                         prefer_display=True)
Beispiel #10
0
class Help(Specification):
    public = True
    table = 'ev_pytis_help'
    title = _("Help")
    def fields(self):
        return (
            Field('help_id',
                  # The computer is only used for help pages (, editable=pp.Editable.NEVER, editable=pp.Editable.NEVER, editable=pp.Editable.NEVER, editable=pp.Editable.NEVER, editable=Editable.NEVERwith page_id) so
                  # we don't need to care about other kinds of help_id.  New
                  # record is always a new page.
                  computer=computer(lambda r, page_id: 'page/%d' % page_id)),
            Field('fullname', _("Fullname"), width=50, editable=Editable.NEVER),
            Field('spec_name', _("Specification Name"), width=50, editable=Editable.NEVER),
            Field('page_id', default=nextval('e_pytis_help_pages_page_id_seq')),
            Field('position'),
            Field('position_nsub'),
            Field('title', _("Title"), width=20, editable=computer(self._is_page),
                  type=_TreeOrderLTree(tree_column_id='position', subcount_column_id='position_nsub'),
                  ),
            Field('description', _("Description"), width=70, editable=computer(self._is_page),),
            Field('content', _("Content"), width=80, height=20, compact=True,
                  text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage),
            Field('menu_help', _(u"Menu item description"), width=80, height=20, compact=True,
                  text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage),
            Field('spec_description', _("Brief form description"), width=80, height=3, compact=True),
            Field('spec_help', _("Detailed form help"), width=80, height=20, compact=True,
                  text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage),
            Field('parent', _("Parent item"), not_null=False,
                  codebook='help.HelpParents', value_column='page_id',
                  editable=computer(self._is_page),
                  runtime_filter=computer(self._parent_filter),
                  descr=_("Choose the directly superordinate item in menu hierarchy. Leave "
                          "empty for pages at the top level menu.")),
            Field('ord', _("Ordering"), width=8, fixed=True, type=pd.Integer, maximum=999998,
                  editable=computer(self._is_page),
                  descr=_("Enter a number denoting the order of the item in menu between "
                          "pages of the same hierarchy level.  Leave empty to put the item "
                          "automatically to bottom.")),
            Field('removed', _("Removed"), editable=Editable.NEVER),
            Field('changed', _("Changed"), editable=Editable.NEVER),
            )
    def row_style(self, row):
        return not row['changed'].value() and pp.Style(background='#ffd') or None

    def _is_page(self, record, page_id):
        return record.new() or page_id is not None

    def _parent_filter(self, page_id):
        return pd.NE('page_id', pd.ival(None))

    def _attachment_storage(self, record):
        if record['page_id'].value():
            table, ref = ('e_pytis_help_pages_attachments', 'page_id')
        elif record['spec_name'].value():
            table, ref = ('e_pytis_help_spec_attachments', 'spec_name')
        else:
            # The attachments are not allowed for some special pages, such as the menu root page.
            return None
        return pp.DbAttachmentStorage(table, ref, record[ref].value())
    
    def redirect(self, record):
        if record['page_id'].value() is not None:
            return None
        elif record['spec_name'].value() is not None:
            return 'help.SpecHelp'
        elif record['fullname'].value() is not None:
            return 'help.MenuHelp'
        else:
            return 'help.NoHelp'
    def bindings(self):
        return (
            Binding('content', _("Content"), uri=lambda r: 'help:'+r['help_id'].value()),
            Binding('fields', _("Fields"), 'help.FieldItemsHelp', 'spec_name'),
            Binding('profiles', _("Profiles"), 'help.ProfileItemsHelp', 'spec_name'),
            Binding('actions', _("Actions"), 'help.ActionItemsHelp', 'spec_name'),
            Binding('bindings', _("Side Forms"), 'help.BindingItemsHelp', 'spec_name'),
            )

    layout = ('title', 'description', 'parent', 'ord', 'content')
    cb = CodebookSpec(display='title')
    columns = ('title', 'description', 'spec_name', 'changed', 'removed')
    sorting = ('position', pd.ASCENDENT),
    profiles = pp.Profiles((pp.Profile('active', _("Active"),
                                       filter=pd.EQ('removed', pd.bval(False)),
                                       columns=('title', 'description', 'spec_name', 'removed')),),
                           default='active')
Beispiel #11
0
class FormProfiles(Specification):
    public = True
    table = 'ev_pytis_form_profiles'
    title = _("Profily formulářů")
    fields = (
        Field('id', _("Identifier"), width=25, editable=Editable.NEVER),
        Field('title', _("Title"), width=25, editable=Editable.NEVER),
        Field('username',
              _("User"),
              not_null=True,
              codebook='statistics.FormUserList',
              value_column='login',
              editable=Editable.NEVER),
        Field('fullname',
              _("Fullname"),
              not_null=True,
              codebook='menu.ApplicationMenuM',
              width=80,
              column_width=30,
              editable=Editable.NEVER),
        Field('spec_name',
              _("Specification Name"),
              width=50,
              column_width=30,
              editable=Editable.NEVER),
        Field('form_name',
              _("Form Class"),
              width=50,
              column_width=30,
              editable=Editable.NEVER),
        Field('profile_id', _("Id profilu"), width=25,
              editable=Editable.NEVER),
        Field('pickled_filter', editable=Editable.NEVER),
        Field('pickled_params', editable=Editable.NEVER),
        Field('dump',
              _("Content"),
              width=80,
              height=8,
              editable=Editable.NEVER),
        Field('errors',
              _("Chyby"),
              width=80,
              height=8,
              editable=Editable.NEVER),
        Field('invalid',
              _("Neplatný"),
              type=pd.Boolean,
              virtual=True,
              width=1,
              computer=computer(lambda r, errors: errors is not None),
              editable=Editable.NEVER),
    )
    cb = CodebookSpec(display='title')
    columns = ('title', 'profile_id', 'username', 'spec_name', 'form_name',
               'invalid')
    layout = HGroup(('title', 'profile_id', 'username'),
                    ('spec_name', 'form_name', 'dump', 'errors'))
    profiles = (
        Profile('invalid-profiles',
                _("Neplatné profily"),
                filter=pd.NE('errors', pd.sval(None))),
        Profile('user-profiles',
                _("Uživatelské profily"),
                filter=pd.WM('profile_id',
                             pd.WMValue(pd.String(), '_user_profile_*'))),
        Profile('system-profiles',
                _("Systémové profily"),
                filter=pd.NW('profile_id',
                             pd.WMValue(pd.String(), '_user_profile_*'))),
    )
Beispiel #12
0
class Continents(Specification):
    """Codebook of continents and their codes.

    Codebook is a regular view.  Any view can be used as a codebook.  It is,
    however, possible to define several properties of a view, which influence
    its use in the codebook context.  They are all covered by the
    'CodebookSpec' instance passed as the 'cb' argument of the specification.
    See the 'CodebookSpec' documentation for more information about the
    available options.

    Using a view as a codebook for a particular field is than done by passing
    its name as the 'codebook' argument in the field specification.  See the
    specification of 'Countries' below for an example.

    """
    public = True
    title = _("Continents")
    table = dbdefs.Continents
    fields = (
        Field('id',
              width=3,
              column_width=6,
              fixed=True,
              filter=TextFilter.ALPHANUMERIC,
              post_process=PostProcess.UPPER),
        Field('name', width=40),
        Field('smallest', codebook='cb.Countries'),
    )
    cb = CodebookSpec(display='name')
    bindings = (
        Binding('all-countries',
                _("All Countries"),
                'cb.Countries',
                'continent',
                search=lambda r: r['id'].value() == 'EU' and pd.sval('CZ') or
                None),
        Binding('islands',
                _("Islands"),
                'cb.Countries',
                'continent',
                condition=lambda r: pd.WM(
                    'name', pd.WMValue(pd.String(), '*island*'))),
        Binding('disabled-countries', _("Disabled Countries"),
                'cb.DisabledCountries', 'continent'),
        Binding('random-numbers',
                _("Random numbers"),
                'misc.RandomNumbers',
                arguments=lambda r: {}),
    )
    prints = (
        (_("Default"),
         'output/None'),  # This spec doen't exist, so defaults are used.
        (_("Countries of the current continent"),
         'cb.PrintContinentCountries'),
        PrintAction('p_countries_continents',
                    _("Countries of the current continent as specification"),
                    'cb.PrintContinentCountries'),
        PrintAction('p_overflow', _("PDF LayoutError"), 'cb.PrintOverflow'),
        (_("Template based"), 'output/Continents'),
        (_("Database template"), 'ContinentsDB'),
        (_("Iterated table"), 'IteratedContinents'),
    )
Beispiel #13
0
class Countries(Specification):
    description = "Codebook of country codes and names."
    help = """

    This view is a codebook and it also uses another codebook -- the field
    'continent' refers to the codebook of continents.

    Link: [form:cb.Continents?select_row=EU]
    Link: [call:demo.defs.cb.pokus?hello=Doly;x=5;b=x;b=3]

    """
    public = True
    title = _("Countries")
    table = dbdefs.Countries
    _continents = 'cb.Continents'  # To be overriden in www/demo.py

    def _customize_fields(self, fields):
        fields.modify_many((
            'id',
            'id3',
            'num',
            'continent',
        ), column_width=6)
        fields.modify_many((
            'id',
            'id3',
            'num',
        ), fixed=True)
        fields.modify_many((
            'id',
            'id3',
        ),
                           filter=TextFilter.ALPHANUMERIC,
                           post_process=PostProcess.UPPER)
        fields.append(
            Field('cname',
                  _("Continent name"),
                  editable=Editable.NEVER,
                  computer=CbComputer('continent', 'name'),
                  virtual=True,
                  type=pd.String()))
        fields.set_property('width',
                            id=2,
                            num=3,
                            continent=2,
                            name=30,
                            fullname=40,
                            cname=20)

    columns = ('id', 'id3', 'continent', 'name')
    cb = CodebookSpec(display='name',
                      columns=('id', 'id3', 'name'),
                      sorting=(('id', pd.ASCENDENT), ))
    sorting = (('id', pd.ASCENDENT), )

    def bindings(self):
        return (
            Binding(
                'continent-details',
                _("Continent details"),
                self._continents,
                'continent',
                single=True,
                descr=
                _("Shows the details of the currently selected main table record."
                  )),
            Binding(
                'other-countries',
                _("Other countries of the same continent"),
                'cb.Countries',
                condition=lambda r: pd.EQ(
                    'continent',
                    r['continent'],
                ),
                prefill=lambda r: {'name': r['name'].value()},
                descr=_(
                    "Displays a table of all countries from the same continent as the "
                    "currently selected main form country.")),
            Binding(
                'webform',
                _("Wikipedia"),
                uri=self._continent_uri,
                descr=
                _("Shows Wikipedia information about the currently selected country."
                  )),
        )

    def _continent_uri(self, record):
        """This method demonstrates usage of web form bindings.

        It returns an URI displayed in the side form browser window for the
        currently selected main form record.  In this case the result is a
        wikipedia article about given country.  The article name may be set
        explicitly through the mapping or it is guessed from the country name.

        """
        mapping = {
            'BV': 'Bouvet_Island',
            'CC': 'Coco_Islands',
            'FK': 'Falkland_Islands',
        }
        country_id = record['id'].value()
        try:
            article_name = mapping[country_id]
        except KeyError:
            article_name = record['name'].value().replace(' ', '_')
        return 'https://en.wikipedia.org/wiki/' + article_name