Compare commits
2 Commits
12.0
...
14.0-patch
Author | SHA1 | Message | Date |
---|---|---|---|
default | 02ab58d24f |
[FIX] allow ``/oauth/userinfo`` GET request with json content-type
This is required by some services (like ``outline``) |
7 months ago |
Nicolas JEUDY | fdc76e8fef |
[MIGRATE] 14.0 migrate
|
3 years ago |
-
1galicea_base/README.md
-
2galicea_base/__init__.py
-
23galicea_base/__manifest__.py
-
6galicea_base/views/base_menu.xml
-
1galicea_environment_checkup/README.md
-
9galicea_environment_checkup/__init__.py
-
32galicea_environment_checkup/__manifest__.py
-
3galicea_environment_checkup/controllers/__init__.py
-
26galicea_environment_checkup/controllers/dashboard.py
-
4galicea_environment_checkup/environment_checkup/__init__.py
-
49galicea_environment_checkup/environment_checkup/core.py
-
30galicea_environment_checkup/environment_checkup/custom.py
-
183galicea_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/icon.png
-
BINgalicea_environment_checkup/static/description/images/custom_screenshot.png
-
BINgalicea_environment_checkup/static/description/images/dependencies_screenshot.png
-
79galicea_environment_checkup/static/description/index.html
-
146galicea_environment_checkup/static/src/js/environment_checkup.js
-
108galicea_environment_checkup/static/src/js/environment_checkup10.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
-
1galicea_git/README.md
-
5galicea_git/__init__.py
-
48galicea_git/__manifest__.py
-
3galicea_git/controllers/__init__.py
-
86galicea_git/controllers/main.py
-
12galicea_git/data/config.xml
-
63galicea_git/http_chunked_fix.py
-
4galicea_git/models/__init__.py
-
42galicea_git/models/config_settings.py
-
105galicea_git/models/repository.py
-
3galicea_git/security/ir.model.access.csv
-
47galicea_git/security/security.xml
-
BINgalicea_git/static/description/icon.png
-
BINgalicea_git/static/description/images/config_screenshot.png
-
BINgalicea_git/static/description/images/console_screenshot.png
-
BINgalicea_git/static/description/images/create_screenshot.png
-
19galicea_git/static/description/index.html
-
37galicea_git/system_checks.py
-
71galicea_git/views/views.org.xml
-
43galicea_git/views/views.xml
-
1galicea_git_oauth/README.md
-
4galicea_git_oauth/__init__.py
-
21galicea_git_oauth/__manifest__.py
-
1galicea_git_oauth/controllers/__init__.py
-
14galicea_git_oauth/controllers/ext_git_main.py
-
1galicea_git_oauth/models/__init__.py
-
24galicea_git_oauth/models/ext_repository.py
-
5galicea_openapi/__init__.py
-
33galicea_openapi/__manifest__.py
-
4galicea_openapi/controllers/__init__.py
-
56galicea_openapi/controllers/api.py
-
22galicea_openapi/doc/helloworld.py
-
41galicea_openapi/doc/test1.py
-
3galicea_openapi/models/__init__.py
-
52galicea_openapi/openapi.py
-
2galicea_openapi/security/ir.model.access.csv
-
BINgalicea_openapi/static/description/icon.png
-
1galicea_openid_connect/__init__.py
-
69galicea_openid_connect/__manifest__.py
-
27galicea_openid_connect/controllers/main.py
-
51galicea_openid_connect/models/client.py
-
35galicea_openid_connect/models/config_parameter.py
-
33galicea_openid_connect/security/__init__.py
-
24galicea_openid_connect/system_checks.py
-
16galicea_openid_connect/views/views.xml
-
30galicea_toolset/README.md
-
1galicea_toolset/__init__.py
-
20galicea_toolset/__manifest__.py
-
1galicea_toolset/static/src/js/.#one2many_flexible_widget.js
-
38galicea_toolset/static/src/js/client_actions.js
-
49galicea_toolset/static/src/js/iframe_widget.js
-
68galicea_toolset/static/src/js/one2many_flexible_widget.js
-
16galicea_toolset/utils.py
-
11galicea_toolset/views/data.xml
@ -1 +0,0 @@ |
|||||
Base menu for Odoo Galicea Ecosystem |
|
@ -1,2 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
@ -1,23 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
{ |
|
||||
'name': "Base menu for Odoo Galicea Ecosystem", |
|
||||
|
|
||||
'summary': """ |
|
||||
Menu only |
|
||||
""", |
|
||||
|
|
||||
'author': "Jurek Wawro", |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://galicea.pl", |
|
||||
|
|
||||
'category': 'Technical Settings', |
|
||||
'version': '12.0.1.0', |
|
||||
|
|
||||
'depends': ['web',], |
|
||||
|
|
||||
'data': [ |
|
||||
'views/base_menu.xml', |
|
||||
], |
|
||||
|
|
||||
'installable': True |
|
||||
} |
|
@ -1,6 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<odoo> |
|
||||
<menuitem name="Galicea" id="galicea_admin_menu" |
|
||||
parent="base.menu_administration" groups="base.group_erp_manager" /> |
|
||||
|
|
||||
</odoo> |
|
@ -1 +0,0 @@ |
|||||
[See add-on page on odoo.com](https://apps.odoo.com/apps/modules/10.0/galicea_environment_checkup/) |
|
@ -1,9 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import models |
|
||||
|
|
||||
from .environment_checkup.custom import custom_check |
|
||||
from .environment_checkup.core import CheckFail, CheckWarning, CheckSuccess |
|
||||
|
|
||||
from . import controllers |
|
||||
|
|
@ -1,32 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
{ |
|
||||
'name': "Galicea Environment Check-up", |
|
||||
|
|
||||
'summary': """ |
|
||||
Programmatically validate environment, including internal and external |
|
||||
dependencies""", |
|
||||
|
|
||||
'author': "Maciej Wawro", |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://galicea.pl", |
|
||||
|
|
||||
'category': 'Technical Settings', |
|
||||
'version': '12.0.1.0', |
|
||||
|
|
||||
'depends': ['web','galicea_base',], |
|
||||
|
|
||||
'data': [ |
|
||||
'views/data.xml', |
|
||||
'views/views.xml', |
|
||||
'views/environment_checks.xml' |
|
||||
], |
|
||||
|
|
||||
'qweb': ['static/src/xml/templates.xml'], |
|
||||
|
|
||||
'images': [ |
|
||||
'static/description/images/custom_screenshot.png', |
|
||||
'static/description/images/dependencies_screenshot.png' |
|
||||
], |
|
||||
|
|
||||
'installable': True |
|
||||
} |
|
@ -1,3 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import dashboard |
|
@ -1,26 +0,0 @@ |
|||||
# -*- 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 |
|
@ -1,4 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import core |
|
||||
from . import custom |
|
@ -1,49 +0,0 @@ |
|||||
# -*- 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') |
|
@ -1,30 +0,0 @@ |
|||||
# -*- 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] |
|
@ -1,183 +0,0 @@ |
|||||
# -*- 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:] |
|
||||
|
|
||||
# Py3 : map -> list(map |
|
||||
# https://stackoverflow.com/questions/33717314/attributeerror-map-obejct-has-no-attribute-index-python-3 |
|
||||
try: |
|
||||
parsed_version = list(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 = list(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 = str(subprocess.check_output([exe, '--version'])) # Py3 str() |
|
||||
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 |
|
@ -1,24 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . 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 |
|
@ -1 +0,0 @@ |
|||||
from . import ext_module |
|
@ -1,19 +0,0 @@ |
|||||
# -*- 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)) |
|
Before Width: 80 | Height: 80 | Size: 3.4 KiB |
Before Width: 625 | Height: 198 | Size: 24 KiB |
Before Width: 966 | Height: 755 | Size: 92 KiB |
@ -1,79 +0,0 @@ |
|||||
<section class="oe_container"> |
|
||||
<div class="oe_row oe_spaced"> |
|
||||
<div class="oe_span12"> |
|
||||
<h2 class="oe_slogan">Galicea Environment Check-up</h2> |
|
||||
<h3 class="oe_slogan"> |
|
||||
Programmatically validate Odoo environment, including internal and external dependencies of your add-on |
|
||||
</h3> |
|
||||
This add-on allows you to: |
|
||||
<ul> |
|
||||
<li>programmatically check software dependencies required by your add-on, as well as inform the Administrator as to how to meet them,</li> |
|
||||
<li>add custom verification for Odoo instance set-up and inform the Administrator about any inconsistencies.</li> |
|
||||
</ul> |
|
||||
<h2>Add-on dependency verification</h2> |
|
||||
<img class="oe_picture oe_screenshot" src="images/dependencies_screenshot.png" /> |
|
||||
<h3>How-to</h3> |
|
||||
Just add <tt>'environment_checkup'</tt> entry to <tt>__manifest__.py</tt>. |
|
||||
<pre> |
|
||||
{ |
|
||||
... |
|
||||
'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' |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
</pre> |
|
||||
<h2>Custom environment verification</h2> |
|
||||
<img class="oe_picture oe_screenshot" src="images/custom_screenshot.png" /> |
|
||||
<h3>How-to</h3> |
|
||||
1. Add the check, e.g. in the <tt>system_checks.py</tt> file: |
|
||||
<pre> |
|
||||
# -*- 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.') |
|
||||
</pre> |
|
||||
2. Make sure it's loaded by <tt>__init__.py</tt> |
|
||||
<pre> |
|
||||
# -*- coding: utf-8 -*- |
|
||||
from . import system_checks |
|
||||
</pre> |
|
||||
</div> |
|
||||
</div> |
|
||||
</section> |
|
@ -1,146 +0,0 @@ |
|||||
odoo.define('galicea_environment_checkup', function (require) { |
|
||||
"use strict"; |
|
||||
|
|
||||
//var SystrayMenu = require('web.SystrayMenu');
|
|
||||
//var Model = require('web.Model');
|
|
||||
|
|
||||
var AbstractAction = require('web.AbstractAction'); |
|
||||
var core = require('web.core'); |
|
||||
//var framework = require('web.framework');
|
|
||||
var session = require('web.session'); |
|
||||
//var Widget = require('web.Widget');
|
|
||||
//////////////////
|
|
||||
var QWeb = core.qweb; |
|
||||
//var _t = core._t;
|
|
||||
|
|
||||
/* SystrayIcon - nie działa poprawnie ??? |
|
||||
//https://www.odoo.com/documentation/12.0/reference/javascript_reference.html
|
|
||||
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}); |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
*/ |
|
||||
|
|
||||
/////////////////////////////
|
|
||||
var Dashboard = AbstractAction.extend({ |
|
||||
// v.10 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})); // v.10: self.replaceElement
|
|
||||
loading_done.resolve(); |
|
||||
}); |
|
||||
return loading_done; |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
//!JW - nowa propozycja: core.action_registry.add('galicea_environment_checkup.environment_checkup', Dashboard);
|
|
||||
core.action_registry.add('galicea_environment_checkup.dashboard', Dashboard); |
|
||||
////////////////////
|
|
||||
/* v.10 |
|
||||
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); |
|
||||
*/ |
|
||||
var FormView = require('web.FormView'); |
|
||||
|
|
||||
var FormWidget = FormView.extend({ |
|
||||
|
|
||||
template: "environment_checks", |
|
||||
|
|
||||
init: function() { |
|
||||
this._super.apply(this, arguments); |
|
||||
this.set("value", "[]"); |
|
||||
}, |
|
||||
|
|
||||
events: { |
|
||||
}, |
|
||||
|
|
||||
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})); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
////////////////////
|
|
||||
return { |
|
||||
//!! SystrayIcon: SystrayIcon,
|
|
||||
Dashboard: Dashboard, |
|
||||
FormWidget: FormWidget |
|
||||
}; |
|
||||
|
|
||||
}); |
|
@ -1,108 +0,0 @@ |
|||||
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 |
|
||||
}; |
|
||||
}); |
|
@ -1,71 +0,0 @@ |
|||||
<?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" ></i> |
|
||||
</t> |
|
||||
<t t-if="check.result == 'warning'"> |
|
||||
<i class="fa fa-exclamation-triangle fa-4x" style="color: orange" ></i> |
|
||||
</t> |
|
||||
<t t-if="check.result == 'fail'"> |
|
||||
<i class="fa fa-exclamation-circle fa-4x" style="color: red" ></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> |
|
@ -1,9 +0,0 @@ |
|||||
<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> |
|
@ -1,17 +0,0 @@ |
|||||
<?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> |
|
@ -1,20 +0,0 @@ |
|||||
<?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="galicea_base.galicea_admin_menu" 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> |
|
@ -1 +0,0 @@ |
|||||
[See add-on page on odoo.com](https://apps.odoo.com/apps/modules/10.0/galicea_git/) |
|
@ -1,5 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import controllers |
|
||||
from . import models |
|
||||
from . import system_checks |
|
@ -1,48 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
{ |
|
||||
'name': "Galicea Git hosting", |
|
||||
|
|
||||
'summary': """Git repository hosting and per-user access checking""", |
|
||||
|
|
||||
'author': "Maciej Wawro", |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://galicea.pl", |
|
||||
|
|
||||
'category': 'Technical Settings', |
|
||||
'version': '12.0.0.2', |
|
||||
|
|
||||
'depends': ['web', 'galicea_environment_checkup','galicea_base'], |
|
||||
|
|
||||
'external_dependencies': { |
|
||||
'bin': ['git'] |
|
||||
}, |
|
||||
|
|
||||
'data': [ |
|
||||
'security/security.xml', |
|
||||
'security/ir.model.access.csv', |
|
||||
|
|
||||
'data/config.xml', |
|
||||
'views/views.xml', |
|
||||
], |
|
||||
|
|
||||
'images': [ |
|
||||
'static/description/images/create_screenshot.png', |
|
||||
'static/description/images/config_screenshot.png', |
|
||||
'static/description/images/console_screenshot.png', |
|
||||
], |
|
||||
|
|
||||
'application': True, |
|
||||
'installable': True, |
|
||||
|
|
||||
'environment_checkup': { |
|
||||
'dependencies': { |
|
||||
'external': [ |
|
||||
{ |
|
||||
'name': 'git', |
|
||||
'version': '>=2.1.4', |
|
||||
'install': "apt install git" |
|
||||
} |
|
||||
] |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,3 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import main |
|
@ -1,86 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
import subprocess, os, io |
|
||||
|
|
||||
from odoo import http |
|
||||
from odoo.tools import config |
|
||||
import werkzeug |
|
||||
|
|
||||
from ..http_chunked_fix import http_input_stream |
|
||||
|
|
||||
class Main(http.Controller): |
|
||||
def authorize(self, request): |
|
||||
auth = request.httprequest.authorization |
|
||||
if auth: |
|
||||
request.session.authenticate(request.session.db, auth.username, auth.password) |
|
||||
|
|
||||
@http.route( |
|
||||
[ |
|
||||
'/git/<repo>', |
|
||||
'/git/<repo>/<path:path>', |
|
||||
], |
|
||||
auth='public', |
|
||||
csrf=False |
|
||||
) |
|
||||
def git(self, request, repo, **kw): |
|
||||
self.authorize(request) |
|
||||
if not request.env.uid or request.env.user.login == 'public': |
|
||||
return werkzeug.Response( |
|
||||
headers=[('WWW-Authenticate', 'Basic')], |
|
||||
status=401 |
|
||||
) |
|
||||
|
|
||||
try: |
|
||||
repository = request.env['galicea_git.repository'].search( |
|
||||
[('system_name', '=', repo)] |
|
||||
) |
|
||||
except AccessError: |
|
||||
return werkzeug.Response( |
|
||||
status=403 |
|
||||
) |
|
||||
if not repository.exists(): |
|
||||
return werkzeug.Response( |
|
||||
status=404 |
|
||||
) |
|
||||
|
|
||||
http_environment = request.httprequest.environ |
|
||||
git_env = { |
|
||||
'REQUEST_METHOD': http_environment['REQUEST_METHOD'], |
|
||||
'QUERY_STRING': http_environment['QUERY_STRING'], |
|
||||
'CONTENT_TYPE': request.httprequest.headers.get('Content-Type'), |
|
||||
'REMOTE_ADDR': http_environment['REMOTE_ADDR'], |
|
||||
'GIT_PROJECT_ROOT': os.path.join(config['data_dir'], 'git'), |
|
||||
'GIT_HTTP_EXPORT_ALL': '1', |
|
||||
'PATH_INFO': http_environment['PATH_INFO'][4:], |
|
||||
'REMOTE_USER': request.env.user.login |
|
||||
} |
|
||||
|
|
||||
command_env = os.environ.copy() |
|
||||
for var in git_env: |
|
||||
command_env[var] = git_env[var] |
|
||||
|
|
||||
git = subprocess.Popen( |
|
||||
['/usr/lib/git-core/git-http-backend'], |
|
||||
stdin=subprocess.PIPE, |
|
||||
stdout=subprocess.PIPE, |
|
||||
stderr=subprocess.PIPE, |
|
||||
env=command_env, |
|
||||
shell=True |
|
||||
) |
|
||||
stdout, stderr = git.communicate(http_input_stream(request).read()) |
|
||||
headers_str, body = stdout.split(b"\r\n\r\n", 2) |
|
||||
|
|
||||
http_status_code = 200 |
|
||||
headers = [] |
|
||||
for header in headers_str.split(b"\r\n"): |
|
||||
name, value = header.split(b': ', 2) |
|
||||
if name == 'Status': |
|
||||
http_code = int(value.split(b' ')[0]) |
|
||||
else: |
|
||||
headers.append((name.decode('ascii'), value.decode('ascii'))) |
|
||||
|
|
||||
return werkzeug.Response( |
|
||||
body, |
|
||||
status = http_status_code, |
|
||||
headers = headers |
|
||||
) |
|
@ -1,12 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<odoo> |
|
||||
<data noupdate="1"> |
|
||||
|
|
||||
<record id="config_git_backend_path" model="ir.config_parameter"> |
|
||||
<field name="key">galicea_git.git_http_backend</field> |
|
||||
<field name="value">/usr/lib/git-core/git-http-backend</field> |
|
||||
<field name="group_ids" eval="[(4, ref('galicea_git.group_admin'))]" /> |
|
||||
</record> |
|
||||
|
|
||||
</data> |
|
||||
</odoo> |
|
@ -1,63 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
import io |
|
||||
|
|
||||
# Werkzeug version bundled into odoo doesn't handle this kind of Transfer-Encoding |
|
||||
# correctly. We copy the fix from https://github.com/pallets/werkzeug/pull/1198/files |
|
||||
class DechunkedInput(io.RawIOBase): |
|
||||
"""An input stream that handles Transfer-Encoding 'chunked'""" |
|
||||
|
|
||||
def __init__(self, rfile): |
|
||||
self._rfile = rfile |
|
||||
self._done = False |
|
||||
self._len = 0 |
|
||||
|
|
||||
def readable(self): |
|
||||
return True |
|
||||
|
|
||||
def read_chunk_len(self): |
|
||||
try: |
|
||||
line = self._rfile.readline().decode('latin1') |
|
||||
_len = int(line.strip(), 16) |
|
||||
except ValueError: |
|
||||
raise IOError('Invalid chunk header') |
|
||||
if _len < 0: |
|
||||
raise IOError('Negative chunk length not allowed') |
|
||||
return _len |
|
||||
|
|
||||
def readinto(self, buf): |
|
||||
read = 0 |
|
||||
while not self._done and read < len(buf): |
|
||||
if self._len == 0: |
|
||||
# This is the first chunk or we fully consumed the previous |
|
||||
# one. Read the next length of the next chunk |
|
||||
self._len = self.read_chunk_len() |
|
||||
|
|
||||
if self._len == 0: |
|
||||
# Found the final chunk of size 0. The stream is now exhausted, |
|
||||
# but there is still a final newline that should be consumed |
|
||||
self._done = True |
|
||||
|
|
||||
if self._len > 0: |
|
||||
# There is data (left) in this chunk, so append it to the |
|
||||
# buffer. If this operation fully consumes the chunk, this will |
|
||||
# reset self._len to 0. |
|
||||
n = min(len(buf), self._len) |
|
||||
buf[read:read + n] = self._rfile.read(n) |
|
||||
self._len -= n |
|
||||
read += n |
|
||||
|
|
||||
if self._len == 0: |
|
||||
# Skip the terminating newline of a chunk that has been fully |
|
||||
# consumed. This also applies to the 0-sized final chunk |
|
||||
terminator = self._rfile.readline() |
|
||||
if terminator not in (b'\n', b'\r\n', b'\r'): |
|
||||
raise IOError('Missing chunk terminating newline') |
|
||||
|
|
||||
return read |
|
||||
|
|
||||
def http_input_stream(request): |
|
||||
if request.httprequest.headers.get('Transfer-Encoding') == 'chunked' \ |
|
||||
and not request.httprequest.environ.get('wsgi.input_terminated'): |
|
||||
return DechunkedInput(request.httprequest.environ['wsgi.input']) |
|
||||
return request.httprequest.stream |
|
@ -1,4 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import repository |
|
||||
from . import config_settings |
|
@ -1,42 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
import os |
|
||||
|
|
||||
from odoo import models, fields, api |
|
||||
from odoo.exceptions import ValidationError |
|
||||
|
|
||||
class ConfigSettings(models.TransientModel): |
|
||||
_name = 'galicea_git.config.settings' |
|
||||
_inherit = 'res.config.settings' |
|
||||
|
|
||||
git_http_backend = fields.Char( |
|
||||
'Absolute path to Git HTTP backend', |
|
||||
required=True |
|
||||
) |
|
||||
git_http_backend_valid = fields.Boolean( |
|
||||
compute='_compute_git_http_backend_valid' |
|
||||
) |
|
||||
|
|
||||
@api.one |
|
||||
@api.depends('git_http_backend') |
|
||||
def _compute_git_http_backend_valid(self): |
|
||||
self.git_http_backend_valid = self.git_http_backend and os.access(self.git_http_backend, os.X_OK) |
|
||||
|
|
||||
@api.one |
|
||||
def set_params(self): |
|
||||
self.env['ir.config_parameter'].set_param('galicea_git.git_http_backend', self.git_http_backend) |
|
||||
|
|
||||
@api.model |
|
||||
def get_default_values(self, fields): |
|
||||
return { |
|
||||
'git_http_backend': self.env['ir.config_parameter'].get_param('galicea_git.git_http_backend') |
|
||||
} |
|
||||
|
|
||||
@api.multi |
|
||||
def execute(self): |
|
||||
self.ensure_one() |
|
||||
if not self.env.user.has_group('galicea_git.group_admin'): |
|
||||
raise AccessError("Only Git administrators can change those settings") |
|
||||
super(ConfigSettings, self.sudo()).execute() |
|
||||
act_window = self.env.ref('galicea_git.config_settings_action') |
|
||||
return act_window.read()[0] |
|
@ -1,105 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
import os |
|
||||
import random |
|
||||
import shutil |
|
||||
import string |
|
||||
import subprocess |
|
||||
try: |
|
||||
import git |
|
||||
except ImportError: |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
from odoo import models, fields, api, http |
|
||||
from odoo.exceptions import ValidationError |
|
||||
from odoo.tools import config, which |
|
||||
|
|
||||
class Repository(models.Model): |
|
||||
_name = 'galicea_git.repository' |
|
||||
|
|
||||
state = fields.Selection( |
|
||||
[('draft', 'Draft'), ('created', 'Created')], |
|
||||
default='draft' |
|
||||
) |
|
||||
|
|
||||
name = fields.Char('User-friendly name', required=True) |
|
||||
system_name = fields.Char( |
|
||||
'Directory name', |
|
||||
required=True, |
|
||||
readonly=True, |
|
||||
index=True, |
|
||||
states={'draft': [('readonly', False)]} |
|
||||
) |
|
||||
collaborator_ids = fields.Many2many( |
|
||||
'res.users', |
|
||||
string='Collaborators' |
|
||||
) |
|
||||
|
|
||||
local_directory = fields.Char( |
|
||||
'Local directory on server', |
|
||||
compute='_compute_local_directory', |
|
||||
groups='galicea_git.group_admin' |
|
||||
) |
|
||||
url = fields.Char( |
|
||||
'Clone', |
|
||||
compute='_compute_url' |
|
||||
) |
|
||||
|
|
||||
@api.one |
|
||||
@api.depends('system_name') |
|
||||
def _compute_url(self): |
|
||||
base_url = http.request.httprequest.host_url if http.request \ |
|
||||
else env['ir.config_parameter'].get_param('web.base.url') + '/' |
|
||||
self.url = u'{}git/{}'.format(base_url, self.system_name) |
|
||||
|
|
||||
@api.one |
|
||||
@api.depends('system_name') |
|
||||
def _compute_local_directory(self): |
|
||||
if self.system_name: |
|
||||
self.local_directory = os.path.join(config['data_dir'], 'git', self.system_name) |
|
||||
|
|
||||
@api.constrains('system_name') |
|
||||
def _validate_system_name(self): |
|
||||
allowed_characters = string.ascii_lowercase + string.digits + '-_' |
|
||||
if not all(c in allowed_characters for c in self.system_name): |
|
||||
raise ValidationError( |
|
||||
'Only lowercase, digits and hyphens (-) are allowed in directory name' |
|
||||
) |
|
||||
|
|
||||
@api.constrains('collaborator_ids') |
|
||||
def _validate_collaborator_ids(self): |
|
||||
invalid_collaborators = self.collaborator_ids.filtered(lambda c: not c.has_group('galicea_git.group_collaborator')) |
|
||||
if invalid_collaborators: |
|
||||
raise ValidationError( |
|
||||
'User {} does not have the {} role. Contact your Administrator'.format( |
|
||||
invalid_collaborators[0].name, |
|
||||
self.env.ref('galicea_git.group_collaborator').full_name |
|
||||
) |
|
||||
) |
|
||||
|
|
||||
@api.model |
|
||||
def create(self, values): |
|
||||
values['state'] = 'created' |
|
||||
ret = super(Repository, self).create(values) |
|
||||
ret.__initialize_repo() |
|
||||
return ret |
|
||||
|
|
||||
@api.multi |
|
||||
def unlink(selfs): |
|
||||
directories_to_move = selfs.mapped(lambda r: r.local_directory) |
|
||||
ret = super(Repository, selfs).unlink() |
|
||||
for directory in directories_to_move: |
|
||||
if os.path.exists(directory): |
|
||||
suffix = ''.join(random.choice(string.ascii_lowercase) for _ in range(8)) |
|
||||
new_directory = directory + '-deleted-' + suffix |
|
||||
shutil.move(directory, new_directory) |
|
||||
|
|
||||
@api.multi |
|
||||
def __initialize_repo(self): |
|
||||
self.ensure_one() |
|
||||
if os.path.exists(self.local_directory): |
|
||||
raise ValidationError( |
|
||||
'Repository {} already exists, choose a different name!'.format(self.system_name) |
|
||||
) |
|
||||
subprocess.check_call([which('git'), 'init', '--bare', self.local_directory]) |
|
@ -1,3 +0,0 @@ |
|||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|
||||
access_repository_collaborator,repository_collaborator,model_galicea_git_repository,galicea_git.group_collaborator,1,0,0,0 |
|
||||
access_repository_admin,repository_admin,model_galicea_git_repository,galicea_git.group_admin,1,1,1,1 |
|
@ -1,47 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<odoo> |
|
||||
<record id="module_category_git" model="ir.module.category"> |
|
||||
<field name="name">Git</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="group_collaborator" model="res.groups"> |
|
||||
<field name="name">Collaborator</field> |
|
||||
<field name="category_id" ref="module_category_git" /> |
|
||||
</record> |
|
||||
|
|
||||
<record id="group_admin" model="res.groups"> |
|
||||
<field name="name">Administrator</field> |
|
||||
<field name="category_id" ref="module_category_git" /> |
|
||||
<field name="implied_ids" eval="[(4,ref('group_collaborator'))]" /> |
|
||||
</record> |
|
||||
|
|
||||
<record id="base.group_erp_manager" model="res.groups"> |
|
||||
<field name="implied_ids" eval="[(4,ref('group_admin'))]" /> |
|
||||
</record> |
|
||||
|
|
||||
<record id="repository_collaborator_access_rule" model="ir.rule"> |
|
||||
<field name="name">Collaborators can only access repositories they are assigned to</field> |
|
||||
<field name="model_id" ref="model_galicea_git_repository" /> |
|
||||
<field name="groups" eval="[(4, ref('group_collaborator'))]"/> |
|
||||
<field name="domain_force"> |
|
||||
[('collaborator_ids', 'in', user.id)] |
|
||||
</field> |
|
||||
<field eval="1" name="perm_read" /> |
|
||||
<field eval="0" name="perm_write" /> |
|
||||
<field eval="0" name="perm_create" /> |
|
||||
<field eval="0" name="perm_unlink" /> |
|
||||
</record> |
|
||||
|
|
||||
<record id="repository_admin_access_rule" model="ir.rule"> |
|
||||
<field name="name">Administrators can access any repositories</field> |
|
||||
<field name="model_id" ref="model_galicea_git_repository" /> |
|
||||
<field name="groups" eval="[(4, ref('group_admin'))]"/> |
|
||||
<field name="domain_force"> |
|
||||
[(1, '=', 1)] |
|
||||
</field> |
|
||||
<field eval="1" name="perm_read" /> |
|
||||
<field eval="0" name="perm_write" /> |
|
||||
<field eval="0" name="perm_create" /> |
|
||||
<field eval="0" name="perm_unlink" /> |
|
||||
</record> |
|
||||
</odoo> |
|
Before Width: 80 | Height: 80 | Size: 3.8 KiB |
Before Width: 800 | Height: 354 | Size: 42 KiB |
Before Width: 738 | Height: 359 | Size: 78 KiB |
Before Width: 805 | Height: 273 | Size: 31 KiB |
@ -1,19 +0,0 @@ |
|||||
<section class="oe_container"> |
|
||||
<div class="oe_row oe_spaced"> |
|
||||
<div class="oe_span12"> |
|
||||
<h2 class="oe_slogan">Galicea Git hosting</h2> |
|
||||
<h3 class="oe_slogan"> |
|
||||
Simple Odoo-based HTTP interface for Git repository hosting |
|
||||
</h3> |
|
||||
<p> |
|
||||
This add-on allows you to create Git repositories hosted by Odoo, and add specific Odoo users as collaborators. Only those users will have access to the repository. It requires <tt>git</tt> package, including <tt>git-http-backend</tt>, installed in the system. For Ubuntu/Debian it's enough to call |
|
||||
<pre>sudo apt install git</pre> |
|
||||
</p> |
|
||||
<h3>Creating repositories</h3> |
|
||||
<img class="oe_picture oe_screenshot" src="images/create_screenshot.png" /> |
|
||||
<img class="oe_picture oe_screenshot" src="images/config_screenshot.png" /> |
|
||||
<h3>Interacting with the repository</h3> |
|
||||
<img class="oe_picture oe_screenshot" src="images/console_screenshot.png" /> |
|
||||
</div> |
|
||||
</div> |
|
||||
</section> |
|
@ -1,37 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
import os |
|
||||
|
|
||||
from odoo.addons.galicea_environment_checkup import \ |
|
||||
custom_check, CheckWarning, CheckSuccess, CheckFail |
|
||||
from odoo import http |
|
||||
|
|
||||
@custom_check |
|
||||
def check_single_db(env): |
|
||||
if not http.request: |
|
||||
raise CheckWarning('Could not detect DB settings.') |
|
||||
|
|
||||
dbs = http.db_list(True, http.request.httprequest) |
|
||||
if len(dbs) == 1: |
|
||||
return CheckSuccess('Odoo runs in a single-DB mode.') |
|
||||
|
|
||||
details = ( |
|
||||
'<p>Odoo runs in a multi-DB mode, which will cause Git HTTP requests to fail.</p>' |
|
||||
'<p>Run Odoo with <tt>--dbfilter</tt> or <tt>--database</tt> flag.</p>' |
|
||||
) |
|
||||
return CheckFail( |
|
||||
'Odoo runs in a multi-DB mode.', |
|
||||
details=details |
|
||||
) |
|
||||
|
|
||||
@custom_check |
|
||||
def check_http_backend(env): |
|
||||
backend_path = env['ir.config_parameter'].sudo().get_param( |
|
||||
'galicea_git.git_http_backend' |
|
||||
) |
|
||||
if not os.access(backend_path, os.X_OK): |
|
||||
raise CheckFail( |
|
||||
'Git HTTP backend not found', |
|
||||
details='<a href="http://galicea.mw-odoo:8080/web#action=galicea_git.config_settings_action">Check the configuration here</a>' |
|
||||
) |
|
||||
return CheckSuccess('Git HTTP backend was found') |
|
@ -1,71 +0,0 @@ |
|||||
<odoo> |
|
||||
<data> |
|
||||
<record id="repository_view_form" model="ir.ui.view"> |
|
||||
<field name="model">galicea_git.repository</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<form> |
|
||||
<group> |
|
||||
<field name="state" invisible="1" /> |
|
||||
<field name="name" /> |
|
||||
<field name="system_name" groups="galicea_git.group_admin" /> |
|
||||
<field name="collaborator_ids" widget="many2many_tags" options="{'no_create': True}" /> |
|
||||
</group> |
|
||||
<group class="oe_read_only"> |
|
||||
<label for="url" /> |
|
||||
<span style="font-family: monospace">git clone <field name="url" nolabel="True" /></span> |
|
||||
<field name="local_directory" style="font-family: monospace" /> |
|
||||
</group> |
|
||||
</form> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="repository_view_tree" model="ir.ui.view"> |
|
||||
<field name="model">galicea_git.repository</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<tree> |
|
||||
<field name="state" invisible="1" /> |
|
||||
<field name="name" /> |
|
||||
<field name="system_name" groups="galicea_git.group_admin" /> |
|
||||
</tree> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<act_window id="repository_action" |
|
||||
name="Git repositories" |
|
||||
res_model="galicea_git.repository" /> |
|
||||
|
|
||||
<record id="config_settings_view_form" model="ir.ui.view"> |
|
||||
<field name="model">galicea_git.config.settings</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<form string="Git hosting settings" class="oe_form_configuration"> |
|
||||
<header> |
|
||||
<button string="Save" type="object" name="execute" class="oe_highlight"/> |
|
||||
<button string="Cancel" type="object" name="cancel" class="oe_link"/> |
|
||||
</header> |
|
||||
<field name="git_http_backend_valid" invisible="1" /> |
|
||||
<group> |
|
||||
<label for="git_http_backend" /> |
|
||||
<span> |
|
||||
<field name="git_http_backend" nolabel="True" class="oe_inline" style="min-width:300px; margin-right:5px" /> |
|
||||
<i class="fa fa-check" aria-hidden="true" style="color: green" |
|
||||
attrs="{'invisible': [('git_http_backend_valid', '=', False)]}" /> |
|
||||
<i class="fa fa-times" aria-hidden="true" style="color: red" |
|
||||
attrs="{'invisible': [('git_http_backend_valid', '=', True)]}" /> |
|
||||
</span> |
|
||||
</group> |
|
||||
</form> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<act_window id="config_settings_action" |
|
||||
name="Settings" |
|
||||
res_model="galicea_git.config.settings" |
|
||||
parent="base.menu_administration" |
|
||||
view_mode="form" target="inline" /> |
|
||||
|
|
||||
<menuitem name="Git hosting" id="git_root_menu" sequence="20" /> |
|
||||
<menuitem name="Repositories" id="repo_menu" parent="galicea_git.git_root_menu" action="repository_action" sequence="1" /> |
|
||||
<menuitem name="Settings" id="settings_menu" parent="galicea_git.git_root_menu" action="config_settings_action" sequence="99" |
|
||||
groups="galicea_git.group_admin" /> |
|
||||
</data> |
|
||||
</odoo> |
|
@ -1,43 +0,0 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||
<odoo> |
|
||||
<data> |
|
||||
|
|
||||
<record id="repository_view_form" model="ir.ui.view"> |
|
||||
<field name="model">galicea_git.repository</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<form> |
|
||||
<group> |
|
||||
<field name="state" invisible="1" /> |
|
||||
<field name="name" /> |
|
||||
<field name="system_name" groups="galicea_git.group_admin" /> |
|
||||
<field name="collaborator_ids" widget="many2many_tags" options="{'no_create': True}" /> |
|
||||
</group> |
|
||||
<group class="oe_read_only"> |
|
||||
<label for="url" /> |
|
||||
<span style="font-family: monospace">git clone <field name="url" nolabel="True" /></span> |
|
||||
<field name="local_directory" style="font-family: monospace" /> |
|
||||
</group> |
|
||||
</form> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="repository_view_tree" model="ir.ui.view"> |
|
||||
<field name="model">galicea_git.repository</field> |
|
||||
<field name="arch" type="xml"> |
|
||||
<tree> |
|
||||
<field name="state" invisible="1" /> |
|
||||
<field name="name" /> |
|
||||
<field name="system_name" groups="galicea_git.group_admin" /> |
|
||||
</tree> |
|
||||
</field> |
|
||||
</record> |
|
||||
|
|
||||
<act_window id="repository_action" |
|
||||
name="Git repositories" |
|
||||
res_model="galicea_git.repository" /> |
|
||||
|
|
||||
<menuitem name="Repositories" id="repo_menu" parent="galicea_base.galicea_admin_menu" |
|
||||
action="repository_action" sequence="1" /> |
|
||||
|
|
||||
</data> |
|
||||
</odoo> |
|
@ -1 +0,0 @@ |
|||||
[See add-on page on odoo.com](https://apps.odoo.com/apps/modules/12.0/galicea_git_oauth/) |
|
@ -1,4 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import models |
|
||||
from . import controllers |
|
@ -1,21 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
{ |
|
||||
'name': "Galicea Git OAuth", |
|
||||
|
|
||||
'summary': """ |
|
||||
Enables Git auth via OAuth token""", |
|
||||
|
|
||||
'author': "Maciej Wawro", |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://galicea.pl", |
|
||||
|
|
||||
'category': 'Technical Settings', |
|
||||
'version': '12.0.1.0', |
|
||||
|
|
||||
'depends': ['galicea_git', 'galicea_openid_connect'], |
|
||||
|
|
||||
'data': [ |
|
||||
], |
|
||||
|
|
||||
'installable': True |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
from . import ext_git_main |
|
@ -1,14 +0,0 @@ |
|||||
from odoo.addons.galicea_git.controllers.main import Main |
|
||||
|
|
||||
class ExtMain(Main): |
|
||||
def authorize(self, req): |
|
||||
auth = req.httprequest.authorization |
|
||||
if auth and auth.password == 'bearer': |
|
||||
access_token = req.httprequest.authorization.username |
|
||||
token = req.env['galicea_openid_connect.access_token'].sudo().search( |
|
||||
[('token', '=', access_token)] |
|
||||
) |
|
||||
if token: |
|
||||
req.uid = token.user_id.id |
|
||||
return |
|
||||
super(ExtMain, self).authorize(req) |
|
@ -1 +0,0 @@ |
|||||
from . import ext_repository |
|
@ -1,24 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from urllib.parse import urlparse |
|
||||
from odoo import models |
|
||||
|
|
||||
class Repository(models.Model): |
|
||||
_inherit = 'galicea_git.repository' |
|
||||
|
|
||||
def authenticated_url(self, client): |
|
||||
""" |
|
||||
@param application galicea_openid.application""" |
|
||||
|
|
||||
token = self.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( |
|
||||
self.env.user.id, |
|
||||
client.id |
|
||||
) |
|
||||
unauthenticated_url = self.url |
|
||||
url_parts = urlparse(unauthenticated_url) |
|
||||
return '{}://{}:bearer@{}{}'.format( |
|
||||
url_parts.scheme, |
|
||||
token.token, |
|
||||
url_parts.netloc, |
|
||||
url_parts.path, |
|
||||
) |
|
@ -1,5 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
#from . import models |
|
||||
from . import controllers |
|
||||
|
|
@ -1,33 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
{ |
|
||||
'name': "openapi", |
|
||||
|
|
||||
'summary': """ |
|
||||
Odoo Opnapi |
|
||||
UWAGA! Obecnie dekorator apiroute ma ograniczoną funkcjonalność. |
|
||||
M.in. tylko jeden URL |
|
||||
controllers/api.py zawiera przykład wykorzystania - |
|
||||
pod adresem /oapi/api zwraca dokumentację w JSON |
|
||||
""", |
|
||||
|
|
||||
'description': """ |
|
||||
|
|
||||
""", |
|
||||
|
|
||||
'author': 'Jerzy Wawro', |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://www.galicea.pl", |
|
||||
'category': 'Tools', |
|
||||
'version': '12.0.0.1', |
|
||||
|
|
||||
'depends': [ |
|
||||
], |
|
||||
'external_dependencies': { |
|
||||
'python': [ 'fastapi', 'pydantic', 'starlette' ] |
|
||||
}, |
|
||||
'data': [ |
|
||||
], |
|
||||
'application': True, |
|
||||
'installable': True, |
|
||||
|
|
||||
} |
|
@ -1,4 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from . import api |
|
||||
|
|
@ -1,56 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
import json |
|
||||
|
|
||||
from fastapi.openapi.docs import get_swagger_ui_html |
|
||||
|
|
||||
from odoo import http, _ |
|
||||
from ..openapi import apiroute |
|
||||
from ..openapi import oapi |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
class OpenApiTest(http.Controller): |
|
||||
|
|
||||
|
|
||||
@http.route(['/oapi/tst1',], type='http', auth="user", website=True) |
|
||||
def tst1(self, **kw): |
|
||||
return "tst1" |
|
||||
|
|
||||
@oapi.get('/oapi/tst2') |
|
||||
@http.route(['/oapi/tst2',], type='http', auth="user", website=True) |
|
||||
def tst2(self): |
|
||||
return 'ok test2' |
|
||||
|
|
||||
@oapi.api_route('/oapi/tst3') |
|
||||
@http.route(['/oapi/tst3',], type='http', auth="user", website=True) |
|
||||
def tst3(self, par1="abc"): |
|
||||
return par1 |
|
||||
|
|
||||
|
|
||||
@oapi.api_route('/oapi/tst4') |
|
||||
@http.route(['/oapi/tst4', ], type='http', auth="user", website=True) |
|
||||
def tst4(self,par1="444"): |
|
||||
return par1 |
|
||||
|
|
||||
|
|
||||
@apiroute('/oapi/tst5') |
|
||||
def tst5(self, par1="555"): |
|
||||
return par1 |
|
||||
|
|
||||
@http.route(['/oapi/api',], type='http', auth="user", website=True) |
|
||||
def api(self, **kw): |
|
||||
return json.dumps(oapi.openapi()) |
|
||||
# wynik możesz skopiować do https://editor.swagger.io/ |
|
||||
|
|
||||
@http.route(['/oapi/docs',], type='http', auth="user", website=True) |
|
||||
def api_UI(self, **kw): |
|
||||
response = get_swagger_ui_html(openapi_url = '/oapi/api', title = 'tytuł') |
|
||||
return response.body |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -1,22 +0,0 @@ |
|||||
# pip install fastapi |
|
||||
# pip install email-validator |
|
||||
# pip install pydantic |
|
||||
# pip install starlette |
|
||||
# pip install uvicorn |
|
||||
|
|
||||
import uvicorn |
|
||||
from fastapi import FastAPI |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
@app.get("/") |
|
||||
def read_root(): |
|
||||
return {"Hello": "World"} |
|
||||
|
|
||||
def run_server(): |
|
||||
uvicorn.run(app) |
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
uvicorn.run(app, #'server:app', |
|
||||
host='127.0.0.1', port=8000, reload=True) |
|
@ -1,41 +0,0 @@ |
|||||
# pip install fastapi |
|
||||
# pip install email-validator |
|
||||
# pip install pydantic |
|
||||
# pip install starlette |
|
||||
# pip install uvicorn |
|
||||
|
|
||||
import uvicorn |
|
||||
from fastapi import FastAPI |
|
||||
from fastapi.openapi.utils import get_openapi |
|
||||
|
|
||||
app = FastAPI() |
|
||||
|
|
||||
|
|
||||
@app.get("/items/") |
|
||||
async def read_items(): |
|
||||
return [{"name": "Foo"}] |
|
||||
|
|
||||
|
|
||||
def custom_openapi(): |
|
||||
if app.openapi_schema: |
|
||||
return app.openapi_schema |
|
||||
openapi_schema = get_openapi( |
|
||||
title="Custom title", |
|
||||
version="2.5.0", |
|
||||
description="This is a very custom OpenAPI schema", |
|
||||
routes=app.routes, |
|
||||
) |
|
||||
openapi_schema["info"]["x-logo"] = { |
|
||||
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" |
|
||||
} |
|
||||
app.openapi_schema = openapi_schema |
|
||||
return app.openapi_schema |
|
||||
|
|
||||
|
|
||||
app.openapi = custom_openapi |
|
||||
|
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
print("see http://127.0.0.1:8000/docs") |
|
||||
uvicorn.run(app, #'server:app', |
|
||||
host='127.0.0.1', port=8000, reload=True) |
|
@ -1,3 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
|
|
@ -1,52 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
|
|
||||
import functools |
|
||||
from fastapi import FastAPI |
|
||||
from fastapi.openapi.utils import get_openapi |
|
||||
|
|
||||
|
|
||||
|
|
||||
def custom_openapi(): |
|
||||
if oapi.openapi_schema: |
|
||||
return oapi.openapi_schema |
|
||||
openapi_schema = get_openapi( |
|
||||
title="Custom title", |
|
||||
version="2.5.0", |
|
||||
description="This is a very custom OpenAPI schema", |
|
||||
routes=oapi.routes, |
|
||||
) |
|
||||
openapi_schema["info"]["x-logo"] = { |
|
||||
"url": "https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" |
|
||||
} |
|
||||
oapi.openapi_schema = openapi_schema |
|
||||
return oapi.openapi_schema |
|
||||
|
|
||||
oapi = FastAPI() |
|
||||
oapi.openapi = custom_openapi |
|
||||
|
|
||||
def apiroute(route=None, **kw): |
|
||||
|
|
||||
routing = kw.copy() |
|
||||
def apidecorator(f): |
|
||||
if route: |
|
||||
if isinstance(route, list): |
|
||||
routes = route |
|
||||
else: |
|
||||
routes = [route] |
|
||||
routing['routes'] = routes |
|
||||
|
|
||||
@functools.wraps(f) |
|
||||
def response_wrap(*args, **kw): |
|
||||
response = f(*args, **kw) |
|
||||
return response |
|
||||
|
|
||||
oapi.add_api_route(routes[0], f, include_in_schema=True) |
|
||||
response_wrap.routing = routing |
|
||||
response_wrap.original_func = f |
|
||||
return response_wrap |
|
||||
|
|
||||
return apidecorator |
|
||||
|
|
||||
|
|
||||
# |
|
@ -1,2 +0,0 @@ |
|||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
|
||||
access_galicea_openapi,galicea_openapi,model_galicea_openapi_openapi_script,galicea_openapi.script,1,1,1,0 |
|
Before Width: 128 | Height: 128 | Size: 10 KiB |
@ -1,51 +1,38 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
{ |
{ |
||||
'name': "Galicea OpenID Connect Provider", |
|
||||
|
|
||||
'summary': """OpenID Connect Provider and OAuth2 resource server""", |
|
||||
|
|
||||
'author': "Maciej Wawro", |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://galicea.pl", |
|
||||
|
|
||||
'category': 'Technical Settings', |
|
||||
'version': '12.0.0.0', |
|
||||
|
|
||||
'depends': ['web', 'galicea_environment_checkup', 'galicea_base' ], |
|
||||
|
|
||||
'external_dependencies': { |
|
||||
'python': ['jwcrypto', 'cryptography'] |
|
||||
}, |
|
||||
|
|
||||
'data': [ |
|
||||
'security/security.xml', |
|
||||
'security/ir.model.access.csv', |
|
||||
|
"name": "Galicea OpenID Connect Provider", |
||||
|
"summary": """OpenID Connect Provider and OAuth2 resource server""", |
||||
|
"author": "Maciej Wawro, Nicolas JEUDY", |
||||
|
"maintainer": "nj.0k.io, Galicea", |
||||
|
"website": "https://nj.0k.io", |
||||
|
"category": "Technical Settings", |
||||
|
"version": "14.0.0.0", |
||||
|
"depends": ["web"], |
||||
|
"external_dependencies": {"python": ["jwcrypto", "cryptography"]}, |
||||
|
"data": [ |
||||
|
"security/security.xml", |
||||
|
"security/ir.model.access.csv", |
||||
# 'security/init.yml', |
# 'security/init.yml', |
||||
'security/init.xml', |
|
||||
'views/views.xml', |
|
||||
'views/templates.xml' |
|
||||
|
"security/init.xml", |
||||
|
"views/views.xml", |
||||
|
"views/templates.xml", |
||||
], |
], |
||||
|
|
||||
'environment_checkup': { |
|
||||
'dependencies': { |
|
||||
'python': [ |
|
||||
|
"environment_checkup": { |
||||
|
"dependencies": { |
||||
|
"python": [ |
||||
|
{"name": "jwcrypto", "install": "pip install 'jwcrypto==0.5.0'"}, |
||||
{ |
{ |
||||
'name': 'jwcrypto', |
|
||||
'install': "pip install 'jwcrypto==0.5.0'" |
|
||||
|
"name": "cryptography", |
||||
|
"version": ">=2.3", |
||||
|
"install": "pip install 'cryptography>=2.3'", |
||||
}, |
}, |
||||
{ |
|
||||
'name': 'cryptography', |
|
||||
'version': '>=2.3', |
|
||||
'install': "pip install 'cryptography>=2.3'" |
|
||||
} |
|
||||
] |
] |
||||
} |
} |
||||
}, |
}, |
||||
|
|
||||
'images': [ |
|
||||
'static/description/images/master_screenshot.png', |
|
||||
'static/description/images/client_screenshot.png', |
|
||||
'static/description/images/login_screenshot.png', |
|
||||
'static/description/images/error_screenshot.png' |
|
||||
] |
|
||||
|
"images": [ |
||||
|
"static/description/images/master_screenshot.png", |
||||
|
"static/description/images/client_screenshot.png", |
||||
|
"static/description/images/login_screenshot.png", |
||||
|
"static/description/images/error_screenshot.png", |
||||
|
], |
||||
} |
} |
@ -1,23 +1,36 @@ |
|||||
# -*- coding: utf-8 -*- |
# -*- coding: utf-8 -*- |
||||
|
|
||||
from .. import random_tokens |
from .. import random_tokens |
||||
|
|
||||
try: |
try: |
||||
from jwcrypto import jwk |
from jwcrypto import jwk |
||||
except ImportError: |
except ImportError: |
||||
pass |
pass |
||||
|
|
||||
|
|
||||
def init_keys(IrConfigParameter): |
def init_keys(IrConfigParameter): |
||||
keys = { |
keys = { |
||||
'galicea_openid_connect.authorization_code_jwk': lambda: \ |
|
||||
jwk.JWK.generate(kty='oct', size=256, kid=random_tokens.alpha_numeric(16), use='sig', alg='HS256').export(), |
|
||||
'galicea_openid_connect.id_token_jwk': lambda: \ |
|
||||
jwk.JWK.generate(kty='RSA', size=2054, kid=random_tokens.alpha_numeric(16), use='sig', alg='RS256').export() |
|
||||
|
"galicea_openid_connect.authorization_code_jwk": lambda: jwk.JWK.generate( |
||||
|
kty="oct", |
||||
|
size=256, |
||||
|
kid=random_tokens.alpha_numeric(16), |
||||
|
use="sig", |
||||
|
alg="HS256", |
||||
|
).export(), |
||||
|
"galicea_openid_connect.id_token_jwk": lambda: jwk.JWK.generate( |
||||
|
kty="RSA", |
||||
|
size=2054, |
||||
|
kid=random_tokens.alpha_numeric(16), |
||||
|
use="sig", |
||||
|
alg="RS256", |
||||
|
).export(), |
||||
} |
} |
||||
|
|
||||
for key, gen in keys.iteritems(): |
for key, gen in keys.iteritems(): |
||||
if not IrConfigParameter.search([('key', '=', key)]): |
|
||||
IrConfigParameter.create({ |
|
||||
'key': key, |
|
||||
'value': gen(), |
|
||||
'group_ids': [(4, IrConfigParameter.env.ref('base.group_erp_manager').id)] |
|
||||
}) |
|
||||
|
if not IrConfigParameter.search([("key", "=", key)]): |
||||
|
IrConfigParameter.create( |
||||
|
{ |
||||
|
"key": key, |
||||
|
"value": gen(), |
||||
|
} |
||||
|
) |
@ -1,24 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from odoo.addons.galicea_environment_checkup import \ |
|
||||
custom_check, CheckWarning, CheckSuccess, CheckFail |
|
||||
|
|
||||
from odoo import http |
|
||||
|
|
||||
@custom_check |
|
||||
def check_single_db(env): |
|
||||
if not http.request: |
|
||||
raise CheckWarning('Could not detect DB settings.') |
|
||||
|
|
||||
dbs = http.db_list(True, http.request.httprequest) |
|
||||
if len(dbs) == 1: |
|
||||
return CheckSuccess('Odoo runs in a single-DB mode.') |
|
||||
|
|
||||
details = ( |
|
||||
'<p>Odoo runs in a multi-DB mode, which will cause API request routing to fail.</p>' |
|
||||
'<p>Run Odoo with <tt>--dbfilter</tt> or <tt>--database</tt> flag.</p>' |
|
||||
) |
|
||||
return CheckFail( |
|
||||
'Odoo runs in a multi-DB mode.', |
|
||||
details=details |
|
||||
) |
|
@ -1,30 +0,0 @@ |
|||||
Widgets |
|
||||
======= |
|
||||
|
|
||||
<field name="url_field" widget="iframe" style="width: 100%" iframe_style="width: 100%" /> |
|
||||
|
|
||||
Creates an iframe with ``url_field`` value as a source. |
|
||||
|
|
||||
<field name="article_ids" widget="one2many_flexible" click_target="current" /> |
|
||||
|
|
||||
Allows changing the target for the item click action. |
|
||||
|
|
||||
Functions |
|
||||
========= |
|
||||
odoo.addons.galicea_toolset.utils.get_base_url(env) |
|
||||
|
|
||||
Client actions |
|
||||
============== |
|
||||
@api.multi |
|
||||
|
|
||||
def button_action(self): |
|
||||
|
|
||||
return { |
|
||||
|
|
||||
'type': 'ir.actions.client', |
|
||||
|
|
||||
'tag': 'galicea_toolset.open_edit_dialog', |
|
||||
|
|
||||
'params': { 'res_id': <id>, 'res_model': <model>, 'title': <title>} |
|
||||
|
|
||||
}; |
|
@ -1 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
@ -1,20 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
{ |
|
||||
'name': "galicea toolset", |
|
||||
|
|
||||
'summary': """ |
|
||||
A couple of small convenience widgets and functions""", |
|
||||
|
|
||||
'author': "Maciej Wawro", |
|
||||
'maintainer': "Galicea", |
|
||||
'website': "http://www.galicea.pl", |
|
||||
|
|
||||
'category': 'Technical Settings', |
|
||||
'version': '12.0.0.1', |
|
||||
|
|
||||
'depends': ['base'], |
|
||||
|
|
||||
'data': [ |
|
||||
'views/data.xml' |
|
||||
], |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
jurek@jurek.15022 |
|
@ -1,38 +0,0 @@ |
|||||
odoo.define('galicea_toolset.client_actions', function(require) { |
|
||||
var Widget = require('web.Widget'); |
|
||||
var core = require('web.core'); |
|
||||
var common = require('web.form_common'); |
|
||||
var ActionManager = require('web.ActionManager'); |
|
||||
|
|
||||
var OpenEditDialogAction = Widget.extend({ |
|
||||
init: function(parent, context) { |
|
||||
this._super.apply(this, arguments); |
|
||||
this.context = context; |
|
||||
if (parent instanceof ActionManager) { |
|
||||
this.am = parent; |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
start: function () { |
|
||||
var params = this.context.params; |
|
||||
|
|
||||
var popup = new common.FormViewDialog(self, { |
|
||||
title: params.title, |
|
||||
res_model: params.res_model, |
|
||||
res_id: params.res_id, |
|
||||
}).open(); |
|
||||
popup.on('closed', this, function() { |
|
||||
this.am && this.am.history_back(); |
|
||||
}); |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
core.action_registry.add( |
|
||||
'galicea_toolset.open_edit_dialog', |
|
||||
OpenEditDialogAction |
|
||||
); |
|
||||
|
|
||||
return { |
|
||||
open_edit_dialog_action: OpenEditDialogAction, |
|
||||
}; |
|
||||
}); |
|
@ -1,49 +0,0 @@ |
|||||
odoo.define('pwste_epub.iframe_widget', function(require) { |
|
||||
|
|
||||
var AbstractField = require('web.AbstractField'); |
|
||||
var fieldRegistry = require('web.field_registry'); |
|
||||
|
|
||||
|
|
||||
|
|
||||
var core = require('web.core'); |
|
||||
var Widget= require('web.Widget'); |
|
||||
var widgetRegistry = require('web.widget_registry'); |
|
||||
var FieldManagerMixin = require('web.FieldManagerMixin'); |
|
||||
|
|
||||
var IFrameWidget = AbstractField.extend({ |
|
||||
|
|
||||
init: function () { |
|
||||
this._super.apply(this, arguments); |
|
||||
// this.set("value", "");
|
|
||||
}, |
|
||||
|
|
||||
_renderReadonly: function() { |
|
||||
window.widget=this; |
|
||||
this.$el.html( |
|
||||
$('<iframe>', { |
|
||||
src: this.value || 'about:blank', |
|
||||
style: this.attrs.iframe_style |
|
||||
}) |
|
||||
); |
|
||||
if (this.attrs.new_window_label && this.value) { |
|
||||
this.$el.prepend( |
|
||||
$('<a>', { |
|
||||
href: this.value, |
|
||||
target: '_blank', |
|
||||
style: 'float:right; margin-bottom: 10px', |
|
||||
'class': 'btn btn-primary', |
|
||||
}).html('<i class="fa fa-external-link-square" aria-hidden="true"></i> Otwórz w nowym oknie') |
|
||||
) |
|
||||
} |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
fieldRegistry.add( |
|
||||
'iframe', IFrameWidget |
|
||||
); |
|
||||
|
|
||||
return { |
|
||||
IFrameWidget: IFrameWidget, |
|
||||
}; |
|
||||
|
|
||||
}); |
|
@ -1,68 +0,0 @@ |
|||||
odoo.define('galicea_toolset.one2many_flexible_widget', function(require) { |
|
||||
var core = require('web.core'); |
|
||||
/* |
|
||||
var view_dialogs = require('web.view_dialogs'), |
|
||||
relational_fields = require('web.relational_fields'), |
|
||||
rpc = require('web.rpc'), |
|
||||
field_registry = require('web.field_registry');*/ |
|
||||
|
|
||||
var form_relational = require('web.form_relational'); |
|
||||
|
|
||||
/* var X2ManyList = form_relational.X2ManyList; |
|
||||
var ListView = require('web.ListView'); |
|
||||
var FieldOne2Many = field_registry.get('one2many'); |
|
||||
var FieldOne2Many = relational_fields.FieldOne2Many; |
|
||||
|
|
||||
|
|
||||
var FormController = require('web.FormController'); |
|
||||
*/ |
|
||||
/* |
|
||||
ListView.include({ |
|
||||
do_activate_record: function (index, id, dataset, view) { |
|
||||
var action = this.ViewManager.action; |
|
||||
if (!action || !action.context || !action.context.open_formview) |
|
||||
return this._super(index, id, dataset, view); |
|
||||
do_action(this, id, action.context); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
|
|
||||
var One2ManyListView = core.one2many_view_registry.get('list'); |
|
||||
*/ |
|
||||
|
|
||||
var One2ManyFlexibleListView = form_relational.One2ManyListView.extend({ |
|
||||
do_activate_record: function(index, id) { |
|
||||
var self = this; |
|
||||
if (!this.x2m.get("effective_readonly")) { |
|
||||
this._super.apply(this, arguments); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
this.do_action({ |
|
||||
'type': 'ir.actions.act_window', |
|
||||
'views': [[false, 'form']], |
|
||||
'res_model': self.x2m.field.relation, |
|
||||
'res_id': id, |
|
||||
'target': self.x2m.node.attrs.click_target || 'current', |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
var FieldOne2Many = core.form_widget_registry.get('one2many'); |
|
||||
|
|
||||
var FieldOne2ManyFlexible = FieldOne2Many.extend({ |
|
||||
init: function() { |
|
||||
this._super.apply(this, arguments); |
|
||||
this.x2many_views = { |
|
||||
kanban: core.view_registry.get('one2many_kanban'), |
|
||||
list: One2ManyFlexibleListView, |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
core.form_widget_registry.add('one2many_flexible', FieldOne2ManyFlexible); |
|
||||
|
|
||||
return { |
|
||||
FieldOne2ManyFlexible: FieldOne2ManyFlexible, |
|
||||
}; |
|
||||
}); |
|
@ -1,16 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
|
|
||||
from odoo import http |
|
||||
|
|
||||
def get_base_url(env): |
|
||||
""" |
|
||||
Better host name detection |
|
||||
@param env odoo.api.Environment""" |
|
||||
|
|
||||
if http.request: |
|
||||
# Preferuj nazwę hosta, która została użyta do tego zapytania |
|
||||
return http.request.httprequest.host_url |
|
||||
else: |
|
||||
# Jeśli nie jesteśmy wewnątrz zapytania HTTP, zwróć domenę ostatnio użytą |
|
||||
# przez admina do zalogowania |
|
||||
return env['ir.config_parameter'].get_param('web.base.url') + '/' |
|
@ -1,11 +0,0 @@ |
|||||
<odoo> |
|
||||
<data> |
|
||||
<template id="assets_backend" inherit_id="web.assets_backend"> |
|
||||
<xpath expr="." position="inside"> |
|
||||
<script src="/galicea_toolset/static/src/js/iframe_widget.js" type="text/javascript" /> |
|
||||
<script src="/galicea_toolset/static/src/js/one2many_flexible_widget.js" type="text/javascript" /> |
|
||||
<script src="/galicea_toolset/static/src/js/client_actions.js" type="text/javascript" /> |
|
||||
</xpath> |
|
||||
</template> |
|
||||
</data> |
|
||||
</odoo> |
|