[MERGE] apps by chs
bzr revid: al@openerp.com-20121220144322-ign0s0u0rbufhedi
This commit is contained in:
+20
-13
@@ -1,14 +1,21 @@
|
||||
.*
|
||||
*.egg-info
|
||||
*.orig
|
||||
*.vim
|
||||
.*.swp
|
||||
.bzrignore
|
||||
.idea
|
||||
.project
|
||||
.pydevproject
|
||||
.ropeproject
|
||||
.settings
|
||||
.DS_Store
|
||||
openerp/addons/*
|
||||
openerp/filestore*
|
||||
.Python
|
||||
*.pyc
|
||||
*.pyo
|
||||
bin/*
|
||||
build/
|
||||
RE:^bin/
|
||||
RE:^dist/
|
||||
RE:^include/
|
||||
|
||||
RE:^share/
|
||||
RE:^man/
|
||||
RE:^lib/
|
||||
|
||||
RE:^addons/\w+/doc/_build/
|
||||
include/
|
||||
lib/
|
||||
share/
|
||||
doc/_build/*
|
||||
win32/*.bat
|
||||
win32/meta.py
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010, 2012 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
@@ -102,5 +102,8 @@ The kernel of OpenERP, needed for all installation.
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
'css': ['static/src/css/modules.css'],
|
||||
'js': [
|
||||
'static/src/js/apps.js',
|
||||
],
|
||||
}
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -24,12 +24,22 @@ from docutils.transforms import Transform, writer_aux
|
||||
from docutils.writers.html4css1 import Writer
|
||||
import imp
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib
|
||||
import urllib2
|
||||
import zipfile
|
||||
import zipimport
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO # NOQA
|
||||
|
||||
import openerp
|
||||
from openerp import modules, pooler, release, tools, addons
|
||||
from openerp import modules, pooler, tools, addons
|
||||
from openerp.modules.db import create_categories
|
||||
from openerp.tools.parse_version import parse_version
|
||||
from openerp.tools.translate import _
|
||||
@@ -43,14 +53,29 @@ ACTION_DICT = {
|
||||
'res_model': 'base.module.upgrade',
|
||||
'target': 'new',
|
||||
'type': 'ir.actions.act_window',
|
||||
'nodestroy':True,
|
||||
'nodestroy': True,
|
||||
}
|
||||
|
||||
def backup(path, raise_exception=True):
|
||||
path = os.path.normpath(path)
|
||||
if not os.path.exists(path):
|
||||
if not raise_exception:
|
||||
return None
|
||||
raise OSError('path does not exists')
|
||||
cnt = 1
|
||||
while True:
|
||||
bck = '%s~%d' % (path, cnt)
|
||||
if not os.path.exists(bck):
|
||||
shutil.move(path, bck)
|
||||
return bck
|
||||
cnt += 1
|
||||
|
||||
|
||||
class module_category(osv.osv):
|
||||
_name = "ir.module.category"
|
||||
_description = "Application"
|
||||
|
||||
def _module_nbr(self,cr,uid, ids, prop, unknow_none, context):
|
||||
def _module_nbr(self, cr, uid, ids, prop, unknow_none, context):
|
||||
cr.execute('SELECT category_id, COUNT(*) \
|
||||
FROM ir_module_module \
|
||||
WHERE category_id IN %(ids)s \
|
||||
@@ -58,7 +83,7 @@ class module_category(osv.osv):
|
||||
FROM ir_module_category \
|
||||
WHERE parent_id IN %(ids)s) \
|
||||
GROUP BY category_id', {'ids': tuple(ids)}
|
||||
)
|
||||
)
|
||||
result = dict(cr.fetchall())
|
||||
for id in ids:
|
||||
cr.execute('select id from ir_module_category where parent_id=%s', (id,))
|
||||
@@ -71,16 +96,16 @@ class module_category(osv.osv):
|
||||
'parent_id': fields.many2one('ir.module.category', 'Parent Application', select=True),
|
||||
'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Child Applications'),
|
||||
'module_nr': fields.function(_module_nbr, string='Number of Modules', type='integer'),
|
||||
'module_ids' : fields.one2many('ir.module.module', 'category_id', 'Modules'),
|
||||
'description' : fields.text("Description", translate=True),
|
||||
'sequence' : fields.integer('Sequence'),
|
||||
'visible' : fields.boolean('Visible'),
|
||||
'module_ids': fields.one2many('ir.module.module', 'category_id', 'Modules'),
|
||||
'description': fields.text("Description", translate=True),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'visible': fields.boolean('Visible'),
|
||||
'xml_id': fields.function(osv.osv.get_external_id, type='char', size=128, string="External ID"),
|
||||
}
|
||||
_order = 'name'
|
||||
|
||||
_defaults = {
|
||||
'visible' : 1,
|
||||
'visible': 1,
|
||||
}
|
||||
|
||||
class MyFilterMessages(Transform):
|
||||
@@ -119,7 +144,6 @@ class module(osv.osv):
|
||||
info = {}
|
||||
try:
|
||||
info = modules.load_information_from_description_file(name)
|
||||
info['version'] = release.major_version + '.' + info['version']
|
||||
except Exception:
|
||||
_logger.debug('Error when trying to fetch informations for '
|
||||
'module %s', name, exc_info=True)
|
||||
@@ -134,9 +158,10 @@ class module(osv.osv):
|
||||
return res
|
||||
|
||||
def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context=None):
|
||||
res = dict.fromkeys(ids, '')
|
||||
default_version = modules.adapt_version('1.0')
|
||||
res = dict.fromkeys(ids, default_version)
|
||||
for m in self.browse(cr, uid, ids):
|
||||
res[m.id] = self.get_module_info(m.name).get('version', '')
|
||||
res[m.id] = self.get_module_info(m.name).get('version', default_version)
|
||||
return res
|
||||
|
||||
def _get_views(self, cr, uid, ids, field_name=None, arg=None, context=None):
|
||||
@@ -167,9 +192,11 @@ class module(osv.osv):
|
||||
continue
|
||||
|
||||
# then, search and group ir.model.data records
|
||||
imd_models = dict( [(m,[]) for m in dmodels])
|
||||
imd_ids = model_data_obj.search(cr,uid,[('module','=', module_rec.name),
|
||||
('model','in',tuple(dmodels))])
|
||||
imd_models = dict([(m, []) for m in dmodels])
|
||||
imd_ids = model_data_obj.search(cr, uid, [
|
||||
('module', '=', module_rec.name),
|
||||
('model', 'in', tuple(dmodels))
|
||||
])
|
||||
|
||||
for imd_res in model_data_obj.read(cr, uid, imd_ids, ['model', 'res_id'], context=context):
|
||||
imd_models[imd_res['model']].append(imd_res['res_id'])
|
||||
@@ -181,7 +208,7 @@ class module(osv.osv):
|
||||
view_ids = imd_models.get('ir.ui.view', [])
|
||||
for v in view_obj.browse(cr, uid, view_ids, context=context):
|
||||
aa = v.inherit_id and '* INHERIT ' or ''
|
||||
res_mod_dic['views_by_module'].append(aa + v.name + '('+v.type+')')
|
||||
res_mod_dic['views_by_module'].append('%s%s (%s)' % (aa, v.name, v.type))
|
||||
|
||||
report_ids = imd_models.get('ir.actions.report.xml', [])
|
||||
for rx in report_obj.browse(cr, uid, report_ids, context=context):
|
||||
@@ -191,15 +218,13 @@ class module(osv.osv):
|
||||
for um in menu_obj.browse(cr, uid, menu_ids, context=context):
|
||||
res_mod_dic['menus_by_module'].append(um.complete_name)
|
||||
except KeyError, e:
|
||||
_logger.warning(
|
||||
'Data not found for items of %s', module_rec.name)
|
||||
_logger.warning('Data not found for items of %s', module_rec.name)
|
||||
except AttributeError, e:
|
||||
_logger.warning(
|
||||
'Data not found for items of %s %s', module_rec.name, str(e))
|
||||
_logger.warning('Data not found for items of %s %s', module_rec.name, str(e))
|
||||
except Exception, e:
|
||||
_logger.warning('Unknown error while fetching data of %s',
|
||||
module_rec.name, exc_info=True)
|
||||
for key, _ in res.iteritems():
|
||||
_logger.warning('Unknown error while fetching data of %s', module_rec.name, exc_info=True)
|
||||
|
||||
for key in res.iterkeys():
|
||||
for k, v in res[key].iteritems():
|
||||
res[key][k] = "\n".join(sorted(v))
|
||||
return res
|
||||
@@ -232,36 +257,34 @@ class module(osv.osv):
|
||||
# installed_version refer the latest version (the one on disk)
|
||||
# latest_version refer the installed version (the one in database)
|
||||
# published_version refer the version available on the repository
|
||||
'installed_version': fields.function(_get_latest_version,
|
||||
string='Latest Version', type='char'),
|
||||
'installed_version': fields.function(_get_latest_version, string='Latest Version', type='char'),
|
||||
'latest_version': fields.char('Installed Version', size=64, readonly=True),
|
||||
'published_version': fields.char('Published Version', size=64, readonly=True),
|
||||
|
||||
'url': fields.char('URL', size=128, readonly=True),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'dependencies_id': fields.one2many('ir.module.module.dependency',
|
||||
'module_id', 'Dependencies', readonly=True),
|
||||
'dependencies_id': fields.one2many('ir.module.module.dependency', 'module_id', 'Dependencies', readonly=True),
|
||||
'auto_install': fields.boolean('Automatic Installation',
|
||||
help='An auto-installable module is automatically installed by the '
|
||||
'system when all its dependencies are satisfied. '
|
||||
'If the module has no dependency, it is always installed.'),
|
||||
help='An auto-installable module is automatically installed by the '
|
||||
'system when all its dependencies are satisfied. '
|
||||
'If the module has no dependency, it is always installed.'),
|
||||
'state': fields.selection([
|
||||
('uninstallable','Not Installable'),
|
||||
('uninstalled','Not Installed'),
|
||||
('installed','Installed'),
|
||||
('to upgrade','To be upgraded'),
|
||||
('to remove','To be removed'),
|
||||
('to install','To be installed')
|
||||
('uninstallable', 'Not Installable'),
|
||||
('uninstalled', 'Not Installed'),
|
||||
('installed', 'Installed'),
|
||||
('to upgrade', 'To be upgraded'),
|
||||
('to remove', 'To be removed'),
|
||||
('to install', 'To be installed')
|
||||
], string='Status', readonly=True, select=True),
|
||||
'demo': fields.boolean('Demo Data', readonly=True),
|
||||
'license': fields.selection([
|
||||
('GPL-2', 'GPL Version 2'),
|
||||
('GPL-2 or any later version', 'GPL-2 or later version'),
|
||||
('GPL-3', 'GPL Version 3'),
|
||||
('GPL-3 or any later version', 'GPL-3 or later version'),
|
||||
('AGPL-3', 'Affero GPL-3'),
|
||||
('Other OSI approved licence', 'Other OSI Approved Licence'),
|
||||
('Other proprietary', 'Other Proprietary')
|
||||
('GPL-2', 'GPL Version 2'),
|
||||
('GPL-2 or any later version', 'GPL-2 or later version'),
|
||||
('GPL-3', 'GPL Version 3'),
|
||||
('GPL-3 or any later version', 'GPL-3 or later version'),
|
||||
('AGPL-3', 'Affero GPL-3'),
|
||||
('Other OSI approved licence', 'Other OSI Approved Licence'),
|
||||
('Other proprietary', 'Other Proprietary')
|
||||
], string='License', readonly=True),
|
||||
'menus_by_module': fields.function(_get_views, string='Menus', type='text', multi="meta", store=True),
|
||||
'reports_by_module': fields.function(_get_views, string='Reports', type='text', multi="meta", store=True),
|
||||
@@ -283,7 +306,7 @@ class module(osv.osv):
|
||||
return _('The name of the module must be unique !')
|
||||
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'UNIQUE (name)',_name_uniq_msg ),
|
||||
('name_uniq', 'UNIQUE (name)', _name_uniq_msg),
|
||||
]
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
@@ -292,10 +315,9 @@ class module(osv.osv):
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
mod_names = []
|
||||
for mod in self.read(cr, uid, ids, ['state','name'], context):
|
||||
for mod in self.read(cr, uid, ids, ['state', 'name'], context):
|
||||
if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'):
|
||||
raise orm.except_orm(_('Error'),
|
||||
_('You try to remove a module that is installed or will be installed'))
|
||||
raise orm.except_orm(_('Error'), _('You try to remove a module that is installed or will be installed'))
|
||||
mod_names.append(mod['name'])
|
||||
#Removing the entry from ir_model_data
|
||||
#ids_meta = self.pool.get('ir.model.data').search(cr, uid, [('name', '=', 'module_meta_information'), ('module', 'in', mod_names)])
|
||||
@@ -340,7 +362,7 @@ class module(osv.osv):
|
||||
raise orm.except_orm(_('Error'), msg % (module_name, e.args[0]))
|
||||
|
||||
def state_update(self, cr, uid, ids, newstate, states_to_update, context=None, level=100):
|
||||
if level<1:
|
||||
if level < 1:
|
||||
raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !'))
|
||||
demo = False
|
||||
for module in self.browse(cr, uid, ids, context=context):
|
||||
@@ -348,9 +370,9 @@ class module(osv.osv):
|
||||
for dep in module.dependencies_id:
|
||||
if dep.state == 'unknown':
|
||||
raise orm.except_orm(_('Error'), _("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,))
|
||||
ids2 = self.search(cr, uid, [('name','=',dep.name)])
|
||||
ids2 = self.search(cr, uid, [('name', '=', dep.name)])
|
||||
if dep.state != newstate:
|
||||
mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level-1,) or mdemo
|
||||
mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level - 1) or mdemo
|
||||
else:
|
||||
od = self.browse(cr, uid, ids2)[0]
|
||||
mdemo = od.demo or mdemo
|
||||
@@ -359,7 +381,7 @@ class module(osv.osv):
|
||||
if not module.dependencies_id:
|
||||
mdemo = module.demo
|
||||
if module.state in states_to_update:
|
||||
self.write(cr, uid, [module.id], {'state': newstate, 'demo':mdemo})
|
||||
self.write(cr, uid, [module.id], {'state': newstate, 'demo': mdemo})
|
||||
demo = demo or mdemo
|
||||
return demo
|
||||
|
||||
@@ -403,7 +425,7 @@ class module(osv.osv):
|
||||
return self._button_immediate_function(cr, uid, ids, self.button_install, context=context)
|
||||
|
||||
def button_install_cancel(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
|
||||
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo': False})
|
||||
return True
|
||||
|
||||
def module_uninstall(self, cr, uid, ids, context=None):
|
||||
@@ -421,12 +443,13 @@ class module(osv.osv):
|
||||
return True
|
||||
|
||||
def downstream_dependencies(self, cr, uid, ids, known_dep_ids=None,
|
||||
exclude_states=['uninstalled','uninstallable','to remove'],
|
||||
exclude_states=['uninstalled', 'uninstallable', 'to remove'],
|
||||
context=None):
|
||||
"""Return the ids of all modules that directly or indirectly depend
|
||||
on the given module `ids`, and that satisfy the `exclude_states`
|
||||
filter"""
|
||||
if not ids: return []
|
||||
if not ids:
|
||||
return []
|
||||
known_dep_ids = set(known_dep_ids or [])
|
||||
cr.execute('''SELECT DISTINCT m.id
|
||||
FROM
|
||||
@@ -437,13 +460,13 @@ class module(osv.osv):
|
||||
d.name IN (SELECT name from ir_module_module where id in %s) AND
|
||||
m.state NOT IN %s AND
|
||||
m.id NOT IN %s ''',
|
||||
(tuple(ids),tuple(exclude_states), tuple(known_dep_ids or ids)))
|
||||
(tuple(ids), tuple(exclude_states), tuple(known_dep_ids or ids)))
|
||||
new_dep_ids = set([m[0] for m in cr.fetchall()])
|
||||
missing_mod_ids = new_dep_ids - known_dep_ids
|
||||
known_dep_ids |= new_dep_ids
|
||||
if missing_mod_ids:
|
||||
known_dep_ids |= set(self.downstream_dependencies(cr, uid, list(missing_mod_ids),
|
||||
known_dep_ids, exclude_states,context))
|
||||
known_dep_ids, exclude_states, context))
|
||||
return list(known_dep_ids)
|
||||
|
||||
def _button_immediate_function(self, cr, uid, ids, function, context=None):
|
||||
@@ -460,9 +483,9 @@ class module(osv.osv):
|
||||
menu_obj = self.pool.get('ir.ui.menu')
|
||||
menu_ids = menu_obj.search(cr, uid, [('parent_id', '=', False)], context=context)
|
||||
return {
|
||||
'type' : 'ir.actions.client',
|
||||
'tag' : 'reload',
|
||||
'params' : {'menu_id' : menu_ids and menu_ids[0] or False}
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
'params': {'menu_id': menu_ids and menu_ids[0] or False}
|
||||
}
|
||||
|
||||
def button_immediate_uninstall(self, cr, uid, ids, context=None):
|
||||
@@ -496,20 +519,19 @@ class module(osv.osv):
|
||||
self.update_list(cr, uid)
|
||||
|
||||
i = 0
|
||||
while i<len(todo):
|
||||
while i < len(todo):
|
||||
mod = todo[i]
|
||||
i += 1
|
||||
if mod.state not in ('installed','to upgrade'):
|
||||
raise orm.except_orm(_('Error'),
|
||||
_("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
|
||||
if mod.state not in ('installed', 'to upgrade'):
|
||||
raise orm.except_orm(_('Error'), _("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
|
||||
self.check_external_dependencies(mod.name, 'to upgrade')
|
||||
iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context)
|
||||
for dep in depobj.browse(cr, uid, iids, context=context):
|
||||
if dep.module_id.state=='installed' and dep.module_id not in todo:
|
||||
if dep.module_id.state == 'installed' and dep.module_id not in todo:
|
||||
todo.append(dep.module_id)
|
||||
|
||||
ids = map(lambda x: x.id, todo)
|
||||
self.write(cr, uid, ids, {'state':'to upgrade'}, context=context)
|
||||
self.write(cr, uid, ids, {'state': 'to upgrade'}, context=context)
|
||||
|
||||
to_install = []
|
||||
for mod in todo:
|
||||
@@ -517,7 +539,7 @@ class module(osv.osv):
|
||||
if dep.state == 'unknown':
|
||||
raise orm.except_orm(_('Error'), _('You try to upgrade a module that depends on the module: %s.\nBut this module is not available in your system.') % (dep.name,))
|
||||
if dep.state == 'uninstalled':
|
||||
ids2 = self.search(cr, uid, [('name','=',dep.name)])
|
||||
ids2 = self.search(cr, uid, [('name', '=', dep.name)])
|
||||
to_install.extend(ids2)
|
||||
|
||||
self.button_install(cr, uid, to_install, context=context)
|
||||
@@ -550,8 +572,9 @@ class module(osv.osv):
|
||||
|
||||
# update the list of available packages
|
||||
def update_list(self, cr, uid, context=None):
|
||||
res = [0, 0] # [update, add]
|
||||
res = [0, 0] # [update, add]
|
||||
|
||||
default_version = modules.adapt_version('1.0')
|
||||
known_mods = self.browse(cr, uid, self.search(cr, uid, []))
|
||||
known_mods_names = dict([(m.name, m) for m in known_mods])
|
||||
|
||||
@@ -570,7 +593,7 @@ class module(osv.osv):
|
||||
updated_values[key] = values[key]
|
||||
if terp.get('installable', True) and mod.state == 'uninstallable':
|
||||
updated_values['state'] = 'uninstalled'
|
||||
if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''):
|
||||
if parse_version(terp.get('version', default_version)) > parse_version(mod.latest_version or default_version):
|
||||
res[0] += 1
|
||||
if updated_values:
|
||||
self.write(cr, uid, mod.id, updated_values)
|
||||
@@ -587,24 +610,32 @@ class module(osv.osv):
|
||||
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
|
||||
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
|
||||
|
||||
# Trigger load_addons if new module have been discovered it exists on
|
||||
# wsgi handlers, so they can react accordingly
|
||||
if tuple(res) != (0, 0):
|
||||
for handler in openerp.service.wsgi_server.module_handlers:
|
||||
if hasattr(handler,'load_addons'):
|
||||
handler.load_addons()
|
||||
|
||||
return res
|
||||
|
||||
def download(self, cr, uid, ids, download=True, context=None):
|
||||
res = []
|
||||
default_version = modules.adapt_version('1.0')
|
||||
for mod in self.browse(cr, uid, ids, context=context):
|
||||
if not mod.url:
|
||||
continue
|
||||
match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I)
|
||||
version = '0'
|
||||
version = default_version
|
||||
if match:
|
||||
version = match.group(1)
|
||||
if parse_version(mod.installed_version or '0') >= parse_version(version):
|
||||
if parse_version(mod.installed_version) >= parse_version(version):
|
||||
continue
|
||||
res.append(mod.url)
|
||||
if not download:
|
||||
continue
|
||||
zip_content = urllib.urlopen(mod.url).read()
|
||||
fname = modules.get_module_path(str(mod.name)+'.zip', downloaded=True)
|
||||
fname = modules.get_module_path(str(mod.name) + '.zip', downloaded=True)
|
||||
try:
|
||||
with open(fname, 'wb') as fp:
|
||||
fp.write(zip_content)
|
||||
@@ -614,17 +645,77 @@ class module(osv.osv):
|
||||
raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,))
|
||||
terp = self.get_module_info(mod.name)
|
||||
self.write(cr, uid, mod.id, self.get_values_from_terp(terp))
|
||||
cr.execute('DELETE FROM ir_module_module_dependency ' \
|
||||
'WHERE module_id = %s', (mod.id,))
|
||||
self._update_dependencies(cr, uid, mod, terp.get('depends',
|
||||
[]))
|
||||
self._update_category(cr, uid, mod, terp.get('category',
|
||||
'Uncategorized'))
|
||||
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (mod.id,))
|
||||
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
|
||||
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
|
||||
# Import module
|
||||
zimp = zipimport.zipimporter(fname)
|
||||
zimp.load_module(mod.name)
|
||||
return res
|
||||
|
||||
def install_from_urls(self, cr, uid, urls, context=None):
|
||||
OPENERP = 'openerp'
|
||||
tmp = tempfile.mkdtemp()
|
||||
try:
|
||||
for module_name, url in urls.items():
|
||||
if not url:
|
||||
continue # nothing to download, local version is already the last one
|
||||
try:
|
||||
content = urllib2.urlopen(url).read()
|
||||
except Exception, e:
|
||||
_logger.exception('ggr')
|
||||
raise osv.except_osv('grrr', e)
|
||||
else:
|
||||
zipfile.ZipFile(StringIO(content)).extractall(tmp)
|
||||
assert os.path.isdir(os.path.join(tmp, module_name))
|
||||
|
||||
for module_name in urls:
|
||||
if module_name == OPENERP:
|
||||
continue # special case. handled below
|
||||
module_path = modules.get_module_path(module_name, downloaded=True, display_warning=False)
|
||||
bck = backup(module_path, False)
|
||||
shutil.move(os.path.join(tmp, module_name), module_path)
|
||||
if bck:
|
||||
shutil.rmtree(bck)
|
||||
|
||||
if urls.get(OPENERP, None):
|
||||
# special case. it containt the server and the base module.
|
||||
# extract path is not the same
|
||||
|
||||
base_path = os.path.dirname(modules.get_module_path('base'))
|
||||
|
||||
# copy all modules in the SERVER/openerp/addons directory to the new "openerp" module (except base itself)
|
||||
for d in os.listdir(base_path):
|
||||
if d != 'base' and os.path.isdir(os.path.join(base_path, d)):
|
||||
destdir = os.path.join(tmp, OPENERP, 'addons', d) # XXX 'openerp' subdirectory ?
|
||||
shutil.copytree(os.path.join(base_path, d), destdir)
|
||||
|
||||
# then replace the server by the new "base" module
|
||||
server_dir = openerp.tools.config['root_path'] # XXX or dirname()
|
||||
bck = backup(server_dir)
|
||||
shutil.move(os.path.join(tmp, OPENERP), server_dir)
|
||||
#if bck:
|
||||
# shutil.rmtree(bck)
|
||||
|
||||
self.update_list(cr, uid, context=context)
|
||||
|
||||
ids = self.search(cr, uid, [('name', 'in', urls.keys())], context=context)
|
||||
if self.search_count(cr, uid, [('id', 'in', ids), ('state', '=', 'installed')], context=context):
|
||||
# if any to update
|
||||
cr.commit()
|
||||
openerp.service.restart_server()
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'home',
|
||||
'params': {'wait': True},
|
||||
}
|
||||
return self.button_immediate_install(cr, uid, ids, context=context)
|
||||
finally:
|
||||
shutil.rmtree(tmp)
|
||||
|
||||
def install_by_names(self, cr, uid, names, context=None):
|
||||
raise NotImplementedError('# TODO')
|
||||
|
||||
def _update_dependencies(self, cr, uid, mod_browse, depends=None):
|
||||
if depends is None:
|
||||
depends = []
|
||||
@@ -679,20 +770,20 @@ class module_dependency(osv.osv):
|
||||
|
||||
_columns = {
|
||||
# The dependency name
|
||||
'name': fields.char('Name', size=128, select=True),
|
||||
'name': fields.char('Name', size=128, select=True),
|
||||
|
||||
# The module that depends on it
|
||||
'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'),
|
||||
|
||||
'state': fields.function(_state, type='selection', selection=[
|
||||
('uninstallable','Uninstallable'),
|
||||
('uninstalled','Not Installed'),
|
||||
('installed','Installed'),
|
||||
('to upgrade','To be upgraded'),
|
||||
('to remove','To be removed'),
|
||||
('to install','To be installed'),
|
||||
('uninstallable', 'Uninstallable'),
|
||||
('uninstalled', 'Not Installed'),
|
||||
('installed', 'Installed'),
|
||||
('to upgrade', 'To be upgraded'),
|
||||
('to remove', 'To be removed'),
|
||||
('to install', 'To be installed'),
|
||||
('unknown', 'Unknown'),
|
||||
], string='Status', readonly=True, select=True),
|
||||
], string='Status', readonly=True, select=True),
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Modules Categories -->
|
||||
|
||||
<record id="view_module_category_form" model="ir.ui.view">
|
||||
<field name="name">ir.module.category.form</field>
|
||||
<field name="model">ir.module.category</field>
|
||||
@@ -32,7 +30,6 @@
|
||||
</record>
|
||||
|
||||
<!-- Click on a category -->
|
||||
|
||||
<record id="view_module_filter" model="ir.ui.view">
|
||||
<field name="name">ir.module.module.list.select</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
@@ -43,7 +40,7 @@
|
||||
<filter name="app" icon="terp-check" string="Apps" domain="[('application', '=', 1)]"/>
|
||||
<filter name="extra" icon="terp-check" string="Extra" domain="[('application', '=', 0)]"/>
|
||||
<separator/>
|
||||
<filter icon="terp-check" string="Installed" domain="[('state', 'in', ['installed', 'to upgrade', 'to remove'])]"/>
|
||||
<filter name="installed" icon="terp-check" string="Installed" domain="[('state', 'in', ['installed', 'to upgrade', 'to remove'])]"/>
|
||||
<filter icon="terp-dialog-close" string="Not Installed" domain="[('state', 'in', ['uninstalled', 'uninstallable', 'to install'])]"/>
|
||||
<field name="category_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
@@ -53,37 +50,6 @@
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="module_view_kanban">
|
||||
<field name="name">Modules Kanban</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban create="false">
|
||||
<field name="icon"/>
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
<field name="summary"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_module_vignette">
|
||||
<t t-set="installed" t-value="record.state.raw_value == 'installed'"/>
|
||||
<img t-attf-src="#{record.icon.value}" class="oe_module_icon"/>
|
||||
<div class="oe_module_desc">
|
||||
<h4><a type="open"><field name="shortdesc"/></a></h4>
|
||||
<p class="oe_module_name">
|
||||
<t t-if="record.summary.raw_value"><field name="summary"/><br/></t>
|
||||
<i><field name="name" groups="base.group_no_one"/></i>
|
||||
</p>
|
||||
<button type="object" name="button_immediate_install" states="uninstalled">Install</button>
|
||||
<button t-if="installed" disabled="disabled">Installed</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_module_open_categ" model="ir.actions.act_window">
|
||||
<field name="name">Modules</field>
|
||||
<field name="res_model">ir.module.module</field>
|
||||
@@ -100,7 +66,6 @@
|
||||
|
||||
|
||||
<!-- Modules -->
|
||||
|
||||
<record id="module_form" model="ir.ui.view">
|
||||
<field name="name">ir.module.module.form</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
@@ -176,20 +141,74 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="module_view_kanban">
|
||||
<field name="name">Modules Kanban</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban create="false">
|
||||
<field name="icon"/>
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
<field name="summary"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_module_vignette">
|
||||
<t t-set="installed" t-value="record.state.raw_value == 'installed'"/>
|
||||
<img t-attf-src="#{record.icon.value}" class="oe_module_icon"/>
|
||||
<div class="oe_module_desc">
|
||||
<h4><a type="open"><field name="shortdesc"/></a></h4>
|
||||
<p class="oe_module_name">
|
||||
<t t-if="record.summary.raw_value"><field name="summary"/><br/></t>
|
||||
<i><field name="name" groups="base.group_no_one"/></i>
|
||||
</p>
|
||||
<button type="object" name="button_immediate_install" states="uninstalled">Install</button>
|
||||
<button t-if="installed" disabled="disabled">Installed</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
<record id="open_module_tree" model="ir.actions.act_window">
|
||||
<field name="name">Install a Module</field>
|
||||
<!-- uncomment on released
|
||||
<field name="name">Installed Modules</field>
|
||||
-->
|
||||
<field name="name">Modules</field>
|
||||
<field name="res_model">ir.module.module</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="context">{'search_default_app':1}</field>
|
||||
<!-- uncomment on released
|
||||
<field name="context">{'search_default_installed':1}</field>
|
||||
-->
|
||||
<field name="search_view_id" ref="view_module_filter"/>
|
||||
<field name="help" type="html">
|
||||
<p><b>No module found!</b></p>
|
||||
<p>You should try others search criteria.</p>
|
||||
</field>
|
||||
</record>
|
||||
<menuitem id="menu_module_tree" parent="base.menu_management" name="Modules"
|
||||
sequence="1" action="open_module_tree"/>
|
||||
<menuitem id="menu_module_tree" parent="menu_management" name="Installed Modules" sequence="10" action="open_module_tree"/>
|
||||
|
||||
|
||||
<!-- Apps modules -->
|
||||
<record model="ir.actions.client" id="modules_act_cl">
|
||||
<field name="name">Apps</field>
|
||||
<field name="tag">apps</field>
|
||||
</record>
|
||||
<!-- uncomment on released
|
||||
<menuitem id="module_mi" parent="base.menu_management" sequence="3" action="modules_act_cl"/>
|
||||
-->
|
||||
<menuitem id="module_mi" parent="base.menu_management" sequence="20" action="modules_act_cl"/>
|
||||
|
||||
<record model="ir.actions.client" id="modules_updates_act_cl">
|
||||
<field name="name">Updates</field>
|
||||
<field name="tag">apps.updates</field>
|
||||
<field name="params">{}</field>
|
||||
</record>
|
||||
<!-- uncomment on released
|
||||
<menuitem id="menu_module_updates" parent="base.menu_management" sequence="7" action="modules_updates_act_cl"/>
|
||||
-->
|
||||
<menuitem id="menu_module_updates" parent="base.menu_management" sequence="30" action="modules_updates_act_cl"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
id="menu_view_base_module_import"
|
||||
parent="menu_management"
|
||||
groups="base.group_no_one"
|
||||
sequence="1"/>
|
||||
sequence="6"/>
|
||||
-->
|
||||
|
||||
</data>
|
||||
|
||||
@@ -18,17 +18,17 @@
|
||||
</group>
|
||||
<footer>
|
||||
<div states="init">
|
||||
<button name="update_module" string="Update" type="object" class="oe_highlight"/> or
|
||||
<button name="update_module" string="Update" type="object" class="oe_highlight"/> or
|
||||
<button special="cancel" string="Cancel" class="oe_link"/>
|
||||
</div>
|
||||
<div states="done">
|
||||
<button name="action_module_open" string="Open Modules" type="object" class="oe_highlight"/> or
|
||||
<button name="action_module_open" string="Open Modules" type="object" class="oe_highlight"/> or
|
||||
<button special="cancel" string="Close" class="oe_link"/>
|
||||
</div>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</record>
|
||||
|
||||
<record id="action_view_base_module_update" model="ir.actions.act_window">
|
||||
<field name="name">Module Update</field>
|
||||
@@ -45,7 +45,7 @@
|
||||
id="menu_view_base_module_update"
|
||||
groups="base.group_no_one"
|
||||
parent="menu_management"
|
||||
sequence="2"
|
||||
sequence="40"
|
||||
icon="STOCK_CONVERT"/>
|
||||
|
||||
</data>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
groups="base.group_no_one"
|
||||
id="menu_view_base_module_upgrade"
|
||||
parent="menu_management"
|
||||
sequence="3"/>
|
||||
sequence="50"/>
|
||||
|
||||
<record id="view_base_module_upgrade_install" model="ir.ui.view">
|
||||
<field name="name">Module Upgrade Install</field>
|
||||
|
||||
@@ -466,7 +466,8 @@ class res_config_settings(osv.osv_memory):
|
||||
groups.append((name, ref(field_group), ref(field.implied_group)))
|
||||
elif name.startswith('module_') and isinstance(field, fields.boolean):
|
||||
mod_ids = ir_module.search(cr, uid, [('name', '=', name[7:])])
|
||||
modules.append((name, ir_module.browse(cr, uid, mod_ids[0], context)))
|
||||
record = ir_module.browse(cr, uid, mod_ids[0], context) if mod_ids else None
|
||||
modules.append((name, record))
|
||||
else:
|
||||
others.append(name)
|
||||
|
||||
@@ -490,7 +491,7 @@ class res_config_settings(osv.osv_memory):
|
||||
|
||||
# modules: which modules are installed/to install
|
||||
for name, module in classified['module']:
|
||||
res[name] = module.state in ('installed', 'to install', 'to upgrade')
|
||||
res[name] = module and module.state in ('installed', 'to install', 'to upgrade')
|
||||
|
||||
# other fields: call all methods that start with 'get_default_'
|
||||
for method in dir(self):
|
||||
@@ -501,9 +502,7 @@ class res_config_settings(osv.osv_memory):
|
||||
|
||||
def execute(self, cr, uid, ids, context=None):
|
||||
ir_values = self.pool.get('ir.values')
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
ir_module = self.pool.get('ir.module.module')
|
||||
res_groups = self.pool.get('res.groups')
|
||||
classified = self._get_classified_fields(cr, uid, context)
|
||||
|
||||
config = self.browse(cr, uid, ids[0], context)
|
||||
@@ -526,17 +525,26 @@ class res_config_settings(osv.osv_memory):
|
||||
getattr(self, method)(cr, uid, ids, context)
|
||||
|
||||
# module fields: install/uninstall the selected modules
|
||||
to_install_ids = []
|
||||
to_install_names = []
|
||||
to_uninstall_ids = []
|
||||
lm = len('module_')
|
||||
for name, module in classified['module']:
|
||||
if config[name]:
|
||||
if module.state == 'uninstalled': to_install_ids.append(module.id)
|
||||
if not module or module.state == 'uninstalled':
|
||||
to_install_names.append(name[lm:])
|
||||
else:
|
||||
if module.state in ('installed','upgrade'): to_uninstall_ids.append(module.id)
|
||||
if module and module.state in ('installed', 'to upgrade'):
|
||||
to_uninstall_ids.append(module.id)
|
||||
|
||||
if to_install_ids or to_uninstall_ids:
|
||||
ir_module.button_uninstall(cr, uid, to_uninstall_ids, context=context)
|
||||
ir_module.button_immediate_install(cr, uid, to_install_ids, context=context)
|
||||
if to_uninstall_ids:
|
||||
ir_module.button_immediate_uninstall(cr, uid, to_uninstall_ids, context=context)
|
||||
|
||||
if to_install_names:
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'apps',
|
||||
'params': {'modules': to_install_names},
|
||||
}
|
||||
|
||||
config = self.pool.get('res.config').next(cr, uid, [], context=context) or {}
|
||||
if config.get('type') not in ('ir.actions.act_window_close',):
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
openerp.base = function(instance) {
|
||||
|
||||
instance.base.apps_remote = null;
|
||||
instance.base.apps_client = null;
|
||||
|
||||
instance.base.Apps = instance.web.Widget.extend({
|
||||
template: 'EmptyComponent',
|
||||
remote_action_id: 'loempia.action_embed',
|
||||
failback_action_id: 'base.open_module_tree',
|
||||
|
||||
init: function(parent, action) {
|
||||
this._super(parent, action);
|
||||
var options = action.params || {};
|
||||
|
||||
if (options.apps_user) {
|
||||
sessionStorage.setItem('apps.login', options.apps_user);
|
||||
}
|
||||
if (options.apps_access_token) {
|
||||
sessionStorage.setItem('apps.access_token', options.apps_access_token);
|
||||
}
|
||||
|
||||
this.params = options; // NOTE read by embedded client action
|
||||
},
|
||||
|
||||
get_client: function() {
|
||||
// return the client via a deferred, resolved or rejected depending if the remote host is available or not.
|
||||
var check_client_available = function(client) {
|
||||
var d = $.Deferred();
|
||||
var i = new Image();
|
||||
i.onerror = function() {
|
||||
d.reject(client);
|
||||
};
|
||||
i.onload = function() {
|
||||
client.session.session_bind(client.origin).then(function() {
|
||||
// check if client can authenticate
|
||||
client.authenticate().then(
|
||||
function() { /* done */
|
||||
d.resolve(client);
|
||||
}, function() { /* fail */
|
||||
if (client.login === 'anonymous') {
|
||||
d.reject(client);
|
||||
} else {
|
||||
sessionStorage.removeItem('apps.login');
|
||||
sessionStorage.removeItem('apps.access_token');
|
||||
client.bind_crendentials(client.dbname, 'anonymous', 'anonymous');
|
||||
client.authenticate().then(
|
||||
function() { /* done */
|
||||
d.resolve(client);
|
||||
}, function() { /* fail */
|
||||
d.reject(client);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif', client.origin);
|
||||
return d.promise();
|
||||
};
|
||||
if (instance.base.apps_client) {
|
||||
return check_client_available(instance.base.apps_client);
|
||||
} else {
|
||||
var ICP = new instance.web.Model('ir.config_parameter');
|
||||
return ICP.call('get_param', ['apps.server', 'https://apps.openerp.com/apps']).then(function(u) {
|
||||
var link = $(_.str.sprintf('<a href="%s"></a>', u))[0];
|
||||
var host = _.str.sprintf('%s//%s', link.protocol, link.host);
|
||||
var dbname = link.pathname;
|
||||
if (dbname[0] === '/') {
|
||||
dbname = dbname.substr(1);
|
||||
}
|
||||
var login = (sessionStorage ? sessionStorage.getItem('apps.login') : null) || 'anonymous';
|
||||
var passwd = (sessionStorage ? sessionStorage.getItem('apps.access_token') : null) || 'anonymous';
|
||||
if (_.isNull(instance.base.apps_remote)) {
|
||||
instance.base.apps_remote = new openerp.init();
|
||||
}
|
||||
var client = new instance.base.apps_remote.web.EmbeddedClient(null, host, dbname, login, passwd);
|
||||
instance.base.apps_client = client;
|
||||
return check_client_available(client);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (instance.base.apps_client) {
|
||||
instance.base.apps_client.destroy();
|
||||
}
|
||||
return this._super();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
var self = this;
|
||||
return self.get_client().
|
||||
done(function(client) {
|
||||
client.replace(self.$el).
|
||||
done(function() {
|
||||
client.$el.removeClass('openerp');
|
||||
client.do_action(self.remote_action_id);
|
||||
});
|
||||
}).
|
||||
fail(function(client) {
|
||||
self.do_warn('Apps Server not reachable.', 'Showing local modules.', true);
|
||||
self.rpc('/web/action/load', {action_id: self.failback_action_id}).done(function(action) {
|
||||
self.do_action(action);
|
||||
instance.webclient.menu.open_action(action.id);
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
instance.base.AppsUpdates = instance.base.Apps.extend({
|
||||
remote_action_id: 'loempia.action_embed_updates'
|
||||
});
|
||||
|
||||
instance.web.client_actions.add("apps", "instance.base.Apps");
|
||||
instance.web.client_actions.add("apps.updates", "instance.base.AppsUpdates");
|
||||
|
||||
};
|
||||
@@ -207,10 +207,15 @@ def quit_on_signals():
|
||||
pass
|
||||
|
||||
config = openerp.tools.config
|
||||
openerp.service.stop_services()
|
||||
|
||||
if getattr(openerp, 'phoenix', False):
|
||||
# like the phoenix, reborn from ashes...
|
||||
openerp.service._reexec()
|
||||
return
|
||||
|
||||
if config['pidfile']:
|
||||
os.unlink(config['pidfile'])
|
||||
|
||||
openerp.service.stop_services()
|
||||
sys.exit(0)
|
||||
|
||||
def configure_babel_localedata_path():
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
@@ -32,7 +32,8 @@ from openerp.modules.module import \
|
||||
load_information_from_description_file, \
|
||||
get_module_resource, zip_directory, \
|
||||
get_module_path, initialize_sys_path, \
|
||||
load_openerp_module, init_module_models
|
||||
load_openerp_module, init_module_models, \
|
||||
adapt_version
|
||||
|
||||
from openerp.modules.loading import load_modules
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
@@ -329,7 +329,7 @@ def load_information_from_description_file(module):
|
||||
'license': 'AGPL-3',
|
||||
'name': False,
|
||||
'post_load': None,
|
||||
'version': '0.0.0',
|
||||
'version': '1.0',
|
||||
'web': False,
|
||||
'website': '',
|
||||
'sequence': 100,
|
||||
@@ -349,6 +349,7 @@ def load_information_from_description_file(module):
|
||||
# 'active' has been renamed 'auto_install'
|
||||
info['auto_install'] = info['active']
|
||||
|
||||
info['version'] = adapt_version(info['version'])
|
||||
return info
|
||||
|
||||
#TODO: refactor the logger in this file to follow the logging guidelines
|
||||
@@ -447,15 +448,22 @@ def get_modules():
|
||||
|
||||
def get_modules_with_version():
|
||||
modules = get_modules()
|
||||
res = {}
|
||||
res = dict.fromkeys(modules, adapt_version('1.0'))
|
||||
for module in modules:
|
||||
try:
|
||||
info = load_information_from_description_file(module)
|
||||
res[module] = "%s.%s" % (release.major_version, info['version'])
|
||||
except Exception, e:
|
||||
res[module] = info['version']
|
||||
except Exception:
|
||||
continue
|
||||
return res
|
||||
|
||||
def adapt_version(version):
|
||||
serie = release.major_version
|
||||
if version == serie or not version.startswith(serie + '.'):
|
||||
version = '%s.%s' % (serie, version)
|
||||
return version
|
||||
|
||||
|
||||
def get_test_modules(module, submodule, explode):
|
||||
"""
|
||||
Return a list of submodules containing tests.
|
||||
|
||||
@@ -50,4 +50,6 @@ author = 'OpenERP S.A.'
|
||||
author_email = 'info@openerp.com'
|
||||
license = 'AGPL-3'
|
||||
|
||||
nt_service_name = "openerp-server-" + serie
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
##############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
@@ -33,6 +37,7 @@ import wsgi_server
|
||||
import openerp.modules
|
||||
import openerp.netsvc
|
||||
import openerp.osv
|
||||
from openerp.release import nt_service_name
|
||||
import openerp.tools
|
||||
|
||||
#.apidoc title: RPC Services
|
||||
@@ -97,27 +102,58 @@ def stop_services():
|
||||
|
||||
_logger.info("Initiating shutdown")
|
||||
_logger.info("Hit CTRL-C again or send a second signal to force the shutdown.")
|
||||
logging.shutdown()
|
||||
|
||||
# Manually join() all threads before calling sys.exit() to allow a second signal
|
||||
# to trigger _force_quit() in case some non-daemon threads won't exit cleanly.
|
||||
# threading.Thread.join() should not mask signals (at least in python 2.5).
|
||||
me = threading.currentThread()
|
||||
_logger.debug('current thread: %r', me)
|
||||
for thread in threading.enumerate():
|
||||
_logger.debug('process %r (%r)', thread, thread.isDaemon())
|
||||
if thread != me and not thread.isDaemon() and thread.ident != main_thread_id:
|
||||
while thread.isAlive():
|
||||
_logger.debug('join and sleep')
|
||||
# Need a busyloop here as thread.join() masks signals
|
||||
# and would prevent the forced shutdown.
|
||||
thread.join(0.05)
|
||||
time.sleep(0.05)
|
||||
|
||||
_logger.debug('--')
|
||||
openerp.modules.registry.RegistryManager.delete_all()
|
||||
logging.shutdown()
|
||||
|
||||
def start_services_workers():
|
||||
import openerp.service.workers
|
||||
openerp.multi_process = True
|
||||
|
||||
openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
|
||||
|
||||
def _reexec():
|
||||
"""reexecute openerp-server process with (nearly) the same arguments"""
|
||||
if openerp.tools.osutil.is_running_as_nt_service():
|
||||
subprocess.call('sc stop {0} && sc start {0}'.format(nt_service_name), shell=True)
|
||||
exe = os.path.basename(sys.executable)
|
||||
strip_args = ['-d', '-u']
|
||||
a = sys.argv[:]
|
||||
args = [x for i, x in enumerate(a) if x not in strip_args and a[max(i - 1, 0)] not in strip_args]
|
||||
if not args or args[0] != exe:
|
||||
args.insert(0, exe)
|
||||
os.execv(sys.executable, args)
|
||||
|
||||
def restart_server():
|
||||
if openerp.multi_process:
|
||||
raise NotImplementedError("Multicorn is not supported (but gunicorn was)")
|
||||
pid = openerp.wsgi.core.arbiter_pid
|
||||
os.kill(pid, signal.SIGHUP)
|
||||
else:
|
||||
if os.name == 'nt':
|
||||
def reborn():
|
||||
stop_services()
|
||||
_reexec()
|
||||
|
||||
# run in a thread to let the current thread return response to the caller.
|
||||
threading.Thread(target=reborn).start()
|
||||
else:
|
||||
openerp.phoenix = True
|
||||
os.kill(os.getpid(), signal.SIGINT)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -54,7 +54,12 @@ from openerp import SUPERUSER_ID
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
RPC_VERSION_1 = {'server_version': '6.1', 'protocol_version': 1}
|
||||
RPC_VERSION_1 = {
|
||||
'server_version': release.version,
|
||||
'server_version_info': release.version_info,
|
||||
'server_serie': release.serie,
|
||||
'protocol_version': 1,
|
||||
}
|
||||
|
||||
# This should be moved to openerp.modules.db, along side initialize().
|
||||
def _initialize_db(serv, id, db_name, demo, lang, user_password):
|
||||
|
||||
@@ -442,5 +442,6 @@ def stop_service():
|
||||
"""
|
||||
if httpd:
|
||||
httpd.shutdown()
|
||||
openerp.netsvc.close_socket(httpd.socket)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -26,6 +26,12 @@ Some functions related to the os and os.path module
|
||||
import os
|
||||
from os.path import join as opj
|
||||
|
||||
if os.name == 'nt':
|
||||
import ctypes
|
||||
import win32service as ws
|
||||
import win32serviceutil as wsu
|
||||
|
||||
|
||||
def listdir(dir, recursive=False):
|
||||
"""Allow to recursively get the file listing"""
|
||||
dir = os.path.normpath(dir)
|
||||
@@ -56,6 +62,59 @@ def walksymlinks(top, topdown=True, onerror=None):
|
||||
yield dirpath, dirnames, filenames
|
||||
|
||||
|
||||
if os.name != 'nt':
|
||||
getppid = os.getppid
|
||||
is_running_as_nt_service = lambda: False
|
||||
else:
|
||||
# based on http://mail.python.org/pipermail/python-win32/2007-June/006174.html
|
||||
_TH32CS_SNAPPROCESS = 0x00000002
|
||||
class _PROCESSENTRY32(ctypes.Structure):
|
||||
_fields_ = [("dwSize", ctypes.c_ulong),
|
||||
("cntUsage", ctypes.c_ulong),
|
||||
("th32ProcessID", ctypes.c_ulong),
|
||||
("th32DefaultHeapID", ctypes.c_ulong),
|
||||
("th32ModuleID", ctypes.c_ulong),
|
||||
("cntThreads", ctypes.c_ulong),
|
||||
("th32ParentProcessID", ctypes.c_ulong),
|
||||
("pcPriClassBase", ctypes.c_ulong),
|
||||
("dwFlags", ctypes.c_ulong),
|
||||
("szExeFile", ctypes.c_char * 260)]
|
||||
|
||||
def getppid():
|
||||
CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot
|
||||
Process32First = ctypes.windll.kernel32.Process32First
|
||||
Process32Next = ctypes.windll.kernel32.Process32Next
|
||||
CloseHandle = ctypes.windll.kernel32.CloseHandle
|
||||
hProcessSnap = CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
|
||||
current_pid = os.getpid()
|
||||
try:
|
||||
pe32 = _PROCESSENTRY32()
|
||||
pe32.dwSize = ctypes.sizeof(_PROCESSENTRY32)
|
||||
if not Process32First(hProcessSnap, ctypes.byref(pe32)):
|
||||
raise OSError('Failed getting first process.')
|
||||
while True:
|
||||
if pe32.th32ProcessID == current_pid:
|
||||
return pe32.th32ParentProcessID
|
||||
if not Process32Next(hProcessSnap, ctypes.byref(pe32)):
|
||||
return None
|
||||
finally:
|
||||
CloseHandle(hProcessSnap)
|
||||
|
||||
from contextlib import contextmanager
|
||||
from openerp.release import nt_service_name
|
||||
|
||||
def is_running_as_nt_service():
|
||||
@contextmanager
|
||||
def close_srv(srv):
|
||||
try:
|
||||
yield srv
|
||||
finally:
|
||||
ws.CloseServiceHandle(srv)
|
||||
|
||||
with close_srv(ws.OpenSCManager(None, None, ws.SC_MANAGER_ALL_ACCESS)) as hscm:
|
||||
with close_srv(wsu.SmartOpenService(hscm, nt_service_name, ws.SERVICE_ALL_ACCESS)) as hs:
|
||||
info = ws.QueryServiceStatusEx(hs)
|
||||
return info['ProcessId'] == getppid()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from pprint import pprint as pp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,14 +15,12 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Win32 python extensions modules
|
||||
import win32serviceutil
|
||||
import win32service
|
||||
import win32event
|
||||
import win32api
|
||||
import win32process
|
||||
import servicemanager
|
||||
@@ -30,35 +28,32 @@ import servicemanager
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import thread
|
||||
|
||||
try:
|
||||
import meta
|
||||
except ImportError:
|
||||
if hasattr(sys, 'frozen'):
|
||||
raise
|
||||
from setup import generate_files
|
||||
generate_files()
|
||||
import meta # noqa
|
||||
|
||||
class OpenERPServerService(win32serviceutil.ServiceFramework):
|
||||
# required info
|
||||
_svc_name_ = "openerp-server-6.1"
|
||||
_svc_display_name_ = "OpenERP Server 6.1"
|
||||
# optionnal info
|
||||
_svc_description_ = "OpenERP Server 6.1 service"
|
||||
_svc_name_ = meta.nt_service_name
|
||||
_svc_display_name_ = "%s %s" % (meta.description, meta.serie)
|
||||
|
||||
def __init__(self, args):
|
||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||
# Create an event which we will use to wait on.
|
||||
# The "service stop" request will set this event.
|
||||
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
|
||||
# a reference to the server's process
|
||||
self.terpprocess = None
|
||||
# info if the service terminates correctly or if the server crashed
|
||||
self.stopping = False
|
||||
|
||||
|
||||
def SvcStop(self):
|
||||
# Before we do anything, tell the SCM we are starting the stop process.
|
||||
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
|
||||
# stop the running TERP Server: say it's a normal exit
|
||||
# stop the running OpenERP Server: say it's a normal exit
|
||||
win32api.TerminateProcess(int(self.terpprocess._handle), 0)
|
||||
servicemanager.LogInfoMsg("OpenERP Server stopped correctly")
|
||||
# And set my event.
|
||||
win32event.SetEvent(self.hWaitStop)
|
||||
|
||||
|
||||
def StartTERP(self):
|
||||
# The server finds now its configuration automatically on Windows
|
||||
@@ -69,29 +64,19 @@ class OpenERPServerService(win32serviceutil.ServiceFramework):
|
||||
server_path = os.path.join(server_dir, 'server', 'openerp-server.exe')
|
||||
self.terpprocess = subprocess.Popen([server_path], cwd=server_dir, creationflags=win32process.CREATE_NO_WINDOW)
|
||||
|
||||
|
||||
def StartControl(self,ws):
|
||||
# this listens to the Service Manager's events
|
||||
win32event.WaitForSingleObject(ws, win32event.INFINITE)
|
||||
self.stopping = True
|
||||
|
||||
def SvcDoRun(self):
|
||||
# Start OpenERP Server itself
|
||||
self.StartTERP()
|
||||
# start the loop waiting for the Service Manager's stop signal
|
||||
thread.start_new_thread(self.StartControl, (self.hWaitStop,))
|
||||
# Log a info message that the server is running
|
||||
servicemanager.LogInfoMsg("OpenERP Server up and running")
|
||||
# verification if the server is really running, else quit with an error
|
||||
self.terpprocess.wait()
|
||||
if not self.stopping:
|
||||
sys.exit("OpenERP Server check: server not running, check the logfile for more info")
|
||||
# exit with same exit code as OpenERP process
|
||||
sys.exit(self.terpprocess.wait())
|
||||
|
||||
|
||||
def option_handler(opts):
|
||||
# configure the service to auto restart on failures...
|
||||
subprocess.call(['sc', 'failure', meta.nt_service_name, 'reset=', '0', 'actions=', 'restart/0/restart/0/restart/0'])
|
||||
|
||||
if __name__=='__main__':
|
||||
if __name__ == '__main__':
|
||||
# Do with the service whatever option is passed in the command line
|
||||
win32serviceutil.HandleCommandLine(OpenERPServerService)
|
||||
win32serviceutil.HandleCommandLine(OpenERPServerService, customOptionHandler=option_handler)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
+41
-10
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -30,15 +30,46 @@ def datas():
|
||||
r.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*')))
|
||||
return r
|
||||
|
||||
setup(service=["OpenERPServerService"],
|
||||
options={"py2exe":{"excludes":["Tkconstants","Tkinter","tcl",
|
||||
"_imagingtk","PIL._imagingtk",
|
||||
"ImageTk", "PIL.ImageTk",
|
||||
"FixTk"],
|
||||
"skip_archive": 1,
|
||||
"optimize": 2,}},
|
||||
meta = {}
|
||||
execfile(os.path.join(os.path.dirname(__file__), '..', 'openerp', 'release.py'), meta)
|
||||
|
||||
def generate_files():
|
||||
actions = {
|
||||
'start': ['stop', 'start'],
|
||||
'stop': ['stop'],
|
||||
}
|
||||
|
||||
files = []
|
||||
for action, steps in actions.items():
|
||||
fname = action + '.bat'
|
||||
files.append(fname)
|
||||
with open(fname, 'w') as fp:
|
||||
fp.write('@PATH=%WINDIR%\system32;%WINDIR%;%WINDIR%\System32\Wbem;.\n')
|
||||
for step in steps:
|
||||
fp.write('@net %s %s\n' % (step, meta['nt_service_name']))
|
||||
|
||||
files.append('meta.py')
|
||||
with open('meta.py', 'w') as fp:
|
||||
for m in 'description serie nt_service_name'.split():
|
||||
fp.write("%s = %r\n" % (m, meta[m],))
|
||||
|
||||
return files
|
||||
|
||||
excludes = "Tkconstants Tkinter tcl _imagingtk PIL._imagingtk ImageTk PIL.ImageTk FixTk".split()
|
||||
|
||||
setup(service = ["OpenERPServerService"],
|
||||
version = meta['version'],
|
||||
license = meta['license'],
|
||||
url = meta['url'],
|
||||
author = meta['author'],
|
||||
author_email = meta['author_email'],
|
||||
data_files = generate_files(),
|
||||
options = {"py2exe": {
|
||||
"excludes": excludes,
|
||||
"skip_archive": 1,
|
||||
"optimize": 2,
|
||||
}},
|
||||
data_files=datas(),
|
||||
)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
@PATH=%WINDIR%\system32;%WINDIR%;%WINDIR%\System32\Wbem;.
|
||||
|
||||
@net stop openerp-server-6.1
|
||||
|
||||
@net start openerp-server-6.1
|
||||
|
||||
cls
|
||||
@@ -1,5 +0,0 @@
|
||||
@PATH=%WINDIR%\system32;%WINDIR%;%WINDIR%\System32\Wbem;.
|
||||
|
||||
@net stop openerp-server-6.1
|
||||
|
||||
cls
|
||||
Reference in New Issue
Block a user