apps_features
This commit is contained in:
parent
004ec42038
commit
1d70e2c81b
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import models
|
||||||
|
from . import wizard
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': 'Odex30-Apps Feature',
|
||||||
|
'version': '18.0.0.1',
|
||||||
|
'summary': 'Enhance Odoo with Addons Path Management and Module History Tracking',
|
||||||
|
'summary': 'Enhance Odoo with Addons Path Management and History Tracking, with Uninstall Restrictions',
|
||||||
|
'description': """
|
||||||
|
This custom module extends Odoo's functionality by introducing features related to Addons Path management and tracking module installation, upgrades, and uninstallations.
|
||||||
|
|
||||||
|
Key Features:
|
||||||
|
- Addons Path Management: Organize and manage Addons Paths with customizable short names and visually distinctive colors.
|
||||||
|
- Module History Tracking: Keep a record of module installation, upgrades, and uninstallations, including the author and timestamp.
|
||||||
|
- Uninstall Restrictions: Admins can set an uninstallation password to add an extra layer of security, preventing unauthorized module removal.
|
||||||
|
|
||||||
|
Note: This module should be used by experienced Odoo administrators to enhance system management and tracking capabilities. The Uninstall Restrictions feature adds an extra layer of security to prevent unauthorized module removal.
|
||||||
|
""",
|
||||||
|
'category': 'Odex30-base',
|
||||||
|
'author': "Expert Co. Ltd.",
|
||||||
|
'website': "http://www.exp-sa.com",
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
|
||||||
|
'depends': ['base'],
|
||||||
|
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'views/ir_module_module_view.xml',
|
||||||
|
'views/ir_module_addons_path_view.xml'
|
||||||
|
,'views/upgrade_button_views.xml'
|
||||||
|
,'wizard/base_module_uninstall_views.xml'
|
||||||
|
,'views/ir_module_history_view.xml',
|
||||||
|
'views/module_multiple_uninstall.xml',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
|
||||||
|
],
|
||||||
|
'qweb': [],
|
||||||
|
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
'application': False,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import ir_module_module
|
||||||
|
from . import ir_module_addons_path
|
||||||
|
from . import ir_module_history
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import random
|
||||||
|
from odoo import models, fields
|
||||||
|
|
||||||
|
|
||||||
|
class IrModuleAddonsPath(models.Model):
|
||||||
|
_name = "ir.module.addons.path"
|
||||||
|
_description = "Addons Path"
|
||||||
|
|
||||||
|
|
||||||
|
def _default_bg_color(self):
|
||||||
|
colors = ['#F06050', '#F4A45F', '#F7CD2E', '#6CC1ED', '#EB7E7F', '#5CC482',
|
||||||
|
'#2c8297', '#D8485E', '#9365B8', '#804967', '#475576', ]
|
||||||
|
res = '#FFFFFF'
|
||||||
|
try:
|
||||||
|
res = random.choice(colors)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return res
|
||||||
|
|
||||||
|
name = fields.Char(string='Short Name')
|
||||||
|
path = fields.Char(string='Full Path')
|
||||||
|
path_temp = fields.Char(string='Shortened Path')
|
||||||
|
color = fields.Char(default=_default_bg_color)
|
||||||
|
module_ids = fields.One2many('ir.module.module', 'addons_path_id')
|
||||||
|
module_count = fields.Integer(compute='_compute_module_count')
|
||||||
|
|
||||||
|
def _compute_module_count(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.module_count = len(rec.module_ids)
|
||||||
|
|
||||||
|
def open_apps_view(self):
|
||||||
|
self.ensure_one()
|
||||||
|
|
||||||
|
return {'type': 'ir.actions.act_window',
|
||||||
|
'name': 'Apps',
|
||||||
|
'view_mode': 'kanban,list,form',
|
||||||
|
'res_model': 'ir.module.module',
|
||||||
|
'context': {},
|
||||||
|
'domain': [('addons_path_id', '=', self.id)],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
|
||||||
|
class IrModuleHistory(models.Model):
|
||||||
|
_name = 'ir.module.history'
|
||||||
|
_description = 'Module History'
|
||||||
|
|
||||||
|
TYPES = [
|
||||||
|
('install', 'Installed'),
|
||||||
|
('upgrade', 'Upgraded'),
|
||||||
|
('uninstall', 'Uninstalled'),
|
||||||
|
]
|
||||||
|
|
||||||
|
module_name = fields.Char(required=True, string='Module')
|
||||||
|
type = fields.Selection(TYPES, required=True, string='Action')
|
||||||
|
user_id = fields.Many2one('res.users', string='Author', required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class IrModuleModule(models.Model):
|
||||||
|
_inherit = 'ir.module.module'
|
||||||
|
|
||||||
|
def _button_immediate_function(self, function):
|
||||||
|
res = super(IrModuleModule, self)._button_immediate_function(function)
|
||||||
|
for module in self:
|
||||||
|
action_type = {
|
||||||
|
'button_install': 'install',
|
||||||
|
'button_upgrade': 'upgrade',
|
||||||
|
'button_uninstall': 'uninstall',
|
||||||
|
}.get(function.__name__)
|
||||||
|
|
||||||
|
if action_type:
|
||||||
|
self.env['ir.module.history'].sudo().create({
|
||||||
|
'module_name': module.name,
|
||||||
|
'type': action_type,
|
||||||
|
'user_id': self._uid,
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
from odoo.modules.module import get_module_path
|
||||||
|
|
||||||
|
|
||||||
|
class IrModule(models.Model):
|
||||||
|
_inherit = "ir.module.module"
|
||||||
|
|
||||||
|
addons_path_id = fields.Many2one('ir.module.addons.path', string='Addons Path Record', readonly=True)
|
||||||
|
addons_path = fields.Char(string='Addons Path', related='addons_path_id.path', readonly=True)
|
||||||
|
|
||||||
|
def module_multiple_uninstall(self):
|
||||||
|
modules_uninstall = self.browse(self.env.context.get('active_ids')).button_immediate_uninstall()
|
||||||
|
|
||||||
|
|
||||||
|
class BaseModuleUpdate(models.TransientModel):
|
||||||
|
_inherit = "base.module.update"
|
||||||
|
|
||||||
|
def update_addons_paths(self):
|
||||||
|
from odoo import addons
|
||||||
|
addons_path_obj = self.env['ir.module.addons.path']
|
||||||
|
ad_paths = addons.__path__
|
||||||
|
|
||||||
|
for path in ad_paths:
|
||||||
|
if not addons_path_obj.search([('path', '=', path)]):
|
||||||
|
path_temp = path
|
||||||
|
|
||||||
|
if len(path_temp) > 42:
|
||||||
|
path_temp = '%s......%s' % (path[:12], path[-19:])
|
||||||
|
|
||||||
|
addons_path_obj.sudo().create({
|
||||||
|
'name': path.split('/')[-1],
|
||||||
|
'path': path,
|
||||||
|
'path_temp': path_temp,
|
||||||
|
})
|
||||||
|
|
||||||
|
for addons_path_id in addons_path_obj.search([]):
|
||||||
|
if addons_path_id.path not in ad_paths:
|
||||||
|
addons_path_id.unlink()
|
||||||
|
|
||||||
|
def update_module_addons_paths(self):
|
||||||
|
addons_path_obj = self.env['ir.module.addons.path']
|
||||||
|
|
||||||
|
for module_id in self.env['ir.module.module'].search([]):
|
||||||
|
module_path = get_module_path(module_id.name)
|
||||||
|
if not module_path:
|
||||||
|
continue
|
||||||
|
addons_path = module_path.rstrip(module_id.name).rstrip('/')
|
||||||
|
addons_path_id = addons_path_obj.search([('path', '=', addons_path)])
|
||||||
|
|
||||||
|
if addons_path_id:
|
||||||
|
module_id.addons_path_id = addons_path_id.id
|
||||||
|
|
||||||
|
def update_module(self):
|
||||||
|
result = super(BaseModuleUpdate, self).update_module()
|
||||||
|
self.sudo().update_addons_paths()
|
||||||
|
self.sudo().update_module_addons_paths()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_ir_module_addons_path,ir.module.addons.path access,model_ir_module_addons_path,base.group_system,1,1,1,1
|
||||||
|
access_ir_module_history,ir.module.history access,model_ir_module_history,base.group_system,1,1,1,1
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!--Kanban-->
|
||||||
|
<record id="view_kanban_ir_module_addons_path" model="ir.ui.view">
|
||||||
|
<field name="name">ir.module.addons.path.kanban</field>
|
||||||
|
<field name="model">ir.module.addons.path</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<kanban class="o_kanban_mobile" create="0" edit="0" delete="0">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="path"/>
|
||||||
|
<field name="path_temp"/>
|
||||||
|
<field name="color"/>
|
||||||
|
<templates>
|
||||||
|
<t t-name="kanban-box">
|
||||||
|
<div t-attf-style="border-left: 3px solid {{record.color.value}}" t-attf-class="oe_kanban_global_click">
|
||||||
|
<div class="oe_kanban_details">
|
||||||
|
<strong class="o_kanban_record_title"><field name="name"/></strong>
|
||||||
|
<ul>
|
||||||
|
<li class="text-muted"><field name="path_temp"/></li>
|
||||||
|
<li>
|
||||||
|
<a name="open_apps_view" href="#" type="object"><field name="module_count"/> Modules</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
|
</kanban>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Tree-->
|
||||||
|
<record id="view_tree_ir_module_addons_path" model="ir.ui.view">
|
||||||
|
<field name="name">ir.module.addons.path.view.tree</field>
|
||||||
|
<field name="model">ir.module.addons.path</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list create="0" edit="0" delete="1">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="path"/>
|
||||||
|
<field name="path_temp" invisible="1"/>
|
||||||
|
<field name="color" invisible="1"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Form-->
|
||||||
|
<record id="view_form_ir_module_addons_path" model="ir.ui.view">
|
||||||
|
<field name="name">ir.module.addons.path.view.form</field>
|
||||||
|
<field name="model">ir.module.addons.path</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form create="0" delete="0">
|
||||||
|
<sheet>
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name" class="oe_edit_only"/>
|
||||||
|
<h1><field name="name" placeholder="My-Addons"/></h1>
|
||||||
|
</div>
|
||||||
|
<group>
|
||||||
|
<field name="path" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Action-->
|
||||||
|
<record id="action_view_ir_module_addons_path" model="ir.actions.act_window">
|
||||||
|
<field name="name">Addons Paths</field>
|
||||||
|
<field name="res_model">ir.module.addons.path</field>
|
||||||
|
<field name="view_mode">kanban,list,form</field>
|
||||||
|
<field name="context">{}</field>
|
||||||
|
<field name="domain">[]</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p>Please Update the Apps List ...</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Menu-->
|
||||||
|
<menuitem name="Addons Paths"
|
||||||
|
id="menu_view_ir_module_addons_path"
|
||||||
|
parent="base.menu_management"
|
||||||
|
action="action_view_ir_module_addons_path"
|
||||||
|
sequence="6"
|
||||||
|
groups="base.group_system"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!--Form-->
|
||||||
|
<record id="view_tree_ir_module_history" model="ir.ui.view">
|
||||||
|
<field name="name">ir.module.history.view.tree</field>
|
||||||
|
<field name="model">ir.module.history</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<list create="0" edit="0" default_order="create_date desc" decoration-danger="type=='uninstall'" delete="0">
|
||||||
|
<field name="create_date" string="Time"/>
|
||||||
|
<field name="module_name"/>
|
||||||
|
<field name="type"/>
|
||||||
|
<field name="user_id"/>
|
||||||
|
</list>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Action-->
|
||||||
|
<record id="action_view_ir_module_history" model="ir.actions.act_window">
|
||||||
|
<field name="name">History</field>
|
||||||
|
<field name="res_model">ir.module.history</field>
|
||||||
|
<field name="view_mode">list</field>
|
||||||
|
<field name="context">{}</field>
|
||||||
|
<field name="domain">[]</field>
|
||||||
|
<field name="help" type="html">
|
||||||
|
<p class="oe_view_nocontent_create">
|
||||||
|
No history
|
||||||
|
</p>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Menu-->
|
||||||
|
<menuitem name="History" id="menu_view_ir_module_history" parent="base.menu_management" action="action_view_ir_module_history" sequence="200" groups="base.group_system"/>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!--Search-->
|
||||||
|
<record id="view_search_ir_module_module_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">ir.module.module.view.search.inherit.app.addons.path</field>
|
||||||
|
<field name="model">ir.module.module</field>
|
||||||
|
<field name="inherit_id" ref="base.view_module_filter"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="/search/group" position="inside">
|
||||||
|
<filter string="Addons Path" name="addons_path" domain="[]" context="{'group_by':'addons_path_id'}"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!--Form-->
|
||||||
|
<record id="view_form_ir_module_module_inherit" model="ir.ui.view">
|
||||||
|
<field name="name">ir.module.module.view.form.inherit.app.addons.path</field>
|
||||||
|
<field name="model">ir.module.module</field>
|
||||||
|
<field name="inherit_id" ref="base.module_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='name']/parent::group/parent::group" position="after">
|
||||||
|
<group groups="base.group_system">
|
||||||
|
<field name="addons_path_id" invisible="1"/>
|
||||||
|
<field name="addons_path"/>
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data>
|
||||||
|
<!--created an action server which appeared in action button with name Modules Un-Install-->
|
||||||
|
<record id="action_server_module_multiple_uninstall" model="ir.actions.server">
|
||||||
|
<field name="name">Modules Un-Install</field>
|
||||||
|
<field name="type">ir.actions.server</field>
|
||||||
|
<field name="model_id" ref="model_ir_module_module"/>
|
||||||
|
<field name="binding_model_id" ref="model_ir_module_module"/>
|
||||||
|
<field name="state">code</field>
|
||||||
|
<field name="code">records.module_multiple_uninstall()</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="custom_upgrade_btn" model="ir.ui.view">
|
||||||
|
<field name="name">Apps Kanban.Inherit.UpgradeButton</field>
|
||||||
|
<field name="model">ir.module.module</field>
|
||||||
|
<field name="inherit_id" ref="base.module_view_kanban"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//div[hasclass('text-muted')][contains(., 'Installed')]" position="attributes">
|
||||||
|
<attribute name="invisible">1</attribute>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//templates/t[@t-name='card']/main/footer" position="inside">
|
||||||
|
<div t-if="record.state.raw_value == 'installed'" class="o_module_action_buttons">
|
||||||
|
<button type="object"
|
||||||
|
class="btn btn-warning btn-sm me-1"
|
||||||
|
name="button_immediate_upgrade"
|
||||||
|
string="Upgrade">
|
||||||
|
<i class="fa fa-upload me-1"/>Upgrade
|
||||||
|
</button>
|
||||||
|
<button type="object"
|
||||||
|
class="btn btn-danger btn-sm"
|
||||||
|
name="button_uninstall_wizard"
|
||||||
|
string="Uninstall">
|
||||||
|
<i class="fa fa-trash me-1"/>Uninstall
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from . import base_module_uninstall
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||||
|
|
||||||
|
from odoo import fields, models, tools
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class BaseModuleUninstall(models.TransientModel):
|
||||||
|
_inherit = "base.module.uninstall"
|
||||||
|
|
||||||
|
is_restrict = fields.Boolean()
|
||||||
|
password = fields.Char(required=True)
|
||||||
|
|
||||||
|
def action_uninstall(self):
|
||||||
|
if not tools.config.get('uninstall_password'):
|
||||||
|
raise ValidationError("Uninstall password not yet set!")
|
||||||
|
if self.password != tools.config.get('uninstall_password').replace('\'', ''):
|
||||||
|
raise ValidationError("Invalid Password!")
|
||||||
|
return super(BaseModuleUninstall, self).action_uninstall()
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_base_module_uninstall_with_password" model="ir.ui.view">
|
||||||
|
<field name="name">Uninstall module</field>
|
||||||
|
<field name="model">base.module.uninstall</field>
|
||||||
|
<field name="inherit_id" ref="base.view_base_module_uninstall"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='show_all']" position="before">
|
||||||
|
<p class='alert alert-danger' role="status">
|
||||||
|
<group>
|
||||||
|
<field name="password" string="Uninstall Password" password="True"/>
|
||||||
|
<hr style="display:none;"/>
|
||||||
|
</group>
|
||||||
|
</p>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Loading…
Reference in New Issue