[ADD] base_install_request: Allow internal users requesting an app install
closes odoo/odoo#99438 Taskid: 2969678 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
@@ -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()
|
||||
@@ -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'
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mail_template_base_install_request" model="mail.template">
|
||||
<field name="name">Mail: Install Request</field>
|
||||
<field name="model_id" ref="base_install_request.model_base_module_install_request"/>
|
||||
<field name="subject">Module Activation Request for "{{ object.module_id.shortdesc }}"</field>
|
||||
<field name="email_from">{{ object.user_id.email_formatted or user.email_formatted }}</field>
|
||||
<field name="partner_to" >{{ ctx['partner'].id}}</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="margin: 0px; padding: 0px;">
|
||||
<p style="margin: 0px; padding: 0px; font-size: 13px;">
|
||||
Hello,
|
||||
<br/><br/>
|
||||
<span style="font-weight: bold;" t-out="object.user_id.name"/> has requested to activate the <span style="font-weight: bold;" t-out="object.module_id.shortdesc"/> module. An administrator role is required to activate it.
|
||||
<br/><br/>
|
||||
<blockquote>
|
||||
<t t-out="object.body_html"/>
|
||||
</blockquote>
|
||||
<br/><br/>
|
||||
<a style="background-color:#875A7B; padding:8px 16px 8px 16px; text-decoration:none; color:#fff; border-radius:5px" t-attf-href="/web?#action=base_install_request.action_base_module_install_review&active_id={{ object.module_id.id }}&menu_id={{ ctx['menu_id'] }}">Review Request</a>
|
||||
<br/><br/>
|
||||
Thanks,
|
||||
<t t-if="not is_html_empty(object.user_id.signature)">
|
||||
<br/><br/>
|
||||
<t t-out="object.user_id.signature or ''">--<br/>Mitchell Admin</t>
|
||||
</t>
|
||||
<br/><br/>
|
||||
</p>
|
||||
</div>
|
||||
</field>
|
||||
<field name="lang">{{ ctx['partner'].lang or user.lang }}</field>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="base_install_request.base_module_install_review_description">
|
||||
<p>The following apps will be installed:</p>
|
||||
<div class="container-fluid">
|
||||
<ul class="list-unstyled row">
|
||||
<t t-foreach="apps" t-as="app">
|
||||
<li class="mt8 col-lg-6" t-if="app.application">
|
||||
<div>
|
||||
<img width="24px" height="24px" class="img-fluid" t-att-src="app.icon"/>
|
||||
<t t-esc="app.shortdesc"/>
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import ir_module_module
|
||||
@@ -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},
|
||||
}
|
||||
@@ -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
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="ir_module_module_view_kanban" model="ir.ui.view">
|
||||
<field name="name">ir.module.module.view.kanban.inherit.mail</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="inherit_id" ref="base.module_view_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<button name="button_immediate_install" position="after">
|
||||
<button type="object" class="btn btn-primary btn-sm" name="action_open_install_request" states="uninstalled" t-if="!record.to_buy.raw_value" groups="!base.group_system">Request Access</button>
|
||||
</button>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="base.menu_management" model="ir.ui.menu">
|
||||
<field name="groups_id" eval="[(5, 0, 0)]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -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
|
||||
@@ -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},
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="base_module_install_request_view_form" model="ir.ui.view">
|
||||
<field name="name">base.module.install.request.view.form</field>
|
||||
<field name="model">base.module.install.request</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<div class="alert alert-warning mb-0" role="alert">
|
||||
<p class="mt-3">
|
||||
This app can be installed, but only an administrator can do it. Fill this form to send an activation request.
|
||||
</p>
|
||||
</div>
|
||||
<form>
|
||||
<field name="module_id" invisible="1"/>
|
||||
<group string="Send to:">
|
||||
<field name="user_ids" widget="many2many_tags" nolabel="1"/>
|
||||
</group>
|
||||
<group string="Why do you need this module ?">
|
||||
<field name="body_html" widget="html" nolabel="1" placeholder="e.g. I'd like to use the SMS Marketing module to organize the promotion of our internal events, and exhibitions. I need access for 3 people of my team."/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Request Activation" class="btn-primary" type="object" name="action_send_request" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="base_module_install_review_view_form" model="ir.ui.view">
|
||||
<field name="name">base.module.install.review.view.form</field>
|
||||
<field name="model">base.module.install.review</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<div class="alert alert-success mb-0" role="alert">
|
||||
<p class="mt-3">
|
||||
An extra cost could be applied to use this application.
|
||||
</p>
|
||||
</div>
|
||||
<form>
|
||||
<field name="module_id" invisible="1"/>
|
||||
<field name="module_ids" widget="many2many_tags" invisible="1"/>
|
||||
<field name="modules_description"/>
|
||||
<footer>
|
||||
<button string="Install App" class="btn-primary" type="object" name="action_install_module" data-hotkey="q"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_base_module_install_review" model="ir.actions.act_window">
|
||||
<field name="name">You are about to install an extra application</field>
|
||||
<field name="res_model">base.module.install.review</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="context">{ 'default_module_id': active_id }</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -73,14 +73,14 @@
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="to_buy" invisible="1"/>
|
||||
<field name="has_iap" invisible="1"/>
|
||||
<button name="button_immediate_install" string="Install" attrs="{'invisible': ['|', ('to_buy','=',True), ('state','!=', 'uninstalled')]}" type="object" class="btn btn-primary me-1"/>
|
||||
<button name="button_immediate_install" string="Install" attrs="{'invisible': ['|', ('to_buy','=',True), ('state','!=', 'uninstalled')]}" type="object" class="btn btn-primary me-1" groups="base.group_system"/>
|
||||
<a href="https://odoo.com/pricing?utm_source=db&utm_medium=module#hosting=on_premise" target="_blank" class="btn btn-primary me-1"
|
||||
attrs="{'invisible': ['|', ('to_buy','=',False), ('state', 'not in', ('uninstalled', 'uninstallable'))]}" role="button">Upgrade</a>
|
||||
<button name="button_immediate_upgrade" states="installed" string="Upgrade" type="object" class="btn btn-primary me-1"/>
|
||||
<button name="button_uninstall_wizard" states="installed" string="Uninstall" type="object" class="btn btn-secondary me-1"/>
|
||||
<button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" type="object" class="btn btn-secondary me-1"/>
|
||||
<button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object" class="btn btn-secondary me-1"/>
|
||||
<button name="button_install_cancel" states="to install" string="Cancel Install" type="object" class="btn btn-secondary me-1"/>
|
||||
<button name="button_immediate_upgrade" states="installed" string="Upgrade" type="object" class="btn btn-primary me-1" groups="base.group_system"/>
|
||||
<button name="button_uninstall_wizard" states="installed" string="Uninstall" type="object" class="btn btn-secondary me-1" groups="base.group_system"/>
|
||||
<button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" type="object" class="btn btn-secondary me-1" groups="base.group_system"/>
|
||||
<button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object" class="btn btn-secondary me-1" groups="base.group_system"/>
|
||||
<button name="button_install_cancel" states="to install" string="Cancel Install" type="object" class="btn btn-secondary me-1" groups="base.group_system"/>
|
||||
</div>
|
||||
<h6 class="text-muted mt-2" attrs="{'invisible': [('has_iap', '=', False)]}">Contains In-App Purchases</h6>
|
||||
</div>
|
||||
@@ -146,7 +146,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<tree create="false" string="Apps" decoration-info="state == 'uninstalled'" decoration-muted="state == 'uninstallable'">
|
||||
<header>
|
||||
<button name="button_immediate_install" type="object" string="Install"/>
|
||||
<button name="button_immediate_install" type="object" string="Install" groups="base.group_system"/>
|
||||
</header>
|
||||
<field name="shortdesc"/>
|
||||
<field name="name" groups="base.group_no_one"/>
|
||||
@@ -184,8 +184,8 @@
|
||||
<div class="dropdown-menu dropdown-menu-end" role="menu" aria-labelledby="dLabel">
|
||||
<a type="edit" class="dropdown-item">Module Info</a>
|
||||
<a t-if="record.website.raw_value" role="menuitem" class="dropdown-item o-hidden-ios" t-att-href="record.website.raw_value" target="_blank">Learn More</a>
|
||||
<a t-if="installed" name="button_immediate_upgrade" type="object" role="menuitem" class="dropdown-item">Upgrade</a>
|
||||
<a t-if="installed" name="button_uninstall_wizard" type="object" role="menuitem" class="dropdown-item">Uninstall</a>
|
||||
<a t-if="installed" name="button_immediate_upgrade" type="object" role="menuitem" class="dropdown-item" groups="base.group_system">Upgrade</a>
|
||||
<a t-if="installed" name="button_uninstall_wizard" type="object" role="menuitem" class="dropdown-item" groups="base.group_system">Uninstall</a>
|
||||
</div>
|
||||
</div>
|
||||
<img t-attf-src="#{record.icon.value}" class="oe_module_icon" alt="Icon"/>
|
||||
@@ -198,13 +198,13 @@
|
||||
<code groups="base.group_no_one"><field name="name"/></code>
|
||||
</p>
|
||||
<div class="oe_module_action d-flex flex-wrap justify-content-between">
|
||||
<button type="object" class="btn btn-primary btn-sm" name="button_immediate_install" states="uninstalled" t-if="! record.to_buy.raw_value">Install</button>
|
||||
<button type="object" class="btn btn-primary btn-sm" name="button_immediate_install" states="uninstalled" t-if="! record.to_buy.raw_value" groups="base.group_system">Activate</button>
|
||||
<div t-if="installed" class="d-flex align-items-center text-muted float-start">Installed</div>
|
||||
<a t-att-href="record.website.raw_value" target="_blank" attrs="{'invisible':[('website', '=', '')]}" class="btn btn-sm btn-secondary float-end o-hidden-ios" role="button">Learn More</a>
|
||||
<a type="edit" class="btn btn-secondary btn-sm float-end" role="button" attrs="{'invisible': [('website', '<>', '')]}">Module Info</a>
|
||||
<a href="https://odoo.com/pricing?utm_source=db&utm_medium=module#hosting=on_premise" target="_blank" class="btn btn-info btn-sm" states="uninstalled,uninstallable" t-if="record.to_buy.raw_value" role="button">Upgrade</a>
|
||||
<button states="to remove" type="object" class="btn btn-sm btn-primary" name="button_uninstall_cancel">Cancel Uninstall</button>
|
||||
<button states="to install" type="object" class="btn btn-sm btn-primary" name="button_install_cancel">Cancel Install</button>
|
||||
<a href="https://odoo.com/pricing?utm_source=db&utm_medium=module#hosting=on_premise" target="_blank" class="btn btn-info btn-sm" states="uninstalled,uninstallable" t-if="record.to_buy.raw_value" role="button" groups="base.group_system">Upgrade</a>
|
||||
<button states="to remove" type="object" class="btn btn-sm btn-primary" name="button_uninstall_cancel" groups="base.group_system">Cancel Uninstall</button>
|
||||
<button states="to install" type="object" class="btn btn-sm btn-primary" name="button_install_cancel" groups="base.group_system">Cancel Install</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user