diff --git a/addons/base_install_request/__init__.py b/addons/base_install_request/__init__.py
new file mode 100644
index 00000000000..3ffa90ff3d4
--- /dev/null
+++ b/addons/base_install_request/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
+from . import wizard
+
+from odoo import api, tools, SUPERUSER_ID
+
+
+def _auto_install_apps(cr, registry):
+ if not tools.config.get('default_productivity_apps', True):
+ return
+ env = api.Environment(cr, SUPERUSER_ID, {})
+ env['ir.module.module'].sudo().search([
+ ('name', 'in', [
+ # Community
+ 'board', 'hr', 'mass_mailing', 'project', 'survey',
+ # Enterprise
+ 'appointment', 'knowledge', 'planning', 'sign',
+ ]),
+ ('state', '=', 'uninstalled')
+ ]).button_install()
diff --git a/addons/base_install_request/__manifest__.py b/addons/base_install_request/__manifest__.py
new file mode 100644
index 00000000000..89fa1dba16e
--- /dev/null
+++ b/addons/base_install_request/__manifest__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': 'Base - Module Install Request',
+ 'category': 'Hidden',
+ 'depends': ['mail'],
+ 'description': """
+Allow internal users requesting a module installation
+=====================================================
+ """,
+ 'auto_install': True,
+ 'data':[
+ 'security/ir.model.access.csv',
+ 'wizard/base_module_install_request_views.xml',
+ 'data/mail_template_data.xml',
+ 'data/mail_templates_module_install.xml',
+ 'views/ir_module_module_views.xml',
+ ],
+ 'license': 'LGPL-3',
+ 'post_init_hook': '_auto_install_apps'
+}
diff --git a/addons/base_install_request/data/mail_template_data.xml b/addons/base_install_request/data/mail_template_data.xml
new file mode 100644
index 00000000000..fa0d255baa2
--- /dev/null
+++ b/addons/base_install_request/data/mail_template_data.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ Mail: Install Request
+
+ Module Activation Request for "{{ object.module_id.shortdesc }}"
+ {{ object.user_id.email_formatted or user.email_formatted }}
+ {{ ctx['partner'].id}}
+
+
+
+ Hello,
+
+ has requested to activate the module. An administrator role is required to activate it.
+
+
+
+
+
+
Review Request
+
+ Thanks,
+
+
+ --
Mitchell Admin
+
+
+
+
+
+ {{ ctx['partner'].lang or user.lang }}
+
+
+
+
diff --git a/addons/base_install_request/data/mail_templates_module_install.xml b/addons/base_install_request/data/mail_templates_module_install.xml
new file mode 100644
index 00000000000..e5b5d7f1cfb
--- /dev/null
+++ b/addons/base_install_request/data/mail_templates_module_install.xml
@@ -0,0 +1,18 @@
+
+
+
+ The following apps will be installed:
+
+
+
+ -
+
+
![]()
+
+
+
+
+
+
+
+
diff --git a/addons/base_install_request/models/__init__.py b/addons/base_install_request/models/__init__.py
new file mode 100644
index 00000000000..de58fda6e31
--- /dev/null
+++ b/addons/base_install_request/models/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import ir_module_module
diff --git a/addons/base_install_request/models/ir_module_module.py b/addons/base_install_request/models/ir_module_module.py
new file mode 100644
index 00000000000..494b4c8c43a
--- /dev/null
+++ b/addons/base_install_request/models/ir_module_module.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models, _
+
+
+class IrModuleModule(models.Model):
+ _inherit = 'ir.module.module'
+
+ def action_open_install_request(self):
+ self.ensure_one()
+ return {
+ 'type': 'ir.actions.act_window',
+ 'target': 'new',
+ 'name': _('Activation Request of "%s"', self.shortdesc),
+ 'view_mode': 'form',
+ 'res_model': 'base.module.install.request',
+ 'context': {'default_module_id': self.id},
+ }
diff --git a/addons/base_install_request/security/ir.model.access.csv b/addons/base_install_request/security/ir.model.access.csv
new file mode 100644
index 00000000000..40b57b1c8a2
--- /dev/null
+++ b/addons/base_install_request/security/ir.model.access.csv
@@ -0,0 +1,6 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_base_module_install_request,access_base_module_install_request,model_base_module_install_request,base.group_user,1,1,1,0
+access_base_module_install_review,access_base_module_install_review,model_base_module_install_review,base.group_system,1,1,1,0
+base.access_ir_module_category_group_user,ir_module_category group_user,base.model_ir_module_category,base.group_user,1,0,0,0
+base.access_ir_module_module_group_user,ir_module_module group_user,base.model_ir_module_module,base.group_user,1,0,0,0
+access_ir_module_module_group_system,ir_module_module group_system,base.model_ir_module_module,base.group_system,1,1,1,1
diff --git a/addons/base_install_request/views/ir_module_module_views.xml b/addons/base_install_request/views/ir_module_module_views.xml
new file mode 100644
index 00000000000..7dd872e06a1
--- /dev/null
+++ b/addons/base_install_request/views/ir_module_module_views.xml
@@ -0,0 +1,17 @@
+
+
+
+ ir.module.module.view.kanban.inherit.mail
+ ir.module.module
+
+
+
+
+
+
+
+
diff --git a/addons/base_install_request/wizard/__init__.py b/addons/base_install_request/wizard/__init__.py
new file mode 100644
index 00000000000..7896df3d9f0
--- /dev/null
+++ b/addons/base_install_request/wizard/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import base_module_install_request
diff --git a/addons/base_install_request/wizard/base_module_install_request.py b/addons/base_install_request/wizard/base_module_install_request.py
new file mode 100644
index 00000000000..54e206ddb98
--- /dev/null
+++ b/addons/base_install_request/wizard/base_module_install_request.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class BaseModuleInstallRequest(models.TransientModel):
+ _name = "base.module.install.request"
+ _description = "Module Activation Request"
+ _rec_name = "module_id"
+
+ module_id = fields.Many2one(
+ 'ir.module.module', string="Module", required=True,
+ domain=[('state', '=', "uninstalled")],
+ ondelete='cascade', readonly=True,
+ )
+ user_id = fields.Many2one('res.users', default=lambda self: self.env.user, required=True)
+ user_ids = fields.Many2many('res.users', string="Send to:", compute='_compute_user_ids')
+ body_html = fields.Html('Body')
+
+ @api.depends('module_id')
+ def _compute_user_ids(self):
+ users = self.env.ref('base.group_system').users
+ self.user_ids = [(6, 0, users.ids)]
+
+ def action_send_request(self):
+ mail_template = self.env.ref('base_install_request.mail_template_base_install_request')
+ menu_id = self.env.ref('base.menu_apps').id
+ for user in self.user_ids:
+ render_ctx = dict(self.env.context, partner=user.partner_id, menu_id=menu_id)
+ mail_template.with_context(render_ctx).send_mail(
+ self.id,
+ force_send=True,
+ email_layout_xmlid='mail.mail_notification_light')
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'type': 'success',
+ 'message': _('Your request has been successfully sent'),
+ 'next': {'type': 'ir.actions.act_window_close'},
+ }
+ }
+
+
+class BaseModuleInstallReview(models.TransientModel):
+ _name = "base.module.install.review"
+ _description = "Module Activation Review"
+ _rec_name = "module_id"
+
+ module_id = fields.Many2one(
+ 'ir.module.module', string="Module", required=True,
+ domain=[('state', '=', "uninstalled")],
+ ondelete='cascade', readonly=True,
+ )
+ module_ids = fields.Many2many(
+ 'ir.module.module', string="Depending Apps", compute='_compute_modules_description')
+ modules_description = fields.Html(compute='_compute_modules_description')
+
+ @api.depends('module_id')
+ def _compute_modules_description(self):
+ for wizard in self:
+ apps = wizard._get_depending_apps(wizard.module_id)
+ wizard.module_ids = [(6, 0, apps.ids)]
+ wizard.modules_description = self.env["ir.qweb"]._render(
+ "base_install_request.base_module_install_review_description", {'apps': apps})
+
+ @api.model
+ def _get_depending_apps(self, module):
+ if not module:
+ raise UserError(_('No module selected.'))
+ if module.state == "installed":
+ raise UserError(_('The module is already installed.'))
+ deps = module.upstream_dependencies()
+ apps = module | deps.filtered(lambda d: d.application)
+ for dep in deps:
+ apps |= dep.upstream_dependencies()
+ return apps
+
+ def action_install_module(self):
+ self.ensure_one()
+ self.module_id.button_immediate_install()
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'home',
+ 'params': {'wait': True},
+ }
diff --git a/addons/base_install_request/wizard/base_module_install_request_views.xml b/addons/base_install_request/wizard/base_module_install_request_views.xml
new file mode 100644
index 00000000000..5dd19b38063
--- /dev/null
+++ b/addons/base_install_request/wizard/base_module_install_request_views.xml
@@ -0,0 +1,58 @@
+
+
+
+ base.module.install.request.view.form
+ base.module.install.request
+ form
+
+
+
+ This app can be installed, but only an administrator can do it. Fill this form to send an activation request.
+
+
+
+
+
+
+
+ base.module.install.review.view.form
+ base.module.install.review
+ form
+
+
+
+ An extra cost could be applied to use this application.
+
+
+
+
+
+
+
+ You are about to install an extra application
+ base.module.install.review
+ form
+ { 'default_module_id': active_id }
+ new
+
+
diff --git a/odoo/addons/base/views/ir_module_views.xml b/odoo/addons/base/views/ir_module_views.xml
index 5aeaeda9add..fdaaa71fc55 100644
--- a/odoo/addons/base/views/ir_module_views.xml
+++ b/odoo/addons/base/views/ir_module_views.xml
@@ -73,14 +73,14 @@
-
+
Upgrade
-
-
-
-
-
+
+
+
+
+
Contains In-App Purchases
@@ -146,7 +146,7 @@
@@ -184,8 +184,8 @@
@@ -198,13 +198,13 @@
-
Install
+
Activate
Installed
Learn More
Module Info
-
Upgrade
-
Cancel Uninstall
-
Cancel Install
+
Upgrade
+
Cancel Uninstall
+
Cancel Install