diff --git a/bin/addons/base/res/res_config.py b/bin/addons/base/res/res_config.py index 6f87e8a83b9..e3b3d2df167 100644 --- a/bin/addons/base/res/res_config.py +++ b/bin/addons/base/res/res_config.py @@ -114,9 +114,97 @@ class res_config_configurable(osv.osv_memory): res_config_configurable() class res_config_installer(osv.osv_memory): - ''' New-style configuration base specialized for modules selection + """ New-style configuration base specialized for addons selection and installation. - ''' + + Basic usage + ----------- + + Subclasses can simply define a number of _columns as + fields.boolean objects. The keys (column names) should be the + names of the addons to install (when selected). Upon action + execution, selected boolean fields (and those only) will be + interpreted as addons to install, and batch-installed. + + Additional addons + ----------------- + + It is also possible to require the installation of an additional + addon set when a specific preset of addons has been marked for + installation (in the basic usage only, additionals can't depend on + one another). + + These additionals are defined through the ``_install_if`` + property. This property is a mapping of a collection of addons (by + name) to a collection of addons (by name) [#]_, and if all the *key* + addons are selected for installation, then the *value* ones will + be selected as well. For example:: + + _install_if = { + ('sale','crm'): ['sale_crm'], + } + + This will install the ``sale_crm`` addon if and only if both the + ``sale`` and ``crm`` addons are selected for installation. + + You can define as many additionals as you wish, and additionals + can overlap in key and value. For instance:: + + _install_if = { + ('sale','crm'): ['sale_crm'], + ('sale','project'): ['project_mrp'], + } + + will install both ``sale_crm`` and ``project_mrp`` if all of + ``sale``, ``crm`` and ``project`` are selected for installation. + + Hook methods + ------------ + + Subclasses might also need to express dependencies more complex + than that provided by additionals. In this case, it's possible to + define methods of the form ``_if_%(name)s`` where ``name`` is the + name of a boolean field. If the field is selected, then the + corresponding module will be marked for installation *and* the + hook method will be executed. + + Hook methods take the usual set of parameters (cr, uid, ids, + context) and can return a collection of additional addons to + install (if they return anything, otherwise they should not return + anything, though returning any "falsy" value such as None or an + empty collection will have the same effect). + + Complete control + ---------------- + + The last hook is to simply overload the ``modules_to_install`` + method, which implements all the mechanisms above. This method + takes the usual set of parameters (cr, uid, ids, context) and + returns a ``set`` of addons to install (addons selected by the + above methods minus addons from the *basic* set which are already + installed) [#]_ so an overloader can simply manipulate the ``set`` + returned by ``res_config_installer.modules_to_install`` to add or + remove addons. + + Skipping the installer + ---------------------- + + Unless it is removed from the view, installers have a *skip* + button which invokes ``action_skip`` (and the ``cancel`` hook from + ``res.config``). Hooks and additionals *are not run* when skipping + installation, even for already installed addons. + + Again, setup your hooks accordinly. + + .. [#] note that since a mapping key needs to be hashable, it's + possible to use a tuple or a frozenset, but not a list or a + regular set + + .. [#] because the already-installed modules are only pruned at + the very end of ``modules_to_install``, additionals and + hooks depending on them *are guaranteed to execute*. Setup + your hooks accordingly. + """ _name = 'res.config.installer' _inherit = 'res.config' @@ -140,7 +228,7 @@ class res_config_installer(osv.osv_memory): context=context) - def _modules_to_install(self, cr, uid, ids, context=None): + def modules_to_install(self, cr, uid, ids, context=None): """ selects all modules to install: * checked boolean fields @@ -210,7 +298,7 @@ class res_config_installer(osv.osv_memory): def execute(self, cr, uid, ids, context=None): modules = self.pool.get('ir.module.module') - to_install = list(self._modules_to_install( + to_install = list(self.modules_to_install( cr, uid, ids, context=context)) self.logger.notifyChannel( 'installer', netsvc.LOG_INFO,