Simplify module information with python configuration
review363001
This commit is contained in:
1 parent
1aae951f92
commit
a09d9a54a1
21 files changed
+189
-526
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Replace __tryton__.py by tryton.cfg
|
||||
* Allow to use Reference field in One2Many & Many2Many
|
||||
* Remove hexmd5 from ModelView.fields_view_get
|
||||
* Allow client to manage model access
|
||||
|
||||
@@ -10,6 +10,7 @@ recursive-include doc *.po
|
||||
recursive-include doc *.pot
|
||||
include etc/*
|
||||
include trytond/backend/*/init.sql
|
||||
include trytond/ir/tryton.cfg
|
||||
include trytond/ir/*.xml
|
||||
include trytond/ir/locale/*.po
|
||||
include trytond/ir/module/*.xml
|
||||
@@ -17,8 +18,11 @@ include trytond/ir/ui/*.xml
|
||||
include trytond/ir/ui/*.rng
|
||||
include trytond/ir/ui/*.rnc
|
||||
include trytond/ir/ui/icons/*.svg
|
||||
include trytond/res/tryton.cfg
|
||||
include trytond/res/*.xml
|
||||
include trytond/res/locale/*.po
|
||||
include trytond/test/tryton.cfg
|
||||
include trytond/test/*.xml
|
||||
include trytond/webdav/tryton.cfg
|
||||
include trytond/webdav/*.xml
|
||||
include trytond/webdav/locale/*.po
|
||||
@@ -15,7 +15,7 @@ A module is a directory in trytond/modules which contains at least two files:
|
||||
|
||||
* ``__init__.py``: a Tryton module must be a Python module.
|
||||
|
||||
* ``__tryton__.py``: a Python file that describes the Tryton module.
|
||||
* ``tryton.cfg``: a Configuration file that describes the Tryton module.
|
||||
|
||||
``__init__.py`` file
|
||||
--------------------
|
||||
@@ -24,69 +24,35 @@ It is the Python ``__init__.py`` to define a module. It must import all the
|
||||
Python files from the module.
|
||||
|
||||
|
||||
``__tryton__.py`` file
|
||||
----------------------
|
||||
``tryton.cfg`` file
|
||||
-------------------
|
||||
|
||||
It is a Python file that must contain only one dictionary with those keywords:
|
||||
|
||||
* ``name``: The name of the module.
|
||||
|
||||
* ``name_language_code``: The name of the module in the language code.
|
||||
It is a configuration file using the format of `ConfigParser`_ that must
|
||||
contain `tryton` section with this following name:
|
||||
|
||||
* ``version``: The version number of the module.
|
||||
|
||||
* ``author``: The author name of the module.
|
||||
* ``depends``: A one per line list of modules on which this module depends.
|
||||
|
||||
* ``email``: The email address of the author (optional).
|
||||
* ``extras_depend``: A one per line list of modules on which this module
|
||||
*may* depend.
|
||||
|
||||
* ``website``: The url of the website for the module (optional).
|
||||
* ``xml``: The one per line list of the XML files of the module. They will
|
||||
be loaded in the given order at the installation or update of the module.
|
||||
|
||||
* ``description``: A long description of the module.
|
||||
Here is an example::
|
||||
|
||||
* ``description_language_code``: The long description in the language code.
|
||||
|
||||
* ``depends``: A list of modules on which this module depends.
|
||||
|
||||
* ``extras_depend``: A list of modules on which this module *may* depend.
|
||||
|
||||
* ``xml``: The list of the XML files of the module. They will be loaded in
|
||||
the given order at the installation or update of the module.
|
||||
|
||||
* ``translation``: The list of csv files that contain the translation. The
|
||||
name of the files must be the language code.
|
||||
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. highlight:: python
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"name" : "Party",
|
||||
"version" : "0.0.1",
|
||||
"author" : "B2CK",
|
||||
'email': 'info@b2ck.com',
|
||||
'website': 'http://www.tryton.org/',
|
||||
"category" : "Generic",
|
||||
"description": "Define parties, addresses and co.",
|
||||
"depends" : [
|
||||
"ir",
|
||||
"res",
|
||||
"country",
|
||||
],
|
||||
"xml" : [
|
||||
"party.xml",
|
||||
"category.xml",
|
||||
"address.xml",
|
||||
"contact_mechanism.xml",
|
||||
],
|
||||
'translation': [
|
||||
'fr_FR.csv',
|
||||
'de_DE.csv',
|
||||
'es_ES.csv',
|
||||
],
|
||||
}
|
||||
[tryton]
|
||||
version=0.0.1
|
||||
depends:
|
||||
ir
|
||||
res
|
||||
country
|
||||
xml:
|
||||
party.xml
|
||||
category.xml
|
||||
address.xml
|
||||
contact_mechanism.xml
|
||||
|
||||
Python Files
|
||||
============
|
||||
@@ -194,3 +160,6 @@ Here is the list of the tags:
|
||||
separated by commas.
|
||||
|
||||
* ``active``: A boolean telling if the menu is active or not.
|
||||
|
||||
|
||||
.. _ConfigParser: http://docs.python.org/library/configparser.html
|
||||
@@ -15,11 +15,10 @@ setup(name=PACKAGE,
|
||||
version=VERSION,
|
||||
description='Tryton server',
|
||||
long_description=read('README'),
|
||||
author='B2CK',
|
||||
author_email='info@b2ck.com',
|
||||
author='Tryton',
|
||||
url=WEBSITE,
|
||||
download_url="http://downloads.tryton.org/" + \
|
||||
VERSION.rsplit('.', 1)[0] + '/',
|
||||
VERSION.rsplit('.', 1)[0] + '/',
|
||||
packages=find_packages(exclude=['*.modules.*', 'modules.*', 'modules',
|
||||
'*.proteus.*', 'proteus.*', 'proteus']),
|
||||
package_data={
|
||||
@@ -27,13 +26,13 @@ setup(name=PACKAGE,
|
||||
'trytond.backend.mysql': ['init.sql'],
|
||||
'trytond.backend.postgresql': ['init.sql'],
|
||||
'trytond.backend.sqlite': ['init.sql'],
|
||||
'trytond.ir': ['*.xml', 'locale/*.po'],
|
||||
'trytond.ir': ['tryton.cfg', '*.xml', 'locale/*.po'],
|
||||
'trytond.ir.module': ['*.xml'],
|
||||
'trytond.ir.ui': ['*.xml', '*.rng', '*.rnc'],
|
||||
'trytond.res': ['*.xml', 'locale/*.po'],
|
||||
'trytond.webdav': ['*.xml', 'locale/*.po'],
|
||||
'trytond.test': ['*.xml'],
|
||||
},
|
||||
'trytond.res': ['tryton.cfg', '*.xml', 'locale/*.po'],
|
||||
'trytond.webdav': ['tryton.cfg', '*.xml', 'locale/*.po'],
|
||||
'trytond.test': ['tryton.cfg', '*.xml'],
|
||||
},
|
||||
scripts=['bin/trytond'],
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
@@ -53,7 +52,7 @@ setup(name=PACKAGE,
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Software Development :: Libraries :: Application Frameworks',
|
||||
],
|
||||
],
|
||||
license=LICENSE,
|
||||
install_requires=[
|
||||
'lxml >= 2.0',
|
||||
@@ -61,7 +60,7 @@ setup(name=PACKAGE,
|
||||
'Genshi',
|
||||
'python-dateutil',
|
||||
'polib',
|
||||
],
|
||||
],
|
||||
extras_require={
|
||||
'PostgreSQL': ['psycopg2 >= 2.0'],
|
||||
'MySQL': ['MySQL-python'],
|
||||
@@ -72,7 +71,7 @@ setup(name=PACKAGE,
|
||||
'timezone': ['pytz'],
|
||||
'simplejson': ['simplejson'],
|
||||
'cdecimal': ['cdecimal'],
|
||||
},
|
||||
},
|
||||
zip_safe=False,
|
||||
test_suite='trytond.tests',
|
||||
test_loader='trytond.test_loader:Loader',
|
||||
|
||||
@@ -171,38 +171,29 @@ class Database(DatabaseInterface):
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.modules import get_module_info
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
if (len(line) > 0) and (not line.isspace()):
|
||||
cursor.execute(line)
|
||||
|
||||
for i in ('ir', 'res', 'webdav'):
|
||||
root_path = os.path.join(os.path.dirname(__file__), '..', '..')
|
||||
tryton_file = os.path.join(root_path, i, '__tryton__.py')
|
||||
with open(tryton_file) as fp:
|
||||
info = safe_eval(fp.read())
|
||||
active = info.get('active', False)
|
||||
if active:
|
||||
for module in ('ir', 'res', 'webdav'):
|
||||
state = 'uninstalled'
|
||||
if module in ('ir', 'res'):
|
||||
state = 'to install'
|
||||
else:
|
||||
state = 'uninstalled'
|
||||
cursor.execute('INSERT INTO ir_module_module ' \
|
||||
'(create_uid, create_date, author, website, name, ' \
|
||||
'shortdesc, description, state) ' \
|
||||
'VALUES (%s, now(), %s, %s, %s, %s, %s, %s)',
|
||||
(0, info.get('author', ''),
|
||||
info.get('website', ''), i, info.get('name', False),
|
||||
info.get('description', ''), state))
|
||||
info = get_module_info(module)
|
||||
cursor.execute('INSERT INTO ir_module_module '
|
||||
'(create_uid, create_date, name, state) '
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module, state))
|
||||
cursor.execute('SELECT LAST_INSERT_ID()')
|
||||
module_id = cursor.fetchone()[0]
|
||||
dependencies = info.get('depends', [])
|
||||
for dependency in dependencies:
|
||||
cursor.execute('INSERT INTO ir_module_module_dependency ' \
|
||||
'(create_uid, create_date, module, name) ' \
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module_id, dependency))
|
||||
module_id, = cursor.fetchone()
|
||||
for dependency in info.get('depends', []):
|
||||
cursor.execute('INSERT INTO ir_module_module_dependency '
|
||||
'(create_uid, create_date, module, name) '
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module_id, dependency))
|
||||
|
||||
|
||||
class _Cursor(MySQLdb.cursors.Cursor):
|
||||
|
||||
@@ -97,13 +97,8 @@ CREATE TABLE ir_module_module (
|
||||
create_date TIMESTAMP NOT NULL,
|
||||
write_date TIMESTAMP NULL,
|
||||
write_uid BIGINT,
|
||||
website VARCHAR(255),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
author VARCHAR(255),
|
||||
url VARCHAR(255),
|
||||
state VARCHAR(255),
|
||||
shortdesc VARCHAR(255),
|
||||
description TEXT,
|
||||
PRIMARY KEY(id),
|
||||
CONSTRAINT ir_module_module_create_uid_fkey FOREIGN KEY (create_uid) REFERENCES res_user (id) ON DELETE SET NULL,
|
||||
CONSTRAINT ir_module_module_write_uid_fkey FOREIGN KEY (write_uid) REFERENCES res_user (id) ON DELETE SET NULL
|
||||
|
||||
@@ -223,38 +223,29 @@ class Database(DatabaseInterface):
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.modules import get_module_info
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
if (len(line) > 0) and (not line.isspace()):
|
||||
cursor.execute(line)
|
||||
|
||||
for i in ('ir', 'res', 'webdav'):
|
||||
root_path = os.path.join(os.path.dirname(__file__), '..', '..')
|
||||
tryton_file = os.path.join(root_path, i, '__tryton__.py')
|
||||
with open(tryton_file) as fp:
|
||||
info = safe_eval(fp.read())
|
||||
active = info.get('active', False)
|
||||
if active:
|
||||
for module in ('ir', 'res', 'webdav'):
|
||||
state = 'uninstalled'
|
||||
if module in ('ir', 'res'):
|
||||
state = 'to install'
|
||||
else:
|
||||
state = 'uninstalled'
|
||||
info = get_module_info(module)
|
||||
cursor.execute('SELECT NEXTVAL(\'ir_module_module_id_seq\')')
|
||||
module_id = cursor.fetchone()[0]
|
||||
cursor.execute('INSERT INTO ir_module_module ' \
|
||||
'(id, create_uid, create_date, author, website, name, ' \
|
||||
'shortdesc, description, state) ' \
|
||||
'VALUES (%s, %s, now(), %s, %s, %s, %s, %s, %s)',
|
||||
(module_id, 0, info.get('author', ''),
|
||||
info.get('website', ''), i, info.get('name', False),
|
||||
info.get('description', ''), state))
|
||||
dependencies = info.get('depends', [])
|
||||
for dependency in dependencies:
|
||||
cursor.execute('INSERT INTO ir_module_module_dependency ' \
|
||||
'(create_uid, create_date, module, name) ' \
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module_id, dependency))
|
||||
cursor.execute('INSERT INTO ir_module_module '
|
||||
'(id, create_uid, create_date, name, state) '
|
||||
'VALUES (%s, %s, now(), %s, %s)',
|
||||
(module_id, 0, module, state))
|
||||
for dependency in info.get('depends', []):
|
||||
cursor.execute('INSERT INTO ir_module_module_dependency '
|
||||
'(create_uid, create_date, module, name) '
|
||||
'VALUES (%s, now(), %s, %s)',
|
||||
(0, module_id, dependency))
|
||||
|
||||
|
||||
class _Cursor(PsycopgCursor):
|
||||
|
||||
@@ -117,13 +117,8 @@ CREATE TABLE ir_module_module (
|
||||
create_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
write_date TIMESTAMP WITHOUT TIME ZONE,
|
||||
write_uid INTEGER,
|
||||
website VARCHAR,
|
||||
name VARCHAR NOT NULL,
|
||||
author VARCHAR,
|
||||
url VARCHAR,
|
||||
state VARCHAR,
|
||||
shortdesc VARCHAR,
|
||||
description TEXT,
|
||||
PRIMARY KEY(id),
|
||||
FOREIGN KEY (create_uid) REFERENCES res_user ON DELETE SET NULL,
|
||||
FOREIGN KEY (write_uid) REFERENCES res_user ON DELETE SET NULL
|
||||
|
||||
@@ -215,38 +215,29 @@ class Database(DatabaseInterface):
|
||||
|
||||
@staticmethod
|
||||
def init(cursor):
|
||||
from trytond.tools import safe_eval
|
||||
from trytond.modules import get_module_info
|
||||
sql_file = os.path.join(os.path.dirname(__file__), 'init.sql')
|
||||
with open(sql_file) as fp:
|
||||
for line in fp.read().split(';'):
|
||||
if (len(line) > 0) and (not line.isspace()):
|
||||
cursor.execute(line)
|
||||
|
||||
for i in ('ir', 'res', 'webdav'):
|
||||
root_path = os.path.join(os.path.dirname(__file__), '..', '..')
|
||||
tryton_file = os.path.join(root_path, i, '__tryton__.py')
|
||||
with open(tryton_file) as fp:
|
||||
info = safe_eval(fp.read())
|
||||
active = info.get('active', False)
|
||||
if active:
|
||||
for module in ('ir', 'res', 'webdav'):
|
||||
state = 'uninstalled'
|
||||
if module in ('ir', 'res'):
|
||||
state = 'to install'
|
||||
else:
|
||||
state = 'uninstalled'
|
||||
cursor.execute('INSERT INTO ir_module_module ' \
|
||||
'(create_uid, create_date, author, website, name, ' \
|
||||
'shortdesc, description, state) ' \
|
||||
'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
|
||||
(0, datetime.datetime.now(), info.get('author', ''),
|
||||
info.get('website', ''), i, info.get('name', False),
|
||||
info.get('description', ''), state))
|
||||
info = get_module_info(module)
|
||||
cursor.execute('INSERT INTO ir_module_module '
|
||||
'(create_uid, create_date, name, state) '
|
||||
'VALUES (%s, %s, %s, %s)',
|
||||
(0, datetime.datetime.now(), module, state))
|
||||
cursor.execute('SELECT last_insert_rowid()')
|
||||
module_id = cursor.fetchone()[0]
|
||||
dependencies = info.get('depends', [])
|
||||
for dependency in dependencies:
|
||||
cursor.execute('INSERT INTO ir_module_module_dependency ' \
|
||||
'(create_uid, create_date, module, name) ' \
|
||||
'VALUES (%s, %s, %s, %s) ',
|
||||
(0, datetime.datetime.now(), module_id, dependency))
|
||||
module_id, = cursor.fetchone()
|
||||
for dependency in info.get('depends', []):
|
||||
cursor.execute('INSERT INTO ir_module_module_dependency '
|
||||
'(create_uid, create_date, module, name) '
|
||||
'VALUES (%s, %s, %s, %s) ',
|
||||
(0, datetime.datetime.now(), module_id, dependency))
|
||||
|
||||
|
||||
class _Cursor(sqlite.Cursor):
|
||||
|
||||
@@ -83,13 +83,8 @@ CREATE TABLE ir_module_module (
|
||||
create_date TIMESTAMP,
|
||||
write_date TIMESTAMP,
|
||||
write_uid INTEGER,
|
||||
website VARCHAR,
|
||||
name VARCHAR,
|
||||
author VARCHAR,
|
||||
url VARCHAR,
|
||||
state VARCHAR,
|
||||
shortdesc VARCHAR,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE ir_module_module_dependency (
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
{
|
||||
'name': 'IR',
|
||||
'name_bg_BG': 'Вътрешна администрация',
|
||||
'name_ca_ES': 'Administració interna',
|
||||
'name_de_DE': 'Interne Administration',
|
||||
'name_es_AR': 'Administración interna',
|
||||
'name_es_ES': 'Administración interna',
|
||||
'name_fr_FR': 'Administration Interne',
|
||||
'name_nl_NL': 'Interne administratie',
|
||||
'name_ru_RU': 'Внутреннее управление',
|
||||
'description': '''Basic module handling internal tasks of the application.
|
||||
Provides concepts and administration of models, actions, sequences, localizations, cron jobs etc.
|
||||
''',
|
||||
'description_bg_BG': '''Общ модул за вътрешните задачи на приложението
|
||||
|
||||
- Предлага предсатва и адмнинистрация на модули, десйтвия, последователности, преводи, планировщици и тн.
|
||||
''',
|
||||
'description_ca_ES': '''Mòdul bàsic que gestiona les tasques internes de l'aplicació.
|
||||
Proporciona els conceptes i administració de models, accions, seqüències, localització, tasques planificades, etc.
|
||||
''',
|
||||
'description_de_DE': '''Basismodul für interne Aufgaben der Anwendung
|
||||
|
||||
- Stellt Konzept und Administration für Modelle, Aktionen, Sequenzen, Lokalisierungen, Zeitplaner etc. zur Verfügung
|
||||
''',
|
||||
'description_es_AR': '''Módulo básico que gestiona las tareas internas de la aplicación.
|
||||
- Provee los conceptos y administración de modelos, acciones, secuencias, localización, tareas programadas, etc...
|
||||
''',
|
||||
'description_es_ES': '''Módulo básico que gestiona las tareas internas de la aplicación.
|
||||
Proporciona los conceptos y administración de modelos, acciones, secuencias, localización, tareas planificadas, etc.
|
||||
''',
|
||||
'description_fr_FR': '''Module de base gérant les tâches internes de l'application.
|
||||
Fournit les concepts et l'administration des modèles, actions, séquences, localizations, planificateur de tâches etc.
|
||||
''',
|
||||
'description_nl_NL': '''Basismodule voor interne taken binnen het programma.
|
||||
|
||||
- Instellen en beheren van modellen, acties, reeksen, lokalisaties, herhalende taken enz.
|
||||
''',
|
||||
'description_ru_RU': '''Базовый модуль обработки внутренних задач приложения.
|
||||
Обеспечивает концепций и моделей, администрация, действия, последовательность, локализация, Cron Jobs т.д.
|
||||
''',
|
||||
'active': True,
|
||||
'xml': [
|
||||
'ir.xml',
|
||||
'ui/ui.xml',
|
||||
'ui/icon.xml',
|
||||
'ui/menu.xml',
|
||||
'ui/view.xml',
|
||||
'action.xml',
|
||||
'model.xml',
|
||||
'sequence.xml',
|
||||
'attachment.xml',
|
||||
'cron.xml',
|
||||
'lang.xml',
|
||||
'translation.xml',
|
||||
'export.xml',
|
||||
'rule.xml',
|
||||
'property.xml',
|
||||
'module/module.xml',
|
||||
'trigger.xml',
|
||||
],
|
||||
'translation': [
|
||||
'locale/cs_CZ.po',
|
||||
'locale/bg_BG.po',
|
||||
'locale/ca_ES.po',
|
||||
'locale/de_DE.po',
|
||||
'locale/es_AR.po',
|
||||
'locale/es_CO.po',
|
||||
'locale/es_ES.po',
|
||||
'locale/fr_FR.po',
|
||||
'locale/nl_NL.po',
|
||||
'locale/ru_RU.po',
|
||||
],
|
||||
}
|
||||
+28
-114
@@ -1,9 +1,7 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import os
|
||||
from trytond.model import ModelView, ModelSQL, fields
|
||||
import trytond.tools as tools
|
||||
from trytond.modules import create_graph, get_module_list
|
||||
from trytond.modules import create_graph, get_module_list, get_module_info
|
||||
from trytond.wizard import Wizard, StateView, Button, StateTransition, \
|
||||
StateAction
|
||||
from trytond.backend import TableHandler
|
||||
@@ -17,10 +15,6 @@ class Module(ModelSQL, ModelView):
|
||||
_name = "ir.module.module"
|
||||
_description = __doc__
|
||||
name = fields.Char("Name", readonly=True, required=True)
|
||||
shortdesc = fields.Char('Short description', readonly=True, translate=True)
|
||||
description = fields.Text("Description", readonly=True, translate=True)
|
||||
author = fields.Char("Author", readonly=True)
|
||||
website = fields.Char("Website", readonly=True)
|
||||
version = fields.Function(fields.Char('Version'), 'get_version')
|
||||
dependencies = fields.One2Many('ir.module.module.dependency',
|
||||
'module', 'Dependencies', readonly=True)
|
||||
@@ -77,26 +71,10 @@ class Module(ModelSQL, ModelView):
|
||||
def default_state(self):
|
||||
return 'uninstalled'
|
||||
|
||||
@staticmethod
|
||||
def get_module_info(name):
|
||||
"Return the content of the __tryton__.py"
|
||||
try:
|
||||
if name in ['ir', 'res', 'webdav']:
|
||||
file_p = tools.file_open(os.path.join(name, '__tryton__.py'))
|
||||
else:
|
||||
file_p = tools.file_open(os.path.join(name, '__tryton__.py'))
|
||||
with file_p:
|
||||
data = file_p.read()
|
||||
info = tools.safe_eval(data)
|
||||
except Exception:
|
||||
return {}
|
||||
return info
|
||||
|
||||
def get_version(self, ids, name):
|
||||
res = {}
|
||||
for module in self.browse(ids):
|
||||
res[module.id] = Module.get_module_info(
|
||||
module.name).get('version', '')
|
||||
res[module.id] = get_module_info(module.name).get('version', '')
|
||||
return res
|
||||
|
||||
def get_parents(self, ids, name):
|
||||
@@ -263,102 +241,38 @@ class Module(ModelSQL, ModelView):
|
||||
|
||||
# update the list of available packages
|
||||
def update_list(self):
|
||||
pool = Pool()
|
||||
lang_obj = pool.get('ir.lang')
|
||||
res = 0
|
||||
with Transaction().set_context(language=False):
|
||||
lang_ids = lang_obj.search([
|
||||
('translatable', '=', True),
|
||||
])
|
||||
lang_codes = [x.code for x in lang_obj.browse(lang_ids)]
|
||||
count = 0
|
||||
module_names = get_module_list()
|
||||
|
||||
module_names = get_module_list()
|
||||
module_ids = self.search([])
|
||||
modules = self.browse(module_ids)
|
||||
name2module = dict((m.name, m) for m in modules)
|
||||
|
||||
module_ids = self.search([])
|
||||
modules = self.browse(module_ids)
|
||||
name2module = {}
|
||||
for module in modules:
|
||||
name2module.setdefault(module.name, {})
|
||||
name2module[module.name]['en_US'] = module
|
||||
for code in lang_codes:
|
||||
with Transaction().set_context(language=code):
|
||||
modules = self.browse(module_ids)
|
||||
for module in modules:
|
||||
name2module[module.name][code] = module
|
||||
# iterate through installed modules and mark them as being so
|
||||
for name in module_names:
|
||||
if name in name2module:
|
||||
module = name2module[name]
|
||||
tryton = get_module_info(name)
|
||||
self._update_dependencies(module, tryton.get('depends', []))
|
||||
continue
|
||||
|
||||
# iterate through installed modules and mark them as being so
|
||||
for name in module_names:
|
||||
mod_name = name
|
||||
if mod_name in name2module.keys():
|
||||
mod = name2module[mod_name]['en_US']
|
||||
tryton = Module.get_module_info(mod_name)
|
||||
|
||||
if mod.description != tryton.get('description',
|
||||
'').decode('utf-8', 'ignore') \
|
||||
or mod.shortdesc != tryton.get('name',
|
||||
'').decode('utf-8', 'ignore') \
|
||||
or mod.author != tryton.get('author',
|
||||
'').decode('utf-8', 'ignore') \
|
||||
or mod.website != tryton.get('website',
|
||||
'').decode('utf-8', 'ignore'):
|
||||
self.write(mod.id, {
|
||||
'description': tryton.get('description', ''),
|
||||
'shortdesc': tryton.get('name', ''),
|
||||
'author': tryton.get('author', ''),
|
||||
'website': tryton.get('website', ''),
|
||||
})
|
||||
|
||||
for code in lang_codes:
|
||||
mod2 = name2module[mod_name][code]
|
||||
if mod2.description != \
|
||||
tryton.get('description_' + code,
|
||||
tryton.get('description', '')
|
||||
).decode('utf-8', 'ignore') \
|
||||
or mod2.shortdesc != \
|
||||
tryton.get('name_' + code,
|
||||
tryton.get('name', '')
|
||||
).decode('utf-8', 'ignore'):
|
||||
with Transaction().set_context(language=code):
|
||||
self.write(mod.id, {
|
||||
'description': tryton.get(
|
||||
'description_' + code, ''),
|
||||
'shortdesc': tryton.get(
|
||||
'name_' + code, ''),
|
||||
})
|
||||
|
||||
self._update_dependencies(mod, tryton.get('depends', []))
|
||||
continue
|
||||
|
||||
tryton = Module.get_module_info(mod_name)
|
||||
if not tryton:
|
||||
continue
|
||||
new_id = self.create({
|
||||
'name': mod_name,
|
||||
tryton = get_module_info(name)
|
||||
if not tryton:
|
||||
continue
|
||||
module_id = self.create({
|
||||
'name': name,
|
||||
'state': 'uninstalled',
|
||||
'description': tryton.get('description', ''),
|
||||
'shortdesc': tryton.get('name', ''),
|
||||
'author': tryton.get('author', 'Unknown'),
|
||||
'website': tryton.get('website', ''),
|
||||
})
|
||||
for code in lang_codes:
|
||||
with Transaction().set_context(language=code):
|
||||
self.write(new_id, {
|
||||
'description': tryton.get(
|
||||
'description_' + code, ''),
|
||||
'shortdesc': tryton.get('name_' + code, ''),
|
||||
})
|
||||
res += 1
|
||||
name2module.setdefault(mod_name, {})
|
||||
name2module[mod_name]['en_US'] = self.browse(new_id)
|
||||
self._update_dependencies(name2module[mod_name]['en_US'],
|
||||
tryton.get('depends', []))
|
||||
return res
|
||||
})
|
||||
count += 1
|
||||
module = self.browse(module_id)
|
||||
self._update_dependencies(module, tryton.get('depends', []))
|
||||
return count
|
||||
|
||||
def _update_dependencies(self, module, depends=None):
|
||||
pool = Pool()
|
||||
dependency_obj = pool.get('ir.module.module.dependency')
|
||||
dependency_obj.delete([x.id for x in module.dependencies
|
||||
if x.name not in depends])
|
||||
if x.name not in depends])
|
||||
if depends is None:
|
||||
depends = []
|
||||
# Restart Browse Cache for deleted dependencies
|
||||
@@ -367,9 +281,9 @@ class Module(ModelSQL, ModelView):
|
||||
for depend in depends:
|
||||
if depend not in dependency_names:
|
||||
dependency_obj.create({
|
||||
'module': module.id,
|
||||
'name': depend,
|
||||
})
|
||||
'module': module.id,
|
||||
'name': depend,
|
||||
})
|
||||
|
||||
Module()
|
||||
|
||||
|
||||
@@ -12,23 +12,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<form string="Module" on_write="on_write">
|
||||
<label name="name"/>
|
||||
<field name="name"/>
|
||||
<label name="shortdesc"/>
|
||||
<field name="shortdesc"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Module" id="module">
|
||||
<separator name="description" colspan="4"/>
|
||||
<field name="description" colspan="4"/>
|
||||
<label name="version"/>
|
||||
<field name="version"/>
|
||||
<label name="author"/>
|
||||
<field name="author"/>
|
||||
<label name="website"/>
|
||||
<field name="website" widget="url"/>
|
||||
</page>
|
||||
<page string="Dependencies" col="1" id="dependencies">
|
||||
<field name="dependencies"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<label name="version"/>
|
||||
<field name="version"/>
|
||||
<field name="dependencies" colspan="4"/>
|
||||
<label name="state"/>
|
||||
<field name="state" readonly="1"/>
|
||||
<group col="2" colspan="2" id="button">
|
||||
@@ -56,13 +42,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<![CDATA[
|
||||
<tree string="Modules" on_write="on_write"
|
||||
colors="If(In(Eval('state', ''), ['to upgrade', 'to install']), 'blue', If(Equal(Eval('state', ''), 'uninstalled'), 'grey', 'black'))">
|
||||
<field name="name"/>
|
||||
<field name="shortdesc"/>
|
||||
<field name="author"/>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="version"/>
|
||||
<field name="website"/>
|
||||
<field name="state"/>
|
||||
<field name="description" tree_invisible="1"/>
|
||||
<button string="Mark for Upgrade" name="upgrade"
|
||||
type="object"/>
|
||||
<button string="Mark for Installation" name="install"
|
||||
@@ -121,7 +103,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<![CDATA[
|
||||
<tree string="Dependencies">
|
||||
<field name="module"/>
|
||||
<field name="name"/>
|
||||
<field name="name" expand="1"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
]]>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[tryton]
|
||||
xml:
|
||||
ir.xml
|
||||
ui/ui.xml
|
||||
ui/icon.xml
|
||||
ui/menu.xml
|
||||
ui/view.xml
|
||||
action.xml
|
||||
model.xml
|
||||
sequence.xml
|
||||
attachment.xml
|
||||
cron.xml
|
||||
lang.xml
|
||||
translation.xml
|
||||
export.xml
|
||||
rule.xml
|
||||
property.xml
|
||||
module/module.xml
|
||||
trigger.xml
|
||||
+29
-29
@@ -8,6 +8,9 @@ import contextlib
|
||||
from functools import reduce
|
||||
import imp
|
||||
import operator
|
||||
import ConfigParser
|
||||
from glob import iglob
|
||||
|
||||
import trytond.tools as tools
|
||||
from trytond.config import CONFIG
|
||||
from trytond.transaction import Transaction
|
||||
@@ -125,35 +128,32 @@ class Node(Singleton):
|
||||
return res
|
||||
|
||||
|
||||
def get_module_info(name):
|
||||
"Return the content of the tryton.cfg"
|
||||
config = ConfigParser.ConfigParser()
|
||||
with tools.file_open(os.path.join(name, 'tryton.cfg')) as fp:
|
||||
config.readfp(fp)
|
||||
directory = os.path.dirname(fp.name)
|
||||
info = dict(config.items('tryton'))
|
||||
info['directory'] = directory
|
||||
for key in ('depends', 'extras_depend', 'xml'):
|
||||
if key in info:
|
||||
info[key] = info[key].strip().splitlines()
|
||||
return info
|
||||
|
||||
|
||||
def create_graph(module_list):
|
||||
graph = Graph()
|
||||
packages = []
|
||||
|
||||
for module in module_list:
|
||||
tryton_file = OPJ(MODULES_PATH, module, '__tryton__.py')
|
||||
mod_path = OPJ(MODULES_PATH, module)
|
||||
if module in ('ir', 'res', 'webdav', 'test'):
|
||||
root_path = os.path.abspath(os.path.dirname(
|
||||
os.path.dirname(__file__)))
|
||||
tryton_file = OPJ(root_path, module, '__tryton__.py')
|
||||
mod_path = OPJ(root_path, module)
|
||||
elif module in EGG_MODULES:
|
||||
ep = EGG_MODULES[module]
|
||||
tryton_file = OPJ(ep.dist.location, 'trytond', 'modules', module,
|
||||
'__tryton__.py')
|
||||
mod_path = OPJ(ep.dist.location, 'trytond', 'modules', module)
|
||||
if not os.path.isfile(tryton_file) or not os.path.isdir(mod_path):
|
||||
# When testing modules from setuptools location is the module
|
||||
# directory
|
||||
tryton_file = OPJ(ep.dist.location, '__tryton__.py')
|
||||
mod_path = os.path.dirname(ep.dist.location)
|
||||
if os.path.isfile(tryton_file):
|
||||
with tools.file_open(tryton_file, subdir='') as fp:
|
||||
info = tools.safe_eval(fp.read())
|
||||
packages.append((module, info.get('depends', []),
|
||||
info.get('extras_depend', []), info))
|
||||
elif module != 'all':
|
||||
raise Exception('Module %s not found' % module)
|
||||
try:
|
||||
info = get_module_info(module)
|
||||
except IOError:
|
||||
if module != 'all':
|
||||
raise Exception('Module %s not found' % module)
|
||||
packages.append((module, info.get('depends', []),
|
||||
info.get('extras_depend', []), info))
|
||||
|
||||
current, later = set([x[0] for x in packages]), set()
|
||||
all_packages = set(current)
|
||||
@@ -242,16 +242,16 @@ def load_module_graph(graph, pool, lang=None):
|
||||
|
||||
modules_todo.append((module, list(tryton_parser.to_delete)))
|
||||
|
||||
for filename in package.info.get('translation', []):
|
||||
for filename in iglob('%s/%s/*.po'
|
||||
% (package.info['directory'], 'locale')):
|
||||
filename = filename.replace('/', os.sep)
|
||||
lang2 = os.path.splitext(os.path.basename(filename))[0]
|
||||
if lang2 not in lang:
|
||||
continue
|
||||
logger.info('%s:loading %s' % (module, filename))
|
||||
with tools.file_open(OPJ(module, filename)) as trans_file:
|
||||
po_path = trans_file.name
|
||||
logger.info('%s:loading %s' % (module,
|
||||
filename[len(package.info['directory']) + 1:]))
|
||||
translation_obj = pool.get('ir.translation')
|
||||
translation_obj.translation_import(lang2, module, po_path)
|
||||
translation_obj.translation_import(lang2, module, filename)
|
||||
|
||||
cursor.execute("UPDATE ir_module_module SET state = 'installed' " \
|
||||
"WHERE name = %s", (package.name,))
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
{
|
||||
'name': 'Res',
|
||||
'name_bg_BG': 'Ресурс',
|
||||
'name_ca_ES': 'Recursos interns',
|
||||
'name_de_DE': 'Benutzerverwaltung',
|
||||
'name_es_AR': 'Gestión de usuarios',
|
||||
'name_es_ES': 'Recursos internos',
|
||||
'name_fr_FR': 'Ressource',
|
||||
'name_nl_NL': 'Interne handelingen',
|
||||
'name_ru_RU': 'Базовые ресурсы сервера',
|
||||
'description': '''Basic module handling internal tasks of the application.
|
||||
Provides concepts and administration of users and internal communication.
|
||||
''',
|
||||
'description_bg_BG': '''Основен модул управляващ вътрешните задачи на приложението
|
||||
|
||||
- Предоставя идеи и администрация на потребители и вътрешна комуникация
|
||||
''',
|
||||
'description_ca_ES': '''Mòdul bàsic que gestiona tasques internes de l'aplicació.
|
||||
Proporciona els conceptes i administració d'usuaris i comunicació interna.
|
||||
''',
|
||||
'description_de_DE': '''Basismodul für interne Aufgaben der Anwendung
|
||||
|
||||
- Stellt Konzept und Administration für die Benutzerverwaltung und interne Kommunikation zur Verfügung
|
||||
''',
|
||||
'description_es_AR': '''Módulo básico que gestiona tareas internas de la aplicación.
|
||||
|
||||
- Provee los conceptos y administración de usuarios y comunicación interna.
|
||||
''',
|
||||
'description_es_ES': '''Módulo básico que gestiona tareas internas de la aplicación.
|
||||
Proporciona los conceptos y administración de usuarios y comunicación interna.
|
||||
''',
|
||||
'description_fr_FR': '''Module de base gérant les tâches internes de l'application.
|
||||
Fournit les concepts et l'administration des utilisateurs et de la communication interne.
|
||||
''',
|
||||
'description_nl_NL': '''Basis module voor de afhandeling van interne taken.
|
||||
Regelt instellingen en beheer van gebruikers en interne communicatie.
|
||||
''',
|
||||
'description_ru_RU': '''Базовый модуль обработки внутренних задач приложения.
|
||||
Обеспечивает концепции и администрирование пользователей и внутренней связи.
|
||||
''',
|
||||
'active': True,
|
||||
'depends': ['ir'],
|
||||
'xml': [
|
||||
'res.xml',
|
||||
'group.xml',
|
||||
'user.xml',
|
||||
'request.xml',
|
||||
'ir.xml',
|
||||
],
|
||||
'translation': [
|
||||
'locale/cs_CZ.po',
|
||||
'locale/bg_BG.po',
|
||||
'locale/ca_ES.po',
|
||||
'locale/fr_FR.po',
|
||||
'locale/de_DE.po',
|
||||
'locale/es_AR.po',
|
||||
'locale/es_ES.po',
|
||||
'locale/es_CO.po',
|
||||
'locale/nl_NL.po',
|
||||
'locale/ru_RU.po',
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[tryton]
|
||||
depends:
|
||||
ir
|
||||
xml:
|
||||
res.xml
|
||||
group.xml
|
||||
user.xml
|
||||
request.xml
|
||||
ir.xml
|
||||
@@ -1,17 +0,0 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
{
|
||||
'name': 'Test',
|
||||
'description': '''A module for unittest.
|
||||
''',
|
||||
'active': True,
|
||||
'depends': ['ir', 'res'],
|
||||
'xml': [
|
||||
'import_data.xml',
|
||||
'sequence.xml',
|
||||
'workflow.xml',
|
||||
'wizard.xml',
|
||||
],
|
||||
'translation': [
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[tryton]
|
||||
depends:
|
||||
ir
|
||||
res
|
||||
xml:
|
||||
import_data.xml
|
||||
sequence.xml
|
||||
workflow.xml
|
||||
wizard.xml
|
||||
@@ -1,51 +0,0 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
{
|
||||
'name': 'WebDAV',
|
||||
'name_bg_BG': 'WebDAV',
|
||||
'name_ca_ES': 'WebDAV',
|
||||
'name_de_DE': 'WebDAV',
|
||||
'name_es_AR': 'WebDAV',
|
||||
'name_es_ES': 'WebDAV',
|
||||
'name_fr_FR': 'WebDAV',
|
||||
'name_nl_NL': 'WebDAV',
|
||||
'name_ru_RU': 'WebDAV',
|
||||
'description': '''Basic module providing concept and administration of integrated WebDAV features.
|
||||
''',
|
||||
'description_bg_BG': '''Общ модул предоставящ представа и администрация на интегрирани WebDAV услуги.
|
||||
''',
|
||||
'description_ca_ES': '''Mòdul bàsic que proporciona el concepte i l'administració de serveis WebDAV integrats.
|
||||
''',
|
||||
'description_de_DE': '''Basismodul für Konzept und Administration der integrierten WebDAV-Komponente
|
||||
''',
|
||||
'description_es_AR': '''Módulo básico que provee el concepto y la administración de servicios WebDAV integrados.
|
||||
''',
|
||||
'description_es_ES': '''Módulo básico que proporciona el concepto y la administración de servicios WebDAV integrados.
|
||||
''',
|
||||
'description_fr_FR': '''Module de base fournissant les concepts et l'administration des fonctionnalités integrées de WebDAV.
|
||||
''',
|
||||
'description_nl_NL': '''Basismodule voor het instellen en beheren van de geïntegreerde WebDAV functionaliteit
|
||||
''',
|
||||
'description_ru_RU': '''Базовый модуль обеспечения и управления встроенными функциями WebDAV.
|
||||
''',
|
||||
'active': True,
|
||||
'depends': [
|
||||
'ir',
|
||||
'res',
|
||||
],
|
||||
'xml': [
|
||||
'webdav.xml',
|
||||
],
|
||||
'translation': [
|
||||
'locale/cs_CZ.po',
|
||||
'locale/bg_BG.po',
|
||||
'locale/ca_ES.po',
|
||||
'locale/fr_FR.po',
|
||||
'locale/de_DE.po',
|
||||
'locale/es_AR.po',
|
||||
'locale/es_ES.po',
|
||||
'locale/es_CO.po',
|
||||
'locale/nl_NL.po',
|
||||
'locale/ru_RU.po',
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
[tryton]
|
||||
depends:
|
||||
ir
|
||||
res
|
||||
xml:
|
||||
webdav.xml
|
||||
Reference in new issue
Block a user