Welcome | Get started | Dive into Lino | Contribute | Reference
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 *
The 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 : Start Invoicing, 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
- Accounting : Purchase journal (analytic), Analytic Account Balances, Accounting Report, Debtors, Creditors
- VAT : Intra-Community purchases, Intra-Community sales
- Configure :
- System : 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 : Legal forms, 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
- 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 : Authorities, User types, User roles, content types, All dashboard widgets, Data checkers, Data problem messages
- Office : Excerpts, Text Field Templates, Mentions, 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, Sales invoice items, Invoicing plans, Sales rules, Invoicing areas
- Calendar : Calendar entries, Tasks, Presences, Subscriptions, Entry states, Presence states, Task states, Planner columns, 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 setup_menu
method.
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 top-level menus¶
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
menu name).
As a result, each plugin is checked whether it has one of the following methods:
setup_master_menu
setup_main_menu
setup_reports_menu
setup_config_menu
setup_explorer_menu
setup_site_menu
- 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.
Introduction to menu groups¶
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.
Defining menu items¶
The setup_XXX_menu()
methods of a lino.core.plugin.Plugin
are
expected to add menu items to some menu.
The 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')
The lino_xl.lib.invoicing
plugin adds an action "Invoicing plan" to the
"Sales" menu:
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')
The lino.modlib.about
plugin also adds a quick link:
def get_quicklinks(site, user):
yield 'about.SiteSearch'
The resolve_action function¶
- 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.Bar
resolves to the value ofBar
in themodels.py
module of pluginfoo
.foo.Bar.baz
resolves to a bound action whenBar
is an actor, otherwise to the class attributebaz
ofBar
.
The resolved specifier can be:
a database model
an actor
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]`).