The XL and application menus¶
We have seen The application menu. You have already read about Lino Extensions Library, a library of shared plugins that are designed to work together. An important aspect of this collaboration is how to integrate the functionalities into the application menu.
This is a tested document. The following instructions are used for initialization:
>>> from lino import startup >>> startup('lino_book.projects.lydia.settings.demo') >>> from lino.api.doctest import *
lino_book.projects.lydia project has the following menu:
>>> show_menu('robin') - Office : My Excerpts, My Upload files - Therapies : My Therapies, -, Individual therapies, Life groups, Other groups, -, My Patients, My Notes - Contacts : Persons, Organizations, Households, Patients, Partner Lists - Calendar : My appointments, Overdue appointments, My unconfirmed appointments, My tasks, My guests, My presences, My overdue appointments, My cash roll, Calendar - Invoicing : Generate invoices, Sales invoices (SLS), Sales credit notes (SLC) - Accounting : - Purchases : Purchase invoices (PRC) - Wages : Paychecks (SAL) - Financial : Bestbank Payment Orders (PMO), Cash book (CSH), Bestbank (BNK) - VAT : VAT declarations (VAT) - Miscellaneous transactions : Miscellaneous transactions (MSC), Preliminary transactions (PRE) - Reports : - Therapies : Status Report - Invoicing : Due invoices, Sales invoice journal - Accounting : Purchase journal (analytic), Analytic Account Balances, Accounting Report, Debtors, Creditors - VAT : Purchase journal, Intra-Community purchases, Intra-Community sales - Configure : - System : Help Texts, Users, Divisions, Site Parameters - Office : Excerpt Types, My Text Field Templates, Library volumes, Upload types - Therapies : Dossier types, Topics, Note Types, Event Types - Places : Countries, Places - Contacts : Organization types, Functions, Household Types, Healthcare plans, Healthcare rules, Procurers, Life modes, List Types - Clients : Client Contact types - Invoicing : Fees, Cash daybooks, Price rules, Paper types, Flatrates, Follow-up rules, Invoicing areas - Calendar : Calendars, Rooms, Recurring events, Guest roles, Service types, Recurrency policies, Remote Calendars, Planner rows - Accounting : Analytical accounts, Sheet items, Accounts, Journals, Fiscal years, Accounting periods, Payment terms - Topics : Topics - Explorer : - System : content types, Authorities, User types, User roles, All dashboard widgets, Data checkers, Data problem messages - Office : Excerpts, Text Field Templates, Upload files, Upload areas - Therapies : Dossiers, Enrolments, Enrolment states, Course layouts, Dossier states, Notes - Contacts : Contact persons, Partners, Household member roles, Household Members, Healthcare tariffs, Healthcare situations, List memberships - Clients : Client Contacts, Known contact types - Invoicing : Price factors, Sales invoices, Product invoice items, Invoicing plans, Sales rules - Calendar : Calendar entries, Tasks, Presences, Subscriptions, Entry states, Presence states, Task states, Planner columns, Access classes, Display colors - SEPA : Bank accounts - Financial : Bank Statements, Journal Entries, Payment Orders - Accounting : Purchase invoices, Accounting Reports, Common sheet items, General account balances, Analytic accounts balances, Partner balances, Sheet item entries, Common accounts, Match rules, Vouchers, Voucher types, Movements, Trade types, Journal groups - Topics : Interests - VAT : Special Belgian VAT declarations, Declaration fields, VAT areas, VAT regimes, VAT classes, VAT columns, Invoices, VAT rules - Site : About, User sessions
This menu is not defined by overriding the
As an application developer you wouldn't want to maintain such a menu for each application.
It was automatically generated by the installed plugins.
Let's explore how this works.
The default implementation of
setup_menu uses a system of predefined top-level
menus that are filled by the different installed plugins.
These predefined top-level menus are themselves configurable in the
top_level_menus attribute of your
application. You might specify your own set of top-level menus.
But the Lino Extensions Library assumes that your application uses the default value, which is:
>>> settings.SITE.top_level_menus [('master', 'Master'), ('main', None), ('reports', 'Reports'), ('config', 'Configure'), ('explorer', 'Explorer'), ('site', 'Site')]
It is a list of tuples with 2 items each. Each tuple has an internal name and a display name. Only the main entry has no display name.
When a Lino application starts up, it loops over the installed plugins and, for
each of them, loops again over these top-level menus and checks whether the
plugin has a method called
setup_XXX_menu() (where XXX is the top-level
As a result, each plugin is checked whether it has one of the following methods:
- Main menu¶
The root menu of an application, which contains a series of submenus called top-level menus.
- Master menu¶
A standard top-level menu meant to contain commands for editing "master data", i.e. data that is visible to normal users but doesn't change very often.
- Configuration menu¶
A standard top-level menu meant to contain commands for editing "configuration data". Actions in this menu usually require some Staff or Admin role.
- Explorer menu¶
A standard top-level menu meant to contain commands for editing "explorer data". These actions are not meant to be generally useful, but might be interesting when exploring database content.
- Site menu¶
A standard top-level menu meant to contain commands about this site.
The different installed plugins (identified by their app_label) are one way to group your database models into different "modules". But this grouping almost never exactly matches how the users would modularize their application.
TODO: write more about it.
setup_XXX_menu() methods of a
expected to add menu items to some menu.
lino.modlib.about plugin adds the "About" action to the "Site" menu:
def setup_site_menu(self, site, user_type, m): m.add_action(site.models.about.About) # or: m.add_action('about.About')
lino_xl.lib.invoicing plugin adds an action "Invoicing plan" to the
def setup_main_menu(self, site, user_type, m): mg = site.plugins.sales m = m.add_menu(mg.app_label, mg.verbose_name) m.add_action('invoicing.MyPlans.start_plan')
lino.modlib.about plugin also adds a quick link:
def get_quicklinks(site, user): yield 'about.SiteSearch'
- lino.core.actors.resolve_action(spec, action=None)¶
Return the bound action corresponding to the given specifier spec and the optional action name.
When the specifier is a string, it is resolved as follows:
foo.Barresolves to the value of
models.pymodule of plugin
foo.Bar.bazresolves to a bound action when
Baris an actor, otherwise to the class attribute
The resolved specifier can be:
a database model
an action instance
a bound action
An action instance will return the bound action on its
Action.defining_actor. A database model will return the default action of the model's default table. An actor will return its default action.
>>> from lino.core.actors import resolve_action >>> resolve_action('contacts.Persons') <BoundAction(contacts.Persons, <lino.core.actions.ShowTable grid>)> >>> resolve_action('contacts.Person') <BoundAction(contacts.Persons, <lino.core.actions.ShowTable grid>)> >>> resolve_action('users.User') <BoundAction(users.AllUsers, <lino.core.actions.ShowTable grid>)>
>>> resolve_action(rt.models.about.About) <BoundAction(about.About, <lino.core.actions.ShowEmptyTable show ('Detail')>)> >>> resolve_action('about.About') <BoundAction(about.About, <lino.core.actions.ShowEmptyTable show ('Detail')>)>
>>> resolve_action('invoicing.Plan', action='start_plan') <BoundAction(invoicing.Plans, <lino_xl.lib.invoicing.actions.StartInvoicing start_plan ('Generate invoices')>)>
>>> resolve_action('foo') Traceback (most recent call last): ... Exception: Invalid action specifier 'foo' (must be of form `plugin_name.ClassName[.action_name]`).
>>> resolve_action('foo.bar.baz.trump') Traceback (most recent call last): ... Exception: Invalid action specifier 'foo.bar.baz.trump' (must be of form `plugin_name.ClassName[.action_name]`).
>>> resolve_action('') Traceback (most recent call last): ... Exception: Invalid action specifier '' (must be of form `plugin_name.ClassName[.action_name]`).