Maciej Wawro
6 years ago
22 changed files with 681 additions and 0 deletions
-
1.gitignore
-
88galicea_environment_checkup/README.rst
-
7galicea_environment_checkup/__init__.py
-
27galicea_environment_checkup/__manifest__.py
-
3galicea_environment_checkup/controllers/__init__.py
-
26galicea_environment_checkup/controllers/dashboard.py
-
0galicea_environment_checkup/environment_checkup/__init__.py
-
49galicea_environment_checkup/environment_checkup/core.py
-
30galicea_environment_checkup/environment_checkup/custom.py
-
181galicea_environment_checkup/environment_checkup/dependencies.py
-
24galicea_environment_checkup/environment_checkup/runtime.py
-
0galicea_environment_checkup/environment_checkup/utils.py
-
1galicea_environment_checkup/models/__init__.py
-
19galicea_environment_checkup/models/ext_module.py
-
BINgalicea_environment_checkup/static/description/custom.png
-
BINgalicea_environment_checkup/static/description/dependencies.png
-
BINgalicea_environment_checkup/static/description/icon.png
-
108galicea_environment_checkup/static/src/js/environment_checkup.js
-
71galicea_environment_checkup/static/src/xml/templates.xml
-
9galicea_environment_checkup/views/data.xml
-
17galicea_environment_checkup/views/environment_checks.xml
-
20galicea_environment_checkup/views/views.xml
@ -0,0 +1 @@ |
|||||
|
*.pyc |
@ -0,0 +1,88 @@ |
|||||
|
About |
||||
|
===== |
||||
|
|
||||
|
This add-on allows you to: |
||||
|
|
||||
|
- programmatically check software dependencies required by your add-on, as well as inform the Administrator as to how to meet them, |
||||
|
- add custom verification for Odoo instance set-up and inform the Administrator about any inconsistencies. |
||||
|
|
||||
|
Dependency checks |
||||
|
================= |
||||
|
.. image:: /galicea_environment_checkup/static/description/dependencies.png |
||||
|
|
||||
|
How-to |
||||
|
------ |
||||
|
Just add ``environment_checkup`` entry to ``__manifest__.py`` |
||||
|
|
||||
|
.. code:: |
||||
|
|
||||
|
{ |
||||
|
... |
||||
|
'environment_checkup': { |
||||
|
'dependencies': { |
||||
|
'python': [ |
||||
|
{ |
||||
|
'name': 'Crypto', |
||||
|
'version': '>=2.6.2', |
||||
|
'install': "pip install 'PyCrypto>=2.6.1'" |
||||
|
}, |
||||
|
], |
||||
|
'external': [ |
||||
|
{ |
||||
|
'name': 'wkhtmltopdf', |
||||
|
'install': "apt install wkhtmltopdf" |
||||
|
}, |
||||
|
{ |
||||
|
'name': 'git', |
||||
|
'version': '^3.0.0', |
||||
|
'install': "apt install git" |
||||
|
} |
||||
|
], |
||||
|
'internal': [ |
||||
|
{ |
||||
|
'name': 'web', |
||||
|
'version': '~10.0.1.0' |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Custom in-code checks |
||||
|
===================== |
||||
|
.. image:: /galicea_environment_checkup/static/description/custom.png |
||||
|
|
||||
|
How-to |
||||
|
------ |
||||
|
|
||||
|
1. Add the check |
||||
|
|
||||
|
``system_checks.py`` |
||||
|
|
||||
|
.. code:: |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import cgi |
||||
|
from odoo.addons.galicea_environment_checkup import custom_check, CheckSuccess, CheckWarning, CheckFail |
||||
|
|
||||
|
@custom_check |
||||
|
def check_mail(env): |
||||
|
users_without_emails = env['res.users'].sudo().search([('email', '=', False)]) |
||||
|
|
||||
|
if users_without_emails: |
||||
|
raise CheckWarning( |
||||
|
'Some users don\'t have their e-mails set up.', |
||||
|
details='See user <tt>{}</tt>.'.format(cgi.escape(users_without_emails[0].name)) |
||||
|
) |
||||
|
|
||||
|
return CheckSuccess('All users have their e-mails set.') |
||||
|
|
||||
|
2. Make sure it's loaded |
||||
|
|
||||
|
``__init__.py`` |
||||
|
|
||||
|
.. code:: |
||||
|
|
||||
|
# -*- coding: utf-8 -*- |
||||
|
from . import system_checks |
@ -0,0 +1,7 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import models |
||||
|
from . import controllers |
||||
|
|
||||
|
from environment_checkup.custom import custom_check |
||||
|
from environment_checkup.core import CheckFail, CheckWarning, CheckSuccess |
@ -0,0 +1,27 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
{ |
||||
|
'name': "Galicea Enviromnent Check-up", |
||||
|
|
||||
|
'summary': """ |
||||
|
Programmatically validate environment, including internal and external |
||||
|
dependencies""", |
||||
|
|
||||
|
'author': "Maciej Wawro", |
||||
|
'maintainer': "Galicea", |
||||
|
'website': "http://galicea.pl", |
||||
|
|
||||
|
'category': 'Technical Settings', |
||||
|
'version': '10.0.1.0', |
||||
|
|
||||
|
'depends': ['web'], |
||||
|
|
||||
|
'data': [ |
||||
|
'views/data.xml', |
||||
|
'views/views.xml', |
||||
|
'views/environment_checks.xml' |
||||
|
], |
||||
|
|
||||
|
'qweb': ['static/src/xml/templates.xml'], |
||||
|
|
||||
|
'installable': True |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from . import dashboard |
@ -0,0 +1,26 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
from odoo import http |
||||
|
from odoo.exceptions import AccessError |
||||
|
from odoo.http import request |
||||
|
|
||||
|
from ..environment_checkup.runtime import all_installed_checks, display_data |
||||
|
from ..environment_checkup.core import CheckResult |
||||
|
|
||||
|
class Dashboard(http.Controller): |
||||
|
@http.route('/galicea_environment_checkup/data', type='json', auth='user') |
||||
|
def data(self, request, **kw): |
||||
|
if not request.env.user.has_group('base.group_erp_manager'): |
||||
|
raise AccessError("Access Denied") |
||||
|
|
||||
|
checks = all_installed_checks(request.env) |
||||
|
response = display_data(request.env, checks) |
||||
|
|
||||
|
priority = { |
||||
|
CheckResult.FAIL: 0, |
||||
|
CheckResult.WARNING: 1, |
||||
|
CheckResult.SUCCESS: 2 |
||||
|
} |
||||
|
response.sort(key=lambda res: (priority[res['result']], res['module'])) |
||||
|
|
||||
|
return response |
@ -0,0 +1,49 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import logging |
||||
|
_logger = logging.getLogger(__name__) |
||||
|
|
||||
|
class CheckResult(object): |
||||
|
SUCCESS = 'success' |
||||
|
WARNING = 'warning' |
||||
|
FAIL = 'fail' |
||||
|
|
||||
|
def __init__(self, result, message, details = None): |
||||
|
super(CheckResult, self).__init__() |
||||
|
|
||||
|
self.result = result |
||||
|
self.message = message |
||||
|
self.details = details |
||||
|
|
||||
|
class CheckSuccess(CheckResult): |
||||
|
def __init__(self, message, **kwargs): |
||||
|
super(CheckSuccess, self).__init__(CheckResult.SUCCESS, message, **kwargs) |
||||
|
|
||||
|
class CheckIssue(CheckResult, Exception): |
||||
|
def __init__(self, result, message, **kwargs): |
||||
|
Exception.__init__(self, message) |
||||
|
CheckResult.__init__(self, result, message, **kwargs) |
||||
|
|
||||
|
class CheckFail(CheckIssue): |
||||
|
def __init__(self, message, **kwargs): |
||||
|
super(CheckFail, self).__init__(CheckResult.FAIL, message, **kwargs) |
||||
|
|
||||
|
class CheckWarning(CheckIssue): |
||||
|
def __init__(self, message, **kwargs): |
||||
|
super(CheckWarning, self).__init__(CheckResult.WARNING, message, **kwargs) |
||||
|
|
||||
|
class Check(object): |
||||
|
def __init__(self, module): |
||||
|
self.module = module |
||||
|
|
||||
|
def run(self, env): |
||||
|
try: |
||||
|
return self._run(env) |
||||
|
except CheckIssue as issue: |
||||
|
return issue |
||||
|
except Exception as ex: |
||||
|
_logger.exception(ex) |
||||
|
return CheckFail('Check failed when processing: {}'.format(ex)) |
||||
|
|
||||
|
def _run(self, env): |
||||
|
raise NotImplementedError('Should be overriden by the subclass') |
@ -0,0 +1,30 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import collections |
||||
|
|
||||
|
from core import Check |
||||
|
|
||||
|
custom_checks_per_module = collections.defaultdict(list) |
||||
|
|
||||
|
class CustomCheck(Check): |
||||
|
def __init__(self, module, func): |
||||
|
super(CustomCheck, self).__init__(module) |
||||
|
self.func = func |
||||
|
|
||||
|
def _run(self, env): |
||||
|
return self.func(env) |
||||
|
|
||||
|
def custom_check(func): |
||||
|
try: |
||||
|
module = func.__module__.split('.')[2] |
||||
|
except IndexError: |
||||
|
module = '' |
||||
|
|
||||
|
custom_checks_per_module[module].append( |
||||
|
CustomCheck(module=module, func=func) |
||||
|
) |
||||
|
|
||||
|
return func |
||||
|
|
||||
|
def get_checks_for_module(module_name): |
||||
|
return custom_checks_per_module[module_name] |
@ -0,0 +1,181 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import subprocess |
||||
|
import re |
||||
|
import cgi |
||||
|
from odoo.modules.module import load_information_from_description_file |
||||
|
from odoo.tools import which |
||||
|
|
||||
|
from core import Check, CheckSuccess, CheckWarning, CheckFail |
||||
|
|
||||
|
class DependencyCheck(Check): |
||||
|
dependency_type = None |
||||
|
|
||||
|
def __init__(self, module, dependency): |
||||
|
super(DependencyCheck, self).__init__(module) |
||||
|
self.dependency = dependency |
||||
|
|
||||
|
def _dependency_installed(self, env, name): |
||||
|
raise NotImplementedError('Should be overriden by the subclass') |
||||
|
|
||||
|
def _installed_version(self, env, name): |
||||
|
raise NotImplementedError('Should be overriden by the subclass') |
||||
|
|
||||
|
def _details(self): |
||||
|
if 'install' in self.dependency: |
||||
|
return 'Install command: <pre>{}</pre>'.format(self.dependency['install']) |
||||
|
return None |
||||
|
|
||||
|
def __has_required_version(self, installed_version, version_expression): |
||||
|
version_operator = '=' |
||||
|
version = self.dependency['version'] |
||||
|
if version[:1] in ['=', '~', '^']: |
||||
|
version_operator = version[:1] |
||||
|
version = version[1:] |
||||
|
elif version[:2] in ['>=']: |
||||
|
version_operator = version[:2] |
||||
|
version = version[2:] |
||||
|
|
||||
|
try: |
||||
|
parsed_version = map(int, version.split('.')) |
||||
|
except ValueError: |
||||
|
raise CheckFail( |
||||
|
'Invalid version expression', |
||||
|
details = """ |
||||
|
Allowed expressions are <pre>=x.y.z</pre>, <pre>>=x.y.z</pre>, <pre>^x.z.y</pre>, |
||||
|
<pre>~x.y.z. Got <pre>{}</pre>""".format(cgi.escape(self.dependency['version'])) |
||||
|
) |
||||
|
parsed_installed_version = map(int, installed_version.split('.')) |
||||
|
|
||||
|
parsed_version.extend(0 for _ in range(len(parsed_installed_version) - len(parsed_version))) |
||||
|
parsed_installed_version.extend(0 for _ in range(len(parsed_version) - len(parsed_installed_version))) |
||||
|
|
||||
|
if version_operator == '^': |
||||
|
if parsed_installed_version[:1] != parsed_version[:1]: |
||||
|
return False |
||||
|
version_operator = '>=' |
||||
|
elif version_operator == '~': |
||||
|
if parsed_installed_version[:2] != parsed_version[:2]: |
||||
|
return False |
||||
|
version_operator = '>=' |
||||
|
|
||||
|
if version_operator == '>=': |
||||
|
return tuple(parsed_installed_version) >= tuple(parsed_version) |
||||
|
elif version_operator == '=': |
||||
|
return tuple(parsed_installed_version) == tuple(parsed_version) |
||||
|
|
||||
|
assert False |
||||
|
|
||||
|
def _run(self, env): |
||||
|
name = self.dependency['name'] |
||||
|
if not self._dependency_installed(env, name): |
||||
|
raise CheckFail( |
||||
|
'Required {} - {} - is not installed.'.format(self.dependency_type, name), |
||||
|
details=self._details() |
||||
|
) |
||||
|
if 'version' in self.dependency: |
||||
|
version_expression = self.dependency['version'] |
||||
|
installed_version = self._installed_version(env, name) |
||||
|
if not self.__has_required_version(installed_version, version_expression): |
||||
|
raise CheckWarning( |
||||
|
'Required {} - {} - has version {}, but {} is needed.'.format( |
||||
|
self.dependency_type, |
||||
|
name, |
||||
|
installed_version, |
||||
|
version_expression |
||||
|
), |
||||
|
details=self._details() |
||||
|
) |
||||
|
return CheckSuccess( |
||||
|
'Required {} - {} - is installed.'.format(self.dependency_type, name), |
||||
|
details=self._details() |
||||
|
) |
||||
|
|
||||
|
class InternalDependencyCheck(DependencyCheck): |
||||
|
dependency_type = 'Odoo module' |
||||
|
|
||||
|
def _dependency_installed(self, env, name): |
||||
|
return name in env.registry._init_modules |
||||
|
|
||||
|
def _installed_version(self, env, name): |
||||
|
return env['ir.module.module'].sudo().search([('name', '=', name)]).latest_version |
||||
|
|
||||
|
class PythonDependencyCheck(DependencyCheck): |
||||
|
dependency_type = 'Python module' |
||||
|
|
||||
|
def _dependency_installed(self, env, name): |
||||
|
try: |
||||
|
__import__(name) |
||||
|
return True |
||||
|
except ImportError: |
||||
|
return False |
||||
|
|
||||
|
def _installed_version(self, env, name): |
||||
|
try: |
||||
|
return __import__(name).__version__ |
||||
|
except AttributeError: |
||||
|
raise CheckWarning( |
||||
|
'Could not detect version of the Python module: {}.'.format(name), |
||||
|
details=self._details() |
||||
|
) |
||||
|
|
||||
|
class ExternalDependencyCheck(DependencyCheck): |
||||
|
dependency_type = 'system executable' |
||||
|
|
||||
|
def _dependency_installed(self, env, name): |
||||
|
try: |
||||
|
which(name) |
||||
|
return True |
||||
|
except IOError: |
||||
|
return False |
||||
|
|
||||
|
def _installed_version(self, env, name): |
||||
|
try: |
||||
|
exe = which(name) |
||||
|
out = subprocess.check_output([exe, '--version']) |
||||
|
match = re.search('[\d.]+', out) |
||||
|
if not match: |
||||
|
raise CheckWarning( |
||||
|
'Unable to detect version for executable {}'.format(name), |
||||
|
details="Command {} --version returned <pre>{}</pre>".format(exe, out) |
||||
|
) |
||||
|
return match.group(0) |
||||
|
except subprocess.CalledProcessError as e: |
||||
|
raise CheckWarning( |
||||
|
'Unable to detect version for executable {}: {}'.format(name, e), |
||||
|
details=self._details() |
||||
|
) |
||||
|
|
||||
|
def get_checks_for_module(module_name): |
||||
|
result = [] |
||||
|
|
||||
|
manifest = load_information_from_description_file(module_name) |
||||
|
manifest_checks = manifest.get('environment_checkup') or {} |
||||
|
dependencies = manifest_checks.get('dependencies') or {} |
||||
|
|
||||
|
for dependency in dependencies.get('python') or []: |
||||
|
result.append(PythonDependencyCheck(module_name, dependency)) |
||||
|
for dependency in dependencies.get('external') or []: |
||||
|
result.append(ExternalDependencyCheck(module_name, dependency)) |
||||
|
for dependency in dependencies.get('internal') or []: |
||||
|
result.append(InternalDependencyCheck(module_name, dependency)) |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
def get_checks_for_module_recursive(module): |
||||
|
class ModuleDFS(object): |
||||
|
def __init__(self): |
||||
|
self.visited_modules = set() |
||||
|
self.checks = [] |
||||
|
|
||||
|
def visit(self, module): |
||||
|
if module.name in self.visited_modules: |
||||
|
return |
||||
|
self.visited_modules.add(module.name) |
||||
|
self.checks += get_checks_for_module(module.name) |
||||
|
for module_dependency in module.dependencies_id: |
||||
|
if module_dependency.depend_id: |
||||
|
self.visit(module_dependency.depend_id) |
||||
|
return self |
||||
|
|
||||
|
return ModuleDFS().visit(module).checks |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import custom, dependencies |
||||
|
|
||||
|
def all_installed_checks(env): |
||||
|
result = [] |
||||
|
installed_modules = env.registry._init_modules |
||||
|
for module_name in installed_modules: |
||||
|
result += custom.get_checks_for_module(module_name) |
||||
|
result += dependencies.get_checks_for_module(module_name) |
||||
|
return result |
||||
|
|
||||
|
def display_data(env, checks): |
||||
|
response = [] |
||||
|
for check in checks: |
||||
|
result = check.run(env) |
||||
|
response.append({ |
||||
|
'module': check.module, |
||||
|
'message': result.message, |
||||
|
'details': result.details, |
||||
|
'result': result.result |
||||
|
}) |
||||
|
|
||||
|
return response |
@ -0,0 +1 @@ |
|||||
|
from . import ext_module |
@ -0,0 +1,19 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
|
import json |
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
from ..environment_checkup import dependencies |
||||
|
from ..environment_checkup.runtime import display_data |
||||
|
|
||||
|
class Module(models.Model): |
||||
|
_inherit = 'ir.module.module' |
||||
|
|
||||
|
dependency_checks = fields.Text( |
||||
|
compute='_compute_dependency_checks' |
||||
|
) |
||||
|
|
||||
|
@api.one |
||||
|
def _compute_dependency_checks(self): |
||||
|
checks = dependencies.get_checks_for_module_recursive(self) |
||||
|
self.dependency_checks = json.dumps(display_data(self.env, checks)) |
After Width: 625 | Height: 198 | Size: 24 KiB |
After Width: 966 | Height: 755 | Size: 92 KiB |
After Width: 600 | Height: 600 | Size: 40 KiB |
@ -0,0 +1,108 @@ |
|||||
|
odoo.define('galicea_environment_checkup', function(require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var core = require('web.core'); |
||||
|
var form_common = require('web.form_common'); |
||||
|
var Widget = require('web.Widget'); |
||||
|
var session = require('web.session'); |
||||
|
var QWeb = core.qweb; |
||||
|
var SystrayMenu = require('web.SystrayMenu'); |
||||
|
var Model = require('web.Model'); |
||||
|
|
||||
|
var Users = new Model('res.users'); |
||||
|
|
||||
|
var SystrayIcon = Widget.extend({ |
||||
|
tagName: 'li', |
||||
|
events: { |
||||
|
"click": "on_click", |
||||
|
}, |
||||
|
|
||||
|
start: function(){ |
||||
|
this.load(this.all_dashboards); |
||||
|
return this._super(); |
||||
|
}, |
||||
|
|
||||
|
load: function(dashboards){ |
||||
|
var self = this; |
||||
|
var loading_done = new $.Deferred(); |
||||
|
Users.call('has_group', ['base.group_erp_manager']).then(function(is_admin) { |
||||
|
if (is_admin) { |
||||
|
session.rpc('/galicea_environment_checkup/data', {}) |
||||
|
.then(function (data) { |
||||
|
var counts = { 'success': 0, 'warning': 0, 'fail': 0 }; |
||||
|
data.forEach(function (check) { ++counts[check.result]; }); |
||||
|
|
||||
|
var result; |
||||
|
if (counts['fail']) { |
||||
|
result = 'fail'; |
||||
|
} else if (counts['warning']) { |
||||
|
result = 'warning'; |
||||
|
} else { |
||||
|
result = 'success'; |
||||
|
} |
||||
|
|
||||
|
self.replaceElement(QWeb.render('GaliceaEnvironmentCheckupIcon', { |
||||
|
'result': result, |
||||
|
'count': counts['warning'] + counts['fail'] |
||||
|
})); |
||||
|
loading_done.resolve(); |
||||
|
}); |
||||
|
} else { |
||||
|
loading_done.resolve(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return loading_done; |
||||
|
}, |
||||
|
|
||||
|
on_click: function (event) { |
||||
|
event.preventDefault(); |
||||
|
this.do_action('galicea_environment_checkup.dashboard_action', {clear_breadcrumbs: true}); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
SystrayMenu.Items.push(SystrayIcon); |
||||
|
|
||||
|
var Dashboard = Widget.extend({ |
||||
|
start: function(){ |
||||
|
return this.load(this.all_dashboards); |
||||
|
}, |
||||
|
|
||||
|
load: function(dashboards) { |
||||
|
var self = this; |
||||
|
var loading_done = new $.Deferred(); |
||||
|
session.rpc('/galicea_environment_checkup/data', {}) |
||||
|
.then(function (data) { |
||||
|
self.replaceElement(QWeb.render('GaliceaEnvironmentCheckupDashboard', {'data': data})); |
||||
|
loading_done.resolve(); |
||||
|
}); |
||||
|
return loading_done; |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
core.action_registry.add('galicea_environment_checkup.dashboard', Dashboard); |
||||
|
|
||||
|
var FormWidget = form_common.AbstractField.extend({ |
||||
|
init: function() { |
||||
|
this._super.apply(this, arguments); |
||||
|
this.set("value", "[]"); |
||||
|
}, |
||||
|
|
||||
|
render_value: function() { |
||||
|
var data = JSON.parse(this.get('value')); |
||||
|
if (data.length == 0) { |
||||
|
this.replaceElement('<div />'); |
||||
|
return; |
||||
|
} |
||||
|
this.replaceElement(QWeb.render('GaliceaEnvironmentCheckupFormWidget', {'data': data})); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
core.form_widget_registry.add('environment_checks', FormWidget); |
||||
|
|
||||
|
return { |
||||
|
SystrayIcon: SystrayIcon, |
||||
|
Dashboard: Dashboard, |
||||
|
FormWidget: FormWidget |
||||
|
}; |
||||
|
}); |
@ -0,0 +1,71 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
|
||||
|
<templates xml:space="preserve"> |
||||
|
<t t-name="GaliceaEnvironmentCheckupIcon"> |
||||
|
<li> |
||||
|
<a href="#" t-att-title="result == 'success' ? 'No system setup issues' : 'Found '+count+' system setup issues'"> |
||||
|
<t t-if="result == 'success'"> |
||||
|
<i class="fa fa-check" style="opacity:0.5"></i> |
||||
|
</t> |
||||
|
<t t-if="result == 'warning'"> |
||||
|
<i class="fa fa-exclamation-triangle" style="color: orange"></i> |
||||
|
</t> |
||||
|
<t t-if="result == 'fail'"> |
||||
|
<i class="fa fa-exclamation-circle" style="color: red"></i> |
||||
|
</t> |
||||
|
<t t-if="result != 'success'"> |
||||
|
<span><t t-raw="count" /></span> |
||||
|
</t> |
||||
|
</a> |
||||
|
</li> |
||||
|
</t> |
||||
|
|
||||
|
<t t-name="GaliceaEnvironmentChecks"> |
||||
|
<div class="row"> |
||||
|
<t t-foreach="data" t-as="check"> |
||||
|
<div class="col-md-12"> |
||||
|
<div style="display: flex; background-color: white; padding: 10px; margin: 10px"> |
||||
|
<div style="flex-grow: 0; flex-shrink: 0; width:50px; margin-right: 20px"> |
||||
|
<t t-if="check.result == 'success'"> |
||||
|
<i class="fa fa-check fa-4x" style="color: green" aria-hidden="true"></i> |
||||
|
</t> |
||||
|
<t t-if="check.result == 'warning'"> |
||||
|
<i class="fa fa-exclamation-triangle fa-4x" style="color: orange" aria-hidden="true"></i> |
||||
|
</t> |
||||
|
<t t-if="check.result == 'fail'"> |
||||
|
<i class="fa fa-exclamation-circle fa-4x" style="color: red" aria-hidden="true"></i> |
||||
|
</t> |
||||
|
</div> |
||||
|
<div style="flex-grow: 1; flex-shrink: 1"> |
||||
|
Module: <t t-esc="check.module" /> |
||||
|
<h4><t t-esc="check.message" /></h4> |
||||
|
<div> |
||||
|
<t t-raw="check.details" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
</div> |
||||
|
</t> |
||||
|
|
||||
|
<t t-name="GaliceaEnvironmentCheckupDashboard"> |
||||
|
<div class="container"> |
||||
|
<div class="row"> |
||||
|
<div class="col-md-12"> |
||||
|
<h2>Environment check-up</h2> |
||||
|
<t t-call="GaliceaEnvironmentChecks"> |
||||
|
<t t-set="data" t-value="data" /> |
||||
|
</t> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
|
||||
|
<t t-name="GaliceaEnvironmentCheckupFormWidget"> |
||||
|
<h1>Module dependencies</h1> |
||||
|
<t t-call="GaliceaEnvironmentChecks"> |
||||
|
<t t-set="data" t-value="data" /> |
||||
|
</t> |
||||
|
</t> |
||||
|
</templates> |
@ -0,0 +1,9 @@ |
|||||
|
<odoo> |
||||
|
<data> |
||||
|
<template id="assets_backend" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script src="/galicea_environment_checkup/static/src/js/environment_checkup.js" type="text/javascript" /> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,17 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<template id="stale_modules"> |
||||
|
<p>The following modules need to be upgraded:</p> |
||||
|
<ul> |
||||
|
<t t-foreach="modules" t-as="module"> |
||||
|
<li> |
||||
|
<a t-att-href="'#id={}&view_type=form&model=ir.module.module'.format(module.id)"> |
||||
|
<t t-esc="module.shortdesc" /> (technical name: <t t-esc="module.name" />; |
||||
|
version on disk: <strong><t t-esc="module.installed_version" /></strong>; |
||||
|
version in DB: <strong><t t-esc="module.latest_version" /></strong>) |
||||
|
</a> |
||||
|
</li> |
||||
|
</t> |
||||
|
</ul> |
||||
|
</template> |
||||
|
</odoo> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<record id="dashboard_action" model="ir.actions.client"> |
||||
|
<field name="name">Environment check-up dashboard</field> |
||||
|
<field name="tag">galicea_environment_checkup.dashboard</field> |
||||
|
</record> |
||||
|
<menuitem name="Environment check-up" id="dashboard_menu" |
||||
|
action="dashboard_action" parent="base.menu_administration" groups="base.group_erp_manager" /> |
||||
|
|
||||
|
<record id="module_form" model="ir.ui.view"> |
||||
|
<field name="name">module_form.checks</field> |
||||
|
<field name="model">ir.module.module</field> |
||||
|
<field name="inherit_id" ref="base.module_form"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<field name="description_html" position="before"> |
||||
|
<field name="dependency_checks" widget="environment_checks" /> |
||||
|
</field> |
||||
|
</field> |
||||
|
</record> |
||||
|
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue